diff --git a/bin/config.d/lora-usb-umesh-1262-30dbm.yaml b/bin/config.d/lora-usb-umesh-1262-30dbm.yaml new file mode 100644 index 000000000..7726eccd1 --- /dev/null +++ b/bin/config.d/lora-usb-umesh-1262-30dbm.yaml @@ -0,0 +1,23 @@ +Lora: + Module: sx1262 + CS: 0 + IRQ: 6 + Reset: 1 + Busy: 4 + RXen: 2 + DIO2_AS_RF_SWITCH: true + spidev: ch341 + USB_PID: 0x5512 + USB_VID: 0x1A86 + DIO3_TCXO_VOLTAGE: true +# USB_Serialnum: 12345678 + SX126X_MAX_POWER: 22 +# Reduce output power to improve EMI + NUM_PA_POINTS: 22 + TX_GAIN_LORA: 12, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 8, 8, 7 +# Note: This module integrates an additional PA to achieve higher output power. +# The 'power' parameter here does not represent the actual RF output. +# TX_GAIN_LORA defines the gain offset applied at each SX1262 input power step (1–22 dBm). +# Each array element corresponds to the additional gain when that input level is set, +# The effective RF output is: Pout ≈ Pset + TX_GAIN_LORA[index]. +# Please refer to https://github.com/linser233/uMesh/blob/main/RF_Power.md for detailed information. diff --git a/bin/config.d/lora-usb-umesh-1262.yaml b/bin/config.d/lora-usb-umesh-1262.yaml deleted file mode 100644 index 6008e63b7..000000000 --- a/bin/config.d/lora-usb-umesh-1262.yaml +++ /dev/null @@ -1,15 +0,0 @@ -Lora: - Module: sx1262 - CS: 0 - IRQ: 6 - Reset: 1 - Busy: 4 - RXen: 2 - DIO2_AS_RF_SWITCH: true - spidev: ch341 - USB_PID: 0x5512 - USB_VID: 0x1A86 - DIO3_TCXO_VOLTAGE: true -# USB_Serialnum: 12345678 - SX126X_MAX_POWER: 30 -# Reduce output power to improve EMI diff --git a/bin/config.d/lora-usb-umesh-1268-30dbm.yaml b/bin/config.d/lora-usb-umesh-1268-30dbm.yaml new file mode 100644 index 000000000..c054a92f9 --- /dev/null +++ b/bin/config.d/lora-usb-umesh-1268-30dbm.yaml @@ -0,0 +1,23 @@ +Lora: + Module: sx1268 + CS: 0 + IRQ: 6 + Reset: 1 + Busy: 4 + RXen: 2 + DIO2_AS_RF_SWITCH: true + spidev: ch341 + USB_PID: 0x5512 + USB_VID: 0x1A86 + DIO3_TCXO_VOLTAGE: true +# USB_Serialnum: 12345678 + SX126X_MAX_POWER: 22 +# Reduce output power to improve EMI + NUM_PA_POINTS: 22 + TX_GAIN_LORA: 12, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 8, 8, 7 +# Note: This module integrates an additional PA to achieve higher output power. +# The 'power' parameter here does not represent the actual RF output. +# TX_GAIN_LORA defines the gain offset applied at each SX1262 input power step (1–22 dBm). +# Each array element corresponds to the additional gain when that input level is set, +# The effective RF output is: Pout ≈ Pset + TX_GAIN_LORA[index]. +# Please refer to https://github.com/linser233/uMesh/blob/main/RF_Power.md for detailed information. diff --git a/bin/config.d/lora-usb-umesh-1268.yaml b/bin/config.d/lora-usb-umesh-1268.yaml deleted file mode 100644 index 637472966..000000000 --- a/bin/config.d/lora-usb-umesh-1268.yaml +++ /dev/null @@ -1,15 +0,0 @@ -Lora: - Module: sx1268 - CS: 0 - IRQ: 6 - Reset: 1 - Busy: 4 - RXen: 2 - DIO2_AS_RF_SWITCH: true - spidev: ch341 - USB_PID: 0x5512 - USB_VID: 0x1A86 - DIO3_TCXO_VOLTAGE: true -# USB_Serialnum: 12345678 - SX126X_MAX_POWER: 30 -# Reduce output power to improve EMI diff --git a/protobufs b/protobufs index 77c8329a5..bc63a57f9 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 77c8329a59a9c96a61c447b5d5f1a52ca583e4f2 +Subproject commit bc63a57f9e5dba8a7c90ee0bd4a9840862d61f6d diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index b2c3d9f82..2468fafb0 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1733,6 +1733,26 @@ int Screen::handleInputEvent(const InputEvent *event) showFrame(FrameDirection::PREVIOUS); } else if (event->inputEvent == INPUT_BROKER_RIGHT || event->inputEvent == INPUT_BROKER_USER_PRESS) { showFrame(FrameDirection::NEXT); + } else if (event->inputEvent == INPUT_BROKER_FN_F1) { + this->ui->switchToFrame(0); + lastScreenTransition = millis(); + setFastFramerate(); + } else if (event->inputEvent == INPUT_BROKER_FN_F2) { + this->ui->switchToFrame(1); + lastScreenTransition = millis(); + setFastFramerate(); + } else if (event->inputEvent == INPUT_BROKER_FN_F3) { + this->ui->switchToFrame(2); + lastScreenTransition = millis(); + setFastFramerate(); + } else if (event->inputEvent == INPUT_BROKER_FN_F4) { + this->ui->switchToFrame(3); + lastScreenTransition = millis(); + setFastFramerate(); + } else if (event->inputEvent == INPUT_BROKER_FN_F5) { + this->ui->switchToFrame(4); + lastScreenTransition = millis(); + setFastFramerate(); } else if (event->inputEvent == INPUT_BROKER_UP_LONG) { // Long press up button for fast frame switching showPrevFrame(); diff --git a/src/graphics/draw/MessageRenderer.cpp b/src/graphics/draw/MessageRenderer.cpp index 72746e415..193164439 100644 --- a/src/graphics/draw/MessageRenderer.cpp +++ b/src/graphics/draw/MessageRenderer.cpp @@ -431,45 +431,6 @@ static int getDrawnLinePixelBottom(int lineTopY, const std::string &line, bool i return iconTop + tallest - 1; } -static void drawRoundedRectOutline(OLEDDisplay *display, int x, int y, int w, int h, int r) -{ - if (w <= 1 || h <= 1) - return; - - if (r < 0) - r = 0; - - int maxR = (std::min(w, h) / 2) - 1; - if (r > maxR) - r = maxR; - - if (r == 0) { - display->drawRect(x, y, w, h); - return; - } - - const int x0 = x; - const int y0 = y; - const int x1 = x + w - 1; - const int y1 = y + h - 1; - - // sides - if (x0 + r <= x1 - r) { - display->drawLine(x0 + r, y0, x1 - r, y0); // top - display->drawLine(x0 + r, y1, x1 - r, y1); // bottom - } - if (y0 + r <= y1 - r) { - display->drawLine(x0, y0 + r, x0, y1 - r); // left - display->drawLine(x1, y0 + r, x1, y1 - r); // right - } - - // corner arcs - display->drawCircleQuads(x0 + r, y0 + r, r, 2); // top left - display->drawCircleQuads(x1 - r, y0 + r, r, 1); // top right - display->drawCircleQuads(x1 - r, y1 - r, r, 8); // bottom right - display->drawCircleQuads(x0 + r, y1 - r, r, 4); // bottom left -} - static std::vector buildMessageBlocks(const std::vector &isHeaderVec, const std::vector &isMineVec) { std::vector blocks; @@ -909,27 +870,37 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 bubbleW = std::max(1, rightEdge - bubbleX); if (bubbleW > 1 && bubbleH > 1) { - int r = BUBBLE_RADIUS; - int maxR = (std::min(bubbleW, bubbleH) / 2) - 1; - if (maxR < 0) - maxR = 0; - if (r > maxR) - r = maxR; - - drawRoundedRectOutline(display, bubbleX, topY, bubbleW, bubbleH, r); - const int extra = 3; - const int rr = r + extra; int x1 = bubbleX + bubbleW - 1; int y1 = topY + bubbleH - 1; - if (!b.mine) { - // top-left corner square - display->drawLine(bubbleX, topY, bubbleX + rr, topY); - display->drawLine(bubbleX, topY, bubbleX, topY + rr); + if (b.mine) { + // Send Message (Right side) + display->drawRect(x1 + 2 - bubbleW, y1 - bubbleH, bubbleW, bubbleH); + // Top Right Corner + display->drawRect(x1, topY, 2, 1); + display->drawRect(x1, topY, 1, 2); + // Bottom Right Corner + display->drawRect(x1 - 1, bottomY - 2, 2, 1); + display->drawRect(x1, bottomY - 3, 1, 2); + // Knock the corners off to make a bubble + display->setColor(BLACK); + display->drawRect(x1 - bubbleW, topY - 1, 1, 1); + display->drawRect(x1 - bubbleW, bottomY - 1, 1, 1); + display->setColor(WHITE); } else { - // bottom-right corner square - display->drawLine(x1 - rr, y1, x1, y1); - display->drawLine(x1, y1 - rr, x1, y1); + // Received Message (Left Side) + display->drawRect(bubbleX, topY, bubbleW + 1, bubbleH); + // Top Left Corner + display->drawRect(bubbleX + 1, topY + 1, 2, 1); + display->drawRect(bubbleX + 1, topY + 1, 1, 2); + // Bottom Left Corner + display->drawRect(bubbleX + 1, bottomY - 1, 2, 1); + display->drawRect(bubbleX + 1, bottomY - 2, 1, 2); + // Knock the corners off to make a bubble + display->setColor(BLACK); + display->drawRect(bubbleX + bubbleW, topY, 1, 1); + display->drawRect(bubbleX + bubbleW, bottomY, 1, 1); + display->setColor(WHITE); } } } diff --git a/src/input/HackadayCommunicatorKeyboard.cpp b/src/input/HackadayCommunicatorKeyboard.cpp index 87c8a24ae..c6a9e0ae8 100644 --- a/src/input/HackadayCommunicatorKeyboard.cpp +++ b/src/input/HackadayCommunicatorKeyboard.cpp @@ -20,20 +20,20 @@ constexpr uint8_t modifierLeftShift = 0b0001; // Num chars per key, Modulus for rotating through characters static uint8_t HackadayCommunicatorTapMod[_TCA8418_NUM_KEYS] = { - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 2, 2, 2, 1, 2, 2, 0, 0, 0, 2, 1, 2, 2, 0, 1, 1, 0, }; static unsigned char HackadayCommunicatorTapMap[_TCA8418_NUM_KEYS][2] = {{}, - {}, + {Key::FUNCTION_F1}, {'+'}, {'9'}, {'8'}, {'7'}, - {'2'}, - {'3'}, - {'4'}, - {'5'}, + {Key::FUNCTION_F2}, + {Key::FUNCTION_F3}, + {Key::FUNCTION_F4}, + {Key::FUNCTION_F5}, {Key::ESC}, {'q', 'Q'}, {'w', 'W'}, @@ -141,6 +141,7 @@ void HackadayCommunicatorKeyboard::pressed(uint8_t key) if (state == Init || state == Busy) { return; } + LOG_DEBUG("Key pressed: %u", key); if (modifierFlag && (millis() - last_modifier_time > _TCA8418_MULTI_TAP_THRESHOLD)) { modifierFlag = 0; diff --git a/src/input/InputBroker.h b/src/input/InputBroker.h index c55d7fa53..970e9969a 100644 --- a/src/input/InputBroker.h +++ b/src/input/InputBroker.h @@ -27,6 +27,11 @@ enum input_broker_event { INPUT_BROKER_SHUTDOWN = 0x9b, INPUT_BROKER_GPS_TOGGLE = 0x9e, INPUT_BROKER_SEND_PING = 0xaf, + INPUT_BROKER_FN_F1 = 0xf1, + INPUT_BROKER_FN_F2 = 0xf2, + INPUT_BROKER_FN_F3 = 0xf3, + INPUT_BROKER_FN_F4 = 0xf4, + INPUT_BROKER_FN_F5 = 0xf5, INPUT_BROKER_MATRIXKEY = 0xFE, INPUT_BROKER_ANYKEY = 0xff diff --git a/src/input/TCA8418KeyboardBase.h b/src/input/TCA8418KeyboardBase.h index 8e509ac7e..e608c6da5 100644 --- a/src/input/TCA8418KeyboardBase.h +++ b/src/input/TCA8418KeyboardBase.h @@ -26,7 +26,12 @@ class TCA8418KeyboardBase GPS_TOGGLE = 0x9E, MUTE_TOGGLE = 0xAC, SEND_PING = 0xAF, - BL_TOGGLE = 0xAB + BL_TOGGLE = 0xAB, + FUNCTION_F1 = 0xF1, + FUNCTION_F2 = 0xF2, + FUNCTION_F3 = 0xF3, + FUNCTION_F4 = 0xF4, + FUNCTION_F5 = 0xF5 }; typedef uint8_t (*i2c_com_fptr_t)(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint8_t len); diff --git a/src/input/kbI2cBase.cpp b/src/input/kbI2cBase.cpp index 12d0822f6..d744ee2ca 100644 --- a/src/input/kbI2cBase.cpp +++ b/src/input/kbI2cBase.cpp @@ -321,6 +321,26 @@ int32_t KbI2cBase::runOnce() e.inputEvent = INPUT_BROKER_ANYKEY; e.kbchar = INPUT_BROKER_MSG_TAB; break; + case TCA8418KeyboardBase::FUNCTION_F1: + e.inputEvent = INPUT_BROKER_FN_F1; + e.kbchar = 0x00; + break; + case TCA8418KeyboardBase::FUNCTION_F2: + e.inputEvent = INPUT_BROKER_FN_F2; + e.kbchar = 0x00; + break; + case TCA8418KeyboardBase::FUNCTION_F3: + e.inputEvent = INPUT_BROKER_FN_F3; + e.kbchar = 0x00; + break; + case TCA8418KeyboardBase::FUNCTION_F4: + e.inputEvent = INPUT_BROKER_FN_F4; + e.kbchar = 0x00; + break; + case TCA8418KeyboardBase::FUNCTION_F5: + e.inputEvent = INPUT_BROKER_FN_F5; + e.kbchar = 0x00; + break; default: if (nextEvent > 127) { e.inputEvent = INPUT_BROKER_NONE; diff --git a/src/main.cpp b/src/main.cpp index 796341b9b..9b26b7799 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1401,7 +1401,43 @@ void loop() if (inputBroker) inputBroker->processInputEventQueue(); #endif -#if ARCH_PORTDUINO && HAS_TFT +#if ARCH_PORTDUINO + if (portduino_config.lora_spi_dev == "ch341" && ch341Hal != nullptr) { + ch341Hal->checkError(); + } + if (portduino_status.LoRa_in_error && rebootAtMsec == 0) { + LOG_ERROR("LoRa in error detected, attempting to recover"); + if (rIf != nullptr) { + delete rIf; + rIf = nullptr; + } + if (portduino_config.lora_spi_dev == "ch341") { + if (ch341Hal != nullptr) { + delete ch341Hal; + ch341Hal = nullptr; + sleep(3); + } + try { + ch341Hal = new Ch341Hal(0, portduino_config.lora_usb_serial_num, portduino_config.lora_usb_vid, + portduino_config.lora_usb_pid); + } catch (std::exception &e) { + std::cerr << e.what() << std::endl; + std::cerr << "Could not initialize CH341 device!" << std::endl; + exit(EXIT_FAILURE); + } + } + if (initLoRa()) { + router->addInterface(rIf); + portduino_status.LoRa_in_error = false; + } else { + LOG_WARN("Reconfigure failed, rebooting"); + if (screen) { + screen->showSimpleBanner("Rebooting..."); + } + rebootAtMsec = millis() + 25; + } + } +#if HAS_TFT if (screen && portduino_config.displayPanel == x11 && config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { auto dispdev = screen->getDisplayDevice(); @@ -1409,6 +1445,7 @@ void loop() static_cast(dispdev)->sdlLoop(); } #endif +#endif #if HAS_SCREEN && ENABLE_MESSAGE_PERSISTENCE messageStoreAutosaveTick(); #endif diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index 341afe78d..a8a2780ed 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -91,10 +91,21 @@ template bool LR11x0Interface::init() LOG_DEBUG("Set RF1 switch to %s", getFreq() < 1e9 ? "SubGHz" : "2.4GHz"); #endif + // Allow extra time for TCXO to stabilize after power-on + delay(10); + int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage); + + // Retry if we get SPI command failed - some units need extra TCXO stabilization time + if (res == RADIOLIB_ERR_SPI_CMD_FAILED) { + LOG_WARN("LR11x0 init failed with %d (SPI_CMD_FAILED), retrying after delay...", res); + delay(100); + res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage); + } + // \todo Display actual typename of the adapter, not just `LR11x0` LOG_INFO("LR11x0 init result %d", res); - if (res == RADIOLIB_ERR_CHIP_NOT_FOUND) + if (res == RADIOLIB_ERR_CHIP_NOT_FOUND || res == RADIOLIB_ERR_SPI_CMD_FAILED) return false; LR11x0VersionInfo_t version; diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 16c904f18..1ba67fe93 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -2247,7 +2247,10 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co // Currently portuino is mostly used for simulation. Make sure the user notices something really bad happened #ifdef ARCH_PORTDUINO - LOG_ERROR("A critical failure occurred, portduino is exiting"); - exit(2); + LOG_ERROR("A critical failure occurred"); + // TODO: Determine if other critical errors should also cause an immediate exit + if (code == meshtastic_CriticalErrorCode_FLASH_CORRUPTION_RECOVERABLE || + code == meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE) + exit(2); #endif } diff --git a/src/mesh/RF95Interface.cpp b/src/mesh/RF95Interface.cpp index 5588fc348..0c12401ca 100644 --- a/src/mesh/RF95Interface.cpp +++ b/src/mesh/RF95Interface.cpp @@ -177,6 +177,9 @@ bool RF95Interface::init() int res = lora->begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength); LOG_INFO("RF95 init result %d", res); + if (res == RADIOLIB_ERR_CHIP_NOT_FOUND || res == RADIOLIB_ERR_SPI_CMD_FAILED) + return false; + LOG_INFO("Frequency set to %f", getFreq()); LOG_INFO("Bandwidth set to %f", bw); LOG_INFO("Power output set to %d", power); diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 498496a3b..9dfc46bee 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -269,8 +269,12 @@ template void SX126xInterface::setStandby() if (err != RADIOLIB_ERR_NONE) LOG_DEBUG("SX126x standby %s%d", radioLibErr, err); +#ifdef ARCH_PORTDUINO + if (err != RADIOLIB_ERR_NONE) + portduino_status.LoRa_in_error = true; +#else assert(err == RADIOLIB_ERR_NONE); - +#endif isReceiving = false; // If we were receiving, not any more activeReceiveStart = 0; disableInterrupt(); @@ -313,7 +317,12 @@ template void SX126xInterface::startReceive() int err = lora.startReceiveDutyCycleAuto(preambleLength, 8, MESHTASTIC_RADIOLIB_IRQ_RX_FLAGS); if (err != RADIOLIB_ERR_NONE) LOG_ERROR("SX126X startReceiveDutyCycleAuto %s%d", radioLibErr, err); +#ifdef ARCH_PORTDUINO + if (err != RADIOLIB_ERR_NONE) + portduino_status.LoRa_in_error = true; +#else assert(err == RADIOLIB_ERR_NONE); +#endif RadioLibInterface::startReceive(); @@ -341,7 +350,12 @@ template bool SX126xInterface::isChannelActive() return true; if (result != RADIOLIB_CHANNEL_FREE) LOG_ERROR("SX126X scanChannel %s%d", radioLibErr, result); +#ifdef ARCH_PORTDUINO + if (result == RADIOLIB_ERR_WRONG_MODEM) + portduino_status.LoRa_in_error = true; +#else assert(result != RADIOLIB_ERR_WRONG_MODEM); +#endif return false; } diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index b4278c636..3ab63df3d 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -69,6 +69,8 @@ template bool SX128xInterface::init() int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength); // \todo Display actual typename of the adapter, not just `SX128x` LOG_INFO("SX128x init result %d", res); + if (res == RADIOLIB_ERR_CHIP_NOT_FOUND || res == RADIOLIB_ERR_SPI_CMD_FAILED) + return false; if ((config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24) && (res == RADIOLIB_ERR_INVALID_FREQUENCY)) { LOG_WARN("Radio only supports 2.4GHz LoRa. Adjusting Region and rebooting"); diff --git a/src/mesh/generated/meshtastic/admin.pb.cpp b/src/mesh/generated/meshtastic/admin.pb.cpp index e358bc96d..01d3fa910 100644 --- a/src/mesh/generated/meshtastic/admin.pb.cpp +++ b/src/mesh/generated/meshtastic/admin.pb.cpp @@ -27,6 +27,15 @@ PB_BIND(meshtastic_SharedContact, meshtastic_SharedContact, AUTO) PB_BIND(meshtastic_KeyVerificationAdmin, meshtastic_KeyVerificationAdmin, AUTO) +PB_BIND(meshtastic_SensorConfig, meshtastic_SensorConfig, AUTO) + + +PB_BIND(meshtastic_SCD4X_config, meshtastic_SCD4X_config, AUTO) + + +PB_BIND(meshtastic_SEN5X_config, meshtastic_SEN5X_config, AUTO) + + diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index efdead91b..f545ed9bf 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -171,6 +171,48 @@ typedef struct _meshtastic_KeyVerificationAdmin { uint32_t security_number; } meshtastic_KeyVerificationAdmin; +typedef struct _meshtastic_SCD4X_config { + /* Set Automatic self-calibration enabled */ + bool has_set_asc; + bool set_asc; + /* Recalibration target CO2 concentration in ppm (FRC or ASC) */ + bool has_set_target_co2_conc; + uint32_t set_target_co2_conc; + /* Reference temperature in degC */ + bool has_set_temperature; + float set_temperature; + /* Altitude of sensor in meters above sea level. 0 - 3000m (overrides ambient pressure) */ + bool has_set_altitude; + uint32_t set_altitude; + /* Sensor ambient pressure in Pa. 70000 - 120000 Pa (overrides altitude) */ + bool has_set_ambient_pressure; + uint32_t set_ambient_pressure; + /* Perform a factory reset of the sensor */ + bool has_factory_reset; + bool factory_reset; + /* Power mode for sensor (true for low power, false for normal) */ + bool has_set_power_mode; + bool set_power_mode; +} meshtastic_SCD4X_config; + +typedef struct _meshtastic_SEN5X_config { + /* Reference temperature in degC */ + bool has_set_temperature; + float set_temperature; + /* One-shot mode (true for low power - one-shot mode, false for normal - continuous mode) */ + bool has_set_one_shot_mode; + bool set_one_shot_mode; +} meshtastic_SEN5X_config; + +typedef struct _meshtastic_SensorConfig { + /* SCD4X CO2 Sensor configuration */ + bool has_scd4x_config; + meshtastic_SCD4X_config scd4x_config; + /* SEN5X PM Sensor configuration */ + bool has_sen5x_config; + meshtastic_SEN5X_config sen5x_config; +} meshtastic_SensorConfig; + typedef PB_BYTES_ARRAY_T(8) meshtastic_AdminMessage_session_passkey_t; /* This message is handled by the Admin module and is responsible for all settings/channel read/write operations. This message is used to do settings operations to both remote AND local nodes. @@ -303,6 +345,8 @@ typedef struct _meshtastic_AdminMessage { bool nodedb_reset; /* Tell the node to reset into the OTA Loader */ meshtastic_AdminMessage_OTAEvent ota_request; + /* Parameters and sensor configuration */ + meshtastic_SensorConfig sensor_config; }; /* The node generates this key and sends it with any get_x_response packets. The client MUST include the same key with any set_x commands. Key expires after 300 seconds. @@ -351,6 +395,9 @@ extern "C" { #define meshtastic_KeyVerificationAdmin_message_type_ENUMTYPE meshtastic_KeyVerificationAdmin_MessageType + + + /* Initializer values for message structs */ #define meshtastic_AdminMessage_init_default {0, {0}, {0, {0}}} #define meshtastic_AdminMessage_InputEvent_init_default {0, 0, 0, 0} @@ -359,6 +406,9 @@ extern "C" { #define meshtastic_NodeRemoteHardwarePinsResponse_init_default {0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}} #define meshtastic_SharedContact_init_default {0, false, meshtastic_User_init_default, 0, 0} #define meshtastic_KeyVerificationAdmin_init_default {_meshtastic_KeyVerificationAdmin_MessageType_MIN, 0, 0, false, 0} +#define meshtastic_SensorConfig_init_default {false, meshtastic_SCD4X_config_init_default, false, meshtastic_SEN5X_config_init_default} +#define meshtastic_SCD4X_config_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_SEN5X_config_init_default {false, 0, false, 0} #define meshtastic_AdminMessage_init_zero {0, {0}, {0, {0}}} #define meshtastic_AdminMessage_InputEvent_init_zero {0, 0, 0, 0} #define meshtastic_AdminMessage_OTAEvent_init_zero {_meshtastic_OTAMode_MIN, {0, {0}}} @@ -366,6 +416,9 @@ extern "C" { #define meshtastic_NodeRemoteHardwarePinsResponse_init_zero {0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}} #define meshtastic_SharedContact_init_zero {0, false, meshtastic_User_init_zero, 0, 0} #define meshtastic_KeyVerificationAdmin_init_zero {_meshtastic_KeyVerificationAdmin_MessageType_MIN, 0, 0, false, 0} +#define meshtastic_SensorConfig_init_zero {false, meshtastic_SCD4X_config_init_zero, false, meshtastic_SEN5X_config_init_zero} +#define meshtastic_SCD4X_config_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_SEN5X_config_init_zero {false, 0, false, 0} /* Field tags (for use in manual encoding/decoding) */ #define meshtastic_AdminMessage_InputEvent_event_code_tag 1 @@ -387,6 +440,17 @@ extern "C" { #define meshtastic_KeyVerificationAdmin_remote_nodenum_tag 2 #define meshtastic_KeyVerificationAdmin_nonce_tag 3 #define meshtastic_KeyVerificationAdmin_security_number_tag 4 +#define meshtastic_SCD4X_config_set_asc_tag 1 +#define meshtastic_SCD4X_config_set_target_co2_conc_tag 2 +#define meshtastic_SCD4X_config_set_temperature_tag 3 +#define meshtastic_SCD4X_config_set_altitude_tag 4 +#define meshtastic_SCD4X_config_set_ambient_pressure_tag 5 +#define meshtastic_SCD4X_config_factory_reset_tag 6 +#define meshtastic_SCD4X_config_set_power_mode_tag 7 +#define meshtastic_SEN5X_config_set_temperature_tag 1 +#define meshtastic_SEN5X_config_set_one_shot_mode_tag 2 +#define meshtastic_SensorConfig_scd4x_config_tag 1 +#define meshtastic_SensorConfig_sen5x_config_tag 2 #define meshtastic_AdminMessage_get_channel_request_tag 1 #define meshtastic_AdminMessage_get_channel_response_tag 2 #define meshtastic_AdminMessage_get_owner_request_tag 3 @@ -443,6 +507,7 @@ extern "C" { #define meshtastic_AdminMessage_factory_reset_config_tag 99 #define meshtastic_AdminMessage_nodedb_reset_tag 100 #define meshtastic_AdminMessage_ota_request_tag 102 +#define meshtastic_AdminMessage_sensor_config_tag 103 #define meshtastic_AdminMessage_session_passkey_tag 101 /* Struct field encoding specification for nanopb */ @@ -503,7 +568,8 @@ X(a, STATIC, ONEOF, INT32, (payload_variant,shutdown_seconds,shutdown_se X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_config,factory_reset_config), 99) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,nodedb_reset,nodedb_reset), 100) \ X(a, STATIC, SINGULAR, BYTES, session_passkey, 101) \ -X(a, STATIC, ONEOF, MESSAGE, (payload_variant,ota_request,ota_request), 102) +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,ota_request,ota_request), 102) \ +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,sensor_config,sensor_config), 103) #define meshtastic_AdminMessage_CALLBACK NULL #define meshtastic_AdminMessage_DEFAULT NULL #define meshtastic_AdminMessage_payload_variant_get_channel_response_MSGTYPE meshtastic_Channel @@ -525,6 +591,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,ota_request,ota_request), 10 #define meshtastic_AdminMessage_payload_variant_add_contact_MSGTYPE meshtastic_SharedContact #define meshtastic_AdminMessage_payload_variant_key_verification_MSGTYPE meshtastic_KeyVerificationAdmin #define meshtastic_AdminMessage_payload_variant_ota_request_MSGTYPE meshtastic_AdminMessage_OTAEvent +#define meshtastic_AdminMessage_payload_variant_sensor_config_MSGTYPE meshtastic_SensorConfig #define meshtastic_AdminMessage_InputEvent_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, event_code, 1) \ @@ -571,6 +638,31 @@ X(a, STATIC, OPTIONAL, UINT32, security_number, 4) #define meshtastic_KeyVerificationAdmin_CALLBACK NULL #define meshtastic_KeyVerificationAdmin_DEFAULT NULL +#define meshtastic_SensorConfig_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, scd4x_config, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, sen5x_config, 2) +#define meshtastic_SensorConfig_CALLBACK NULL +#define meshtastic_SensorConfig_DEFAULT NULL +#define meshtastic_SensorConfig_scd4x_config_MSGTYPE meshtastic_SCD4X_config +#define meshtastic_SensorConfig_sen5x_config_MSGTYPE meshtastic_SEN5X_config + +#define meshtastic_SCD4X_config_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, BOOL, set_asc, 1) \ +X(a, STATIC, OPTIONAL, UINT32, set_target_co2_conc, 2) \ +X(a, STATIC, OPTIONAL, FLOAT, set_temperature, 3) \ +X(a, STATIC, OPTIONAL, UINT32, set_altitude, 4) \ +X(a, STATIC, OPTIONAL, UINT32, set_ambient_pressure, 5) \ +X(a, STATIC, OPTIONAL, BOOL, factory_reset, 6) \ +X(a, STATIC, OPTIONAL, BOOL, set_power_mode, 7) +#define meshtastic_SCD4X_config_CALLBACK NULL +#define meshtastic_SCD4X_config_DEFAULT NULL + +#define meshtastic_SEN5X_config_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, FLOAT, set_temperature, 1) \ +X(a, STATIC, OPTIONAL, BOOL, set_one_shot_mode, 2) +#define meshtastic_SEN5X_config_CALLBACK NULL +#define meshtastic_SEN5X_config_DEFAULT NULL + extern const pb_msgdesc_t meshtastic_AdminMessage_msg; extern const pb_msgdesc_t meshtastic_AdminMessage_InputEvent_msg; extern const pb_msgdesc_t meshtastic_AdminMessage_OTAEvent_msg; @@ -578,6 +670,9 @@ extern const pb_msgdesc_t meshtastic_HamParameters_msg; extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePinsResponse_msg; extern const pb_msgdesc_t meshtastic_SharedContact_msg; extern const pb_msgdesc_t meshtastic_KeyVerificationAdmin_msg; +extern const pb_msgdesc_t meshtastic_SensorConfig_msg; +extern const pb_msgdesc_t meshtastic_SCD4X_config_msg; +extern const pb_msgdesc_t meshtastic_SEN5X_config_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define meshtastic_AdminMessage_fields &meshtastic_AdminMessage_msg @@ -587,6 +682,9 @@ extern const pb_msgdesc_t meshtastic_KeyVerificationAdmin_msg; #define meshtastic_NodeRemoteHardwarePinsResponse_fields &meshtastic_NodeRemoteHardwarePinsResponse_msg #define meshtastic_SharedContact_fields &meshtastic_SharedContact_msg #define meshtastic_KeyVerificationAdmin_fields &meshtastic_KeyVerificationAdmin_msg +#define meshtastic_SensorConfig_fields &meshtastic_SensorConfig_msg +#define meshtastic_SCD4X_config_fields &meshtastic_SCD4X_config_msg +#define meshtastic_SEN5X_config_fields &meshtastic_SEN5X_config_msg /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_ADMIN_PB_H_MAX_SIZE meshtastic_AdminMessage_size @@ -596,6 +694,9 @@ extern const pb_msgdesc_t meshtastic_KeyVerificationAdmin_msg; #define meshtastic_HamParameters_size 31 #define meshtastic_KeyVerificationAdmin_size 25 #define meshtastic_NodeRemoteHardwarePinsResponse_size 496 +#define meshtastic_SCD4X_config_size 29 +#define meshtastic_SEN5X_config_size 7 +#define meshtastic_SensorConfig_size 40 #define meshtastic_SharedContact_size 127 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/telemetry.pb.cpp b/src/mesh/generated/meshtastic/telemetry.pb.cpp index 345d7a157..fff75ebc1 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.cpp +++ b/src/mesh/generated/meshtastic/telemetry.pb.cpp @@ -33,6 +33,9 @@ PB_BIND(meshtastic_Telemetry, meshtastic_Telemetry, 2) PB_BIND(meshtastic_Nau7802Config, meshtastic_Nau7802Config, AUTO) +PB_BIND(meshtastic_SEN5XState, meshtastic_SEN5XState, AUTO) + + diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h index 131dd9949..dc9d876dc 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.h +++ b/src/mesh/generated/meshtastic/telemetry.pb.h @@ -435,6 +435,25 @@ typedef struct _meshtastic_Nau7802Config { float calibrationFactor; } meshtastic_Nau7802Config; +/* SEN5X State, for saving to flash */ +typedef struct _meshtastic_SEN5XState { + /* Last cleaning time for SEN5X */ + uint32_t last_cleaning_time; + /* Last cleaning time for SEN5X - valid flag */ + bool last_cleaning_valid; + /* Config flag for one-shot mode (see admin.proto) */ + bool one_shot_mode; + /* Last VOC state time for SEN55 */ + bool has_voc_state_time; + uint32_t voc_state_time; + /* Last VOC state validity flag for SEN55 */ + bool has_voc_state_valid; + bool voc_state_valid; + /* VOC state array (8x uint8t) for SEN55 */ + bool has_voc_state_array; + uint64_t voc_state_array; +} meshtastic_SEN5XState; + #ifdef __cplusplus extern "C" { @@ -455,6 +474,7 @@ extern "C" { + /* Initializer values for message structs */ #define meshtastic_DeviceMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_EnvironmentMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} @@ -465,6 +485,7 @@ extern "C" { #define meshtastic_HostMetrics_init_default {0, 0, 0, false, 0, false, 0, 0, 0, 0, false, ""} #define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}} #define meshtastic_Nau7802Config_init_default {0, 0} +#define meshtastic_SEN5XState_init_default {0, 0, 0, false, 0, false, 0, false, 0} #define meshtastic_DeviceMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_EnvironmentMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_PowerMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} @@ -474,6 +495,7 @@ extern "C" { #define meshtastic_HostMetrics_init_zero {0, 0, 0, false, 0, false, 0, 0, 0, 0, false, ""} #define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}} #define meshtastic_Nau7802Config_init_zero {0, 0} +#define meshtastic_SEN5XState_init_zero {0, 0, 0, false, 0, false, 0, false, 0} /* Field tags (for use in manual encoding/decoding) */ #define meshtastic_DeviceMetrics_battery_level_tag 1 @@ -581,6 +603,12 @@ extern "C" { #define meshtastic_Telemetry_host_metrics_tag 8 #define meshtastic_Nau7802Config_zeroOffset_tag 1 #define meshtastic_Nau7802Config_calibrationFactor_tag 2 +#define meshtastic_SEN5XState_last_cleaning_time_tag 1 +#define meshtastic_SEN5XState_last_cleaning_valid_tag 2 +#define meshtastic_SEN5XState_one_shot_mode_tag 3 +#define meshtastic_SEN5XState_voc_state_time_tag 4 +#define meshtastic_SEN5XState_voc_state_valid_tag 5 +#define meshtastic_SEN5XState_voc_state_array_tag 6 /* Struct field encoding specification for nanopb */ #define meshtastic_DeviceMetrics_FIELDLIST(X, a) \ @@ -731,6 +759,16 @@ X(a, STATIC, SINGULAR, FLOAT, calibrationFactor, 2) #define meshtastic_Nau7802Config_CALLBACK NULL #define meshtastic_Nau7802Config_DEFAULT NULL +#define meshtastic_SEN5XState_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, last_cleaning_time, 1) \ +X(a, STATIC, SINGULAR, BOOL, last_cleaning_valid, 2) \ +X(a, STATIC, SINGULAR, BOOL, one_shot_mode, 3) \ +X(a, STATIC, OPTIONAL, UINT32, voc_state_time, 4) \ +X(a, STATIC, OPTIONAL, BOOL, voc_state_valid, 5) \ +X(a, STATIC, OPTIONAL, FIXED64, voc_state_array, 6) +#define meshtastic_SEN5XState_CALLBACK NULL +#define meshtastic_SEN5XState_DEFAULT NULL + extern const pb_msgdesc_t meshtastic_DeviceMetrics_msg; extern const pb_msgdesc_t meshtastic_EnvironmentMetrics_msg; extern const pb_msgdesc_t meshtastic_PowerMetrics_msg; @@ -740,6 +778,7 @@ extern const pb_msgdesc_t meshtastic_HealthMetrics_msg; extern const pb_msgdesc_t meshtastic_HostMetrics_msg; extern const pb_msgdesc_t meshtastic_Telemetry_msg; extern const pb_msgdesc_t meshtastic_Nau7802Config_msg; +extern const pb_msgdesc_t meshtastic_SEN5XState_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define meshtastic_DeviceMetrics_fields &meshtastic_DeviceMetrics_msg @@ -751,6 +790,7 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg; #define meshtastic_HostMetrics_fields &meshtastic_HostMetrics_msg #define meshtastic_Telemetry_fields &meshtastic_Telemetry_msg #define meshtastic_Nau7802Config_fields &meshtastic_Nau7802Config_msg +#define meshtastic_SEN5XState_fields &meshtastic_SEN5XState_msg /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_TELEMETRY_PB_H_MAX_SIZE meshtastic_Telemetry_size @@ -762,6 +802,7 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg; #define meshtastic_LocalStats_size 87 #define meshtastic_Nau7802Config_size 16 #define meshtastic_PowerMetrics_size 81 +#define meshtastic_SEN5XState_size 27 #define meshtastic_Telemetry_size 272 #ifdef __cplusplus diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp index 11b05165c..0376a1dad 100644 --- a/src/platform/nrf52/main-nrf52.cpp +++ b/src/platform/nrf52/main-nrf52.cpp @@ -464,22 +464,6 @@ void cpuDeepSleep(uint32_t msecToWake) // FIXME, use non-init RAM per // https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled -#ifdef ELECROW_ThinkNode_M1 - nrf_gpio_cfg_input(PIN_BUTTON1, NRF_GPIO_PIN_PULLUP); // Configure the pin to be woken up as an input - nrf_gpio_pin_sense_t sense = NRF_GPIO_PIN_SENSE_LOW; - nrf_gpio_cfg_sense_set(PIN_BUTTON1, sense); - - nrf_gpio_cfg_input(PIN_BUTTON2, NRF_GPIO_PIN_PULLUP); - nrf_gpio_pin_sense_t sense1 = NRF_GPIO_PIN_SENSE_LOW; - nrf_gpio_cfg_sense_set(PIN_BUTTON2, sense1); -#endif - -#ifdef PROMICRO_DIY_TCXO - nrf_gpio_cfg_input(BUTTON_PIN, NRF_GPIO_PIN_PULLUP); // Enable internal pull-up on the button pin - nrf_gpio_pin_sense_t sense = NRF_GPIO_PIN_SENSE_LOW; // Configure SENSE signal on low edge - nrf_gpio_cfg_sense_set(BUTTON_PIN, sense); // Apply SENSE to wake up the device from the deep sleep -#endif - #ifdef BATTERY_LPCOMP_INPUT // Wake up if power rises again nrf_lpcomp_config_t c; diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index 9159d5954..d0d8ba40f 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,7 @@ #include "platform/portduino/USBHal.h" portduino_config_struct portduino_config; +portduino_status_struct portduino_status; std::ofstream traceFile; std::ofstream JSONFile; Ch341Hal *ch341Hal = nullptr; @@ -400,6 +402,11 @@ void portduinoSetup() if (found_hat) { product_config = cleanupNameForAutoconf("lora-hat-" + std::string(hat_vendor) + "-" + autoconf_product + ".yaml"); + if (strncmp(hat_vendor, "RAK", strlen("RAK")) == 0 && + strncmp(autoconf_product, "6421 Pi Hat", strlen("6421 Pi Hat")) == 0) { + std::cout << "autoconf: Setting hardwareModel to RAK6421" << std::endl; + portduino_status.hardwareModel = meshtastic_HardwareModel_RAK6421; + } } else if (found_ch341) { product_config = cleanupNameForAutoconf("lora-usb-" + std::string(autoconf_product) + ".yaml"); // look for more data after the null terminator @@ -408,6 +415,10 @@ void portduinoSetup() memcpy(portduino_config.device_id, autoconf_product + len + 1, 16); if (!memfll(portduino_config.device_id, '\0', 16) && !memfll(portduino_config.device_id, 0xff, 16)) { portduino_config.has_device_id = true; + if (strncmp(autoconf_product, "MESHSTICK 1262", strlen("MESHSTICK 1262")) == 0) { + std::cout << "autoconf: Setting hardwareModel to Meshstick 1262" << std::endl; + portduino_status.hardwareModel = meshtastic_HardwareModel_MESHSTICK_1262; + } } } } diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index eb4eb8925..af511be6e 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -1,15 +1,22 @@ #pragma once #include #include +#include #include #include #include "LR11x0Interface.h" #include "Module.h" #include "mesh/generated/meshtastic/mesh.pb.h" -#include "platform/portduino/USBHal.h" #include "yaml-cpp/yaml.h" +extern struct portduino_status_struct { + bool LoRa_in_error = false; + _meshtastic_HardwareModel hardwareModel = meshtastic_HardwareModel_PORTDUINO; +} portduino_status; + +#include "platform/portduino/USBHal.h" + // Product strings for auto-configuration // {"PRODUCT_STRING", "CONFIG.YAML"} // YAML paths are relative to `meshtastic/available.d` diff --git a/src/platform/portduino/USBHal.h b/src/platform/portduino/USBHal.h index ecc292430..441f75b10 100644 --- a/src/platform/portduino/USBHal.h +++ b/src/platform/portduino/USBHal.h @@ -9,6 +9,8 @@ #include #include +extern uint32_t rebootAtMsec; + // include the library for Raspberry GPIO pins #define PI_RISING (PINEDIO_INT_MODE_RISING) @@ -45,7 +47,7 @@ class Ch341Hal : public RadioLibHal int32_t ret = pinedio_init(&pinedio, NULL); if (ret != 0) { std::string s = "Could not open SPI: "; - throw(s + std::to_string(ret)); + throw std::runtime_error(s + std::to_string(ret)); } pinedio_set_option(&pinedio, PINEDIO_OPTION_AUTO_CS, 0); @@ -74,30 +76,55 @@ class Ch341Hal : public RadioLibHal // RADIOLIB_NC as an alias for non-connected pins void pinMode(uint32_t pin, uint32_t mode) override { + if (checkError()) { + return; + } if (pin == RADIOLIB_NC) { return; } - pinedio_set_pin_mode(&pinedio, pin, mode); + auto res = pinedio_set_pin_mode(&pinedio, pin, mode); + if (res < 0 && rebootAtMsec == 0) { + LOG_ERROR("USBHal pinMode: Could not set pin %u mode to %u: %d", pin, mode, res); + } } void digitalWrite(uint32_t pin, uint32_t value) override { + if (checkError()) { + return; + } if (pin == RADIOLIB_NC) { return; } - pinedio_digital_write(&pinedio, pin, value); + auto res = pinedio_digital_write(&pinedio, pin, value); + if (res < 0 && rebootAtMsec == 0) { + LOG_ERROR("USBHal digitalWrite: Could not write pin %u: %d", pin, res); + portduino_status.LoRa_in_error = true; + } } uint32_t digitalRead(uint32_t pin) override { + if (checkError()) { + return 0; + } if (pin == RADIOLIB_NC) { return 0; } - return pinedio_digital_read(&pinedio, pin); + auto res = pinedio_digital_read(&pinedio, pin); + if (res < 0 && rebootAtMsec == 0) { + LOG_ERROR("USBHal digitalRead: Could not read pin %u: %d", pin, res); + portduino_status.LoRa_in_error = true; + return 0; + } + return res; } void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override { + if (checkError()) { + return; + } if (interruptNum == RADIOLIB_NC) { return; } @@ -107,6 +134,9 @@ class Ch341Hal : public RadioLibHal void detachInterrupt(uint32_t interruptNum) override { + if (checkError()) { + return; + } if (interruptNum == RADIOLIB_NC) { return; } @@ -152,6 +182,9 @@ class Ch341Hal : public RadioLibHal void spiTransfer(uint8_t *out, size_t len, uint8_t *in) { + if (checkError()) { + return; + } int32_t ret = pinedio_transceive(&this->pinedio, out, in, len); if (ret < 0) { std::cerr << "Could not perform SPI transfer: " << ret << std::endl; @@ -160,9 +193,22 @@ class Ch341Hal : public RadioLibHal void spiEndTransaction() {} void spiEnd() {} + bool checkError() + { + if (pinedio.in_error) { + if (!has_warned) + LOG_ERROR("USBHal: libch341 in_error detected"); + portduino_status.LoRa_in_error = true; + has_warned = true; + return true; + } + has_warned = false; + return false; + } private: pinedio_inst pinedio = {0}; + bool has_warned = false; }; #endif diff --git a/src/platform/portduino/architecture.h b/src/platform/portduino/architecture.h index e10519d21..9ee8ad366 100644 --- a/src/platform/portduino/architecture.h +++ b/src/platform/portduino/architecture.h @@ -6,7 +6,7 @@ // set HW_VENDOR // -#define HW_VENDOR meshtastic_HardwareModel_PORTDUINO +#define HW_VENDOR portduino_status.hardwareModel #ifndef HAS_BUTTON #define HAS_BUTTON 1 diff --git a/variants/esp32s3/heltec_wireless_tracker_v2/platformio.ini b/variants/esp32s3/heltec_wireless_tracker_v2/platformio.ini index 8bdb71541..0b486618b 100644 --- a/variants/esp32s3/heltec_wireless_tracker_v2/platformio.ini +++ b/variants/esp32s3/heltec_wireless_tracker_v2/platformio.ini @@ -1,8 +1,17 @@ [env:heltec-wireless-tracker-v2] +custom_meshtastic_support_level = 1 +custom_meshtastic_images = heltec_wireless_tracker_v2.svg +custom_meshtastic_tags = Heltec + extends = esp32s3_base board = heltec_wireless_tracker_v2 board_build.partitions = default_8MB.csv upload_protocol = esptool +custom_meshtastic_hw_model = 113 +custom_meshtastic_hw_model_slug = HELTEC_WIRELESS_TRACKER_V2 +custom_meshtastic_architecture = esp32s3 +custom_meshtastic_display_name = Heltec Wireless Tracker V2 +custom_meshtastic_actively_supported = true build_flags = ${esp32s3_base.build_flags} diff --git a/variants/native/portduino.ini b/variants/native/portduino.ini index cc6c39aa2..55905481a 100644 --- a/variants/native/portduino.ini +++ b/variants/native/portduino.ini @@ -29,7 +29,7 @@ lib_deps = # renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX lovyan03/LovyanGFX@1.2.7 # renovate: datasource=git-refs depName=libch341-spi-userspace packageName=https://github.com/pine64/libch341-spi-userspace gitBranch=main - https://github.com/pine64/libch341-spi-userspace/archive/af9bc27c9c30fa90772279925b7c5913dff789b4.zip + https://github.com/pine64/libch341-spi-userspace/archive/23c42319a69cffcb65868e3c72e6bed83974a393.zip # renovate: datasource=custom.pio depName=adafruit/Adafruit seesaw Library packageName=adafruit/library/Adafruit seesaw Library adafruit/Adafruit seesaw Library@1.7.9 # renovate: datasource=git-refs depName=RAK12034-BMX160 packageName=https://github.com/RAKWireless/RAK12034-BMX160 gitBranch=main diff --git a/variants/nrf52840/ELECROW-ThinkNode-M1/variant.cpp b/variants/nrf52840/ELECROW-ThinkNode-M1/variant.cpp index 2ce84bc57..1560cde73 100644 --- a/variants/nrf52840/ELECROW-ThinkNode-M1/variant.cpp +++ b/variants/nrf52840/ELECROW-ThinkNode-M1/variant.cpp @@ -59,4 +59,11 @@ void variant_shutdown() NRF_GPIO->DIRCLR = (1 << pin); } } + nrf_gpio_cfg_input(PIN_BUTTON1, NRF_GPIO_PIN_PULLUP); // Configure the pin to be woken up as an input + nrf_gpio_pin_sense_t sense = NRF_GPIO_PIN_SENSE_LOW; + nrf_gpio_cfg_sense_set(PIN_BUTTON1, sense); + + nrf_gpio_cfg_input(PIN_BUTTON2, NRF_GPIO_PIN_PULLUP); + nrf_gpio_pin_sense_t sense1 = NRF_GPIO_PIN_SENSE_LOW; + nrf_gpio_cfg_sense_set(PIN_BUTTON2, sense1); } \ No newline at end of file diff --git a/variants/nrf52840/ELECROW-ThinkNode-M4/variant.cpp b/variants/nrf52840/ELECROW-ThinkNode-M4/variant.cpp index af9bed998..5c4b6215b 100644 --- a/variants/nrf52840/ELECROW-ThinkNode-M4/variant.cpp +++ b/variants/nrf52840/ELECROW-ThinkNode-M4/variant.cpp @@ -49,3 +49,21 @@ void initVariant() pinMode(Battery_LED_4, OUTPUT); ledOff(Battery_LED_4); } + +/// called from main-nrf52.cpp during the cpuDeepSleep() function +void variant_shutdown() +{ + for (int pin = 0; pin < 48; pin++) { + if (pin == PIN_GPS_EN || pin == PIN_BUTTON1) { + continue; + } + pinMode(pin, OUTPUT); + digitalWrite(pin, LOW); + if (pin >= 32) { + NRF_P1->DIRCLR = (1 << (pin - 32)); + } else { + NRF_GPIO->DIRCLR = (1 << pin); + } + } + digitalWrite(PIN_GPS_EN, HIGH); +} diff --git a/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/variant.cpp b/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/variant.cpp index 5869ed1d4..384c618e2 100644 --- a/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/variant.cpp +++ b/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/variant.cpp @@ -36,3 +36,10 @@ void initVariant() pinMode(PIN_3V3_EN, OUTPUT); digitalWrite(PIN_3V3_EN, HIGH); } + +void variant_shutdown() +{ + nrf_gpio_cfg_input(BUTTON_PIN, NRF_GPIO_PIN_PULLUP); // Enable internal pull-up on the button pin + nrf_gpio_pin_sense_t sense = NRF_GPIO_PIN_SENSE_LOW; // Configure SENSE signal on low edge + nrf_gpio_cfg_sense_set(BUTTON_PIN, sense); // Apply SENSE to wake up the device from the deep sleep +}