Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: meshcore-dev
6 changes: 3 additions & 3 deletions .github/actions/setup-build-environment/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ runs:
steps:

- name: Init Cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
~/.cache/pip
~/.platformio/.cache
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
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/build-companion-firmwares.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 }}
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/build-repeater-firmwares.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 }}
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/build-room-server-firmwares.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 }}
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/github-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@ 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: |
pip install mkdocs-material
mkdocs build
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
uses: peaceiris/actions-gh-pages@v4.1.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
cname: docs.meshcore.io
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-build-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions docs/cli_commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand Down
8 changes: 2 additions & 6 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -392,10 +391,7 @@ Another way to download map tiles is to use this Python script to get the tiles
<https://github.com/fistulareffigy/MTD-Script>

There is also a modified script that adds additional error handling and parallel downloads:
<https://discord.com/channels/826570251612323860/1330643963501351004/1338775811548905572>

UK map tiles are available separately from Andy Kirby on his discord server:
<https://discord.com/channels/826570251612323860/1330643963501351004/1331346597367386224>
<https://github.com/TheBestJohn/MTD-Script>

### 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.
Expand Down Expand Up @@ -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*
<https://www.youtube.com/watch?v=WJvg6dt13hk> *(Link referenced in the Discord post)*

Expand Down
40 changes: 37 additions & 3 deletions examples/simple_room_server/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,43 @@ struct ServerStats {
};

void MyMesh::addPost(ClientInfo *client, const char *postData) {
storePost(client->id, 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);
}

void MyMesh::storePost(const mesh::Identity &author, const char *postData) {
int idx = next_post_idx;
// TODO: suggested postData format: <title>/<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
Expand Down Expand Up @@ -932,6 +957,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
}
Expand Down
2 changes: 2 additions & 0 deletions examples/simple_room_server/MyMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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; }
Expand Down
90 changes: 89 additions & 1 deletion examples/simple_room_server/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,42 @@ 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

#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_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;
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() {
Expand All @@ -24,6 +60,24 @@ 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_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_active ? "closed" : "opened");
#endif
#endif

#ifdef DISPLAY_CLASS
if (display.begin()) {
display.startFrame();
Expand Down Expand Up @@ -99,8 +153,9 @@ 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!
if (reply[0]) {
Serial.print(" -> "); Serial.println(reply);
Expand All @@ -109,6 +164,39 @@ 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 &&
(long)(millis() - gpio_contact_debounce_until) >= 0 &&
gpio_active != gpio_contact_active &&
millis() - gpio_contact_last_change >= GPIO_CONTACT_COOLDOWN_MS) {
gpio_contact_active = gpio_active;
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.println(msg);
#endif

the_mesh.addSystemPost(msg);
}
#endif

the_mesh.loop();
sensors.loop();
#ifdef DISPLAY_CLASS
Expand Down
Loading