diff --git a/boards/tbeam-s3-core.json b/boards/tbeam-s3-core.json new file mode 100644 index 000000000..82d858df8 --- /dev/null +++ b/boards/tbeam-s3-core.json @@ -0,0 +1,48 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32s3_out.ld" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DLILYGO_TBEAM_S3_CORE", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_USB_DFU_ON_BOOT=1", + "-DARDUINO_USB_MSC_ON_BOOT=1", + "-DARDUINO_USB_MODE=1", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dio", + "hwids": [ + [ + "0X303A", + "0x1001" + ] + ], + "mcu": "esp32s3", + "variant": "tbeam-s3-core" + }, + "connectivity": [ + "wifi" + ], + "debug": { + "openocd_target": "esp32s3.cfg" + }, + "frameworks": [ + "arduino" + ], + "name": "LilyGo TBeam-S3-Core", + "upload": { + "flash_size": "8MB", + "maximum_ram_size": 327680, + "maximum_size": 8388608, + "require_upload_port": true, + "speed": 921600 + }, + "url": "http://www.lilygo.cn/", + "vendor": "LilyGo" +} diff --git a/platformio.ini b/platformio.ini index 20b319cdb..e1dbe47ef 100644 --- a/platformio.ini +++ b/platformio.ini @@ -3,6 +3,7 @@ [platformio] default_envs = tbeam +;default_envs = tbeam-s3-core ;default_envs = tbeam0.7 ;default_envs = heltec-v1 ;default_envs = heltec-v2.0 @@ -13,7 +14,7 @@ default_envs = tbeam ;default_envs = tlora-v2 ;default_envs = tlora-v2-1-1.6 ;default_envs = lora-relay-v1 # nrf board -;default_envs = t-echo +; default_envs = t-echo ;default_envs = nrf52840dk-geeksville ;default_envs = native # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here ;default_envs = nano-g1 @@ -110,8 +111,8 @@ lib_deps = h2zero/NimBLE-Arduino@1.4.0 arduino-libraries/NTPClient@^3.1.0 lorol/LittleFS_esp32@^1.0.6 - https://github.com/meshtastic/AXP202X_Library.git#8404abb6d4b486748636bc6ad72d2a47baaf5460 - + https://github.com/lewisxhe/XPowersLib.git + lib_ignore = segger_rtt ESP32 BLE Arduino @@ -203,3 +204,54 @@ lib_deps = https://github.com/kokke/tiny-AES-c.git lib_ignore = mathertel/OneButton@^2.0.3 + + + +[esp32s3_base] +extends = arduino_base +platform = espressif32 +build_src_filter = + ${arduino_base.build_src_filter} - - - +upload_speed = 961200 +monitor_speed = 115200 +debug_init_break = tbreak setup + +# Remove -DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL for low level BLE logging. +# See library directory for BLE logging possible values: .pio/libdeps/tbeam/NimBLE-Arduino/src/log_common/log_common.h +# This overrides the BLE logging default of LOG_LEVEL_INFO (1) from: .pio/libdeps/tbeam/NimBLE-Arduino/src/esp_nimble_cfg.h +build_flags = + ${arduino_base.build_flags} + -Wall + -Wextra + -Isrc/platform/esp32 + -std=c++11 + -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG + -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG + -DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL + -DAXP_DEBUG_PORT=Serial + -DCONFIG_BT_NIMBLE_ENABLED + -DCONFIG_NIMBLE_CPP_LOG_LEVEL=2 + -DCONFIG_BT_NIMBLE_MAX_CCCDS=20 + +lib_deps = + ${arduino_base.lib_deps} + ${networking_base.lib_deps} + ${environmental_base.lib_deps} + ; https://github.com/meshtastic/esp32_https_server.git + ; PR has been submitted and can be deleted after merging ,https://github.com/meshtastic/esp32_https_server/pull/1 + https://github.com/lewisxhe/esp32_https_server.git + h2zero/NimBLE-Arduino@1.4.0 + arduino-libraries/NTPClient@^3.1.0 + https://github.com/lewisxhe/XPowersLib.git + + +lib_ignore = + segger_rtt + ESP32 BLE Arduino +platform_packages = + framework-arduinoespressif32@ 3.20004.220825 + +; customize the partition table +; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables +board_build.partitions = partition-table.csv + diff --git a/src/ButtonThread.h b/src/ButtonThread.h index ed9fc613c..af0a8e1ec 100644 --- a/src/ButtonThread.h +++ b/src/ButtonThread.h @@ -128,8 +128,8 @@ class ButtonThread : public concurrency::OSThread #endif // If user button is held down for 5 seconds, shutdown the device. if ((millis() - longPressTime > 5 * 1000) && (longPressTime > 0)) { -#ifdef HAS_AXP192 - if (axp192_found == true) { +#ifdef HAS_PMU + if (pmu_found == true) { setLed(false); power->shutdown(); } diff --git a/src/FSCommon.h b/src/FSCommon.h index 5def77ecb..4336e88dd 100644 --- a/src/FSCommon.h +++ b/src/FSCommon.h @@ -23,9 +23,16 @@ #endif #if defined(ARCH_ESP32) +#if CONFIG_IDF_TARGET_ESP32S3 +#include //esp32s3 uses the framework's built-in LittleFS +#define FSCom LittleFS +#else // ESP32 version #include "LITTLEFS.h" #define FSCom LITTLEFS +#endif + + #define FSBegin() FSCom.begin(true) #define FILE_O_WRITE "w" #define FILE_O_READ "r" diff --git a/src/Power.cpp b/src/Power.cpp index 0254819e9..2c3fdd57b 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -7,10 +7,11 @@ #include "utils.h" #include "buzz/buzz.h" -#ifdef HAS_AXP192 -#include "axp20x.h" - -AXP20X_Class axp; +#ifdef HAS_PMU +#include "XPowersLibInterface.hpp" +#include "XPowersAXP2101.tpp" +#include "XPowersAXP192.tpp" +XPowersLibInterface *PMU = NULL; #else // Copy of the base class defined in axp20x.h. // I'd rather not inlude axp20x.h as it brings Wire dependency. @@ -20,20 +21,20 @@ class HasBatteryLevel /** * Battery state of charge, from 0 to 100 or -1 for unknown */ - virtual int getBattPercentage() { return -1; } + virtual int getBatteryPercent() { return -1; } /** * The raw voltage of the battery or NAN if unknown */ - virtual float getBattVoltage() { return NAN; } + virtual uint16_t getBattVoltage() { return 0; } /** * return true if there is a battery installed in this unit */ virtual bool isBatteryConnect() { return false; } - virtual bool isVBUSPlug() { return false; } - virtual bool isChargeing() { return false; } + virtual bool isVbusIn() { return false; } + virtual bool isCharging() { return false; } }; #endif @@ -75,7 +76,7 @@ class AnalogBatteryLevel : public HasBatteryLevel * * FIXME - use a lipo lookup table, the current % full is super wrong */ - virtual int getBattPercentage() override + virtual int getBatteryPercent() override { float v = getBattVoltage(); @@ -94,7 +95,7 @@ class AnalogBatteryLevel : public HasBatteryLevel /** * The raw voltage of the batteryin millivolts or NAN if unknown */ - virtual float getBattVoltage() override + virtual uint16_t getBattVoltage() override { #ifndef ADC_MULTIPLIER @@ -130,27 +131,27 @@ class AnalogBatteryLevel : public HasBatteryLevel #endif // DEBUG_MSG("battery gpio %d raw val=%u scaled=%u\n", BATTERY_PIN, raw, (uint32_t)(scaled)); last_read_value = scaled; - return scaled; + return scaled * 1000; } else { return last_read_value; } #else - return NAN; + return 0; #endif } /** * return true if there is a battery installed in this unit */ - virtual bool isBatteryConnect() override { return getBattPercentage() != -1; } + virtual bool isBatteryConnect() override { return getBatteryPercent() != -1; } /// If we see a battery voltage higher than physics allows - assume charger is pumping /// in power - virtual bool isVBUSPlug() override { return getBattVoltage() > chargingVolt; } + virtual bool isVbusIn() override { return getBattVoltage() > chargingVolt; } /// Assume charging if we have a battery and external power is connected. /// we can't be smart enough to say 'full'? - virtual bool isChargeing() override { return isBatteryConnect() && isVBUSPlug(); } + virtual bool isCharging() override { return isBatteryConnect() && isVbusIn(); } private: /// If we see a battery voltage higher than physics allows - assume charger is pumping @@ -219,7 +220,7 @@ bool Power::analogInit() bool Power::setup() { - bool found = axp192Init(); + bool found = axpChipInit(); if (!found) { found = analogInit(); @@ -232,10 +233,14 @@ bool Power::setup() void Power::shutdown() { -#ifdef HAS_AXP192 + + +#ifdef HAS_PMU DEBUG_MSG("Shutting down\n"); - axp.setChgLEDMode(AXP20X_LED_OFF); - axp.shutdown(); + if(PMU){ + PMU->setChargingLedMode(XPOWERS_CHG_LED_OFF); + PMU->shutdown(); + } #elif defined(ARCH_NRF52) playBeep(); ledOff(PIN_LED1); @@ -256,8 +261,8 @@ void Power::readPowerStatus() if (hasBattery) { batteryVoltageMv = batteryLevel->getBattVoltage(); // If the AXP192 returns a valid battery percentage, use it - if (batteryLevel->getBattPercentage() >= 0) { - batteryChargePercent = batteryLevel->getBattPercentage(); + if (batteryLevel->getBatteryPercent() >= 0) { + batteryChargePercent = batteryLevel->getBatteryPercent(); } else { // If the AXP192 returns a percentage less than 0, the feature is either not supported or there is an error // In that case, we compute an estimate of the charge percent based on maximum and minimum voltages defined in @@ -270,8 +275,8 @@ void Power::readPowerStatus() // Notify any status instances that are observing us const PowerStatus powerStatus2 = - PowerStatus(hasBattery ? OptTrue : OptFalse, batteryLevel->isVBUSPlug() ? OptTrue : OptFalse, - batteryLevel->isChargeing() ? OptTrue : OptFalse, batteryVoltageMv, batteryChargePercent); + PowerStatus(hasBattery ? OptTrue : OptFalse, batteryLevel->isVbusIn() ? OptTrue : OptFalse, + batteryLevel->isCharging() ? OptTrue : OptFalse, batteryVoltageMv, batteryChargePercent); DEBUG_MSG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d\n", powerStatus2.getHasUSB(), powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent()); newStatus.notifyObservers(&powerStatus2); @@ -304,41 +309,46 @@ int32_t Power::runOnce() { readPowerStatus(); -#ifdef HAS_AXP192 +#ifdef HAS_PMU // WE no longer use the IRQ line to wake the CPU (due to false wakes from sleep), but we do poll // the IRQ status by reading the registers over I2C - axp.readIRQ(); + if(PMU){ - if (axp.isVbusRemoveIRQ()) { - DEBUG_MSG("USB unplugged\n"); - powerFSM.trigger(EVENT_POWER_DISCONNECTED); - } - if (axp.isVbusPlugInIRQ()) { - DEBUG_MSG("USB plugged In\n"); - powerFSM.trigger(EVENT_POWER_CONNECTED); - } - /* - Other things we could check if we cared... + PMU->getIrqStatus(); - if (axp.isChargingIRQ()) { - DEBUG_MSG("Battery start charging\n"); + if(PMU->isVbusRemoveIrq()){ + DEBUG_MSG("USB unplugged\n"); + powerFSM.trigger(EVENT_POWER_DISCONNECTED); + } + + if (PMU->isVbusInsertIrq()) { + DEBUG_MSG("USB plugged In\n"); + powerFSM.trigger(EVENT_POWER_CONNECTED); + } + + /* + Other things we could check if we cared... + + if (PMU->isBatChagerStartIrq()) { + DEBUG_MSG("Battery start charging\n"); + } + if (PMU->isBatChagerDoneIrq()) { + DEBUG_MSG("Battery fully charged\n"); + } + if (PMU->isBatInsertIrq()) { + DEBUG_MSG("Battery inserted\n"); + } + if (PMU->isBatRemoveIrq()) { + DEBUG_MSG("Battery removed\n"); + } + if (PMU->isPekeyShortPressIrq()) { + DEBUG_MSG("PEK short button press\n"); + } + */ + + PMU->clearIrqStatus(); } - if (axp.isChargingDoneIRQ()) { - DEBUG_MSG("Battery fully charged\n"); - } - if (axp.isBattPlugInIRQ()) { - DEBUG_MSG("Battery inserted\n"); - } - if (axp.isBattRemoveIRQ()) { - DEBUG_MSG("Battery removed\n"); - } - if (axp.isPEKShortPressIRQ()) { - DEBUG_MSG("PEK short button press\n"); - } - */ - axp.clearIRQ(); #endif - // Only read once every 20 seconds once the power status for the app has been initialized return (statusHandler && statusHandler->isInitialized()) ? (1000 * 20) : RUN_SAME; } @@ -351,77 +361,204 @@ int32_t Power::runOnce() share the same i2c bus, instead use ssd1306 sleep mode DCDC2 -> unused DCDC3 0.7-3.5V @ 700mA max -> ESP32 (keep this on!) LDO1 30mA -> charges GPS backup battery // charges the tiny J13 battery by the GPS to power the GPS ram (for a couple of days), can not be turned off LDO2 200mA -> LORA LDO3 200mA -> GPS + * */ -bool Power::axp192Init() +bool Power::axpChipInit() { -#ifdef HAS_AXP192 - if (axp192_found) { - if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) { - batteryLevel = &axp; - DEBUG_MSG("AXP192 Begin PASS\n"); +#ifdef HAS_PMU - // axp.setChgLEDMode(LED_BLINK_4HZ); - DEBUG_MSG("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("----------------------------------------\n"); - - axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); // LORA radio - // axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); // GPS main power - now turned on in setGpsPower - axp.setPowerOutPut(AXP192_DCDC2, AXP202_ON); - axp.setPowerOutPut(AXP192_EXTEN, AXP202_ON); - axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON); - axp.setDCDC1Voltage(3300); // for the OLED power - - DEBUG_MSG("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE"); - - // this was the previous 'Unset' default. - axp.setChargeControlCur(AXP1XX_CHARGE_CUR_450MA); - -#if 0 - - // Not connected - //val = 0xfc; - //axp._writeByte(AXP202_VHTF_CHGSET, 1, &val); // Set temperature protection - - //not used - //val = 0x46; - //axp._writeByte(AXP202_OFF_CTL, 1, &val); // enable bat detection -#endif - axp.debugCharging(); - -#ifdef PMU_IRQ - pinMode(PMU_IRQ, INPUT); - attachInterrupt( - PMU_IRQ, [] { pmu_irq = true; }, FALLING); - - axp.adc1Enable(AXP202_BATT_CUR_ADC1, 1); - // we do not look for AXP202_CHARGING_FINISHED_IRQ & AXP202_CHARGING_IRQ because it occurs repeatedly while there is - // no battery also it could cause inadvertent waking from light sleep just because the battery filled - // we don't look for AXP202_BATT_REMOVED_IRQ because it occurs repeatedly while no battery installed - // we don't look at AXP202_VBUS_REMOVED_IRQ because we don't have anything hooked to vbus - axp.enableIRQ(AXP202_BATT_CONNECT_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_PEK_SHORTPRESS_IRQ, 1); - - axp.clearIRQ(); -#endif - readPowerStatus(); + if (!PMU) { + PMU = new XPowersAXP2101(Wire, I2C_SDA, I2C_SCL); + if (!PMU->init()) { + DEBUG_MSG("Warning: Failed to find AXP2101 power management\n"); + delete PMU; + PMU = NULL; } else { - DEBUG_MSG("AXP192 Begin FAIL\n"); + DEBUG_MSG("AXP2101 PMU init succeeded, using AXP2101 PMU\n"); } - } else { - DEBUG_MSG("AXP192 not found\n"); } - return axp192_found; + if (!PMU) { + PMU = new XPowersAXP192(Wire, I2C_SDA, I2C_SCL); + if (!PMU->init()) { + DEBUG_MSG("Warning: Failed to find AXP192 power management\n"); + delete PMU; + PMU = NULL; + } else { + DEBUG_MSG("AXP192 PMU init succeeded, using AXP192 PMU\n"); + } + } + + if (!PMU) { + /* + * In XPowersLib, if the XPowersAXPxxx object is released, Wire.end() will be called at the same time. + * In order not to affect other devices, if the initialization of the PMU fails, Wire needs to be re-initialized once, + * if there are multiple devices sharing the bus. + * * */ + Wire.begin(I2C_SDA, I2C_SCL); + return false; + } + + batteryLevel = PMU; + + if (PMU->getChipModel() == XPOWERS_AXP192) { + + // lora radio power channel + PMU->setPowerChannelVoltage(XPOWERS_LDO2, 3300); + PMU->enablePowerOutput(XPOWERS_LDO2); + + + // oled module power channel, + // disable it will cause abnormal communication between boot and AXP power supply, + // do not turn it off + PMU->setPowerChannelVoltage(XPOWERS_DCDC1, 3300); + // enable oled power + PMU->enablePowerOutput(XPOWERS_DCDC1); + + + // gnss module power channel - now turned on in setGpsPower + PMU->setPowerChannelVoltage(XPOWERS_LDO3, 3300); + // PMU->enablePowerOutput(XPOWERS_LDO3); + + + //protected oled power source + PMU->setProtectedChannel(XPOWERS_DCDC1); + //protected esp32 power source + PMU->setProtectedChannel(XPOWERS_DCDC3); + + //disable not use channel + PMU->disablePowerOutput(XPOWERS_DCDC2); + + //disable all axp chip interrupt + PMU->disableIRQ(XPOWERS_AXP192_ALL_IRQ); + + // Set constant current charging current + PMU->setChargerConstantCurr(XPOWERS_AXP192_CHG_CUR_450MA); + + + } else if (PMU->getChipModel() == XPOWERS_AXP2101) { + + // t-beam s3 core + + // gnss module power channel - now turned on in setGpsPower + // PMU->setPowerChannelVoltage(XPOWERS_ALDO4, 3300); + // PMU->enablePowerOutput(XPOWERS_ALDO4); + + // lora radio power channel + PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300); + PMU->enablePowerOutput(XPOWERS_ALDO3); + + // m.2 interface + PMU->setPowerChannelVoltage(XPOWERS_DCDC3, 3300); + PMU->enablePowerOutput(XPOWERS_DCDC3); + + // PMU->setPowerChannelVoltage(XPOWERS_DCDC4, 3300); + // PMU->enablePowerOutput(XPOWERS_DCDC4); + + //not use channel + PMU->disablePowerOutput(XPOWERS_DCDC2); //not elicited + PMU->disablePowerOutput(XPOWERS_DCDC5); //not elicited + PMU->disablePowerOutput(XPOWERS_DLDO1); //Invalid power channel, it does not exist + PMU->disablePowerOutput(XPOWERS_DLDO2); //Invalid power channel, it does not exist + PMU->disablePowerOutput(XPOWERS_VBACKUP); + + //disable all axp chip interrupt + PMU->disableIRQ(XPOWERS_AXP2101_ALL_IRQ); + + //Set the constant current charging current of AXP2101, temporarily use 500mA by default + PMU->setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_500MA); + + } + + + PMU->clearIrqStatus(); + + // TBeam1.1 /T-Beam S3-Core has no external TS detection, + // it needs to be disabled, otherwise it will cause abnormal charging + PMU->disableTSPinMeasure(); + + // PMU->enableSystemVoltageMeasure(); + PMU->enableVbusVoltageMeasure(); + PMU->enableBattVoltageMeasure(); + + DEBUG_MSG("=======================================================================\n"); + if (PMU->isChannelAvailable(XPOWERS_DCDC1)) { + DEBUG_MSG("DC1 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC1) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC1)); + } + if (PMU->isChannelAvailable(XPOWERS_DCDC2)) { + DEBUG_MSG("DC2 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC2)); + } + if (PMU->isChannelAvailable(XPOWERS_DCDC3)) { + DEBUG_MSG("DC3 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC3) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC3)); + } + if (PMU->isChannelAvailable(XPOWERS_DCDC4)) { + DEBUG_MSG("DC4 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC4) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC4)); + } + if (PMU->isChannelAvailable(XPOWERS_LDO2)) { + DEBUG_MSG("LDO2 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_LDO2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_LDO2)); + } + if (PMU->isChannelAvailable(XPOWERS_LDO3)) { + DEBUG_MSG("LDO3 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_LDO3) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_LDO3)); + } + if (PMU->isChannelAvailable(XPOWERS_ALDO1)) { + DEBUG_MSG("ALDO1: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO1) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO1)); + } + if (PMU->isChannelAvailable(XPOWERS_ALDO2)) { + DEBUG_MSG("ALDO2: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO2)); + } + if (PMU->isChannelAvailable(XPOWERS_ALDO3)) { + DEBUG_MSG("ALDO3: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO3) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO3)); + } + if (PMU->isChannelAvailable(XPOWERS_ALDO4)) { + DEBUG_MSG("ALDO4: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO4) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO4)); + } + if (PMU->isChannelAvailable(XPOWERS_BLDO1)) { + DEBUG_MSG("BLDO1: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_BLDO1) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_BLDO1)); + } + if (PMU->isChannelAvailable(XPOWERS_BLDO2)) { + DEBUG_MSG("BLDO2: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_BLDO2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_BLDO2)); + } + DEBUG_MSG("=======================================================================\n"); + + + //Set up the charging voltage, AXP2101/AXP192 4.2V gear is the same + // XPOWERS_AXP192_CHG_VOL_4V2 = XPOWERS_AXP2101_CHG_VOL_4V2 + PMU->setChargeTargetVoltage(XPOWERS_AXP192_CHG_VOL_4V2); + + // Set PMU shutdown voltage at 2.6V to maximize battery utilization + PMU->setSysPowerDownVoltage(2600); + + + +#ifdef PMU_IRQ + uint64_t pmuIrqMask = 0; + + if (PMU->getChipModel() == XPOWERS_AXP192) { + pmuIrqMask = XPOWERS_AXP192_VBUS_INSERT_IRQ | XPOWERS_AXP192_BAT_INSERT_IRQ | XPOWERS_AXP192_PKEY_SHORT_IRQ; + } else if (PMU->getChipModel() == XPOWERS_AXP2101) { + pmuIrqMask = XPOWERS_AXP2101_VBUS_INSERT_IRQ | XPOWERS_AXP2101_BAT_INSERT_IRQ | XPOWERS_AXP2101_PKEY_SHORT_IRQ; + } + + pinMode(PMU_IRQ, INPUT); + attachInterrupt( + PMU_IRQ, [] { pmu_irq = true; }, FALLING); + + // we do not look for AXPXXX_CHARGING_FINISHED_IRQ & AXPXXX_CHARGING_IRQ because it occurs repeatedly while there is + // no battery also it could cause inadvertent waking from light sleep just because the battery filled + // we don't look for AXPXXX_BATT_REMOVED_IRQ because it occurs repeatedly while no battery installed + // we don't look at AXPXXX_VBUS_REMOVED_IRQ because we don't have anything hooked to vbus + PMU->enableIRQ(pmuIrqMask); + + PMU->clearIrqStatus(); +#endif /*PMU_IRQ*/ + + readPowerStatus(); + + pmu_found = true; + + return pmu_found; + #else return false; #endif diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 178e61a0f..2a495999b 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -12,7 +12,7 @@ static bool isPowered() { // Circumvent the battery sensing logic and assumes constant power if no battery pin or power mgmt IC - #if !defined(BATTERY_PIN) && !defined(HAS_AXP192) + #if !defined(BATTERY_PIN) && !defined(HAS_AXP192) && !defined(HAS_AXP2101) return true; #endif diff --git a/src/detect/i2cScan.h b/src/detect/i2cScan.h index c1c6f8f87..f5f8effc9 100644 --- a/src/detect/i2cScan.h +++ b/src/detect/i2cScan.h @@ -3,8 +3,9 @@ #include #include "mesh/generated/telemetry.pb.h" -#ifdef HAS_AXP192 -#include "axp20x.h" +// AXP192 and AXP2101 have the same device address, we just need to identify it in Power.cpp +#ifndef XPOWERS_AXP192_AXP2101_ADDRESS +#define XPOWERS_AXP192_AXP2101_ADDRESS 0x34 #endif #if HAS_WIRE @@ -114,10 +115,10 @@ void scanI2Cdevice(void) screen_found = addr; DEBUG_MSG("st7567 display found\n"); } -#ifdef HAS_AXP192 - if (addr == AXP192_SLAVE_ADDRESS) { - axp192_found = true; - DEBUG_MSG("axp192 PMU found\n"); +#ifdef HAS_PMU + if (addr == XPOWERS_AXP192_AXP2101_ADDRESS) { + pmu_found = true; + DEBUG_MSG("axp192/axp2101 PMU found\n"); } #endif if (addr == BME_ADDR || addr == BME_ADDR_ALTERNATE) { diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index f46123560..9cedc82a2 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -64,15 +64,40 @@ bool GPS::setupGPS() if (_serial_gps && !didSerialInit) { didSerialInit = true; +#if CONFIG_IDF_TARGET_ESP32S3 + // In esp32s3 framework, setRxBufferSize needs to be initialized before Serial + _serial_gps->setRxBufferSize(2048); // the default is 256 +#endif + // ESP32 has a special set of parameters vs other arduino ports #if defined(GPS_RX_PIN) && defined(ARCH_ESP32) _serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN); #else _serial_gps->begin(GPS_BAUDRATE); #endif -#ifdef ARCH_ESP32 + +#if CONFIG_IDF_TARGET_ESP32 _serial_gps->setRxBufferSize(2048); // the default is 256 #endif + +#ifdef LILYGO_TBEAM_S3_CORE + /* + * t-beam-s3-core uses the same L76K GNSS module as t-echo. + * Unlike t-echo, L76K uses 9600 baud rate for communication by default. + * */ + _serial_gps->begin(9600); + delay(250); + // Initialize the L76K Chip, use GPS + GLONASS + _serial_gps->write("$PCAS04,5*1C\r\n"); + delay(250); + // only ask for RMC and GGA + _serial_gps->write("$PCAS03,1,0,0,0,1,0,0,0,0,0,,,0,0*02\r\n"); + delay(250); + // Switch to Vehicle Mode, since SoftRF enables Aviation < 2g + _serial_gps->write("$PCAS11,3*1E\r\n"); + delay(250); +#endif + #ifdef TTGO_T_ECHO // Switch to 9600 baud, then close and reopen port _serial_gps->end(); diff --git a/src/input/kbI2cBase.cpp b/src/input/kbI2cBase.cpp index b1ae6ec29..4d94f31b0 100644 --- a/src/input/kbI2cBase.cpp +++ b/src/input/kbI2cBase.cpp @@ -2,6 +2,8 @@ #include "configuration.h" #include +extern uint8_t cardkb_found; + KbI2cBase::KbI2cBase(const char *name) : concurrency::OSThread(name) { this->_originName = name; diff --git a/src/main.cpp b/src/main.cpp index 167ab8cc5..0ef46107b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -84,7 +84,7 @@ bool eink_found = true; uint32_t serialSinceMsec; -bool axp192_found; +bool pmu_found; // Array map of sensor types (as array index) and i2c address as value we'll find in the i2c scan uint8_t nodeTelemetrySensorsMap[7] = { 0, 0, 0, 0, 0, 0, 0 }; @@ -301,8 +301,8 @@ void setup() setupModules(); // Do this after service.init (because that clears error_code) -#ifdef HAS_AXP192 - if (!axp192_found) +#ifdef HAS_PMU + if (!pmu_found) RECORD_CRITICALERROR(CriticalErrorCode_NO_AXP192); // Record a hardware fault for missing hardware #endif diff --git a/src/main.h b/src/main.h index 02dce04b3..3f4bea7e2 100644 --- a/src/main.h +++ b/src/main.h @@ -15,7 +15,7 @@ extern uint8_t faceskb_found; extern uint8_t rtc_found; extern bool eink_found; -extern bool axp192_found; +extern bool pmu_found; extern bool isCharging; extern bool isUSBPowered; diff --git a/src/mesh/http/WiFiAPClient.cpp b/src/mesh/http/WiFiAPClient.cpp index 1ee830829..f7868106f 100644 --- a/src/mesh/http/WiFiAPClient.cpp +++ b/src/mesh/http/WiFiAPClient.cpp @@ -238,8 +238,10 @@ bool initWifi(bool forceSoftAP) WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); DEBUG_MSG("MY IP AP ADDRESS: %s\n", WiFi.softAPIP().toString().c_str()); +#if !CONFIG_IDF_TARGET_ESP32S3 // This is needed to improve performance. esp_wifi_set_ps(WIFI_PS_NONE); // Disable radio power saving +#endif dnsServer.start(53, "*", apIP); @@ -252,14 +254,19 @@ bool initWifi(bool forceSoftAP) WiFi.setHostname(ourHost); WiFi.onEvent(WiFiEvent); +#if !CONFIG_IDF_TARGET_ESP32S3 // This is needed to improve performance. esp_wifi_set_ps(WIFI_PS_NONE); // Disable radio power saving +#endif WiFi.onEvent( [](WiFiEvent_t event, WiFiEventInfo_t info) { Serial.print("\nWiFi lost connection. Reason: "); + #if CONFIG_IDF_TARGET_ESP32S3 + Serial.println(info.wifi_sta_disconnected.reason); + wifiDisconnectReason = info.wifi_sta_disconnected.reason; + #else Serial.println(info.disconnected.reason); - /* If we are disconnected from the AP for some reason, save the error code. @@ -268,8 +275,14 @@ bool initWifi(bool forceSoftAP) https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-reason-code */ wifiDisconnectReason = info.disconnected.reason; + #endif + }, +#if CONFIG_IDF_TARGET_ESP32S3 + WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED); +#else WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED); +#endif DEBUG_MSG("JOINING WIFI soon: ssid=%s\n", wifiName); wifiReconnect = new Periodic("WifiConnect", reconnectWiFi); diff --git a/src/platform/esp32/ESP32CryptoEngine.cpp b/src/platform/esp32/ESP32CryptoEngine.cpp index 2003a235b..60026c4db 100644 --- a/src/platform/esp32/ESP32CryptoEngine.cpp +++ b/src/platform/esp32/ESP32CryptoEngine.cpp @@ -1,16 +1,16 @@ #include "CryptoEngine.h" #include "configuration.h" +#if CONFIG_IDF_TARGET_ESP32S3 +#include "mbedtls/aes.h" +#else #include "crypto/includes.h" - #include "crypto/common.h" - // #include "esp_system.h" - #include "crypto/aes.h" #include "crypto/aes_wrap.h" #include "mbedtls/aes.h" - +#endif class ESP32CryptoEngine : public CryptoEngine diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index 8f8bd8b86..527f2947e 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -34,6 +34,9 @@ #define HAS_RTC 1 #endif +#if defined(HAS_AXP192) || defined(HAS_AXP2101) +#define HAS_PMU +#endif // // set HW_VENDOR // @@ -44,6 +47,8 @@ #define HW_VENDOR HardwareModel_TBEAM #elif defined(TBEAM_V07) #define HW_VENDOR HardwareModel_TBEAM_V0P7 +#elif defined(LILYGO_TBEAM_S3_CORE) + #define HW_VENDOR HardwareModel_LILYGO_TBEAM_S3_CORE #elif defined(DIY_V1) #define HW_VENDOR HardwareModel_DIY_V1 #elif defined(RAK_11200) diff --git a/src/power.h b/src/power.h index 5e5005a64..64043eb86 100644 --- a/src/power.h +++ b/src/power.h @@ -32,8 +32,8 @@ class Power : private concurrency::OSThread protected: meshtastic::PowerStatus *statusHandler; - /// Setup a axp192, return true if found - bool axp192Init(); + /// Setup a xpowers chip axp192/axp2101, return true if found + bool axpChipInit(); /// Setup a simple ADC input based battery sensor bool analogInit(); diff --git a/src/shutdown.h b/src/shutdown.h index 306f84d22..f37339641 100644 --- a/src/shutdown.h +++ b/src/shutdown.h @@ -35,8 +35,8 @@ void powerCommandsCheck() if (shutdownAtMsec && millis() > shutdownAtMsec) { DEBUG_MSG("Shutting down from admin command\n"); -#ifdef HAS_AXP192 - if (axp192_found == true) { +#ifdef HAS_PMU + if (pmu_found == true) { playShutdownMelody(); power->shutdown(); } diff --git a/src/sleep.cpp b/src/sleep.cpp index f63c1a62f..055cf770d 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -19,9 +19,9 @@ esp_sleep_source_t wakeCause; // the reason we booted this time #endif -#ifdef HAS_AXP192 -#include "axp20x.h" -extern AXP20X_Class axp; +#ifdef HAS_PMU +#include "XPowersLibInterface.hpp" +extern XPowersLibInterface *PMU; #endif /// Called to ask any observers if they want to veto sleep. Return 1 to veto or 0 to allow sleep to happen @@ -78,10 +78,10 @@ void setLed(bool ledOn) digitalWrite(LED_PIN, ledOn ^ LED_INVERTED); #endif -#ifdef HAS_AXP192 - if (axp192_found) { +#ifdef HAS_PMU + if (pmu_found && PMU) { // blink the axp led - axp.setChgLEDMode(ledOn ? AXP20X_LED_LOW_LEVEL : AXP20X_LED_OFF); + PMU->setChargingLedMode(ledOn ? XPOWERS_CHG_LED_ON : XPOWERS_CHG_LED_OFF); } #endif } @@ -90,9 +90,17 @@ void setGPSPower(bool on) { DEBUG_MSG("Setting GPS power=%d\n", on); -#ifdef HAS_AXP192 - if (axp192_found) - axp.setPowerOutPut(AXP192_LDO3, on ? AXP202_ON : AXP202_OFF); // GPS main power +#ifdef HAS_PMU + if (pmu_found && PMU){ + uint8_t model = PMU->getChipModel(); + if(model == XPOWERS_AXP2101){ + // t-beam-s3-core GNSS power channel + on ? PMU->enablePowerOutput(XPOWERS_ALDO4) : PMU->disablePowerOutput(XPOWERS_ALDO4); + }else if(model == XPOWERS_AXP192){ + // t-beam GNSS power channel + on ? PMU->enablePowerOutput(XPOWERS_LDO3) : PMU->disablePowerOutput(XPOWERS_LDO3); + } + } #endif } @@ -185,8 +193,8 @@ void doDeepSleep(uint64_t msecToWake) digitalWrite(VEXT_ENABLE, 1); // turn off the display power #endif -#ifdef HAS_AXP192 - if (axp192_found) { +#ifdef HAS_PMU + if (pmu_found && PMU) { // Obsolete comment: from back when we we used to receive lora packets while CPU was in deep sleep. // We no longer do that, because our light-sleep current draws are low enough and it provides fast start/low cost // wake. We currently use deep sleep only for 'we want our device to actually be off - because our battery is @@ -198,7 +206,12 @@ void doDeepSleep(uint64_t msecToWake) // in its sequencer (true?) so the average power draw should be much lower even if we were listinging for packets // all the time. - axp.setPowerOutPut(AXP192_LDO2, AXP202_OFF); // LORA radio + uint8_t model = PMU->getChipModel(); + if(model == XPOWERS_AXP2101){ + PMU->disablePowerOutput(XPOWERS_ALDO3); // lora radio power channel + }else if(model == XPOWERS_AXP192){ + PMU->disablePowerOutput(XPOWERS_LDO2); // lora radio power channel + } } #endif @@ -255,7 +268,7 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r #endif #ifdef PMU_IRQ // wake due to PMU can happen repeatedly if there is no battery installed or the battery fills - if (axp192_found) + if (pmu_found) gpio_wakeup_enable((gpio_num_t)PMU_IRQ, GPIO_INTR_LOW_LEVEL); // pmu irq #endif assert(esp_sleep_enable_gpio_wakeup() == ESP_OK); @@ -284,7 +297,12 @@ void enableModemSleep() { static esp_pm_config_esp32_t esp32_config; // filled with zeros because bss + +#if CONFIG_IDF_TARGET_ESP32S3 + esp32_config.max_freq_mhz = CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ; +#else esp32_config.max_freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; +#endif esp32_config.min_freq_mhz = 20; // 10Mhz is minimum recommended esp32_config.light_sleep_enable = false; int rv = esp_pm_configure(&esp32_config); diff --git a/variants/tbeam-s3-core/pins_arduino.h b/variants/tbeam-s3-core/pins_arduino.h new file mode 100644 index 000000000..7b0193e0e --- /dev/null +++ b/variants/tbeam-s3-core/pins_arduino.h @@ -0,0 +1,29 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define USB_VID 0x303a +#define USB_PID 0x1001 + +#define EXTERNAL_NUM_INTERRUPTS 46 +#define NUM_DIGITAL_PINS 48 +#define NUM_ANALOG_INPUTS 20 + +#define analogInputToDigitalPin(p) (((p)<20)?(analogChannelToDigitalPin(p)):-1) +#define digitalPinToInterrupt(p) (((p)<48)?(p):-1) +#define digitalPinHasPWM(p) (p < 46) + +static const uint8_t TX = 43; +static const uint8_t RX = 44; + +static const uint8_t SDA = 42; +static const uint8_t SCL = 41; + +static const uint8_t SS = 10; +static const uint8_t MOSI = 11; +static const uint8_t MISO = 13; +static const uint8_t SCK = 12; + + +#endif /* Pins_Arduino_h */ diff --git a/variants/tbeam-s3-core/platformio.ini b/variants/tbeam-s3-core/platformio.ini new file mode 100644 index 000000000..9adfb90dc --- /dev/null +++ b/variants/tbeam-s3-core/platformio.ini @@ -0,0 +1,11 @@ +; The 1.0 release of the LilyGo TBEAM-S3-Core board +[env:tbeam-s3-core] +extends = esp32s3_base +board = tbeam-s3-core + +lib_deps = + ${esp32s3_base.lib_deps} +build_flags = + ${esp32s3_base.build_flags} + -Ivariants/tbeam-s3-core + diff --git a/variants/tbeam-s3-core/variant.h b/variants/tbeam-s3-core/variant.h new file mode 100644 index 000000000..8fe181ad0 --- /dev/null +++ b/variants/tbeam-s3-core/variant.h @@ -0,0 +1,49 @@ +// #define BUTTON_NEED_PULLUP // if set we need to turn on the internal CPU pullup during sleep + +#define I2C_SDA 42 +#define I2C_SCL 41 + +#define BUTTON_PIN 0 // The middle button GPIO on the T-Beam S3 +//#define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed. Does anyone use this? It is not documented anywhere. +// #define EXT_NOTIFY_OUT 13 // Default pin to use for Ext Notify Module. + +#define LED_INVERTED 1 +// #define LED_PIN 4 // Newer tbeams (1.1) have an extra led on GPIO4 + +// TTGO uses a common pinout for their SX1262 vs RF95 modules - both can be enabled and we will probe at runtime for RF95 and if +// not found then probe for SX1262 +#define USE_SX1262 +#define USE_SX1268 + +#define LORA_DIO0 -1 // a No connect on the SX1262 module +#define LORA_RESET 5 +#define LORA_DIO1 1 // SX1262 IRQ +#define LORA_DIO2 4 // SX1262 BUSY +#define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TXCO is enabled + +#ifdef USE_SX1262 +#define SX126X_CS 10 // FIXME - we really should define LORA_CS instead +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY LORA_DIO2 +#define SX126X_RESET LORA_RESET +#define SX126X_E22 // Not really an E22 but TTGO seems to be trying to clone that +// Internally the TTGO module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for the sx1262interface +// code) +#endif + +// Leave undefined to disable our PMU IRQ handler. DO NOT ENABLE THIS because the pmuirq can cause sperious interrupts +// and waking from light sleep +// #define PMU_IRQ 40 +#define HAS_AXP2101 + +#define RF95_SCK 12 +#define RF95_MISO 13 +#define RF95_MOSI 11 +#define RF95_NSS 10 + +#define GPS_RX_PIN 9 +#define GPS_TX_PIN 8 +#define GPS_WAKEUP_PIN 7 +#define GPS_1PPS_PIN 6 + +