diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini index 07151c4a3..53f06c9f3 100644 --- a/arch/portduino/portduino.ini +++ b/arch/portduino/portduino.ini @@ -1,6 +1,6 @@ ; The Portduino based sim environment on top of any host OS, all hardware will be simulated [portduino_base] -platform = https://github.com/meshtastic/platform-native.git#c95616208ffff4c8a36d48df810a3f072cce3521 +platform = https://github.com/meshtastic/platform-native.git#6fb39b6f94ece9c042141edb4afb91aca94dcaab framework = arduino build_src_filter = diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index 5a8e658cb..f729f1ac7 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -101,18 +101,20 @@ Display: # Height: 240 Touchscreen: +### Note, at least for now, the touchscreen must have a CS pin defined, even if you let Linux manage the CS switching. + # Module: STMPE610 # CS: 7 # IRQ: 24 -# Module: XPT2046 +# Module: XPT2046 # Waveshare 2.8inch # CS: 7 # IRQ: 17 ### Configure device for direct keyboard input Input: -# KeyboardDevice: /dev/input/event0 +# KeyboardDevice: /dev/input/by-id/usb-_Raspberry_Pi_Internal_Keyboard-event-kbd ### diff --git a/boards/CDEBYTE_EoRa-S3.json b/boards/CDEBYTE_EoRa-S3.json new file mode 100644 index 000000000..9ecee3c9f --- /dev/null +++ b/boards/CDEBYTE_EoRa-S3.json @@ -0,0 +1,38 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld" + }, + "core": "esp32", + "extra_flags": [ + "-D CDEBYTE_EORA_S3", + "-D ARDUINO_USB_CDC_ON_BOOT=1", + "-D ARDUINO_USB_MODE=0", + "-D ARDUINO_RUNNING_CORE=1", + "-D ARDUINO_EVENT_RUNNING_CORE=1", + "-D BOARD_HAS_PSRAM" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dio", + "hwids": [["0x303A", "0x1001"]], + "mcu": "esp32s3", + "variant": "CDEBYTE_EoRa-S3" + }, + "connectivity": ["wifi"], + "debug": { + "openocd_target": "esp32s3.cfg" + }, + "frameworks": ["arduino", "espidf"], + "name": "CDEBYTE EoRa-S3", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "wait_for_upload_port": true, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://www.cdebyte.com/Module-Testkits-EoRaPI", + "vendor": "CDEBYTE" +} diff --git a/boards/tbeam-s3-core.json b/boards/tbeam-s3-core.json index 8d2c3eed6..4c82a2789 100644 --- a/boards/tbeam-s3-core.json +++ b/boards/tbeam-s3-core.json @@ -7,7 +7,6 @@ "extra_flags": [ "-DBOARD_HAS_PSRAM", "-DLILYGO_TBEAM_S3_CORE", - "-DARDUINO_USB_CDC_ON_BOOT=1", "-DARDUINO_USB_MODE=1", "-DARDUINO_RUNNING_CORE=1", "-DARDUINO_EVENT_RUNNING_CORE=1" diff --git a/protobufs b/protobufs index f92900c5f..ecf105f66 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit f92900c5f884b04388fb7abf61d4df66783015e4 +Subproject commit ecf105f66d182531423b73f4408c53701313c4eb diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index ac48e664c..4f42b36b5 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -185,10 +185,12 @@ static void powerEnter() screen->setOn(true); setBluetoothEnable(true); // within enter() the function getState() returns the state we came from - if (strcmp(powerFSM.getState()->name, "BOOT") != 0 && strcmp(powerFSM.getState()->name, "POWER") != 0 && + + // Mothballed: print change of power-state to device screen + /* if (strcmp(powerFSM.getState()->name, "BOOT") != 0 && strcmp(powerFSM.getState()->name, "POWER") != 0 && strcmp(powerFSM.getState()->name, "DARK") != 0) { screen->print("Powered...\n"); - } + }*/ } } @@ -205,8 +207,10 @@ static void powerExit() { screen->setOn(true); setBluetoothEnable(true); - if (!isPowered()) - screen->print("Unpowered...\n"); + + // Mothballed: print change of power-state to device screen + /*if (!isPowered()) + screen->print("Unpowered...\n");*/ } static void onEnter() diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 16906e2e0..e09e5fe30 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -182,11 +182,11 @@ size_t RedirectablePrint::log(const char *logLevel, const char *format, ...) void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16_t len) { const char alphabet[17] = "0123456789abcdef"; - log(logLevel, " +------------------------------------------------+ +----------------+\n"); - log(logLevel, " |.0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .a .b .c .d .e .f | | ASCII |\n"); + log(logLevel, " +------------------------------------------------+ +----------------+\n"); + log(logLevel, " |.0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .a .b .c .d .e .f | | ASCII |\n"); for (uint16_t i = 0; i < len; i += 16) { if (i % 128 == 0) - log(logLevel, " +------------------------------------------------+ +----------------+\n"); + log(logLevel, " +------------------------------------------------+ +----------------+\n"); char s[] = "| | | |\n"; uint8_t ix = 1, iy = 52; for (uint8_t j = 0; j < 16; j++) { @@ -208,7 +208,7 @@ void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16 log(logLevel, "."); log(logLevel, s); } - log(logLevel, " +------------------------------------------------+ +----------------+\n"); + log(logLevel, " +------------------------------------------------+ +----------------+\n"); } std::string RedirectablePrint::mt_sprintf(const std::string fmt_str, ...) diff --git a/src/configuration.h b/src/configuration.h index 37b67f666..701e07a32 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -74,6 +74,13 @@ along with this program. If not, see . #define RTC_DATA_ATTR #endif +// ----------------------------------------------------------------------------- +// Regulatory overrides for producing regional builds +// ----------------------------------------------------------------------------- + +// Define if region should override user saved region +// #define LORA_REGIONCODE meshtastic_Config_LoRaConfig_RegionCode_SG_923 + // ----------------------------------------------------------------------------- // Feature toggles // ----------------------------------------------------------------------------- diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 6a0e3e44a..0d0bfd9a2 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -7,6 +7,8 @@ #include "main.h" // pmu_found #include "sleep.h" + +#include "cas.h" #include "ubx.h" #ifdef ARCH_PORTDUINO @@ -51,6 +53,28 @@ void GPS::UBXChecksum(uint8_t *message, size_t length) message[length - 1] = CK_B; } +// Calculate the checksum for a CAS packet +void GPS::CASChecksum(uint8_t *message, size_t length) +{ + uint32_t cksum = ((uint32_t)message[5] << 24); // Message ID + cksum += ((uint32_t)message[4]) << 16; // Class + cksum += message[2]; // Payload Len + + // Iterate over the payload as a series of uint32_t's and + // accumulate the cksum + uint32_t *payload = (uint32_t *)(message + 6); + for (size_t i = 0; i < (length - 10) / 4; i++) { + uint32_t p = payload[i]; + cksum += p; + } + + // Place the checksum values in the message + message[length - 4] = (cksum & 0xFF); + message[length - 3] = (cksum & (0xFF << 8)) >> 8; + message[length - 2] = (cksum & (0xFF << 16)) >> 16; + message[length - 1] = (cksum & (0xFF << 24)) >> 24; +} + // Function to create a ublox packet for editing in memory uint8_t GPS::makeUBXPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg) { @@ -72,6 +96,41 @@ uint8_t GPS::makeUBXPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_siz return (payload_size + 8); } +// Function to create a CAS packet for editing in memory +uint8_t GPS::makeCASPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg) +{ + // General CAS structure + // | H1 | H2 | payload_len | cls | msg | Payload ... | Checksum | + // Size: | 1 | 1 | 2 | 1 | 1 | payload_len | 4 | + // Pos: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 ... | 6 + payload_len ... | + // |------|------|-------------|------|------|------|--------------|---------------------------| + // | 0xBA | 0xCE | 0xXX | 0xXX | 0xXX | 0xXX | 0xXX | 0xXX ... | 0xXX | 0xXX | 0xXX | 0xXX | + + // Construct the CAS packet + UBXscratch[0] = 0xBA; // header 1 (0xBA) + UBXscratch[1] = 0xCE; // header 2 (0xCE) + UBXscratch[2] = payload_size; // length 1 + UBXscratch[3] = 0; // length 2 + UBXscratch[4] = class_id; // class + UBXscratch[5] = msg_id; // id + + UBXscratch[6 + payload_size] = 0x00; // Checksum + UBXscratch[7 + payload_size] = 0x00; + UBXscratch[8 + payload_size] = 0x00; + UBXscratch[9 + payload_size] = 0x00; + + for (int i = 0; i < payload_size; i++) { + UBXscratch[6 + i] = pgm_read_byte(&msg[i]); + } + CASChecksum(UBXscratch, (payload_size + 10)); + +#if defined(GPS_DEBUG) && defined(DEBUG_PORT) + LOG_DEBUG("Constructed CAS packet: \n"); + DEBUG_PORT.hexDump(MESHTASTIC_LOG_LEVEL_DEBUG, UBXscratch, payload_size + 10); +#endif + return (payload_size + 10); +} + GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis) { uint8_t buffer[768] = {0}; @@ -81,6 +140,7 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis) while (millis() < startTimeout) { if (_serial_gps->available()) { b = _serial_gps->read(); + #ifdef GPS_DEBUG LOG_DEBUG("%02X", (char *)buffer); #endif @@ -104,6 +164,67 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis) return GNSS_RESPONSE_NONE; } +GPS_RESPONSE GPS::getACKCas(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) +{ + uint32_t startTime = millis(); + uint8_t buffer[CAS_ACK_NACK_MSG_SIZE] = {0}; + uint8_t bufferPos = 0; + + // CAS-ACK-(N)ACK structure + // | H1 | H2 | Payload Len | cls | msg | Payload | Checksum (4) | + // | | | | | | Cls | Msg | Reserved | | + // |------|------|-------------|------|------|------|------|-------------|---------------------------| + // ACK-NACK| 0xBA | 0xCE | 0x04 | 0x00 | 0x05 | 0x00 | 0xXX | 0xXX | 0x00 | 0x00 | 0xXX | 0xXX | 0xXX | 0xXX | + // ACK-ACK | 0xBA | 0xCE | 0x04 | 0x00 | 0x05 | 0x01 | 0xXX | 0xXX | 0x00 | 0x00 | 0xXX | 0xXX | 0xXX | 0xXX | + + while (millis() - startTime < waitMillis) { + if (_serial_gps->available()) { + buffer[bufferPos++] = _serial_gps->read(); + + // keep looking at the first two bytes of buffer until + // we have found the CAS frame header (0xBA, 0xCE), if not + // keep reading bytes until we find a frame header or we run + // out of time. + if ((bufferPos == 2) && !(buffer[0] == 0xBA && buffer[1] == 0xCE)) { + buffer[0] = buffer[1]; + buffer[1] = 0; + bufferPos = 1; + } + } + + // we have read all the bytes required for the Ack/Nack (14-bytes) + // and we must have found a frame to get this far + if (bufferPos == sizeof(buffer) - 1) { + uint8_t msg_cls = buffer[4]; // message class should be 0x05 + uint8_t msg_msg_id = buffer[5]; // message id should be 0x00 or 0x01 + uint8_t payload_cls = buffer[6]; // payload class id + uint8_t payload_msg = buffer[7]; // payload message id + + // Check for an ACK-ACK for the specified class and message id + if ((msg_cls == 0x05) && (msg_msg_id == 0x01) && payload_cls == class_id && payload_msg == msg_id) { +#ifdef GPS_DEBUG + LOG_INFO("Got ACK for class %02X message %02X in %d millis.\n", class_id, msg_id, millis() - startTime); +#endif + return GNSS_RESPONSE_OK; + } + + // Check for an ACK-NACK for the specified class and message id + if ((msg_cls == 0x05) && (msg_msg_id == 0x00) && payload_cls == class_id && payload_msg == msg_id) { +#ifdef GPS_DEBUG + LOG_WARN("Got NACK for class %02X message %02X in %d millis.\n", class_id, msg_id, millis() - startTime); +#endif + return GNSS_RESPONSE_NAK; + } + + // This isn't the frame we are looking for, clear the buffer + // and try again until we run out of time. + memset(buffer, 0x0, sizeof(buffer)); + bufferPos = 0; + } + } + return GNSS_RESPONSE_NONE; +} + GPS_RESPONSE GPS::getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) { uint8_t b; @@ -313,6 +434,33 @@ bool GPS::setup() // Switch to Fitness Mode, for running and walking purpose with low speed (<5 m/s) _serial_gps->write("$PMTK886,1*29\r\n"); delay(250); + } else if (gnssModel == GNSS_MODEL_ATGM336H) { + // Set the intial configuration of the device - these _should_ work for most AT6558 devices + msglen = makeCASPacket(0x06, 0x07, sizeof(_message_CAS_CFG_NAVX_CONF), _message_CAS_CFG_NAVX_CONF); + _serial_gps->write(UBXscratch, msglen); + if (getACKCas(0x06, 0x07, 250) != GNSS_RESPONSE_OK) { + LOG_WARN("ATGM336H - Could not set Configuration"); + } + + // Set the update frequence to 1Hz + msglen = makeCASPacket(0x06, 0x04, sizeof(_message_CAS_CFG_RATE_1HZ), _message_CAS_CFG_RATE_1HZ); + _serial_gps->write(UBXscratch, msglen); + if (getACKCas(0x06, 0x04, 250) != GNSS_RESPONSE_OK) { + LOG_WARN("ATGM336H - Could not set Update Frequency"); + } + + // Set the NEMA output messages + // Ask for only RMC and GGA + uint8_t fields[] = {CAS_NEMA_RMC, CAS_NEMA_GGA}; + for (int i = 0; i < sizeof(fields); i++) { + // Construct a CAS-CFG-MSG packet + uint8_t cas_cfg_msg_packet[] = {0x4e, fields[i], 0x01, 0x00}; + msglen = makeCASPacket(0x06, 0x01, sizeof(cas_cfg_msg_packet), cas_cfg_msg_packet); + _serial_gps->write(UBXscratch, msglen); + if (getACKCas(0x06, 0x01, 250) != GNSS_RESPONSE_OK) { + LOG_WARN("ATGM336H - Could not enable NMEA MSG: %d\n", fields[i]); + } + } } else if (gnssModel == GNSS_MODEL_UC6580) { // The Unicore UC6580 can use a lot of sat systems, enable it to // use GPS L1 & L5 + BDS B1I & B2a + GLONASS L1 + GALILEO E1 & E5a + SBAS @@ -948,10 +1096,18 @@ GnssModel_t GPS::probe(int serialSpeed) uint8_t buffer[768] = {0}; delay(100); - // Close all NMEA sentences , Only valid for L76K MTK platform + // Close all NMEA sentences, valid for L76K, ATGM336H (and likely other AT6558 devices) _serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n"); delay(20); + // Get version information + clearBuffer(); + _serial_gps->write("$PCAS06,1*1A\r\n"); + if (getACK("$GPTXT,01,01,02,HW=ATGM336H", 500) == GNSS_RESPONSE_OK) { + LOG_INFO("ATGM336H GNSS init succeeded, using ATGM336H Module\n"); + return GNSS_MODEL_ATGM336H; + } + // Get version information clearBuffer(); _serial_gps->write("$PCAS06,0*1B\r\n"); @@ -1216,6 +1372,11 @@ bool GPS::factoryReset() LOG_INFO("GNSS Factory Reset via PCAS10,3\n"); _serial_gps->write("$PCAS10,3*1F\r\n"); delay(100); + } else if (gnssModel == GNSS_MODEL_ATGM336H) { + LOG_INFO("Factory Reset via CAS-CFG-RST\n"); + uint8_t msglen = makeCASPacket(0x06, 0x02, sizeof(_message_CAS_CFG_RST_FACTORY), _message_CAS_CFG_RST_FACTORY); + _serial_gps->write(UBXscratch, msglen); + delay(100); } else { // fire this for good measure, if we have an L76B - won't harm other devices. _serial_gps->write("$PMTK104*37\r\n"); diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 49f27e29f..77c6c0269 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -22,7 +22,14 @@ struct uBloxGnssModelInfo { char extension[10][30]; }; -typedef enum { GNSS_MODEL_MTK, GNSS_MODEL_UBLOX, GNSS_MODEL_UC6580, GNSS_MODEL_UNKNOWN, GNSS_MODEL_MTK_L76B } GnssModel_t; +typedef enum { + GNSS_MODEL_ATGM336H, + GNSS_MODEL_MTK, + GNSS_MODEL_UBLOX, + GNSS_MODEL_UC6580, + GNSS_MODEL_UNKNOWN, + GNSS_MODEL_MTK_L76B +} GnssModel_t; typedef enum { GNSS_RESPONSE_NONE, @@ -133,6 +140,11 @@ class GPS : private concurrency::OSThread static const uint8_t _message_VALSET_DISABLE_SBAS_RAM[]; static const uint8_t _message_VALSET_DISABLE_SBAS_BBR[]; + // CASIC commands for ATGM336H + static const uint8_t _message_CAS_CFG_RST_FACTORY[]; + static const uint8_t _message_CAS_CFG_NAVX_CONF[]; + static const uint8_t _message_CAS_CFG_RATE_1HZ[]; + meshtastic_Position p = meshtastic_Position_init_default; GPS() : concurrency::OSThread("GPS") {} @@ -174,6 +186,7 @@ class GPS : private concurrency::OSThread // Create a ublox packet for editing in memory uint8_t makeUBXPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg); + uint8_t makeCASPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg); // scratch space for creating ublox packets uint8_t UBXscratch[250] = {0}; @@ -184,6 +197,8 @@ class GPS : private concurrency::OSThread GPS_RESPONSE getACK(uint8_t c, uint8_t i, uint32_t waitMillis); GPS_RESPONSE getACK(const char *message, uint32_t waitMillis); + GPS_RESPONSE getACKCas(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis); + /** * Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode * @@ -243,6 +258,7 @@ class GPS : private concurrency::OSThread // Calculate checksum void UBXChecksum(uint8_t *message, size_t length); + void CASChecksum(uint8_t *message, size_t length); /** Get how long we should stay looking for each aquisition */ diff --git a/src/gps/cas.h b/src/gps/cas.h new file mode 100644 index 000000000..53d75cda9 --- /dev/null +++ b/src/gps/cas.h @@ -0,0 +1,63 @@ +#pragma once + +// CASIC binary message definitions +// Reference: https://www.icofchina.com/d/file/xiazai/2020-09-22/20f1b42b3a11ac52089caf3603b43fb5.pdf +// ATGM33H-5N: https://www.icofchina.com/pro/mokuai/2016-08-01/4.html +// (https://www.icofchina.com/d/file/xiazai/2016-12-05/b5c57074f4b1fcc62ba8c7868548d18a.pdf) + +// NEMA (Class ID - 0x4e) message IDs +#define CAS_NEMA_GGA 0x00 +#define CAS_NEMA_GLL 0x01 +#define CAS_NEMA_GSA 0x02 +#define CAS_NEMA_GSV 0x03 +#define CAS_NEMA_RMC 0x04 +#define CAS_NEMA_VTG 0x05 +#define CAS_NEMA_GST 0x07 +#define CAS_NEMA_ZDA 0x08 +#define CAS_NEMA_DHV 0x0D + +// Size of a CAS-ACK-(N)ACK message (14 bytes) +#define CAS_ACK_NACK_MSG_SIZE 0x0E + +// CFG-RST (0x06, 0x02) +// Factory reset +const uint8_t GPS::_message_CAS_CFG_RST_FACTORY[] = { + 0xFF, 0x03, // Fields to clear + 0x01, // Reset Mode: Controlled Software reset + 0x03 // Startup Mode: Factory +}; + +// CFG_RATE (0x06, 0x01) +// 1HZ update rate, this should always be the case after +// factory reset but update it regardless +const uint8_t GPS::_message_CAS_CFG_RATE_1HZ[] = { + 0xE8, 0x03, // Update Rate: 0x03E8 = 1000ms + 0x00, 0x00 // Reserved +}; + +// CFG-NAVX (0x06, 0x07) +// Initial ATGM33H-5N configuration, Updates for Dynamic Mode, Fix Mode, and SV system +// Qwirk: The ATGM33H-5N-31 should only support GPS+BDS, however it will happily enable +// and use GPS+BDS+GLONASS iff the correct CFG_NAVX command is used. +const uint8_t GPS::_message_CAS_CFG_NAVX_CONF[] = { + 0x03, 0x01, 0x00, 0x00, // Update Mask: Dynamic Mode, Fix Mode, Nav Settings + 0x03, // Dynamic Mode: Automotive + 0x03, // Fix Mode: Auto 2D/3D + 0x00, // Min SV + 0x00, // Max SVs + 0x00, // Min CNO + 0x00, // Reserved1 + 0x00, // Init 3D fix + 0x00, // Min Elevation + 0x00, // Dr Limit + 0x07, // Nav System: 2^0 = GPS, 2^1 = BDS 2^2 = GLONASS: 2^3 + // 3=GPS+BDS, 7=GPS+BDS+GLONASS + 0x00, 0x00, // Rollover Week + 0x00, 0x00, 0x00, 0x00, // Fix Altitude + 0x00, 0x00, 0x00, 0x00, // Fix Height Error + 0x00, 0x00, 0x00, 0x00, // PDOP Maximum + 0x00, 0x00, 0x00, 0x00, // TDOP Maximum + 0x00, 0x00, 0x00, 0x00, // Position Accuracy Max + 0x00, 0x00, 0x00, 0x00, // Time Accuracy Max + 0x00, 0x00, 0x00, 0x00 // Static Hold Threshold +}; \ No newline at end of file diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp index fb64553ef..12e549424 100644 --- a/src/graphics/TFTDisplay.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -1,6 +1,7 @@ #include "configuration.h" #include "main.h" #if ARCH_PORTDUINO +#include "mesh_bus_spi.h" #include "platform/portduino/PortduinoGlue.h" #endif @@ -339,7 +340,7 @@ static TFT_eSPI *tft = nullptr; // Invoke library, pins defined in User_Setup.h class LGFX : public lgfx::LGFX_Device { lgfx::Panel_LCD *_panel_instance; - lgfx::Bus_SPI _bus_instance; + lgfx::Mesh_Bus_SPI _bus_instance; lgfx::ITouch *_touch_instance; @@ -356,6 +357,7 @@ class LGFX : public lgfx::LGFX_Device _panel_instance = new lgfx::Panel_ILI9341; auto buscfg = _bus_instance.config(); buscfg.spi_mode = 0; + _bus_instance.spi_device(DisplaySPI, settingsStrings[displayspidev]); buscfg.pin_dc = settingsMap[displayDC]; // Set SPI DC pin number (-1 = disable) @@ -411,8 +413,7 @@ class LGFX : public lgfx::LGFX_Device lgfx::Panel_HX8357D _panel_instance; lgfx::Bus_SPI _bus_instance; #if defined(USE_XPT2046) - lgfx::ITouch *_touch_instance; -// lgfx::Touch_XPT2046 _touch_instance; + lgfx::Touch_XPT2046 _touch_instance; #endif public: @@ -466,8 +467,7 @@ class LGFX : public lgfx::LGFX_Device #if defined(USE_XPT2046) { // Configure settings for touch control. - _touch_instance = new lgfx::Touch_XPT2046; - auto touch_cfg = _touch_instance->config(); + auto touch_cfg = _touch_instance.config(); touch_cfg.pin_cs = TOUCH_CS; touch_cfg.x_min = 0; @@ -478,8 +478,8 @@ class LGFX : public lgfx::LGFX_Device touch_cfg.bus_shared = true; touch_cfg.offset_rotation = 1; - _touch_instance->config(touch_cfg); - //_panel_instance->setTouch(_touch_instance); + _touch_instance.config(touch_cfg); + _panel_instance.setTouch(&_touch_instance); } #endif setPanel(&_panel_instance); @@ -496,6 +496,11 @@ static LGFX *tft = nullptr; #include "TFTDisplay.h" #include +#ifdef UNPHONE +#include "unPhone.h" +extern unPhone unphone; +#endif + TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus) { LOG_DEBUG("TFTDisplay!\n"); @@ -576,11 +581,7 @@ void TFTDisplay::sendCommand(uint8_t com) digitalWrite(VTFT_CTRL, LOW); #endif #ifdef UNPHONE - Wire.beginTransmission(0x26); - Wire.write(0x02); - Wire.write(0x04); // Backlight on - Wire.write(0x22); // G&B LEDs off - Wire.endTransmission(); + unphone.backlight(true); // using unPhone library #endif #ifdef RAK14014 #elif !defined(M5STACK) @@ -612,11 +613,7 @@ void TFTDisplay::sendCommand(uint8_t com) digitalWrite(VTFT_CTRL, HIGH); #endif #ifdef UNPHONE - Wire.beginTransmission(0x26); - Wire.write(0x02); - Wire.write(0x00); // Backlight off - Wire.write(0x22); // G&B LEDs off - Wire.endTransmission(); + unphone.backlight(false); // using unPhone library #endif #ifdef RAK14014 #elif !defined(M5STACK) @@ -690,11 +687,7 @@ bool TFTDisplay::connect() digitalWrite(ST7735_BL_V05, TFT_BACKLIGHT_ON); #endif #ifdef UNPHONE - Wire.beginTransmission(0x26); - Wire.write(0x02); - Wire.write(0x04); // Backlight on - Wire.write(0x22); // G&B LEDs off - Wire.endTransmission(); + unphone.backlight(true); // using unPhone library LOG_INFO("Power to TFT Backlight\n"); #endif diff --git a/src/graphics/mesh_bus_spi.cpp b/src/graphics/mesh_bus_spi.cpp new file mode 100644 index 000000000..a9536d490 --- /dev/null +++ b/src/graphics/mesh_bus_spi.cpp @@ -0,0 +1,188 @@ +// This code has been copied from LovyanGFX to make the SPI device selectable for touchscreens. +// Ideally this could eventually be an inherited class from BUS_SPI, +// but currently too many internal objects are set private. + +#include "configuration.h" +#if ARCH_PORTDUINO +#include "lgfx/v1/misc/pixelcopy.hpp" +#include "main.h" +#include "mesh_bus_spi.h" +#include +#include + +namespace lgfx +{ +inline namespace v1 +{ +//---------------------------------------------------------------------------- + +void Mesh_Bus_SPI::config(const config_t &config) +{ + _cfg = config; + + if (_cfg.pin_dc >= 0) { + pinMode(_cfg.pin_dc, pin_mode_t::output); + gpio_hi(_cfg.pin_dc); + } +} + +bool Mesh_Bus_SPI::init(void) +{ + dc_h(); + pinMode(_cfg.pin_dc, pin_mode_t::output); + if (SPIName != "") + PrivateSPI->begin(SPIName.c_str()); + else + PrivateSPI->begin(); + return true; +} + +void Mesh_Bus_SPI::release(void) +{ + PrivateSPI->end(); +} + +void Mesh_Bus_SPI::spi_device(HardwareSPI *newSPI, std::string newSPIName) +{ + PrivateSPI = newSPI; + SPIName = newSPIName; +} +void Mesh_Bus_SPI::beginTransaction(void) +{ + dc_h(); + SPISettings setting(_cfg.freq_write, MSBFIRST, _cfg.spi_mode); + PrivateSPI->beginTransaction(setting); +} + +void Mesh_Bus_SPI::endTransaction(void) +{ + PrivateSPI->endTransaction(); + dc_h(); +} + +void Mesh_Bus_SPI::beginRead(void) +{ + PrivateSPI->endTransaction(); + // SPISettings setting(_cfg.freq_read, BitOrder::MSBFIRST, _cfg.spi_mode, false); + SPISettings setting(_cfg.freq_read, MSBFIRST, _cfg.spi_mode); + PrivateSPI->beginTransaction(setting); +} + +void Mesh_Bus_SPI::endRead(void) +{ + PrivateSPI->endTransaction(); + beginTransaction(); +} + +void Mesh_Bus_SPI::wait(void) {} + +bool Mesh_Bus_SPI::busy(void) const +{ + return false; +} + +bool Mesh_Bus_SPI::writeCommand(uint32_t data, uint_fast8_t bit_length) +{ + dc_l(); + PrivateSPI->transfer((uint8_t *)&data, bit_length >> 3); + dc_h(); + return true; +} + +void Mesh_Bus_SPI::writeData(uint32_t data, uint_fast8_t bit_length) +{ + PrivateSPI->transfer((uint8_t *)&data, bit_length >> 3); +} + +void Mesh_Bus_SPI::writeDataRepeat(uint32_t data, uint_fast8_t bit_length, uint32_t length) +{ + const uint8_t dst_bytes = bit_length >> 3; + uint32_t limit = (dst_bytes == 3) ? 12 : 16; + auto buf = _flip_buffer.getBuffer(512); + size_t fillpos = 0; + reinterpret_cast(buf)[0] = data; + fillpos += dst_bytes; + uint32_t len; + do { + len = ((length - 1) % limit) + 1; + if (limit <= 64) + limit <<= 1; + + while (fillpos < len * dst_bytes) { + memcpy(&buf[fillpos], buf, fillpos); + fillpos += fillpos; + } + + PrivateSPI->transfer(buf, len * dst_bytes); + } while (length -= len); +} + +void Mesh_Bus_SPI::writePixels(pixelcopy_t *param, uint32_t length) +{ + const uint8_t dst_bytes = param->dst_bits >> 3; + uint32_t limit = (dst_bytes == 3) ? 12 : 16; + uint32_t len; + do { + len = ((length - 1) % limit) + 1; + if (limit <= 32) + limit <<= 1; + auto buf = _flip_buffer.getBuffer(len * dst_bytes); + param->fp_copy(buf, 0, len, param); + PrivateSPI->transfer(buf, len * dst_bytes); + } while (length -= len); +} + +void Mesh_Bus_SPI::writeBytes(const uint8_t *data, uint32_t length, bool dc, bool use_dma) +{ + if (dc) + dc_h(); + else + dc_l(); + PrivateSPI->transfer(const_cast(data), length); + if (!dc) + dc_h(); +} + +uint32_t Mesh_Bus_SPI::readData(uint_fast8_t bit_length) +{ + uint32_t res = 0; + bit_length >>= 3; + if (!bit_length) + return res; + int idx = 0; + do { + res |= PrivateSPI->transfer(0) << idx; + idx += 8; + } while (--bit_length); + return res; +} + +bool Mesh_Bus_SPI::readBytes(uint8_t *dst, uint32_t length, bool use_dma) +{ + do { + dst[0] = PrivateSPI->transfer(0); + ++dst; + } while (--length); + return true; +} + +void Mesh_Bus_SPI::readPixels(void *dst, pixelcopy_t *param, uint32_t length) +{ + uint32_t bytes = param->src_bits >> 3; + uint32_t dstindex = 0; + uint32_t len = 4; + uint8_t buf[24]; + param->src_data = buf; + do { + if (len > length) + len = length; + readBytes((uint8_t *)buf, len * bytes, true); + param->src_x = 0; + dstindex = param->fp_copy(dst, dstindex, dstindex + len, param); + length -= len; + } while (length); +} + +} // namespace v1 +} // namespace lgfx +#endif \ No newline at end of file diff --git a/src/graphics/mesh_bus_spi.h b/src/graphics/mesh_bus_spi.h new file mode 100644 index 000000000..903f7ad9d --- /dev/null +++ b/src/graphics/mesh_bus_spi.h @@ -0,0 +1,100 @@ +#if ARCH_PORTDUINO +/*----------------------------------------------------------------------------/ + Lovyan GFX - Graphics library for embedded devices. + +Original Source: + https://github.com/lovyan03/LovyanGFX/ + +Licence: + [FreeBSD](https://github.com/lovyan03/LovyanGFX/blob/master/license.txt) + +Author: + [lovyan03](https://twitter.com/lovyan03) + +Contributors: + [ciniml](https://github.com/ciniml) + [mongonta0716](https://github.com/mongonta0716) + [tobozo](https://github.com/tobozo) +/----------------------------------------------------------------------------*/ +#pragma once + +#include + +#include "lgfx/v1/Bus.hpp" +#include "lgfx/v1/platforms/common.hpp" + +namespace lgfx +{ +inline namespace v1 +{ +//---------------------------------------------------------------------------- + +class Mesh_Bus_SPI : public IBus +{ + public: + struct config_t { + uint32_t freq_write = 16000000; + uint32_t freq_read = 8000000; + // bool spi_3wire = true; + // bool use_lock = true; + int16_t pin_sclk = -1; + int16_t pin_miso = -1; + int16_t pin_mosi = -1; + int16_t pin_dc = -1; + uint8_t spi_mode = 0; + }; + + const config_t &config(void) const { return _cfg; } + + void config(const config_t &config); + + bus_type_t busType(void) const override { return bus_type_t::bus_spi; } + + bool init(void) override; + void release(void) override; + void spi_device(HardwareSPI *newSPI, std::string newSPIName); + + void beginTransaction(void) override; + void endTransaction(void) override; + void wait(void) override; + bool busy(void) const override; + + bool writeCommand(uint32_t data, uint_fast8_t bit_length) override; + void writeData(uint32_t data, uint_fast8_t bit_length) override; + void writeDataRepeat(uint32_t data, uint_fast8_t bit_length, uint32_t count) override; + void writePixels(pixelcopy_t *param, uint32_t length) override; + void writeBytes(const uint8_t *data, uint32_t length, bool dc, bool use_dma) override; + + void initDMA(void) {} + void flush(void) {} + void addDMAQueue(const uint8_t *data, uint32_t length) override { writeBytes(data, length, true, true); } + void execDMAQueue(void) {} + uint8_t *getDMABuffer(uint32_t length) override { return _flip_buffer.getBuffer(length); } + + void beginRead(void) override; + void endRead(void) override; + uint32_t readData(uint_fast8_t bit_length) override; + bool readBytes(uint8_t *dst, uint32_t length, bool use_dma) override; + void readPixels(void *dst, pixelcopy_t *param, uint32_t length) override; + + private: + HardwareSPI *PrivateSPI; + std::string SPIName; + __attribute__((always_inline)) inline void dc_h(void) { gpio_hi(_cfg.pin_dc); } + __attribute__((always_inline)) inline void dc_l(void) { gpio_lo(_cfg.pin_dc); } + + config_t _cfg; + FlipBuffer _flip_buffer; + bool _need_wait; + uint32_t _mask_reg_dc; + uint32_t _last_apb_freq = -1; + uint32_t _clkdiv_write; + uint32_t _clkdiv_read; + volatile uint32_t *_gpio_reg_dc_h; + volatile uint32_t *_gpio_reg_dc_l; +}; + +//---------------------------------------------------------------------------- +} // namespace v1 +} // namespace lgfx +#endif \ No newline at end of file diff --git a/src/input/TouchScreenImpl1.cpp b/src/input/TouchScreenImpl1.cpp index 3e4ed4163..c863ead69 100644 --- a/src/input/TouchScreenImpl1.cpp +++ b/src/input/TouchScreenImpl1.cpp @@ -4,7 +4,7 @@ #include "configuration.h" #include "modules/ExternalNotificationModule.h" -#ifdef ARCH_PORTDUINO +#if ARCH_PORTDUINO #include "platform/portduino/PortduinoGlue.h" #endif diff --git a/src/main.cpp b/src/main.cpp index 587bcb56e..b1a15634f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -590,20 +590,6 @@ void setup() if (config.display.oled != meshtastic_Config_DisplayConfig_OledType_OLED_AUTO) screen_model = config.display.oled; -#ifdef UNPHONE - // initialise IO expander with pinmodes - Wire.beginTransmission(0x26); - Wire.write(0x06); - Wire.write(0x7A); - Wire.write(0xDD); - Wire.endTransmission(); - Wire.beginTransmission(0x26); - Wire.write(0x02); - Wire.write(0x04); // Backlight on - Wire.write(0x22); // G&B LEDs off - Wire.endTransmission(); -#endif - #if defined(USE_SH1107) screen_model = meshtastic_Config_DisplayConfig_OledType_OLED_SH1107; // set dimension of 128x128 display_geometry = GEOMETRY_128_128; @@ -644,14 +630,12 @@ void setup() pinMode(LORA_CS, OUTPUT); digitalWrite(LORA_CS, HIGH); SPI1.begin(false); -#else // HW_SPI1_DEVICE +#else // HW_SPI1_DEVICE SPI.setSCK(LORA_SCK); SPI.setTX(LORA_MOSI); SPI.setRX(LORA_MISO); SPI.begin(false); -#endif // HW_SPI1_DEVICE -#elif ARCH_PORTDUINO - SPI.begin(settingsStrings[spidev].c_str()); +#endif // HW_SPI1_DEVICE #elif !defined(ARCH_ESP32) // ARCH_RP2040 SPI.begin(); #else @@ -738,7 +722,7 @@ void setup() if (settingsMap[use_sx1262]) { if (!rIf) { LOG_DEBUG("Attempting to activate sx1262 radio on SPI port %s\n", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); + LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(*LoraSPI, spiSettings); rIf = new SX1262Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { @@ -752,7 +736,7 @@ void setup() } else if (settingsMap[use_rf95]) { if (!rIf) { LOG_DEBUG("Attempting to activate rf95 radio on SPI port %s\n", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); + LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(*LoraSPI, spiSettings); rIf = new RF95Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { @@ -767,7 +751,7 @@ void setup() } else if (settingsMap[use_sx1280]) { if (!rIf) { LOG_DEBUG("Attempting to activate sx1280 radio on SPI port %s\n", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); + LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(*LoraSPI, spiSettings); rIf = new SX1280Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { @@ -1017,4 +1001,4 @@ void loop() mainDelay.delay(delayMsec); } // if (didWake) LOG_DEBUG("wake!\n"); -} +} \ No newline at end of file diff --git a/src/main.h b/src/main.h index 132fd190b..bb812b7b6 100644 --- a/src/main.h +++ b/src/main.h @@ -22,6 +22,11 @@ extern NimbleBluetooth *nimbleBluetooth; extern NRF52Bluetooth *nrf52Bluetooth; #endif +#if ARCH_PORTDUINO +extern HardwareSPI *DisplaySPI; +extern HardwareSPI *LoraSPI; + +#endif extern ScanI2C::DeviceAddress screen_found; extern ScanI2C::DeviceAddress cardkb_found; extern uint8_t kb_model; diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp index c8dd7f3d1..2ef46e4db 100644 --- a/src/mesh/MeshModule.cpp +++ b/src/mesh/MeshModule.cpp @@ -12,7 +12,7 @@ const meshtastic_MeshPacket *MeshModule::currentRequest; /** * If any of the current chain of modules has already sent a reply, it will be here. This is useful to allow - * the RoutingPlugin to avoid sending redundant acks + * the RoutingModule to avoid sending redundant acks */ meshtastic_MeshPacket *MeshModule::currentReply; @@ -40,7 +40,7 @@ meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, Nod c.error_reason = err; c.which_variant = meshtastic_Routing_error_reason_tag; - // Now that we have moded sendAckNak up one level into the class hierarchy we can no longer assume we are a RoutingPlugin + // Now that we have moded sendAckNak up one level into the class hierarchy we can no longer assume we are a RoutingModule // So we manually call pb_encode_to_bytes and specify routing port number // auto p = allocDataProtobuf(c); meshtastic_MeshPacket *p = router->allocForSending(); @@ -54,7 +54,8 @@ meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, Nod p->to = to; p->decoded.request_id = idFrom; p->channel = chIndex; - LOG_ERROR("Alloc an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id); + if (err != meshtastic_Routing_Error_NONE) + LOG_ERROR("Alloc an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id); return p; } @@ -68,7 +69,7 @@ meshtastic_MeshPacket *MeshModule::allocErrorResponse(meshtastic_Routing_Error e return r; } -void MeshModule::callPlugins(meshtastic_MeshPacket &mp, RxSource src) +void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src) { // LOG_DEBUG("In call modules\n"); bool moduleFound = false; @@ -258,7 +259,7 @@ void MeshModule::observeUIEvents(Observer *observer) } } -AdminMessageHandleResult MeshModule::handleAdminMessageForAllPlugins(const meshtastic_MeshPacket &mp, +AdminMessageHandleResult MeshModule::handleAdminMessageForAllModules(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request, meshtastic_AdminMessage *response) { diff --git a/src/mesh/MeshModule.h b/src/mesh/MeshModule.h index 6c431adb4..2e2af33e0 100644 --- a/src/mesh/MeshModule.h +++ b/src/mesh/MeshModule.h @@ -64,11 +64,11 @@ class MeshModule /** For use only by MeshService */ - static void callPlugins(meshtastic_MeshPacket &mp, RxSource src = RX_SRC_RADIO); + static void callModules(meshtastic_MeshPacket &mp, RxSource src = RX_SRC_RADIO); static std::vector GetMeshModulesWithUIFrames(); static void observeUIEvents(Observer *observer); - static AdminMessageHandleResult handleAdminMessageForAllPlugins(const meshtastic_MeshPacket &mp, + static AdminMessageHandleResult handleAdminMessageForAllModules(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request, meshtastic_AdminMessage *response); #if HAS_SCREEN @@ -195,4 +195,4 @@ class MeshModule /** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet * This ensures that if the request packet was sent reliably, the reply is sent that way as well. */ -void setReplyTo(meshtastic_MeshPacket *p, const meshtastic_MeshPacket &to); +void setReplyTo(meshtastic_MeshPacket *p, const meshtastic_MeshPacket &to); \ No newline at end of file diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 73aa29bbf..710b21593 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -527,8 +527,8 @@ void NodeDB::installDefaultDeviceState() void NodeDB::pickNewNodeNum() { NodeNum nodeNum = myNodeInfo.my_node_num; + getMacAddr(ourMacAddr); // Make sure ourMacAddr is set if (nodeNum == 0) { - getMacAddr(ourMacAddr); // Make sure ourMacAddr is set // Pick an initial nodenum based on the macaddr nodeNum = (ourMacAddr[2] << 24) | (ourMacAddr[3] << 16) | (ourMacAddr[4] << 8) | ourMacAddr[5]; } diff --git a/src/mesh/RF95Interface.cpp b/src/mesh/RF95Interface.cpp index b658a8ff6..8c6c349fd 100644 --- a/src/mesh/RF95Interface.cpp +++ b/src/mesh/RF95Interface.cpp @@ -215,7 +215,7 @@ bool RF95Interface::isChannelActive() // LOG_DEBUG("Channel is busy!\n"); return true; } - if (result != RADIOLIB_ERR_WRONG_MODEM) + if (result != RADIOLIB_CHANNEL_FREE) LOG_ERROR("Radiolib error %d when attempting RF95 isChannelActive!\n", result); assert(result != RADIOLIB_ERR_WRONG_MODEM); diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 3aac9dfce..63912a03e 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -151,10 +151,16 @@ static uint8_t bytes[MAX_RHPACKETLEN]; void initRegion() { const RegionInfo *r = regions; +#ifdef LORA_REGIONCODE + for (; r->code != meshtastic_Config_LoRaConfig_RegionCode_UNSET && r->code != LORA_REGIONCODE; r++) + ; + LOG_INFO("Wanted region %d, regulatory override to %s\n", config.lora.region, r->name); +#else for (; r->code != meshtastic_Config_LoRaConfig_RegionCode_UNSET && r->code != config.lora.region; r++) ; - myRegion = r; LOG_INFO("Wanted region %d, using %s\n", config.lora.region, r->name); +#endif + myRegion = r; } /** diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 3fa933bb1..e4d67f019 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -479,7 +479,7 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src) // call modules here if (!skipHandle) - MeshModule::callPlugins(*p, src); + MeshModule::callModules(*p, src); } void Router::perhapsHandleReceived(meshtastic_MeshPacket *p) @@ -499,4 +499,4 @@ void Router::perhapsHandleReceived(meshtastic_MeshPacket *p) handleReceived(p); packetPool.release(p); -} +} \ No newline at end of file diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index 564b80494..9e4fbfa77 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -280,7 +280,7 @@ template bool SX128xInterface::isChannelActive() result = lora.scanChannel(); if (result == RADIOLIB_LORA_DETECTED) return true; - if (result != RADIOLIB_ERR_WRONG_MODEM) + if (result != RADIOLIB_CHANNEL_FREE) LOG_ERROR("Radiolib error %d when attempting SX128X scanChannel!\n", result); assert(result != RADIOLIB_ERR_WRONG_MODEM); diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index e674e28bb..67b2edd15 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -146,6 +146,8 @@ typedef enum _meshtastic_HardwareModel { /* Teledatics TD-LORAC NRF52840 based M.2 LoRA module Compatible with the TD-WRLS development board */ meshtastic_HardwareModel_TD_LORAC = 60, + /* CDEBYTE EoRa-S3 board using their own MM modules, clone of LILYGO T3S3 */ + meshtastic_HardwareModel_CDEBYTE_EORA_S3 = 61, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index b40633af0..54eb577f7 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -256,7 +256,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta default: meshtastic_AdminMessage res = meshtastic_AdminMessage_init_default; - AdminMessageHandleResult handleResult = MeshModule::handleAdminMessageForAllPlugins(mp, r, &res); + AdminMessageHandleResult handleResult = MeshModule::handleAdminMessageForAllModules(mp, r, &res); if (handleResult == AdminMessageHandleResult::HANDLED_WITH_RESPONSE) { myReply = allocDataProtobuf(res); diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 05d5486b2..da1c204b8 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -656,6 +656,7 @@ std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp) msgPayload["voltage"] = new JSONValue(decoded->variant.device_metrics.voltage); msgPayload["channel_utilization"] = new JSONValue(decoded->variant.device_metrics.channel_utilization); msgPayload["air_util_tx"] = new JSONValue(decoded->variant.device_metrics.air_util_tx); + msgPayload["uptime_seconds"] = new JSONValue((uint)decoded->variant.device_metrics.uptime_seconds); } else if (decoded->which_variant == meshtastic_Telemetry_environment_metrics_tag) { msgPayload["temperature"] = new JSONValue(decoded->variant.environment_metrics.temperature); msgPayload["relative_humidity"] = new JSONValue(decoded->variant.environment_metrics.relative_humidity); diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index 6855265ac..15e437bb5 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -117,6 +117,8 @@ #define HW_VENDOR meshtastic_HardwareModel_HELTEC_WIRELESS_PAPER #elif defined(TLORA_T3S3_V1) #define HW_VENDOR meshtastic_HardwareModel_TLORA_T3_S3 +#elif defined(CDEBYTE_ELORA_S3) +#define HW_VENDOR meshtastic_HardwareModel_CDEBYTE_ELORA_S3 #elif defined(BETAFPV_2400_TX) #define HW_VENDOR meshtastic_HardwareModel_BETAFPV_2400_TX #elif defined(NANO_G1_EXPLORER) diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index f686ef3dc..f3415aaee 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -15,6 +15,8 @@ #include #include +HardwareSPI *DisplaySPI; +HardwareSPI *LoraSPI; std::map settingsMap; std::map settingsStrings; char *configPath = nullptr; @@ -81,6 +83,7 @@ void portduinoSetup() settingsStrings[keyboardDevice] = ""; settingsStrings[webserverrootpath] = ""; settingsStrings[spidev] = ""; + settingsStrings[displayspidev] = ""; YAML::Node yamlConfig; @@ -187,6 +190,9 @@ void portduinoSetup() settingsMap[displayOffsetY] = yamlConfig["Display"]["OffsetY"].as(0); settingsMap[displayRotate] = yamlConfig["Display"]["Rotate"].as(false); settingsMap[displayInvert] = yamlConfig["Display"]["Invert"].as(false); + if (yamlConfig["Display"]["spidev"]) { + settingsStrings[displayspidev] = "/dev/" + yamlConfig["Display"]["spidev"].as("spidev0.1"); + } } settingsMap[touchscreenModule] = no_touchscreen; if (yamlConfig["Touchscreen"]) { @@ -196,6 +202,9 @@ void portduinoSetup() settingsMap[touchscreenModule] = stmpe610; settingsMap[touchscreenCS] = yamlConfig["Touchscreen"]["CS"].as(-1); settingsMap[touchscreenIRQ] = yamlConfig["Touchscreen"]["IRQ"].as(-1); + if (yamlConfig["Touchscreen"]["spidev"]) { + settingsStrings[touchscreenspidev] = "/dev/" + yamlConfig["Touchscreen"]["spidev"].as(""); + } } if (yamlConfig["Input"]) { settingsStrings[keyboardDevice] = (yamlConfig["Input"]["KeyboardDevice"]).as(""); @@ -267,6 +276,26 @@ void portduinoSetup() initGPIOPin(settingsMap[touchscreenIRQ], gpioChipName); } + // if we specify a touchscreen dev, that is SPI. + // else if we specify a screen dev, that is SPI + // else if we specify a LoRa dev, that is SPI. + if (settingsStrings[touchscreenspidev] != "") { + SPI.begin(settingsStrings[touchscreenspidev].c_str()); + DisplaySPI = new HardwareSPI; + DisplaySPI->begin(settingsStrings[displayspidev].c_str()); + LoraSPI = new HardwareSPI; + LoraSPI->begin(settingsStrings[spidev].c_str()); + } else if (settingsStrings[displayspidev] != "") { + SPI.begin(settingsStrings[displayspidev].c_str()); + DisplaySPI = &SPI; + LoraSPI = new HardwareSPI; + LoraSPI->begin(settingsStrings[spidev].c_str()); + } else { + SPI.begin(settingsStrings[spidev].c_str()); + LoraSPI = &SPI; + DisplaySPI = &SPI; + } + return; } diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index 505c436d6..980fc63b8 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -21,6 +21,8 @@ enum configNames { touchscreenModule, touchscreenCS, touchscreenIRQ, + touchscreenspidev, + displayspidev, displayPanel, displayWidth, displayHeight, @@ -45,4 +47,6 @@ enum { level_error, level_warn, level_info, level_debug }; extern std::map settingsMap; extern std::map settingsStrings; -int initGPIOPin(int pinNum, std::string gpioChipname); \ No newline at end of file +int initGPIOPin(int pinNum, std::string gpioChipname); +extern HardwareSPI *DisplaySPI; +extern HardwareSPI *LoraSPI; \ No newline at end of file diff --git a/variants/CDEBYTE_EoRa-S3/pins_arduino.h b/variants/CDEBYTE_EoRa-S3/pins_arduino.h new file mode 100644 index 000000000..38a9103f0 --- /dev/null +++ b/variants/CDEBYTE_EoRa-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/CDEBYTE_EoRa-S3/platformio.ini b/variants/CDEBYTE_EoRa-S3/platformio.ini new file mode 100644 index 000000000..1ff54de88 --- /dev/null +++ b/variants/CDEBYTE_EoRa-S3/platformio.ini @@ -0,0 +1,8 @@ +[env:CDEBYTE_EoRa-S3] +extends = esp32s3_base +board = CDEBYTE_EoRa-S3 +build_flags = + ${esp32s3_base.build_flags} + -D CDEBYTE_EORA_S3 + -I variants/CDEBYTE_EoRa-S3 + -D GPS_POWER_TOGGLE diff --git a/variants/CDEBYTE_EoRa-S3/variant.h b/variants/CDEBYTE_EoRa-S3/variant.h new file mode 100644 index 000000000..5da99667b --- /dev/null +++ b/variants/CDEBYTE_EoRa-S3/variant.h @@ -0,0 +1,63 @@ +// LED - status indication +#define LED_PIN 37 + +// Button - user interface +#define BUTTON_PIN 0 // This is the BOOT button, and it has its own pull-up resistor + +// SD card - TODO: test, currently untested, copied from T3S3 variant +#define HAS_SDCARD +#define SDCARD_USE_SPI1 +// TODO: rename this to make this SD-card specific +#define SPI_CS 13 +#define SPI_SCK 14 +#define SPI_MOSI 11 +#define SPI_MISO 2 +// FIXME: there are two other SPI pins that are not defined here +// Compatibility +#define SDCARD_CS SPI_CS + +// Battery voltage monitoring - TODO: test, currently untested, copied from T3S3 variant +#define BATTERY_PIN 1 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage +#define ADC_CHANNEL ADC1_GPIO1_CHANNEL +#define ADC_MULTIPLIER \ + 2.11 // ratio of voltage divider = 2.0 (R10=1M, R13=1M), plus some undervoltage correction - TODO: this was carried over from + // the T3S3, test to see if the undervoltage correction is needed. + +// Display - OLED connected via I2C by the default hardware configuration +#define HAS_SCREEN 1 +#define USE_SSD1306 +#define I2C_SCL 17 +#define I2C_SDA 18 + +// UART - The 1mm JST SH connector closest to the USB-C port +#define UART_TX 43 +#define UART_RX 44 + +// Peripheral I2C - The 1mm JST SH connector furthest from the USB-C port which follows Adafruit connection standard. There are no +// pull-up resistors on these lines, the downstream device needs to include them. TODO: test, currently untested +#define I2C_SCL1 21 +#define I2C_SDA1 10 + +// Radio +#define USE_SX1262 // CDEBYTE EoRa-S3-900TB <- CDEBYTE E22-900MM22S <- Semtech SX1262 +#define USE_SX1268 // CDEBYTE EoRa-S3-400TB <- CDEBYTE E22-400MM22S <- Semtech SX1268 + +#define SX126X_CS 7 +#define LORA_SCK 5 +#define LORA_MOSI 6 +#define LORA_MISO 3 +#define SX126X_RESET 8 +#define SX126X_BUSY 34 +#define SX126X_DIO1 33 + +#define SX126X_DIO2_AS_RF_SWITCH // All switching is performed with DIO2, it is automatically inverted using circuitry. +// CDEBYTE EoRa-S3 uses an XTAL, thus we do not need DIO3 as TCXO voltage reference. Don't define SX126X_DIO3_TCXO_VOLTAGE for +// simplicity rather than defining it as 0. +#define SX126X_MAX_POWER \ + 22 // E22-900MM22S and E22-400MM22S have a raw SX1262 or SX1268 respsectively, they are rated to output up and including 22 + // dBm out of their SX126x IC. + +// Compatibility with old variant.h file structure - FIXME: this should be done in the respective radio interface modules to clean +// up all variants. +#define LORA_CS SX126X_CS +#define LORA_DIO1 SX126X_DIO1 \ No newline at end of file diff --git a/variants/unphone/platformio.ini b/variants/unphone/platformio.ini index 06314eaa3..dad9a7177 100644 --- a/variants/unphone/platformio.ini +++ b/variants/unphone/platformio.ini @@ -1,5 +1,7 @@ +; platformio.ini for unphone meshtastic + [env:unphone] -;build_type = debug ; to make it possible to step through our jtag debugger + extends = esp32s3_base board_level = extra board = unphone9 @@ -14,6 +16,15 @@ build_flags = ${esp32_base.build_flags} -D UNPHONE -I variants/unphone -D ARDUINO_USB_MODE=0 + -D UNPHONE_ACCEL=0 + -D UNPHONE_TOUCHS=0 + -D UNPHONE_SDCARD=0 + -D UNPHONE_UI0=0 + -D UNPHONE_LORA=0 + -D UNPHONE_FACTORY_MODE=0 + +build_src_filter = ${esp32_base.build_src_filter} +<../variants/unphone> lib_deps = ${esp32s3_base.lib_deps} - lovyan03/LovyanGFX@^1.1.8 \ No newline at end of file + lovyan03/LovyanGFX @ ^1.1.8 + https://gitlab.com/hamishcunningham/unphonelibrary#meshtastic @ ^9.0.0 \ No newline at end of file diff --git a/variants/unphone/variant.cpp b/variants/unphone/variant.cpp new file mode 100644 index 000000000..3f6d1c54d --- /dev/null +++ b/variants/unphone/variant.cpp @@ -0,0 +1,20 @@ +// meshtastic/firmware/variants/unphone/variant.cpp + +#include "unPhone.h" +unPhone unphone = unPhone("meshtastic_unphone"); + +void initVariant() +{ + unphone.begin(); // initialise hardware etc. + unphone.store(unphone.buildTime); + unphone.printWakeupReason(); // what woke us up? (stored, not printed :|) + unphone.checkPowerSwitch(); // if power switch is off, shutdown + unphone.backlight(false); // setup backlight and make sure its off + + for (int i = 0; i < 3; i++) { // buzz a bit + unphone.vibe(true); + delay(150); + unphone.vibe(false); + delay(150); + } +} \ No newline at end of file diff --git a/variants/unphone/variant.h b/variants/unphone/variant.h index 9306537f2..180fdfe2c 100644 --- a/variants/unphone/variant.h +++ b/variants/unphone/variant.h @@ -1,3 +1,7 @@ +// meshtastic/firmware/variants/unphone/variant.h + +#pragma once + #define SPI_SCK 39 #define SPI_MOSI 40 #define SPI_MISO 41 @@ -28,7 +32,7 @@ #define TFT_WIDTH 320 #define TFT_OFFSET_X 0 #define TFT_OFFSET_Y 0 -#define TFT_OFFSET_ROTATION 6 // the unPhone's screen is wired unusually, 0 is typical value here +#define TFT_OFFSET_ROTATION 6 // unPhone's screen wired unusually, 0 typical #define TFT_INVERT false #define SCREEN_ROTATE true #define SCREEN_TRANSITION_FRAMERATE 5 @@ -37,7 +41,10 @@ #define USE_XPT2046 1 #define TOUCH_CS 38 -#define HAS_GPS 0 // the unphone doesn't have a gps module +#define HAS_GPS \ + 0 // the unphone doesn't have a gps module by default (though + // GPS featherwing -- https://www.adafruit.com/product/3133 + // -- can be added) #undef GPS_RX_PIN #undef GPS_TX_PIN @@ -49,6 +56,7 @@ #define BUTTON_PIN 21 // Button 3 - square - top button in landscape mode #define BUTTON_NEED_PULLUP // we do need a helping hand up +#define BUTTON_PIN_ALT 45 // Button 1 - triangle - bottom button in landscape mode #define I2C_SDA 3 // I2C pins for this board #define I2C_SCL 4 @@ -58,6 +66,6 @@ // ratio of voltage divider = 3.20 (R1=100k, R2=220k) // #define ADC_MULTIPLIER 3.2 -// #define BATTERY_PIN 13 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage +// #define BATTERY_PIN 13 // battery V measurement pin; vbat divider is here // #define ADC_CHANNEL ADC2_GPIO13_CHANNEL // #define BAT_MEASURE_ADC_UNIT 2 \ No newline at end of file