From d9031610ab00d35757f080eaeed8b1164595585a Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 3 Feb 2023 08:50:10 -0600 Subject: [PATCH] Connection status admin message --- src/BluetoothCommon.h | 12 ++- src/main.cpp | 6 ++ src/main.h | 8 ++ src/modules/AdminModule.cpp | 140 +++++++++++--------------- src/modules/AdminModule.h | 62 ++++++------ src/nimble/NimbleBluetooth.cpp | 17 +++- src/nimble/NimbleBluetooth.h | 5 +- src/platform/esp32/main-esp32.cpp | 2 - src/platform/nrf52/NRF52Bluetooth.cpp | 10 ++ src/platform/nrf52/NRF52Bluetooth.h | 5 +- src/platform/nrf52/main-nrf52.cpp | 5 +- 11 files changed, 149 insertions(+), 123 deletions(-) diff --git a/src/BluetoothCommon.h b/src/BluetoothCommon.h index 4352eba13..586ffaa3c 100644 --- a/src/BluetoothCommon.h +++ b/src/BluetoothCommon.h @@ -17,4 +17,14 @@ extern const uint8_t MESH_SERVICE_UUID_16[], TORADIO_UUID_16[16u], FROMRADIO_UUID_16[], FROMNUM_UUID_16[]; /// Given a level between 0-100, update the BLE attribute -void updateBatteryLevel(uint8_t level); \ No newline at end of file +void updateBatteryLevel(uint8_t level); + +class BluetoothApi +{ + public: + virtual void setup(); + virtual void shutdown(); + virtual void clearBonds(); + virtual bool isConnected(); + virtual int getRssi() = 0; +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 4f65ff51b..49963a415 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -33,6 +33,12 @@ #ifdef ARCH_ESP32 #include "mesh/http/WebServer.h" #include "nimble/NimbleBluetooth.h" +NimbleBluetooth *nimbleBluetooth; +#endif + +#ifdef ARCH_NRF52 +#include "NRF52Bluetooth.h" +NRF52Bluetooth *nrf52Bluetooth; #endif #if HAS_WIFI diff --git a/src/main.h b/src/main.h index 9d965b0fc..b1bee1b06 100644 --- a/src/main.h +++ b/src/main.h @@ -9,6 +9,14 @@ #if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) #include #endif +#if defined(ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) +#include "nimble/NimbleBluetooth.h" +extern NimbleBluetooth *nimbleBluetooth; +#endif +#ifdef ARCH_NRF52 +#include "NRF52Bluetooth.h" +extern NRF52Bluetooth *nrf52Bluetooth; +#endif extern uint8_t screen_found; extern uint8_t screen_model; diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 16dee49a9..7beea8804 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -2,13 +2,16 @@ #include "Channels.h" #include "MeshService.h" #include "NodeDB.h" +#include "PowerFSM.h" #ifdef ARCH_ESP32 #include "BleOta.h" #endif #include "Router.h" #include "configuration.h" #include "main.h" - +#ifdef ARCH_NRF52 +#include "main.h" +#endif #ifdef ARCH_PORTDUINO #include "unistd.h" #endif @@ -30,8 +33,7 @@ static const char *secretReserved = "sekrit"; /// If buf is the reserved secret word, replace the buffer with currentVal static void writeSecret(char *buf, size_t bufsz, const char *currentVal) { - if (strcmp(buf, secretReserved) == 0) - { + if (strcmp(buf, secretReserved) == 0) { strncpy(buf, currentVal, bufsz); } } @@ -49,8 +51,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta bool handled = false; assert(r); - switch (r->which_payload_variant) - { + switch (r->which_payload_variant) { /** * Getters @@ -70,8 +71,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta handleGetModuleConfig(mp, r->get_module_config_request); break; - case meshtastic_AdminMessage_get_channel_request_tag: - { + case meshtastic_AdminMessage_get_channel_request_tag: { uint32_t i = r->get_channel_request - 1; LOG_INFO("Client is getting channel %u\n", i); if (i >= MAX_NUM_CHANNELS) @@ -110,22 +110,17 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta /** * Other */ - case meshtastic_AdminMessage_reboot_seconds_tag: - { + case meshtastic_AdminMessage_reboot_seconds_tag: { reboot(r->reboot_seconds); break; } - case meshtastic_AdminMessage_reboot_ota_seconds_tag: - { + case meshtastic_AdminMessage_reboot_ota_seconds_tag: { int32_t s = r->reboot_ota_seconds; #ifdef ARCH_ESP32 - if (BleOta::getOtaAppVersion().isEmpty()) - { + if (BleOta::getOtaAppVersion().isEmpty()) { LOG_INFO("No OTA firmware available, scheduling regular reboot in %d seconds\n", s); screen->startRebootScreen(); - } - else - { + } else { screen->startFirmwareUpdateScreen(); BleOta::switchToOtaApp(); LOG_INFO("Rebooting to OTA in %d seconds\n", s); @@ -137,46 +132,45 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000); break; } - case meshtastic_AdminMessage_shutdown_seconds_tag: - { + case meshtastic_AdminMessage_shutdown_seconds_tag: { int32_t s = r->shutdown_seconds; LOG_INFO("Shutdown in %d seconds\n", s); shutdownAtMsec = (s < 0) ? 0 : (millis() + s * 1000); break; } - case meshtastic_AdminMessage_get_device_metadata_request_tag: - { + case meshtastic_AdminMessage_get_device_metadata_request_tag: { LOG_INFO("Client is getting device metadata\n"); handleGetDeviceMetadata(mp); break; } - case meshtastic_AdminMessage_factory_reset_tag: - { + case meshtastic_AdminMessage_factory_reset_tag: { LOG_INFO("Initiating factory reset\n"); nodeDB.factoryReset(); reboot(DEFAULT_REBOOT_SECONDS); break; } - case meshtastic_AdminMessage_nodedb_reset_tag: - { + case meshtastic_AdminMessage_nodedb_reset_tag: { LOG_INFO("Initiating node-db reset\n"); nodeDB.resetNodes(); reboot(DEFAULT_REBOOT_SECONDS); break; } - case meshtastic_AdminMessage_begin_edit_settings_tag: - { + case meshtastic_AdminMessage_begin_edit_settings_tag: { LOG_INFO("Beginning transaction for editing settings\n"); hasOpenEditTransaction = true; break; } - case meshtastic_AdminMessage_commit_edit_settings_tag: - { + case meshtastic_AdminMessage_commit_edit_settings_tag: { LOG_INFO("Committing transaction for edited settings\n"); hasOpenEditTransaction = false; saveChanges(SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS); break; } + case meshtastic_AdminMessage_get_device_connection_status_request_tag: { + LOG_INFO("Client is getting device connection status\n"); + handleGetDeviceConnectionStatus(mp); + break; + } #ifdef ARCH_PORTDUINO case meshtastic_AdminMessage_exit_simulator_tag: LOG_INFO("Exiting simulator\n"); @@ -188,16 +182,11 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta meshtastic_AdminMessage res = meshtastic_AdminMessage_init_default; AdminMessageHandleResult handleResult = MeshModule::handleAdminMessageForAllPlugins(mp, r, &res); - if (handleResult == AdminMessageHandleResult::HANDLED_WITH_RESPONSE) - { + if (handleResult == AdminMessageHandleResult::HANDLED_WITH_RESPONSE) { myReply = allocDataProtobuf(res); - } - else if (mp.decoded.want_response) - { + } else if (mp.decoded.want_response) { LOG_DEBUG("We did not responded to a request that wanted a respond. req.variant=%d\n", r->which_payload_variant); - } - else if (handleResult != AdminMessageHandleResult::HANDLED) - { + } else if (handleResult != AdminMessageHandleResult::HANDLED) { // Probably a message sent by us or sent to our local node. FIXME, we should avoid scanning these messages LOG_INFO("Ignoring nonrelevant admin %d\n", r->which_payload_variant); } @@ -205,8 +194,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta } // If asked for a response and it is not yet set, generate an 'ACK' response - if (mp.decoded.want_response && !myReply) - { + if (mp.decoded.want_response && !myReply) { myReply = allocErrorResponse(meshtastic_Routing_Error_NONE, &mp); } @@ -222,31 +210,26 @@ void AdminModule::handleSetOwner(const meshtastic_User &o) int changed = 0; bool licensed_changed = false; - if (*o.long_name) - { + if (*o.long_name) { changed |= strcmp(owner.long_name, o.long_name); strncpy(owner.long_name, o.long_name, sizeof(owner.long_name)); } - if (*o.short_name) - { + if (*o.short_name) { changed |= strcmp(owner.short_name, o.short_name); strncpy(owner.short_name, o.short_name, sizeof(owner.short_name)); } - if (*o.id) - { + if (*o.id) { changed |= strcmp(owner.id, o.id); strncpy(owner.id, o.id, sizeof(owner.id)); } - if (owner.is_licensed != o.is_licensed) - { + if (owner.is_licensed != o.is_licensed) { changed = 1; licensed_changed = true; owner.is_licensed = o.is_licensed; config.lora.override_duty_cycle = owner.is_licensed; // override duty cycle for licensed operators } - if (changed) - { // If nothing really changed, don't broadcast on the network or write to flash + if (changed) { // If nothing really changed, don't broadcast on the network or write to flash service.reloadOwner(!hasOpenEditTransaction); licensed_changed ? saveChanges(SEGMENT_CONFIG | SEGMENT_DEVICESTATE) : saveChanges(SEGMENT_DEVICESTATE); } @@ -257,8 +240,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) auto existingRole = config.device.role; bool isRegionUnset = (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET); - switch (c.which_payload_variant) - { + switch (c.which_payload_variant) { case meshtastic_Config_device_tag: LOG_INFO("Setting config: Device\n"); config.has_device = true; @@ -293,8 +275,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) LOG_INFO("Setting config: LoRa\n"); config.has_lora = true; config.lora = c.payload_variant.lora; - if (isRegionUnset && config.lora.region > meshtastic_Config_LoRaConfig_RegionCode_UNSET) - { + if (isRegionUnset && config.lora.region > meshtastic_Config_LoRaConfig_RegionCode_UNSET) { config.lora.tx_enabled = true; } break; @@ -310,8 +291,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) void AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c) { - switch (c.which_payload_variant) - { + switch (c.which_payload_variant) { case meshtastic_ModuleConfig_mqtt_tag: LOG_INFO("Setting module config: MQTT\n"); moduleConfig.has_mqtt = true; @@ -375,8 +355,7 @@ void AdminModule::handleSetChannel(const meshtastic_Channel &cc) void AdminModule::handleGetOwner(const meshtastic_MeshPacket &req) { - if (req.decoded.want_response) - { + if (req.decoded.want_response) { // We create the reply here meshtastic_AdminMessage res = meshtastic_AdminMessage_init_default; res.get_owner_response = owner; @@ -390,10 +369,8 @@ void AdminModule::handleGetConfig(const meshtastic_MeshPacket &req, const uint32 { meshtastic_AdminMessage res = meshtastic_AdminMessage_init_default; - if (req.decoded.want_response) - { - switch (configType) - { + if (req.decoded.want_response) { + switch (configType) { case meshtastic_AdminMessage_ConfigType_DEVICE_CONFIG: LOG_INFO("Getting config: Device\n"); res.get_config_response.which_payload_variant = meshtastic_Config_device_tag; @@ -448,10 +425,8 @@ void AdminModule::handleGetModuleConfig(const meshtastic_MeshPacket &req, const { meshtastic_AdminMessage res = meshtastic_AdminMessage_init_default; - if (req.decoded.want_response) - { - switch (configType) - { + if (req.decoded.want_response) { + switch (configType) { case meshtastic_AdminMessage_ModuleConfigType_MQTT_CONFIG: LOG_INFO("Getting module config: MQTT\n"); res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_mqtt_tag; @@ -541,8 +516,7 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r conn.has_wifi = true; conn.wifi.status.status.is_connected = WiFi.status() != WL_CONNECTED; strncpy(conn.wifi.ssid, config.network.wifi_ssid, 33); - if (conn.wifi.status.status.is_connected) - { + if (conn.wifi.status.status.is_connected) { conn.wifi.rssi = WiFi.RSSI(); conn.wifi.status.status.ip_address = WiFi.localIP(); conn.wifi.status.status.is_mqtt_connected = mqtt && mqtt->connected(); @@ -552,18 +526,27 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r #if HAS_ETHERNET conn.has_ethernet = true; - // conn.ethernet. + if (Ethernet.linkStatus() == LinkON) { + conn.conn.ethernet.status.is_connected = true; + conn.conn.ethernet.status.ip_address = Ethernet.localIP(); + conn.wifi.status.status.is_mqtt_connected = mqtt && mqtt->connected(); + conn.wifi.status.status.is_syslog_connected = false; // FIXME wire this up + } + conn.conn.ethernet.status.is_connected = false; #endif #if HAS_BLUETOOTH conn.has_bluetooth = HAS_BLUETOOTH; - // nimbleBluetooth-> - // #if ARCH_ESP32 - - // #elif + conn.bluetooth.pin = config.bluetooth.fixed_pin; +#endif +#ifdef ARCH_ESP32 + conn.bluetooth.is_connected = nimbleBluetooth->isConnected(); +#elif defined(ARCH_NRF52) + conn.bluetooth.is_connected = nrf52Bluetooth->isConnected(); #endif - conn.has_serial = true; // No serial-less devices + conn.serial.is_connected = powerFSM.getState() == &stateSERIAL; + conn.serial.baud = SERIAL_BAUD; r.get_device_connection_status_response = conn; r.which_payload_variant = meshtastic_AdminMessage_get_device_connection_status_response_tag; @@ -572,8 +555,7 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r void AdminModule::handleGetChannel(const meshtastic_MeshPacket &req, uint32_t channelIndex) { - if (req.decoded.want_response) - { + if (req.decoded.want_response) { // We create the reply here meshtastic_AdminMessage r = meshtastic_AdminMessage_init_default; r.get_channel_response = channels.getByIndex(channelIndex); @@ -591,17 +573,13 @@ void AdminModule::reboot(int32_t seconds) void AdminModule::saveChanges(int saveWhat, bool shouldReboot) { - if (!hasOpenEditTransaction) - { + if (!hasOpenEditTransaction) { LOG_INFO("Saving changes to disk\n"); service.reloadConfig(saveWhat); // Calls saveToDisk among other things - } - else - { + } else { LOG_INFO("Delaying save of changes to disk until the open transaction is committed\n"); } - if (shouldReboot) - { + if (shouldReboot) { reboot(DEFAULT_REBOOT_SECONDS); } } diff --git a/src/modules/AdminModule.h b/src/modules/AdminModule.h index 6924bfd7e..76f6f8033 100644 --- a/src/modules/AdminModule.h +++ b/src/modules/AdminModule.h @@ -9,41 +9,41 @@ */ class AdminModule : public ProtobufModule { -public: - /** Constructor - * name is for debugging output - */ - AdminModule(); + public: + /** Constructor + * name is for debugging output + */ + AdminModule(); -protected: - /** Called to handle a particular incoming message + protected: + /** Called to handle a particular incoming message - @return true if you've guaranteed you've handled this message and no other handlers should be considered for it - */ - virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *p) override; + @return true if you've guaranteed you've handled this message and no other handlers should be considered for it + */ + virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *p) override; -private: - bool hasOpenEditTransaction = false; + private: + bool hasOpenEditTransaction = false; - void saveChanges(int saveWhat, bool shouldReboot = true); - /** - * Getters - */ - void handleGetOwner(const meshtastic_MeshPacket &req); - void handleGetConfig(const meshtastic_MeshPacket &req, uint32_t configType); - void handleGetModuleConfig(const meshtastic_MeshPacket &req, uint32_t configType); - void handleGetChannel(const meshtastic_MeshPacket &req, uint32_t channelIndex); - void handleGetDeviceMetadata(const meshtastic_MeshPacket &req); - void handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &req); - /** - * Setters - */ - void handleSetOwner(const meshtastic_User &o); - void handleSetChannel(const meshtastic_Channel &cc); - void handleSetConfig(const meshtastic_Config &c); - void handleSetModuleConfig(const meshtastic_ModuleConfig &c); - void handleSetChannel(); - void reboot(int32_t seconds); + void saveChanges(int saveWhat, bool shouldReboot = true); + /** + * Getters + */ + void handleGetOwner(const meshtastic_MeshPacket &req); + void handleGetConfig(const meshtastic_MeshPacket &req, uint32_t configType); + void handleGetModuleConfig(const meshtastic_MeshPacket &req, uint32_t configType); + void handleGetChannel(const meshtastic_MeshPacket &req, uint32_t channelIndex); + void handleGetDeviceMetadata(const meshtastic_MeshPacket &req); + void handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &req); + /** + * Setters + */ + void handleSetOwner(const meshtastic_User &o); + void handleSetChannel(const meshtastic_Channel &cc); + void handleSetConfig(const meshtastic_Config &c); + void handleSetModuleConfig(const meshtastic_ModuleConfig &c); + void handleSetChannel(); + void reboot(int32_t seconds); }; extern AdminModule *adminModule; diff --git a/src/nimble/NimbleBluetooth.cpp b/src/nimble/NimbleBluetooth.cpp index 79be9a35b..1f06b25f2 100644 --- a/src/nimble/NimbleBluetooth.cpp +++ b/src/nimble/NimbleBluetooth.cpp @@ -93,7 +93,6 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks passkeyShowing = false; screen->stopBluetoothPinScreen(); } - // bluetoothPhoneAPI->setInitialState(); } virtual void onDisconnect(NimBLEServer *pServer, ble_gap_conn_desc *desc) { LOG_INFO("BLE disconnect\n"); } @@ -117,6 +116,21 @@ bool NimbleBluetooth::isActive() return bleServer; } +bool NimbleBluetooth::isConnected() +{ + return bleServer->getConnectedCount() > 0; +} + +int NimbleBluetooth::getRssi() +{ + if (bleServer && isConnected()) { + auto service = bleServer->getServiceByUUID(MESH_SERVICE_UUID); + uint16_t handle = service->getHandle(); + return NimBLEDevice::getClientByID(handle)->getRssi(); + } + return 0; // FIXME figure out where to source this +} + void NimbleBluetooth::setup() { // Uncomment for testing @@ -135,7 +149,6 @@ void NimbleBluetooth::setup() NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback(); bleServer->setCallbacks(serverCallbacks, true); - setupService(); startAdvertising(); } diff --git a/src/nimble/NimbleBluetooth.h b/src/nimble/NimbleBluetooth.h index ec0fe0841..4080a7cbc 100644 --- a/src/nimble/NimbleBluetooth.h +++ b/src/nimble/NimbleBluetooth.h @@ -1,12 +1,15 @@ #pragma once +#include "BluetoothCommon.h" -class NimbleBluetooth +class NimbleBluetooth : BluetoothApi { public: void setup(); void shutdown(); void clearBonds(); bool isActive(); + bool isConnected(); + int getRssi(); private: void setupService(); diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp index ca757a6e5..f6bd4f50e 100644 --- a/src/platform/esp32/main-esp32.cpp +++ b/src/platform/esp32/main-esp32.cpp @@ -19,11 +19,9 @@ #include #if !defined(CONFIG_IDF_TARGET_ESP32S2) -NimbleBluetooth *nimbleBluetooth; void setBluetoothEnable(bool on) { - if (!isWifiAvailable() && config.bluetooth.enabled == true) { if (!nimbleBluetooth) { nimbleBluetooth = new NimbleBluetooth(); diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index 06b07f593..044b57ae6 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -213,6 +213,16 @@ void NRF52Bluetooth::shutdown() Bluefruit.Advertising.stop(); } +bool NRF52Bluetooth::isConnected() +{ + return Bluefruit.connected(connectionHandle); +} + +int NRF52Bluetooth::getRssi() +{ + return 0; // FIXME figure out where to source this +} + void NRF52Bluetooth::setup() { // Initialise the Bluefruit module diff --git a/src/platform/nrf52/NRF52Bluetooth.h b/src/platform/nrf52/NRF52Bluetooth.h index b4438ff39..193e86cf8 100644 --- a/src/platform/nrf52/NRF52Bluetooth.h +++ b/src/platform/nrf52/NRF52Bluetooth.h @@ -1,13 +1,16 @@ #pragma once +#include "BluetoothCommon.h" #include -class NRF52Bluetooth +class NRF52Bluetooth : BluetoothApi { public: void setup(); void shutdown(); void clearBonds(); + bool isConnected(); + int getRssi(); private: static void onConnectionSecured(uint16_t conn_handle); diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp index 0384073a4..1cbe05631 100644 --- a/src/platform/nrf52/main-nrf52.cpp +++ b/src/platform/nrf52/main-nrf52.cpp @@ -8,9 +8,8 @@ #include // #include #include "NodeDB.h" - -#include "NRF52Bluetooth.h" #include "error.h" +#include "main.h" #ifdef BQ25703A_ADDR #include "BQ25713.h" @@ -63,8 +62,6 @@ static void initBrownout() // We don't bother with setting up brownout if soft device is disabled - because during production we always use softdevice } -NRF52Bluetooth *nrf52Bluetooth; - static bool bleOn = false; static const bool useSoftDevice = true; // Set to false for easier debugging