Fix several features of M1 and M2 (i know what the 7 is now ...) (#6507)

* Fix several features of M1 and M2 (i know what the 7 is now ...)

* 'THe' should be 'The'.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* remove floating definition

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
This commit is contained in:
Thomas Göttgens 2025-04-07 12:46:22 +02:00 committed by GitHub
parent 860e8eca5a
commit 606abfc116
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 221 additions and 124 deletions

View File

@ -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);

View File

@ -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();

View File

@ -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
}
}

View File

@ -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

View File

@ -30,6 +30,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#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);

View File

@ -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();

View File

@ -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;

View File

@ -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());

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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