Compare commits

..

2 Commits

Author SHA1 Message Date
Thomas Göttgens
bbabd8ffe0 ThinkNode G3, ETH support WIP 2026-01-15 21:28:56 +01:00
Ben Meadors
d493f5f171 Merge branch 'master' into develop 2026-01-15 08:26:09 -06:00
17 changed files with 124 additions and 166 deletions

View File

@@ -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
// -----------------------------------------------------------------------------
@@ -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

View File

@@ -105,43 +105,6 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr;
#include <string>
#endif
#ifdef ARCH_ESP32
#ifdef DEBUG_PARTITION_TABLE
#include "esp_partition.h"
void printPartitionTable()
{
printf("\n--- Partition Table ---\n");
// Print Column Headers
printf("| %-16s | %-4s | %-7s | %-10s | %-10s |\n", "Label", "Type", "Subtype", "Offset", "Size");
printf("|------------------|------|---------|------------|------------|\n");
// Create an iterator to find ALL partitions (Type ANY, Subtype ANY)
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
// Loop through the iterator
if (it != NULL) {
do {
const esp_partition_t *part = esp_partition_get(it);
// Print details: Label, Type (Hex), Subtype (Hex), Offset (Hex), Size (Hex)
printf("| %-16s | 0x%02x | 0x%02x | 0x%08x | 0x%08x |\n", part->label, part->type, part->subtype, part->address,
part->size);
// Move to next partition
it = esp_partition_next(it);
} while (it != NULL);
// Release the iterator memory
esp_partition_iterator_release(it);
} else {
printf("No partitions found.\n");
}
printf("-----------------------\n");
}
#endif // DEBUG_PARTITION_TABLE
#endif // ARCH_ESP32
#if HAS_BUTTON || defined(ARCH_PORTDUINO)
#include "input/ButtonThread.h"
@@ -374,7 +337,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
@@ -685,11 +648,7 @@ void setup()
sensor_detected = true;
#endif
}
#ifdef ARCH_ESP32
#ifdef DEBUG_PARTITION_TABLE
printPartitionTable();
#endif
#endif // ARCH_ESP32
#ifdef ARCH_ESP32
// Don't init display if we don't have one or we are waking headless due to a timer event
if (wakeCause == ESP_SLEEP_WAKEUP_TIMER) {

View File

@@ -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>;

View File

@@ -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"

View File

@@ -1,7 +1,7 @@
#pragma once
#include "ServerAPI.h"
#ifndef USE_WS5500
#if !defined(USE_WS5500) && !defined(USE_CH390D)
#include <RAK13800_W5100S.h>
/**

View File

@@ -384,13 +384,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 (!isReconnecting) {
WiFi.disconnect(false, true);
@@ -442,13 +442,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:

View File

@@ -235,46 +235,22 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
}
case meshtastic_AdminMessage_ota_request_tag: {
#if defined(ARCH_ESP32)
LOG_INFO("OTA Requested");
if (r->ota_request.ota_hash.size != 32) {
suppressRebootBanner = true;
sendWarningAndLog("Cannot start OTA: Invalid `ota_hash` provided.");
LOG_INFO("OTA Failed: Invalid `ota_hash` provided");
break;
}
meshtastic_OTAMode mode = r->ota_request.reboot_ota_mode;
const char *mode_name = (mode == METHOD_OTA_BLE ? "BLE" : "WiFi");
// Check that we have an OTA partition
const esp_partition_t *part = MeshtasticOTA::getAppPartition();
if (part == NULL) {
suppressRebootBanner = true;
sendWarningAndLog("Cannot start OTA: Cannot find OTA Loader partition.");
break;
}
static esp_app_desc_t app_desc;
if (!MeshtasticOTA::getAppDesc(part, &app_desc)) {
suppressRebootBanner = true;
sendWarningAndLog("Cannot start OTA: Device does have a valid OTA Loader.");
break;
}
if (!MeshtasticOTA::checkOTACapability(&app_desc, mode)) {
suppressRebootBanner = true;
sendWarningAndLog("OTA Loader does not support %s", mode_name);
break;
}
if (MeshtasticOTA::trySwitchToOTA()) {
LOG_INFO("OTA Requested");
suppressRebootBanner = true;
if (screen)
screen->startFirmwareUpdateScreen();
MeshtasticOTA::saveConfig(&config.network, mode, r->ota_request.ota_hash.bytes);
sendWarningAndLog("Rebooting to %s OTA", mode_name);
LOG_INFO("Rebooting to WiFi OTA");
} else {
sendWarningAndLog("Unable to switch to the OTA partition.");
LOG_INFO("WIFI OTA Failed");
}
#endif
int s = 1; // Reboot in 1 second, hard coded
@@ -1262,7 +1238,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) {
@@ -1496,43 +1472,15 @@ void AdminModule::handleSendInputEvent(const meshtastic_AdminMessage_InputEvent
#endif
}
void AdminModule::sendWarning(const char *format, ...)
void AdminModule::sendWarning(const char *message)
{
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
if (!cn)
return;
cn->level = meshtastic_LogRecord_Level_WARNING;
cn->time = getValidTime(RTCQualityFromNet);
va_list args;
va_start(args, format);
// Format the arguments directly into the notification object
vsnprintf(cn->message, sizeof(cn->message), format, args);
va_end(args);
strncpy(cn->message, message, sizeof(cn->message));
service->sendClientNotification(cn);
}
void AdminModule::sendWarningAndLog(const char *format, ...)
{
// We need a temporary buffer to hold the formatted text so we can log it
// Using 250 bytes as a safe upper limit for typical text notifications
char buf[250];
va_list args;
va_start(args, format);
vsnprintf(buf, sizeof(buf), format, args);
va_end(args);
LOG_WARN(buf);
// 2. Call sendWarning
// SECURITY NOTE: We pass "%s", buf instead of just 'buf'.
// If 'buf' contained a % symbol (e.g. "Battery 50%"), passing it directly
// would crash sendWarning. "%s" treats it purely as text.
sendWarning("%s", buf);
}
void disableBluetooth()
{
#if HAS_BLUETOOTH

View File

@@ -1,9 +1,7 @@
#pragma once
#ifdef ESP_PLATFORM
#include <esp_ota_ops.h>
#endif
#include "ProtobufModule.h"
#include <sys/types.h>
#pragma once
#include "ProtobufModule.h"
#if HAS_WIFI
#include "mesh/wifi/WiFiAPClient.h"
#endif
@@ -73,8 +71,7 @@ class AdminModule : public ProtobufModule<meshtastic_AdminMessage>, public Obser
bool messageIsResponse(const meshtastic_AdminMessage *r);
bool messageIsRequest(const meshtastic_AdminMessage *r);
void sendWarning(const char *format, ...) __attribute__((format(printf, 2, 3)));
void sendWarningAndLog(const char *format, ...) __attribute__((format(printf, 2, 3)));
void sendWarning(const char *message);
};
static constexpr const char *licensedModeMessage =

View File

@@ -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

View File

@@ -1,17 +1,13 @@
#include "MeshtasticOTA.h"
#include "configuration.h"
#ifdef ESP_PLATFORM
#include <Preferences.h>
#include <esp_ota_ops.h>
#endif
namespace MeshtasticOTA
{
static const char *nvsNamespace = "MeshtasticOTA";
static const char *combinedAppProjectName = "MeshtasticOTA";
static const char *bleOnlyAppProjectName = "MeshtasticOTA-BLE";
static const char *wifiOnlyAppProjectName = "MeshtasticOTA-WiFi";
static const char *appProjectName = "MeshtasticOTA";
static bool updated = false;
@@ -72,44 +68,21 @@ bool getAppDesc(const esp_partition_t *part, esp_app_desc_t *app_desc)
LOG_INFO("esp_ota_get_partition_description failed");
return false;
}
if (strcmp(app_desc->project_name, appProjectName) != 0) {
LOG_INFO("app_desc->project_name == 0");
return false;
}
return true;
}
bool checkOTACapability(esp_app_desc_t *app_desc, uint8_t method)
{
// Combined loader supports all (both) transports, BLE and WiFi
if (strcmp(app_desc->project_name, combinedAppProjectName) == 0) {
LOG_INFO("OTA partition contains combined BLE/WiFi OTA Loader");
return true;
}
if (method == METHOD_OTA_BLE && strcmp(app_desc->project_name, bleOnlyAppProjectName) == 0) {
LOG_INFO("OTA partition contains BLE-only OTA Loader");
return true;
}
if (method == METHOD_OTA_WIFI && strcmp(app_desc->project_name, wifiOnlyAppProjectName) == 0) {
LOG_INFO("OTA partition contains WiFi-only OTA Loader");
return true;
}
LOG_INFO("OTA partition does not contain a known OTA loader");
return false;
}
bool trySwitchToOTA()
{
const esp_partition_t *part = getAppPartition();
if (part == NULL) {
LOG_WARN("Unable to get app partition in preparation of OTA reboot");
esp_app_desc_t app_desc;
if (!getAppDesc(part, &app_desc))
return false;
}
uint8_t result = esp_ota_set_boot_partition(part);
// Partition and app checks should now be done in the AdminModule before this is called
if (result != ESP_OK) {
LOG_WARN("Unable to switch to OTA partiton. (Reason %d)", result);
if (esp_ota_set_boot_partition(part) != ESP_OK)
return false;
}
return true;
}

View File

@@ -3,20 +3,12 @@
#include "mesh-pb-constants.h"
#include <Arduino.h>
#ifdef ESP_PLATFORM
#include <esp_ota_ops.h>
#endif
#define METHOD_OTA_BLE 1
#define METHOD_OTA_WIFI 2
namespace MeshtasticOTA
{
void initialize();
bool isUpdated();
const esp_partition_t *getAppPartition();
bool getAppDesc(const esp_partition_t *part, esp_app_desc_t *app_desc);
bool checkOTACapability(esp_app_desc_t *app_desc, uint8_t method);
void recoverConfig(meshtastic_Config_NetworkConfig *network);
void saveConfig(meshtastic_Config_NetworkConfig *network, meshtastic_OTAMode method, uint8_t *ota_hash);
bool trySwitchToOTA();

View File

@@ -10,8 +10,6 @@ custom_meshtastic_tags = M5Stack
extends = esp32c6_base
board = esp32-c6-devkitc-1
board_upload.flash_size = 16MB
board_build.partitions = default_16MB.csv
;OpenOCD flash method
;upload_protocol = esp-builtin
;Normal method

View File

@@ -0,0 +1,26 @@
#ifndef Pins_Arduino_h
#define Pins_Arduino_h
#include <stdint.h>
#define USB_VID 0x303a
#define USB_PID 0x1001
// The default Wire will be mapped to PMU and RTC
static const uint8_t SDA = 17;
static const uint8_t SCL = 18;
// Default SPI will be mapped to Radio
static const uint8_t SS = 39;
static const uint8_t MOSI = 40;
static const uint8_t MISO = 41;
static const uint8_t SCK = 42;
// #define SPI_MOSI (11)
// #define SPI_SCK (10)
// #define SPI_MISO (9)
// #define SPI_CS (12)
// #define SDCARD_CS SPI_CS
#endif /* Pins_Arduino_h */

View File

@@ -0,0 +1,22 @@
[env:thinknode_g3]
extends = esp32s3_base
board = ESP32-S3-WROOM-1-N4
board_build.psram_type = opi
build_flags =
${esp32s3_base.build_flags}
-D ELECROW_ThinkNode_G3
-D HAS_UDP_MULTICAST=1
-D BOARD_HAS_PSRAM
-D PRIVATE_HW
# -D CONFIG_ETH_ENABLED=1
# -D CONFIG_ETH_USE_ESP32_EMAC=1
-I variants/esp32s3/ELECROW-ThinkNode-G3
-mfix-esp32-psram-cache-issue
lib_ignore =
Ethernet
lib_deps =
${esp32s3_base.lib_deps}
# file://../ESP32-CH390/ESP32-CH390-1.0.1.tar.gz

View File

@@ -0,0 +1,36 @@
#define HAS_GPS 0
#define HAS_WIRE 0
#define I2C_NO_RESCAN
#define WIFI_LED 5
#define WIFI_STATE_ON 0
#define LED_PIN 6 // The blue LORA LED
#define LED_STATE_ON 0
#define BUTTON_PIN 4 // the external user button of the device, BOOT and RESET are not accessible without opening it up.
#define USE_SX1262
#define LORA_SCK 42
#define LORA_MISO 41
#define LORA_MOSI 40
#define LORA_CS 39
#define LORA_RESET 21
#define SX126X_CS LORA_CS
#define SX126X_DIO1 15
#define SX126X_BUSY 47
#define SX126X_RESET LORA_RESET
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#define PIN_POWER_EN 45
// #define HAS_ETHERNET 1
// #define USE_CH390D 1 // this driver uses the same stack as the ESP32 Wifi driver
// #define ETH_MISO_PIN 47
// #define ETH_MOSI_PIN 21
// #define ETH_SCLK_PIN 48
// #define ETH_CS_PIN 45
// #define ETH_INT_PIN 14
// #define ETH_RST_PIN -1
// #define ETH_ADDR 1

View File

@@ -16,7 +16,7 @@ lib_deps =
# renovate: datasource=git-refs depName=NMIoT-meshsolar packageName=https://github.com/NMIoT/meshsolar gitBranch=main
https://github.com/NMIoT/meshsolar/archive/dfc5330dad443982e6cdd37a61d33fc7252f468b.zip
# renovate: datasource=custom.pio depName=ArduinoJson packageName=bblanchon/library/ArduinoJson
bblanchon/ArduinoJson@7.4.2
bblanchon/ArduinoJson@6.21.5
[env:heltec-mesh-solar]
custom_meshtastic_hw_model = 108

View File

@@ -36,7 +36,7 @@ lib_deps =
# renovate: datasource=git-refs depName=RAK12034-BMX160 packageName=https://github.com/RAKWireless/RAK12034-BMX160 gitBranch=main
https://github.com/RAKWireless/RAK12034-BMX160/archive/dcead07ffa267d3c906e9ca4a1330ab989e957e2.zip
# renovate: datasource=custom.pio depName=ArduinoJson packageName=bblanchon/library/ArduinoJson
bblanchon/ArduinoJson@7.4.2
bblanchon/ArduinoJson@6.21.5
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds
;upload_protocol = jlink