Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b9bdbe4
use EnvironmentSensorManager in t114 variant
Dec 12, 2025
906f866
thinknode_m3: initial commit
fdlamotte Nov 28, 2025
450f59f
Add devcontainer configuration for vscode
jbrazio Nov 23, 2025
abc2aa0
Refactor devcontainer runArgs
jbrazio Dec 12, 2025
5b7b816
Added default temperature from ESP32 MCU and NRF52 MCU
IoTThinks Dec 12, 2025
d1b53ab
Fixed to call getMCUTemperature once.
IoTThinks Dec 13, 2025
e07a1c3
variants: IkokaNrf52Board: Use NRF52Board base class
fschrempf Dec 17, 2025
7a39f58
Deduplicate reboot() for NRF52 boards
fschrempf Dec 9, 2025
a0cd596
Use common NRF52 begin() and deduplicate() startup reason init
fschrempf Dec 9, 2025
b16660c
Deduplicate DC/DC regulator enable for NRF52 boards
fschrempf Dec 9, 2025
c696647
Deduplicate NRF52 startOTAUpdate()
fschrempf Dec 9, 2025
680fefe
NRF52Board.h: Mark getMCUTemperature() as virtual
fschrempf Dec 17, 2025
f5a6a8c
variants: RAK4631: Enable DC/DC regulator to reduce power consumption
fschrempf Dec 9, 2025
bb04942
queue throttling + slave latency and minor refactor
liquidraver Dec 16, 2025
a0cb0fe
move showalert after saveprefs
liquidraver Dec 11, 2025
5421f20
* check for 'early receive' ACK
Dec 27, 2025
8f9cc0c
Add RS232 bridge environment configuration for ProMicro
jbrazio Dec 28, 2025
39b7735
Fixed T1000-E temperature and lux sensors
entr0p1 Dec 19, 2025
b261c74
EnvironmentSensorManager.cpp: Mitigate BME280 self-heating causing in…
entr0p1 Dec 20, 2025
6ccd003
To fix the default temperature to be overridden by external sensors (…
IoTThinks Dec 27, 2025
36ce749
To get and average the temperature so it is more accurate, especially…
IoTThinks Dec 27, 2025
4a2111b
Fix TX LED stuck on when StartTransmit() fails
entr0p1 Dec 20, 2025
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
44 changes: 44 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "MeshCore",
"image": "mcr.microsoft.com/devcontainers/python:3-bookworm",
"features": {
"ghcr.io/rocker-org/devcontainer-features/apt-packages:1": {
"packages": [
"sudo"
]
}
},
"runArgs": [
"--privileged",
// arch tty* is owned by uucp (986)
// debian tty* is owned by uucp (20) - no change needed
"--group-add=986",
"--network=host",
"--volume=/dev/bus/usb:/dev/bus/usb:ro"
],
"postCreateCommand": {
"platformio": "pipx install platformio"
},
"customizations": {
"vscode": {
"settings": {
"platformio-ide.disablePIOHomeStartup": true,
"editor.formatOnSave": false,
"workbench.colorCustomizations": {
"titleBar.activeBackground": "#0d1a2b",
"titleBar.activeForeground": "#ffffff",
"titleBar.inactiveBackground": "#0d1a2b99",
"titleBar.inactiveForeground": "#ffffff99"
}
},
"extensions": [
"platformio.platformio-ide",
"github.vscode-github-actions",
"GitHub.vscode-pull-request-github"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}
}
}
72 changes: 72 additions & 0 deletions boards/thinknode_m3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
[
"0x239A",
"0x4405"
],
[
"0x239A",
"0x0029"
],
[
"0x239A",
"0x002A"
]
],
"usb_product": "elecrow_eink",
"mcu": "nrf52840",
"variant": "ELECROW-ThinkNode-M3",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": [
"bluetooth"
],
"debug": {
"jlink_device": "nRF52840_xxAA",
"onboard_tools": [
"jlink"
],
"svd_path": "nrf52840.svd",
"openocd_target": "nrf52.cfg"
},
"frameworks": [
"arduino"
],
"name": "elecrow nrf",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true,
"protocol": "nrfutil",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"stlink"
]
},
"url": "https://github.com/Elecrow-RD",
"vendor": "ELECROW"
}
6 changes: 2 additions & 4 deletions examples/companion_radio/ui-new/UITask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -892,14 +892,13 @@ void UITask::toggleGPS() {
_sensors->setSettingValue("gps", "0");
_node_prefs->gps_enabled = 0;
notify(UIEventType::ack);
showAlert("GPS: Disabled", 800);
} else {
_sensors->setSettingValue("gps", "1");
_node_prefs->gps_enabled = 1;
notify(UIEventType::ack);
showAlert("GPS: Enabled", 800);
}
the_mesh.savePrefs();
showAlert(_node_prefs->gps_enabled ? "GPS: Enabled" : "GPS: Disabled", 800);
_next_refresh = 0;
break;
}
Expand All @@ -913,13 +912,12 @@ void UITask::toggleBuzzer() {
if (buzzer.isQuiet()) {
buzzer.quiet(false);
notify(UIEventType::ack);
showAlert("Buzzer: ON", 800);
} else {
buzzer.quiet(true);
showAlert("Buzzer: OFF", 800);
}
_node_prefs->buzzer_quiet = buzzer.isQuiet();
the_mesh.savePrefs();
showAlert(buzzer.isQuiet() ? "Buzzer: OFF" : "Buzzer: ON", 800);
_next_refresh = 0; // trigger refresh
#endif
}
7 changes: 7 additions & 0 deletions examples/simple_repeater/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,19 @@ int MyMesh::handleRequest(ClientInfo *sender, uint32_t sender_timestamp, uint8_t

telemetry.reset();
telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f);

