diff --git a/src/ButtonThread.cpp b/src/ButtonThread.cpp index 2363f804c..375029c99 100644 --- a/src/ButtonThread.cpp +++ b/src/ButtonThread.cpp @@ -116,46 +116,55 @@ ButtonThread::ButtonThread() : OSThread("Button") #endif } +void ButtonThread::switchPage() +{ +#ifdef BUTTON_PIN +#if !defined(USERPREFS_BUTTON_PIN) + if (((config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN) != + moduleConfig.canned_message.inputbroker_pin_press) || + !(moduleConfig.canned_message.updown1_enabled || moduleConfig.canned_message.rotary1_enabled) || + !moduleConfig.canned_message.enabled) { + powerFSM.trigger(EVENT_PRESS); + } +#endif +#if defined(USERPREFS_BUTTON_PIN) + if (((config.device.button_gpio ? config.device.button_gpio : USERPREFS_BUTTON_PIN) != + moduleConfig.canned_message.inputbroker_pin_press) || + !(moduleConfig.canned_message.updown1_enabled || moduleConfig.canned_message.rotary1_enabled) || + !moduleConfig.canned_message.enabled) { + powerFSM.trigger(EVENT_PRESS); + } +#endif + +#endif +#if defined(ARCH_PORTDUINO) + if ((settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC) && + (settingsMap[user] != moduleConfig.canned_message.inputbroker_pin_press) || + !moduleConfig.canned_message.enabled) { + powerFSM.trigger(EVENT_PRESS); + } +#endif +} + +void ButtonThread::sendAdHocPosition() +{ + service->refreshLocalMeshNode(); + auto sentPosition = service->trySendPosition(NODENUM_BROADCAST, true); + if (screen) { + if (sentPosition) + screen->print("Sent ad-hoc position\n"); + else + screen->print("Sent ad-hoc nodeinfo\n"); + screen->forceDisplay(true); // Force a new UI frame, then force an EInk update + } +} + int32_t ButtonThread::runOnce() { // If the button is pressed we suppress CPU sleep until release canSleep = true; // Assume we should not keep the board awake #if defined(BUTTON_PIN) || defined(USERPREFS_BUTTON_PIN) - // #if defined(ELECROW_ThinkNode_M1) || defined(ELECROW_ThinkNode_M2) - // buzzer_updata(); - // if (buttonPressed) { - // buttonPressed = false; // 清除标志 - // LOG_INFO("PIN_BUTTON2 pressed!"); // 串口打印信息 - // // off_currentTime = millis(); - // while (digitalRead(PIN_BUTTON2) == HIGH) { - // if (cont < 40) { - // // unsigned long currentTime = millis(); // 获取当前时间 - // // if (currentTime - off_currentTime >= 1000) { - // cont++; - // // off_currentTime = currentTime; - // // } - // delay(100); - // } else { - - // currentState = OFF; - // isBuzzing = false; - // cont = 0; - // BEEP_STATE = false; - // analogWrite(M2_buzzer, 0); - // pinMode(M2_buzzer, INPUT); - // screen->setOn(false); - // cont = 0; - // LOG_INFO("GGGGGGGGGGGGGGGGGGGGGGGGG"); - // pinMode(1, OUTPUT); - // digitalWrite(1, LOW); - // pinMode(6, OUTPUT); - // digitalWrite(6, LOW); - // } - // } - // } - - // #endif userButton.tick(); canSleep &= userButton.isIdle(); #elif defined(ARCH_PORTDUINO) @@ -180,32 +189,27 @@ int32_t ButtonThread::runOnce() // If a nag notification is running, stop it and prevent other actions if (moduleConfig.external_notification.enabled && (externalNotificationModule->nagCycleCutoff != UINT32_MAX)) { externalNotificationModule->stopNow(); - return 50; - } -#ifdef BUTTON_PIN -#if !defined(USERPREFS_BUTTON_PIN) - if (((config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN) != -#endif -#if defined(USERPREFS_BUTTON_PIN) - if (((config.device.button_gpio ? config.device.button_gpio : USERPREFS_BUTTON_PIN) != -#endif - moduleConfig.canned_message.inputbroker_pin_press) || - !(moduleConfig.canned_message.updown1_enabled || moduleConfig.canned_message.rotary1_enabled) || - !moduleConfig.canned_message.enabled) { - powerFSM.trigger(EVENT_PRESS); - } -#endif -#if defined(ARCH_PORTDUINO) - if ((settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC) && - (settingsMap[user] != moduleConfig.canned_message.inputbroker_pin_press) || - !moduleConfig.canned_message.enabled) { - powerFSM.trigger(EVENT_PRESS); + break; } +#ifdef ELECROW_ThinkNode_M1 + sendAdHocPosition(); + break; #endif + switchPage(); break; } case BUTTON_EVENT_PRESSED_SCREEN: { + LOG_BUTTON("AltPress!"); +#ifdef ELECROW_ThinkNode_M1 + // If a nag notification is running, stop it and prevent other actions + if (moduleConfig.external_notification.enabled && (externalNotificationModule->nagCycleCutoff != UINT32_MAX)) { + externalNotificationModule->stopNow(); + break; + } + switchPage(); + break; +#endif // turn screen on or off screen_flag = !screen_flag; if (screen) @@ -215,22 +219,18 @@ int32_t ButtonThread::runOnce() case BUTTON_EVENT_DOUBLE_PRESSED: { LOG_BUTTON("Double press!"); - service->refreshLocalMeshNode(); - auto sentPosition = service->trySendPosition(NODENUM_BROADCAST, true); - if (screen) { - if (sentPosition) - screen->print("Sent ad-hoc position\n"); - else - screen->print("Sent ad-hoc nodeinfo\n"); - screen->forceDisplay(true); // Force a new UI frame, then force an EInk update - } +#ifdef ELECROW_ThinkNode_M1 + digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW); + break; +#endif + sendAdHocPosition(); break; } case BUTTON_EVENT_MULTI_PRESSED: { LOG_BUTTON("Mulitipress! %hux", multipressClickCount); switch (multipressClickCount) { -#if HAS_GPS +#if HAS_GPS && !defined(ELECROW_ThinkNode_M1) // 3 clicks: toggle GPS case 3: if (!config.device.disable_triple_click && (gps != nullptr)) { @@ -239,17 +239,17 @@ int32_t ButtonThread::runOnce() screen->forceDisplay(true); // Force a new UI frame, then force an EInk update } break; -#elif defined(ELECROW_ThinkNode_M2) +#elif defined(ELECROW_ThinkNode_M1) || defined(ELECROW_ThinkNode_M2) case 3: LOG_INFO("3 clicks: toggle buzzer"); buzzer_flag = !buzzer_flag; - if (buzzer_flag) { - playBeep(); - } + if (!buzzer_flag) + noTone(PIN_BUZZER); break; + #endif -#if defined(USE_EINK) && defined(PIN_EINK_EN) // i.e. T-Echo +#if defined(USE_EINK) && defined(PIN_EINK_EN) && !defined(ELECROW_ThinkNode_M1) // i.e. T-Echo // 4 clicks: toggle backlight case 4: digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW); diff --git a/src/ButtonThread.h b/src/ButtonThread.h index a8f1f77c3..3af700dd0 100644 --- a/src/ButtonThread.h +++ b/src/ButtonThread.h @@ -37,6 +37,9 @@ class ButtonThread : public concurrency::OSThread void attachButtonInterrupts(); void detachButtonInterrupts(); void storeClickCount(); + bool isBuzzing() { return buzzer_flag; } + void setScreenFlag(bool flag) { screen_flag = flag; } + bool getScreenFlag() { return screen_flag; } // Disconnect and reconnect interrupts for light sleep #ifdef ARCH_ESP32 @@ -72,14 +75,12 @@ class ButtonThread : public concurrency::OSThread static void wakeOnIrq(int irq, int mode); + static void sendAdHocPosition(); + static void switchPage(); + // IRQ callbacks static void userButtonPressed() { btnEvent = BUTTON_EVENT_PRESSED; } - static void userButtonPressedScreen() - { - if (millis() > c_holdOffTime) { - btnEvent = BUTTON_EVENT_PRESSED_SCREEN; - } - } + static void userButtonPressedScreen() { btnEvent = BUTTON_EVENT_PRESSED_SCREEN; } static void userButtonDoublePressed() { btnEvent = BUTTON_EVENT_DOUBLE_PRESSED; } static void userButtonMultiPressed(void *callerThread); // Retrieve click count from non-static Onebutton while still valid static void userButtonPressedLongStart(); diff --git a/src/Power.cpp b/src/Power.cpp index 0dec0fc21..f11f8eac3 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -380,6 +380,20 @@ class AnalogBatteryLevel : public HasBatteryLevel // if we have a integrated device with a battery, we can assume that the battery is always connected #ifdef BATTERY_IMMUTABLE virtual bool isBatteryConnect() override { return true; } +#elif defined(ADC_V) + virtual bool isBatteryConnect() override + { + int lastReading = digitalRead(ADC_V); + // 判断值是否变化 + for (int i = 2; i < 500; i++) { + int reading = digitalRead(ADC_V); + if (reading != lastReading) { + return false; // 有变化,USB供电, 没接电池 + } + } + + return true; + } #else virtual bool isBatteryConnect() override { return getBatteryPercent() != -1; } #endif @@ -533,9 +547,6 @@ Power::Power() : OSThread("Power") { statusHandler = {}; low_voltage_counter = 0; -#if defined(ELECROW_ThinkNode_M1) || defined(POWER_CFG) - low_voltage_counter_led3 = 0; -#endif #ifdef DEBUG_HEAP lastheap = memGet.getFreeHeap(); #endif @@ -716,9 +727,6 @@ void Power::readPowerStatus() const PowerStatus powerStatus2 = PowerStatus(hasBattery, usbPowered, isChargingNow, batteryVoltageMv, batteryChargePercent); LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d", powerStatus2.getHasUSB(), powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent()); -#if defined(ELECROW_ThinkNode_M1) || defined(POWER_CFG) - power_num = powerStatus2.getBatteryVoltageMv(); -#endif newStatus.notifyObservers(&powerStatus2); #ifdef DEBUG_HEAP if (lastheap != memGet.getFreeHeap()) { @@ -766,9 +774,6 @@ void Power::readPowerStatus() if (batteryLevel && powerStatus2.getHasBattery() && !powerStatus2.getHasUSB()) { if (batteryLevel->getBattVoltage() < OCV[NUM_OCV_POINTS - 1]) { low_voltage_counter++; -#if defined(ELECROW_ThinkNode_M1) - low_voltage_counter_led3 = low_voltage_counter; -#endif LOG_DEBUG("Low voltage counter: %d/10", low_voltage_counter); if (low_voltage_counter > 10) { #ifdef ARCH_NRF52 @@ -781,13 +786,7 @@ void Power::readPowerStatus() } } else { low_voltage_counter = 0; -#if defined(ELECROW_ThinkNode_M1) - low_voltage_counter_led3 = low_voltage_counter; -#endif } -#ifdef POWER_CFG - low_voltage_counter_led3 = low_voltage_counter; -#endif } } diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index a2e7ebbc7..689f5e204 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -12,6 +12,7 @@ #include "RTC.h" #include "Throttle.h" #include "buzz.h" +#include "concurrency/Periodic.h" #include "meshUtils.h" #include "main.h" // pmu_found @@ -89,6 +90,45 @@ static const char *getGPSPowerStateString(GPSPowerState state) } } +#ifdef PIN_GPS_SWITCH +// If we have a hardware switch, define a periodic watcher outside of the GPS runOnce thread, since this can be sleeping +// idefinitely + +int lastState = LOW; +bool firstrun = true; + +static int32_t gpsSwitch() +{ + if (gps) { + int currentState = digitalRead(PIN_GPS_SWITCH); + + // if the switch is set to zero, disable the GPS Thread + if (firstrun) + if (currentState == LOW) + lastState = HIGH; + + if (currentState != lastState) { + if (currentState == LOW) { + config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED; + if (!firstrun) + playGPSDisableBeep(); + gps->disable(); + } else { + config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED; + if (!firstrun) + playGPSEnableBeep(); + gps->enable(); + } + lastState = currentState; + } + firstrun = false; + } + return 1000; +} + +static concurrency::Periodic *gpsPeriodic; +#endif + static void UBXChecksum(uint8_t *message, size_t length) { uint8_t CK_A = 0, CK_B = 0; @@ -1390,6 +1430,12 @@ GPS *GPS::createGps() pinMode(PIN_GPS_PPS, INPUT); #endif +#ifdef PIN_GPS_SWITCH + // toggle GPS via external GPIO switch + pinMode(PIN_GPS_SWITCH, INPUT); + gpsPeriodic = new concurrency::Periodic("GPSSwitch", gpsSwitch); +#endif + // Currently disabled per issue #525 (TinyGPS++ crash bug) // when fixed upstream, can be un-disabled to enable 3D FixType and PDOP #ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index e27495f54..8075dd468 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -30,6 +30,7 @@ along with this program. If not, see . #if !MESHTASTIC_EXCLUDE_GPS #include "GPS.h" #endif +#include "ButtonThread.h" #include "MeshService.h" #include "NodeDB.h" #include "error.h" @@ -1606,6 +1607,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) if (on != screenOn) { if (on) { LOG_INFO("Turn on screen"); + buttonThread->setScreenFlag(true); powerMon->setState(meshtastic_PowerMon_State_Screen_On); #ifdef T_WATCH_S3 PMU->enablePowerOutput(XPOWERS_ALDO2); @@ -1641,6 +1643,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) setScreensaverFrames(einkScreensaver); #endif LOG_INFO("Turn off screen"); + buttonThread->setScreenFlag(false); #ifdef ELECROW_ThinkNode_M1 if (digitalRead(PIN_EINK_EN) == HIGH) { digitalWrite(PIN_EINK_EN, LOW); diff --git a/src/main.cpp b/src/main.cpp index fd65830ef..a528da8af 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -212,6 +212,60 @@ const char *getDeviceName() return name; } +#if defined(ELECROW_ThinkNode_M1) || defined(ELECROW_ThinkNode_M2) +static int32_t ledBlinkCount = 0; + +static int32_t elecrowLedBlinker() +{ + // are we in alert buzzer mode? + if (buttonThread->isBuzzing()) { + + // blink LED three times for 3 seconds, then 3 times for a second, with one second pause + if (ledBlinkCount % 2) { // odd means LED OFF + ledBlink.set(false); + ledBlinkCount++; + if (ledBlinkCount >= 12) + ledBlinkCount = 0; + noTone(PIN_BUZZER); + return 1000; + } else { + if (ledBlinkCount < 6) { + ledBlink.set(true); + tone(PIN_BUZZER, 4000, 3000); + ledBlinkCount++; + return 3000; + } else { + ledBlink.set(true); + tone(PIN_BUZZER, 4000, 1000); + ledBlinkCount++; + return 1000; + } + } + } else { + ledBlinkCount = 0; + if (config.device.led_heartbeat_disabled) + return 1000; + + static bool ledOn; + // when fully charged, remain on! + if (powerStatus->getIsCharging() && powerStatus->getBatteryChargePercent() >= 100) { + ledOn = true; + } else { + ledOn ^= 1; + } + ledBlink.set(ledOn); + // when charging, blink 0.5Hz square wave rate to indicate that + if (powerStatus->getIsCharging()) { + return 500; + } + // When almost empty, blink rapidly + if (!powerStatus->getIsCharging() && powerStatus->getBatteryChargePercent() < 10) { + return 250; + } + } + return 1000; +} +#else static int32_t ledBlinker() { // Still set up the blinking (heartbeat) interval but skip code path below, so LED will blink if @@ -227,6 +281,7 @@ static int32_t ledBlinker() // have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that return powerStatus->getIsCharging() ? 1000 : (ledOn ? 1 : 1000); } +#endif uint32_t timeLastPowered = 0; @@ -263,11 +318,6 @@ void printInfo() void setup() { -#ifdef POWER_CHRG - pinMode(POWER_CHRG, OUTPUT); - digitalWrite(POWER_CHRG, HIGH); -#endif - #if defined(PIN_POWER_EN) pinMode(PIN_POWER_EN, OUTPUT); digitalWrite(PIN_POWER_EN, HIGH); @@ -278,11 +328,6 @@ void setup() digitalWrite(LED_POWER, HIGH); #endif -#ifdef POWER_LED - pinMode(POWER_LED, OUTPUT); - digitalWrite(POWER_LED, HIGH); -#endif - #ifdef USER_LED pinMode(USER_LED, OUTPUT); digitalWrite(USER_LED, LOW); @@ -414,7 +459,12 @@ void setup() OSThread::setup(); +#if defined(ELECROW_ThinkNode_M1) || defined(ELECROW_ThinkNode_M2) + // The ThinkNodes have their own blink logic + ledPeriodic = new Periodic("Blink", elecrowLedBlinker); +#else ledPeriodic = new Periodic("Blink", ledBlinker); +#endif fsInit(); diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 9bb63652a..c89abbe74 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -743,6 +743,15 @@ void NodeDB::installDefaultModuleConfig() moduleConfig.external_notification.output_ms = 100; moduleConfig.external_notification.active = true; #endif +#ifdef ELECROW_ThinkNode_M1 + // Default to Elecrow USER_LED (blue) + moduleConfig.external_notification.enabled = true; + moduleConfig.external_notification.output = USER_LED; + moduleConfig.external_notification.active = true; + moduleConfig.external_notification.alert_message = true; + moduleConfig.external_notification.output_ms = 1000; + moduleConfig.external_notification.nag_timeout = 60; +#endif #ifdef BUTTON_SECONDARY_CANNEDMESSAGES // Use a board's second built-in button as input source for canned messages moduleConfig.canned_message.enabled = true; diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp index ab1e5c922..3c4faac3e 100644 --- a/src/platform/esp32/main-esp32.cpp +++ b/src/platform/esp32/main-esp32.cpp @@ -109,9 +109,8 @@ void esp32Setup() randomSeed(seed); */ -#ifdef POWER_FULL - pinMode(POWER_FULL, INPUT); - pinMode(7, INPUT); +#ifdef ADC_V + pinMode(ADC_V, INPUT); #endif LOG_DEBUG("Total heap: %d", ESP.getHeapSize()); diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp index 53971e95a..9accd2a02 100644 --- a/src/platform/nrf52/main-nrf52.cpp +++ b/src/platform/nrf52/main-nrf52.cpp @@ -235,10 +235,6 @@ void nrf52InitSemiHosting() void nrf52Setup() { -#ifdef USB_CHECK - pinMode(USB_CHECK, INPUT); -#endif - #ifdef ADC_V pinMode(ADC_V, INPUT); #endif @@ -288,7 +284,7 @@ void cpuDeepSleep(uint32_t msecToWake) #endif // This may cause crashes as debug messages continue to flow. Serial.end(); -#ifdef PIN_SERIAL_RX1 +#ifdef PIN_SERIAL1_RX Serial1.end(); #endif setBluetoothEnable(false); diff --git a/src/power.h b/src/power.h index 97944fef7..e9c0deb7c 100644 --- a/src/power.h +++ b/src/power.h @@ -84,11 +84,6 @@ class Power : private concurrency::OSThread void setStatusHandler(meshtastic::PowerStatus *handler) { statusHandler = handler; } const uint16_t OCV[11] = {OCV_ARRAY}; -#if defined(ELECROW_ThinkNode_M1) || defined(POWER_CFG) - uint8_t low_voltage_counter_led3; - int power_num = 0; -#endif - protected: meshtastic::PowerStatus *statusHandler; diff --git a/variants/ELECROW-ThinkNode-M1/variant.h b/variants/ELECROW-ThinkNode-M1/variant.h index fc2fddbdf..2e91e378d 100644 --- a/variants/ELECROW-ThinkNode-M1/variant.h +++ b/variants/ELECROW-ThinkNode-M1/variant.h @@ -41,16 +41,15 @@ extern "C" { #define NUM_ANALOG_INPUTS (1) #define NUM_ANALOG_OUTPUTS (0) -#define PIN_LED1 -1 #define PIN_LED2 -1 #define PIN_LED3 -1 // LED -#define POWER_LED (32 + 6) // red +#define PIN_LED1 (32 + 6) // red #define LED_POWER (32 + 4) #define USER_LED (0 + 13) // green // USB_CHECK -#define USB_CHECK (32 + 3) +#define EXT_PWR_DETECT (32 + 3) #define ADC_V (0 + 8) #define LED_RED PIN_LED3 @@ -59,7 +58,7 @@ extern "C" { #define LED_BUILTIN LED_BLUE #define LED_CONN PIN_GREEN #define LED_STATE_ON 0 // State when LED is lit // LED灯亮时的状态 -#define M1_buzzer (0 + 6) +#define PIN_BUZZER (0 + 6) /* * Buttons */ @@ -82,6 +81,7 @@ extern "C" { static const uint8_t A0 = PIN_A0; #define ADC_RESOLUTION 14 +#define BATTERY_SENSE_SAMPLES 30 #define PIN_NFC1 (9) #define PIN_NFC2 (10) @@ -159,7 +159,7 @@ External serial flash WP25R1635FZUIL0 #define GPS_THREAD_INTERVAL 50 -#define PIN_GPS_PPS (32 + 1) // GPS开关判断 +#define PIN_GPS_SWITCH (32 + 1) // GPS开关判断 #define PIN_SERIAL1_RX GPS_TX_PIN #define PIN_SERIAL1_TX GPS_RX_PIN diff --git a/variants/ELECROW-ThinkNode-M2/variant.h b/variants/ELECROW-ThinkNode-M2/variant.h index 801d5606f..55f35e498 100644 --- a/variants/ELECROW-ThinkNode-M2/variant.h +++ b/variants/ELECROW-ThinkNode-M2/variant.h @@ -1,14 +1,13 @@ // Status -#define LED_PIN_POWER 1 -#define BIAS_T_ENABLE LED_PIN_POWER -#define BIAS_T_VALUE HIGH +#define LED_PIN 1 #define PIN_BUTTON1 47 // 功能键 #define PIN_BUTTON2 4 // 电源键 -#define POWER_CFG -#define POWER_CHRG 6 -#define POWER_FULL 42 +#define LED_PIN_POWER 6 +#define ADC_V 42 +// USB_CHECK +#define EXT_PWR_DETECT 7 #define PIN_BUZZER 5