diff --git a/.github/workflows/build-clang-doxy.yml b/.github/workflows/build-clang-doxy.yml index 79fd6afad..72348d7c0 100644 --- a/.github/workflows/build-clang-doxy.yml +++ b/.github/workflows/build-clang-doxy.yml @@ -370,7 +370,8 @@ jobs: "dfrobot_beetle_esp32c3", "wippersnapper_qtpy_esp32c3", "espressif_esp32c5_devkitc_1_n8r4", - "wippersnapper_feather_esp32c6" + "wippersnapper_feather_esp32c6", + "arduino_nesso_n1" ] include: - offset: "0x1000" @@ -382,6 +383,8 @@ jobs: arduino-platform: "espressif_esp32c5_devkitc_1_n8r4" - offset: "0x0" arduino-platform: "wippersnapper_feather_esp32c6" + - offset: "0x0" + arduino-platform: "arduino_nesso_n1" steps: - name: "skip if unwanted" continue-on-error: true @@ -418,10 +421,15 @@ jobs: with: repository: adafruit/Wippersnapper_Boards path: ws-boards + ref: arduino-nesso-n1 - name: Install CI-Arduino run: bash ci/actions_install.sh - name: Install extra Arduino libraries run: | + # adafruit gfx, nesso branch + git clone --quiet --branch nesso https://github.com/adafruit/Adafruit-GFX-Library.git /home/runner/Arduino/libraries/Adafruit_GFX_Library + # adafruit st7735, ExpanderPin branch + git clone --quiet --branch ExpanderPin https://github.com/adafruit/Adafruit-ST7735-Library.git /home/runner/Arduino/libraries/Adafruit_ST7735_and_ST7789_Library git clone --quiet --branch v4.0.3 https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library git clone --quiet https://github.com/pstolarz/OneWireNg.git /home/runner/Arduino/libraries/OneWireNg - name: Download stable Nanopb diff --git a/examples/Wippersnapper_demo/.arduino_nesso_n1.generate b/examples/Wippersnapper_demo/.arduino_nesso_n1.generate new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/Wippersnapper_demo/.arduino_nesso_n1.generate @@ -0,0 +1 @@ + diff --git a/examples/wippersnapper_debug/.arduino_nesso_n1.test.skip b/examples/wippersnapper_debug/.arduino_nesso_n1.test.skip new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/wippersnapper_debug/.arduino_nesso_n1.test.skip @@ -0,0 +1 @@ + diff --git a/platformio.ini b/platformio.ini index 7d651d14f..e181560e3 100644 --- a/platformio.ini +++ b/platformio.ini @@ -73,7 +73,8 @@ lib_deps = adafruit/Adafruit BME680 Library adafruit/Adafruit MAX1704X adafruit/Adafruit ADT7410 Library - adafruit/Adafruit GFX Library + ; adafruit/Adafruit GFX Library + https://github.com/adafruit/Adafruit-GFX-Library.git#nesso adafruit/Adafruit STMPE610 adafruit/Adafruit TouchScreen adafruit/Adafruit MQTT Library @@ -84,7 +85,8 @@ lib_deps = adafruit/Adafruit SH110X adafruit/Adafruit SSD1306 adafruit/Adafruit EPD - adafruit/Adafruit ST7735 and ST7789 Library + ; adafruit/Adafruit ST7735 and ST7789 Library + https://github.com/adafruit/Adafruit-ST7735-Library.git#ExpanderPin https://github.com/tyeth/omron-devhub_d6t-arduino.git https://github.com/pstolarz/OneWireNg.git ; COMMENT OUT FOR RP2040/RP2350 BOARDS @@ -101,7 +103,7 @@ lib_deps = ; Common build environment for ESP32 platform [common:esp32] -platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.33/platform-espressif32.zip +platform = https://github.com/tyeth/platform-espressif32/releases/download/55.03.33-nesso.1/platform-espressif32-55.03.33-nesso.1.zip ; This is needed for occasional new features and bug fixes ; platform = https://github.com/pioarduino/platform-espressif32#develop lib_ignore = WiFiNINA, WiFiNINA_-_Adafruit_Fork, WiFi101, OneWire @@ -201,6 +203,27 @@ build_flags = board_build.filesystem = littlefs board_build.partitions = min_spiffs.csv +; Arduino Nesso-N1 (Espressif ESP32-C5 16MB FLASH) +[env:arduino_nesso_n1] +extends = common:esp32 +board = arduino_nesso_n1 +; build_type = release +build_type = debug +; upload_protocol = esp-builtin +; debug_tool = esp-builtin +; debug_init_break = tbreak provision +build_flags = + -DARDUINO_ARDUINO_NESSO_N1 + ; -DDEBUG=1 + -DARDUINO_USB_MODE=1 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DESP_LOG_LEVEL=5 + -DARDUINO_LOG_LEVEL=5 + -DCORE_DEBUG_LEVEL=5 + ; -DMQTT_DEBUG=1 +board_build.filesystem = littlefs +board_build.partitions = huge_app.csv + ; Espressif ESP32-C6 4MB NO PSRAM esp32-c6-devkitm-1 [env:espressif_esp32-c6-devkitm-1] extends = common:esp32 diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index 72f77fa04..728fedc7f 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -98,6 +98,49 @@ void Wippersnapper::provision() { // Obtain device's MAC address getMacAddr(); + // Board specific initializations +#ifdef ARDUINO_ARDUINO_NESSO_N1 + Wire.begin(SDA, SCL, 100000); + + // verify chip id 0x49 at 0x0Ah + Wire.beginTransmission(0x49); + Wire.write(0x0A); + Wire.endTransmission(); + Wire.requestFrom(0x49, 1); + uint8_t chipId = Wire.read(); + if (chipId != 0x49) { + WS_DEBUG_PRINTLN("ERROR: AW32001E not found on I2C bus!"); + Wire.endTransmission(); + Wire.end(); + } else { + WS_DEBUG_PRINTLN("AW32001E detected on I2C bus."); + // Disable AW32001E watchdog timer, read 05h, & 0x1F, write back + Wire.beginTransmission(0x49); + Wire.write(0x05); + Wire.endTransmission(); + Wire.requestFrom(0x49, 1); + uint8_t regVal = Wire.read(); + Wire.endTransmission(); + WS_DEBUG_PRINTLN("AW32001E WDT reg before disable: " + String(regVal, BIN)); + delay(10); + regVal &= + 0b00011111; // Clear bits 5:6 to disable Watchdog timer, 7 for discharge + Wire.beginTransmission(0x49); + Wire.write(0x05); + Wire.write(regVal); + Wire.endTransmission(); + Wire.end(); + delay(10); + + battery.enableCharge(); + } + + // // digitalWrite(LORA_ENABLE, FALSE); + // // digitalWrite(LORA_LNA_ENABLE, FALSE); + // // digitalWrite(GROVE_POWER_EN, TRUE); + // delay(10); +#endif + // Initialize the status LED for signaling FS errors initStatusLED(); @@ -2272,7 +2315,7 @@ bool Wippersnapper::generateWSTopics() { strlen(_device_uid) + strlen("/wprsnpr/") + strlen(TOPIC_SIGNALS) + strlen("broker/uart") + 1; -// Allocate memory for dynamic MQTT topic + // Allocate memory for dynamic MQTT topic #ifdef USE_PSRAM WS._topic_signal_uart_brkr = (char *)ps_malloc(topicLen); #else @@ -2301,7 +2344,7 @@ bool Wippersnapper::generateWSTopics() { strlen("/wprsnpr/") + strlen(TOPIC_SIGNALS) + strlen("device/uart") + 1; -// Allocate memory for dynamic MQTT topic + // Allocate memory for dynamic MQTT topic #ifdef USE_PSRAM WS._topic_signal_uart_device = (char *)ps_malloc(topicLen); #else @@ -2325,7 +2368,7 @@ bool Wippersnapper::generateWSTopics() { strlen("/wprsnpr/") + strlen(TOPIC_SIGNALS) + strlen("broker") + strlen(TOPIC_DISPLAY) + 1; -// Pre-allocate memory for topic + // Pre-allocate memory for topic #ifdef USE_PSRAM WS._topic_signal_display_brkr = (char *)ps_malloc(topicLen); #else @@ -2356,7 +2399,7 @@ bool Wippersnapper::generateWSTopics() { strlen("/wprsnpr/") + strlen(TOPIC_SIGNALS) + strlen("device") + strlen(TOPIC_DISPLAY) + 1; -// Allocate memory for dynamic MQTT topic + // Allocate memory for dynamic MQTT topic #ifdef USE_PSRAM WS._topic_signal_display_device = (char *)ps_malloc(topicLen); #else diff --git a/src/Wippersnapper.h b/src/Wippersnapper.h index d1ea1b1dd..f06a91a8a 100644 --- a/src/Wippersnapper.h +++ b/src/Wippersnapper.h @@ -241,6 +241,10 @@ class ws_pixels; class ws_uart; class DisplayController; +#ifdef ARDUINO_ARDUINO_NESSO_N1 +static NessoBattery battery; ///< Nesso-N1 Battery instance +#endif + /**************************************************************************/ /*! @brief Class that provides storage and functions for the Adafruit IO diff --git a/src/Wippersnapper_Boards.h b/src/Wippersnapper_Boards.h index a456c2fc0..b63051cfe 100644 --- a/src/Wippersnapper_Boards.h +++ b/src/Wippersnapper_Boards.h @@ -153,6 +153,7 @@ #define USE_LITTLEFS #define USE_STATUS_LED #define STATUS_LED_PIN 0 +#define STATUS_LED_INVERTED #elif defined(ARDUINO_ADAFRUIT_ITSYBITSY_ESP32) #define BOARD_ID "itsybitsy-esp32" #define USE_LITTLEFS @@ -165,6 +166,12 @@ #define USE_LITTLEFS #define USE_STATUS_LED #define STATUS_LED_PIN 13 +#elif defined(ARDUINO_ARDUINO_NESSO_N1) +#define BOARD_ID "arduino-nesso-n1" +#define USE_LITTLEFS +#define USE_STATUS_LED +#define STATUS_LED_PIN LED_BUILTIN +#define STATUS_LED_INVERTED #elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32C6) #define BOARD_ID "feather-esp32c6" #define USE_LITTLEFS diff --git a/src/components/digitalIO/Wippersnapper_DigitalGPIO.cpp b/src/components/digitalIO/Wippersnapper_DigitalGPIO.cpp index 577c9780d..bbe33b286 100644 --- a/src/components/digitalIO/Wippersnapper_DigitalGPIO.cpp +++ b/src/components/digitalIO/Wippersnapper_DigitalGPIO.cpp @@ -66,9 +66,13 @@ void Wippersnapper_DigitalGPIO::initDigitalPin( wippersnapper_pin_v1_ConfigurePinRequest_Direction_DIRECTION_OUTPUT) { #ifdef STATUS_LED_PIN +#if !defined(ARDUINO_ARDUINO_NESSO_N1) + // if (String("D") + pinName == STATUS_LED_PIN.pin) + // #else // deinit status led, use it as a dio component instead if (pinName == STATUS_LED_PIN) releaseStatusLED(); +#endif #endif pinMode(pinName, OUTPUT); @@ -76,10 +80,16 @@ void Wippersnapper_DigitalGPIO::initDigitalPin( WS_DEBUG_PRINTLN(pinName); // Initialize LOW -#if defined(ARDUINO_ESP8266_ADAFRUIT_HUZZAH) - // The Adafruit Feather ESP8266's built-in LED is reverse wired so setting - // the pin LOW will turn the LED on. - digitalWrite(STATUS_LED_PIN, !0); +#if defined(ARDUINO_ESP8266_ADAFRUIT_HUZZAH) // not until we support + // ExpanderPins || + // defined(STATUS_LED_INVERTED) + if (pinName == STATUS_LED_PIN) { + // The Adafruit Feather ESP8266's built-in LED is reverse wired so setting + // the pin LOW will turn the LED on. + digitalWrite(STATUS_LED_PIN, !0); + } else { + digitalWrite(pinName, LOW); + } #else pinMode(pinName, OUTPUT); digitalWrite(pinName, LOW); // initialize LOW @@ -155,9 +165,11 @@ void Wippersnapper_DigitalGPIO::deinitDigitalPin( // if prv. in-use by DIO, release pin back to application #ifdef STATUS_LED_PIN +#if !defined(ARDUINO_ARDUINO_NESSO_N1) // not until we support ExpanderPins if (pinName == STATUS_LED_PIN) initStatusLED(); #endif +#endif } /********************************************************************/ @@ -175,6 +187,40 @@ int Wippersnapper_DigitalGPIO::digitalReadSvc(int pinName) { return pinVal; } +#if defined(ARDUINO_ARDUINO_NESSO_N1) +/********************************************************************/ +/*! + @brief High-level digitalRead service impl. which performs a + digitalRead. + @param pin + The ExpanderPin instance + @returns The pin's value. +*/ +/********************************************************************/ +int Wippersnapper_DigitalGPIO::digitalReadSvc(ExpanderPin pin) { + // Service using arduino `digitalRead` + int pinVal = digitalRead(pin); + return pinVal; +} + +/*******************************************************************************/ +/*! + @brief Writes a value to a pin. + @param pinName + The pin's name. + @param pinValue + The pin's value. +*/ +/*******************************************************************************/ +void Wippersnapper_DigitalGPIO::digitalWriteSvc(ExpanderPin pin, int pinValue) { + WS_DEBUG_PRINT("Digital Pin Event: Set "); + WS_DEBUG_PRINT(pin.pin); + WS_DEBUG_PRINT(" to "); + WS_DEBUG_PRINTLN(pinValue); + digitalWrite(pin, pinValue); +} +#endif + /*******************************************************************************/ /*! @brief Writes a value to a pin. diff --git a/src/components/digitalIO/Wippersnapper_DigitalGPIO.h b/src/components/digitalIO/Wippersnapper_DigitalGPIO.h index 960687ee5..27e0d02fe 100644 --- a/src/components/digitalIO/Wippersnapper_DigitalGPIO.h +++ b/src/components/digitalIO/Wippersnapper_DigitalGPIO.h @@ -50,6 +50,20 @@ class Wippersnapper_DigitalGPIO { int digitalReadSvc(int pinName); void digitalWriteSvc(uint8_t pinName, int pinValue); +#if defined(ARDUINO_ARDUINO_NESSO_N1) + // void + // initDigitalPin(wippersnapper_pin_v1_ConfigurePinRequest_Direction + // direction, + // ExpanderPin pinName, float period, + // wippersnapper_pin_v1_ConfigurePinRequest_Pull pull); + // void + // deinitDigitalPin(wippersnapper_pin_v1_ConfigurePinRequest_Direction + // direction, + // ExpanderPin pinName); + + int digitalReadSvc(ExpanderPin pinName); + void digitalWriteSvc(ExpanderPin pinName, int pinValue); +#endif void processDigitalInputs(); digitalInputPin *_digital_input_pins; /*!< Array of gpio pin objects */ diff --git a/src/components/display/assets/icons.h b/src/components/display/assets/icons.h index feff4ead6..f1a3cefed 100644 --- a/src/components/display/assets/icons.h +++ b/src/components/display/assets/icons.h @@ -18,6 +18,23 @@ /** * @brief cloud-thin-full icon from FontAwesome (16x16px) + * + * ................ + * ................ + * .....#####...... + * ....##.##....... + * ...##...####.... + * ...#........##.. + * ..##..........#. + * .###........##.. + * ##..........##.. + * ##............## + * ##............## + * .#............## + * .###........##.. + * ..############.. + * ................ + * ................ */ const unsigned char epd_bmp_cloud_online[] = { 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x0d, 0xc0, 0x18, 0x78, 0x10, @@ -26,6 +43,23 @@ const unsigned char epd_bmp_cloud_online[] = { /** * @brief cloud-xmark-thin-full icon from FontAwesome (16x16px) + * + * ................ + * ................ + * .....#####...... + * ....##.##....... + * ...##...####.... + * ...#........##.. + * ..##..........#. + * .###..##..##..## + * ###...###....##. + * ##....####....## + * ##....####....## + * .#............## + * .###........##.. + * ..############.. + * ................ + * ................ */ const unsigned char epd_bmp_cloud_offline[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x0d, 0xc0, 0x18, 0x78, 0x10, @@ -34,6 +68,23 @@ const unsigned char epd_bmp_cloud_offline[] PROGMEM = { /** * @brief wifi-thin-full icon from FontAwesome (16x16px) + * + * ................ + * .....######..... + * ..####....####.. + * .###........###. + * ##............## + * ......####...... + * ....########.... + * ...###....###... + * ...##......##... + * ................ + * ................ + * ......####...... + * ......####...... + * ......####...... + * ................ + * ................ */ const unsigned char epd_bmp_wifi_full[] = { 0x00, 0x00, 0x00, 0x00, 0x07, 0xe0, 0x3c, 0x3c, 0x70, 0x0e, 0xc0, @@ -42,6 +93,23 @@ const unsigned char epd_bmp_wifi_full[] = { /** * @brief wifi-fair-thin-full icon from FontAwesome (16x16px) + * + * ................ + * ................ + * ................ + * ................ + * ................ + * ......####...... + * ....########.... + * ...###....###... + * ...##......##... + * ................ + * ................ + * ......####...... + * ......####...... + * ......####...... + * ................ + * ................ */ const unsigned char epd_bmp_wifi_fair[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -50,6 +118,23 @@ const unsigned char epd_bmp_wifi_fair[] = { /** * @brief wifi-weak-thin-full icon from FontAwesome (16x16px) + * + * ................ + * ................ + * ................ + * ................ + * ................ + * ................ + * ................ + * ................ + * ................ + * ................ + * ................ + * ......####...... + * ......####...... + * ......####...... + * ................ + * ................ */ const unsigned char epd_bmp_wifi_weak[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -58,6 +143,23 @@ const unsigned char epd_bmp_wifi_weak[] = { /** * @brief wifi-slash-thin-full icon from FontAwesome (16x16px) + * + * ................ + * .#.............. + * ..#..#####...... + * ...####...###... + * .##.#.......#... + * .#...#......##.. + * ......##......## + * .......#..#..... + * ........#....... + * .........#...... + * ........##.#.... + * ......###...#... + * .......#.....#.. + * ..............#. + * ................ + * ................ */ const unsigned char epd_bmp_wifi_no_signal[] = { 0x00, 0x00, 0x40, 0x00, 0x27, 0xc0, 0x1e, 0x38, 0x68, 0x00, 0x44, @@ -66,10 +168,126 @@ const unsigned char epd_bmp_wifi_no_signal[] = { /** * @brief battery-full-thin-full icon from FontAwesome (16x16px) + * + * ................ + * ................ + * ................ + * .##############. + * ##............## + * #############.#. + * ####........#.## + * ####........#.## + * ####........#.## + * ####........#.## + * #############.#. + * ##............## + * .##############. + * ................ + * ................ + * ................ */ const unsigned char epd_bmp_bat_full[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0xc0, 0x06, 0xff, 0xfa, 0xf0, 0x0b, 0xf0, 0x0b, 0xf0, 0x0b, 0xf0, 0x0b, 0xff, 0xfa, 0xc0, 0x06, 0x7f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +/** * @brief battery-75-thin-full icon from FontAwesome (16x16px) + * + * ................ + * ................ + * ................ + * .##############. + * ##......... ## + * ##......... ## + * ##......... ## + * ##......... ## + * ##......... ## + * ##......... ## + * ##......... ## + * ##......... ## + * .##############. + * ................ + * ................ + * ................ + */ +const unsigned char epd_bmp_bat_75[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0xc0, 0x06, 0xff, + 0xe6, 0xff, 0xe6, 0xff, 0xe6, 0xff, 0xe6, 0xff, 0xe6, 0xff, 0xe6, + 0xc0, 0x06, 0x7f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/** + * @brief battery-50-thin-full icon from FontAwesome (16x16px) + * + * ................ + * ................ + * ................ + * .##############. + * ##...... ## + * ##...... ## + * ##...... ## + * ##...... ## + * ##...... ## + * ##...... ## + * ##...... ## + * ##...... ## + * .##############. + * ................ + * ................ + * ................ + */ +const unsigned char epd_bmp_bat_50[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0xc0, 0x06, 0xff, + 0x06, 0xff, 0x06, 0xff, 0x06, 0xff, 0x06, 0xff, 0x06, 0xff, 0x06, + 0xc0, 0x06, 0x7f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/** + * @brief battery-25-thin-full icon from FontAwesome (16x16px) + * + * ................ + * ................ + * ................ + * .##############. + * ##... ## + * ##... ## + * ##... ## + * ##... ## + * ##... ## + * ##... ## + * ##... ## + * ##... ## + * .##############. + * ................ + * ................ + * ................ + */ +const unsigned char epd_bmp_bat_25[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0xc0, 0x06, 0xf8, + 0x06, 0xf8, 0x06, 0xf8, 0x06, 0xf8, 0x06, 0xf8, 0x06, 0xf8, 0x06, + 0xc0, 0x06, 0x7f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/** + * @brief battery-empty-thin-full icon from FontAwesome (16x16px) + * + * ................ + * ................ + * ................ + * .##############. + * ## ## + * ## ## + * ## ## + * ## ## + * ## ## + * ## ## + * ## ## + * ## ## + * .##############. + * ................ + * ................ + * ................ + */ +const unsigned char epd_bmp_bat_empty[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0xc0, 0x06, 0xc0, + 0x06, 0xc0, 0x06, 0xc0, 0x06, 0xc0, 0x06, 0xc0, 0x06, 0xc0, 0x06, + 0xc0, 0x06, 0x7f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + #endif // _ICONS_H_ \ No newline at end of file diff --git a/src/components/display/controller.cpp b/src/components/display/controller.cpp index 7c8c1f7d2..15d5b5470 100644 --- a/src/components/display/controller.cpp +++ b/src/components/display/controller.cpp @@ -174,7 +174,16 @@ void DisplayController::update(int32_t rssi, bool is_connected) { // Note: For now, battery is always 100% as we don't have a way to read it // yet. WS_DEBUG_PRINTLN("[display] Updating status bar..."); +#if defined(ARDUINO_ARDUINO_NESSO_N1) + WS_DEBUG_PRINTLN("[display] Reading battery details..."); + WS_DEBUG_PRINT(battery.getVoltage()); + WS_DEBUG_PRINT("V, "); + WS_DEBUG_PRINT(battery.getChargeLevel()); + WS_DEBUG_PRINTLN("%"); + hw_instance->updateStatusBar(rssi, battery.getChargeLevel(), is_connected); +#else hw_instance->updateStatusBar(rssi, 100, is_connected); +#endif WS.runNetFSM(); } } diff --git a/src/components/display/drivers/dispDrvBase.h b/src/components/display/drivers/dispDrvBase.h index a9fa11b68..3e8b0c313 100644 --- a/src/components/display/drivers/dispDrvBase.h +++ b/src/components/display/drivers/dispDrvBase.h @@ -145,6 +145,15 @@ class dispDrvBase { // No-op for base class } + /*! + @brief Draws the battery icon based on the current battery level. + @param bat + The current battery level as a percentage (0-100). + */ + virtual void drawBatteryIcon(uint8_t bat) { + // No-op for base class + } + /*! @brief Updates the status bar with current information (battery level, connectivity status, etc). @@ -175,13 +184,12 @@ class dispDrvBase { int16_t _width; ///< Width of the display uint8_t _rotation; ///< Rotation of the display // statusbar properties - int _statusbar_icons_y; ///< Y position of status bar icons - int _statusbar_icon_battery_x; ///< X position of battery icon - int _statusbar_icon_wifi_x; ///< X position of WiFi icon - int _statusbar_icon_cloud_x; ///< X position of cloud icon - int8_t _statusbar_rssi; ///< RSSI value for status bar - uint8_t - _statusbar_bat; ///< Battery level, as a percentage, for the status bar + int _statusbar_icons_y; ///< Y position of status bar icons + int _statusbar_icon_battery_x; ///< X position of battery icon + int _statusbar_icon_wifi_x; ///< X position of WiFi icon + int _statusbar_icon_cloud_x; ///< X position of cloud icon + int8_t _statusbar_rssi; ///< RSSI value for status bar + uint8_t _statusbar_bat = 100; ///< Battery level %for the status bar bool _statusbar_mqtt_connected; ///< MQTT connection status for the status bar }; diff --git a/src/components/display/drivers/dispDrvSt7789.h b/src/components/display/drivers/dispDrvSt7789.h index b8047b652..a3cc974d4 100644 --- a/src/components/display/drivers/dispDrvSt7789.h +++ b/src/components/display/drivers/dispDrvSt7789.h @@ -67,6 +67,8 @@ class dispDrvSt7789 : public dispDrvBase { defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2_REVTFT) || \ defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT) digitalWrite(TFT_BACKLITE, LOW); +#elif defined(ARDUINO_ARDUINO_NESSO_N1) + digitalWrite(LCD_BACKLIGHT, LOW); #endif } @@ -75,12 +77,31 @@ class dispDrvSt7789 : public dispDrvBase { @return True if the display was initialized successfully, false otherwise. */ bool begin() override { - - _display = new Adafruit_ST7789(_pin_cs, _pin_dc, _pin_rst); +#if defined(ARDUINO_ARDUINO_NESSO_N1) + WS_DEBUG_PRINT("pin_cs: "); + WS_DEBUG_PRINTLN(_pin_cs); + WS_DEBUG_PRINT(" == LCD_CS "); + WS_DEBUG_PRINTLN(_pin_cs == LCD_CS); + if (_pin_cs == LCD_CS) + _display = + new Adafruit_ST7789((int8_t)_pin_cs, (int8_t)_pin_dc, &LCD_RESET); + else +#endif + _display = new Adafruit_ST7789(_pin_cs, _pin_dc, _pin_rst); if (!_display) return false; - _display->init(_width, _height); +// Built in displays with custom init / swapped w+h +#ifdef ARDUINO_ARDUINO_NESSO_N1 + if (_pin_cs == LCD_CS) { + _display->init(135, 240); // Init ST7789 240x135 + _display->invertDisplay(true); + } else { +#endif + _display->init(_width, _height); +#if defined(ARDUINO_ARDUINO_NESSO_N1) + } +#endif _display->setRotation(_rotation); _display->fillScreen(ST77XX_BLACK); _display->setTextColor(ST77XX_WHITE); @@ -96,11 +117,42 @@ class dispDrvSt7789 : public dispDrvBase { defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT) pinMode(TFT_BACKLITE, OUTPUT); digitalWrite(TFT_BACKLITE, HIGH); +#elif defined(ARDUINO_ARDUINO_NESSO_N1) + pinMode(LCD_BACKLIGHT, OUTPUT); + digitalWrite(LCD_BACKLIGHT, HIGH); #endif return true; } + /*! + @brief Draws the battery icon based on the current battery level. + @param bat + The current battery level as a percentage (0-100). + */ + void drawBatteryIcon(uint8_t bat) override { + const unsigned char *bat_icon = epd_bmp_bat_empty; + if (bat >= 75) { + bat_icon = epd_bmp_bat_full; + } else if (bat < 75 && bat >= 50) { + bat_icon = epd_bmp_bat_75; + } else if (bat < 50 && bat >= 25) { + bat_icon = epd_bmp_bat_50; + } else if (bat < 25 && bat >= 10) { + bat_icon = epd_bmp_bat_25; + } else { + bat_icon = epd_bmp_bat_empty; + } + // Clear and draw the new battery icon, based on battery level + _display->fillRect(_statusbar_icon_battery_x, _statusbar_icons_y, + ST7789_STATUSBAR_ICON_SZ, ST7789_STATUSBAR_ICON_SZ, + ST77XX_WHITE); + _display->drawBitmap(_statusbar_icon_battery_x, _statusbar_icons_y, + bat_icon, ST7789_STATUSBAR_ICON_SZ, + ST7789_STATUSBAR_ICON_SZ, ST77XX_BLACK); + _statusbar_bat = bat; + } + /*! @brief Displays the splash screen on the display. */ @@ -162,9 +214,11 @@ class dispDrvSt7789 : public dispDrvBase { _display->drawBitmap(_statusbar_icon_wifi_x, _statusbar_icons_y, epd_bmp_wifi_full, ST7789_STATUSBAR_ICON_SZ, ST7789_STATUSBAR_ICON_SZ, ST77XX_BLACK); - _display->drawBitmap(_statusbar_icon_battery_x, _statusbar_icons_y, - epd_bmp_bat_full, ST7789_STATUSBAR_ICON_SZ, - ST7789_STATUSBAR_ICON_SZ, ST77XX_BLACK); + // _display->drawBitmap(_statusbar_icon_battery_x, _statusbar_icons_y, + // epd_bmp_bat_full, ST7789_STATUSBAR_ICON_SZ, + // ST7789_STATUSBAR_ICON_SZ, ST77XX_BLACK); + + drawBatteryIcon(_statusbar_bat); // Reset text color and size for main text area _display->setTextColor(ST77XX_WHITE); @@ -189,9 +243,10 @@ class dispDrvSt7789 : public dispDrvBase { bool update_rssi = abs(rssi - _statusbar_rssi) >= 3; // Only update cloud icon if MQTT status has changed bool update_mqtt = mqtt_status != _statusbar_mqtt_connected; + bool update_battery = bat != _statusbar_bat; // No need to update if nothing has changed - if (!update_rssi && !update_mqtt) + if (!update_rssi && !update_mqtt && !update_battery) return; if (update_mqtt) { @@ -234,6 +289,11 @@ class dispDrvSt7789 : public dispDrvBase { ST7789_STATUSBAR_ICON_SZ, ST77XX_BLACK); _statusbar_rssi = rssi; } + + // Update battery icon only if battery level has changed + if (update_battery) { + drawBatteryIcon(bat); + } } /*! diff --git a/src/components/pwm/ws_pwm.cpp b/src/components/pwm/ws_pwm.cpp index 2e3ba0dc2..6bb57ee5c 100644 --- a/src/components/pwm/ws_pwm.cpp +++ b/src/components/pwm/ws_pwm.cpp @@ -88,7 +88,9 @@ void ws_pwm::detach(uint8_t pin) { void ws_pwm::writeDutyCycle(uint8_t pin, int dutyCycle) { #if defined(ARDUINO_ARCH_ESP32) _ledcMgr->analogWrite(pin, dutyCycle); -#elif defined(ARDUINO_ESP8266_ADAFRUIT_HUZZAH) && defined(STATUS_LED_PIN) +#elif (defined(ARDUINO_ESP8266_ADAFRUIT_HUZZAH) || \ + defined(STATUS_LED_INVERTED)) && \ + defined(STATUS_LED_PIN) // Adafruit Feather ESP8266's analogWrite() is inverted because its LED pin is // reverse-wired analogWrite(pin, 255 - dutyCycle); diff --git a/src/components/statusLED/Wippersnapper_StatusLED.cpp b/src/components/statusLED/Wippersnapper_StatusLED.cpp index 4cbdd4c85..ac57d0628 100644 --- a/src/components/statusLED/Wippersnapper_StatusLED.cpp +++ b/src/components/statusLED/Wippersnapper_StatusLED.cpp @@ -82,13 +82,33 @@ void initStatusLED() { // Turn off LED initially #if defined(ARDUINO_ESP8266_ADAFRUIT_HUZZAH) analogWrite(STATUS_LED_PIN, 255); +#elif defined(ARDUINO_ARDUINO_NESSO_N1) || defined(STATUS_LED_INVERTED) + digitalWrite(STATUS_LED_PIN, HIGH); #elif defined(ARDUINO_ARCH_ESP32) WS._pwmComponent->attach(STATUS_LED_PIN, LEDC_BASE_FREQ, LEDC_TIMER_12_BIT); - WS._pwmComponent->writeDutyCycle(STATUS_LED_PIN, 0); // turn OFF + WS._pwmComponent->writeDutyCycle(STATUS_LED_PIN, +#if defined(STATUS_LED_INVERTED) + 255 +#else + 0 +#endif + ); // turn OFF #elif defined(ARDUINO_ARCH_RP2040) - digitalWrite(STATUS_LED_PIN, 0); + digitalWrite(STATUS_LED_PIN, +#if defined(STATUS_LED_INVERTED) + 255 +#else + 0 +#endif + ); // turn OFF +#else + analogWrite(STATUS_LED_PIN, +#if defined(STATUS_LED_INVERTED) + 255 #else - analogWrite(STATUS_LED_PIN, 0); + 0 +#endif + ); // turn OFF #endif WS.lockStatusLED = true; // set global pin "lock" flag @@ -116,7 +136,13 @@ void releaseStatusLED() { #endif #ifdef USE_STATUS_LED - digitalWrite(STATUS_LED_PIN, 0); // turn off + digitalWrite(STATUS_LED_PIN, +#if defined(STATUS_LED_INVERTED) + 255 +#else + 0 +#endif + ); // turn OFF pinMode(STATUS_LED_PIN, INPUT); // "release" for use by setting to input (hi-z) WS.lockStatusLED = false; // un-set global pin "lock" flag @@ -181,14 +207,33 @@ void setStatusLEDColor(uint32_t color) { #ifdef USE_STATUS_LED if (!WS.lockStatusLED) return; // status pixel is in-use elsewhere -#ifdef ARDUINO_ARCH_RP2040 - digitalWrite(STATUS_LED_PIN, color > 0); +#if defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARDUINO_NESSO_N1) || \ + defined(STATUS_LED_INVERTED) + digitalWrite(STATUS_LED_PIN, +#if defined(STATUS_LED_INVERTED) + !(color > 0) +#else + color > 0 +#endif + ); #else if (color != BLACK) - WS._pwmComponent->writeDutyCycle( - STATUS_LED_PIN, map(WS.status_pixel_brightness, 0.0, 1.0, 0, 1023)); + WS._pwmComponent->writeDutyCycle(STATUS_LED_PIN, + map(WS.status_pixel_brightness, 0.0, 1.0, +#if defined(STATUS_LED_INVERTED) + 1023, 0 +#else + 0, 1023 +#endif + )); else - WS._pwmComponent->writeDutyCycle(STATUS_LED_PIN, 0); + WS._pwmComponent->writeDutyCycle(STATUS_LED_PIN, +#if defined(STATUS_LED_INVERTED) + 1023 +#else + 0 +#endif + ); #endif #endif } @@ -240,15 +285,33 @@ void setStatusLEDColor(uint32_t color, int brightness) { if (!WS.lockStatusLED) return; -#ifdef ARDUINO_ARCH_RP2040 - digitalWrite(STATUS_LED_PIN, color > 0); +#if defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARDUINO_NESSO_N1) + digitalWrite(STATUS_LED_PIN, +#if defined(STATUS_LED_INVERTED) + !(color > 0) +#else + color > 0 +#endif + ); #else if (color != BLACK) { // re-map for pixel as a LED - int pulseWidth = map(brightness, 0, 255, 0, 1023); + int pulseWidth = map(brightness, 0, 255, +#if defined(STATUS_LED_INVERTED) + 1023, 0 +#else + 0, 1023 +#endif + ); WS._pwmComponent->writeDutyCycle(STATUS_LED_PIN, pulseWidth); } else { - WS._pwmComponent->writeDutyCycle(STATUS_LED_PIN, 0); + WS._pwmComponent->writeDutyCycle(STATUS_LED_PIN, +#if defined(STATUS_LED_INVERTED) + 1023 +#else + 0 +#endif + ); } #endif #endif diff --git a/src/provisioning/littlefs/WipperSnapper_LittleFS.cpp b/src/provisioning/littlefs/WipperSnapper_LittleFS.cpp index 5c58b8b22..5cca6b684 100644 --- a/src/provisioning/littlefs/WipperSnapper_LittleFS.cpp +++ b/src/provisioning/littlefs/WipperSnapper_LittleFS.cpp @@ -12,7 +12,7 @@ * BSD license, all text here must be included in any redistribution. * */ -#if defined(ARDUINO_FEATHER_ESP32) || \ +#if defined(ARDUINO_FEATHER_ESP32) || defined(ARDUINO_ARDUINO_NESSO_N1) || \ defined(ARDUINO_ESP8266_ADAFRUIT_HUZZAH) || \ defined(ARDUINO_ADAFRUIT_ITSYBITSY_ESP32) || \ defined(ARDUINO_ADAFRUIT_FEATHER_ESP32_V2) || \ diff --git a/src/provisioning/tinyusb/Wippersnapper_FS.cpp b/src/provisioning/tinyusb/Wippersnapper_FS.cpp index 2db5d1bde..7371420a5 100644 --- a/src/provisioning/tinyusb/Wippersnapper_FS.cpp +++ b/src/provisioning/tinyusb/Wippersnapper_FS.cpp @@ -28,8 +28,7 @@ defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S3_REVTFT) || \ defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2_REVTFT) || \ defined(ARDUINO_ADAFRUIT_QTPY_ESP32S3_N4R2) || \ - defined(ARDUINO_XIAO_ESP32S3) || \ - defined(ARDUINO_ADAFRUIT_FRUITJAM_RP2350) + defined(ARDUINO_XIAO_ESP32S3) || defined(ARDUINO_ADAFRUIT_FRUITJAM_RP2350) #include "Wippersnapper_FS.h" #include "print_dependencies.h" @@ -296,8 +295,8 @@ bool Wippersnapper_FS::createBootFile() { bootFile.print("Board ID: "); bootFile.println(BOARD_ID); -#if defined(ADAFRUIT_PYPORTAL_M4_TITANO) || defined(USE_AIRLIFT) || \ - defined(ADAFRUIT_METRO_M4_AIRLIFT_LITE) || defined(ADAFRUIT_PYPORTAL) || \ +#if defined(ADAFRUIT_PYPORTAL_M4_TITANO) || defined(USE_AIRLIFT) || \ + defined(ADAFRUIT_METRO_M4_AIRLIFT_LITE) || defined(ADAFRUIT_PYPORTAL) || \ defined(ARDUINO_ADAFRUIT_FRUITJAM_RP2350) bootFile.print("AirLift FW Revision: "); bootFile.println(WS._airlift_version); @@ -357,7 +356,8 @@ void Wippersnapper_FS::createSecretsFile() { secretsFile.flush(); secretsFile.close(); - writeToBootOut("ERROR: Please edit the secrets.json file. Then, reset your board.\n"); + writeToBootOut( + "ERROR: Please edit the secrets.json file. Then, reset your board.\n"); // Re-attach the USB device for file access delay(500); initUSBMSC();