diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index a50219eb06..faa3f4b73a 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1455,6 +1455,7 @@ void MyMesh::loop() { #endif mesh::Mesh::loop(); + _cli.loop(); if (next_flood_advert && millisHasNowPassed(next_flood_advert)) { mesh::Packet *pkt = createSelfAdvert(); @@ -1498,6 +1499,7 @@ void MyMesh::loop() { // To check if there is pending work bool MyMesh::hasPendingWork() const { + if (_cli.isPinActive()) return true; #if defined(WITH_BRIDGE) if (bridge.isRunning()) return true; // bridge needs WiFi radio, can't sleep #endif diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 53e7d69722..683d3c3023 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -994,6 +994,7 @@ bool MyMesh::saveFilter(ClientInfo* client) { void MyMesh::loop() { mesh::Mesh::loop(); + _cli.loop(); if (millisHasNowPassed(next_push) && acl.getNumClients() > 0) { // check for ACK timeouts @@ -1080,5 +1081,6 @@ void MyMesh::loop() { // To check if there is pending work bool MyMesh::hasPendingWork() const { + if (_cli.isPinActive()) return true; return _mgr->getOutboundTotal() > 0; } diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index 7bd89ab27c..97d5d0d396 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -907,6 +907,7 @@ bool SensorMesh::getGPS(uint8_t channel, float& lat, float& lon, float& alt) { void SensorMesh::loop() { mesh::Mesh::loop(); + _cli.loop(); if (next_flood_advert && millisHasNowPassed(next_flood_advert)) { mesh::Packet* pkt = createSelfAdvert(); @@ -991,5 +992,6 @@ void SensorMesh::loop() { // To check if there is pending work bool SensorMesh::hasPendingWork() const { + if (_cli.isPinActive()) return true; return _mgr->getOutboundTotal() > 0; } \ No newline at end of file diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index fc6e38d7ad..318df1b6db 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -510,11 +510,107 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re _callbacks->formatRadioStatsReply(reply); } else if (sender_timestamp == 0 && memcmp(command, "stats-core", 10) == 0 && (command[10] == 0 || command[10] == ' ')) { _callbacks->formatStatsReply(reply); +#if defined(PIN_GPIO) + } else if (memcmp(command, "pin ", 4) == 0 || strcmp(command, "pin") == 0) { + const char* sub = (command[3] == ' ') ? command + 4 : ""; + if (strcmp(sub, "on") == 0 || strcmp(sub, "1") == 0) { + pinMode(PIN_GPIO, OUTPUT); + digitalWrite(PIN_GPIO, HIGH); + _pin_state = true; + _pin_off_time = 0; + _pin_in_interval = false; + sprintf(reply, "OK - pin %d ON", PIN_GPIO); + } else if (strcmp(sub, "off") == 0 || strcmp(sub, "0") == 0) { + pinMode(PIN_GPIO, OUTPUT); + digitalWrite(PIN_GPIO, LOW); + _pin_state = false; + _pin_off_time = 0; + _pin_in_interval = false; + sprintf(reply, "OK - pin %d OFF", PIN_GPIO); + } else if (memcmp(sub, "on ", 3) == 0) { + uint32_t duration_s = atoi(sub + 3); + if (duration_s > 0) { + pinMode(PIN_GPIO, OUTPUT); + digitalWrite(PIN_GPIO, HIGH); + _pin_state = true; + _pin_off_time = millis() + duration_s * 1000UL; + _pin_in_interval = false; + sprintf(reply, "OK - pin %d ON for %us", PIN_GPIO, duration_s); + } else { + strcpy(reply, "ERR: duration must be > 0"); + } + } else if (memcmp(sub, "interval ", 9) == 0 || memcmp(sub, "interval,", 9) == 0) { + const char* p = sub + 9; + uint32_t interval_s = 0; + uint32_t duration_s = 0; + if (sscanf(p, "%u,%u", &interval_s, &duration_s) == 2 || sscanf(p, "%u %u", &interval_s, &duration_s) == 2) { + if (interval_s > 0 && duration_s > 0) { + _pin_interval_s = interval_s; + _pin_duration_s = duration_s; + _pin_in_interval = true; + pinMode(PIN_GPIO, OUTPUT); + digitalWrite(PIN_GPIO, HIGH); + _pin_state = true; + uint32_t now = millis(); + _pin_off_time = now + duration_s * 1000UL; + _pin_next_toggle_time = 0; + sprintf(reply, "OK - pin %d interval %us, duration %us", PIN_GPIO, interval_s, duration_s); + } else { + strcpy(reply, "ERR: interval and duration must be > 0"); + } + } else { + strcpy(reply, "ERR: usage pin interval ,"); + } + } else if (strlen(sub) > 0 && isdigit(sub[0])) { + uint32_t duration_s = atoi(sub); + if (duration_s > 0) { + pinMode(PIN_GPIO, OUTPUT); + digitalWrite(PIN_GPIO, HIGH); + _pin_state = true; + _pin_off_time = millis() + duration_s * 1000UL; + _pin_in_interval = false; + sprintf(reply, "OK - pin %d ON for %us", PIN_GPIO, duration_s); + } else { + strcpy(reply, "ERR: duration must be > 0"); + } + } else { + sprintf(reply, "pin %d state: %s", PIN_GPIO, _pin_state ? "HIGH" : "LOW"); + } +#else + } else if (memcmp(command, "pin", 3) == 0) { + strcpy(reply, "ERR: PIN_GPIO not defined"); +#endif } else { strcpy(reply, "Unknown command"); } } +void CommonCLI::loop() { +#if defined(PIN_GPIO) + uint32_t now = millis(); + if (_pin_state) { + if (_pin_off_time != 0 && (int32_t)(now - _pin_off_time) >= 0) { + digitalWrite(PIN_GPIO, LOW); + _pin_state = false; + _pin_off_time = 0; + if (_pin_in_interval && _pin_interval_s > 0) { + _pin_next_toggle_time = now + _pin_interval_s * 1000UL; + } + } + } else { + if (_pin_in_interval && _pin_interval_s > 0 && _pin_next_toggle_time != 0) { + if ((int32_t)(now - _pin_next_toggle_time) >= 0) { + pinMode(PIN_GPIO, OUTPUT); + digitalWrite(PIN_GPIO, HIGH); + _pin_state = true; + _pin_off_time = now + _pin_duration_s * 1000UL; + _pin_next_toggle_time = 0; + } + } + } +#endif +} + void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* reply) { const char* config = &command[4]; if (memcmp(config, "dutycycle ", 10) == 0) { diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 3e6aa90b09..777eb667cf 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -269,6 +269,13 @@ class CommonCLI { ClientACL* _acl; char tmp[PRV_KEY_SIZE*2 + 4]; + bool _pin_state = false; + uint32_t _pin_off_time = 0; + uint32_t _pin_interval_s = 0; + uint32_t _pin_duration_s = 0; + uint32_t _pin_next_toggle_time = 0; + bool _pin_in_interval = false; + mesh::RTCClock* getRTCClock() { return _rtc; } void savePrefs(); void loadPrefsInt(FILESYSTEM* _fs, const char* filename); @@ -288,4 +295,6 @@ class CommonCLI { bool savePrefs(FILESYSTEM* _fs); void handleCommand(uint32_t sender_timestamp, char* command, char* reply); uint8_t buildAdvertData(uint8_t node_type, uint8_t* app_data); + void loop(); + bool isPinActive() const { return _pin_state || _pin_in_interval; } }; diff --git a/variants/esp32c3_supermini/platformio.ini b/variants/esp32c3_supermini/platformio.ini index 2260970100..32a719a9f7 100644 --- a/variants/esp32c3_supermini/platformio.ini +++ b/variants/esp32c3_supermini/platformio.ini @@ -18,6 +18,7 @@ build_flags = -D PIN_BOARD_SCL=9 -D PIN_GPS_RX=21 -D PIN_GPS_TX=20 + -D PIN_GPIO=1 -D USE_SX1262 -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper