diff --git a/boards/esp32c3_supermini.json b/boards/esp32c3_supermini.json new file mode 100644 index 0000000000..ba697d6f80 --- /dev/null +++ b/boards/esp32c3_supermini.json @@ -0,0 +1,44 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32c3_out.ld" + }, + "core": "esp32", + "extra_flags": [ + "-DARDUINO_ESP32C3_SUPERMINI", + "-DARDUINO_USB_MODE=1", + "-DARDUINO_USB_CDC_ON_BOOT=1" + ], + "f_cpu": "160000000L", + "f_flash": "80000000L", + "flash_mode": "dio", + "hwids": [ + [ + "0x303a", + "0x1001" + ] + ], + "mcu": "esp32c3", + "variant": "esp32c3_supermini" + }, + "connectivity": [ + "wifi" + ], + "debug": { + "openocd_target": "esp32c3.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "ESP32-C3 SuperMini V1 (Maker go)", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://www.aliexpress.com/item/1005006155740481.html", + "vendor": "Maker go" +} diff --git a/src/helpers/radiolib/CustomLR2021.h b/src/helpers/radiolib/CustomLR2021.h new file mode 100644 index 0000000000..856c086728 --- /dev/null +++ b/src/helpers/radiolib/CustomLR2021.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include "MeshCore.h" + +class CustomLR2021 : public LR2021 { + bool _rx_boosted = false; + + public: + CustomLR2021(Module *mod) : LR2021(mod) { + irqDioNum = 9; + } + + size_t getPacketLength(bool update) override { + size_t len = LR2021::getPacketLength(update); + if (len == 0 && getIrqStatus() & RADIOLIB_LR11X0_IRQ_HEADER_ERR) { + MESH_DEBUG_PRINTLN("LR2021: got header err, calling standby()"); + standby(); + } + return len; + } + + float getFreqMHz() const { return freqMHz; } + + uint8_t getSpreadingFactor() const { return spreadingFactor; } + + int16_t setRxBoostedGainMode(uint8_t level) { + _rx_boosted = (level > 0); + return LR2021::setRxBoostedGainMode(level); + } + + bool getRxBoostedGainMode() const { return _rx_boosted; } + + bool isReceiving() { + uint32_t irq = getIrqStatus(); + bool detected = (irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID) || + (irq & RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED); + return detected; + } + + bool std_init(SPIClass *spi = NULL) { + (void)spi; + + int status = begin(LORA_FREQ, LORA_BW, LORA_SF, 5, + RADIOLIB_LR2021_LORA_SYNC_WORD_PRIVATE, + LORA_TX_POWER, 16, 0.0); + if (status != RADIOLIB_ERR_NONE) { + MESH_DEBUG_PRINTLN("LR2021: radio init failed: %d", status); + return false; + } + + setCRC(1); + return true; + } +}; diff --git a/src/helpers/radiolib/CustomLR2021Wrapper.h b/src/helpers/radiolib/CustomLR2021Wrapper.h new file mode 100644 index 0000000000..afa1221c19 --- /dev/null +++ b/src/helpers/radiolib/CustomLR2021Wrapper.h @@ -0,0 +1,74 @@ +#pragma once + +#include "CustomLR2021.h" +#include "RadioLibWrappers.h" + +class CustomLR2021Wrapper : public RadioLibWrapper { +public: + CustomLR2021Wrapper(CustomLR2021 &radio, mesh::MainBoard &board) + : RadioLibWrapper(radio, board) {} + + void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override { + ((CustomLR2021 *)_radio)->setFrequency(freq); + ((CustomLR2021 *)_radio)->setSpreadingFactor(sf); + ((CustomLR2021 *)_radio)->setBandwidth(bw); + ((CustomLR2021 *)_radio)->setCodingRate(cr); + updatePreamble(sf); + } + + bool isReceivingPacket() override { + return ((CustomLR2021 *)_radio)->isReceiving(); + } + + float getCurrentRSSI() override { + float rssi = -110; + ((CustomLR2021 *)_radio)->getRssiInst(&rssi); + return rssi; + } + + void onSendFinished() override { + RadioLibWrapper::onSendFinished(); + _radio->setPreambleLength( + preambleLengthForSF(getSpreadingFactor())); + } + + float getLastRSSI() const override { + return ((CustomLR2021 *)_radio)->getRSSI(); + } + + float getLastSNR() const override { + return ((CustomLR2021 *)_radio)->getSNR(); + } + + uint8_t getSpreadingFactor() const override { + return ((CustomLR2021 *)_radio)->getSpreadingFactor(); + } + + float packetScore(float snr, int packet_len) override { + int sf = ((CustomLR2021 *)_radio)->getSpreadingFactor(); + return packetScoreInt(snr, sf, packet_len); + } + + void powerOff() override { + ((CustomLR2021 *)_radio)->sleep(false, 0); + } + + void doResetAGC() override { + CustomLR2021 *r = (CustomLR2021 *)_radio; + float freq = r->getFreqMHz(); + r->sleep(true, 0); + r->standby(RADIOLIB_LR2021_STANDBY_RC, true); + r->calibrate(RADIOLIB_LR2021_CALIBRATE_ALL); + r->setFrequency(freq); + r->setRxBoostedGainMode(RADIOLIB_LR2021_RX_BOOST_LF); + } + + void setRxBoostedGainMode(bool en) override { + ((CustomLR2021 *)_radio)->setRxBoostedGainMode( + en ? RADIOLIB_LR2021_RX_BOOST_LF : 0); + } + + bool getRxBoostedGainMode() const override { + return ((CustomLR2021 *)_radio)->getRxBoostedGainMode(); + } +}; diff --git a/variants/esp32c3_supermini/pins_arduino.h b/variants/esp32c3_supermini/pins_arduino.h new file mode 100644 index 0000000000..2ac5b380fe --- /dev/null +++ b/variants/esp32c3_supermini/pins_arduino.h @@ -0,0 +1,42 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define PIN_NEOPIXEL 8 +static const uint8_t LED_BUILTIN = 8; +#define BUILTIN_LED LED_BUILTIN +#define RGB_BUILTIN LED_BUILTIN +#define RGB_BRIGHTNESS 64 + +static const uint8_t TX = 21; +static const uint8_t RX = 20; + +static const uint8_t SDA = 8; +static const uint8_t SCL = 9; + +static const uint8_t SS = 10; +static const uint8_t MOSI = 7; +static const uint8_t MISO = 2; +static const uint8_t SCK = 6; + +static const uint8_t A0 = 0; +static const uint8_t A1 = 1; +static const uint8_t A2 = 2; +static const uint8_t A3 = 3; +static const uint8_t A4 = 4; +static const uint8_t A5 = 5; + +static const uint8_t D0 = 0; +static const uint8_t D1 = 1; +static const uint8_t D2 = 2; +static const uint8_t D3 = 3; +static const uint8_t D4 = 4; +static const uint8_t D5 = 5; +static const uint8_t D6 = 6; +static const uint8_t D7 = 7; +static const uint8_t D8 = 8; +static const uint8_t D9 = 9; +static const uint8_t D10 = 10; + +#endif /* Pins_Arduino_h */ diff --git a/variants/nicerf_lr2021/EspIdfHal.h b/variants/nicerf_lr2021/EspIdfHal.h new file mode 100644 index 0000000000..18a8052cf0 --- /dev/null +++ b/variants/nicerf_lr2021/EspIdfHal.h @@ -0,0 +1,174 @@ +#pragma once + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "driver/spi_master.h" +#include "esp_timer.h" + +#define HAL_LOW (0x0) +#define HAL_HIGH (0x1) +#define HAL_INPUT (0x01) +#define HAL_OUTPUT (0x03) +#define HAL_RISING (0x01) +#define HAL_FALLING (0x02) +#define NOP() asm volatile("nop") + +class EspIdfHal : public RadioLibHal { +public: + EspIdfHal(int8_t sck, int8_t miso, int8_t mosi) + : RadioLibHal(HAL_INPUT, HAL_OUTPUT, HAL_LOW, HAL_HIGH, HAL_RISING, + HAL_FALLING), + spiSCK(sck), spiMISO(miso), spiMOSI(mosi), spiDev(nullptr), + spiInitialized(false) {} + + void init() override { spiBegin(); } + + void term() override { spiEnd(); } + + void pinMode(uint32_t pin, uint32_t mode) override { + if (pin == RADIOLIB_NC) + return; + gpio_config_t conf = {}; + conf.pin_bit_mask = (1ULL << pin); + conf.mode = (gpio_mode_t)mode; + conf.pull_up_en = GPIO_PULLUP_DISABLE; + conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + conf.intr_type = GPIO_INTR_DISABLE; + gpio_config(&conf); + } + + void digitalWrite(uint32_t pin, uint32_t value) override { + if (pin == RADIOLIB_NC) + return; + gpio_set_level((gpio_num_t)pin, value); + } + + uint32_t digitalRead(uint32_t pin) override { + if (pin == RADIOLIB_NC) + return (0); + return (gpio_get_level((gpio_num_t)pin)); + } + + void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), + uint32_t mode) override { + if (interruptNum == RADIOLIB_NC) + return; + gpio_install_isr_service((int)ESP_INTR_FLAG_IRAM); + gpio_set_intr_type((gpio_num_t)interruptNum, + (gpio_int_type_t)(mode & 0x7)); + gpio_isr_handler_add((gpio_num_t)interruptNum, + (void (*)(void *))interruptCb, NULL); + } + + void detachInterrupt(uint32_t interruptNum) override { + if (interruptNum == RADIOLIB_NC) + return; + gpio_isr_handler_remove((gpio_num_t)interruptNum); + gpio_set_intr_type((gpio_num_t)interruptNum, GPIO_INTR_DISABLE); + } + + void delay(unsigned long ms) override { + vTaskDelay(ms / portTICK_PERIOD_MS); + } + + void delayMicroseconds(unsigned long us) override { + uint64_t m = (uint64_t)esp_timer_get_time(); + if (us) { + uint64_t e = (m + us); + if (m > e) { + while ((uint64_t)esp_timer_get_time() > e) { + NOP(); + } + } + while ((uint64_t)esp_timer_get_time() < e) { + NOP(); + } + } + } + + unsigned long millis() override { + return ((unsigned long)(esp_timer_get_time() / 1000ULL)); + } + + unsigned long micros() override { + return ((unsigned long)(esp_timer_get_time())); + } + + long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override { + if (pin == RADIOLIB_NC) + return (0); + this->pinMode(pin, HAL_INPUT); + uint32_t start = this->micros(); + uint32_t curtick = this->micros(); + while (this->digitalRead(pin) == state) { + if ((this->micros() - curtick) > timeout) + return (0); + } + return (this->micros() - start); + } + + void spiBegin() override { + if (spiInitialized) + return; + spi_bus_config_t bus_cfg = {}; + bus_cfg.mosi_io_num = this->spiMOSI; + bus_cfg.miso_io_num = this->spiMISO; + bus_cfg.sclk_io_num = this->spiSCK; + bus_cfg.quadwp_io_num = -1; + bus_cfg.quadhd_io_num = -1; + bus_cfg.max_transfer_sz = 256; + esp_err_t ret = spi_bus_initialize(SPI2_HOST, &bus_cfg, SPI_DMA_CH_AUTO); + if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) { + return; + } + + spi_device_interface_config_t dev_cfg = {}; + dev_cfg.mode = 0; + dev_cfg.clock_speed_hz = 2000000; + dev_cfg.spics_io_num = -1; + dev_cfg.queue_size = 1; + ret = spi_bus_add_device(SPI2_HOST, &dev_cfg, &this->spiDev); + if (ret != ESP_OK) { + return; + } + this->spiInitialized = true; + } + + void spiBeginTransaction() override {} + + void spiTransfer(uint8_t *out, size_t len, uint8_t *in) override { + uint8_t *buf = out; + for (size_t i = 0; i < len; i++) { + spi_transaction_t trans = {}; + trans.flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA; + trans.length = 8; + trans.tx_data[0] = buf[i]; + esp_err_t ret = spi_device_polling_transmit(this->spiDev, &trans); + if (ret != ESP_OK) { + in[i] = 0xFF; + } else { + in[i] = trans.rx_data[0]; + } + } + } + + void spiEndTransaction() override {} + + void spiEnd() override { + if (this->spiDev) { + spi_bus_remove_device(this->spiDev); + this->spiDev = nullptr; + } + spi_bus_free(SPI2_HOST); + this->spiInitialized = false; + } + +private: + int8_t spiSCK; + int8_t spiMISO; + int8_t spiMOSI; + spi_device_handle_t spiDev; + bool spiInitialized; +}; diff --git a/variants/nicerf_lr2021/NiceRFLR2021Board.h b/variants/nicerf_lr2021/NiceRFLR2021Board.h new file mode 100644 index 0000000000..ef4340bd1f --- /dev/null +++ b/variants/nicerf_lr2021/NiceRFLR2021Board.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include + +#include +#include + +class NiceRFLR2021Board : 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)) { + startup_reason = BD_STARTUP_RX_PACKET; + } + } + +#ifdef PIN_VBAT_READ + pinMode(PIN_VBAT_READ, INPUT); +#endif + } + + void enterDeepSleep(uint32_t secs, int8_t wake_pin = -1) { + gpio_set_direction(gpio_num_t(P_LORA_DIO_1), GPIO_MODE_INPUT); + if (wake_pin >= 0) { + gpio_set_direction((gpio_num_t)wake_pin, GPIO_MODE_INPUT); + } + + gpio_deep_sleep_hold_en(); + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + + if (wake_pin >= 0) { + esp_deep_sleep_enable_gpio_wakeup( + (1 << P_LORA_DIO_1) | (1 << wake_pin), ESP_GPIO_WAKEUP_GPIO_HIGH); + } else { + esp_deep_sleep_enable_gpio_wakeup(1 << P_LORA_DIO_1, + ESP_GPIO_WAKEUP_GPIO_HIGH); + } + + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000); + } + + esp_deep_sleep_start(); + } + + uint16_t getBattMilliVolts() override { +#ifdef PIN_VBAT_READ + analogReadResolution(12); + uint32_t raw = 0; + for (int i = 0; i < 4; i++) { + raw += analogReadMilliVolts(PIN_VBAT_READ); + } + raw = raw / 4; + return (2 * raw); +#else + return 0; +#endif + } + + const char *getManufacturerName() const override { + return "NiceRF LR2021"; + } +}; diff --git a/variants/nicerf_lr2021/platformio.ini b/variants/nicerf_lr2021/platformio.ini new file mode 100644 index 0000000000..0f02a9abdf --- /dev/null +++ b/variants/nicerf_lr2021/platformio.ini @@ -0,0 +1,147 @@ +; NiceRF LoRa2021 (LR2021 Gen 4) + ESP32-C3_Mini_V1 +; Sub-GHz LoRa only (868 MHz band) +; Uses ESP-IDF SPI HAL (Arduino SPIClass returns all-zeros on this board) + +[NiceRF_LR2021] +extends = esp32_base +board = esp32c3_supermini +build_flags = + ${esp32_base.build_flags} + -I variants/nicerf_lr2021 + -D ESP32_CPU_FREQ=80 + -D PIN_VBAT_READ=0 + -D P_LORA_SCLK=6 + -D P_LORA_MISO=2 + -D P_LORA_MOSI=7 + -D P_LORA_DIO_1=5 + -D P_LORA_NSS=10 + -D P_LORA_RESET=3 + -D P_LORA_BUSY=4 + -D USE_LR2021 + -D RADIO_CLASS=CustomLR2021 + -D WRAPPER_CLASS=CustomLR2021Wrapper + -D LORA_TX_POWER=22 + -D RADIOLIB_EXCLUDE_SX126X=1 +build_src_filter = ${esp32_base.build_src_filter} + +<../variants/nicerf_lr2021> + + +lib_deps = + ${esp32_base.lib_deps} + +[env:LR2021_companion_radio_usb] +extends = NiceRF_LR2021 +build_src_filter = ${NiceRF_LR2021.build_src_filter} + +<../examples/companion_radio/*.cpp> + + +build_flags = + ${NiceRF_LR2021.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D OFFLINE_QUEUE_SIZE=256 +lib_deps = + ${NiceRF_LR2021.lib_deps} + ${esp32_ota.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:LR2021_companion_radio_ble] +extends = NiceRF_LR2021 +build_src_filter = ${NiceRF_LR2021.build_src_filter} + +<../examples/companion_radio/*.cpp> + + +build_flags = + ${NiceRF_LR2021.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D OFFLINE_QUEUE_SIZE=256 + -D BLE_PIN_CODE=123456 +lib_deps = + ${NiceRF_LR2021.lib_deps} + ${esp32_ota.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:LR2021_companion_radio_wifi] +extends = NiceRF_LR2021 +build_src_filter = ${NiceRF_LR2021.build_src_filter} + +<../examples/companion_radio/*.cpp> + + +build_flags = + ${NiceRF_LR2021.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D OFFLINE_QUEUE_SIZE=256 + -D WIFI_DEBUG_LOGGING=1 +lib_deps = + ${NiceRF_LR2021.lib_deps} + ${esp32_ota.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:LR2021_repeater] +extends = NiceRF_LR2021 +build_src_filter = ${NiceRF_LR2021.build_src_filter} + +<../examples/simple_repeater/*.cpp> +build_flags = + ${NiceRF_LR2021.build_flags} + ${sensor_base.build_flags} + -UENV_INCLUDE_GPS + -UENV_INCLUDE_VL53L0X + -D ADVERT_NAME='"LR2021 Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +lib_deps = + ${NiceRF_LR2021.lib_deps} + ${sensor_base.lib_deps} + ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 + +[env:LR2021_room_server] +extends = NiceRF_LR2021 +build_src_filter = ${NiceRF_LR2021.build_src_filter} + +<../examples/simple_room_server/*.cpp> +build_flags = + ${NiceRF_LR2021.build_flags} + ${sensor_base.build_flags} + -UENV_INCLUDE_GPS + -UENV_INCLUDE_VL53L0X + -D ADVERT_NAME='"LR2021 Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +lib_deps = + ${NiceRF_LR2021.lib_deps} + ${sensor_base.lib_deps} + ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 + +[env:LR2021_secure_chat] +extends = NiceRF_LR2021 +build_src_filter = ${NiceRF_LR2021.build_src_filter} + +<../examples/simple_secure_chat/*.cpp> +build_flags = + ${NiceRF_LR2021.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D OFFLINE_QUEUE_SIZE=256 + -D ADVERT_NAME='"LR2021 Chat"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 +lib_deps = + ${NiceRF_LR2021.lib_deps} + ${esp32_ota.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:LR2021_kiss_modem] +extends = NiceRF_LR2021 +build_src_filter = ${NiceRF_LR2021.build_src_filter} + +<../examples/kiss_modem/*.cpp> + + +build_flags = + ${NiceRF_LR2021.build_flags} + -D ADVERT_NAME='"LR2021 KISS"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 +lib_deps = + ${NiceRF_LR2021.lib_deps} + ${esp32_ota.lib_deps} diff --git a/variants/nicerf_lr2021/target.cpp b/variants/nicerf_lr2021/target.cpp new file mode 100644 index 0000000000..b98ddfb8e0 --- /dev/null +++ b/variants/nicerf_lr2021/target.cpp @@ -0,0 +1,34 @@ +#include +#include "target.h" +#include "EspIdfHal.h" + +NiceRFLR2021Board board; + +static EspIdfHal hal(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); +RADIO_CLASS radio(new Module(&hal, P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, + P_LORA_BUSY)); +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); + + hal.init(); + return radio.std_init(NULL); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); +} diff --git a/variants/nicerf_lr2021/target.h b/variants/nicerf_lr2021/target.h new file mode 100644 index 0000000000..397880c9c5 --- /dev/null +++ b/variants/nicerf_lr2021/target.h @@ -0,0 +1,17 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include + +extern NiceRFLR2021Board board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern EnvironmentSensorManager sensors; + +bool radio_init(); +mesh::LocalIdentity radio_new_identity();