diff --git a/protobufs b/protobufs index c85791a85..737d1fc01 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit c85791a8543a5210786dc3579e099cb5ffbf72b9 +Subproject commit 737d1fc01bd7f57e48e9b8cd53b780b314b09c5b diff --git a/src/ButtonThread.h b/src/ButtonThread.h index 088642099..dff871f1f 100644 --- a/src/ButtonThread.h +++ b/src/ButtonThread.h @@ -4,6 +4,7 @@ #include "concurrency/OSThread.h" #include "configuration.h" #include "graphics/Screen.h" +#include "modules/ExternalNotificationModule.h" #include "power.h" #include @@ -114,6 +115,10 @@ class ButtonThread : public concurrency::OSThread { // DEBUG_MSG("press!\n"); #ifdef BUTTON_PIN + // If a nag notification is running, stop it + if (externalNotificationModule->nagCycleCutoff != UINT32_MAX) { + externalNotificationModule->nagCycleCutoff = 0; + } if ((BUTTON_PIN != moduleConfig.canned_message.inputbroker_pin_press) || !moduleConfig.canned_message.enabled) { powerFSM.trigger(EVENT_PRESS); diff --git a/src/mesh/generated/localonly.pb.h b/src/mesh/generated/localonly.pb.h index 6f0635bdc..c154a98db 100644 --- a/src/mesh/generated/localonly.pb.h +++ b/src/mesh/generated/localonly.pb.h @@ -151,7 +151,7 @@ extern const pb_msgdesc_t LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define LocalConfig_size 387 -#define LocalModuleConfig_size 378 +#define LocalModuleConfig_size 376 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/module_config.pb.h b/src/mesh/generated/module_config.pb.h index 9e77dd54c..47f410a5c 100644 --- a/src/mesh/generated/module_config.pb.h +++ b/src/mesh/generated/module_config.pb.h @@ -93,12 +93,13 @@ typedef struct _ModuleConfig_ExternalNotificationConfig { bool alert_message; bool alert_bell; bool use_pwm; - uint32_t output_vibra; - uint32_t output_buzzer; + uint8_t output_vibra; + uint8_t output_buzzer; bool alert_message_vibra; bool alert_message_buzzer; bool alert_bell_vibra; bool alert_bell_buzzer; + uint16_t nag_timeout; } ModuleConfig_ExternalNotificationConfig; typedef struct _ModuleConfig_MQTTConfig { @@ -193,7 +194,7 @@ extern "C" { #define ModuleConfig_MQTTConfig_init_default {0, "", "", "", 0, 0} #define ModuleConfig_AudioConfig_init_default {0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} #define ModuleConfig_SerialConfig_init_default {0, 0, 0, 0, _ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _ModuleConfig_SerialConfig_Serial_Mode_MIN} -#define ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +#define ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define ModuleConfig_StoreForwardConfig_init_default {0, 0, 0, 0, 0} #define ModuleConfig_RangeTestConfig_init_default {0, 0, 0} #define ModuleConfig_TelemetryConfig_init_default {0, 0, 0, 0, 0} @@ -202,7 +203,7 @@ extern "C" { #define ModuleConfig_MQTTConfig_init_zero {0, "", "", "", 0, 0} #define ModuleConfig_AudioConfig_init_zero {0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} #define ModuleConfig_SerialConfig_init_zero {0, 0, 0, 0, _ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _ModuleConfig_SerialConfig_Serial_Mode_MIN} -#define ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +#define ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define ModuleConfig_StoreForwardConfig_init_zero {0, 0, 0, 0, 0} #define ModuleConfig_RangeTestConfig_init_zero {0, 0, 0} #define ModuleConfig_TelemetryConfig_init_zero {0, 0, 0, 0, 0} @@ -240,6 +241,7 @@ extern "C" { #define ModuleConfig_ExternalNotificationConfig_alert_message_buzzer_tag 11 #define ModuleConfig_ExternalNotificationConfig_alert_bell_vibra_tag 12 #define ModuleConfig_ExternalNotificationConfig_alert_bell_buzzer_tag 13 +#define ModuleConfig_ExternalNotificationConfig_nag_timeout_tag 14 #define ModuleConfig_MQTTConfig_enabled_tag 1 #define ModuleConfig_MQTTConfig_address_tag 2 #define ModuleConfig_MQTTConfig_username_tag 3 @@ -341,7 +343,8 @@ X(a, STATIC, SINGULAR, UINT32, output_buzzer, 9) \ X(a, STATIC, SINGULAR, BOOL, alert_message_vibra, 10) \ X(a, STATIC, SINGULAR, BOOL, alert_message_buzzer, 11) \ X(a, STATIC, SINGULAR, BOOL, alert_bell_vibra, 12) \ -X(a, STATIC, SINGULAR, BOOL, alert_bell_buzzer, 13) +X(a, STATIC, SINGULAR, BOOL, alert_bell_buzzer, 13) \ +X(a, STATIC, SINGULAR, UINT32, nag_timeout, 14) #define ModuleConfig_ExternalNotificationConfig_CALLBACK NULL #define ModuleConfig_ExternalNotificationConfig_DEFAULT NULL @@ -409,7 +412,7 @@ extern const pb_msgdesc_t ModuleConfig_CannedMessageConfig_msg; /* Maximum encoded size of messages (where known) */ #define ModuleConfig_AudioConfig_size 19 #define ModuleConfig_CannedMessageConfig_size 49 -#define ModuleConfig_ExternalNotificationConfig_size 42 +#define ModuleConfig_ExternalNotificationConfig_size 40 #define ModuleConfig_MQTTConfig_size 169 #define ModuleConfig_RangeTestConfig_size 10 #define ModuleConfig_SerialConfig_size 26 diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp index 5a95ceec1..e496e1699 100644 --- a/src/modules/ExternalNotificationModule.cpp +++ b/src/modules/ExternalNotificationModule.cpp @@ -11,41 +11,9 @@ #define PIN_BUZZER false #endif -//#include - /* - Documentation: - https://github.com/meshtastic/firmware/blob/master/docs/software/modules/ExternalNotificationModule.md - - This module supports: - https://github.com/meshtastic/firmware/issues/654 - - - Quick reference: - - moduleConfig.external_notification.enabled - 0 = Disabled (Default) - 1 = Enabled - - moduleConfig.external_notification.active - 0 = Active Low (Default) - 1 = Active High - - moduleConfig.external_notification.alert_message - 0 = Disabled (Default) - 1 = Alert when a text message comes - - moduleConfig.external_notification.alert_bell - 0 = Disabled (Default) - 1 = Alert when the bell character is received - - moduleConfig.external_notification.output - GPIO of the output. (Default = 13) - - moduleConfig.external_notification.output_ms - Amount of time in ms for the alert. Default is 1000. - + https://meshtastic.org/docs/settings/moduleconfig/external-notification */ // Default configurations @@ -58,8 +26,11 @@ #define ASCII_BELL 0x07 -bool externalCurrentState = 0; -uint32_t externalTurnedOn = 0; +ExternalNotificationModule *externalNotificationModule; + +bool externalCurrentState[3] = {}; + +uint32_t externalTurnedOn[3] = {}; int32_t ExternalNotificationModule::runOnce() { @@ -76,38 +47,89 @@ int32_t ExternalNotificationModule::runOnce() // moduleConfig.external_notification.output_ms = 1000; // moduleConfig.external_notification.output = 13; - if (externalCurrentState && !moduleConfig.external_notification.use_pwm) { + if (moduleConfig.external_notification.use_pwm || !moduleConfig.external_notification.enabled) { + return INT32_MAX; // we don't need this thread here... + } else { // If the output is turned on, turn it back off after the given period of time. - if (externalTurnedOn + (moduleConfig.external_notification.output_ms + if (nagCycleCutoff != UINT32_MAX) { + if (externalTurnedOn[0] + (moduleConfig.external_notification.output_ms ? moduleConfig.external_notification.output_ms - : EXT_NOTIFICATION_MODULE_OUTPUT_MS) < - millis()) { - DEBUG_MSG("Turning off external notification\n"); - setExternalOff(); + : EXT_NOTIFICATION_MODULE_OUTPUT_MS) < millis()) { + getExternal(0) ? setExternalOff(0) : setExternalOn(0); + } + if (externalTurnedOn[1] + (moduleConfig.external_notification.output_ms + ? moduleConfig.external_notification.output_ms + : EXT_NOTIFICATION_MODULE_OUTPUT_MS) < millis()) { + getExternal(1) ? setExternalOff(1) : setExternalOn(1); + } + if (externalTurnedOn[2] + (moduleConfig.external_notification.output_ms + ? moduleConfig.external_notification.output_ms + : EXT_NOTIFICATION_MODULE_OUTPUT_MS) < millis()) { + getExternal(2) ? setExternalOff(2) : setExternalOn(2); + } + } + + if (nagCycleCutoff < millis()) { + nagCycleCutoff = UINT32_MAX; + DEBUG_MSG("Turning off external notification: "); + for (int i = 0; i < 2; i++) { + if (getExternal(i)) { + setExternalOff(i); + externalTurnedOn[i] = 0; + DEBUG_MSG("%d ", i); + } + } + DEBUG_MSG("\n"); + return INT32_MAX; // save cycles till we're needed again } - } - if (moduleConfig.external_notification.use_pwm) - return INT32_MAX; // we don't need this thread here... - else return 25; + } } -void ExternalNotificationModule::setExternalOn() +void ExternalNotificationModule::setExternalOn(uint8_t index) { - externalCurrentState = 1; - externalTurnedOn = millis(); + externalCurrentState[index] = 1; + externalTurnedOn[index] = millis(); - digitalWrite(output, - (moduleConfig.external_notification.active ? true : false)); + switch(index) { + case 1: + if(moduleConfig.external_notification.output_vibra) + digitalWrite(moduleConfig.external_notification.output_vibra, true); + break; + case 2: + if(moduleConfig.external_notification.output_buzzer) + digitalWrite(moduleConfig.external_notification.output_buzzer, true); + break; + default: + digitalWrite(output, (moduleConfig.external_notification.active ? true : false)); + break; + } } -void ExternalNotificationModule::setExternalOff() +void ExternalNotificationModule::setExternalOff(uint8_t index) { - externalCurrentState = 0; + externalCurrentState[index] = 0; + externalTurnedOn[index] = millis(); - digitalWrite(output, - (moduleConfig.external_notification.active ? false : true)); + switch(index) { + case 1: + if(moduleConfig.external_notification.output_vibra) + digitalWrite(moduleConfig.external_notification.output_vibra, false); + break; + case 2: + if(moduleConfig.external_notification.output_buzzer) + digitalWrite(moduleConfig.external_notification.output_buzzer, false); + break; + default: + digitalWrite(output, (moduleConfig.external_notification.active ? false : true)); + break; + } +} + +bool ExternalNotificationModule::getExternal(uint8_t index) +{ + return externalCurrentState[index]; } // -------- @@ -141,8 +163,20 @@ ExternalNotificationModule::ExternalNotificationModule() // Set the direction of a pin DEBUG_MSG("Using Pin %i in digital mode\n", output); pinMode(output, OUTPUT); - // Turn off the pin - setExternalOff(); + setExternalOff(0); + externalTurnedOn[0] = 0; + if(moduleConfig.external_notification.output_vibra) { + DEBUG_MSG("Using Pin %i for vibra motor\n", moduleConfig.external_notification.output_vibra); + pinMode(moduleConfig.external_notification.output_vibra, OUTPUT); + setExternalOff(1); + externalTurnedOn[1] = 0; + } + if(moduleConfig.external_notification.output_buzzer) { + DEBUG_MSG("Using Pin %i for buzzer\n", moduleConfig.external_notification.output_buzzer); + pinMode(moduleConfig.external_notification.output_buzzer, OUTPUT); + setExternalOff(2); + externalTurnedOn[2] = 0; + } } else { config.device.buzzer_gpio = config.device.buzzer_gpio ? config.device.buzzer_gpio @@ -163,17 +197,53 @@ ProcessMessage ExternalNotificationModule::handleReceived(const MeshPacket &mp) if (getFrom(&mp) != nodeDB.getNodeNum()) { - // TODO: This may be a problem if messages are sent in unicide, but I'm not sure if it will. - // Need to know if and how this could be a problem. + // Check if the message contains a bell character. Don't do this loop for every pin, just once. + auto &p = mp.decoded; + bool containsBell = false; + for (int i = 0; i < p.payload.size; i++) { + if (p.payload.bytes[i] == ASCII_BELL) { + containsBell = true; + } + } + if (moduleConfig.external_notification.alert_bell) { - auto &p = mp.decoded; - DEBUG_MSG("externalNotificationModule - Notification Bell\n"); - for (int i = 0; i < p.payload.size; i++) { - if (p.payload.bytes[i] == ASCII_BELL) { - if (!moduleConfig.external_notification.use_pwm) { - setExternalOn(); + if (containsBell) { + DEBUG_MSG("externalNotificationModule - Notification Bell\n"); + if (!moduleConfig.external_notification.use_pwm) { + setExternalOn(0); + if (moduleConfig.external_notification.nag_timeout) { + nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; } else { - playBeep(); + nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms; + } + // run_once now + } else { + playBeep(); + } + } + } + + if (!moduleConfig.external_notification.use_pwm) { + if (moduleConfig.external_notification.alert_bell_vibra) { + if (containsBell) { + DEBUG_MSG("externalNotificationModule - Notification Bell (Vibra)\n"); + setExternalOn(1); + if (moduleConfig.external_notification.nag_timeout) { + nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; + } else { + nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms; + } + } + } + + if (moduleConfig.external_notification.alert_bell_buzzer) { + if (containsBell) { + DEBUG_MSG("externalNotificationModule - Notification Bell (Buzzer)\n"); + setExternalOn(2); + if (moduleConfig.external_notification.nag_timeout) { + nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; + } else { + nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms; } } } @@ -182,11 +252,39 @@ ProcessMessage ExternalNotificationModule::handleReceived(const MeshPacket &mp) if (moduleConfig.external_notification.alert_message) { DEBUG_MSG("externalNotificationModule - Notification Module\n"); if (!moduleConfig.external_notification.use_pwm) { - setExternalOn(); + setExternalOn(0); + if (moduleConfig.external_notification.nag_timeout) { + nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; + } else { + nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms; + } } else { playBeep(); } } + + if (!moduleConfig.external_notification.use_pwm) { + if (moduleConfig.external_notification.alert_message_vibra) { + DEBUG_MSG("externalNotificationModule - Notification Module (Vibra)\n"); + setExternalOn(1); + if (moduleConfig.external_notification.nag_timeout) { + nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; + } else { + nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms; + } + } + + if (moduleConfig.external_notification.alert_message_buzzer) { + DEBUG_MSG("externalNotificationModule - Notification Module (Buzzer)\n"); + setExternalOn(2); + if (moduleConfig.external_notification.nag_timeout) { + nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; + } else { + nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms; + } + } + } + setIntervalFromNow(0); // run once so we know if we should do something } } else { diff --git a/src/modules/ExternalNotificationModule.h b/src/modules/ExternalNotificationModule.h index b5ab64b3c..1b1aeff21 100644 --- a/src/modules/ExternalNotificationModule.h +++ b/src/modules/ExternalNotificationModule.h @@ -17,18 +17,19 @@ class ExternalNotificationModule : public SinglePortModule, private concurrency: public: ExternalNotificationModule(); - void setExternalOn(); - void setExternalOff(); - void getExternal(); + uint32_t nagCycleCutoff = UINT32_MAX; + + void setExternalOn(uint8_t index = 0); + void setExternalOff(uint8_t index = 0); + bool getExternal(uint8_t index = 0); protected: - // virtual MeshPacket *allocReply(); - /** Called to handle a particular incoming message - @return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for it */ virtual ProcessMessage handleReceived(const MeshPacket &mp) override; virtual int32_t runOnce() override; }; + +extern ExternalNotificationModule *externalNotificationModule; diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index ece160ced..9978f2487 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -69,7 +69,7 @@ void setupModules() #ifdef ARCH_ESP32 // Only run on an esp32 based device. audioModule = new AudioModule(); - new ExternalNotificationModule(); + externalNotificationModule = new ExternalNotificationModule(); storeForwardModule = new StoreForwardModule();