diff --git a/boards/ESP32-S3-WROOM-1-N4.json b/boards/ESP32-S3-WROOM-1-N4.json new file mode 100644 index 000000000..3620a711d --- /dev/null +++ b/boards/ESP32-S3-WROOM-1-N4.json @@ -0,0 +1,39 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld" + }, + "core": "esp32", + "extra_flags": [ + "-D ARDUINO_USB_CDC_ON_BOOT=0", + "-D ARDUINO_USB_MSC_ON_BOOT=0", + "-D ARDUINO_USB_DFU_ON_BOOT=0", + "-D ARDUINO_USB_MODE=0", + "-D ARDUINO_RUNNING_CORE=1", + "-D ARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [["0x303A", "0x1001"]], + "mcu": "esp32s3", + "variant": "ESP32-S3-WROOM-1-N4" + }, + "connectivity": ["wifi"], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": ["esp-builtin"], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": ["arduino", "espidf"], + "name": "ESP32-S3-WROOM-1-N4 (4 MB Flash, No PSRAM)", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 524288, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://www.espressif.com/sites/default/files/documentation/esp32-s3-wroom-1_wroom-1u_datasheet_en.pdf", + "vendor": "Espressif" +} diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index e2e2188b6..ba2820a77 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -279,6 +279,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port) } else { // Unknown device LOG_INFO("No INA3221 found at address 0x%x\n", (uint8_t)addr.address); } + break; case MCP9808_ADDR: registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x07), 2); if (registerValue == 0x0400) { diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp index 26af7cac2..85931900f 100644 --- a/src/gps/RTC.cpp +++ b/src/gps/RTC.cpp @@ -104,13 +104,15 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv) bool shouldSet; if (q > currentQuality) { shouldSet = true; - LOG_DEBUG("Upgrading time to quality %d\n", q); - } else if (q == RTCQualityGPS && (now - lastSetMsec) > (12 * 60 * 60 * 1000UL)) { - // Every 12 hrs we will slam in a new GPS time, to correct for local RTC clock drift + LOG_DEBUG("Upgrading time to quality %s\n", RtcName(q)); + } else if (q >= RTCQualityNTP && (now - lastSetMsec) > (12 * 60 * 60 * 1000UL)) { + // Every 12 hrs we will slam in a new GPS or Phone GPS / NTP time, to correct for local RTC clock drift shouldSet = true; LOG_DEBUG("Reapplying external time to correct clock drift %ld secs\n", tv->tv_sec); - } else + } else { shouldSet = false; + LOG_DEBUG("Current RTC quality: %s. Ignoring time of RTC quality of %s\n", RtcName(currentQuality), RtcName(q)); + } if (shouldSet) { currentQuality = q; @@ -162,6 +164,24 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv) } } +const char *RtcName(RTCQuality quality) +{ + switch (quality) { + case RTCQualityNone: + return "None"; + case RTCQualityDevice: + return "Device"; + case RTCQualityFromNet: + return "Net"; + case RTCQualityNTP: + return "NTP"; + case RTCQualityGPS: + return "GPS"; + default: + return "Unknown"; + } +} + /** * Sets the RTC time if the provided time is of higher quality than the current RTC time. * diff --git a/src/gps/RTC.h b/src/gps/RTC.h index 0561819bd..1d609f136 100644 --- a/src/gps/RTC.h +++ b/src/gps/RTC.h @@ -28,6 +28,9 @@ RTCQuality getRTCQuality(); bool perhapsSetRTC(RTCQuality q, const struct timeval *tv); bool perhapsSetRTC(RTCQuality q, struct tm &t); +/// Return a string name for the quality +const char *RtcName(RTCQuality quality); + /// Return time since 1970 in secs. While quality is RTCQualityNone we will be returning time based at zero uint32_t getTime(bool local = false); diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 57040fbd6..4946672ec 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -150,12 +150,13 @@ class NodeDB void setLocalPosition(meshtastic_Position position, bool timeOnly = false) { if (timeOnly) { - LOG_DEBUG("Setting local position time only: time=%i\n", position.time); + LOG_DEBUG("Setting local position time only: time=%u timestamp=%u\n", position.time, position.timestamp); localPosition.time = position.time; + localPosition.timestamp = position.timestamp > 0 ? position.timestamp : position.time; return; } - LOG_DEBUG("Setting local position: latitude=%i, longitude=%i, time=%i\n", position.latitude_i, position.longitude_i, - position.time); + LOG_DEBUG("Setting local position: latitude=%i, longitude=%i, time=%u, timestamp=%u\n", position.latitude_i, + position.longitude_i, position.time, position.timestamp); localPosition = position; } diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp index 236178498..94e0aeadc 100644 --- a/src/modules/ExternalNotificationModule.cpp +++ b/src/modules/ExternalNotificationModule.cpp @@ -303,7 +303,8 @@ void ExternalNotificationModule::stopNow() { rtttl::stop(); #ifdef HAS_I2S - audioThread->stop(); + if (audioThread->isPlaying()) + audioThread->stop(); #endif nagCycleCutoff = 1; // small value isNagging = false; diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index dcfe03f7f..658b8b5a7 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -17,6 +17,7 @@ extern "C" { #include "mesh/compression/unishox2.h" +#include } PositionModule *positionModule; @@ -63,21 +64,15 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes } // Log packet size and data fields - LOG_INFO("POSITION node=%08x l=%d latI=%d lonI=%d msl=%d hae=%d geo=%d pdop=%d hdop=%d vdop=%d siv=%d fxq=%d fxt=%d pts=%d " - "time=%d\n", - getFrom(&mp), mp.decoded.payload.size, p.latitude_i, p.longitude_i, p.altitude, p.altitude_hae, - p.altitude_geoidal_separation, p.PDOP, p.HDOP, p.VDOP, p.sats_in_view, p.fix_quality, p.fix_type, p.timestamp, - p.time); + LOG_DEBUG("POSITION node=%08x l=%d latI=%d lonI=%d msl=%d hae=%d geo=%d pdop=%d hdop=%d vdop=%d siv=%d fxq=%d fxt=%d pts=%d " + "time=%d\n", + getFrom(&mp), mp.decoded.payload.size, p.latitude_i, p.longitude_i, p.altitude, p.altitude_hae, + p.altitude_geoidal_separation, p.PDOP, p.HDOP, p.VDOP, p.sats_in_view, p.fix_quality, p.fix_type, p.timestamp, + p.time); if (p.time && channels.getByIndex(mp.channel).role == meshtastic_Channel_Role_PRIMARY) { - struct timeval tv; - uint32_t secs = p.time; - - tv.tv_sec = secs; - tv.tv_usec = 0; - // Set from phone RTC Quality to RTCQualityNTP since it should be approximately so - perhapsSetRTC(isLocal ? RTCQualityNTP : RTCQualityFromNet, &tv); + trySetRtc(p, isLocal); } nodeDB->updatePosition(getFrom(&mp), p); @@ -92,6 +87,16 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes return false; // Let others look at this message also if they want } +void PositionModule::trySetRtc(meshtastic_Position p, bool isLocal) +{ + struct timeval tv; + uint32_t secs = p.time; + + tv.tv_sec = secs; + tv.tv_usec = 0; + perhapsSetRTC(isLocal ? RTCQualityNTP : RTCQualityFromNet, &tv); +} + meshtastic_MeshPacket *PositionModule::allocReply() { if (precision == 0) { @@ -222,6 +227,16 @@ meshtastic_MeshPacket *PositionModule::allocAtakPli() return mp; } +void PositionModule::sendOurPosition() +{ + bool requestReplies = currentGeneration != radioGeneration; + currentGeneration = radioGeneration; + + // If we changed channels, ask everyone else for their latest info + LOG_INFO("Sending pos@%x:6 to mesh (wantReplies=%d)\n", localPosition.timestamp, requestReplies); + sendOurPosition(NODENUM_BROADCAST, requestReplies); +} + void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t channel) { // cancel any not yet sent (now stale) position packets @@ -299,12 +314,7 @@ int32_t PositionModule::runOnce() lastGpsLatitude = node->position.latitude_i; lastGpsLongitude = node->position.longitude_i; - // If we changed channels, ask everyone else for their latest info - bool requestReplies = currentGeneration != radioGeneration; - currentGeneration = radioGeneration; - - LOG_INFO("Sending pos@%x:6 to mesh (wantReplies=%d)\n", localPosition.timestamp, requestReplies); - sendOurPosition(NODENUM_BROADCAST, requestReplies); + sendOurPosition(); if (config.device.role == meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND) { sendLostAndFoundText(); } @@ -314,29 +324,23 @@ int32_t PositionModule::runOnce() if (hasValidPosition(node2)) { // The minimum time (in seconds) that would pass before we are able to send a new position packet. - const uint32_t minimumTimeThreshold = - Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30); auto smartPosition = getDistanceTraveledSinceLastSend(node->position); + uint32_t msSinceLastSend = now - lastGpsSend; - if (smartPosition.hasTraveledOverThreshold && msSinceLastSend >= minimumTimeThreshold) { - bool requestReplies = currentGeneration != radioGeneration; - currentGeneration = radioGeneration; + if (smartPosition.hasTraveledOverThreshold && + Throttle::execute( + &lastGpsSend, minimumTimeThreshold, []() { positionModule->sendOurPosition(); }, + []() { LOG_DEBUG("Skipping send smart broadcast due to time throttling\n"); })) { - LOG_INFO("Sending smart pos@%x:6 to mesh (distanceTraveled=%fm, minDistanceThreshold=%im, timeElapsed=%ims, " - "minTimeInterval=%ims)\n", - localPosition.timestamp, smartPosition.distanceTraveled, smartPosition.distanceThreshold, - msSinceLastSend, minimumTimeThreshold); - sendOurPosition(NODENUM_BROADCAST, requestReplies); + LOG_DEBUG("Sent smart pos@%x:6 to mesh (distanceTraveled=%fm, minDistanceThreshold=%im, timeElapsed=%ims, " + "minTimeInterval=%ims)\n", + localPosition.timestamp, smartPosition.distanceTraveled, smartPosition.distanceThreshold, + msSinceLastSend, minimumTimeThreshold); // Set the current coords as our last ones, after we've compared distance with current and decided to send lastGpsLatitude = node->position.latitude_i; lastGpsLongitude = node->position.longitude_i; - - /* Update lastGpsSend to now. This means if the device is stationary, then - getPref_position_broadcast_secs will still apply. - */ - lastGpsSend = now; } } } @@ -396,27 +400,21 @@ void PositionModule::handleNewPosition() meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); const meshtastic_NodeInfoLite *node2 = service.refreshLocalMeshNode(); // should guarantee there is now a position // We limit our GPS broadcasts to a max rate - uint32_t now = millis(); - uint32_t msSinceLastSend = now - lastGpsSend; - if (hasValidPosition(node2)) { auto smartPosition = getDistanceTraveledSinceLastSend(node->position); - if (smartPosition.hasTraveledOverThreshold) { - bool requestReplies = currentGeneration != radioGeneration; - currentGeneration = radioGeneration; - - LOG_INFO("Sending smart pos@%x:6 to mesh (distanceTraveled=%fm, minDistanceThreshold=%im, timeElapsed=%ims)\n", - localPosition.timestamp, smartPosition.distanceTraveled, smartPosition.distanceThreshold, msSinceLastSend); - sendOurPosition(NODENUM_BROADCAST, requestReplies); + uint32_t msSinceLastSend = millis() - lastGpsSend; + if (smartPosition.hasTraveledOverThreshold && + Throttle::execute( + &lastGpsSend, minimumTimeThreshold, []() { positionModule->sendOurPosition(); }, + []() { LOG_DEBUG("Skipping send smart broadcast due to time throttling\n"); })) { + LOG_DEBUG("Sent smart pos@%x:6 to mesh (distanceTraveled=%fm, minDistanceThreshold=%im, timeElapsed=%ims, " + "minTimeInterval=%ims)\n", + localPosition.timestamp, smartPosition.distanceTraveled, smartPosition.distanceThreshold, msSinceLastSend, + minimumTimeThreshold); // Set the current coords as our last ones, after we've compared distance with current and decided to send lastGpsLatitude = node->position.latitude_i; lastGpsLongitude = node->position.longitude_i; - - /* Update lastGpsSend to now. This means if the device is stationary, then - getPref_position_broadcast_secs will still apply. - */ - lastGpsSend = now; } } } diff --git a/src/modules/PositionModule.h b/src/modules/PositionModule.h index 68171ab0e..89ff50c64 100644 --- a/src/modules/PositionModule.h +++ b/src/modules/PositionModule.h @@ -1,4 +1,5 @@ #pragma once +#include "Default.h" #include "ProtobufModule.h" #include "concurrency/OSThread.h" @@ -29,7 +30,8 @@ class PositionModule : public ProtobufModule, private concu /** * Send our position into the mesh */ - void sendOurPosition(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false, uint8_t channel = 0); + void sendOurPosition(NodeNum dest, bool wantReplies = false, uint8_t channel = 0); + void sendOurPosition(); void handleNewPosition(); @@ -50,8 +52,12 @@ class PositionModule : public ProtobufModule, private concu private: struct SmartPosition getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition); meshtastic_MeshPacket *allocAtakPli(); + void trySetRtc(meshtastic_Position p, bool isLocal); uint32_t precision; void sendLostAndFoundText(); + + const uint32_t minimumTimeThreshold = + Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30); }; struct SmartPosition { diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index c6d90970f..27088f86f 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -127,6 +127,10 @@ #define HW_VENDOR meshtastic_HardwareModel_BETAFPV_900_NANO_TX #elif defined(PICOMPUTER_S3) #define HW_VENDOR meshtastic_HardwareModel_PICOMPUTER_S3 +#elif defined(HELTEC_HT62) +#define HW_VENDOR meshtastic_HardwareModel_HELTEC_HT62 +#elif defined(EBYTE_ESP32_S3) +#define HW_VENDOR meshtastic_HardwareModel_EBYTE_ESP32_S3 #elif defined(ESP32_S3_PICO) #define HW_VENDOR meshtastic_HardwareModel_ESP32_S3_PICO #elif defined(SENSELORA_S3) diff --git a/variants/EBYTE_ESP32-S3/pins_arduino.h b/variants/EBYTE_ESP32-S3/pins_arduino.h new file mode 100644 index 000000000..38a9103f0 --- /dev/null +++ b/variants/EBYTE_ESP32-S3/pins_arduino.h @@ -0,0 +1,37 @@ +// Need this file for ESP32-S3 +// No need to modify this file, changes to pins imported from variant.h +// Most is similar to https://github.com/espressif/arduino-esp32/blob/master/variants/esp32s3/pins_arduino.h + +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include + +#define USB_VID 0x303a +#define USB_PID 0x1001 + +#define EXTERNAL_NUM_INTERRUPTS 46 +#define NUM_DIGITAL_PINS 48 +#define NUM_ANALOG_INPUTS 20 + +#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) +#define digitalPinToInterrupt(p) \ + (((p) < 48) ? (p) : -1) // Maybe it should be <= 48 but this is from a trustworthy source so it is likely correct +#define digitalPinHasPWM(p) (p < 46) + +// Serial +static const uint8_t TX = UART_TX; +static const uint8_t RX = UART_RX; + +// Default SPI will be mapped to Radio +static const uint8_t SS = LORA_CS; +static const uint8_t SCK = LORA_SCK; +static const uint8_t MOSI = LORA_MOSI; +static const uint8_t MISO = LORA_MISO; + +// The default Wire will be mapped to PMU and RTC +static const uint8_t SCL = I2C_SCL; +static const uint8_t SDA = I2C_SDA; + +#endif /* Pins_Arduino_h */ diff --git a/variants/EBYTE_ESP32-S3/platformio.ini b/variants/EBYTE_ESP32-S3/platformio.ini new file mode 100644 index 000000000..10de91386 --- /dev/null +++ b/variants/EBYTE_ESP32-S3/platformio.ini @@ -0,0 +1,9 @@ +[env:EBYTE_ESP32-S3] +extends = esp32s3_base +; board assumes the lowest spec WROOM module: 4 MB (Quad SPI) Flash, No PSRAM +board = ESP32-S3-WROOM-1-N4 +board_level = extra +build_flags = + ${esp32s3_base.build_flags} + -D EBYTE_ESP32_S3 + -I variants/EBYTE_ESP32-S3 diff --git a/variants/EBYTE_ESP32-S3/variant.h b/variants/EBYTE_ESP32-S3/variant.h new file mode 100644 index 000000000..10b39617b --- /dev/null +++ b/variants/EBYTE_ESP32-S3/variant.h @@ -0,0 +1,193 @@ +// Supporting information: https://github.com/S5NC/EBYTE_ESP32-S3/ + +// Originally developed for E22-900M30S with ESP32-S3-WROOM-1-N4 +// NOTE: Uses ESP32-S3-WROOM-1-N4.json in boards folder (via platformio.ini board field), assumes 4 MB (quad SPI) flash, no PSRAM + +// FIXME: implement SX12 module type autodetection and have setup for each case (add E32 support) +// E32 has same pinout except having extra pins. I assume that the GND on it is connected internally to other GNDs so it is not a +// problem to NC the extra GND pins. + +// For each EBYTE module pin in this section, provide the pin number of the ESP32-S3 you connected it to +// The ESP32-S3 is great because YOU CAN USE PRACTICALLY ANY PINS for the connections, but avoid some pins (such as on the WROOM +// modules the following): strapping pins (except 0 as a user button input as it already has a pulldown resistor in typical +// application schematic) (0, 3, 45, 46), USB-reserved (19, 20), and pins which aren't present on the WROOM-2 module for +// compatiblity as it uses octal SPI, or are likely connected internally in either WROOM version (26-37), and avoid pins whose +// voltages are set by the SPI voltage (47, 48), and pins that don't exist (22-25) You can ALSO set the SPI pins (SX126X_CS, +// SX126X_SCK, SX126X_MISO, SX126X_MOSI) to any pin with the ESP32-S3 due to \ GPIO Matrix / IO MUX / RTC IO MUX \, and also the +// serial pins, but this isn't recommended for Serial0 as the WROOM modules have a 499 Ohm resistor on U0TXD (to reduce harmonics +// but also acting as a sort of protection) + +// We have many free pins on the ESP32-S3-WROOM-X-Y module, perhaps it is best to use one of its pins to control TXEN, and use +// DIO2 as an extra interrupt, but right now Meshtastic does not benefit from having another interrupt pin available. + +// Adding two 0-ohm links on your PCB design so that you can choose between the two modes for controlling the E22's TXEN would +// enable future software to make the most of an extra available interrupt pin + +// Possible improvement: can add extremely low resistance MOSFET to physically toggle power to E22 module when in full sleep (not +// waiting for interrupt)? + +// PA stands for Power Amplifier, used when transmitting to increase output power +// LNA stands for Low Noise Amplifier, used when \ listening for / receiving \ data to increase sensitivity + +////////////////////////////////////////////////////////////////////////////////// +// // +// Have custom connections or functionality? Configure them in this section // +// // +////////////////////////////////////////////////////////////////////////////////// + +#define SX126X_CS 14 // EBYTE module's NSS pin // FIXME: rename to SX126X_SS +#define LORA_SCK 21 // EBYTE module's SCK pin +#define LORA_MOSI 38 // EBYTE module's MOSI pin +#define LORA_MISO 39 // EBYTE module's MISO pin +#define SX126X_RESET 40 // EBYTE module's NRST pin +#define SX126X_BUSY 41 // EBYTE module's BUSY pin +#define SX126X_DIO1 42 // EBYTE module's DIO1 pin +// We don't define a pin for SX126X_DIO2 as Meshtastic doesn't use it as an interrupt output, so it is never connected to an MCU +// pin! Also E22 module datasheets say not to connect it to an MCU pin. +// We don't define a pin for SX126X_DIO3 as Meshtastic doesn't use it as an interrupt output, so it is never connected to an MCU +// pin! Also E22 module datasheets say to use it as the TCXO's reference voltage. +// E32 module (which uses SX1276) may not have ability to set TCXO voltage using a DIO pin. + +// The radio module needs to be told whether to enable RX mode or TX mode. Each radio module takes different actions based on +// these values, but generally the path from the antenna to SX1262 is changed from signal output to signal input. Also, if there +// are LNAs (Low-Noise Amplifiers) or PAs (Power Amplifiers) in the output or input paths, their power is also controlled by +// these pins. You should never have both TXEN and RXEN set high, this can cause problems for some radio modules, and is +// commonly referred to as 'undefined behaviour' in datasheets. For the SX1262, you shouldn't connect DIO2 to the MCU. DIO2 is +// an output only, and can be controlled via SPI instructions, the use for this is to save an MCU pin by using the DIO2 pin to +// control the RF switching mode. + +// Choose ONLY ONE option from below, comment in/out the '/*'s and '*/'s +// SX126X_TXEN is the E22's [SX1262's] TXEN pin, SX126X_RXEN is the E22's [SX1262's] RXEN pin + +// Option 1: E22's TXEN pin connected to E22's DIO2 pin, E22's RXEN pin connected to NEGATED output of E22's DIO2 pin (more +// expensive option hardware-wise, is the 'most proper' way, removes need for routing one/two traces from MCU to RF switching +// pins), however you can't have E22 in low-power 'sleep' mode (TXEN and RXEN both low cannot be achieved this this option). +/* +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_TXEN RADIOLIB_NC +#define SX126X_RXEN RADIOLIB_NC +*/ + +// Option 2: E22's TXEN pin connected to E22's DIO2 pin, E22's RXEN pin connected to MCU pin (cheaper option hardware-wise, +// removes need for routing another trace from MCU to an RF switching pin). +// /* +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_TXEN RADIOLIB_NC +#define SX126X_RXEN 10 +// */ + +// Option 3: E22's TXEN pin connected to MCU pin, E22's RXEN pin connected to MCU pin (cheaper option hardware-wise, allows for +// ramping up PA before transmission (add/expand on feature yourself in RadioLib) if PA takes a while to stabilise) +// Don't define DIO2_AS_RF_SWITCH because we only use DIO2 or an MCU pin mutually exclusively to connect to E22's TXEN (to prevent +// a short if they are both connected at the same time (suboptimal PCB design) and there's a slight non-neglibible delay and/or +// voltage difference between DIO2 and TXEN). Can use DIO2 as an IRQ (but not in Meshtastic at the moment). +/* +#define SX126X_TXEN 9 +#define SX126X_RXEN 10 +*/ + +// (NOT RECOMMENDED, if need to ramp up PA before transmission, better to use option 3) +// Option 4: E22's TXEN pin connected to MCU pin, E22's RXEN pin connected to NEGATED output of E22's DIO2 pin (more expensive +// option hardware-wise, allows for ramping up PA before transmission (add/expand on feature yourself in RadioLib) if PA takes +// a while to stabilise, removes need for routing another trace from MCU to an RF switching pin, however may mean if in +// RadioLib you don't tell DIO2 to go high to indicate transmission (so the negated output goes to RXEN to turn the LNA off) +// then you may end up enabling E22's TXEN and RXEN pins at the same time whilst you ramp up the PA which is not ideal, +// changing DIO2's switching advance in RadioLib may not even be possible, may be baked into the SX126x). +/* +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_TXEN 9 +#define SX126X_RXEN RADIOLIB_NC +*/ + +// Status +#define LED_PIN 1 +#define LED_INVERTED 0 +// External notification +// FIXME: Check if EXT_NOTIFY_OUT actualy has any effect and removes the need for setting the external notication pin in the +// app/preferences +#define EXT_NOTIFY_OUT 2 // The GPIO pin that acts as the external notification output (here we connect an LED to it) +// Buzzer +#define PIN_BUZZER 11 +// Buttons +#define BUTTON_PIN 0 // Use the BOOT button as the user button +// I2C +#define I2C_SCL 18 +#define I2C_SDA 8 +// UART +#define UART_TX 43 +#define UART_RX 44 + +// Power +// Outputting 22dBm from SX1262 results in ~30dBm E22-900M30S output (module only uses last stage of the YP2233W PA) +// Respect local regulations! If your E22-900M30S outputs the advertised 30 dBm and you use a 6 dBi antenna, you are at the +// equivalent of 36 EIRP (Effective Isotropic Radiated Power), which in this case is the limit for non-HAM users in the US (4W +// EIRP, at SPECIFIC frequencies). +// In the EU (and UK), as of now, you are allowed 27 dBm ERP which is 29.15 EIRP. +// https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32022D0180 +// https://www.legislation.gov.uk/uksi/1999/930/schedule/6/made +// To respect the 29.15 dBm EIRP (at SPECIFIC frequencies, others are lower) EU limit with a 2.5 dBi gain antenna, consulting +// https://github.com/S5NC/EBYTE_ESP32-S3/blob/main/power%20testing.txt, assuming 0.1 dBm insertion loss, output 20 dBm from the +// E22-900M30S's SX1262. It is worth noting that if you are in this situation and don't have a HAM license, you may be better off +// with a lower gain antenna, and output the difference as a higher total power input into the antenna, as your EIRP would be the +// same, but you would get a wider angle of coverage. Also take insertion loss and possibly VSWR into account +// (https://www.everythingrf.com/tech-resources/vswr). Please check regulations yourself and check airtime, usage (for example +// whether you are airborne), frequency, and power laws. +#define SX126X_MAX_POWER 22 // SX126xInterface.cpp defaults to 22 if not defined, but here we define it for good practice + +// Display +// FIXME: change behavior in src to default to not having screen if is undefined +// FIXME: remove 0/1 option for HAS_SCREEN in src, change to being defined or not +// FIXME: check if it actually causes a crash when not specifiying that a display isn't present +#define HAS_SCREEN 0 // Assume no screen present by default to prevent crash... + +// GPS +// FIXME: unsure what to define HAS_GPS as if GPS isn't always present +#define HAS_GPS 1 // Don't need to set this to 0 to prevent a crash as it doesn't crash if GPS not found, will probe by default +#define PIN_GPS_EN 15 +#define GPS_EN_ACTIVE 1 +#define GPS_TX_PIN 16 +#define GPS_RX_PIN 17 + +///////////////////////////////////////////////////////////////////////////////// +// // +// You should have no need to modify the code below, nor in pins_arduino.h // +// // +///////////////////////////////////////////////////////////////////////////////// + +#define USE_SX1262 // E22-900M30S, E22-900M22S, and E22-900MM22S (not E220!) use SX1262 +#define USE_SX1268 // E22-400M30S, E22-400M33S, E22-400M22S, and E22-400MM22S use SX1268 + +// The below isn't needed as we directly define SX126X_TXEN and SX126X_RXEN instead of using proxies E22_TXEN and E22_RXEN +/* +// FALLBACK: If somehow E22_TXEN isn't defined or clearly isn't a valid pin number, set it to RADIOLIB_NC to avoid SX126X_TXEN +being defined but having no value #if (!defined(E22_TXEN) || !(0 <= E22_TXEN && E22_TXEN <= 48)) #define E22_TXEN RADIOLIB_NC +#endif +// FALLBACK: If somehow E22_RXEN isn't defined or clearly isn't a valid pin number, set it to RADIOLIB_NC to avoid SX126X_RXEN +being defined but having no value #if (!defined(E22_RXEN) || !(0 <= E22_RXEN && E22_RXEN <= 48)) #define E22_RXEN RADIOLIB_NC +#endif +#define SX126X_TXEN E22_TXEN +#define SX126X_RXEN E22_RXEN +*/ + +// E22 series TCXO voltage is 1.8V per https://www.ebyte.com/en/pdf-down.aspx?id=781 (source +// https://github.com/jgromes/RadioLib/issues/12#issuecomment-520695575), so set it as such +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +#define LORA_CS SX126X_CS // FIXME: for some reason both are used in /src + +// Many of the below values would only be used if USE_RF95 was defined, but it's not as we aren't actually using an RF95, just +// that the 4 pins above are named like it If they aren't used they don't need to be defined and doing so cause confusion to those +// adapting this file LORA_RESET value is never used in src (as we are not using RF95), so no need to define LORA_DIO0 is not used +// in src (as we are not using RF95) as SX1262 does not have it per SX1262 datasheet, so no need to define +// FIXME: confirm that the linked lines below are actually only called when using the SX126x or SX128x and no other modules +// then use SX126X_DIO1 and SX128X_DIO1 respectively for that purpose, removing the need for RF95-style LORA_* definitions when +// the RF95 isn't used +#define LORA_DIO1 \ + SX126X_DIO1 // The old name is used in + // https://github.com/meshtastic/firmware/blob/7eff5e7bcb2084499b723c5e3846c15ee089e36d/src/sleep.cpp#L298, so + // must also define the old name +// LORA_DIO2 value is never used in src (as we are not using RF95), so no need to define, and if DIO2_AS_RF_SWITCH is set then it +// cannot serve any extra function even if requested to LORA_DIO3 value is never used in src (as we are not using RF95), so no +// need to define, and DIO3_AS_TCXO_AT_1V8 is set so it cannot serve any extra function even if requested to (from 13.3.2.1 +// DioxMask in SX1262 datasheet: Note that if DIO2 or DIO3 are used to control the RF Switch or the TCXO, the IRQ will not be +// generated even if it is mapped to the pins.) \ No newline at end of file