// query other sensors -- target specific
if ((sender->permissions & PERM_ACL_ROLE_MASK) == PERM_ACL_GUEST) {
perm_mask = 0x00; // just base telemetry allowed
}
sensors.querySensors(perm_mask, telemetry);

// This default temperature will be overridden by external sensors (if any)
float temperature = board.getMCUTemperature();
if(!isnan(temperature)) { // Supported boards with built-in temperature sensor. ESP32-C3 may return NAN
telemetry.addTemperature(TELEM_CHANNEL_SELF, temperature); // Built-in MCU Temperature
}

uint8_t tlen = telemetry.getSize();
memcpy(&reply_data[4], telemetry.getBuffer(), tlen);
return 4 + tlen; // reply_len
Expand Down
10 changes: 10 additions & 0 deletions src/Mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
}

if (pkt->isRouteDirect() && pkt->path_len >= PATH_HASH_SIZE) {
// check for 'early received' ACK
if (pkt->getPayloadType() == PAYLOAD_TYPE_ACK) {
int i = 0;
uint32_t ack_crc;
memcpy(&ack_crc, &pkt->payload[i], 4); i += 4;
if (i <= pkt->payload_len) {
onAckRecv(pkt, ack_crc);
}
}

if (self_id.isHashMatch(pkt->path) && allowPacketForward(pkt)) {
if (pkt->getPayloadType() == PAYLOAD_TYPE_MULTIPART) {
return forwardMultipartDirect(pkt);
Expand Down
2 changes: 2 additions & 0 deletions src/MeshCore.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <stdint.h>
#include <math.h>

#define MAX_HASH_SIZE 8
#define PUB_KEY_SIZE 32
Expand Down Expand Up @@ -42,6 +43,7 @@ namespace mesh {
class MainBoard {
public:
virtual uint16_t getBattMilliVolts() = 0;
virtual float getMCUTemperature() { return NAN; }
virtual bool setAdcMultiplier(float multiplier) { return false; };
virtual float getAdcMultiplier() const { return 0.0f; }
virtual const char* getManufacturerName() const = 0;
Expand Down
12 changes: 12 additions & 0 deletions src/helpers/ESP32Board.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ class ESP32Board : public mesh::MainBoard {
#endif
}

// Temperature from ESP32 MCU
float getMCUTemperature() override {
uint32_t raw = 0;

// To get and average the temperature so it is more accurate, especially in low temperature
for (int i = 0; i < 4; i++) {
raw += temperatureRead();
}

return raw / 4;
}

uint8_t getStartupReason() const override { return startup_reason; }

#if defined(P_LORA_TX_LED)
Expand Down
104 changes: 104 additions & 0 deletions src/helpers/NRF52Board.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#if defined(NRF52_PLATFORM)
#include "NRF52Board.h"

#include <bluefruit.h>

static BLEDfu bledfu;

static void connect_callback(uint16_t conn_handle) {
(void)conn_handle;
MESH_DEBUG_PRINTLN("BLE client connected");
}

static void disconnect_callback(uint16_t conn_handle, uint8_t reason) {
(void)conn_handle;
(void)reason;

MESH_DEBUG_PRINTLN("BLE client disconnected");
}

void NRF52Board::begin() {
startup_reason = BD_STARTUP_NORMAL;
}

void NRF52BoardDCDC::begin() {
NRF52Board::begin();

// Enable DC/DC converter for improved power efficiency
uint8_t sd_enabled = 0;
sd_softdevice_is_enabled(&sd_enabled);
if (sd_enabled) {
sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE);
} else {
NRF_POWER->DCDCEN = 1;
}
}

// Temperature from NRF52 MCU
float NRF52Board::getMCUTemperature() {
NRF_TEMP->TASKS_START = 1; // Start temperature measurement

long startTime = millis();
while (NRF_TEMP->EVENTS_DATARDY == 0) { // Wait for completion. Should complete in 50us
if(millis() - startTime > 5) { // To wait 5ms just in case
NRF_TEMP->TASKS_STOP = 1;
return NAN;
}
}

NRF_TEMP->EVENTS_DATARDY = 0; // Clear event flag

int32_t temp = NRF_TEMP->TEMP; // In 0.25 *C units
NRF_TEMP->TASKS_STOP = 1;

return temp * 0.25f; // Convert to *C
}

bool NRF52BoardOTA::startOTAUpdate(const char *id, char reply[]) {
// Config the peripheral connection with maximum bandwidth
// more SRAM required by SoftDevice
// Note: All config***() function must be called before begin()
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16);

Bluefruit.begin(1, 0);
// Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4
Bluefruit.setTxPower(4);
// Set the BLE device name
Bluefruit.setName(ota_name);

Bluefruit.Periph.setConnectCallback(connect_callback);
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);

// To be consistent OTA DFU should be added first if it exists
bledfu.begin();

// Set up and start advertising
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
Bluefruit.Advertising.addName();

/* Start Advertising
- Enable auto advertising if disconnected
- Interval: fast mode = 20 ms, slow mode = 152.5 ms
- Timeout for fast mode is 30 seconds
- Start(timeout) with timeout = 0 will advertise forever (until connected)

For recommended advertising interval
https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds

uint8_t mac_addr[6];
memset(mac_addr, 0, sizeof(mac_addr));
Bluefruit.getAddr(mac_addr);
sprintf(reply, "OK - mac: %02X:%02X:%02X:%02X:%02X:%02X", mac_addr[5], mac_addr[4], mac_addr[3],
mac_addr[2], mac_addr[1], mac_addr[0]);

return true;
}
#endif
39 changes: 39 additions & 0 deletions src/helpers/NRF52Board.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once

#include <Arduino.h>
#include <MeshCore.h>

#if defined(NRF52_PLATFORM)

class NRF52Board : public mesh::MainBoard {
protected:
uint8_t startup_reason;

public:
virtual void begin();
virtual uint8_t getStartupReason() const override { return startup_reason; }
virtual float getMCUTemperature() override;
virtual void reboot() override { NVIC_SystemReset(); }
};

/*
* The NRF52 has an internal DC/DC regulator that allows increased efficiency
* compared to the LDO regulator. For being able to use it, the module/board
* needs to have the required inductors and and capacitors populated. If the
* hardware requirements are met, this subclass can be used to enable the DC/DC
* regulator.
*/
class NRF52BoardDCDC : virtual public NRF52Board {
public:
virtual void begin() override;
};

class NRF52BoardOTA : virtual public NRF52Board {
private:
char *ota_name;

public:
NRF52BoardOTA(char *name) : ota_name(name) {}
virtual bool startOTAUpdate(const char *id, char reply[]) override;
};
#endif
Loading