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
1bfeefb
ThinkNode G3, ETH support WIP
caveman99 Sep 22, 2025
7aa0af3
Merge branch 'develop' into thinknode-g3
thebentern Oct 5, 2025
48af1cd
ThinkNode G3, ETH support WIP
caveman99 Sep 22, 2025
1d341bb
Merge branch 'thinknode-g3' of https://github.com/meshtastic/firmware…
caveman99 Nov 27, 2025
2b56e25
ThinkNode G3, ETH support WIP
caveman99 Sep 22, 2025
adfc95d
Merge branch 'thinknode-g3' of https://github.com/meshtastic/firmware…
caveman99 Jan 15, 2026
bbabd8f
ThinkNode G3, ETH support WIP
caveman99 Sep 22, 2025
2ac35d2
Merge branch 'thinknode-g3' of https://github.com/meshtastic/firmware…
caveman99 Jan 15, 2026
93e2c94
ThinkNode G3, ETH support WIP
caveman99 Sep 22, 2025
b44070e
Merge branch 'thinknode-g3' of https://github.com/meshtastic/firmware…
caveman99 Jan 16, 2026
e65ce4d
ThinkNode G3, ETH support WIP
caveman99 Sep 22, 2025
d697eb4
Merge branch 'thinknode-g3' of https://github.com/meshtastic/firmware…
caveman99 Feb 19, 2026
6951739
ThinkNode G3, ETH support WIP
caveman99 Sep 22, 2025
4ccbf72
Merge branch 'thinknode-g3' of https://github.com/meshtastic/firmware…
caveman99 Apr 12, 2026
cecd409
rename variant and add guard macros
caveman99 Apr 12, 2026
c19e2f0
older G3 operational. M7 next.
caveman99 Apr 13, 2026
9721ad8
Merge branch 'develop' into thinknode-g3
caveman99 Apr 13, 2026
f70234f
Merge branch 'develop' into thinknode-g3
caveman99 Apr 14, 2026
3c586c2
Split out G3 and M7 to different variants. Completely new PCB design.…
caveman99 Apr 14, 2026
847c805
Merge branch 'develop' into thinknode-g3
caveman99 Apr 14, 2026
a2189f4
Merge branch 'develop' into thinknode-g3
Copilot Apr 14, 2026
33db993
Define button behaviour and use all of the device flash
caveman99 Apr 14, 2026
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
42 changes: 42 additions & 0 deletions boards/ThinkNode-M7.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"memory_type": "qio_opi"
},
"core": "esp32",
"extra_flags": [
"-D BOARD_HAS_PSRAM",
"-D ARDUINO_USB_CDC_ON_BOOT=0",
"-D ARDUINO_USB_MODE=0",
"-D ARDUINO_RUNNING_CORE=1",
"-D ARDUINO_EVENT_RUNNING_CORE=0"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"psram_type": "qio_opi",
"hwids": [["0x303A", "0x1001"]],
"mcu": "esp32s3",
"variant": "ELECROW-ThinkNode-M7"
},
"connectivity": ["wifi", "bluetooth", "lora"],
"debug": {
"default_tool": "esp-builtin",
"onboard_tools": ["esp-builtin"],
"openocd_target": "esp32s3.cfg"
},
"frameworks": ["arduino", "espidf"],
"name": "ELECROW ThinkNode M7",
"upload": {
"flash_size": "8MB",
"maximum_ram_size": 524288,
"maximum_size": 8388608,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 921600
},
"url": "https://www.elecrow.com",
"vendor": "ELECROW"
}
2 changes: 1 addition & 1 deletion src/DebugConfiguration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *mess
if (!this->_enabled)
return false;

if ((this->_server == NULL && this->_ip == INADDR_NONE) || this->_port == 0)
if ((this->_server == NULL && this->_ip == IPAddress(0, 0, 0, 0)) || this->_port == 0)
return false;

// Check priority against priMask values.
Expand Down
9 changes: 8 additions & 1 deletion src/DebugConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ extern MemGet memGet;
#define LED_STATE_ON 1
#endif

// WIFI LED
#ifndef WIFI_STATE_ON
#define WIFI_STATE_ON 1
#endif

// -----------------------------------------------------------------------------
// DEBUG
// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -147,7 +152,9 @@ extern "C" void logLegacy(const char *level, const char *fmt, ...);
// Default Bluetooth PIN
#define defaultBLEPin 123456

#if HAS_ETHERNET && !defined(USE_WS5500)
#if HAS_ETHERNET && defined(USE_CH390D)
#include <ESP32_CH390.h>
#elif HAS_ETHERNET && !defined(USE_WS5500)
#include <RAK13800_W5100S.h>
#endif // HAS_ETHERNET

