diff --git a/PCB/BOM.csv b/PCB/BOM.csv index 79d3275b..3477969c 100644 --- a/PCB/BOM.csv +++ b/PCB/BOM.csv @@ -1,30 +1,30 @@ Item,Quantity,Designator,Value,Footprint,LCSC Part #,Comment 1,1,AE1,Antenna,RFANT5220110A2T,C454142, -2,11,C1, C10, C12, C14, C17, C20, C22, C23, C24, C26, C9,100nF,0603,C1591, +2,11,C1; C10; C12; C14; C17; C20; C22; C23; C24; C26; C9,100nF,0603,C1591, 3,1,C11,2.2uF,0603,C913904, -4,10,C13, C16, C21, C25, C28, C3, C5, C6, C7, C8,10uF,0805,C15850, -5,3,C15, C2, C4,1uF,0603,C15849, +4,10,C13; C16; C21; C25; C28; C3; C5; C6; C7; C8,10uF,0805,C15850, +5,3,C15; C2; C4,1uF,0603,C15849, 6,1,D1,MBR0520,D_SOD-123,C475717, 7,1,D2,SFH4547 ,LED_5mm_Side_Mount,C405273, -8,3,D3, D4, D5,LED,0603,C965807, +8,3,D3; D4; D5,LED,0603,C965807, 9,1,F1,500mA,1206,C220146, 10,1,J1,USB_C_Receptacle_USB2.0,USB_C_Receptacle_HRO_TYPE-C-31-M-12,C2982555, 12,1,J2,Conn_01x50,TFT_2.83IN_240X320_50PIN,C11098, 13,1,J3,JST_PH_B2B-PH,JST_PH_B2B-PH-SM4-TB_1x02-1MP_P2.00mm_Vertical,C160352, -14,4,Q1, Q3, Q4, Q5,DMG2301L,SOT-23,C102619, +14,4,Q1; Q3; Q4; Q5,DMG2301L,SOT-23,C102619, 15,1,Q2,MBT3904DW1,SOT-363_SC-70-6,C110150, 16,1,Q6,FS8205A,SOT-23-6,C908265, -17,4,R1, R39, R40, R5,2k2,0603,C114662, -18,4,R11, R12, R25, R8,4k7,0603,C99782, -19,15,R13, R14, R19, R20, R22, R23, R26, R30, R31, R32, R33, R34, R35, R37, R6,10k,0603,C98220, -20,4,R15, R16, R17, R18,10,0603,C109318, +17,4,R1; R39; R40; R5,2k2,0603,C114662, +18,4,R11; R12; R25; R8,4k7,0603,C99782, +19,15,R13; R14; R19; R20; R22; R23; R26; R30; R31; R32; R33; R34; R35; R37; R6,10k,0603,C98220, +20,4,R15; R16; R17; R18,10,0603,C109318, 21,1,R21,3R3,0805,C2849062, -22,3,R27, R28, R29,330,0603,C105881, +22,3,R27; R28; R29,330,0603,C105881, 23,1,R3,1k,0603,C22548, 24,1,R36,0,0603,C116699, 25,1,R4,100,0603,C125923, -26,2,R7, R9,5.1k,0603,C105580, -27,25,S1, S10, S11, S12, S13, S14, S15, S16, S17, S18, S19, S2, S20, S22, S23, S24, S25, S26, S3, S4, S5, S6, S7, S8, S9,TL3342F160QG,SW_SPST_TL3342,C778172 , +26,2,R7; R9,5.1k,0603,C105580, +27,25,S1; S10; S11; S12; S13; S14; S15; S16; S17; S18; S19; S2; S20; S22; S23; S24; S25; S26; S3; S4; S5; S6; S7; S8; S9,TL3342F160QG,SW_SPST_TL3342,C778172 , 28,1,U1,DW01A,SOT-23-6,C18164398, 29,1,U11,TP4056,Infineon_PG-DSO-8-27_3.9x4.9mm_EP2.65x3mm,C725790, 30,1,U2,XC6220B331,SOT-23-5,C86534, diff --git a/PCB/production/BOM with D2.xlsx b/PCB/production/BOM with D2.xlsx new file mode 100644 index 00000000..e979bc1c Binary files /dev/null and b/PCB/production/BOM with D2.xlsx differ diff --git a/PCB/production/BOM.xlsx b/PCB/production/BOM.xlsx new file mode 100644 index 00000000..f57922e6 Binary files /dev/null and b/PCB/production/BOM.xlsx differ diff --git a/PCB/production/CPL.csv b/PCB/production/CPL.csv new file mode 100644 index 00000000..e20a82b4 --- /dev/null +++ b/PCB/production/CPL.csv @@ -0,0 +1,113 @@ +Designator,Mid X,Mid Y,Rotation,Layer +AE1,55.500146,-85.711992,0.0,top +C1,236.2,-91.2,270.0,top +C10,85.7,-84.3,90.0,top +C11,67.25625,-109.45,270.0,top +C12,58.4,-114.9,90.0,top +C13,67.65,-93.6,0.0,top +C14,67.9,-91.85,180.0,top +C15,68.4,-122.2,90.0,top +C16,95.35,-113.45,90.0,top +C17,87.2,-84.3,90.0,top +C2,238.7,-109.1,90.0,top +C20,226.25,-96.52,90.0,top +C21,67.65,-95.6,0.0,top +C22,75.4,-86.2,180.0,top +C23,97.3,-91.0,0.0,top +C24,227.5,-109.2,0.0,top +C25,97.6,-94.1,270.0,top +C26,239.5,-111.4,180.0,top +C28,238.0,-91.4,90.0,top +C3,89.3,-107.2,270.0,top +C4,233.043,-109.195114,90.0,top +C5,226.15,-90.95,90.0,top +C6,83.1,-107.2,90.0,top +C7,86.7,-93.9,90.0,top +C8,88.7,-93.9,90.0,top +C9,99.4,-93.9,90.0,top +D1,226.9,-119.3,270.0,top +D2,59.676677,-102.0,90.0,top +D3,88.9,-101.7,180.0,top +D4,226.7,-87.0,180.0,top +D5,65.4,-106.1,90.0,top +F1,232.9,-96.4,0.0,top +J1,239.8,-102.0,90.0,top +J2,103.162852,-102.0,270.0,top +J3,224.4,-113.5,270.0,top +Q1,223.78125,-119.95,0.0,top +Q2,222.300146,-103.111992,180.0,top +Q3,94.6,-109.8,90.0,top +Q4,87.6,-90.5,180.0,top +Q5,63.0,-109.5,0.0,top +Q6,236.3,-113.2,90.0,top +R1,63.9,-106.1,270.0,top +R11,92.5,-90.3,90.0,top +R12,94.0,-90.3,90.0,top +R13,222.310276,-100.94946,0.0,top +R14,222.400146,-105.254952,180.0,top +R15,97.837763,-113.008033,180.0,top +R16,97.837763,-110.008033,180.0,top +R17,97.837763,-114.508033,180.0,top +R18,97.837763,-111.508033,180.0,top +R19,98.8,-103.7,180.0,top +R20,65.75625,-109.45,90.0,top +R21,62.1,-106.0625,90.0,top +R22,68.6,-116.05,90.0,top +R23,81.070665,-80.576144,180.0,top +R25,94.8,-107.0,180.0,top +R26,84.9,-90.925,270.0,top +R27,66.49375,-111.7,0.0,top +R28,83.4375,-90.925,270.0,top +R29,94.8,-105.4,180.0,top +R3,233.8,-113.1,270.0,top +R30,97.3,-89.5,0.0,top +R31,132.175,-125.0,0.0,top +R32,132.175,-123.5,0.0,top +R33,132.175,-122.0,0.0,top +R34,132.2,-120.2,0.0,top +R35,132.2,-118.7,0.0,top +R36,69.35,-88.911809,0.0,top +R37,80.2,-97.0,0.0,top +R39,226.7,-88.5,0.0,top +R4,232.3,-113.1,270.0,top +R40,58.4,-117.9,90.0,top +R5,88.905113,-103.203134,0.0,top +R6,220.9,-120.2,270.0,top +R7,233.5,-99.9,0.0,top +R8,236.2,-94.4,90.0,top +R9,234.4,-104.7,90.0,top +S1,177.376677,-120.5,180.0,bottom +S10,174.876677,-102.0,180.0,bottom +S11,220.376677,-118.0,180.0,bottom +S12,207.876677,-117.0,180.0,bottom +S13,220.376677,-86.0,180.0,bottom +S14,220.376677,-107.333333,180.0,bottom +S15,220.376677,-96.666667,180.0,bottom +S16,136.376677,-120.5,180.0,bottom +S17,55.93694,-119.005171,270.0,bottom +S18,177.376677,-83.5,180.0,bottom +S19,150.876677,-102.0,180.0,bottom +S2,190.876677,-117.0,180.0,bottom +S20,162.876677,-90.0,180.0,bottom +S22,136.376677,-108.166667,180.0,bottom +S23,148.376677,-83.5,180.0,bottom +S24,136.376677,-95.833333,180.0,bottom +S25,136.376677,-83.5,180.0,bottom +S26,73.4,-123.3,180.0,top +S3,207.876677,-87.0,180.0,bottom +S4,190.876677,-102.0,180.0,bottom +S5,207.876677,-102.0,180.0,bottom +S6,148.376677,-120.5,180.0,bottom +S7,162.876677,-114.0,180.0,bottom +S8,190.876677,-87.0,180.0,bottom +S9,162.876677,-102.0,180.0,bottom +U1,239.7,-114.2,270.0,top +U11,231.64,-92.15,90.0,top +U2,86.2,-107.2,180.0,top +U3,228.0,-103.25,90.0,top +U4,233.7,-102.05,90.0,top +U5,89.75,-84.786992,90.0,top +U6,55.4,-116.3,90.0,top +U7,76.85,-91.55,270.0,top +U8,235.8375,-109.05,90.0,top +U9,223.6,-96.75,90.0,top diff --git a/Platformio/hardware/ESP32/boardtest/main_boardtest.cpp b/Platformio/hardware/ESP32/boardtest/main_boardtest.cpp new file mode 100644 index 00000000..a1451c7f --- /dev/null +++ b/Platformio/hardware/ESP32/boardtest/main_boardtest.cpp @@ -0,0 +1,658 @@ +// OMOTE firmware for ESP32 +// 2023 Maximilian Kern + +#include // Hardware-specific library +#include // modified for inverted logic +#include +#include "SparkFunLIS3DH.h" +#include "Wire.h" +#include +#include +#include +#include +#include +#include "WiFi.h" +#include +#include "driver/ledc.h" +#include +#include "secrets.h" + +#define ENABLE_WIFI // Comment out to diable connected features + +// Pin assignment ----------------------------------------------------------------------------------------------------------------------- + +#define LCD_DC 9 // defined in TFT_eSPI User_Setup.h +#define LCD_CS 5 +#define LCD_MOSI 23 +#define LCD_SCK 18 +#define LCD_BL 4 +#define LCD_EN 10 + +#define USER_LED 2 + +#define SW_1 32 // 1...5: Output +#define SW_2 26 +#define SW_3 27 +#define SW_4 14 +#define SW_5 12 +#define SW_A 37 // A...E: Input +#define SW_B 38 +#define SW_C 39 +#define SW_D 34 +#define SW_E 35 + +#define IR_RX 15 // IR receiver input +#define ADC_BAT 36 // Battery voltage sense input (1/2 divider) +#define IR_VCC 25 // IR receiver power +#define IR_LED 33 // IR LED output + +#define SCL 22 +#define SDA 19 +#define ACC_INT 20 + +#define CRG_STAT 21 // battery charger feedback + +// Variables and Object declarations ------------------------------------------------------------------------------------------------------ + +// Battery declares +int battery_voltage = 0; +int battery_percentage = 100; +bool battery_ischarging = false; +SFE_MAX1704X fuelGauge(MAX1704X_MAX17048); + +// IMU declarations +int motion = 0; +#define SLEEP_TIMEOUT 20000 // time until device enters sleep mode in milliseconds +#define MOTION_THRESHOLD 50 // motion above threshold keeps device awake +int standbyTimer = SLEEP_TIMEOUT; +bool wakeupByIMUEnabled = true; +LIS3DH IMU(I2C_MODE, 0x19); // Default constructor is I2C, addr 0x19. + +// LCD declarations +TFT_eSPI tft = TFT_eSPI(); +#define screenWidth 240 +#define screenHeight 320 +Adafruit_FT6206 touch = Adafruit_FT6206(); +TS_Point touchPoint; +TS_Point oldPoint; +int backlight_brightness = 255; + +// LVGL declarations +static lv_disp_draw_buf_t draw_buf; +static lv_color_t bufA[ screenWidth * screenHeight / 10 ]; +static lv_color_t bufB[ screenWidth * screenHeight / 10 ]; +lv_obj_t* objBattPercentage; +lv_obj_t* objBattIcon; +lv_obj_t* panel; +lv_color_t color_primary = lv_color_hex(0x303030); // gray + +// Keypad declarations +const byte ROWS = 5; //four rows +const byte COLS = 5; //four columns +//define the symbols on the buttons of the keypads +char hexaKeys[ROWS][COLS] = { + {'s','^','-','m','e'}, // source, channel+, Volume-, mute, record + {'i','r','+','k','d'}, // info, right, Volume+, OK, down + {'4','v','1','3','2'}, // blue, channel-, red, yellow, green + {'>','o','b','u','l'}, // forward, off, back, up, left + {'?','p','c','<','='} // ?, play, config, rewind, stop +}; +byte rowPins[ROWS] = {SW_A, SW_B, SW_C, SW_D, SW_E}; //connect to the row pinouts of the keypad +byte colPins[COLS] = {SW_1, SW_2, SW_3, SW_4, SW_5}; //connect to the column pinouts of the keypad +Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS); +#define BUTTON_PIN_BITMASK 0b1110110000000000000000000010000000000000 //IO34+IO35+IO37+IO38+IO39(+IO13) +byte keyMapTechnisat[ROWS][COLS] = { + {0x69,0x20,0x11,0x0D,0x56}, + {0x4F,0x37,0x10,0x57,0x51}, + {0x6E,0x21,0x6B,0x6D,0x6C}, + {0x34,0x0C,0x22,0x50,0x55}, + {'?' ,0x35,0x2F,0x32,0x36} +}; +byte virtualKeyMapTechnisat[10] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x0}; +byte currentDevice = 1; // Current Device to control (allows switching mappings between devices) + +// IR declarations +IRsend IrSender(IR_LED, true); +IRrecv IrReceiver(IR_RX); + +// Other declarations +byte wakeup_reason; +enum Wakeup_reasons{WAKEUP_BY_RESET, WAKEUP_BY_IMU, WAKEUP_BY_KEYPAD}; +Preferences preferences; + +lv_obj_t* WifiLabel; +lv_obj_t* ChecksTable; +WiFiClient espClient; + +// array for checking which keys have already been recognized +char keysWorking[ROWS][COLS] = { + {'0','0','0','0','0'}, + {'0','0','0','0','0'}, + {'0','0','0','0','0'}, + {'0','0','0','0','0'}, + {'x','0','0','0','0'} +}; + +// Helper Functions ----------------------------------------------------------------------------------------------------------------------- + +// Set the page indicator scroll position relative to the tabview scroll position +static void store_scroll_value_event_cb(lv_event_t* e){ + float bias = (150.0 + 8.0) / 240.0; + int offset = 240 / 2 - 150 / 2 - 8 - 50 - 3; + lv_obj_t* screen = lv_event_get_target(e); + lv_obj_scroll_to_x(panel, lv_obj_get_scroll_x(screen) * bias - offset, LV_ANIM_OFF); +} + +// Wakeup by IMU Switch Event handler +static void WakeEnableSetting_event_cb(lv_event_t * e){ + wakeupByIMUEnabled = lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED); +} + +// Display flushing +void my_disp_flush( lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p ){ + uint32_t w = ( area->x2 - area->x1 + 1 ); + uint32_t h = ( area->y2 - area->y1 + 1 ); + + tft.startWrite(); + tft.setAddrWindow( area->x1, area->y1, w, h ); + tft.pushPixelsDMA( ( uint16_t * )&color_p->full, w * h); + tft.endWrite(); + + lv_disp_flush_ready( disp ); +} + +// Read the touchpad +void my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data){ + // int16_t touchX, touchY; + touchPoint = touch.getPoint(); + int16_t touchX = touchPoint.x; + int16_t touchY = touchPoint.y; + bool touched = false; + if ((touchX > 0) || (touchY > 0)) { + touched = true; + standbyTimer = SLEEP_TIMEOUT; + } + + if( !touched ){ + data->state = LV_INDEV_STATE_REL; + } + else{ + data->state = LV_INDEV_STATE_PR; + + // Set the coordinates + data->point.x = screenWidth - touchX; + data->point.y = screenHeight - touchY; + + //Serial.print( "touchpoint: x" ); + //Serial.print( touchX ); + //Serial.print( " y" ); + //Serial.println( touchY ); + //tft.drawFastHLine(0, screenHeight - touchY, screenWidth, TFT_RED); + //tft.drawFastVLine(screenWidth - touchX, 0, screenHeight, TFT_RED); + } +} + +void activityDetection(){ + static int accXold; + static int accYold; + static int accZold; + int accX = IMU.readFloatAccelX()*1000; + int accY = IMU.readFloatAccelY()*1000; + int accZ = IMU.readFloatAccelZ()*1000; + + // determine motion value as da/dt + motion = (abs(accXold - accX) + abs(accYold - accY) + abs(accZold - accZ)); + // Calculate time to standby + standbyTimer -= 100; + if(standbyTimer < 0) standbyTimer = 0; + // If the motion exceeds the threshold, the standbyTimer is reset + if(motion > MOTION_THRESHOLD) standbyTimer = SLEEP_TIMEOUT; + + // Store the current acceleration and time + accXold = accX; + accYold = accY; + accZold = accZ; +} + +void configIMUInterrupts() +{ + uint8_t dataToWrite = 0; + + //LIS3DH_INT1_CFG + //dataToWrite |= 0x80;//AOI, 0 = OR 1 = AND + //dataToWrite |= 0x40;//6D, 0 = interrupt source, 1 = 6 direction source + //Set these to enable individual axes of generation source (or direction) + // -- high and low are used generically + dataToWrite |= 0x20;//Z high + //dataToWrite |= 0x10;//Z low + dataToWrite |= 0x08;//Y high + //dataToWrite |= 0x04;//Y low + dataToWrite |= 0x02;//X high + //dataToWrite |= 0x01;//X low + if(wakeupByIMUEnabled) IMU.writeRegister(LIS3DH_INT1_CFG, 0b00101010); + else IMU.writeRegister(LIS3DH_INT1_CFG, 0b00000000); + + //LIS3DH_INT1_THS + dataToWrite = 0; + //Provide 7 bit value, 0x7F always equals max range by accelRange setting + dataToWrite |= 0x45; + IMU.writeRegister(LIS3DH_INT1_THS, dataToWrite); + + //LIS3DH_INT1_DURATION + dataToWrite = 0; + //minimum duration of the interrupt + //LSB equals 1/(sample rate) + dataToWrite |= 0x00; // 1 * 1/50 s = 20ms + IMU.writeRegister(LIS3DH_INT1_DURATION, dataToWrite); + + //LIS3DH_CTRL_REG5 + //Int1 latch interrupt and 4D on int1 (preserve fifo en) + IMU.readRegister(&dataToWrite, LIS3DH_CTRL_REG5); + dataToWrite &= 0xF3; //Clear bits of interest + dataToWrite |= 0x08; //Latch interrupt (Cleared by reading int1_src) + //dataToWrite |= 0x04; //Pipe 4D detection from 6D recognition to int1? + IMU.writeRegister(LIS3DH_CTRL_REG5, dataToWrite); + + //LIS3DH_CTRL_REG3 + //Choose source for pin 1 + dataToWrite = 0; + //dataToWrite |= 0x80; //Click detect on pin 1 + dataToWrite |= 0x40; //AOI1 event (Generator 1 interrupt on pin 1) + dataToWrite |= 0x20; //AOI2 event () + //dataToWrite |= 0x10; //Data ready + //dataToWrite |= 0x04; //FIFO watermark + //dataToWrite |= 0x02; //FIFO overrun + IMU.writeRegister(LIS3DH_CTRL_REG3, dataToWrite); + +} + +// Enter Sleep Mode +void enterSleep(){ + // Save settings to internal flash memory + preferences.putBool("wkpByIMU", wakeupByIMUEnabled); + preferences.putUChar("blBrightness", backlight_brightness); + preferences.putUChar("currentDevice", currentDevice); + if(!preferences.getBool("alreadySetUp")) preferences.putBool("alreadySetUp", true); + preferences.end(); + + // Configure IMU + uint8_t intDataRead; + IMU.readRegister(&intDataRead, LIS3DH_INT1_SRC);//clear interrupt + configIMUInterrupts(); + IMU.readRegister(&intDataRead, LIS3DH_INT1_SRC);//really clear interrupt + + #ifdef ENABLE_WIFI + // Power down modem + WiFi.disconnect(); + WiFi.mode(WIFI_OFF); + #endif + + // Prepare IO states + digitalWrite(LCD_DC, LOW); // LCD control signals off + digitalWrite(LCD_CS, LOW); + digitalWrite(LCD_MOSI, LOW); + digitalWrite(LCD_SCK, LOW); + digitalWrite(LCD_EN, HIGH); // LCD logic off + digitalWrite(LCD_BL, HIGH); // LCD backlight off + pinMode(CRG_STAT, INPUT); // Disable Pull-Up + digitalWrite(IR_VCC, LOW); // IR Receiver off + + // Configure button matrix for ext1 interrupt + pinMode(SW_1, OUTPUT); + pinMode(SW_2, OUTPUT); + pinMode(SW_3, OUTPUT); + pinMode(SW_4, OUTPUT); + pinMode(SW_5, OUTPUT); + digitalWrite(SW_1, HIGH); + digitalWrite(SW_2, HIGH); + digitalWrite(SW_3, HIGH); + digitalWrite(SW_4, HIGH); + digitalWrite(SW_5, HIGH); + gpio_hold_en((gpio_num_t)SW_1); + gpio_hold_en((gpio_num_t)SW_2); + gpio_hold_en((gpio_num_t)SW_3); + gpio_hold_en((gpio_num_t)SW_4); + gpio_hold_en((gpio_num_t)SW_5); + // Force display pins to high impedance + // Without this the display might not wake up from sleep + pinMode(LCD_BL, INPUT); + pinMode(LCD_EN, INPUT); + gpio_hold_en((gpio_num_t)LCD_BL); + gpio_hold_en((gpio_num_t)LCD_EN); + gpio_deep_sleep_hold_en(); + + esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK, ESP_EXT1_WAKEUP_ANY_HIGH); + + delay(100); + // Sleep + esp_deep_sleep_start(); +} + +#ifdef ENABLE_WIFI +// WiFi status event +void WiFiEvent(WiFiEvent_t event){ + //Serial.printf("[WiFi-event] event: %d\n", event); + if(event == ARDUINO_EVENT_WIFI_STA_GOT_IP){ + + } + // Set status bar icon based on WiFi status + if(event == ARDUINO_EVENT_WIFI_STA_GOT_IP || event == ARDUINO_EVENT_WIFI_STA_GOT_IP6){ + //lv_label_set_text(WifiLabel, LV_SYMBOL_WIFI); + Serial.println(" WiFi Connected!"); + lv_table_set_cell_value_fmt(ChecksTable, 2, 1, LV_SYMBOL_OK); + } + else{ + //lv_label_set_text(WifiLabel, ""); + } +} +#endif + +// Setup ---------------------------------------------------------------------------------------------------------------------------------- + +int FuelGaugeInitSuccessful; + +void setup() { + + setCpuFrequencyMhz(240); // Make sure ESP32 is running at full speed + + // Find out wakeup cause + if(esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT1){ + if(log(esp_sleep_get_ext1_wakeup_status())/log(2) == 13) wakeup_reason = WAKEUP_BY_IMU; + else wakeup_reason = WAKEUP_BY_KEYPAD; + } + else { + wakeup_reason = WAKEUP_BY_RESET; + } + + // --- IO Initialization --- + + // Button Pin Definition + pinMode(SW_1, OUTPUT); + pinMode(SW_2, OUTPUT); + pinMode(SW_3, OUTPUT); + pinMode(SW_4, OUTPUT); + pinMode(SW_5, OUTPUT); + pinMode(SW_A, INPUT); + pinMode(SW_B, INPUT); + pinMode(SW_C, INPUT); + pinMode(SW_D, INPUT); + pinMode(SW_E, INPUT); + + // Power Pin Definition + pinMode(CRG_STAT, INPUT_PULLUP); + pinMode(ADC_BAT, INPUT); + + // IR Pin Definition + pinMode(IR_RX, INPUT); + pinMode(IR_LED, OUTPUT); + pinMode(IR_VCC, OUTPUT); + digitalWrite(IR_LED, HIGH); // HIGH off - LOW on + digitalWrite(IR_VCC, LOW); // HIGH on - LOW off + + // LCD Pin Definition + pinMode(LCD_EN, OUTPUT); + digitalWrite(LCD_EN, HIGH); + pinMode(LCD_BL, OUTPUT); + digitalWrite(LCD_BL, HIGH); + + // Other Pin Definition + pinMode(ACC_INT, INPUT); + pinMode(USER_LED, OUTPUT); + digitalWrite(USER_LED, LOW); + + // Release GPIO hold in case we are coming out of standby + gpio_hold_dis((gpio_num_t)SW_1); + gpio_hold_dis((gpio_num_t)SW_2); + gpio_hold_dis((gpio_num_t)SW_3); + gpio_hold_dis((gpio_num_t)SW_4); + gpio_hold_dis((gpio_num_t)SW_5); + gpio_hold_dis((gpio_num_t)LCD_EN); + gpio_hold_dis((gpio_num_t)LCD_BL); + gpio_deep_sleep_hold_dis(); + + // Configure the backlight PWM + // Manual setup because ledcSetup() briefly turns on the backlight + ledc_channel_config_t ledc_channel_left; + ledc_channel_left.gpio_num = (gpio_num_t)LCD_BL; + ledc_channel_left.speed_mode = LEDC_HIGH_SPEED_MODE; + ledc_channel_left.channel = LEDC_CHANNEL_5; + ledc_channel_left.intr_type = LEDC_INTR_DISABLE; + ledc_channel_left.timer_sel = LEDC_TIMER_1; + ledc_channel_left.flags.output_invert = 1; // Can't do this with ledcSetup() + ledc_channel_left.duty = 0; + + ledc_timer_config_t ledc_timer; + ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE; + ledc_timer.duty_resolution = LEDC_TIMER_8_BIT; + ledc_timer.timer_num = LEDC_TIMER_1; + ledc_timer.freq_hz = 640; + + ledc_channel_config(&ledc_channel_left); + ledc_timer_config(&ledc_timer); + + // --- Startup --- + + Serial.begin(115200); + + // Setup TFT + + // Slowly charge the VSW voltage to prevent a brownout + // Workaround for hardware rev 1! + for(int i = 0; i < 100; i++){ + digitalWrite(LCD_EN, HIGH); // LCD Logic off + delayMicroseconds(1); + digitalWrite(LCD_EN, LOW); // LCD Logic on + } + + delay(100); // Wait for the LCD driver to power on + tft.init(); + tft.initDMA(); + tft.setRotation(0); + tft.fillScreen(TFT_BLACK); + tft.setSwapBytes(true); + + // Setup touchscreen + Wire.begin(SDA, SCL, 400000); // Configure i2c pins and set frequency to 400kHz + bool TouchInitSuccessful = touch.begin(128); // Initialize touchscreen and set sensitivity threshold + + // Setup LVGL + lv_init(); + lv_disp_draw_buf_init( &draw_buf, bufA, bufB, screenWidth * screenHeight / 10 ); + + // Initialize the display driver + static lv_disp_drv_t disp_drv; + lv_disp_drv_init( &disp_drv ); + disp_drv.hor_res = screenWidth; + disp_drv.ver_res = screenHeight; + disp_drv.flush_cb = my_disp_flush; + disp_drv.draw_buf = &draw_buf; + lv_disp_drv_register( &disp_drv ); + + // Initialize the touchscreen driver + static lv_indev_drv_t indev_drv; + lv_indev_drv_init( &indev_drv ); + indev_drv.type = LV_INDEV_TYPE_POINTER; + indev_drv.read_cb = my_touchpad_read; + lv_indev_drv_register( &indev_drv ); + + // --- LVGL UI Configuration --- + + // Set the background color + lv_obj_set_style_bg_color(lv_scr_act(), lv_color_black(), LV_PART_MAIN); + + + + // Add content to the settings tab + // With a flex layout, setting groups/boxes will position themselves automatically + + + ChecksTable = lv_table_create(lv_scr_act()); + lv_table_set_col_width(ChecksTable, 0, 140); + lv_table_set_col_width(ChecksTable, 1, 60); + lv_table_set_col_cnt(ChecksTable, 2); + lv_obj_remove_style(ChecksTable, NULL, LV_PART_ITEMS | LV_STATE_PRESSED); + + lv_table_set_cell_value_fmt(ChecksTable, 0, 0, "IR LED"); + lv_table_set_cell_value_fmt(ChecksTable, 1, 0, "IR Receiver"); + lv_table_set_cell_value_fmt(ChecksTable, 2, 0, "WiFi"); + lv_table_set_cell_value_fmt(ChecksTable, 3, 0, "Buttons"); + lv_table_set_cell_value_fmt(ChecksTable, 4, 0, "Touchscreen"); + lv_table_set_cell_value_fmt(ChecksTable, 5, 0, "Fuel Gauge"); + lv_table_set_cell_value_fmt(ChecksTable, 6, 0, "IMU"); + + for(int i = 0; i < 7; i++){ //lv_table_get_row_cnt(table) + lv_table_set_cell_value_fmt(ChecksTable, i, 1, "?"); + } + + + + + + + + + + + + + // --- End of LVGL configuration --- + + + #ifdef ENABLE_WIFI + // Setup WiFi + WiFi.setHostname("OMOTE"); //define hostname + WiFi.onEvent(WiFiEvent); + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + WiFi.setSleep(true); + #endif + + // Setup IMU + IMU.settings.accelSampleRate = 50; //Hz. Can be: 0,1,10,25,50,100,200,400,1600,5000 Hz + IMU.settings.accelRange = 2; //Max G force readable. Can be: 2, 4, 8, 16 + IMU.settings.adcEnabled = 0; + IMU.settings.tempEnabled = 0; + IMU.settings.xAccelEnabled = 1; + IMU.settings.yAccelEnabled = 1; + IMU.settings.zAccelEnabled = 1; + int IMUInitSuccessful = IMU.begin(); + uint8_t intDataRead; + IMU.readRegister(&intDataRead, LIS3DH_INT1_SRC);//clear interrupt + + // Setup IR + IrSender.begin(); + digitalWrite(IR_VCC, HIGH); // Turn on IR receiver + IrReceiver.enableIRIn(); // Start the receiver + + // Setup Fuel Gauge IC + FuelGaugeInitSuccessful = fuelGauge.begin(); + + + lv_timer_handler(); // Run the LVGL UI once before the loop takes over + + + Serial.print("Setup finished in "); + Serial.print(millis()); + Serial.println("ms."); + + + // Automated Checks + + + + uint64_t _chipmacid = 0LL; + esp_efuse_mac_get_default((uint8_t*) (&_chipmacid)); + Serial.print("ESP32 MAC: "); + Serial.println(_chipmacid); + + + if(IMUInitSuccessful == 0) lv_table_set_cell_value_fmt(ChecksTable, 6, 1, LV_SYMBOL_OK); + else lv_table_set_cell_value_fmt(ChecksTable, 6, 1, LV_SYMBOL_WARNING); + + if(TouchInitSuccessful) lv_table_set_cell_value_fmt(ChecksTable, 4, 1, LV_SYMBOL_OK); + else lv_table_set_cell_value_fmt(ChecksTable, 4, 1, LV_SYMBOL_WARNING); + + if(FuelGaugeInitSuccessful) lv_table_set_cell_value_fmt(ChecksTable, 5, 1, LV_SYMBOL_OK); + else lv_table_set_cell_value_fmt(ChecksTable, 5, 1, LV_SYMBOL_WARNING); + + lv_table_set_cell_value_fmt(ChecksTable, 2, 1, LV_SYMBOL_WARNING); + +} + +// Loop ------------------------------------------------------------------------------------------------------------------------------------ + +void loop() { + + // Update Backlight brightness + static int fadeInTimer = millis(); // fadeInTimer = time after setup + if(millis() < fadeInTimer + backlight_brightness){ // Fade in the backlight brightness + ledcWrite(5, millis()-fadeInTimer); + } + else { // Dim Backlight before entering standby + if(standbyTimer < 2000) ledcWrite(5, 85); // Backlight dim + else ledcWrite(5, backlight_brightness); // Backlight on + } + + // Update LVGL UI + lv_timer_handler(); + + // Blink debug LED at 1 Hz + digitalWrite(USER_LED, millis() % 1000 > 500); + + // Refresh IMU data at 10Hz + static unsigned long IMUTaskTimer = millis(); + if(millis() - IMUTaskTimer >= 100){ + activityDetection(); + if(standbyTimer == 0){ + Serial.println("Entering Sleep Mode. Goodbye."); + enterSleep(); + } + IMUTaskTimer = millis(); + } + + // Update battery stats at 1Hz + static unsigned long batteryTaskTimer = millis() + 1000; // add 1s to start immediately + if(FuelGaugeInitSuccessful && (millis() - batteryTaskTimer >= 1000)){ + battery_voltage = (int)(fuelGauge.getVoltage()*1000); + battery_percentage = (int)fuelGauge.getSOC(); + battery_ischarging = !digitalRead(CRG_STAT); + batteryTaskTimer = millis(); + } + + // Keypad Handling + customKeypad.getKey(); // Populate key list + for(int i=0; i +<../hardware/ESP32/*> ;+<../hardware/ESP32/lib/ESP32-BLE-Keyboard/*> + -<../hardware/ESP32/boardtest/main_boardtest.cpp> - +[env:esp32_testboard] +extends = env:esp32 +build_flags = + ${env:esp32.build_flags} +build_src_filter = + +<../hardware/ESP32/boardtest/main_boardtest.cpp> + +<../hardware/ESP32/lib/Keypad/src/*> + ; use this if you are using Ubuntu or WSL2 (64 bit compiler) [env:linux_64bit] platform = native@^1.2.1 @@ -166,7 +175,7 @@ build_flags = -D LV_MEM_SIZE="(88U * 1024U)" ;-- OMOTE ----------------------------------------------------------------- -D SDL_HOR_RES=${env.custom_screen_width} - -D SDL_VER_RES=${env.custom_screen_heigth} + -D SDL_VER_RES=${env.custom_screen_height} -D SDL_ZOOM=2 ;-- hardware abstraction, needed to find hardwareLayer.h ------------------ -I hardware diff --git a/README.md b/README.md index 32815624..19098b05 100644 --- a/README.md +++ b/README.md @@ -56,11 +56,11 @@ For details, please see the [wiki for the software simulator for fast creating a ### Building the hardware -The central component of OMOTE is its PCB. If you want to build the PCB yourself, you will need SMT-reflow tools like a hot plate or a hot-air station. The 2-layered board and a solder paste stencil can be ordered from any PCB manufacturer using the [KiCad files](https://github.com/CoretechR/OMOTE/tree/main/PCB). Manufacturers like OSHPARK or Aisler will accept these files directly. For JLCPCB or PCBWay, you can use their plugin to export the optimized Gerber files. A [zip archive](https://github.com/CoretechR/OMOTE/blob/main/PCB/production/gerber.zip) with theses Gerber files is also included in this repository. +The central component of OMOTE is its PCB. If you want to build the PCB yourself, you will need SMT-reflow tools like a hot plate or a hot-air station. The 2-layered board and a solder paste stencil can be ordered from any PCB manufacturer using the [KiCad files](https://github.com/CoretechR/OMOTE/tree/main/PCB). Manufacturers like OSHPARK or Aisler will accept these files directly. For JLCPCB or PCBWay, you can use their plugin to export the optimized Gerber files. A [zip archive](https://github.com/CoretechR/OMOTE/blob/main/PCB/production/gerber.zip) with theses Gerber files is also included in this repository. You can also choose to order assembled PCBs from JLCPCB using the [instructions](https://github.com/CoretechR/OMOTE/wiki/How-to-order-assembled-PCBs) in the Wiki. -I sourced the electrical parts from LCSC, but most of them should be available from the usual suppliers like Digikey or Mouser as well. You can check out the [BOM](https://github.com/CoretechR/OMOTE/blob/main/PCB/BOM.csv) for all the necessary components. +The electrical components can be sourced from LCSC, but most of them should be available from the usual suppliers like Digikey or Mouser as well. You can check out the [BOM](https://github.com/CoretechR/OMOTE/blob/main/PCB/BOM.csv) for all the necessary components. -The project uses a 2000mAh Li-Ion battery with a JST-PHR-2 connector. Any 3.7V Li-Ion battery that fits into the 50x34x10mm dimensions should work alright. Please ***make sure to choose a battery with integrated undervoltage protection*** (usually visible as a small PCB under Kapton tape between the battery cables). +The project uses a 2000mAh Li-Ion battery with a JST-PHR-2 connector. Any 3.7V Li-Ion battery that fits into the 50x34x10mm dimensions should work alright. Board revision 4 includes battery protection features agains overcurrent and undervoltage. It cannot hurt to use a battery with integrated protection anyway (usually visible as a small PCB under Kapton tape between the battery cables). The 2.8" capacitive touchscreen can be sourced from Adafruit ([2770](https://www.adafruit.com/product/2770)). If you look for the part number CH280QV10-CT, you can also buy this display directly from the manufacturer via [Alibaba](https://www.alibaba.com/product-detail/High-Quality-240-3-rgb-320_1600408828330.html). Shipping from China is expensive, so this only makes sense if you order multiple displays. In general, the cost for a single OMOTE is quite high. Check out the buy-sell page on the [Discord](https://discord.com/channels/1138116475559882852/1153343867681243279) to see if you can share the cost of the PCBs and components with others. @@ -68,7 +68,7 @@ The 2.8" capacitive touchscreen can be sourced from Adafruit ([2770](https://www -The [housing and buttons](https://github.com/CoretechR/OMOTE/tree/main/CAD) can be printed using PLA or PETG. I sliced the models with PrusaSlicer with a layer height of 0.25mm and printed them using ColorFabb PETG. It is important that the case part is printed with its flat side towards the print bed using lots of support structures. If your printer is well calibrated, the cover plate will snap onto the case. +The [housing and buttons](https://github.com/CoretechR/OMOTE/tree/main/CAD) can be printed using PLA or PETG. The parts from the project photos were sliced with PrusaSlicer with a layer height of 0.25mm and printed using ColorFabb PETG. It is important that the case part is printed with its flat side towards the print bed using lots of support structures. If your printer is well calibrated, the cover plate will snap onto the case. ### To Dos for software