From e94b28d3f999797f9bd516ac21197920ff626970 Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Thu, 25 Dec 2025 19:02:02 +0100 Subject: [PATCH 1/7] Rename oeplnrf.json to nrf52840custom.json --- boards/{oeplnrf.json => nrf52840custom.json} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename boards/{oeplnrf.json => nrf52840custom.json} (99%) diff --git a/boards/oeplnrf.json b/boards/nrf52840custom.json similarity index 99% rename from boards/oeplnrf.json rename to boards/nrf52840custom.json index 38474bd..3a041a1 100644 --- a/boards/oeplnrf.json +++ b/boards/nrf52840custom.json @@ -61,4 +61,4 @@ }, "url": "https://os.mbed.com/platforms/Nordic-nRF52840-DK/", "vendor": "Nordic" -} \ No newline at end of file +} From 7d0f1ece61c10c970455c96f6143fa825735af40 Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Thu, 25 Dec 2025 19:02:51 +0100 Subject: [PATCH 2/7] rename --- variants/nrf52840custom/variant.cpp | 84 +++++++++++++++++ variants/nrf52840custom/variant.h | 140 ++++++++++++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 variants/nrf52840custom/variant.cpp create mode 100644 variants/nrf52840custom/variant.h diff --git a/variants/nrf52840custom/variant.cpp b/variants/nrf52840custom/variant.cpp new file mode 100644 index 0000000..98dfe99 --- /dev/null +++ b/variants/nrf52840custom/variant.cpp @@ -0,0 +1,84 @@ +#include "variant.h" +#include "wiring_constants.h" +#include "wiring_digital.h" +#include "nrf.h" + +const uint32_t g_ADigitalPinMap[] = { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63 +}; + +void initVariant() +{ + pinMode(PIN_QSPI_CS, OUTPUT); + digitalWrite(PIN_QSPI_CS, HIGH); + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(PIN_QSPI_CS, HIGH); + pinMode(LED_GREEN, OUTPUT); + digitalWrite(LED_GREEN, HIGH); + pinMode(LED_BLUE, OUTPUT); + digitalWrite(LED_BLUE, HIGH); +} + diff --git a/variants/nrf52840custom/variant.h b/variants/nrf52840custom/variant.h new file mode 100644 index 0000000..f74b860 --- /dev/null +++ b/variants/nrf52840custom/variant.h @@ -0,0 +1,140 @@ +#ifndef _SEEED_XIAO_NRF52840_SENSE_H_ +#define _SEEED_XIAO_NRF52840_SENSE_H_ + +#define TARGET_SEEED_XIAO_NRF52840_SENSE + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF +//#define USE_LFRC // Board uses RC for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +#define PINS_COUNT (64) +#define NUM_DIGITAL_PINS (64) +#define NUM_ANALOG_INPUTS (8) // A6 is used for battery, A7 is analog reference +#define NUM_ANALOG_OUTPUTS (0) + +// LEDs +#define PIN_LED LED_RED +#define LED_PWR (PINS_COUNT) +#define PIN_NEOPIXEL (PINS_COUNT) +#define NEOPIXEL_NUM 0 + +#define LED_BUILTIN PIN_LED + +#define LED_RED 11 +#define LED_GREEN 13 +#define LED_BLUE 12 + +#define LED_STATE_ON 1 // State when LED is litted + +/* + * Buttons + */ +#define PIN_BUTTON1 (PINS_COUNT) + +//Digital PINs +#define D0 (2) +#define D1 (3) +#define D2 (28) +#define D3 (29) +#define D4 (4) +#define D5 (5) +#define D6 (43) +#define D7 (44) +#define D8 (45) +#define D9 (46) +#define D10 (47) + +/* + * Analog pins + */ +#define PIN_A0 (2) +#define PIN_A1 (3) +#define PIN_A2 (28) +#define PIN_A3 (29) +#define PIN_A4 (4) +#define PIN_A5 (5) +#define PIN_VBAT (31) +#define VBAT_ENABLE (14) + +static const uint8_t A0 = PIN_A0 ; +static const uint8_t A1 = PIN_A1 ; +static const uint8_t A2 = PIN_A2 ; +static const uint8_t A3 = PIN_A3 ; +static const uint8_t A4 = PIN_A4 ; +static const uint8_t A5 = PIN_A5 ; +#define ADC_RESOLUTION 12 + +// Other pins +#define PIN_NFC1 (9) +#define PIN_NFC2 (10) + +//static const uint8_t AREF = PIN_AREF; + +/* + * Serial interfaces + */ +#define PIN_SERIAL1_RX (9) +#define PIN_SERIAL1_TX (10) + +/* + * SPI Interfaces + */ +#define SPI_INTERFACES_COUNT 2 + +#define PIN_SPI_MISO (46) +#define PIN_SPI_MOSI (47) +#define PIN_SPI_SCK (45) + +static const uint8_t MOSI = PIN_SPI_MOSI ; +static const uint8_t MISO = PIN_SPI_MISO ; +static const uint8_t SCK = PIN_SPI_SCK ; + +#define PIN_SPI1_MISO (37) +#define PIN_SPI1_MOSI (39) +#define PIN_SPI1_SCK (35) + +/* + * Wire Interfaces + */ +#define WIRE_INTERFACES_COUNT 1 + +#define PIN_WIRE_SDA (4) +#define PIN_WIRE_SCL (5) + +static const uint8_t SDA = PIN_WIRE_SDA; +static const uint8_t SCL = PIN_WIRE_SCL; + +// QSPI Pins +#define PIN_QSPI_SCK (21) +#define PIN_QSPI_CS (25) +#define PIN_QSPI_IO0 (20) +#define PIN_QSPI_IO1 (24) +#define PIN_QSPI_IO2 (22) +#define PIN_QSPI_IO3 (23) + +// On-board QSPI Flash +#define EXTERNAL_FLASH_DEVICES P25Q16H +#define EXTERNAL_FLASH_USE_QSPI + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif From 6bd0bd715a39024fcced24d99399f6fb617519cd Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Thu, 25 Dec 2025 19:03:14 +0100 Subject: [PATCH 3/7] Delete variants/oeplnrf directory --- variants/oeplnrf/variant.cpp | 84 --------------------- variants/oeplnrf/variant.h | 140 ----------------------------------- 2 files changed, 224 deletions(-) delete mode 100644 variants/oeplnrf/variant.cpp delete mode 100644 variants/oeplnrf/variant.h diff --git a/variants/oeplnrf/variant.cpp b/variants/oeplnrf/variant.cpp deleted file mode 100644 index 98dfe99..0000000 --- a/variants/oeplnrf/variant.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "variant.h" -#include "wiring_constants.h" -#include "wiring_digital.h" -#include "nrf.h" - -const uint32_t g_ADigitalPinMap[] = { - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63 -}; - -void initVariant() -{ - pinMode(PIN_QSPI_CS, OUTPUT); - digitalWrite(PIN_QSPI_CS, HIGH); - pinMode(LED_BUILTIN, OUTPUT); - digitalWrite(PIN_QSPI_CS, HIGH); - pinMode(LED_GREEN, OUTPUT); - digitalWrite(LED_GREEN, HIGH); - pinMode(LED_BLUE, OUTPUT); - digitalWrite(LED_BLUE, HIGH); -} - diff --git a/variants/oeplnrf/variant.h b/variants/oeplnrf/variant.h deleted file mode 100644 index f74b860..0000000 --- a/variants/oeplnrf/variant.h +++ /dev/null @@ -1,140 +0,0 @@ -#ifndef _SEEED_XIAO_NRF52840_SENSE_H_ -#define _SEEED_XIAO_NRF52840_SENSE_H_ - -#define TARGET_SEEED_XIAO_NRF52840_SENSE - -/** Master clock frequency */ -#define VARIANT_MCK (64000000ul) - -#define USE_LFXO // Board uses 32khz crystal for LF -//#define USE_LFRC // Board uses RC for LF - -/*---------------------------------------------------------------------------- - * Headers - *----------------------------------------------------------------------------*/ - -#include "WVariant.h" - -#ifdef __cplusplus -extern "C" -{ -#endif // __cplusplus - -#define PINS_COUNT (64) -#define NUM_DIGITAL_PINS (64) -#define NUM_ANALOG_INPUTS (8) // A6 is used for battery, A7 is analog reference -#define NUM_ANALOG_OUTPUTS (0) - -// LEDs -#define PIN_LED LED_RED -#define LED_PWR (PINS_COUNT) -#define PIN_NEOPIXEL (PINS_COUNT) -#define NEOPIXEL_NUM 0 - -#define LED_BUILTIN PIN_LED - -#define LED_RED 11 -#define LED_GREEN 13 -#define LED_BLUE 12 - -#define LED_STATE_ON 1 // State when LED is litted - -/* - * Buttons - */ -#define PIN_BUTTON1 (PINS_COUNT) - -//Digital PINs -#define D0 (2) -#define D1 (3) -#define D2 (28) -#define D3 (29) -#define D4 (4) -#define D5 (5) -#define D6 (43) -#define D7 (44) -#define D8 (45) -#define D9 (46) -#define D10 (47) - -/* - * Analog pins - */ -#define PIN_A0 (2) -#define PIN_A1 (3) -#define PIN_A2 (28) -#define PIN_A3 (29) -#define PIN_A4 (4) -#define PIN_A5 (5) -#define PIN_VBAT (31) -#define VBAT_ENABLE (14) - -static const uint8_t A0 = PIN_A0 ; -static const uint8_t A1 = PIN_A1 ; -static const uint8_t A2 = PIN_A2 ; -static const uint8_t A3 = PIN_A3 ; -static const uint8_t A4 = PIN_A4 ; -static const uint8_t A5 = PIN_A5 ; -#define ADC_RESOLUTION 12 - -// Other pins -#define PIN_NFC1 (9) -#define PIN_NFC2 (10) - -//static const uint8_t AREF = PIN_AREF; - -/* - * Serial interfaces - */ -#define PIN_SERIAL1_RX (9) -#define PIN_SERIAL1_TX (10) - -/* - * SPI Interfaces - */ -#define SPI_INTERFACES_COUNT 2 - -#define PIN_SPI_MISO (46) -#define PIN_SPI_MOSI (47) -#define PIN_SPI_SCK (45) - -static const uint8_t MOSI = PIN_SPI_MOSI ; -static const uint8_t MISO = PIN_SPI_MISO ; -static const uint8_t SCK = PIN_SPI_SCK ; - -#define PIN_SPI1_MISO (37) -#define PIN_SPI1_MOSI (39) -#define PIN_SPI1_SCK (35) - -/* - * Wire Interfaces - */ -#define WIRE_INTERFACES_COUNT 1 - -#define PIN_WIRE_SDA (4) -#define PIN_WIRE_SCL (5) - -static const uint8_t SDA = PIN_WIRE_SDA; -static const uint8_t SCL = PIN_WIRE_SCL; - -// QSPI Pins -#define PIN_QSPI_SCK (21) -#define PIN_QSPI_CS (25) -#define PIN_QSPI_IO0 (20) -#define PIN_QSPI_IO1 (24) -#define PIN_QSPI_IO2 (22) -#define PIN_QSPI_IO3 (23) - -// On-board QSPI Flash -#define EXTERNAL_FLASH_DEVICES P25Q16H -#define EXTERNAL_FLASH_USE_QSPI - -#ifdef __cplusplus -} -#endif - -/*---------------------------------------------------------------------------- - * Arduino objects - C++ only - *----------------------------------------------------------------------------*/ - -#endif From 19aeff216b72b924f6761934399f2b8d6c1d2065 Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Thu, 25 Dec 2025 19:03:56 +0100 Subject: [PATCH 4/7] Update platformio.ini --- platformio.ini | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/platformio.ini b/platformio.ini index b273ff3..2c7ffe1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -3,23 +3,19 @@ lib_deps = https://github.com/bitbank2/bb_epaper.git https://github.com/pfalcon/uzlib -[env:oeplnrf] -#build_unflags = -DUSE_LFXO -#build_flags = -DNRF52840_XXAA -DUSE_LFRC -DNRF52_S140 -DNRF52 -DNRF52840 +[env:nrf52840custom] build_flags = -DTARGET_NRF -#lib_compat_mode = soft platform = https://github.com/maxgerhardt/platform-nordicnrf52 -#platform = nordicnrf52 framework = arduino board_build.variants_dir = variants -board_build.variant = oeplnrf +board_build.variant = nrf52840custom board = xiaoblesense_adafruit -#board = oeplnrf monitor_speed = 115200 build_src_filter = +<*> - -[env:oeplesp32-s3-N16R8] + +[env:esp32-s3-N16R8] platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip framework = arduino build_flags = @@ -41,7 +37,7 @@ board_upload.maximum_ram_size = 327680 board_upload.flash_size = 16MB monitor_speed = 115200 -[env:oeplesp32-s3-N8R8] +[env:esp32-s3-N8R8] platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip framework = arduino build_flags = @@ -63,7 +59,7 @@ board_upload.maximum_ram_size = 327680 board_upload.flash_size = 8MB monitor_speed = 115200 -[env:oeplesp32-s3-N32R8] +[env:esp32-s3-N32R8] platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip framework = arduino build_flags = From e2c7af069e0043b48de58ed5f07195a78570aa7d Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Thu, 25 Dec 2025 19:04:55 +0100 Subject: [PATCH 5/7] Update name + ESP deep sleep --- src/main.cpp | 275 +++++++++++++++++++++++++++++++++++++++++++------- src/main.h | 31 +++++- src/structs.h | 7 +- 3 files changed, 275 insertions(+), 38 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index fdf5503..d4d4929 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,6 +17,22 @@ void setup() { } else { writeSerial("Git SHA: (not set)"); } + #ifdef TARGET_ESP32 + esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause(); + bool is_deep_sleep_wake = (wakeup_reason != ESP_SLEEP_WAKEUP_UNDEFINED); + if (is_deep_sleep_wake) { + woke_from_deep_sleep = true; + deep_sleep_count++; + writeSerial("=== WOKE FROM DEEP SLEEP ==="); + writeSerial("Wake-up reason: " + String(wakeup_reason)); + writeSerial("Deep sleep count: " + String(deep_sleep_count)); + minimalSetup(); + return; + } else { + woke_from_deep_sleep = false; + writeSerial("=== NORMAL BOOT ==="); + } + #endif writeSerial("Starting setup..."); full_config_init(); initio(); @@ -30,6 +46,28 @@ void setup() { void loop() { #ifdef TARGET_ESP32 + if (woke_from_deep_sleep && advertising_timeout_active) { + if (pServer && pServer->getConnectedCount() > 0) { + writeSerial("BLE connection established - switching to full mode"); + advertising_timeout_active = false; + fullSetupAfterConnection(); + woke_from_deep_sleep = false; + return; + } + uint32_t advertising_duration = millis() - advertising_start_time; + uint32_t advertising_timeout_ms = globalConfig.power_option.sleep_timeout_ms; + if (advertising_timeout_ms == 0) { + advertising_timeout_ms = 10000; + } + if (advertising_duration >= advertising_timeout_ms) { + writeSerial("BLE advertising timeout (" + String(advertising_duration) + " ms) - no connection, returning to deep sleep"); + advertising_timeout_active = false; + enterDeepSleep(); + return; + } + delay(100); + return; + } if (commandQueueTail != commandQueueHead) { writeSerial("ESP32: Processing queued command (" + String(commandQueue[commandQueueTail].len) + " bytes)"); imageDataWritten(NULL, NULL, commandQueue[commandQueueTail].data, commandQueue[commandQueueTail].len); @@ -53,9 +91,21 @@ void loop() { if (bleActive) { delay(10); } else { - if(globalConfig.power_option.sleep_timeout_ms > 0){ - delay(globalConfig.power_option.sleep_timeout_ms); - updatemsdata(); + if (!woke_from_deep_sleep && deep_sleep_count == 0 && globalConfig.power_option.power_mode == 1) { + if (!firstBootDelayInitialized) { + firstBootDelayInitialized = true; + firstBootDelayStart = millis(); + writeSerial("First boot: waiting 60s before entering deep sleep"); + } + uint32_t elapsed = millis() - firstBootDelayStart; + if (elapsed < 60000) { + delay(500); + return; + } + writeSerial("First boot delay elapsed, deep sleep permitted"); + } + if(globalConfig.power_option.deep_sleep_time_seconds > 0 && globalConfig.power_option.power_mode == 1){ + enterDeepSleep(); } else{ delay(2000); @@ -69,7 +119,6 @@ void loop() { uint32_t chunkDelay = (remainingDelay > CHECK_INTERVAL_MS) ? CHECK_INTERVAL_MS : remainingDelay; delay(chunkDelay); remainingDelay -= chunkDelay; - //sd_app_evt_wait(); } updatemsdata(); } @@ -94,7 +143,6 @@ void initio(){ digitalWrite(globalConfig.leds[0].led_2_g, HIGH); digitalWrite(globalConfig.leds[0].led_3_b, HIGH); } - if(globalConfig.system_config.pwr_pin != 0xFF){ pinMode(globalConfig.system_config.pwr_pin, OUTPUT); digitalWrite(globalConfig.system_config.pwr_pin, LOW); @@ -176,7 +224,7 @@ void initDataBuses(){ writeSerial("NOTE: nRF52840 using default I2C pins (config pins: SCL=" + String(bus->pin_1) + ", SDA=" + String(bus->pin_2) + ")"); #endif writeSerial("I2C bus " + String(i) + " initialized: SCL=pin" + String(bus->pin_1) + ", SDA=pin" + String(bus->pin_2) + ", Speed=" + String(busSpeed) + "Hz"); - scanI2CDevices(); + //scanI2CDevices(); } else { writeSerial("WARNING: I2C bus " + String(i) + " configured but not initialized (only first bus supported)"); writeSerial(" SCL=pin" + String(bus->pin_1) + ", SDA=pin" + String(bus->pin_2) + ", Speed=" + String(busSpeed) + "Hz"); @@ -521,6 +569,61 @@ void readAXP2101Data(){ writeSerial("=== AXP2101 Data Read Complete ==="); } +void powerDownAXP2101(){ + writeSerial("=== Powering Down AXP2101 PMIC Rails ==="); + Wire.beginTransmission(AXP2101_SLAVE_ADDRESS); + uint8_t error = Wire.endTransmission(); + if(error != 0){ + writeSerial("ERROR: AXP2101 not found at address 0x" + String(AXP2101_SLAVE_ADDRESS, HEX) + " (error: " + String(error) + ")"); + return; + } + // Disable DCDC1 (clear bit 0 in DC_ONOFF_DVM_CTRL) + //that powers off the full board, so better not do it + //Wire.beginTransmission(AXP2101_SLAVE_ADDRESS); + //Wire.write(AXP2101_REG_DC_ONOFF_DVM_CTRL); + //error = Wire.endTransmission(); + //uint8_t dcEnable = 0x00; + //if(error == 0){ + // Wire.requestFrom(AXP2101_SLAVE_ADDRESS, (uint8_t)1); + // if(Wire.available()){ + // dcEnable = Wire.read(); + // } + //} + //dcEnable &= ~0x01; // Clear bit 0 to disable DCDC1 + //Wire.beginTransmission(AXP2101_SLAVE_ADDRESS); + //Wire.write(AXP2101_REG_DC_ONOFF_DVM_CTRL); + //Wire.write(dcEnable); + //error = Wire.endTransmission(); + //if(error == 0){ + // writeSerial("DCDC1 disabled"); + //} else { + // writeSerial("ERROR: Failed to disable DCDC1"); + //} + + // Disable ALDO4 (clear bit 3 in LDO_ONOFF_CTRL0) + Wire.beginTransmission(AXP2101_SLAVE_ADDRESS); + Wire.write(AXP2101_REG_LDO_ONOFF_CTRL0); + error = Wire.endTransmission(); + uint8_t aldoEnable = 0x00; + if(error == 0){ + Wire.requestFrom(AXP2101_SLAVE_ADDRESS, (uint8_t)1); + if(Wire.available()){ + aldoEnable = Wire.read(); + } + } + aldoEnable &= ~0x08; // Clear bit 3 to disable ALDO4 + Wire.beginTransmission(AXP2101_SLAVE_ADDRESS); + Wire.write(AXP2101_REG_LDO_ONOFF_CTRL0); + Wire.write(aldoEnable); + error = Wire.endTransmission(); + if(error == 0){ + writeSerial("ALDO4 disabled"); + } else { + writeSerial("ERROR: Failed to disable ALDO4"); + } + writeSerial("=== AXP2101 PMIC Rails Powered Down ==="); +} + void updatemsdata(){ float batteryVoltage = readBatteryVoltage(); float chipTemperature = readChipTemperature(); @@ -579,7 +682,7 @@ if (advertisementData != nullptr) { BLEAdvertisementData freshAdvertisementData; static String savedDeviceName = ""; if (savedDeviceName.length() == 0) { - savedDeviceName = "OEPL" + getChipIdHex(); + savedDeviceName = "OD" + getChipIdHex(); } freshAdvertisementData.setName(savedDeviceName); freshAdvertisementData.setManufacturerData(manufacturerDataStr); @@ -646,7 +749,7 @@ void ble_init(){ Bluefruit.Periph.setConnectCallback(connect_callback); Bluefruit.Periph.setDisconnectCallback(disconnect_callback); writeSerial("BLE callbacks registered"); - String deviceName = "OEPL" + getChipIdHex(); + String deviceName = "OD" + getChipIdHex(); Bluefruit.setName(deviceName.c_str()); writeSerial("Device name set to: " + deviceName); writeSerial("Configuring power management..."); @@ -665,8 +768,14 @@ void ble_init(){ Bluefruit.Advertising.start(0); #endif #ifdef TARGET_ESP32 + ble_init_esp32(true); // Update manufacturer data for full setup + #endif +} + +#ifdef TARGET_ESP32 +void ble_init_esp32(bool update_manufacturer_data) { writeSerial("=== Initializing ESP32 BLE ==="); - String deviceName = "OEPL" + getChipIdHex(); + String deviceName = "OD" + getChipIdHex(); writeSerial("Device name will be: " + deviceName); BLEDevice::init(deviceName.c_str()); writeSerial("Setting BLE MTU to 512..."); @@ -708,11 +817,11 @@ void ble_init(){ } pAdvertising->addServiceUUID(serviceUUID); writeSerial("Service UUID added to advertising"); - // Use global advertisementData object (not a local variable) advertisementData->setName(deviceName); writeSerial("Device name added to advertising"); - // Add manufacturer data using updatemsdata function - updatemsdata(); + if (update_manufacturer_data) { + updatemsdata(); + } pAdvertising->setAdvertisementData(*advertisementData); pAdvertising->setScanResponse(false); pAdvertising->setMinPreferred(0x0006); @@ -724,19 +833,92 @@ void ble_init(){ writeSerial("=== BLE advertising started successfully ==="); writeSerial("Device ready: " + deviceName); writeSerial("Waiting for BLE connections..."); -#endif } +void minimalSetup() { + writeSerial("=== Minimal Setup (Deep Sleep Wake) ==="); + full_config_init(); + initio(); + ble_init_esp32(true); // Update manufacturer data + writeSerial("=== BLE advertising started (minimal mode) ==="); + writeSerial("Advertising for 10 seconds, waiting for connection..."); + advertising_timeout_active = true; + advertising_start_time = millis(); +} + +void fullSetupAfterConnection() { + writeSerial("=== Full Setup After Connection ==="); + memset(&bbep, 0, sizeof(BBEPDISP)); + int panelType = mapEpd(globalConfig.displays[0].panel_ic_type); + writeSerial("Panel type: " + String(panelType)); + bbepSetPanelType(&bbep, panelType); + writeSerial("=== Full setup completed ==="); +} + +void enterDeepSleep() { + // Only enter deep sleep if configured for battery power (power_mode == 1) + if (globalConfig.power_option.power_mode != 1) { + writeSerial("Skipping deep sleep - not battery powered (power_mode: " + String(globalConfig.power_option.power_mode) + ")"); + delay(2000); + return; + } + + if (globalConfig.power_option.deep_sleep_time_seconds == 0) { + writeSerial("Skipping deep sleep - deep_sleep_time_seconds is 0"); + delay(2000); + return; + } + + writeSerial("Entering deep sleep for " + String(globalConfig.power_option.deep_sleep_time_seconds) + " seconds"); + //pwrmgm(false); + // Set flag for next wake-up + woke_from_deep_sleep = true; // Will be true on next boot + + // Stop BLE advertising + if (pServer != nullptr) { + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + if (pAdvertising != nullptr) { + pAdvertising->stop(); + writeSerial("BLE advertising stopped"); + } + } + + // Deinitialize BLE + BLEDevice::deinit(true); + writeSerial("BLE deinitialized"); + + // Configure deep sleep + uint64_t sleep_timeout_us = (uint64_t)globalConfig.power_option.deep_sleep_time_seconds * 1000000ULL; + esp_sleep_enable_timer_wakeup(sleep_timeout_us); + + // Note: Power domain configuration is optional and varies by ESP32 variant + // Timer wake-up works without explicit power domain configuration + // For optimal power savings, power domains can be configured per variant if needed + + writeSerial("Entering deep sleep..."); + delay(100); // Brief delay to ensure serial output is sent + + // Enter deep sleep + esp_deep_sleep_start(); + // Code will not reach here - device will restart after wake-up +} +#endif + void pwrmgm(bool onoff){ if(globalConfig.display_count == 0){ writeSerial("No display configured"); return; } + uint8_t axp2101_bus_id = 0xFF; + bool axp2101_found = false; + for(uint8_t i = 0; i < globalConfig.sensor_count; i++){ + if(globalConfig.sensors[i].sensor_type == 0x0003){ + axp2101_bus_id = globalConfig.sensors[i].bus_id; + axp2101_found = true; + break; + } + } if(globalConfig.system_config.pwr_pin != 0xFF){ - #ifdef TARGET_ESP32 - digitalWrite(globalConfig.system_config.pwr_pin, HIGH); - #endif - #ifdef TARGET_NRF if(onoff){ digitalWrite(globalConfig.system_config.pwr_pin, HIGH); pinMode(globalConfig.displays[0].reset_pin, OUTPUT); @@ -756,11 +938,20 @@ void pwrmgm(bool onoff){ pinMode(globalConfig.displays[0].data_pin, INPUT); digitalWrite(globalConfig.system_config.pwr_pin, LOW); } - #endif } - else{ - writeSerial("Power pin not set"); - } + else if(axp2101_found){ + if(onoff){ + writeSerial("Powering up AXP2101 PMIC..."); + initAXP2101(axp2101_bus_id); + } + else{ + writeSerial("Powering down AXP2101 PMIC..."); + powerDownAXP2101(); + } + } + else{ + writeSerial("Power pin not set"); + } } void writeSerial(String message, bool newLine){ @@ -768,7 +959,6 @@ void writeSerial(String message, bool newLine){ if (newLine == true) Serial.println(message); else Serial.print(message); #endif - // When USB is disabled, writeSerial does nothing (no-op) to save power } void xiaoinit(){ @@ -786,18 +976,13 @@ void xiaoinit(){ bool powerDownExternalFlash(uint8_t mosiPin, uint8_t misoPin, uint8_t sckPin, uint8_t csPin, uint8_t wpPin, uint8_t holdPin) { #ifdef TARGET_NRF - // Software SPI transfer function (bit-banging) auto spiTransfer = [&](uint8_t data) -> uint8_t { uint8_t result = 0; for (int i = 7; i >= 0; i--) { - // Set MOSI to current bit digitalWrite(mosiPin, (data >> i) & 1); - // Clock low digitalWrite(sckPin, LOW); delayMicroseconds(1); - // Read MISO result |= (digitalRead(misoPin) << i); - // Clock high digitalWrite(sckPin, HIGH); delayMicroseconds(1); } @@ -825,7 +1010,6 @@ bool powerDownExternalFlash(uint8_t mosiPin, uint8_t misoPin, uint8_t sckPin, ui digitalWrite(csPin, HIGH); delay(10); // Wait for flash to wake up (typically 3-35us, using 10ms for safety) writeSerial("Wake-up command sent, waiting 10ms..."); - // Read JEDEC ID before power-down (for verification) writeSerial("Reading JEDEC ID before power-down..."); digitalWrite(csPin, LOW); spiTransfer(0x9F); // JEDEC ID command @@ -978,7 +1162,7 @@ void initDisplay(){ bbepWakeUp(&bbep); bbepSendCMDSequence(&bbep, bbep.pInitFull); String chipId = getChipIdHex(); - String infoText = "openepaperlink.org\nName: OEPL" + chipId + "\nFW: " + String(getFirmwareMajor()) + "." + String(getFirmwareMinor()) + "\nEpaper driver by\nLarry Bank\ngithub.com/bitbank2"; + String infoText = "opendisplay.org\nName: OD" + chipId + "\nFW: " + String(getFirmwareMajor()) + "." + String(getFirmwareMinor()) + "\nFirmware by\nJonas Niesner"; if (globalConfig.displays[0].transmission_modes & TRANSMISSION_MODE_CLEAR_ON_BOOT) writeTextAndFill(""); else writeTextAndFill(infoText.c_str()); bbepRefresh(&bbep, REFRESH_FULL); @@ -1631,7 +1815,8 @@ void printConfigSummary(){ writeSerial("Battery Capacity: " + String(globalConfig.power_option.battery_capacity_mah[0]) + " " + String(globalConfig.power_option.battery_capacity_mah[1]) + " " + String(globalConfig.power_option.battery_capacity_mah[2]) + " mAh"); - writeSerial("Sleep Timeout: " + String(globalConfig.power_option.sleep_timeout_ms) + " ms"); + writeSerial("Awake Timeout: " + String(globalConfig.power_option.sleep_timeout_ms) + " ms"); + writeSerial("Deep Sleep Time: " + String(globalConfig.power_option.deep_sleep_time_seconds) + " seconds"); writeSerial("TX Power: " + String(globalConfig.power_option.tx_power)); writeSerial("Sleep Flags: 0x" + String(globalConfig.power_option.sleep_flags, HEX)); writeSerial("Battery Sense Pin: " + String(globalConfig.power_option.battery_sense_pin)); @@ -1650,7 +1835,7 @@ void printConfigSummary(){ writeSerial(" Panel IC Type: 0x" + String(globalConfig.displays[i].panel_ic_type, HEX)); writeSerial(" Resolution: " + String(globalConfig.displays[i].pixel_width) + "x" + String(globalConfig.displays[i].pixel_height)); writeSerial(" Size: " + String(globalConfig.displays[i].active_width_mm) + "x" + String(globalConfig.displays[i].active_height_mm) + " mm"); - writeSerial(" OEPL Tag Type: 0x" + String(globalConfig.displays[i].oepl_tagtype, HEX)); + writeSerial(" Tag Type: 0x" + String(globalConfig.displays[i].tag_type, HEX)); writeSerial(" Rotation: " + String(globalConfig.displays[i].rotation * 90) + " degrees"); writeSerial(" Reset Pin: " + String(globalConfig.displays[i].reset_pin)); writeSerial(" Busy Pin: " + String(globalConfig.displays[i].busy_pin)); @@ -1845,9 +2030,13 @@ void handleDirectWriteStart(uint8_t* data, uint16_t len) { writeSerial("Expected total bytes: " + String(directWriteTotalBytes) + (directWriteBitplanes ? " per plane" : "")); directWriteActive = true; directWriteBytesWritten = 0; + writeSerial("a"); pwrmgm(true); + writeSerial("b"); bbepInitIO(&bbep, globalConfig.displays[0].dc_pin, globalConfig.displays[0].reset_pin, globalConfig.displays[0].busy_pin, globalConfig.displays[0].cs_pin, globalConfig.displays[0].data_pin, globalConfig.displays[0].clk_pin, 8000000); + writeSerial("c"); bbepWakeUp(&bbep); + writeSerial("d"); bbepSendCMDSequence(&bbep, bbep.pInitFull);// important for some displays bbepSetAddrWindow(&bbep, 0, 0, globalConfig.displays[0].pixel_width, globalConfig.displays[0].pixel_height); bbepStartWrite(&bbep, directWriteBitplanes ? PLANE_0 : getplane()); @@ -2359,7 +2548,7 @@ void decompressDirectWriteData() { } } -void handleDirectWriteEnd() { +void handleDirectWriteEnd(uint8_t* data, uint16_t len) { if (!directWriteActive) { writeSerial("WARNING: Direct write end called but mode not active"); return; @@ -2377,7 +2566,24 @@ void handleDirectWriteEnd() { if (directWriteCompressed) { writeSerial("Compressed bytes received: " + String(directWriteCompressedReceived)); } - bbepRefresh(&bbep, REFRESH_FULL); + int refreshMode = REFRESH_FULL; + if (data != nullptr && len >= 1) { + uint8_t refreshFlag = data[0]; + if (refreshFlag == 1) { + refreshMode = REFRESH_FAST; + writeSerial("Using fast/partial refresh mode (requested)"); + } else if (refreshFlag == 0) { + refreshMode = REFRESH_FULL; + writeSerial("Full refresh explicitly requested"); + } else { + refreshMode = REFRESH_FULL; + writeSerial("Unknown refresh flag value (" + String(refreshFlag) + "), using full refresh"); + } + } else { + writeSerial("No refresh mode specified, using full refresh (backward compatible)"); + } + + bbepRefresh(&bbep, refreshMode); waitforrefresh(60); pwrmgm(false); directWriteActive = false; @@ -2390,6 +2596,7 @@ void handleDirectWriteEnd() { directWriteWidth = 0; directWriteHeight = 0; directWriteTotalBytes = 0; + directWriteRefreshMode = 0; // Reset refresh mode flag uint8_t ackResponse[] = {0x00, 0x72}; sendResponse(ackResponse, sizeof(ackResponse)); writeSerial("Direct write completed and display refreshed"); @@ -2435,7 +2642,7 @@ void imageDataWritten(BLEConnHandle conn_hdl, BLECharPtr chr, uint8_t* data, uin break; case 0x0072: // Direct Write End command writeSerial("=== DIRECT WRITE END COMMAND (0x0072) ==="); - handleDirectWriteEnd(); + handleDirectWriteEnd(data + 2, len - 2); // Pass data after command bytes (2 bytes for command) break; default: writeSerial("ERROR: Unknown command: 0x" + String(command, HEX)); diff --git a/src/main.h b/src/main.h index 0b38ffa..7b47848 100644 --- a/src/main.h +++ b/src/main.h @@ -76,12 +76,17 @@ extern "C" { #include #include #include +#include "esp_sleep.h" extern BLEServer* pServer; extern BLEService* pService; extern BLECharacteristic* pTxCharacteristic; extern BLECharacteristic* pRxCharacteristic; extern BLEAdvertisementData* advertisementData; // Pointer to global advertisementData object + +// RTC memory variables for deep sleep state tracking (declared in main.cpp) +extern bool advertising_timeout_active; +extern uint32_t advertising_start_time; #endif BBEPDISP bbep; @@ -125,6 +130,7 @@ uint32_t directWriteDecompressedTotal = 0; // Expected decompressed size uint16_t directWriteWidth = 0; // Display width in pixels uint16_t directWriteHeight = 0; // Display height in pixels uint32_t directWriteTotalBytes = 0; // Total bytes expected per plane (for bitplanes) or total (for others) +uint8_t directWriteRefreshMode = 0; // 0 = FULL (default), 1 = FAST/PARTIAL (if supported) // Direct write compressed mode: use same buffer as regular image upload uint32_t directWriteCompressedSize = 0; // Total compressed size expected @@ -139,6 +145,14 @@ void writeSerial(String message, bool newLine = true); void connect_callback(uint16_t conn_handle); void disconnect_callback(uint16_t conn_handle, uint8_t reason); void initDisplay(); +#ifdef TARGET_ESP32 +void minimalSetup(); +void fullSetupAfterConnection(); +void enterDeepSleep(); +void ble_init_esp32(bool update_manufacturer_data = true); +extern bool advertising_timeout_active; +extern uint32_t advertising_start_time; +#endif String getChipIdHex(); // Platform-specific type aliases for BLE callback @@ -158,6 +172,7 @@ void scanI2CDevices(); void initSensors(); void initAXP2101(uint8_t busId); void readAXP2101Data(); +void powerDownAXP2101(); void updatemsdata(); void ble_init(); void full_config_init(); @@ -172,7 +187,7 @@ void handleWriteConfigChunk(uint8_t* data, uint16_t len); void handleFirmwareVersion(); void handleDirectWriteStart(uint8_t* data, uint16_t len); void handleDirectWriteData(uint8_t* data, uint16_t len); -void handleDirectWriteEnd(); +void handleDirectWriteEnd(uint8_t* data = nullptr, uint16_t len = 0); void handleDirectWriteCompressedData(uint8_t* data, uint16_t len); void printConfigSummary(); void reboot(); @@ -208,6 +223,20 @@ chunked_write_state_t chunkedWriteState = {false, 0, 0, {0}, 0, 0}; struct GlobalConfig globalConfig = {0}; uint8_t configReadResponseBuffer[128]; +#ifdef TARGET_ESP32 +// RTC memory variables for deep sleep state tracking +RTC_DATA_ATTR bool woke_from_deep_sleep = false; +RTC_DATA_ATTR uint32_t deep_sleep_count = 0; + +// Advertising timeout state variables +bool advertising_timeout_active = false; +uint32_t advertising_start_time = 0; + +// First-boot holdoff before allowing deep sleep +static bool firstBootDelayInitialized = false; +static uint32_t firstBootDelayStart = 0; +#endif + #define AXP2101_SLAVE_ADDRESS 0x34 #define AXP2101_REG_POWER_STATUS 0x00 #define AXP2101_REG_POWER_ON_STATUS 0x01 diff --git a/src/structs.h b/src/structs.h index dfa96de..e2a4edc 100644 --- a/src/structs.h +++ b/src/structs.h @@ -39,7 +39,7 @@ struct ManufacturerData { struct PowerOption { uint8_t power_mode; // Power source type enum uint8_t battery_capacity_mah[3]; // Battery capacity in mAh (3 bytes) - uint16_t sleep_timeout_ms; // Nominal sleep time in milliseconds + uint16_t sleep_timeout_ms; // Nominal awake time in milliseconds (advertising timeout) uint8_t tx_power; // Transmit power setting uint8_t sleep_flags; // Sleep-related flags (bitfield) uint8_t battery_sense_pin; // Pin used to measure battery voltage (0xFF if none) @@ -48,7 +48,8 @@ struct PowerOption { uint8_t capacity_estimator; // Battery chemistry estimator enum uint16_t voltage_scaling_factor; // Voltage scaling / divider factor uint32_t deep_sleep_current_ua; // Deep sleep current in microamperes - uint8_t reserved[12]; // Reserved bytes for future use + uint16_t deep_sleep_time_seconds; // Deep sleep duration in seconds (0 if not used) + uint8_t reserved[10]; // Reserved bytes for future use } __attribute__((packed)); // 0x20: display (repeatable, max 4 instances) @@ -60,7 +61,7 @@ struct DisplayConfig { uint16_t pixel_height; // Pixel height of panel uint16_t active_width_mm; // Active width of panel in millimeters uint16_t active_height_mm; // Active height of panel in millimeters - uint16_t oepl_tagtype; // Legacy OEPL tag type (optional) + uint16_t tag_type; // Legacy tag type (optional) uint8_t rotation; // Physical rotation in degrees (enum) uint8_t reset_pin; // Pin number for panel reset (0xFF if none) uint8_t busy_pin; // Pin number to read panel busy status (0xFF if none) From 84bee79ffe51a009c73bf4091c026b3362ebd2b5 Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Thu, 25 Dec 2025 19:10:38 +0100 Subject: [PATCH 6/7] Update main.yaml --- .github/workflows/main.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 39ea2e8..d2de215 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -22,8 +22,8 @@ jobs: - name: Build NRF firmware run: | - platformio run --environment oeplnrf + platformio run --environment nrf52840custom - name: Build ESP32 firmware run: | - platformio run --environment oeplesp32-s3-N16R8 + platformio run --environment esp32-s3-N16R8 From d3b8f0a48e64b2c7169351e30d4dedc0e6b852ea Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Thu, 25 Dec 2025 19:12:43 +0100 Subject: [PATCH 7/7] Update release.yml --- .github/workflows/release.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0a2501e..afc8557 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,9 +27,9 @@ jobs: - name: Build & Package NRF Firmware run: | export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA" - pio run --environment oeplnrf + pio run --environment nrf52840custom mkdir firmware_output - cp .pio/build/oeplnrf/firmware.hex firmware_output/NRF52840.hex + cp .pio/build/nrf52840custom/firmware.hex firmware_output/NRF52840.hex TAG_NAME=${{ github.event.release.tag_name }} adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application firmware_output/NRF52840.hex firmware_output/NRF52840.zip python uf2conv.py firmware_output/NRF52840.hex --family 0xADA52840 --output firmware_output/NRF52840.uf2 @@ -37,7 +37,7 @@ jobs: - name: Build & Package esp32-s3-N16R8 Firmware run: | export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA" - pioenv=oeplesp32-s3-N16R8 + pioenv=esp32-s3-N16R8 pio run --environment ${pioenv} mkdir ${pioenv} cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin ${pioenv}/boot_app0.bin @@ -53,7 +53,7 @@ jobs: - name: Build & Package esp32-s3-N8R8 Firmware run: | export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA" - pioenv=oeplesp32-s3-N8R8 + pioenv=esp32-s3-N8R8 pio run --environment ${pioenv} mkdir ${pioenv} cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin ${pioenv}/boot_app0.bin @@ -69,7 +69,7 @@ jobs: - name: Build & Package esp32-s3-N32R8 Firmware run: | export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA" - pioenv=oeplesp32-s3-N32R8 + pioenv=esp32-s3-N32R8 pio run --environment ${pioenv} mkdir ${pioenv} cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin ${pioenv}/boot_app0.bin