From c9f4302ffb89d262b2927aadf5b3be67492febe7 Mon Sep 17 00:00:00 2001 From: mloszand Date: Fri, 14 Aug 2026 08:42:07 +0200 Subject: [PATCH] Add board variant for Waveshare ESP32-C3-Zero New variant for the Waveshare ESP32-C3-Zero. Fixes a chip-detection bug where SPIClass was constructed without specifying the SPI bus, causing radio init to silently fail on this board. WiFi companion mode tested and working; BLE companion is included but currently non-functional (documented in platformio.ini). --- .../waveshare_c3_zero/WaveshareC3ZeroBoard.h | 64 +++++++++ variants/waveshare_c3_zero/platformio.ini | 127 ++++++++++++++++++ variants/waveshare_c3_zero/target.cpp | 46 +++++++ variants/waveshare_c3_zero/target.h | 17 +++ 4 files changed, 254 insertions(+) create mode 100644 variants/waveshare_c3_zero/WaveshareC3ZeroBoard.h create mode 100644 variants/waveshare_c3_zero/platformio.ini create mode 100644 variants/waveshare_c3_zero/target.cpp create mode 100644 variants/waveshare_c3_zero/target.h diff --git a/variants/waveshare_c3_zero/WaveshareC3ZeroBoard.h b/variants/waveshare_c3_zero/WaveshareC3ZeroBoard.h new file mode 100644 index 0000000000..c066c6b7a7 --- /dev/null +++ b/variants/waveshare_c3_zero/WaveshareC3ZeroBoard.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include + +#include + +class WaveshareC3ZeroBoard : public ESP32Board { +public: + void begin() { + ESP32Board::begin(); + + esp_reset_reason_t reason = esp_reset_reason(); + if (reason == ESP_RST_DEEPSLEEP) { + long wakeup_source = esp_sleep_get_gpio_wakeup_status(); + if (wakeup_source & (1 << P_LORA_DIO_1)) { // received a LoRa packet (while in deep sleep) + startup_reason = BD_STARTUP_RX_PACKET; + } + + #if defined(LORA_TX_BOOST_PIN) + gpio_hold_dis((gpio_num_t) LORA_TX_BOOST_PIN); + gpio_deep_sleep_hold_dis(); + #endif + } + + #ifdef LORA_TX_BOOST_PIN + pinMode(LORA_TX_BOOST_PIN, OUTPUT); + digitalWrite(LORA_TX_BOOST_PIN, HIGH); + #endif + + #ifdef P_LORA_TX_LED + pinMode(P_LORA_TX_LED, OUTPUT); + digitalWrite(P_LORA_TX_LED, LOW); + #endif + } + +#if defined(LORA_TX_BOOST_PIN) || defined(P_LORA_TX_LED) + void onBeforeTransmit() override { + #if defined(P_LORA_TX_LED) + digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on + #endif + #if defined(LORA_TX_BOOST_PIN) + digitalWrite(LORA_TX_BOOST_PIN, LOW); + delay(5); + #endif + } + void onAfterTransmit() override { + #if defined(LORA_TX_BOOST_PIN) + digitalWrite(LORA_TX_BOOST_PIN, HIGH); + #endif + #if defined(P_LORA_TX_LED) + digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off + #endif + } +#endif + + uint16_t getBattMilliVolts() override { + return 0; // no battery-voltage-divider circuit on this board + } + + const char* getManufacturerName() const override { + return "Waveshare ESP32-C3-Zero"; + } +}; diff --git a/variants/waveshare_c3_zero/platformio.ini b/variants/waveshare_c3_zero/platformio.ini new file mode 100644 index 0000000000..9ab5cfc106 --- /dev/null +++ b/variants/waveshare_c3_zero/platformio.ini @@ -0,0 +1,127 @@ +[Waveshare_C3_Zero] +extends = esp32_base +board = esp32-c3-devkitm-1 +board_build.flash_mode = dio +build_flags = + ${esp32_base.build_flags} + -I variants/waveshare_c3_zero + -D ARDUINO_USB_CDC_ON_BOOT=1 + -D ARDUINO_USB_MODE=1 + -D P_LORA_NSS=7 + -D P_LORA_DIO_1=3 + -D P_LORA_RESET=0 + -D P_LORA_BUSY=1 + -D P_LORA_SCLK=4 + -D P_LORA_MISO=5 + -D P_LORA_MOSI=6 + -D SX126X_RXEN=10 + -D USE_SX1262 + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D SX126X_RX_BOOSTED_GAIN=1 + -D LORA_TX_POWER=14 + -D SX126X_DIO2_AS_RF_SWITCH=true + -D SX126X_DIO3_TCXO_VOLTAGE=1.8 + -D SX126X_CURRENT_LIMIT=140 +build_src_filter = ${esp32_base.build_src_filter} + +<../variants/waveshare_c3_zero> + + +lib_deps = + ${esp32_base.lib_deps} + +[env:Waveshare_C3_Zero_repeater] +extends = Waveshare_C3_Zero +build_src_filter = ${Waveshare_C3_Zero.build_src_filter} + +<../examples/simple_repeater/*.cpp> +build_flags = + ${Waveshare_C3_Zero.build_flags} + ${sensor_base.build_flags} + -UENV_INCLUDE_GPS + -UENV_INCLUDE_VL53L0X + -D ADVERT_NAME='"Waveshare C3 Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +lib_deps = + ${Waveshare_C3_Zero.lib_deps} + ${sensor_base.lib_deps} + ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 + +[env:Waveshare_C3_Zero_room_server] +extends = Waveshare_C3_Zero +build_src_filter = ${Waveshare_C3_Zero.build_src_filter} + +<../examples/simple_room_server/*.cpp> +build_flags = + ${Waveshare_C3_Zero.build_flags} + ${sensor_base.build_flags} + -UENV_INCLUDE_GPS + -UENV_INCLUDE_VL53L0X + -D ADVERT_NAME='"Waveshare C3 Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +lib_deps = + ${Waveshare_C3_Zero.lib_deps} + ${sensor_base.lib_deps} + ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 + +[env:Waveshare_C3_Zero_companion_radio_usb] +extends = Waveshare_C3_Zero +build_src_filter = ${Waveshare_C3_Zero.build_src_filter} + +<../examples/companion_radio/*.cpp> + + +build_flags = + ${Waveshare_C3_Zero.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D OFFLINE_QUEUE_SIZE=256 + -D ENABLE_USB_INTERFACE +lib_deps = + ${Waveshare_C3_Zero.lib_deps} + ${esp32_ota.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:Waveshare_C3_Zero_companion_radio_ble] +extends = Waveshare_C3_Zero +build_src_filter = ${Waveshare_C3_Zero.build_src_filter} + +<../examples/companion_radio/*.cpp> + + +build_flags = + ${Waveshare_C3_Zero.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D OFFLINE_QUEUE_SIZE=256 +lib_deps = + ${Waveshare_C3_Zero.lib_deps} + ${esp32_ota.lib_deps} + densaugeo/base64 @ ~1.4.0 +# KNOWN ISSUE: this environment currently silent-reboot-loops before setup() +# ever runs (no serial output at all, not even the first line of setup()). +# Isolated so far to something inside the linked ESP32 BLE Arduino library +# itself -- NOT MeshCore's own SerialBLEInterface (its constructor is +# trivial field-init only) and NOT board/radio/pin config, since the WiFi +# and USB companion environments above work correctly with identical board +# config. Root cause not yet found; flagged here for whoever picks it up. + +[env:Waveshare_C3_Zero_companion_radio_wifi] +extends = Waveshare_C3_Zero +build_src_filter = ${Waveshare_C3_Zero.build_src_filter} + +<../examples/companion_radio/*.cpp> + + +build_flags = + ${Waveshare_C3_Zero.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D OFFLINE_QUEUE_SIZE=256 + -D WIFI_DEBUG_LOGGING=1 + -D WIFI_SSID='"myssid"' + -D WIFI_PWD='"mypwd"' +lib_deps = + ${Waveshare_C3_Zero.lib_deps} + ${esp32_ota.lib_deps} + densaugeo/base64 @ ~1.4.0 diff --git a/variants/waveshare_c3_zero/target.cpp b/variants/waveshare_c3_zero/target.cpp new file mode 100644 index 0000000000..cb334fbb35 --- /dev/null +++ b/variants/waveshare_c3_zero/target.cpp @@ -0,0 +1,46 @@ +#include +#include "target.h" + +WaveshareC3ZeroBoard board; + +#if defined(P_LORA_SCLK) + // NOTE: ESP32-C3 only exposes one general-purpose SPI peripheral (FSPI). + // The default SPIClass() constructor does not reliably bind to it on this + // chip, which silently produces an SPI object that never talks to real + // hardware -- radio chip-detection then fails every time with no error + // other than RADIOLIB_ERR_CHIP_NOT_FOUND. Always pass FSPI explicitly. + static SPIClass spi(FSPI); + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); +#else + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); +#endif + +WRAPPER_CLASS radio_driver(radio, board); + +ESP32RTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); + +#if ENV_INCLUDE_GPS + #include + MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); + EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); +#else + EnvironmentSensorManager sensors; +#endif + +bool radio_init() { + fallback_clock.begin(); + rtc_clock.begin(Wire); + +#if defined(P_LORA_SCLK) + spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); + return radio.std_init(&spi); +#else + return radio.std_init(); +#endif +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} diff --git a/variants/waveshare_c3_zero/target.h b/variants/waveshare_c3_zero/target.h new file mode 100644 index 0000000000..f17f197c02 --- /dev/null +++ b/variants/waveshare_c3_zero/target.h @@ -0,0 +1,17 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include + +extern WaveshareC3ZeroBoard board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern EnvironmentSensorManager sensors; + +bool radio_init(); +mesh::LocalIdentity radio_new_identity(); \ No newline at end of file