Expand Down
6 changes: 6 additions & 0 deletions src/input/InputBroker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,12 @@ void InputBroker::Init()
BaseType_t higherWake = 0;
concurrency::mainDelay.interruptFromISR(&higherWake);
};
#if defined(ELECROW_ThinkNode_M7)
userConfigNoScreen.longLongPressTime = 15 * 1000;
userConfigNoScreen.longLongPress = INPUT_BROKER_FACTORY_RST;
#else
userConfigNoScreen.longLongPress = INPUT_BROKER_SHUTDOWN;
#endif
userConfigNoScreen.singlePress = INPUT_BROKER_USER_PRESS;
userConfigNoScreen.longPress = INPUT_BROKER_NONE;
userConfigNoScreen.longPressTime = 500;
Expand Down
1 change: 1 addition & 0 deletions src/input/InputBroker.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ enum input_broker_event {
INPUT_BROKER_USER_PRESS,
INPUT_BROKER_ALT_PRESS,
INPUT_BROKER_ALT_LONG,
INPUT_BROKER_FACTORY_RST = 0x9a,
INPUT_BROKER_SHUTDOWN = 0x9b,
INPUT_BROKER_GPS_TOGGLE = 0x9e,
INPUT_BROKER_SEND_PING = 0xaf,
Expand Down
6 changes: 3 additions & 3 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ NimbleBluetooth *nimbleBluetooth = nullptr;
NRF52Bluetooth *nrf52Bluetooth = nullptr;
#endif

#if HAS_WIFI || defined(USE_WS5500)
#if HAS_WIFI || defined(USE_WS5500) || defined(USE_CH390D)
#include "mesh/api/WiFiServerAPI.h"
#include "mesh/wifi/WiFiAPClient.h"
#endif

#if HAS_ETHERNET && !defined(USE_WS5500)
#if HAS_ETHERNET && !defined(USE_WS5500) && !defined(USE_CH390D)
#include "mesh/api/ethServerAPI.h"
#include "mesh/eth/ethClient.h"
#endif
Expand Down Expand Up @@ -335,7 +335,7 @@ void setup()

#ifdef WIFI_LED
pinMode(WIFI_LED, OUTPUT);
digitalWrite(WIFI_LED, LOW);
digitalWrite(WIFI_LED, HIGH ^ WIFI_STATE_ON);
#endif

#ifdef BLE_LED
Expand Down
2 changes: 1 addition & 1 deletion src/mesh/InterfacesTemplates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ template class LR11x0Interface<LR1121>;
template class SX126xInterface<STM32WLx>;
#endif

#if HAS_ETHERNET && !defined(USE_WS5500)
#if HAS_ETHERNET && !defined(USE_WS5500) && !defined(USE_CH390D)
#include "api/ethServerAPI.h"
template class ServerAPI<EthernetClient>;
template class APIServerPort<ethServerAPI, EthernetServer>;
Expand Down
2 changes: 1 addition & 1 deletion src/mesh/api/ethServerAPI.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include "configuration.h"
#include <Arduino.h>

#if HAS_ETHERNET && !defined(USE_WS5500)
#if HAS_ETHERNET && !defined(USE_WS5500) && !defined(USE_CH390D)

#include "ethServerAPI.h"

Expand Down
2 changes: 1 addition & 1 deletion src/mesh/api/ethServerAPI.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma once

#include "ServerAPI.h"
#ifndef USE_WS5500
#if !defined(USE_WS5500) && !defined(USE_CH390D)
#include <RAK13800_W5100S.h>

/**
Expand Down
2 changes: 1 addition & 1 deletion src/mesh/eth/ethClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include <RAK13800_W5100S.h>
#include <SPI.h>

#if HAS_NETWORKING
#if HAS_NETWORKING && !defined(USE_WS5500) && !defined(USE_CH390D)

#ifndef DISABLE_NTP
#include <NTPClient.h>
Expand Down
87 changes: 78 additions & 9 deletions src/mesh/wifi/WiFiAPClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
#define ETH ETH2
#endif // HAS_ETHERNET

#if HAS_ETHERNET && defined(USE_CH390D)
#include "ESP32_CH390.h"
#include "hal/spi_types.h"
#define ETH CH390
#endif // HAS_ETHERNET

#include <WiFiUdp.h>
#ifdef ARCH_ESP32
#if !MESHTASTIC_EXCLUDE_WEBSERVER
Expand Down Expand Up @@ -56,12 +62,43 @@ unsigned long lastrun_ntp = 0;

bool needReconnect = true; // If we create our reconnector, run it once at the beginning
bool isReconnecting = false; // If we are currently reconnecting
#if defined(USE_WS5500) || defined(USE_CH390D)
static volatile bool ethNetworkConnectedPending = false;
#endif

WiFiUDP syslogClient;
meshtastic::Syslog syslog(syslogClient);

Periodic *wifiReconnect;

#if defined(USE_WS5500) || defined(USE_CH390D)
static void onNetworkConnected();
static uint32_t lastEthIP = 0;
static int32_t ethNetworkConnectedPoll()
{
if (ethNetworkConnectedPending) {
ethNetworkConnectedPending = false;
uint32_t ip = (uint32_t)ETH.localIP();
bool ipChanged = APStartupComplete && ip != 0 && ip != lastEthIP;
onNetworkConnected();
if (ipChanged) {
LOG_INFO("Ethernet IP changed (%u.%u.%u.%u), restarting mDNS", ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) & 0xff,
(ip >> 24) & 0xff);
MDNS.end();
if (MDNS.begin("Meshtastic")) {
MDNS.addService("meshtastic", "tcp", SERVER_API_DEFAULT_PORT);
MDNS.addServiceTxt("meshtastic", "tcp", "shortname", String(owner.short_name));
MDNS.addServiceTxt("meshtastic", "tcp", "id", String(nodeDB->getNodeId().c_str()));
MDNS.addServiceTxt("meshtastic", "tcp", "pio_env", optstr(APP_ENV));
}
}
if (ip != 0)
lastEthIP = ip;
}
return 500;
}
#endif

#ifdef USE_WS5500
// Startup Ethernet
bool initEthernet()
Expand All @@ -72,6 +109,38 @@ bool initEthernet()
#if !MESHTASTIC_EXCLUDE_WEBSERVER
createSSLCert(); // For WebServer
#endif
new concurrency::Periodic("EthConnect", ethNetworkConnectedPoll);
return true;
}

return false;
}
#endif

#ifdef USE_CH390D
// Startup Ethernet
bool initEthernet()
{
// Configure CH390
ch390_config_t ch390_conf = CH390_DEFAULT_CONFIG();
ch390_conf.spi_host = SPI3_HOST;
ch390_conf.spi_cs_gpio = ETH_CS_PIN;
ch390_conf.spi_sck_gpio = ETH_SCLK_PIN;
ch390_conf.spi_mosi_gpio = ETH_MOSI_PIN;
ch390_conf.spi_miso_gpio = ETH_MISO_PIN;
ch390_conf.int_gpio = ETH_INT_PIN;
#ifdef ETH_RST_PIN
ch390_conf.reset_gpio = ETH_RST_PIN;
#else
ch390_conf.reset_gpio = -1;
#endif
ch390_conf.spi_clock_mhz = 20;
if ((config.network.eth_enabled) && (ETH.begin(ch390_conf))) {
WiFi.onEvent(WiFiEvent);
#if !MESHTASTIC_EXCLUDE_WEBSERVER
createSSLCert(); // For WebServer
#endif
new concurrency::Periodic("EthConnect", ethNetworkConnectedPoll);
return true;
}

Expand Down Expand Up @@ -234,7 +303,7 @@ bool isWifiAvailable()

if (config.network.wifi_enabled && (config.network.wifi_ssid[0])) {
return true;
#ifdef USE_WS5500
#if defined(USE_WS5500) || defined(USE_CH390D)
} else if (config.network.eth_enabled) {
return true;
#endif
Expand Down Expand Up @@ -384,13 +453,13 @@ static void WiFiEvent(WiFiEvent_t event)
#endif
}
#ifdef WIFI_LED
digitalWrite(WIFI_LED, HIGH);
digitalWrite(WIFI_LED, LOW ^ WIFI_STATE_ON);
#endif
break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
LOG_INFO("Disconnected from WiFi access point");
#ifdef WIFI_LED
digitalWrite(WIFI_LED, LOW);
digitalWrite(WIFI_LED, HIGH ^ WIFI_STATE_ON);
#endif
#if HAS_UDP_MULTICAST
if (udpHandler) {
Expand Down Expand Up @@ -452,13 +521,13 @@ static void WiFiEvent(WiFiEvent_t event)
case ARDUINO_EVENT_WIFI_AP_START:
LOG_INFO("WiFi access point started");
#ifdef WIFI_LED
digitalWrite(WIFI_LED, HIGH);
digitalWrite(WIFI_LED, LOW ^ WIFI_STATE_ON);
#endif
break;
case ARDUINO_EVENT_WIFI_AP_STOP:
LOG_INFO("WiFi access point stopped");
#ifdef WIFI_LED
digitalWrite(WIFI_LED, LOW);
digitalWrite(WIFI_LED, HIGH ^ WIFI_STATE_ON);
#endif
break;
case ARDUINO_EVENT_WIFI_AP_STACONNECTED:
Expand Down Expand Up @@ -494,18 +563,18 @@ static void WiFiEvent(WiFiEvent_t event)
LOG_INFO("Ethernet disconnected");
break;
case ARDUINO_EVENT_ETH_GOT_IP:
#ifdef USE_WS5500
#if defined(USE_WS5500) || defined(USE_CH390D)
LOG_INFO("Obtained IP address: %s, %u Mbps, %s", ETH.localIP().toString().c_str(), ETH.linkSpeed(),
ETH.fullDuplex() ? "FULL_DUPLEX" : "HALF_DUPLEX");
onNetworkConnected();
ethNetworkConnectedPending = true;
#endif
break;
case ARDUINO_EVENT_ETH_GOT_IP6:
#ifdef USE_WS5500
#if defined(USE_WS5500) || defined(USE_CH390D)
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
LOG_INFO("Obtained Local IP6 address: %s", ETH.linkLocalIPv6().toString().c_str());
LOG_INFO("Obtained GlobalIP6 address: %s", ETH.globalIPv6().toString().c_str());
#else
#elif defined(USE_WS5500)
LOG_INFO("Obtained IP6 address: %s", ETH.localIPv6().toString().c_str());
#endif
#endif
Expand Down
2 changes: 1 addition & 1 deletion src/mesh/wifi/WiFiAPClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ bool isWifiAvailable();

uint8_t getWifiDisconnectReason();

#ifdef USE_WS5500
#if defined(USE_WS5500) || defined(USE_CH390D)
// Startup Ethernet
bool initEthernet();
#endif
2 changes: 1 addition & 1 deletion src/modules/AdminModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1313,7 +1313,7 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r
}
#endif

#if HAS_ETHERNET && !defined(USE_WS5500)
#if HAS_ETHERNET && !defined(USE_WS5500) && !defined(USE_CH390D)
conn.has_ethernet = true;
conn.ethernet.has_status = true;
if (Ethernet.linkStatus() == LinkON) {
Expand Down
11 changes: 11 additions & 0 deletions src/modules/SystemCommandsModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,17 @@ int SystemCommandsModule::handleInputEvent(const InputEvent *event)
case INPUT_BROKER_SHUTDOWN:
shutdownAtMsec = millis();
return true;
// factory reset
case INPUT_BROKER_FACTORY_RST:
disableBluetooth();
LOG_INFO("Initiate full factory reset");
nodeDB->factoryReset(true);
// reboot(DEFAULT_REBOOT_SECONDS);
LOG_INFO("Reboot in %d seconds", DEFAULT_REBOOT_SECONDS);
if (screen)
screen->showSimpleBanner("Rebooting...", 0); // stays on screen
rebootAtMsec = (DEFAULT_REBOOT_SECONDS < 0) ? 0 : (millis() + DEFAULT_REBOOT_SECONDS * 1000);
return true;

default:
// No other input events handled here
Expand Down
6 changes: 6 additions & 0 deletions src/mqtt/MQTT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
#if HAS_ETHERNET && defined(USE_WS5500)
#include <ETHClass2.h>
#define ETH ETH2
#elif HAS_ETHERNET && defined(USE_CH390D)
#include "ESP32_CH390.h"
#define ETH CH390
#endif // HAS_ETHERNET
#include "Default.h"
#if !defined(ARCH_NRF52) || NRF52_USE_JSON
Expand Down Expand Up @@ -344,6 +347,9 @@ inline bool isConnectedToNetwork()
#ifdef USE_WS5500
if (ETH.connected())
return true;
#elif defined(USE_CH390D)
if (ETH.isConnected())
return true;
#endif

#if HAS_WIFI
Expand Down
2 changes: 1 addition & 1 deletion src/mqtt/MQTT.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#include <WiFiClientSecure.h>
#endif
#endif
#if HAS_ETHERNET && !defined(USE_WS5500)
#if HAS_ETHERNET && !defined(USE_WS5500) && !defined(USE_CH390D)
#include <EthernetClient.h>
#endif

Expand Down
2 changes: 2 additions & 0 deletions src/platform/esp32/architecture.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@
#define HW_VENDOR meshtastic_HardwareModel_THINKNODE_M2
#elif defined(ELECROW_ThinkNode_M5)
#define HW_VENDOR meshtastic_HardwareModel_THINKNODE_M5
#elif defined(ELECROW_ThinkNode_M7)
#define HW_VENDOR meshtastic_HardwareModel_THINKNODE_M7
#elif defined(ESP32_S3_PICO)
#define HW_VENDOR meshtastic_HardwareModel_ESP32_S3_PICO
#elif defined(SENSELORA_S3)
Expand Down
2 changes: 1 addition & 1 deletion src/platform/esp32/main-esp32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ void variant_shutdown() {}
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_BLUETOOTH
void setBluetoothEnable(bool enable)
{
#ifdef USE_WS5500
#if defined(USE_WS5500) || defined(USE_CH390D)
if ((config.bluetooth.enabled == true) && (config.network.wifi_enabled == false))
#elif HAS_WIFI
if (!isWifiAvailable() && config.bluetooth.enabled == true)
Expand Down
Loading
Loading