From 68ce17b9ebdde6b7e78f6540e8b0d88da70149b9 Mon Sep 17 00:00:00 2001 From: Liam Cottle Date: Sun, 26 Apr 2026 00:24:40 +1200 Subject: [PATCH 01/14] add FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..262a9ee4be --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: meshcore-dev From 2f9c3e0548a8163e497bfc6faf25e39a18147b73 Mon Sep 17 00:00:00 2001 From: uncle lit <43320854+LitBomb@users.noreply.github.com> Date: Sun, 26 Apr 2026 18:35:02 -0700 Subject: [PATCH 02/14] Removed links to outdated resources and links Removed links to outdated resources and links --- docs/faq.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index f82d3c9fdc..2942023186 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -111,7 +111,6 @@ Anyone is able to build anything they like on top of MeshCore without paying any - MeshCore Firmware on GitHub: [https://github.com/meshcore-dev/MeshCore](https://github.com/meshcore-dev/MeshCore) - MeshCore Companion Web App: [https://app.meshcore.nz](https://app.meshcore.nz) - MeshCore Map: [https://map.meshcore.io](https://map.meshcore.io) -- Andy Kirby's [MeshCore Intro Video](https://www.youtube.com/watch?v=t1qne8uJBAc) - Liam Cottle's [MeshCore Technical Presentation](https://www.youtube.com/watch?v=OwmkVkZQTf4) You need LoRa hardware devices to run MeshCore firmware as clients or server (repeater and room server). @@ -394,9 +393,6 @@ Another way to download map tiles is to use this Python script to get the tiles There is also a modified script that adds additional error handling and parallel downloads: -UK map tiles are available separately from Andy Kirby on his discord server: - - ### 4.8. Q: Where do the map tiles go? Once you have the tiles downloaded, copy the `\tiles` folder to the root of your T-Deck's SD card. @@ -553,7 +549,7 @@ pio run -e RAK_4631_Repeater ``` then you'll find `firmware.zip` in `.pio/build/RAK_4631_Repeater` -Andy also has a video on how to build using VS Code: +Andy also has a video on how to build using VS Code: *How to build and flash Meshcore repeater firmware | Heltec V3* *(Link referenced in the Discord post)* From 75ecc640fbd48aa23f535c0758626be5739537cf Mon Sep 17 00:00:00 2001 From: Keith Tweed Date: Sun, 26 Apr 2026 19:51:33 -0600 Subject: [PATCH 03/14] Update script link in FAQ 4.7 --- docs/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.md b/docs/faq.md index 2942023186..35f298383b 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -391,7 +391,7 @@ Another way to download map tiles is to use this Python script to get the tiles There is also a modified script that adds additional error handling and parallel downloads: - + ### 4.8. Q: Where do the map tiles go? Once you have the tiles downloaded, copy the `\tiles` folder to the root of your T-Deck's SD card. From 9d424ea1faf9b1e15d76a80216f13cb95216b6dc Mon Sep 17 00:00:00 2001 From: OhYou-0 Date: Fri, 24 Apr 2026 10:16:19 -0700 Subject: [PATCH 04/14] Add LilyGo T-ETH Elite board support --- variants/lilygo_teth_elite/TETHEliteBoard.h | 10 +++ variants/lilygo_teth_elite/platformio.ini | 99 +++++++++++++++++++++ variants/lilygo_teth_elite/target.cpp | 43 +++++++++ variants/lilygo_teth_elite/target.h | 20 +++++ 4 files changed, 172 insertions(+) create mode 100644 variants/lilygo_teth_elite/TETHEliteBoard.h create mode 100644 variants/lilygo_teth_elite/platformio.ini create mode 100644 variants/lilygo_teth_elite/target.cpp create mode 100644 variants/lilygo_teth_elite/target.h diff --git a/variants/lilygo_teth_elite/TETHEliteBoard.h b/variants/lilygo_teth_elite/TETHEliteBoard.h new file mode 100644 index 0000000000..15eb9533ef --- /dev/null +++ b/variants/lilygo_teth_elite/TETHEliteBoard.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class TETHEliteBoard : public ESP32Board { +public: + const char* getManufacturerName() const override { + return "LilyGO T-ETH Elite"; + } +}; diff --git a/variants/lilygo_teth_elite/platformio.ini b/variants/lilygo_teth_elite/platformio.ini new file mode 100644 index 0000000000..97728f8b4c --- /dev/null +++ b/variants/lilygo_teth_elite/platformio.ini @@ -0,0 +1,99 @@ +[LilyGo_TETH_Elite_sx1262] +extends = esp32_base +board = esp32s3box +board_build.partitions = default_16MB.csv +board_upload.flash_size = 16MB +build_flags = + ${esp32_base.build_flags} + -I variants/lilygo_teth_elite + -D BOARD_HAS_PSRAM + -D LILYGO_TETH_ELITE + -D LILYGO_T_ETH_ELITE_ESP32S3 + -D ARDUINO_USB_CDC_ON_BOOT=1 + -D P_LORA_DIO_1=8 + -D P_LORA_NSS=40 + -D P_LORA_RESET=46 + -D P_LORA_BUSY=16 + -D P_LORA_SCLK=10 + -D P_LORA_MISO=9 + -D P_LORA_MOSI=11 + -D P_LORA_TX_LED=38 + -D SX126X_DIO2_AS_RF_SWITCH=true + -D SX126X_DIO3_TCXO_VOLTAGE=1.8 + -D SX126X_CURRENT_LIMIT=140 + -D USE_SX1262 + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=8 + -D SX126X_RX_BOOSTED_GAIN=1 +build_src_filter = ${esp32_base.build_src_filter} + +<../variants/lilygo_teth_elite> +lib_deps = + ${esp32_base.lib_deps} + +[env:LilyGo_TETH_Elite_sx1262_repeater] +extends = LilyGo_TETH_Elite_sx1262 +build_flags = + ${LilyGo_TETH_Elite_sx1262.build_flags} + -D ADVERT_NAME='"T-ETH Elite Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TETH_Elite_sx1262.build_src_filter} + +<../examples/simple_repeater> +lib_deps = + ${LilyGo_TETH_Elite_sx1262.lib_deps} + ${esp32_ota.lib_deps} + +[env:LilyGo_TETH_Elite_sx1262_room_server] +extends = LilyGo_TETH_Elite_sx1262 +build_flags = + ${LilyGo_TETH_Elite_sx1262.build_flags} + -D ADVERT_NAME='"T-ETH Elite Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TETH_Elite_sx1262.build_src_filter} + +<../examples/simple_room_server> +lib_deps = + ${LilyGo_TETH_Elite_sx1262.lib_deps} + ${esp32_ota.lib_deps} + +[env:LilyGo_TETH_Elite_sx1262_companion_radio_usb] +extends = LilyGo_TETH_Elite_sx1262 +build_flags = + ${LilyGo_TETH_Elite_sx1262.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TETH_Elite_sx1262.build_src_filter} + +<../examples/companion_radio/*.cpp> +lib_deps = + ${LilyGo_TETH_Elite_sx1262.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:LilyGo_TETH_Elite_sx1262_companion_radio_ble] +extends = LilyGo_TETH_Elite_sx1262 +build_flags = + ${LilyGo_TETH_Elite_sx1262.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TETH_Elite_sx1262.build_src_filter} + + + +<../examples/companion_radio/*.cpp> +lib_deps = + ${LilyGo_TETH_Elite_sx1262.lib_deps} + densaugeo/base64 @ ~1.4.0 diff --git a/variants/lilygo_teth_elite/target.cpp b/variants/lilygo_teth_elite/target.cpp new file mode 100644 index 0000000000..4dc377d620 --- /dev/null +++ b/variants/lilygo_teth_elite/target.cpp @@ -0,0 +1,43 @@ +#include +#include "target.h" + +TETHEliteBoard board; + +static SPIClass spi(HSPI); +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); +WRAPPER_CLASS radio_driver(radio, board); + +ESP32RTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); +SensorManager sensors; + +#ifndef LORA_CR + #define LORA_CR 5 +#endif + +bool radio_init() { + fallback_clock.begin(); + rtc_clock.begin(Wire); + + return radio.std_init(&spi); +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); +} + +void radio_set_tx_power(int8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); +} diff --git a/variants/lilygo_teth_elite/target.h b/variants/lilygo_teth_elite/target.h new file mode 100644 index 0000000000..a842186cf6 --- /dev/null +++ b/variants/lilygo_teth_elite/target.h @@ -0,0 +1,20 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include "TETHEliteBoard.h" + +extern TETHEliteBoard board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern SensorManager sensors; + +bool radio_init(); +uint32_t radio_get_rng_seed(); +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); +void radio_set_tx_power(int8_t dbm); +mesh::LocalIdentity radio_new_identity(); From 66638cbb7355752840ad7fc8c0f92a0c12a114d7 Mon Sep 17 00:00:00 2001 From: AI7NC <77077873+AI7NC@users.noreply.github.com> Date: Tue, 12 May 2026 12:42:33 -0700 Subject: [PATCH 05/14] Update cli_commands.md to include 'ver' Include the 'ver' command for retrieving the firmware version --- docs/cli_commands.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 8d4ae0f925..14c3c705c7 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -391,6 +391,11 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### View this node's firmware version +**Usage:** `ver` + +--- + #### View this node's configured role **Usage:** `get role` From 23da5bd1a661b2b9da5cec2603e2a8d37df47a3d Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Mon, 11 May 2026 17:24:09 -0600 Subject: [PATCH 06/14] fix(mesh): widen TRACE offset to uint16 to avoid narrowing --- src/Mesh.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 7252974a92..87ad61af2e 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -50,7 +50,9 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { uint8_t path_sz = flags & 0x03; // NEW v1.11+: lower 2 bits is path hash size uint8_t len = pkt->payload_len - i; - uint8_t offset = pkt->path_len << path_sz; + // path_len*entry_size can exceed 255 (path_len up to 63, entry_size up to 8); + // a uint8_t offset would wrap and steer the isHashMatch() read to the wrong place. + uint16_t offset = (uint16_t)pkt->path_len << path_sz; if (offset >= len) { // TRACE has reached end of given path onTraceRecv(pkt, trace_tag, auth_code, flags, pkt->path, &pkt->payload[i], len); } else if (self_id.isHashMatch(&pkt->payload[i + offset], 1 << path_sz) && allowPacketForward(pkt) && !_tables->hasSeen(pkt)) { From 1978b95d6b4e9f6fff6515ebff7f3d56149b26e7 Mon Sep 17 00:00:00 2001 From: Rastislav Vysoky Date: Fri, 15 May 2026 16:42:29 +0200 Subject: [PATCH 07/14] Change MeshCore intro video link to The Comms Channel's MC intro playlist --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b14aff344b..af56aaf33b 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ MeshCore provides the ability to create wireless mesh networks, similar to Mesht ## 🚀 How to Get Started -- Watch the [MeshCore Intro Video](https://www.youtube.com/watch?v=t1qne8uJBAc) by Andy Kirby. +- Watch the [MeshCore QuickStart Playlist](https://www.youtube.com/watch?v=iaFltojJrAc&list=PLshzThxhw4O4WU_iZo3NmNZOv6KMrUuF9) by The Comms Channel - Watch the [MeshCore Technical Presentation](https://www.youtube.com/watch?v=OwmkVkZQTf4) by Liam Cottle. - Read through our [Frequently Asked Questions](./docs/faq.md) and [Documentation](https://docs.meshcore.io). - Flash the MeshCore firmware on a supported device. From 35422b9446783e9a74374e1d761ac66b5a4b6d62 Mon Sep 17 00:00:00 2001 From: Sefinek Date: Thu, 28 May 2026 16:01:59 +0200 Subject: [PATCH 08/14] ci: update GitHub Actions and Python version, fix ruby-version typo --- .github/actions/setup-build-environment/action.yml | 6 +++--- .github/workflows/build-companion-firmwares.yml | 6 +++--- .github/workflows/build-repeater-firmwares.yml | 6 +++--- .github/workflows/build-room-server-firmwares.yml | 6 +++--- .github/workflows/github-pages.yml | 8 ++++---- .github/workflows/pr-build-check.yml | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/actions/setup-build-environment/action.yml b/.github/actions/setup-build-environment/action.yml index 2ba7617e9f..02aaf4249b 100644 --- a/.github/actions/setup-build-environment/action.yml +++ b/.github/actions/setup-build-environment/action.yml @@ -4,7 +4,7 @@ runs: steps: - name: Init Cache - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: | ~/.cache/pip @@ -12,9 +12,9 @@ runs: key: ${{ runner.os }}-pio - name: Install Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: - python-version: '3.11' + python-version: '3.13' - name: Install PlatformIO shell: bash diff --git a/.github/workflows/build-companion-firmwares.yml b/.github/workflows/build-companion-firmwares.yml index 721076a175..771fa6d5bc 100644 --- a/.github/workflows/build-companion-firmwares.yml +++ b/.github/workflows/build-companion-firmwares.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Clone Repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Build Environment uses: ./.github/actions/setup-build-environment @@ -27,13 +27,13 @@ jobs: run: /usr/bin/env bash build.sh build-companion-firmwares - name: Upload Workflow Artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: companion-firmwares path: out - name: Create Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@v3 if: startsWith(github.ref, 'refs/tags/') with: name: Companion Firmware ${{ env.GIT_TAG_VERSION }} diff --git a/.github/workflows/build-repeater-firmwares.yml b/.github/workflows/build-repeater-firmwares.yml index f12bd82949..3185d4b21c 100644 --- a/.github/workflows/build-repeater-firmwares.yml +++ b/.github/workflows/build-repeater-firmwares.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Clone Repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Build Environment uses: ./.github/actions/setup-build-environment @@ -27,13 +27,13 @@ jobs: run: /usr/bin/env bash build.sh build-repeater-firmwares - name: Upload Workflow Artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: repeater-firmwares path: out - name: Create Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@v3 if: startsWith(github.ref, 'refs/tags/') with: name: Repeater Firmware ${{ env.GIT_TAG_VERSION }} diff --git a/.github/workflows/build-room-server-firmwares.yml b/.github/workflows/build-room-server-firmwares.yml index a488af6a1c..127095a8df 100644 --- a/.github/workflows/build-room-server-firmwares.yml +++ b/.github/workflows/build-room-server-firmwares.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Clone Repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Build Environment uses: ./.github/actions/setup-build-environment @@ -27,13 +27,13 @@ jobs: run: /usr/bin/env bash build.sh build-room-server-firmwares - name: Upload Workflow Artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: room-server-firmwares path: out - name: Create Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@v3 if: startsWith(github.ref, 'refs/tags/') with: name: Room Server Firmware ${{ env.GIT_TAG_VERSION }} diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 5fd2734bfd..9aa5fc0b45 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -15,12 +15,12 @@ jobs: steps: - name: Checkout Repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: - ruby-version: 3.x + python-version: '3.13' - name: Build run: | @@ -28,7 +28,7 @@ jobs: mkdocs build - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} cname: docs.meshcore.io diff --git a/.github/workflows/pr-build-check.yml b/.github/workflows/pr-build-check.yml index 67468b5691..84fbc19598 100644 --- a/.github/workflows/pr-build-check.yml +++ b/.github/workflows/pr-build-check.yml @@ -46,7 +46,7 @@ jobs: steps: - name: Clone Repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Build Environment uses: ./.github/actions/setup-build-environment From ace6acd6dd93d3dbb6f2b806b0d602619916d9d2 Mon Sep 17 00:00:00 2001 From: Sefinek Date: Thu, 28 May 2026 16:10:46 +0200 Subject: [PATCH 09/14] ci: pin peaceiris/actions-gh-pages to v4.1.0 for Node.js 24 support --- .github/workflows/github-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 9aa5fc0b45..b01ddc939c 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -28,7 +28,7 @@ jobs: mkdocs build - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v4 + uses: peaceiris/actions-gh-pages@v4.1.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} cname: docs.meshcore.io From 061c231756c2c823f767ef11ab1bee977722a345 Mon Sep 17 00:00:00 2001 From: Che177 Date: Tue, 2 Jun 2026 23:39:53 -0700 Subject: [PATCH 10/14] room_server: add optional system posts from local contact. GPIO example based on lilygo_tlora_v2_1 variant --- examples/simple_room_server/MyMesh.cpp | 37 +++++++++++-- examples/simple_room_server/MyMesh.h | 2 + examples/simple_room_server/main.cpp | 64 +++++++++++++++++++++++ variants/lilygo_tlora_v2_1/platformio.ini | 18 +++++++ 4 files changed, 118 insertions(+), 3 deletions(-) diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index e3710d27cd..c08aecaf38 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -39,18 +39,40 @@ struct ServerStats { }; void MyMesh::addPost(ClientInfo *client, const char *postData) { + storePost(client->id, postData); +} + +void MyMesh::addSystemPost(const char *postData) { +#if defined(ENABLE_ROOM_POST_DEBUG) && ENABLE_ROOM_POST_DEBUG == 1 + Serial.print("room.post: addSystemPost: "); Serial.println(postData); +#endif + storePost(self_id, postData); +} + +void MyMesh::storePost(const mesh::Identity &author, const char *postData) { + int idx = next_post_idx; // TODO: suggested postData format: /<descrption> - posts[next_post_idx].author = client->id; // add to cyclic queue - StrHelper::strncpy(posts[next_post_idx].text, postData, MAX_POST_TEXT_LEN); + posts[idx].author = author; // add to cyclic queue + StrHelper::strncpy(posts[idx].text, postData, MAX_POST_TEXT_LEN); - posts[next_post_idx].post_timestamp = getRTCClock()->getCurrentTimeUnique(); + posts[idx].post_timestamp = getRTCClock()->getCurrentTimeUnique(); +#if defined(ENABLE_ROOM_POST_DEBUG) && ENABLE_ROOM_POST_DEBUG == 1 + Serial.printf("room.post: storePost idx=%d text=%s\n", idx, posts[idx].text); + Serial.printf("room.post: timestamp=%u\n", posts[idx].post_timestamp); +#endif next_post_idx = (next_post_idx + 1) % MAX_UNSYNCED_POSTS; next_push = futureMillis(PUSH_NOTIFY_DELAY_MILLIS); _num_posted++; // stats +#if defined(ENABLE_ROOM_POST_DEBUG) && ENABLE_ROOM_POST_DEBUG == 1 + Serial.printf("room.post: next_post_idx=%d num_posted=%d push scheduled\n", next_post_idx, _num_posted); +#endif } void MyMesh::pushPostToClient(ClientInfo *client, PostInfo &post) { +#if defined(ENABLE_ROOM_POST_DEBUG) && ENABLE_ROOM_POST_DEBUG == 1 + Serial.print("room.post: pushPostToClient text="); Serial.println(post.text); +#endif int len = 0; memcpy(&reply_data[len], &post.post_timestamp, 4); len += 4; // this is a PAST timestamp... but should be accepted by client @@ -932,6 +954,15 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply Serial.printf("\n"); } reply[0] = 0; + } else if (strncmp(command, "room.post", 9) == 0) { + char* msg = command + 9; + while (*msg == ' ') msg++; + if (*msg == 0) { + snprintf(reply, MAX_POST_TEXT_LEN, "ERR empty message"); + } else { + addSystemPost(msg); + snprintf(reply, MAX_POST_TEXT_LEN, "OK"); + } } else{ _cli.handleCommand(sender_timestamp, command, reply); // common CLI commands } diff --git a/examples/simple_room_server/MyMesh.h b/examples/simple_room_server/MyMesh.h index 1b35ae95a1..6fbb1f7a15 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -119,6 +119,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { int matching_peer_indexes[MAX_CLIENTS]; void addPost(ClientInfo* client, const char* postData); + void storePost(const mesh::Identity& author, const char* postData); void pushPostToClient(ClientInfo* client, PostInfo& post); uint8_t getUnsyncedCount(ClientInfo* client); bool processAck(const uint8_t *data); @@ -173,6 +174,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { MyMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables); void begin(FILESYSTEM* fs); + void addSystemPost(const char* postData); const char* getFirmwareVer() override { return FIRMWARE_VERSION; } const char* getBuildDate() override { return FIRMWARE_BUILD_DATE; } diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index a3798b2175..b08f02cef4 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -16,6 +16,31 @@ void halt() { while (1) ; } +#ifndef GPIO_CONTACT_ACTIVE_LOW +#define GPIO_CONTACT_ACTIVE_LOW 1 +#endif +#ifndef GPIO_CONTACT_COOLDOWN_MS +#define GPIO_CONTACT_COOLDOWN_MS 5000 +#endif +#ifndef GPIO_CONTACT_OPEN_TEXT +#define GPIO_CONTACT_OPEN_TEXT "GPIO contact opened" +#endif +#ifndef GPIO_CONTACT_CLOSED_TEXT +#define GPIO_CONTACT_CLOSED_TEXT "GPIO contact closed" +#endif + +#if defined(ENABLE_GPIO_CONTACT_INPUT) && ENABLE_GPIO_CONTACT_INPUT == 1 + #ifndef GPIO_CONTACT_PIN + #error "GPIO_CONTACT_PIN must be defined when ENABLE_GPIO_CONTACT_INPUT=1" + #endif +static bool gpio_contact_state = false; +static int gpio_contact_last_raw = HIGH; +static unsigned long gpio_contact_debounce_until = 0; +static unsigned long gpio_contact_last_change = 0; +static bool gpio_contact_initialized = false; +static const unsigned long GPIO_CONTACT_DEBOUNCE_MS = 50; +#endif + static char command[MAX_POST_TEXT_LEN+1]; void setup() { @@ -24,6 +49,19 @@ void setup() { board.begin(); +#if defined(ENABLE_GPIO_CONTACT_INPUT) && ENABLE_GPIO_CONTACT_INPUT == 1 + pinMode(GPIO_CONTACT_PIN, INPUT_PULLUP); + int raw = digitalRead(GPIO_CONTACT_PIN); + gpio_contact_last_raw = raw; + gpio_contact_state = (raw == (GPIO_CONTACT_ACTIVE_LOW ? LOW : HIGH)); + gpio_contact_debounce_until = millis() + GPIO_CONTACT_DEBOUNCE_MS; + gpio_contact_last_change = millis(); + gpio_contact_initialized = true; + #if defined(ENABLE_GPIO_CONTACT_DEBUG) && ENABLE_GPIO_CONTACT_DEBUG == 1 + Serial.printf("GPIO contact enabled: pin=%d active_low=%d initial_raw=%d initial_state=%s\n", (int)GPIO_CONTACT_PIN, (int)GPIO_CONTACT_ACTIVE_LOW, raw==LOW?0:1, gpio_contact_state?"closed":"opened"); + #endif +#endif + #ifdef DISPLAY_CLASS if (display.begin()) { display.startFrame(); @@ -101,6 +139,7 @@ void loop() { if (len > 0 && command[len - 1] == '\r') { // received complete line command[len - 1] = 0; // replace newline with C string null terminator char reply[160]; + memset(reply, 0, sizeof(reply)); the_mesh.handleCommand(0, command, reply); // NOTE: there is no sender_timestamp via serial! if (reply[0]) { Serial.print(" -> "); Serial.println(reply); @@ -109,6 +148,31 @@ void loop() { command[0] = 0; // reset command buffer } +#if defined(ENABLE_GPIO_CONTACT_INPUT) && ENABLE_GPIO_CONTACT_INPUT == 1 + int gpio_raw = digitalRead(GPIO_CONTACT_PIN); + bool gpio_active = (gpio_raw == (GPIO_CONTACT_ACTIVE_LOW ? LOW : HIGH)); + + if (gpio_raw != gpio_contact_last_raw) { + gpio_contact_last_raw = gpio_raw; + gpio_contact_debounce_until = millis() + GPIO_CONTACT_DEBOUNCE_MS; + #if defined(ENABLE_GPIO_CONTACT_DEBUG) && ENABLE_GPIO_CONTACT_DEBUG == 1 + Serial.printf("GPIO contact raw changed: pin=%d raw=%d active=%d\n", (int)GPIO_CONTACT_PIN, gpio_raw==LOW?0:1, gpio_active?1:0); + #endif + } + + if (gpio_contact_initialized && millis() >= gpio_contact_debounce_until && + gpio_active != gpio_contact_state && + millis() - gpio_contact_last_change >= GPIO_CONTACT_COOLDOWN_MS) { + gpio_contact_state = gpio_active; + gpio_contact_last_change = millis(); + #if defined(ENABLE_GPIO_CONTACT_DEBUG) && ENABLE_GPIO_CONTACT_DEBUG == 1 + const char* msg = (gpio_contact_state ? GPIO_CONTACT_CLOSED_TEXT : GPIO_CONTACT_OPEN_TEXT); + Serial.print("GPIO contact posting: "); Serial.println(msg); + #endif + the_mesh.addSystemPost(gpio_contact_state ? GPIO_CONTACT_CLOSED_TEXT : GPIO_CONTACT_OPEN_TEXT); + } +#endif + the_mesh.loop(); sensors.loop(); #ifdef DISPLAY_CLASS diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index 3673166861..928eda2670 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -120,6 +120,24 @@ build_flags = -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' -D ROOM_PASSWORD='"hello"' +; Debug local room.post path + -D ENABLE_ROOM_POST_DEBUG=1 +; Diagnostic GPIO contact input. + ; GPIO25 is the LED/TX LED pin, so this is NOT ideal for final wiring, + ; but it is useful for testing because it visibly changes and does not reset LoRa. + ; Wiring/test: GPIO25 -> GND + -D ENABLE_GPIO_CONTACT_INPUT=1 + -D ENABLE_GPIO_CONTACT_DEBUG=1 + -D GPIO_CONTACT_PIN=25 + -D GPIO_CONTACT_ACTIVE_LOW=1 + -D GPIO_CONTACT_COOLDOWN_MS=3000 + ; Optional custom post text. + ; Keep commented until the basic GPIO build is confirmed. + ; Defaults in code are: + ; "GPIO contact opened" + ; "GPIO contact closed" + -D GPIO_CONTACT_OPEN_TEXT='"Door opened"' + -D GPIO_CONTACT_CLOSED_TEXT='"Door closed"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = From a3186a5eaa3619d38c400f1b0a40a441b75925c8 Mon Sep 17 00:00:00 2001 From: Che177 <github@kitagor.com> Date: Thu, 4 Jun 2026 22:39:54 -0700 Subject: [PATCH 11/14] config cleanup ini sections moved to variants-specific platformio.ini file duplicate init code removed from main.cpp --- examples/simple_room_server/main.cpp | 27 ++++++++++---- platformio.ini | 43 ++++++++++------------- variants/lilygo_tlora_v2_1/platformio.ini | 38 ++++++++++---------- 3 files changed, 59 insertions(+), 49 deletions(-) diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index b08f02cef4..1a896c4d01 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -16,6 +16,12 @@ void halt() { while (1) ; } +#ifndef ENABLE_GPIO_CONTACT_INPUT +#define ENABLE_GPIO_CONTACT_INPUT 0 +#endif +#ifndef ENABLE_GPIO_CONTACT_DEBUG +#define ENABLE_GPIO_CONTACT_DEBUG 0 +#endif #ifndef GPIO_CONTACT_ACTIVE_LOW #define GPIO_CONTACT_ACTIVE_LOW 1 #endif @@ -137,7 +143,7 @@ void loop() { } if (len > 0 && command[len - 1] == '\r') { // received complete line - command[len - 1] = 0; // replace newline with C string null terminator + command[len - 1] = 0; // replace carriage return with C string null terminator char reply[160]; memset(reply, 0, sizeof(reply)); the_mesh.handleCommand(0, command, reply); // NOTE: there is no sender_timestamp via serial! @@ -155,21 +161,30 @@ void loop() { if (gpio_raw != gpio_contact_last_raw) { gpio_contact_last_raw = gpio_raw; gpio_contact_debounce_until = millis() + GPIO_CONTACT_DEBOUNCE_MS; + #if defined(ENABLE_GPIO_CONTACT_DEBUG) && ENABLE_GPIO_CONTACT_DEBUG == 1 - Serial.printf("GPIO contact raw changed: pin=%d raw=%d active=%d\n", (int)GPIO_CONTACT_PIN, gpio_raw==LOW?0:1, gpio_active?1:0); + Serial.printf("GPIO contact raw changed: pin=%d raw=%d active=%d\n", + (int)GPIO_CONTACT_PIN, + gpio_raw == LOW ? 0 : 1, + gpio_active ? 1 : 0); #endif } - if (gpio_contact_initialized && millis() >= gpio_contact_debounce_until && + if (gpio_contact_initialized && + (long)(millis() - gpio_contact_debounce_until) >= 0 && gpio_active != gpio_contact_state && millis() - gpio_contact_last_change >= GPIO_CONTACT_COOLDOWN_MS) { gpio_contact_state = gpio_active; gpio_contact_last_change = millis(); + + const char* msg = gpio_contact_state ? GPIO_CONTACT_CLOSED_TEXT : GPIO_CONTACT_OPEN_TEXT; + #if defined(ENABLE_GPIO_CONTACT_DEBUG) && ENABLE_GPIO_CONTACT_DEBUG == 1 - const char* msg = (gpio_contact_state ? GPIO_CONTACT_CLOSED_TEXT : GPIO_CONTACT_OPEN_TEXT); - Serial.print("GPIO contact posting: "); Serial.println(msg); + Serial.print("GPIO contact posting: "); + Serial.println(msg); #endif - the_mesh.addSystemPost(gpio_contact_state ? GPIO_CONTACT_CLOSED_TEXT : GPIO_CONTACT_OPEN_TEXT); + + the_mesh.addSystemPost(msg); } #endif diff --git a/platformio.ini b/platformio.ini index e16f7b8304..1919b4f978 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,8 +10,8 @@ [platformio] extra_configs = - variants/*/platformio.ini - platformio.local.ini + variants/*/platformio.ini + platformio.local.ini [arduino_base] framework = arduino @@ -81,25 +81,24 @@ platform = https://github.com/pioarduino/platform-espressif32/releases/download/ extends = arduino_base platform = nordicnrf52 platform_packages = - ; use internal fork that includes patch to ble stack to prevent firmware lockup during rapid connect/disconnect - ; https://github.com/meshcore-dev/MeshCore/pull/1177 - ; https://github.com/meshcore-dev/MeshCore/pull/1295 - framework-arduinoadafruitnrf52 @ https://github.com/meshcore-dev/Adafruit_nRF52_Arduino#d541301 -extra_scripts = create-uf2.py + framework-arduinoadafruitnrf52 @ 1.10700.0 +extra_scripts = + create-uf2.py + arch/nrf52/extra_scripts/patch_bluefruit.py build_flags = ${arduino_base.build_flags} -D NRF52_PLATFORM -D LFS_NO_ASSERT=1 -D EXTRAFS=1 lib_deps = ${arduino_base.lib_deps} - https://github.com/oltaco/CustomLFS#0.2.2 + https://github.com/oltaco/CustomLFS @ 0.2.1 ; ----------------- RP2040 --------------------- [rp2040_base] extends = arduino_base upload_protocol = picotool board_build.core = earlephilhower -platform = https://github.com/maxgerhardt/platform-raspberrypi.git#4e22a0d ; framework-arduinopico @ 1.50600.0+sha.6a1d13e9 +platform = https://github.com/maxgerhardt/platform-raspberrypi.git build_flags = ${arduino_base.build_flags} -D RP2040_PLATFORM @@ -107,17 +106,17 @@ build_flags = ${arduino_base.build_flags} [stm32_base] extends = arduino_base -platform = ststm32 +platform = platformio/ststm32@19.1.0 +platform_packages = platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32/archive/2.10.1.zip extra_scripts = post:arch/stm32/build_hex.py build_flags = ${arduino_base.build_flags} -D STM32_PLATFORM -I src/helpers/stm32 - -I $PROJECT_PACKAGES_DIR/framework-arduinoststm32/libraries/SubGhz/src build_src_filter = ${arduino_base.build_src_filter} +<helpers/stm32> lib_deps = ${arduino_base.lib_deps} file://arch/stm32/Adafruit_LittleFS_stm32 - SubGhz + adafruit/Adafruit BusIO @ 1.17.2 [sensor_base] build_flags = @@ -153,16 +152,10 @@ lib_deps = adafruit/Adafruit BME680 Library @ ^2.0.4 adafruit/Adafruit BMP085 Library @ ^1.2.4 -; ----------------- TESTING --------------------- - -[env:native] -platform = native -build_flags = -std=c++17 - -I src - -I test/mocks -test_build_src = yes -build_src_filter = - -<*> - +<../src/Utils.cpp> -lib_deps = - google/googletest @ 1.17.0 +[room_server_system_posts] +build_flags = + -D ENABLE_ROOM_POST_DEBUG=0 + -D ENABLE_GPIO_CONTACT_INPUT=0 + -D ENABLE_GPIO_CONTACT_DEBUG=0 + -D GPIO_CONTACT_ACTIVE_LOW=1 + -D GPIO_CONTACT_COOLDOWN_MS=5000 \ No newline at end of file diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index 928eda2670..14aebdecd6 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -120,26 +120,28 @@ build_flags = -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' -D ROOM_PASSWORD='"hello"' -; Debug local room.post path - -D ENABLE_ROOM_POST_DEBUG=1 -; Diagnostic GPIO contact input. - ; GPIO25 is the LED/TX LED pin, so this is NOT ideal for final wiring, - ; but it is useful for testing because it visibly changes and does not reset LoRa. - ; Wiring/test: GPIO25 -> GND - -D ENABLE_GPIO_CONTACT_INPUT=1 - -D ENABLE_GPIO_CONTACT_DEBUG=1 - -D GPIO_CONTACT_PIN=25 - -D GPIO_CONTACT_ACTIVE_LOW=1 - -D GPIO_CONTACT_COOLDOWN_MS=3000 - ; Optional custom post text. - ; Keep commented until the basic GPIO build is confirmed. - ; Defaults in code are: - ; "GPIO contact opened" - ; "GPIO contact closed" - -D GPIO_CONTACT_OPEN_TEXT='"Door opened"' - -D GPIO_CONTACT_CLOSED_TEXT='"Door closed"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 + +; Optional debug logging for room post development. +; Keep disabled for normal builds. +; -D ENABLE_ROOM_POST_DEBUG=1 + +; Optional local contact input example. +; Select a safe GPIO for your board before enabling. +; GPIO25 is also the TX LED pin on this variant; useful for testing only. +; Wiring/test: with GPIO_CONTACT_ACTIVE_LOW=1, connect GPIO25 to GND to report the contact as closed/active. +; -D ENABLE_GPIO_CONTACT_INPUT=1 +; -D GPIO_CONTACT_PIN=25 +; -D GPIO_CONTACT_ACTIVE_LOW=1 +; -D GPIO_CONTACT_COOLDOWN_MS=5000 +; -D GPIO_CONTACT_OPEN_TEXT='"Door closed"' +; -D GPIO_CONTACT_CLOSED_TEXT='"Door opened"' + +; Optional debug logging for GPIO contact development. +; Keep disabled for normal builds. +; -D ENABLE_GPIO_CONTACT_DEBUG=1 + lib_deps = ${LilyGo_TLora_V2_1_1_6.lib_deps} ${esp32_ota.lib_deps} From f861692bc396b32ed8060a21cc41b19d2e418a36 Mon Sep 17 00:00:00 2001 From: Che177 <github@kitagor.com> Date: Thu, 4 Jun 2026 22:52:51 -0700 Subject: [PATCH 12/14] room_server: rename GPIO contact state flag for clarity --- examples/simple_room_server/main.cpp | 19 ++++++++++++------- variants/lilygo_tlora_v2_1/platformio.ini | 6 +++--- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 1a896c4d01..ba50126e5a 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -39,7 +39,7 @@ void halt() { #ifndef GPIO_CONTACT_PIN #error "GPIO_CONTACT_PIN must be defined when ENABLE_GPIO_CONTACT_INPUT=1" #endif -static bool gpio_contact_state = false; +static bool gpio_contact_active = false; static int gpio_contact_last_raw = HIGH; static unsigned long gpio_contact_debounce_until = 0; static unsigned long gpio_contact_last_change = 0; @@ -59,12 +59,17 @@ void setup() { pinMode(GPIO_CONTACT_PIN, INPUT_PULLUP); int raw = digitalRead(GPIO_CONTACT_PIN); gpio_contact_last_raw = raw; - gpio_contact_state = (raw == (GPIO_CONTACT_ACTIVE_LOW ? LOW : HIGH)); + gpio_contact_active = (raw == (GPIO_CONTACT_ACTIVE_LOW ? LOW : HIGH)); gpio_contact_debounce_until = millis() + GPIO_CONTACT_DEBOUNCE_MS; gpio_contact_last_change = millis(); gpio_contact_initialized = true; + #if defined(ENABLE_GPIO_CONTACT_DEBUG) && ENABLE_GPIO_CONTACT_DEBUG == 1 - Serial.printf("GPIO contact enabled: pin=%d active_low=%d initial_raw=%d initial_state=%s\n", (int)GPIO_CONTACT_PIN, (int)GPIO_CONTACT_ACTIVE_LOW, raw==LOW?0:1, gpio_contact_state?"closed":"opened"); + Serial.printf("GPIO contact enabled: pin=%d active_low=%d initial_raw=%d initial_state=%s\n", + (int)GPIO_CONTACT_PIN, + (int)GPIO_CONTACT_ACTIVE_LOW, + raw == LOW ? 0 : 1, + gpio_contact_active ? "closed" : "opened"); #endif #endif @@ -172,13 +177,13 @@ void loop() { if (gpio_contact_initialized && (long)(millis() - gpio_contact_debounce_until) >= 0 && - gpio_active != gpio_contact_state && + gpio_active != gpio_contact_active && millis() - gpio_contact_last_change >= GPIO_CONTACT_COOLDOWN_MS) { - gpio_contact_state = gpio_active; + gpio_contact_active = gpio_active; gpio_contact_last_change = millis(); - const char* msg = gpio_contact_state ? GPIO_CONTACT_CLOSED_TEXT : GPIO_CONTACT_OPEN_TEXT; - + const char* msg = gpio_contact_active ? GPIO_CONTACT_CLOSED_TEXT : GPIO_CONTACT_OPEN_TEXT; + #if defined(ENABLE_GPIO_CONTACT_DEBUG) && ENABLE_GPIO_CONTACT_DEBUG == 1 Serial.print("GPIO contact posting: "); Serial.println(msg); diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index 14aebdecd6..384d4f5386 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -130,13 +130,13 @@ build_flags = ; Optional local contact input example. ; Select a safe GPIO for your board before enabling. ; GPIO25 is also the TX LED pin on this variant; useful for testing only. -; Wiring/test: with GPIO_CONTACT_ACTIVE_LOW=1, connect GPIO25 to GND to report the contact as closed/active. +; Wiring/test: with GPIO_CONTACT_ACTIVE_LOW=1, connect GPIO25 to GND to report the contact input as closed/active. ; -D ENABLE_GPIO_CONTACT_INPUT=1 ; -D GPIO_CONTACT_PIN=25 ; -D GPIO_CONTACT_ACTIVE_LOW=1 ; -D GPIO_CONTACT_COOLDOWN_MS=5000 -; -D GPIO_CONTACT_OPEN_TEXT='"Door closed"' -; -D GPIO_CONTACT_CLOSED_TEXT='"Door opened"' +; -D GPIO_CONTACT_OPEN_TEXT='"GPIO contact opened"' +; -D GPIO_CONTACT_CLOSED_TEXT='"GPIO contact closed"' ; Optional debug logging for GPIO contact development. ; Keep disabled for normal builds. From aff765181192b506a66dad19819a141ed5993ed0 Mon Sep 17 00:00:00 2001 From: Che177 <github@kitagor.com> Date: Thu, 4 Jun 2026 22:56:10 -0700 Subject: [PATCH 13/14] room_server: clean up GPIO contact handling --- examples/simple_room_server/main.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index ba50126e5a..184bb6c68c 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -19,18 +19,23 @@ void halt() { #ifndef ENABLE_GPIO_CONTACT_INPUT #define ENABLE_GPIO_CONTACT_INPUT 0 #endif + #ifndef ENABLE_GPIO_CONTACT_DEBUG #define ENABLE_GPIO_CONTACT_DEBUG 0 #endif + #ifndef GPIO_CONTACT_ACTIVE_LOW #define GPIO_CONTACT_ACTIVE_LOW 1 #endif + #ifndef GPIO_CONTACT_COOLDOWN_MS #define GPIO_CONTACT_COOLDOWN_MS 5000 #endif + #ifndef GPIO_CONTACT_OPEN_TEXT #define GPIO_CONTACT_OPEN_TEXT "GPIO contact opened" #endif + #ifndef GPIO_CONTACT_CLOSED_TEXT #define GPIO_CONTACT_CLOSED_TEXT "GPIO contact closed" #endif @@ -183,9 +188,8 @@ void loop() { gpio_contact_last_change = millis(); const char* msg = gpio_contact_active ? GPIO_CONTACT_CLOSED_TEXT : GPIO_CONTACT_OPEN_TEXT; - + #if defined(ENABLE_GPIO_CONTACT_DEBUG) && ENABLE_GPIO_CONTACT_DEBUG == 1 - Serial.print("GPIO contact posting: "); Serial.println(msg); #endif From 435e9841adaadfc42eaf1fcf13f939377da1c15d Mon Sep 17 00:00:00 2001 From: Che177 <github@kitagor.com> Date: Thu, 4 Jun 2026 23:01:30 -0700 Subject: [PATCH 14/14] room_server: make system post helper defensive --- examples/simple_room_server/MyMesh.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index c08aecaf38..d5598e5224 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -43,9 +43,12 @@ void MyMesh::addPost(ClientInfo *client, const char *postData) { } void MyMesh::addSystemPost(const char *postData) { + if (!postData || postData[0] == 0) return; + #if defined(ENABLE_ROOM_POST_DEBUG) && ENABLE_ROOM_POST_DEBUG == 1 Serial.print("room.post: addSystemPost: "); Serial.println(postData); #endif + storePost(self_id, postData); }