diff --git a/.vscode/settings.json b/.vscode/settings.json index 21bf462ee..fa125784a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -52,7 +52,8 @@ "shared_mutex": "cpp", "iostream": "cpp", "esp_nimble_hci.h": "c", - "map": "cpp" + "map": "cpp", + "random": "cpp" }, "cSpell.words": [ "Blox", diff --git a/bin/device-install.bat b/bin/device-install.bat old mode 100644 new mode 100755 diff --git a/bin/device-update.bat b/bin/device-update.bat old mode 100644 new mode 100755 diff --git a/platformio.ini b/platformio.ini index 7cb590823..20b319cdb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -21,6 +21,7 @@ default_envs = tbeam ;default_envs = meshtastic-diy-v1 ;default_envs = meshtastic-diy-v1.1 ;default_envs = m5stack-coreink +;default_envs = rak4631 extra_configs = variants/*/platformio.ini @@ -79,8 +80,6 @@ lib_deps = lib_deps = adafruit/Adafruit BusIO@^1.11.4 adafruit/Adafruit Unified Sensor@^1.1.4 - paulstoffregen/OneWire@^2.3.5 - robtillaart/DS18B20@^0.1.11 adafruit/Adafruit BMP280 Library@^2.6.3 adafruit/Adafruit BME280 Library@^2.2.2 adafruit/Adafruit BME680 Library@^2.0.1 @@ -99,11 +98,10 @@ debug_init_break = tbreak setup # Remove -DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL for low level BLE logging. # See library directory for BLE logging possible values: .pio/libdeps/tbeam/NimBLE-Arduino/src/log_common/log_common.h # This overrides the BLE logging default of LOG_LEVEL_INFO (1) from: .pio/libdeps/tbeam/NimBLE-Arduino/src/esp_nimble_cfg.h -# -DUSE_NEW_ESP32_BLUETOOTH will enable the new NimBLE C++ api build_flags = ${arduino_base.build_flags} -Wall -Wextra -Isrc/platform/esp32 -lnimble -std=c++11 -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG -DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL - -DAXP_DEBUG_PORT=Serial -DUSE_NEW_ESP32_BLUETOOTH -DCONFIG_BT_NIMBLE_ENABLED -DCONFIG_NIMBLE_CPP_LOG_LEVEL=1 -DCONFIG_BT_NIMBLE_MAX_CCCDS=20 + -DAXP_DEBUG_PORT=Serial -DCONFIG_BT_NIMBLE_ENABLED -DCONFIG_NIMBLE_CPP_LOG_LEVEL=2 -DCONFIG_BT_NIMBLE_MAX_CCCDS=20 lib_deps = ${arduino_base.lib_deps} ${networking_base.lib_deps} diff --git a/protobufs b/protobufs index 2fadf011e..579308947 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 2fadf011e574c180afb395bc2a751b3f7e56839c +Subproject commit 579308947366b35f7eb6908d5eaabd0114bba244 diff --git a/src/ButtonThread.h b/src/ButtonThread.h index 58c5d525f..ed9fc613c 100644 --- a/src/ButtonThread.h +++ b/src/ButtonThread.h @@ -7,10 +7,6 @@ #include "power.h" #include -#ifdef ARCH_ESP32 -#include "nimble/BluetoothUtil.h" -#endif - namespace concurrency { /** diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index c6444adee..f2a7d473d 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -240,6 +240,7 @@ Fsm powerFSM(&stateBOOT); void PowerFSM_setup() { bool isRouter = (config.device.role == Config_DeviceConfig_Role_Router ? 1 : 0); + uint32_t screenOnSecs = config.display.screen_on_secs ? config.display.screen_on_secs : default_screen_on_secs; bool hasPower = isPowered(); DEBUG_MSG("PowerFSM init, USB power=%d\n", hasPower); @@ -251,8 +252,7 @@ void PowerFSM_setup() // We need this transition, because we might not transition if we were waiting to enter light-sleep, because when we wake from // light sleep we _always_ transition to NB or dark and - powerFSM.add_transition(&stateLS, isRouter ? &stateNB : &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, - "Received packet, exiting light sleep"); + powerFSM.add_transition(&stateLS, isRouter ? &stateNB : &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, "Received packet, exiting light sleep"); powerFSM.add_transition(&stateNB, &stateNB, EVENT_PACKET_FOR_PHONE, NULL, "Received packet, resetting win wake"); // Handle press events - note: we ignore button presses when in API mode @@ -261,8 +261,7 @@ void PowerFSM_setup() powerFSM.add_transition(&stateDARK, &stateON, EVENT_PRESS, NULL, "Press"); powerFSM.add_transition(&statePOWER, &statePOWER, EVENT_PRESS, screenPress, "Press"); powerFSM.add_transition(&stateON, &stateON, EVENT_PRESS, screenPress, "Press"); // reenter On to restart our timers - powerFSM.add_transition(&stateSERIAL, &stateSERIAL, EVENT_PRESS, screenPress, - "Press"); // Allow button to work while in serial API + powerFSM.add_transition(&stateSERIAL, &stateSERIAL, EVENT_PRESS, screenPress, "Press"); // Allow button to work while in serial API // Handle critically low power battery by forcing deep sleep powerFSM.add_transition(&stateBOOT, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); @@ -333,10 +332,7 @@ void PowerFSM_setup() powerFSM.add_transition(&stateDARK, &stateON, EVENT_FIRMWARE_UPDATE, NULL, "Got firmware update"); powerFSM.add_transition(&stateON, &stateON, EVENT_FIRMWARE_UPDATE, NULL, "Got firmware update"); - powerFSM.add_timed_transition(&stateON, &stateDARK, - config.display.screen_on_secs ? config.display.screen_on_secs - : 60 * 1000 * 10, - NULL, "Screen-on timeout"); + powerFSM.add_timed_transition(&stateON, &stateDARK, screenOnSecs, NULL, "Screen-on timeout"); // On most boards we use light-sleep to be our main state, but on NRF52 we just stay in DARK State *lowPowerState = &stateLS; @@ -348,17 +344,12 @@ void PowerFSM_setup() // See: https://github.com/meshtastic/Meshtastic-device/issues/1071 if (isRouter || config.power.is_power_saving) { - powerFSM.add_timed_transition(&stateNB, &stateLS, - config.power.min_wake_secs ? config.power.min_wake_secs - : default_min_wake_secs * 1000, - NULL, "Min wake timeout"); - powerFSM.add_timed_transition(&stateDARK, &stateLS, - config.power.wait_bluetooth_secs - ? config.power.wait_bluetooth_secs - : default_wait_bluetooth_secs * 1000, - NULL, "Bluetooth timeout"); - meshSds = config.power.mesh_sds_timeout_secs ? config.power.mesh_sds_timeout_secs - : default_mesh_sds_timeout_secs; + uint32_t minWakeSecs = config.power.min_wake_secs ? config.power.min_wake_secs : default_min_wake_secs * 1000; + uint32_t waitBluetoothSecs = config.power.wait_bluetooth_secs ? config.power.wait_bluetooth_secs : default_wait_bluetooth_secs * 1000; + + powerFSM.add_timed_transition(&stateNB, &stateLS, minWakeSecs, NULL, "Min wake timeout"); + powerFSM.add_timed_transition(&stateDARK, &stateLS, waitBluetoothSecs, NULL, "Bluetooth timeout"); + meshSds = config.power.mesh_sds_timeout_secs ? config.power.mesh_sds_timeout_secs : default_mesh_sds_timeout_secs; } else { diff --git a/src/commands.h b/src/commands.h index 13ddc7284..b52ed5db4 100644 --- a/src/commands.h +++ b/src/commands.h @@ -14,4 +14,5 @@ enum class Cmd { STOP_BOOT_SCREEN, PRINT, START_SHUTDOWN_SCREEN, + START_REBOOT_SCREEN, }; \ No newline at end of file diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index beb8a3c97..22bd2acfb 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -228,26 +228,17 @@ static void drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16 // Used when booting without a region set static void drawWelcomeScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { - + display->setFont(FONT_SMALL); + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->drawString(64 + x, y, "//\\ E S H T /\\ S T / C"); + display->drawString(64 + x, y + FONT_HEIGHT_SMALL, getDeviceName()); + display->setTextAlignment(TEXT_ALIGN_LEFT); + if ((millis() / 10000) % 2) { - display->setFont(FONT_SMALL); - - display->setTextAlignment(TEXT_ALIGN_CENTER); - display->drawString(64 + x, y, "//\\ E S H T /\\ S T / C"); - - display->setTextAlignment(TEXT_ALIGN_LEFT); - display->drawString(x, y + FONT_HEIGHT_SMALL * 2 - 3, "Set the region using the"); display->drawString(x, y + FONT_HEIGHT_SMALL * 3 - 3, "Meshtastic Android, iOS,"); display->drawString(x, y + FONT_HEIGHT_SMALL * 4 - 3, "Flasher or CLI client."); } else { - display->setFont(FONT_SMALL); - - display->setTextAlignment(TEXT_ALIGN_CENTER); - display->drawString(64 + x, y, "//\\ E S H T /\\ S T / C"); - - display->setTextAlignment(TEXT_ALIGN_LEFT); - display->drawString(x, y + FONT_HEIGHT_SMALL * 2 - 3, "Visit meshtastic.org"); display->drawString(x, y + FONT_HEIGHT_SMALL * 3 - 3, "for more information."); display->drawString(x, y + FONT_HEIGHT_SMALL * 4 - 3, ""); @@ -299,8 +290,13 @@ static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Enter this code"); display->setFont(FONT_LARGE); - display->drawString(64 + x, 26 + y, btPIN); + auto displayPin = new String(btPIN); + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->drawString(12 + x, 26 + y, displayPin->substring(0, 3)); + display->drawString(72 + x, 26 + y, displayPin->substring(3, 6)); + + display->setTextAlignment(TEXT_ALIGN_CENTER); display->setFont(FONT_SMALL); char buf[30]; const char *name = "Name: "; @@ -317,6 +313,14 @@ static void drawFrameShutdown(OLEDDisplay *display, OLEDDisplayUiState *state, i display->drawString(64 + x, 26 + y, "Shutting down..."); } +static void drawFrameReboot(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + display->setTextAlignment(TEXT_ALIGN_CENTER); + + display->setFont(FONT_MEDIUM); + display->drawString(64 + x, 26 + y, "Rebooting..."); +} + static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { display->setTextAlignment(TEXT_ALIGN_CENTER); @@ -329,9 +333,6 @@ static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, i } else { display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Please wait . . "); } - - // display->setFont(FONT_LARGE); - // display->drawString(64 + x, 26 + y, btPIN); } /// Draw the last text message we received @@ -1042,6 +1043,9 @@ int32_t Screen::runOnce() case Cmd::START_SHUTDOWN_SCREEN: handleShutdownScreen(); break; + case Cmd::START_REBOOT_SCREEN: + handleRebootScreen(); + break; default: DEBUG_MSG("BUG: invalid cmd\n"); } @@ -1234,6 +1238,18 @@ void Screen::handleShutdownScreen() setFastFramerate(); } +void Screen::handleRebootScreen() +{ + DEBUG_MSG("showing reboot screen\n"); + showingNormalScreen = false; + + static FrameCallback rebootFrames[] = {drawFrameReboot}; + + ui.disableAllIndicators(); + ui.setFrames(rebootFrames, 1); + setFastFramerate(); +} + void Screen::handleStartFirmwareUpdateScreen() { DEBUG_MSG("showing firmware screen\n"); diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index b8d3f6f58..18e2285e0 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -20,6 +20,7 @@ class Screen void forceDisplay() {} void startBluetoothPinScreen(uint32_t pin) {} void stopBluetoothPinScreen() {} + void startRebootScreen() {} }; } @@ -167,6 +168,13 @@ class Screen : public concurrency::OSThread enqueueCmd(cmd); } + void startRebootScreen() + { + ScreenCmd cmd; + cmd.cmd = Cmd::START_REBOOT_SCREEN; + enqueueCmd(cmd); + } + /// Stops showing the bluetooth PIN screen. void stopBluetoothPinScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BLUETOOTH_PIN_SCREEN}); } @@ -280,6 +288,7 @@ class Screen : public concurrency::OSThread void handlePrint(const char *text); void handleStartFirmwareUpdateScreen(); void handleShutdownScreen(); + void handleRebootScreen(); /// Rebuilds our list of frames (screens) to default ones. void setFrames(); diff --git a/src/main.cpp b/src/main.cpp index 6ed406d13..ece1fc868 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -33,13 +33,7 @@ #ifdef ARCH_ESP32 #include "mesh/http/WebServer.h" - -#ifdef USE_NEW_ESP32_BLUETOOTH -#include "platform/esp32/ESP32Bluetooth.h" -#else -#include "nimble/BluetoothUtil.h" -#endif - +#include "nimble/NimbleBluetooth.h" #endif #if HAS_WIFI diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 374fc609c..dafceafe0 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -16,6 +16,10 @@ #include "modules/PositionModule.h" #include "power.h" +#ifdef ARCH_ESP32 +#include "nimble/NimbleBluetooth.h" +#endif + /* receivedPacketQueue - this is a queue of messages we've received from the mesh, which we are keeping to deliver to the phone. It is implemented with a FreeRTos queue (wrapped with a little RTQueue class) of pointers to MeshPacket protobufs (which were diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 952b684aa..f9c795776 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -179,12 +179,11 @@ extern NodeDB nodeDB; #define default_sds_secs 365 * 24 * 60 * 60 #define default_ls_secs IF_ROUTER(24 * 60 * 60, 5 * 60) #define default_min_wake_secs 10 - +#define default_screen_on_secs 60 * 1000 * 10 inline uint32_t getIntervalOrDefaultMs(uint32_t interval) { - if (interval > 0) - return interval * 1000; + if (interval > 0) return interval * 1000; return default_broadcast_interval_secs * 1000; } diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index 26030ec7a..9a0066bad 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -116,13 +116,11 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) */ size_t PhoneAPI::getFromRadio(uint8_t *buf) { + DEBUG_MSG("getFromRadio, state=%d\n", state); if (!available()) { - // DEBUG_MSG("getFromRadio, !available\n"); + // DEBUG_MSG("PhoneAPI::getFromRadio, !available\n"); return 0; } - - DEBUG_MSG("getFromRadio, state=%d\n", state); - // In case we send a FromRadio packet memset(&fromRadioScratch, 0, sizeof(fromRadioScratch)); @@ -278,7 +276,10 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) return 0; } -void PhoneAPI::handleDisconnect() {} +void PhoneAPI::handleDisconnect() +{ + DEBUG_MSG("PhoneAPI disconnect\n"); +} void PhoneAPI::releasePhonePacket() { @@ -294,35 +295,28 @@ void PhoneAPI::releasePhonePacket() bool PhoneAPI::available() { switch (state) { - case STATE_SEND_NOTHING: - return false; - - case STATE_SEND_MY_INFO: - return true; - - case STATE_SEND_CONFIG: - return true; - - case STATE_SEND_MODULECONFIG: - return true; - - case STATE_SEND_NODEINFO: - if (!nodeInfoForPhone) - nodeInfoForPhone = nodeDB.readNextInfo(); - return true; // Always say we have something, because we might need to advance our state machine - - case STATE_SEND_COMPLETE_ID: - return true; - - case STATE_SEND_PACKETS: { - // Try to pull a new packet from the service (if we haven't already) - if (!packetForPhone) - packetForPhone = service.getForPhone(); - bool hasPacket = !!packetForPhone; - // DEBUG_MSG("available hasPacket=%d\n", hasPacket); - return hasPacket; + case STATE_SEND_NOTHING: + return false; + case STATE_SEND_MY_INFO: + return true; + case STATE_SEND_CONFIG: + return true; + case STATE_SEND_MODULECONFIG: + return true; + case STATE_SEND_NODEINFO: + if (!nodeInfoForPhone) + nodeInfoForPhone = nodeDB.readNextInfo(); + return true; // Always say we have something, because we might need to advance our state machine + case STATE_SEND_COMPLETE_ID: + return true; + case STATE_SEND_PACKETS: { + // Try to pull a new packet from the service (if we haven't already) + if (!packetForPhone) + packetForPhone = service.getForPhone(); + bool hasPacket = !!packetForPhone; + // DEBUG_MSG("available hasPacket=%d\n", hasPacket); + return hasPacket; } - default: assert(0); // unexpected state - FIXME, make an error code and reboot } diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h index 649217243..fb372bde1 100644 --- a/src/mesh/PhoneAPI.h +++ b/src/mesh/PhoneAPI.h @@ -59,7 +59,7 @@ class PhoneAPI // Call this when the client drops the connection, resets the state to STATE_SEND_NOTHING // Unregisters our observer. A closed connection **can** be reopened by calling init again. virtual void close(); - + /** * Handle a ToRadio protobuf * @return true true if a packet was queued for sending (so that caller can yield) @@ -81,6 +81,8 @@ class PhoneAPI bool isConnected() { return state != STATE_SEND_NOTHING; } + void setInitialState() { state = STATE_SEND_MY_INFO; } + /// emit a debugging log character, FIXME - implement void debugOut(char c) { } diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index 510782837..4936fd282 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -112,7 +112,7 @@ void ReliableRouter::sniffReceived(const MeshPacket *p, const Routing *c) // We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records if (ackId || nakId) { if (ackId) { - DEBUG_MSG("Received a ack for 0x%x, stopping retransmissions\n", ackId); + DEBUG_MSG("Received an ack for 0x%x, stopping retransmissions\n", ackId); stopRetransmission(p->to, ackId); } else { DEBUG_MSG("Received a nak for 0x%x, stopping retransmissions\n", nakId); diff --git a/src/mesh/generated/deviceonly.pb.h b/src/mesh/generated/deviceonly.pb.h index 228edbb00..a940de2f6 100644 --- a/src/mesh/generated/deviceonly.pb.h +++ b/src/mesh/generated/deviceonly.pb.h @@ -165,7 +165,7 @@ extern const pb_msgdesc_t OEMStore_msg; /* Maximum encoded size of messages (where known) */ #define ChannelFile_size 630 -#define DeviceState_size 22536 +#define DeviceState_size 22218 #define OEMStore_size 2106 #ifdef __cplusplus diff --git a/src/mesh/generated/mesh.pb.c b/src/mesh/generated/mesh.pb.c index 14c24f662..2d46c960c 100644 --- a/src/mesh/generated/mesh.pb.c +++ b/src/mesh/generated/mesh.pb.c @@ -21,7 +21,7 @@ PB_BIND(Routing, Routing, AUTO) PB_BIND(Data, Data, 2) -PB_BIND(Location, Location, AUTO) +PB_BIND(Waypoint, Waypoint, AUTO) PB_BIND(MeshPacket, MeshPacket, 2) diff --git a/src/mesh/generated/mesh.pb.h b/src/mesh/generated/mesh.pb.h index ab7a3dd83..c00504c48 100644 --- a/src/mesh/generated/mesh.pb.h +++ b/src/mesh/generated/mesh.pb.h @@ -245,24 +245,38 @@ typedef struct _Compressed { Compressed_data_t data; } Compressed; -/* Location of a waypoint to associate with a message */ -typedef struct _Location { - /* Id of the location */ - uint32_t id; - /* latitude_i */ - int32_t latitude_i; - /* longitude_i */ - int32_t longitude_i; - /* Time the location is to expire (epoch) */ - uint32_t expire; - /* If true, only allow the original sender to update the location. */ - bool locked; - /* Name of the location - max 30 chars */ - char name[30]; - /* * - Description of the location - max 100 chars */ - char description[100]; -} Location; +typedef PB_BYTES_ARRAY_T(237) Data_payload_t; +/* (Formerly called SubPacket) + The payload portion fo a packet, this is the actual bytes that are sent + inside a radio packet (because from/to are broken out by the comms library) */ +typedef struct _Data { + /* Formerly named typ and of type Type */ + PortNum portnum; + /* TODO: REPLACE */ + Data_payload_t payload; + /* Not normally used, but for testing a sender can request that recipient + responds in kind (i.e. if it received a position, it should unicast back it's position). + Note: that if you set this on a broadcast you will receive many replies. */ + bool want_response; + /* The address of the destination node. + This field is is filled in by the mesh radio device software, application + layer software should never need it. + RouteDiscovery messages _must_ populate this. + Other message types might need to if they are doing multihop routing. */ + uint32_t dest; + /* The address of the original sender for this message. + This field should _only_ be populated for reliable multihop packets (to keep + packets small). */ + uint32_t source; + /* Only used in routing or response messages. + Indicates the original message ID that this message is reporting failure on. (formerly called original_id) */ + uint32_t request_id; + /* If set, this message is intened to be a reply to a previously sent message with the defined id. */ + uint32_t reply_id; + /* Defaults to false. If true, then what is in the payload should be treated as an emoji like giving + a message a heart or poop emoji. */ + uint32_t emoji; +} Data; /* Debug output from the device. To minimize the size of records inside the device code, if a time/source/level is not set @@ -461,91 +475,24 @@ typedef struct _User { bool is_licensed; } User; -typedef PB_BYTES_ARRAY_T(237) Data_payload_t; -/* (Formerly called SubPacket) - The payload portion fo a packet, this is the actual bytes that are sent - inside a radio packet (because from/to are broken out by the comms library) */ -typedef struct _Data { - /* Formerly named typ and of type Type */ - PortNum portnum; - /* TODO: REPLACE */ - Data_payload_t payload; - /* Not normally used, but for testing a sender can request that recipient - responds in kind (i.e. if it received a position, it should unicast back it's position). - Note: that if you set this on a broadcast you will receive many replies. */ - bool want_response; - /* The address of the destination node. - This field is is filled in by the mesh radio device software, application - layer software should never need it. - RouteDiscovery messages _must_ populate this. - Other message types might need to if they are doing multihop routing. */ - uint32_t dest; - /* The address of the original sender for this message. - This field should _only_ be populated for reliable multihop packets (to keep - packets small). */ - uint32_t source; - /* Only used in routing or response messages. - Indicates the original message ID that this message is reporting failure on. (formerly called original_id) */ - uint32_t request_id; - /* If set, this message is intened to be a reply to a previously sent message with the defined id. */ - uint32_t reply_id; - /* Defaults to false. If true, then what is in the payload should be treated as an emoji like giving - a message a heart or poop emoji. */ - uint32_t emoji; - /* Location structure */ - bool has_location; - Location location; -} Data; - -/* The bluetooth to device link: - Old BTLE protocol docs from TODO, merge in above and make real docs... - use protocol buffers, and NanoPB - messages from device to phone: - POSITION_UPDATE (..., time) - TEXT_RECEIVED(from, text, time) - OPAQUE_RECEIVED(from, payload, time) (for signal messages or other applications) - messages from phone to device: - SET_MYID(id, human readable long, human readable short) (send down the unique ID - string used for this node, a human readable string shown for that id, and a very - short human readable string suitable for oled screen) SEND_OPAQUE(dest, payload) - (for signal messages or other applications) SEND_TEXT(dest, text) Get all - nodes() (returns list of nodes, with full info, last time seen, loc, battery - level etc) SET_CONFIG (switches device to a new set of radio params and - preshared key, drops all existing nodes, force our node to rejoin this new group) - Full information about a node on the mesh */ -typedef struct _NodeInfo { - /* The node number */ - uint32_t num; - /* The user info for this node */ - bool has_user; - User user; - /* This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true. - Position.time now indicates the last time we received a POSITION from that node. */ - bool has_position; - Position position; - /* Returns the Signal-to-noise ratio (SNR) of the last received message, - as measured by the receiver. Return SNR of the last received message in dB */ - float snr; - /* Set to indicate the last time we received a packet from this node */ - uint32_t last_heard; - /* The latest device metrics for the node. */ - bool has_device_metrics; - DeviceMetrics device_metrics; -} NodeInfo; - -/* A Routing control Data packet handled by the routing module */ -typedef struct _Routing { - pb_size_t which_variant; - union { - /* A route request going from the requester */ - RouteDiscovery route_request; - /* A route reply */ - RouteDiscovery route_reply; - /* A failure in delivering a message (usually used for routing control messages, but might be provided - in addition to ack.fail_id to provide details on the type of failure). */ - Routing_Error error_reason; - }; -} Routing; +/* Waypoint message, used to share arbitrary locations across the mesh */ +typedef struct _Waypoint { + /* Id of the waypoint */ + uint32_t id; + /* latitude_i */ + int32_t latitude_i; + /* longitude_i */ + int32_t longitude_i; + /* Time the waypoint is to expire (epoch) */ + uint32_t expire; + /* If true, only allow the original sender to update the waypoint. */ + bool locked; + /* Name of the waypoint - max 30 chars */ + char name[30]; + /* * + Description of the waypoint - max 100 chars */ + char description[100]; +} Waypoint; typedef PB_BYTES_ARRAY_T(256) MeshPacket_encrypted_t; /* A packet envelope sent/received over the mesh @@ -619,6 +566,56 @@ typedef struct _MeshPacket { MeshPacket_Delayed delayed; } MeshPacket; +/* The bluetooth to device link: + Old BTLE protocol docs from TODO, merge in above and make real docs... + use protocol buffers, and NanoPB + messages from device to phone: + POSITION_UPDATE (..., time) + TEXT_RECEIVED(from, text, time) + OPAQUE_RECEIVED(from, payload, time) (for signal messages or other applications) + messages from phone to device: + SET_MYID(id, human readable long, human readable short) (send down the unique ID + string used for this node, a human readable string shown for that id, and a very + short human readable string suitable for oled screen) SEND_OPAQUE(dest, payload) + (for signal messages or other applications) SEND_TEXT(dest, text) Get all + nodes() (returns list of nodes, with full info, last time seen, loc, battery + level etc) SET_CONFIG (switches device to a new set of radio params and + preshared key, drops all existing nodes, force our node to rejoin this new group) + Full information about a node on the mesh */ +typedef struct _NodeInfo { + /* The node number */ + uint32_t num; + /* The user info for this node */ + bool has_user; + User user; + /* This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true. + Position.time now indicates the last time we received a POSITION from that node. */ + bool has_position; + Position position; + /* Returns the Signal-to-noise ratio (SNR) of the last received message, + as measured by the receiver. Return SNR of the last received message in dB */ + float snr; + /* Set to indicate the last time we received a packet from this node */ + uint32_t last_heard; + /* The latest device metrics for the node. */ + bool has_device_metrics; + DeviceMetrics device_metrics; +} NodeInfo; + +/* A Routing control Data packet handled by the routing module */ +typedef struct _Routing { + pb_size_t which_variant; + union { + /* A route request going from the requester */ + RouteDiscovery route_request; + /* A route reply */ + RouteDiscovery route_reply; + /* A failure in delivering a message (usually used for routing control messages, but might be provided + in addition to ack.fail_id to provide details on the type of failure). */ + Routing_Error error_reason; + }; +} Routing; + /* Packets from the radio to the phone will appear on the fromRadio characteristic. It will support READ and NOTIFY. When a new packet arrives the device will BLE notify? It will sit in that descriptor until consumed by the phone, @@ -730,8 +727,8 @@ extern "C" { #define User_init_default {"", "", "", {0}, _HardwareModel_MIN, 0} #define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}} #define Routing_init_default {0, {RouteDiscovery_init_default}} -#define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, Location_init_default} -#define Location_init_default {0, 0, 0, 0, 0, "", ""} +#define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0} +#define Waypoint_init_default {0, 0, 0, 0, 0, "", ""} #define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN} #define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0, false, DeviceMetrics_init_default} #define MyNodeInfo_init_default {0, 0, "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, 0, 0} @@ -744,8 +741,8 @@ extern "C" { #define User_init_zero {"", "", "", {0}, _HardwareModel_MIN, 0} #define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}} #define Routing_init_zero {0, {RouteDiscovery_init_zero}} -#define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, Location_init_zero} -#define Location_init_zero {0, 0, 0, 0, 0, "", ""} +#define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0} +#define Waypoint_init_zero {0, 0, 0, 0, 0, "", ""} #define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN} #define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0, false, DeviceMetrics_init_zero} #define MyNodeInfo_init_zero {0, 0, "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, 0, 0} @@ -758,13 +755,14 @@ extern "C" { /* Field tags (for use in manual encoding/decoding) */ #define Compressed_portnum_tag 1 #define Compressed_data_tag 2 -#define Location_id_tag 1 -#define Location_latitude_i_tag 2 -#define Location_longitude_i_tag 3 -#define Location_expire_tag 4 -#define Location_locked_tag 5 -#define Location_name_tag 6 -#define Location_description_tag 7 +#define Data_portnum_tag 1 +#define Data_payload_tag 2 +#define Data_want_response_tag 3 +#define Data_dest_tag 4 +#define Data_source_tag 5 +#define Data_request_id_tag 6 +#define Data_reply_id_tag 7 +#define Data_emoji_tag 8 #define LogRecord_message_tag 1 #define LogRecord_time_tag 2 #define LogRecord_source_tag 3 @@ -816,24 +814,13 @@ extern "C" { #define User_macaddr_tag 4 #define User_hw_model_tag 6 #define User_is_licensed_tag 7 -#define Data_portnum_tag 1 -#define Data_payload_tag 2 -#define Data_want_response_tag 3 -#define Data_dest_tag 4 -#define Data_source_tag 5 -#define Data_request_id_tag 6 -#define Data_reply_id_tag 7 -#define Data_emoji_tag 8 -#define Data_location_tag 9 -#define NodeInfo_num_tag 1 -#define NodeInfo_user_tag 2 -#define NodeInfo_position_tag 3 -#define NodeInfo_snr_tag 4 -#define NodeInfo_last_heard_tag 5 -#define NodeInfo_device_metrics_tag 6 -#define Routing_route_request_tag 1 -#define Routing_route_reply_tag 2 -#define Routing_error_reason_tag 3 +#define Waypoint_id_tag 1 +#define Waypoint_latitude_i_tag 2 +#define Waypoint_longitude_i_tag 3 +#define Waypoint_expire_tag 4 +#define Waypoint_locked_tag 5 +#define Waypoint_name_tag 6 +#define Waypoint_description_tag 7 #define MeshPacket_from_tag 1 #define MeshPacket_to_tag 2 #define MeshPacket_channel_tag 3 @@ -847,6 +834,15 @@ extern "C" { #define MeshPacket_priority_tag 12 #define MeshPacket_rx_rssi_tag 13 #define MeshPacket_delayed_tag 15 +#define NodeInfo_num_tag 1 +#define NodeInfo_user_tag 2 +#define NodeInfo_position_tag 3 +#define NodeInfo_snr_tag 4 +#define NodeInfo_last_heard_tag 5 +#define NodeInfo_device_metrics_tag 6 +#define Routing_route_request_tag 1 +#define Routing_route_reply_tag 2 +#define Routing_error_reason_tag 3 #define FromRadio_id_tag 1 #define FromRadio_my_info_tag 3 #define FromRadio_node_info_tag 4 @@ -920,13 +916,11 @@ X(a, STATIC, SINGULAR, FIXED32, dest, 4) \ X(a, STATIC, SINGULAR, FIXED32, source, 5) \ X(a, STATIC, SINGULAR, FIXED32, request_id, 6) \ X(a, STATIC, SINGULAR, FIXED32, reply_id, 7) \ -X(a, STATIC, SINGULAR, FIXED32, emoji, 8) \ -X(a, STATIC, OPTIONAL, MESSAGE, location, 9) +X(a, STATIC, SINGULAR, FIXED32, emoji, 8) #define Data_CALLBACK NULL #define Data_DEFAULT NULL -#define Data_location_MSGTYPE Location -#define Location_FIELDLIST(X, a) \ +#define Waypoint_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, id, 1) \ X(a, STATIC, SINGULAR, SFIXED32, latitude_i, 2) \ X(a, STATIC, SINGULAR, SFIXED32, longitude_i, 3) \ @@ -934,8 +928,8 @@ X(a, STATIC, SINGULAR, UINT32, expire, 4) \ X(a, STATIC, SINGULAR, BOOL, locked, 5) \ X(a, STATIC, SINGULAR, STRING, name, 6) \ X(a, STATIC, SINGULAR, STRING, description, 7) -#define Location_CALLBACK NULL -#define Location_DEFAULT NULL +#define Waypoint_CALLBACK NULL +#define Waypoint_DEFAULT NULL #define MeshPacket_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, FIXED32, from, 1) \ @@ -1042,7 +1036,7 @@ extern const pb_msgdesc_t User_msg; extern const pb_msgdesc_t RouteDiscovery_msg; extern const pb_msgdesc_t Routing_msg; extern const pb_msgdesc_t Data_msg; -extern const pb_msgdesc_t Location_msg; +extern const pb_msgdesc_t Waypoint_msg; extern const pb_msgdesc_t MeshPacket_msg; extern const pb_msgdesc_t NodeInfo_msg; extern const pb_msgdesc_t MyNodeInfo_msg; @@ -1058,7 +1052,7 @@ extern const pb_msgdesc_t Compressed_msg; #define RouteDiscovery_fields &RouteDiscovery_msg #define Routing_fields &Routing_msg #define Data_fields &Data_msg -#define Location_fields &Location_msg +#define Waypoint_fields &Waypoint_msg #define MeshPacket_fields &MeshPacket_msg #define NodeInfo_fields &NodeInfo_msg #define MyNodeInfo_fields &MyNodeInfo_msg @@ -1070,19 +1064,19 @@ extern const pb_msgdesc_t Compressed_msg; /* Maximum encoded size of messages (where known) */ #define Compressed_size 243 -#define Data_size 429 -#define FromRadio_size 489 -#define Location_size 156 +#define Data_size 270 +#define FromRadio_size 330 #define LogRecord_size 81 -#define MeshPacket_size 480 +#define MeshPacket_size 321 #define MyNodeInfo_size 197 #define NodeInfo_size 263 #define Position_size 142 #define RouteDiscovery_size 40 #define Routing_size 42 #define ToRadio_PeerInfo_size 8 -#define ToRadio_size 483 +#define ToRadio_size 324 #define User_size 77 +#define Waypoint_size 156 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/module_config.pb.h b/src/mesh/generated/module_config.pb.h index 866acf5c2..5ff22a807 100644 --- a/src/mesh/generated/module_config.pb.h +++ b/src/mesh/generated/module_config.pb.h @@ -72,7 +72,7 @@ typedef struct _ModuleConfig_ExternalNotificationConfig { } ModuleConfig_ExternalNotificationConfig; typedef struct _ModuleConfig_MQTTConfig { - bool disabled; + bool enabled; char address[32]; char username[32]; char password[32]; @@ -187,7 +187,7 @@ extern "C" { #define ModuleConfig_ExternalNotificationConfig_active_tag 4 #define ModuleConfig_ExternalNotificationConfig_alert_message_tag 5 #define ModuleConfig_ExternalNotificationConfig_alert_bell_tag 6 -#define ModuleConfig_MQTTConfig_disabled_tag 1 +#define ModuleConfig_MQTTConfig_enabled_tag 1 #define ModuleConfig_MQTTConfig_address_tag 2 #define ModuleConfig_MQTTConfig_username_tag 3 #define ModuleConfig_MQTTConfig_password_tag 4 @@ -240,7 +240,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,canned_message,payloadVariant #define ModuleConfig_payloadVariant_canned_message_MSGTYPE ModuleConfig_CannedMessageConfig #define ModuleConfig_MQTTConfig_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, BOOL, disabled, 1) \ +X(a, STATIC, SINGULAR, BOOL, enabled, 1) \ X(a, STATIC, SINGULAR, STRING, address, 2) \ X(a, STATIC, SINGULAR, STRING, username, 3) \ X(a, STATIC, SINGULAR, STRING, password, 4) \ diff --git a/src/mesh/generated/portnums.pb.h b/src/mesh/generated/portnums.pb.h index e5830b154..b65ac86f0 100644 --- a/src/mesh/generated/portnums.pb.h +++ b/src/mesh/generated/portnums.pb.h @@ -48,7 +48,8 @@ typedef enum _PortNum { PortNum_ADMIN_APP = 6, /* Compressed TEXT_MESSAGE payloads. */ PortNum_TEXT_MESSAGE_COMPRESSED_APP = 7, - /* Waypoint payloads. */ + /* Waypoint payloads. + Payload is a [Waypoint](/docs/developers/protobufs/api#waypoint) message */ PortNum_WAYPOINT_APP = 8, /* Provides a 'ping' service that replies to any packet it receives. Also serves as a small example module. */ diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index acd8d3d6e..b1f5cc928 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -170,45 +170,54 @@ void AdminModule::handleSetOwner(const User &o) void AdminModule::handleSetConfig(const Config &c) { + bool requiresReboot = false; switch (c.which_payloadVariant) { - case Config_device_tag: - DEBUG_MSG("Setting config: Device\n"); - config.has_device = true; - config.device = c.payloadVariant.device; - break; - case Config_position_tag: - DEBUG_MSG("Setting config: Position\n"); - config.has_position = true; - config.position = c.payloadVariant.position; - break; - case Config_power_tag: - DEBUG_MSG("Setting config: Power\n"); - config.has_power = true; - config.power = c.payloadVariant.power; - break; - case Config_wifi_tag: - DEBUG_MSG("Setting config: WiFi\n"); - config.has_wifi = true; - config.wifi = c.payloadVariant.wifi; - break; - case Config_display_tag: - DEBUG_MSG("Setting config: Display\n"); - config.has_display = true; - config.display = c.payloadVariant.display; - break; - case Config_lora_tag: - DEBUG_MSG("Setting config: LoRa\n"); - config.has_lora = true; - config.lora = c.payloadVariant.lora; - break; - case Config_bluetooth_tag: - DEBUG_MSG("Setting config: Bluetooth\n"); - config.has_bluetooth = true; - config.bluetooth = c.payloadVariant.bluetooth; - break; + case Config_device_tag: + DEBUG_MSG("Setting config: Device\n"); + config.has_device = true; + config.device = c.payloadVariant.device; + break; + case Config_position_tag: + DEBUG_MSG("Setting config: Position\n"); + config.has_position = true; + config.position = c.payloadVariant.position; + break; + case Config_power_tag: + DEBUG_MSG("Setting config: Power\n"); + config.has_power = true; + config.power = c.payloadVariant.power; + break; + case Config_wifi_tag: + DEBUG_MSG("Setting config: WiFi\n"); + config.has_wifi = true; + config.wifi = c.payloadVariant.wifi; + break; + case Config_display_tag: + DEBUG_MSG("Setting config: Display\n"); + config.has_display = true; + config.display = c.payloadVariant.display; + break; + case Config_lora_tag: + DEBUG_MSG("Setting config: LoRa\n"); + config.has_lora = true; + config.lora = c.payloadVariant.lora; + requiresReboot = true; + break; + case Config_bluetooth_tag: + DEBUG_MSG("Setting config: Bluetooth\n"); + config.has_bluetooth = true; + config.bluetooth = c.payloadVariant.bluetooth; + requiresReboot = true; + break; } service.reloadConfig(); + // Reboot 5 seconds after a config that requires rebooting is set + if (requiresReboot) { + DEBUG_MSG("Rebooting due to config changes\n"); + screen->startRebootScreen(); + rebootAtMsec = millis() + (5 * 1000); + } } void AdminModule::handleSetModuleConfig(const ModuleConfig &c) diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index 8fc7964ea..32dfa86c5 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -13,8 +13,9 @@ #include "modules/ReplyModule.h" #include "modules/RoutingModule.h" #include "modules/TextMessageModule.h" -#include "modules/Telemetry/DeviceTelemetry.h" +#include "modules/WaypointModule.h" #if HAS_TELEMETRY +#include "modules/Telemetry/DeviceTelemetry.h" #include "modules/Telemetry/EnvironmentTelemetry.h" #endif #ifdef ARCH_ESP32 @@ -34,6 +35,7 @@ void setupModules() adminModule = new AdminModule(); nodeInfoModule = new NodeInfoModule(); positionModule = new PositionModule(); + waypointModule = new WaypointModule(); textMessageModule = new TextMessageModule(); // Note: if the rest of meshtastic doesn't need to explicitly use your module, you do not need to assign the instance @@ -70,6 +72,8 @@ void setupModules() storeForwardModule = new StoreForwardModule(); new RangeTestModule(); +#elif defined(ARCH_NRF52) +new ExternalNotificationModule(); #endif // NOTE! This module must be added LAST because it likes to check for replies from other modules and avoid sending extra acks diff --git a/src/modules/WaypointModule.cpp b/src/modules/WaypointModule.cpp new file mode 100644 index 000000000..fbc519d58 --- /dev/null +++ b/src/modules/WaypointModule.cpp @@ -0,0 +1,17 @@ +#include "configuration.h" +#include "WaypointModule.h" +#include "NodeDB.h" +#include "PowerFSM.h" + +WaypointModule *waypointModule; + +ProcessMessage WaypointModule::handleReceived(const MeshPacket &mp) +{ + auto &p = mp.decoded; + DEBUG_MSG("Received waypoint msg from=0x%0x, id=0x%x, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes); + + + notifyObservers(&mp); + + return ProcessMessage::CONTINUE; // Let others look at this message also if they want +} diff --git a/src/modules/WaypointModule.h b/src/modules/WaypointModule.h new file mode 100644 index 000000000..1c940fd35 --- /dev/null +++ b/src/modules/WaypointModule.h @@ -0,0 +1,25 @@ +#pragma once +#include "SinglePortModule.h" +#include "Observer.h" + +/** + * Waypoint message handling for meshtastic + */ +class WaypointModule : public SinglePortModule, public Observable +{ + public: + /** Constructor + * name is for debugging output + */ + WaypointModule() : SinglePortModule("waypoint", PortNum_WAYPOINT_APP) {} + + protected: + + /** Called to handle a particular incoming message + + @return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for it + */ + virtual ProcessMessage handleReceived(const MeshPacket &mp) override; +}; + +extern WaypointModule *waypointModule; diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 41e45e833..284b09c6a 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -175,9 +175,7 @@ bool MQTT::wantsLink() const { bool hasChannel = false; - if (moduleConfig.mqtt.disabled) { - // DEBUG_MSG("MQTT disabled...\n"); - } else { + if (moduleConfig.mqtt.enabled) { // No need for link if no channel needed it size_t numChan = channels.getNumChannels(); for (size_t i = 0; i < numChan; i++) { diff --git a/src/nimble/BluetoothUtil.cpp b/src/nimble/BluetoothUtil.cpp deleted file mode 100644 index 021358453..000000000 --- a/src/nimble/BluetoothUtil.cpp +++ /dev/null @@ -1,662 +0,0 @@ -#ifndef USE_NEW_ESP32_BLUETOOTH - -#include "BluetoothUtil.h" -#include "BluetoothSoftwareUpdate.h" -#include "NimbleBluetoothAPI.h" -#include "PhoneAPI.h" -#include "PowerFSM.h" -#include "configuration.h" -#include "esp_bt.h" -#include "host/util/util.h" -#include "main.h" -#include "nimble/NimbleDefs.h" -#include "services/gap/ble_svc_gap.h" -#include "services/gatt/ble_svc_gatt.h" -#include "sleep.h" -#include - -#ifdef ARCH_ESP32 -#include "mesh/http/WiFiAPClient.h" -#include -#endif - -static bool pinShowing; -static uint32_t doublepressed; - -static bool bluetoothActive; - -//put the wider device into a bluetooth pairing mode, and show the pin on screen. -//called in this file only -static void startCb(uint32_t pin) -{ - pinShowing = true; - powerFSM.trigger(EVENT_BLUETOOTH_PAIR); - screen->startBluetoothPinScreen(pin); -}; - -//pairing has ended -//called in this file only -static void stopCb() -{ - if (pinShowing) { - pinShowing = false; - screen->stopBluetoothPinScreen(); - } -}; - -static uint8_t own_addr_type; - -// Force arduino to keep ble data around -extern "C" bool btInUse() -{ - return true; -} - -/// Given a level between 0-100, update the BLE attribute -void updateBatteryLevel(uint8_t level) -{ - // FIXME -} - -//shutdown the bluetooth and tear down all the data structures. to prevent memory leaks -//called here only -void deinitBLE() -{ - if (bluetoothActive) { - bluetoothActive = false; - - // DEBUG_MSG("Shutting down bluetooth\n"); - // ble_gatts_show_local(); - - // FIXME - do we need to dealloc things? - what needs to stay alive across light sleep? - auto ret = nimble_port_stop(); - assert(ret == ESP_OK); - - nimble_port_deinit(); // teardown nimble datastructures - - // DEBUG_MSG("BLE port_deinit done\n"); - - ret = esp_nimble_hci_and_controller_deinit(); - assert(ret == ESP_OK); - - // DEBUG_MSG("BLE task exiting\n"); - - DEBUG_MSG("Done shutting down bluetooth\n"); - } -} - -void loopBLE() -{ - // FIXME -} - -extern "C" void ble_store_config_init(void); - -/// Print a macaddr - bytes are sometimes stored in reverse order -//called here only -static void print_addr(const uint8_t v[], bool isReversed = true) -{ - const int macaddrlen = 6; - - for (int i = 0; i < macaddrlen; i++) { - DEBUG_MSG("%02x%c", v[isReversed ? macaddrlen - i : i], (i == macaddrlen - 1) ? '\n' : ':'); - } -} - -/** - * Logs information about a connection to the console. - * called here only - */ -static void print_conn_desc(struct ble_gap_conn_desc *desc) -{ - DEBUG_MSG("handle=%d our_ota_addr_type=%d our_ota_addr=", desc->conn_handle, desc->our_ota_addr.type); - print_addr(desc->our_ota_addr.val); - DEBUG_MSG(" our_id_addr_type=%d our_id_addr=", desc->our_id_addr.type); - print_addr(desc->our_id_addr.val); - DEBUG_MSG(" peer_ota_addr_type=%d peer_ota_addr=", desc->peer_ota_addr.type); - print_addr(desc->peer_ota_addr.val); - DEBUG_MSG(" peer_id_addr_type=%d peer_id_addr=", desc->peer_id_addr.type); - print_addr(desc->peer_id_addr.val); - DEBUG_MSG(" conn_itvl=%d conn_latency=%d supervision_timeout=%d " - "encrypted=%d authenticated=%d bonded=%d\n", - desc->conn_itvl, desc->conn_latency, desc->supervision_timeout, desc->sec_state.encrypted, - desc->sec_state.authenticated, desc->sec_state.bonded); -} - -static void advertise(); - -/** - * The nimble host executes this callback when a GAP event occurs. The - * application associates a GAP event callback with each connection that forms. - * bleprph uses the same callback for all connections. - * - * @param event The type of event being signalled. - * @param ctxt Various information pertaining to the event. - * @param arg Application-specified argument; unused by - * bleprph. - * - * @return 0 if the application successfully handled the - * event; nonzero on failure. The semantics - * of the return code is specific to the - * particular GAP event being signalled. - */ -static int gap_event(struct ble_gap_event *event, void *arg) -{ - struct ble_gap_conn_desc desc; - int rc; - uint32_t now = millis(); - - switch (event->type) { - case BLE_GAP_EVENT_CONNECT: - /* A new connection was established or a connection attempt failed. */ - DEBUG_MSG("connection %s; status=%d ", event->connect.status == 0 ? "established" : "failed", event->connect.status); - if (event->connect.status == 0) { - rc = ble_gap_conn_find(event->connect.conn_handle, &desc); - assert(rc == 0); - print_conn_desc(&desc); - curConnectionHandle = event->connect.conn_handle; - } - DEBUG_MSG("\n"); - - if (event->connect.status != 0) { - /* Connection failed; resume advertising. */ - advertise(); - } - return 0; - - case BLE_GAP_EVENT_DISCONNECT: - DEBUG_MSG("disconnect; reason=%d ", event->disconnect.reason); - print_conn_desc(&event->disconnect.conn); - DEBUG_MSG("\n"); - - curConnectionHandle = -1; - - /* Connection terminated; resume advertising. */ - advertise(); - return 0; - - case BLE_GAP_EVENT_CONN_UPDATE: - /* The central has updated the connection parameters. */ - DEBUG_MSG("connection updated; status=%d ", event->conn_update.status); - rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); - assert(rc == 0); - print_conn_desc(&desc); - DEBUG_MSG("\n"); - return 0; - - case BLE_GAP_EVENT_ADV_COMPLETE: - DEBUG_MSG("advertise complete; reason=%d", event->adv_complete.reason); - advertise(); - return 0; - - case BLE_GAP_EVENT_ENC_CHANGE: - /* Encryption has been enabled or disabled for this connection. */ - DEBUG_MSG("encryption change event; status=%d ", event->enc_change.status); - rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc); - assert(rc == 0); - print_conn_desc(&desc); - DEBUG_MSG("\n"); - - // Remove our custom PIN request screen. - stopCb(); - return 0; - - case BLE_GAP_EVENT_SUBSCRIBE: - DEBUG_MSG("subscribe event; conn_handle=%d attr_handle=%d " - "reason=%d prevn=%d curn=%d previ=%d curi=%d\n", - event->subscribe.conn_handle, event->subscribe.attr_handle, event->subscribe.reason, - event->subscribe.prev_notify, event->subscribe.cur_notify, event->subscribe.prev_indicate, - event->subscribe.cur_indicate); - return 0; - - case BLE_GAP_EVENT_MTU: - DEBUG_MSG("mtu update event; conn_handle=%d cid=%d mtu=%d\n", event->mtu.conn_handle, event->mtu.channel_id, - event->mtu.value); - return 0; - - case BLE_GAP_EVENT_REPEAT_PAIRING: - DEBUG_MSG("repeat pairing event; conn_handle=%d " - "cur_key_sz=%d cur_auth=%d cur_sc=%d " - "new_key_sz=%d new_auth=%d new_sc=%d " - "new_bonding=%d\n", - event->repeat_pairing.conn_handle, event->repeat_pairing.cur_key_size, event->repeat_pairing.cur_authenticated, - event->repeat_pairing.cur_sc, event->repeat_pairing.new_key_size, event->repeat_pairing.new_authenticated, - event->repeat_pairing.new_sc, event->repeat_pairing.new_bonding); - /* We already have a bond with the peer, but it is attempting to - * establish a new secure link. This app sacrifices security for - * convenience: just throw away the old bond and accept the new link. - */ - - /* Delete the old bond. */ - rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc); - assert(rc == 0); - ble_store_util_delete_peer(&desc.peer_id_addr); - - /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should - * continue with the pairing operation. - */ - return BLE_GAP_REPEAT_PAIRING_RETRY; - - case BLE_GAP_EVENT_PASSKEY_ACTION: - DEBUG_MSG("PASSKEY_ACTION_EVENT started \n"); - struct ble_sm_io pkey = {0}; - - if (event->passkey.params.action == BLE_SM_IOACT_DISP) { - pkey.action = event->passkey.params.action; - DEBUG_MSG("dp: %d now:%d\n", doublepressed, now); - if (doublepressed > 0 && (doublepressed + (30 * 1000)) > now) { - DEBUG_MSG("User has overridden passkey or no display available\n"); - pkey.passkey = defaultBLEPin; - } else { - DEBUG_MSG("Using random passkey\n"); - pkey.passkey = random( - 100000, 999999); // This is the passkey to be entered on peer - we pick a number >100,000 to ensure 6 digits - } - DEBUG_MSG("*** Enter passkey %d on the peer side ***\n", pkey.passkey); - - startCb(pkey.passkey); - - rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); - DEBUG_MSG("ble_sm_inject_io result: %d\n", rc); - } else { - DEBUG_MSG("FIXME - unexpected auth type %d\n", event->passkey.params.action); - } - return 0; - } - - return 0; -} -/** - * Enables advertising with the following parameters: - * o General discoverable mode. - * o Undirected connectable mode. - * - * Called here only - */ -static void advertise(void) -{ - /** - * Set the advertisement data included in our advertisements: - * o Flags (indicates advertisement type and other general info). - * o Advertising tx power. - * o Device name. - * o 16-bit service UUIDs (alert notifications). - */ - - struct ble_hs_adv_fields adv_fields; - memset(&adv_fields, 0, sizeof adv_fields); - - /* Advertise two flags: - * o Discoverability in forthcoming advertisement (general) - * o BLE-only (BR/EDR unsupported). - */ - adv_fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP; - - /* Indicate that the TX power level field should be included; have the - * stack fill this value automatically. This is done by assigning the - * special value BLE_HS_ADV_TX_PWR_LVL_AUTO. - */ - adv_fields.tx_pwr_lvl_is_present = 1; - adv_fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; - - const char *name = ble_svc_gap_device_name(); - adv_fields.name = (uint8_t *)name; - adv_fields.name_len = strlen(name); - adv_fields.name_is_complete = 1; - - auto rc = ble_gap_adv_set_fields(&adv_fields); - if (rc != 0) { - DEBUG_MSG("error setting advertisement data; rc=%d\n", rc); - return; - } - - // add scan response fields - struct ble_hs_adv_fields scan_fields; - memset(&scan_fields, 0, sizeof scan_fields); - scan_fields.uuids128 = const_cast(&mesh_service_uuid); - scan_fields.num_uuids128 = 1; - scan_fields.uuids128_is_complete = 1; - - rc = ble_gap_adv_rsp_set_fields(&scan_fields); - if (rc != 0) { - DEBUG_MSG("error setting scan response data; rc=%d\n", rc); - return; - } - - /* Begin advertising. */ - struct ble_gap_adv_params adv_params; - memset(&adv_params, 0, sizeof adv_params); - adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; - adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; - // FIXME - use RPA for first parameter - rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER, &adv_params, gap_event, NULL); - if (rc != 0) { - DEBUG_MSG("error enabling advertisement; rc=%d\n", rc); - return; - } -} - -//callback -//doesn't do anything -static void on_reset(int reason) -{ - // 19 == BLE_HS_ETIMEOUT_HCI - DEBUG_MSG("Resetting state; reason=%d\n", reason); -} - -//callback -// -static void on_sync(void) -{ - int rc; - - rc = ble_hs_util_ensure_addr(0); - assert(rc == 0); - - /* Figure out address to use while advertising (no privacy for now) */ - rc = ble_hs_id_infer_auto(0, &own_addr_type); - if (rc != 0) { - DEBUG_MSG("error determining address type; rc=%d\n", rc); - return; - } - - /* Printing ADDR */ - uint8_t addr_val[6] = {0}; - int isPrivate = 0; - rc = ble_hs_id_copy_addr(own_addr_type, addr_val, &isPrivate); - assert(rc == 0); - DEBUG_MSG("BLE advertisting type=%d, Private=%d, Device Address: ", own_addr_type, isPrivate); - print_addr(addr_val); - DEBUG_MSG("\n"); - /* Begin advertising. */ - advertise(); -} - -//do the bluetooth tasks -static void ble_host_task(void *param) -{ - DEBUG_MSG("BLE task running\n"); - nimble_port_run(); // This function will return only when nimble_port_stop() is executed. - - // DEBUG_MSG("BLE run complete\n"); - - nimble_port_freertos_deinit(); // delete the task -} - -//saves the stream handles when characteristics are successfully registered -void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) -{ - char buf[BLE_UUID_STR_LEN]; - - switch (ctxt->op) { - case BLE_GATT_REGISTER_OP_SVC: - DEBUG_MSG("registered service %s with handle=%d\n", ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf), ctxt->svc.handle); - break; - - case BLE_GATT_REGISTER_OP_CHR: - /* DEBUG_MSG("registering characteristic %s with " - "def_handle=%d val_handle=%d\n", - ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), ctxt->chr.def_handle, ctxt->chr.val_handle); */ - - if (ctxt->chr.chr_def->uuid == &fromnum_uuid.u) { - fromNumValHandle = ctxt->chr.val_handle; - // DEBUG_MSG("FromNum handle %d\n", fromNumValHandle); - } - if (ctxt->chr.chr_def->uuid == &update_result_uuid.u) { - updateResultHandle = ctxt->chr.val_handle; - // DEBUG_MSG("update result handle %d\n", updateResultHandle); - } - break; - - case BLE_GATT_REGISTER_OP_DSC: - DEBUG_MSG("registering descriptor %s with handle=%d\n", ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), ctxt->dsc.handle); - break; - - default: - assert(0); - break; - } -} - -/** - * A helper function that implements simple read and write handling for a uint32_t - * - * If a read, the provided value will be returned over bluetooth. If a write, the value from the received packet - * will be written into the variable. - * - * used a few places - */ -int chr_readwrite32le(uint32_t *v, struct ble_gatt_access_ctxt *ctxt) -{ - uint8_t le[4]; - - if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { - DEBUG_MSG("BLE reading a uint32\n"); - put_le32(le, *v); - auto rc = os_mbuf_append(ctxt->om, le, sizeof(le)); - assert(rc == 0); - } else if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { - uint16_t len = 0; - - auto rc = ble_hs_mbuf_to_flat(ctxt->om, le, sizeof(le), &len); - assert(rc == 0); - if (len < sizeof(le)) { - DEBUG_MSG("Error: wrongsized write32\n"); - *v = 0; - return BLE_ATT_ERR_UNLIKELY; - } else { - *v = get_le32(le); - DEBUG_MSG("BLE writing a uint32\n"); - } - } else { - DEBUG_MSG("Unexpected readwrite32 op\n"); - return BLE_ATT_ERR_UNLIKELY; - } - - return 0; // success -} - -/** - * A helper for readwrite access to an array of bytes (with no endian conversion) - */ -int chr_readwrite8(uint8_t *v, size_t vlen, struct ble_gatt_access_ctxt *ctxt) -{ - if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { - DEBUG_MSG("BLE reading bytes\n"); - auto rc = os_mbuf_append(ctxt->om, v, vlen); - assert(rc == 0); - } else if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { - uint16_t len = 0; - - auto rc = ble_hs_mbuf_to_flat(ctxt->om, v, vlen, &len); - assert(rc == 0); - if (len < vlen) { - DEBUG_MSG("Error: wrongsized write\n"); - return BLE_ATT_ERR_UNLIKELY; - } else { - DEBUG_MSG("BLE writing bytes\n"); - } - } else { - DEBUG_MSG("Unexpected readwrite8 op\n"); - return BLE_ATT_ERR_UNLIKELY; - } - - return 0; // success -} - -void disablePin() -{ - DEBUG_MSG("User Override, disabling bluetooth pin requirement\n"); - // keep track of when it was pressed, so we know it was within X seconds - - // Flash the LED - setLed(true); - delay(100); - setLed(false); - delay(100); - setLed(true); - delay(100); - setLed(false); - delay(100); - setLed(true); - delay(100); - setLed(false); - - doublepressed = millis(); -} - -// This should go somewhere else. -void clearNVS() -{ -#ifdef ARCH_ESP32 - - // As soon as the LED flashing from double click is done, immediately do a tripple click to - // erase nvs memory. - if (doublepressed > (millis() - 2000)) { - DEBUG_MSG("Clearing NVS memory\n"); - - // This will erase ble pairings, ssl key and persistent preferences. - nvs_flash_erase(); - - DEBUG_MSG("Restarting...\n"); - ESP.restart(); - } -#endif -} - -// This routine is called multiple times, once each time we come back from sleep -void reinitBluetooth() -{ - auto isFirstTime = !bluetoothPhoneAPI; - - DEBUG_MSG("Starting bluetooth\n"); - if (isFirstTime) { - bluetoothPhoneAPI = new BluetoothPhoneAPI(); - } - - // FIXME - if waking from light sleep, only esp_nimble_hci_init? - auto res = esp_nimble_hci_and_controller_init(); // : esp_nimble_hci_init(); - // DEBUG_MSG("BLE result %d\n", res); - assert(res == ESP_OK); - - nimble_port_init(); - - ble_att_set_preferred_mtu(512); - - res = ble_gatts_reset(); // Teardown the service tables, so the next restart assigns the same handle numbers - assert(res == ESP_OK); - - /* Initialize the NimBLE host configuration. */ - ble_hs_cfg.reset_cb = on_reset; - ble_hs_cfg.sync_cb = on_sync; - ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb; - ble_hs_cfg.store_status_cb = ble_store_util_status_rr; - - ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_DISP_ONLY; - ble_hs_cfg.sm_bonding = 1; - ble_hs_cfg.sm_mitm = 1; - ble_hs_cfg.sm_sc = 1; - // per https://github.com/espressif/esp-idf/issues/5530#issuecomment-652933685 - ble_hs_cfg.sm_our_key_dist = BLE_SM_PAIR_KEY_DIST_ID | BLE_SM_PAIR_KEY_DIST_ENC; - ble_hs_cfg.sm_their_key_dist = BLE_SM_PAIR_KEY_DIST_ID | BLE_SM_PAIR_KEY_DIST_ENC; - - // add standard GAP services - ble_svc_gap_init(); - ble_svc_gatt_init(); - - res = ble_gatts_count_cfg(gatt_svr_svcs); // assigns handles? see docstring for note about clearing the handle list - // before calling SLEEP SUPPORT - assert(res == 0); - - res = ble_gatts_add_svcs(gatt_svr_svcs); - assert(res == 0); - - reinitUpdateService(); - - /* Set the default device name. */ - res = ble_svc_gap_device_name_set(getDeviceName()); - assert(res == 0); - - /* XXX Need to have template for store */ - ble_store_config_init(); - - nimble_port_freertos_init(ble_host_task); - bluetoothActive = true; -} - -bool bluetoothOn; - -// Enable/disable bluetooth. -void setBluetoothEnable(bool on) -{ - if (on != bluetoothOn) { - DEBUG_MSG("Setting bluetooth enable=%d\n", on); - - bluetoothOn = on; - if (on) { - if (!isWifiAvailable()) { - Serial.printf("Pre BT: %u heap size\n", ESP.getFreeHeap()); - // ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) ); - reinitBluetooth(); - } - } else { - // shutdown wifi - deinitWifi(); - - // We have to totally teardown our bluetooth objects to prevent leaks - deinitBLE(); - - Serial.printf("Shutdown BT: %u heap size\n", ESP.getFreeHeap()); - // ESP_ERROR_CHECK( heap_trace_stop() ); - // heap_trace_dump(); - } - } -} - -#if 0 - -static BLECharacteristic *batteryLevelC; - -/** - * Create a battery level service - */ -BLEService *createBatteryService(BLEServer *server) -{ - // Create the BLE Service - BLEService *pBattery = server->createService(BLEUUID((uint16_t)0x180F)); - - batteryLevelC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_BATTERY_LEVEL), - BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); - - addWithDesc(pBattery, batteryLevelC, "Percentage 0 - 100"); - batteryLevelC->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification - - // I don't think we need to advertise this? and some phones only see the first thing advertised anyways... - // server->getAdvertising()->addServiceUUID(pBattery->getUUID()); - pBattery->start(); - - return pBattery; -} - -/** - * Update the battery level we are currently telling clients. - * level should be a pct between 0 and 100 - */ -void updateBatteryLevel(uint8_t level) -{ - if (batteryLevelC) { - DEBUG_MSG("set BLE battery level %u\n", level); - batteryLevelC->setValue(&level, 1); - batteryLevelC->notify(); - } -} - - - -// Note: these callbacks might be coming in from a different thread. -BLEServer *serve = initBLE(, , getDeviceName(), HW_VENDOR, optstr(APP_VERSION), - optstr(HW_VERSION)); // FIXME, use a real name based on the macaddr - -#endif - -#endif //#ifndef USE_NEW_ESP32_BLUETOOTH \ No newline at end of file diff --git a/src/nimble/BluetoothUtil.h b/src/nimble/BluetoothUtil.h deleted file mode 100644 index bd2c08e27..000000000 --- a/src/nimble/BluetoothUtil.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef USE_NEW_ESP32_BLUETOOTH - -#pragma once - -#include - -/// We only allow one BLE connection at a time -extern int16_t curConnectionHandle; - -// TODO(girts): create a class for the bluetooth utils helpers? -using StartBluetoothPinScreenCallback = std::function; -using StopBluetoothPinScreenCallback = std::function; - -/// Given a level between 0-100, update the BLE attribute -void updateBatteryLevel(uint8_t level); -void deinitBLE(); -void loopBLE(); -void reinitBluetooth(); -void disablePin(); -void clearNVS(); - -/** - * A helper function that implements simple read and write handling for a uint32_t - * - * If a read, the provided value will be returned over bluetooth. If a write, the value from the received packet - * will be written into the variable. - */ -int chr_readwrite32le(uint32_t *v, struct ble_gatt_access_ctxt *ctxt); - -/** - * A helper for readwrite access to an array of bytes (with no endian conversion) - */ -int chr_readwrite8(uint8_t *v, size_t vlen, struct ble_gatt_access_ctxt *ctxt); - -#endif //#ifndef USE_NEW_ESP32_BLUETOOTH \ No newline at end of file diff --git a/src/platform/esp32/ESP32Bluetooth.cpp b/src/nimble/NimbleBluetooth.cpp similarity index 71% rename from src/platform/esp32/ESP32Bluetooth.cpp rename to src/nimble/NimbleBluetooth.cpp index 27c6c76d4..501262f83 100644 --- a/src/platform/esp32/ESP32Bluetooth.cpp +++ b/src/nimble/NimbleBluetooth.cpp @@ -1,7 +1,5 @@ -#ifdef USE_NEW_ESP32_BLUETOOTH - #include "configuration.h" -#include "ESP32Bluetooth.h" +#include "NimbleBluetooth.h" #include "BluetoothCommon.h" #include "PowerFSM.h" #include "sleep.h" @@ -46,7 +44,7 @@ static BluetoothPhoneAPI *bluetoothPhoneAPI; * Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies) */ -class ESP32BluetoothToRadioCallback : public NimBLECharacteristicCallbacks +class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks { virtual void onWrite(NimBLECharacteristic *pCharacteristic) { DEBUG_MSG("To Radio onwrite\n"); @@ -56,7 +54,7 @@ class ESP32BluetoothToRadioCallback : public NimBLECharacteristicCallbacks } }; -class ESP32BluetoothFromRadioCallback : public NimBLECharacteristicCallbacks +class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks { virtual void onRead(NimBLECharacteristic *pCharacteristic) { DEBUG_MSG("From Radio onread\n"); @@ -69,7 +67,7 @@ class ESP32BluetoothFromRadioCallback : public NimBLECharacteristicCallbacks } }; -class ESP32BluetoothServerCallback : public NimBLEServerCallbacks +class NimbleBluetoothServerCallback : public NimBLEServerCallbacks { virtual uint32_t onPassKeyRequest() { uint32_t passkey = config.bluetooth.fixed_pin; @@ -100,6 +98,7 @@ class ESP32BluetoothServerCallback : public NimBLEServerCallbacks passkeyShowing = false; screen->stopBluetoothPinScreen(); } + // bluetoothPhoneAPI->setInitialState(); } virtual void onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc *desc) @@ -108,10 +107,10 @@ class ESP32BluetoothServerCallback : public NimBLEServerCallbacks } }; -static ESP32BluetoothToRadioCallback *toRadioCallbacks; -static ESP32BluetoothFromRadioCallback *fromRadioCallbacks; +static NimbleBluetoothToRadioCallback *toRadioCallbacks; +static NimbleBluetoothFromRadioCallback *fromRadioCallbacks; -void ESP32Bluetooth::shutdown() +void NimbleBluetooth::shutdown() { // Shutdown bluetooth for minimum power draw DEBUG_MSG("Disable bluetooth\n"); @@ -121,58 +120,63 @@ void ESP32Bluetooth::shutdown() pAdvertising->stop(); } -bool ESP32Bluetooth::isActive() +bool NimbleBluetooth::isActive() { NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); return bleServer && (bleServer->getConnectedCount() > 0 || pAdvertising->isAdvertising()); } -void ESP32Bluetooth::setup() +void NimbleBluetooth::setup() { - DEBUG_MSG("Initialise the ESP32 bluetooth module\n"); + // Uncomment for testing + // NimbleBluetooth::clearBonds(); + + DEBUG_MSG("Initialise the NimBLE bluetooth module\n"); NimBLEDevice::init(getDeviceName()); NimBLEDevice::setPower(ESP_PWR_LVL_P9); - // FIXME fails in iOS - if (config.bluetooth.mode == Config_BluetoothConfig_PairingMode_NoPin) { - NimBLEDevice::setSecurityIOCap(BLE_HS_IO_NO_INPUT_OUTPUT); - NimBLEDevice::setSecurityAuth(true, false, true); - } - else { + if (config.bluetooth.mode != Config_BluetoothConfig_PairingMode_NoPin) { NimBLEDevice::setSecurityAuth(true, true, true); NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); } bleServer = NimBLEDevice::createServer(); - ESP32BluetoothServerCallback *serverCallbacks = new ESP32BluetoothServerCallback(); + NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback(); bleServer->setCallbacks(serverCallbacks, true); setupService(); startAdvertising(); } -void ESP32Bluetooth::setupService() +void NimbleBluetooth::setupService() { NimBLEService *bleService = bleServer->createService(MESH_SERVICE_UUID); - - //define the characteristics that the app is looking for - NimBLECharacteristic *ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_AUTHEN | NIMBLE_PROPERTY::WRITE_ENC); - NimBLECharacteristic *FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC); - fromNumCharacteristic = bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC); - + NimBLECharacteristic *ToRadioCharacteristic; + NimBLECharacteristic *FromRadioCharacteristic; + // Define the characteristics that the app is looking for + if (config.bluetooth.mode == Config_BluetoothConfig_PairingMode_NoPin) { + ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, NIMBLE_PROPERTY::WRITE); + FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, NIMBLE_PROPERTY::READ); + fromNumCharacteristic = bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ); + } + else { + ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_AUTHEN | NIMBLE_PROPERTY::WRITE_ENC); + FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC); + fromNumCharacteristic = bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC); + } bluetoothPhoneAPI = new BluetoothPhoneAPI(); - toRadioCallbacks = new ESP32BluetoothToRadioCallback(); + toRadioCallbacks = new NimbleBluetoothToRadioCallback(); ToRadioCharacteristic->setCallbacks(toRadioCallbacks); - fromRadioCallbacks = new ESP32BluetoothFromRadioCallback(); + fromRadioCallbacks = new NimbleBluetoothFromRadioCallback(); FromRadioCharacteristic->setCallbacks(fromRadioCallbacks); bleService->start(); } -void ESP32Bluetooth::startAdvertising() +void NimbleBluetooth::startAdvertising() { NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); pAdvertising->reset(); @@ -186,7 +190,7 @@ void updateBatteryLevel(uint8_t level) //blebas.write(level); } -void ESP32Bluetooth::clearBonds() +void NimbleBluetooth::clearBonds() { DEBUG_MSG("Clearing bluetooth bonds!\n"); NimBLEDevice::deleteAllBonds(); @@ -195,7 +199,9 @@ void ESP32Bluetooth::clearBonds() void clearNVS() { NimBLEDevice::deleteAllBonds(); +#ifdef ARCH_ESP32 ESP.restart(); +#endif } void disablePin() @@ -218,5 +224,3 @@ void disablePin() doublepressed = millis(); } - -#endif diff --git a/src/platform/esp32/ESP32Bluetooth.h b/src/nimble/NimbleBluetooth.h similarity index 80% rename from src/platform/esp32/ESP32Bluetooth.h rename to src/nimble/NimbleBluetooth.h index 76d9e47ab..5592b6d69 100644 --- a/src/platform/esp32/ESP32Bluetooth.h +++ b/src/nimble/NimbleBluetooth.h @@ -1,8 +1,6 @@ -#ifdef USE_NEW_ESP32_BLUETOOTH - #pragma once -class ESP32Bluetooth +class NimbleBluetooth { public: void setup(); @@ -18,5 +16,3 @@ class ESP32Bluetooth void setBluetoothEnable(bool on); void clearNVS(); void disablePin(); - -#endif diff --git a/src/nimble/NimbleBluetoothAPI.cpp b/src/nimble/NimbleBluetoothAPI.cpp deleted file mode 100644 index a2bd718d7..000000000 --- a/src/nimble/NimbleBluetoothAPI.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef USE_NEW_ESP32_BLUETOOTH - -#include "NimbleBluetoothAPI.h" -#include "PhoneAPI.h" -#include "configuration.h" -#include "nimble/BluetoothUtil.h" -#include "nimble/NimbleDefs.h" - -// This scratch buffer is used for various bluetooth reads/writes - but it is safe because only one bt operation can be in -// proccess at once -static uint8_t trBytes[FromRadio_size < ToRadio_size ? ToRadio_size : FromRadio_size]; -static uint32_t fromNum; - -uint16_t fromNumValHandle; - -/// We only allow one BLE connection at a time -int16_t curConnectionHandle = -1; - -PhoneAPI *bluetoothPhoneAPI; - -void BluetoothPhoneAPI::onNowHasData(uint32_t fromRadioNum) -{ - PhoneAPI::onNowHasData(fromRadioNum); - - fromNum = fromRadioNum; - if (curConnectionHandle >= 0 && fromNumValHandle) { - DEBUG_MSG("BLE notify fromNum\n"); - auto res = ble_gattc_notify(curConnectionHandle, fromNumValHandle); - assert(res == 0); - } else { - DEBUG_MSG("No BLE notify\n"); - } -} - -bool BluetoothPhoneAPI::checkIsConnected() { - return curConnectionHandle >= 0; -} - -int toradio_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) -{ - auto om = ctxt->om; - uint16_t len = 0; - - auto rc = ble_hs_mbuf_to_flat(om, trBytes, sizeof(trBytes), &len); - if (rc != 0) { - return BLE_ATT_ERR_UNLIKELY; - } - - /// DEBUG_MSG("toRadioWriteCb data %p, len %u\n", trBytes, len); - - bluetoothPhoneAPI->handleToRadio(trBytes, len); - return 0; -} - -int fromradio_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) -{ - size_t numBytes = bluetoothPhoneAPI->getFromRadio(trBytes); - - DEBUG_MSG("BLE fromRadio called omlen=%d, ourlen=%d\n", OS_MBUF_PKTLEN(ctxt->om), - numBytes); // the normal case has omlen 1 here - - // Someone is going to read our value as soon as this callback returns. So fill it with the next message in the queue - // or make empty if the queue is empty - auto rc = os_mbuf_append(ctxt->om, trBytes, numBytes); - assert(rc == 0); - - return 0; // success -} - -int fromnum_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) -{ - return chr_readwrite32le(&fromNum, ctxt); -} - -#endif //#ifndef USE_NEW_ESP32_BLUETOOTH \ No newline at end of file diff --git a/src/nimble/NimbleBluetoothAPI.h b/src/nimble/NimbleBluetoothAPI.h deleted file mode 100644 index 0945ae422..000000000 --- a/src/nimble/NimbleBluetoothAPI.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef USE_NEW_ESP32_BLUETOOTH - -#pragma once - -#include "PhoneAPI.h" - -extern uint16_t fromNumValHandle; - -class BluetoothPhoneAPI : public PhoneAPI -{ -protected: - /** - * Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies) - */ - virtual void onNowHasData(uint32_t fromRadioNum) override; - - /// Check the current underlying physical link to see if the client is currently connected - virtual bool checkIsConnected() override; -}; - -extern PhoneAPI *bluetoothPhoneAPI; - -#endif //#ifndef USE_NEW_ESP32_BLUETOOTH \ No newline at end of file diff --git a/src/nimble/NimbleDefs.c b/src/nimble/NimbleDefs.c deleted file mode 100644 index 8be1401ae..000000000 --- a/src/nimble/NimbleDefs.c +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef USE_NEW_ESP32_BLUETOOTH - -#include "NimbleDefs.h" - -// NRF52 wants these constants as byte arrays -// Generated here https://yupana-engineering.com/online-uuid-to-c-array-converter - but in REVERSE BYTE ORDER -const ble_uuid128_t mesh_service_uuid = - BLE_UUID128_INIT(0xfd, 0xea, 0x73, 0xe2, 0xca, 0x5d, 0xa8, 0x9f, 0x1f, 0x46, 0xa8, 0x15, 0x18, 0xb2, 0xa1, 0x6b); - -static const ble_uuid128_t toradio_uuid = - BLE_UUID128_INIT(0xe7, 0x01, 0x44, 0x12, 0x66, 0x78, 0xdd, 0xa1, 0xad, 0x4d, 0x9e, 0x12, 0xd2, 0x76, 0x5c, 0xf7); - -static const ble_uuid128_t fromradio_uuid = - BLE_UUID128_INIT(0xd5, 0x54, 0xe4, 0xc5, 0x25, 0xc5, 0x31, 0xa5, 0x55, 0x4a, 0x02, 0xee, 0xc2, 0xbc, 0xa2, 0x8b); - -const ble_uuid128_t fromnum_uuid = - BLE_UUID128_INIT(0x53, 0x44, 0xe3, 0x47, 0x75, 0xaa, 0x70, 0xa6, 0x66, 0x4f, 0x00, 0xa8, 0x8c, 0xa1, 0x9d, 0xed); - -const struct ble_gatt_svc_def gatt_svr_svcs[] = { - { - /*** Service: Security test. */ - .type = BLE_GATT_SVC_TYPE_PRIMARY, - .uuid = &mesh_service_uuid.u, - .characteristics = - (struct ble_gatt_chr_def[]){{ - .uuid = &toradio_uuid.u, - .access_cb = toradio_callback, - .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN, - }, - { - .uuid = &fromradio_uuid.u, - .access_cb = fromradio_callback, - .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN, - }, - { - .uuid = &fromnum_uuid.u, - .access_cb = fromnum_callback, - .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN | BLE_GATT_CHR_F_NOTIFY, - }, - { - 0, /* No more characteristics in this service. */ - }}, - }, - - { - 0, /* No more services. */ - }, -}; - -#endif //#ifndef USE_NEW_ESP32_BLUETOOTH \ No newline at end of file diff --git a/src/nimble/NimbleDefs.h b/src/nimble/NimbleDefs.h deleted file mode 100644 index e7c2c4c0f..000000000 --- a/src/nimble/NimbleDefs.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef USE_NEW_ESP32_BLUETOOTH - -#pragma once - -// Keep nimble #defs from messing up the build -#ifndef max -#define max max -#define min min -#endif - -#include "esp_nimble_hci.h" -#include "host/ble_hs.h" -#include "host/ble_uuid.h" -#include "nimble/nimble_port.h" -#include "nimble/nimble_port_freertos.h" - -#ifdef __cplusplus -extern "C" { -#endif - -int toradio_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); - -int fromradio_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); - -int fromnum_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); - -extern const struct ble_gatt_svc_def gatt_svr_svcs[]; - -extern const ble_uuid128_t mesh_service_uuid, fromnum_uuid; - -#ifdef __cplusplus -}; -#endif - -#endif //#ifndef USE_NEW_ESP32_BLUETOOTH \ No newline at end of file diff --git a/src/platform/esp32/BluetoothSoftwareUpdate.cpp b/src/platform/esp32/BluetoothSoftwareUpdate.cpp deleted file mode 100644 index 174a64fca..000000000 --- a/src/platform/esp32/BluetoothSoftwareUpdate.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#ifndef USE_NEW_ESP32_BLUETOOTH - -#include - -#include "../concurrency/LockGuard.h" -#include "../graphics/Screen.h" -#include "../main.h" -#include "BluetoothSoftwareUpdate.h" -#include "NodeDB.h" -#include "PowerFSM.h" -#include "RadioLibInterface.h" -#include "configuration.h" -#include "nimble/BluetoothUtil.h" - -#include -#include - -int16_t updateResultHandle = -1; - -static CRC32 crc; - -static uint32_t updateExpectedSize, updateActualSize; -static uint8_t update_result; -static uint8_t update_region; - -static concurrency::Lock *updateLock; - -/// Handle writes & reads to total size -int update_size_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) -{ - concurrency::LockGuard g(updateLock); - - // Check if there is enough to OTA Update - chr_readwrite32le(&updateExpectedSize, ctxt); - - if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR && updateExpectedSize != 0) { - updateActualSize = 0; - crc.reset(); - if (Update.isRunning()) - Update.abort(); - bool canBegin = Update.begin(updateExpectedSize, update_region); - DEBUG_MSG("Setting region %d update size %u, result %d\n", update_region, updateExpectedSize, canBegin); - if (!canBegin) { - // Indicate failure by forcing the size to 0 (client will read it back) - updateExpectedSize = 0; - } else { - // This totally breaks abstraction to up up into the app layer for this, but quick hack to make sure we only - // talk to one service during the sw update. - // DEBUG_MSG("FIXME, crufty shutdown of mesh bluetooth for sw update."); - // void stopMeshBluetoothService(); - // stopMeshBluetoothService(); - - screen->startFirmwareUpdateScreen(); - if (RadioLibInterface::instance) - RadioLibInterface::instance->disable(); // FIXME, nasty hack - the RF95 ISR/SPI code on ESP32 can fail while we - // are writing flash - shut the radio off during updates - } - } - - return 0; -} - -#define MAX_BLOCKSIZE_FOR_BT 512 - -/// Handle writes to data -int update_data_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) -{ - concurrency::LockGuard g(updateLock); - - static uint8_t - data[MAX_BLOCKSIZE_FOR_BT]; // we temporarily copy here because I'm worried that a fast sender might be able overwrite srcbuf - - uint16_t len = 0; - - auto rc = ble_hs_mbuf_to_flat(ctxt->om, data, sizeof(data), &len); - assert(rc == 0); - - // DEBUG_MSG("Writing %u\n", len); - crc.update(data, len); - Update.write(data, len); - updateActualSize += len; - powerFSM.trigger(EVENT_FIRMWARE_UPDATE); - - return 0; -} - -/// Handle writes to crc32 -int update_crc32_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) -{ - concurrency::LockGuard g(updateLock); - uint32_t expectedCRC = 0; - chr_readwrite32le(&expectedCRC, ctxt); - - uint32_t actualCRC = crc.finalize(); - DEBUG_MSG("expected CRC %u\n", expectedCRC); - - uint8_t result = 0xff; - - if (updateActualSize != updateExpectedSize) { - DEBUG_MSG("Expected %u bytes, but received %u bytes!\n", updateExpectedSize, updateActualSize); - result = 0xe1; // FIXME, use real error codes - } else if (actualCRC != expectedCRC) // Check the CRC before asking the update to happen. - { - DEBUG_MSG("Invalid CRC! expected=%u, actual=%u\n", expectedCRC, actualCRC); - result = 0xe0; // FIXME, use real error codes - } else { - if (Update.end()) { - if (update_region == U_SPIFFS) { - DEBUG_MSG("Filesystem updated!\n"); - nodeDB.saveToDisk(); // Since we just wiped the filesystem, we need to save our current state - } else { - DEBUG_MSG("Appload updated, rebooting in 5 seconds!\n"); - rebootAtMsec = millis() + 5000; - } - } else { - DEBUG_MSG("Error Occurred. Error #: %d\n", Update.getError()); - } - result = Update.getError(); - } - - if (RadioLibInterface::instance) - RadioLibInterface::instance->startReceive(); // Resume radio - - assert(updateResultHandle >= 0); - update_result = result; - DEBUG_MSG("BLE notify update result\n"); - auto res = ble_gattc_notify(curConnectionHandle, updateResultHandle); - assert(res == 0); - - return 0; -} - -int update_result_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) -{ - return chr_readwrite8(&update_result, sizeof(update_result), ctxt); -} - -int update_region_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) -{ - return chr_readwrite8(&update_region, sizeof(update_region), ctxt); -} - -/* -See bluetooth-api.md - - */ -void reinitUpdateService() -{ - if (!updateLock) - updateLock = new concurrency::Lock(); - - auto res = ble_gatts_count_cfg(gatt_update_svcs); // assigns handles? see docstring for note about clearing the handle list - // before calling SLEEP SUPPORT - assert(res == 0); - - res = ble_gatts_add_svcs(gatt_update_svcs); - assert(res == 0); -} - -#endif //#ifndef USE_NEW_ESP32_BLUETOOTH \ No newline at end of file diff --git a/src/platform/esp32/BluetoothSoftwareUpdate.h b/src/platform/esp32/BluetoothSoftwareUpdate.h deleted file mode 100644 index 468cc6060..000000000 --- a/src/platform/esp32/BluetoothSoftwareUpdate.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef USE_NEW_ESP32_BLUETOOTH - -#pragma once - -#include "nimble/NimbleDefs.h" - -void reinitUpdateService(); - -#ifdef __cplusplus -extern "C" { -#endif - -int update_size_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); -int update_data_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); -int update_result_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); -int update_crc32_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); -int update_region_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); - -extern const struct ble_gatt_svc_def gatt_update_svcs[]; - -extern const ble_uuid128_t update_result_uuid, update_region_uuid; - -extern int16_t updateResultHandle; - -#ifdef __cplusplus -}; -#endif - -#endif //#ifndef USE_NEW_ESP32_BLUETOOTH \ No newline at end of file diff --git a/src/platform/esp32/NimbleSoftwareUpdate.c b/src/platform/esp32/NimbleSoftwareUpdate.c deleted file mode 100644 index 6f7ebf6cc..000000000 --- a/src/platform/esp32/NimbleSoftwareUpdate.c +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef USE_NEW_ESP32_BLUETOOTH -#include "BluetoothSoftwareUpdate.h" - -// NRF52 wants these constants as byte arrays -// Generated here https://yupana-engineering.com/online-uuid-to-c-array-converter - but in REVERSE BYTE ORDER - -// "cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30" -const ble_uuid128_t update_service_uuid = - BLE_UUID128_INIT(0x30, 0xee, 0x44, 0x31, 0x2e, 0x44, 0xbb, 0xbd, 0x0d, 0x4c, 0x4c, 0xa8, 0x0b, 0x9a, 0x0b, 0xcb); - -// "e74dd9c0-a301-4a6f-95a1-f0e1dbea8e1e" write|read -const ble_uuid128_t update_size_uuid = - BLE_UUID128_INIT(0x1e, 0x8e, 0xea, 0xdb, 0xe1, 0xf0, 0xa1, 0x95, 0x6f, 0x4a, 0x01, 0xa3, 0xc0, 0xd9, 0x4d, 0xe7); - -// "e272ebac-d463-4b98-bc84-5cc1a39ee517" write -const ble_uuid128_t update_data_uuid = - BLE_UUID128_INIT(0x17, 0xe5, 0x9e, 0xa3, 0xc1, 0x5c, 0x84, 0xbc, 0x98, 0x4b, 0x63, 0xd4, 0xac, 0xeb, 0x72, 0xe2); - -// "4826129c-c22a-43a3-b066-ce8f0d5bacc6" write -const ble_uuid128_t update_crc32_uuid = - BLE_UUID128_INIT(0xc6, 0xac, 0x5b, 0x0d, 0x8f, 0xce, 0x66, 0xb0, 0xa3, 0x43, 0x2a, 0xc2, 0x9c, 0x12, 0x26, 0x48); - -// "5e134862-7411-4424-ac4a-210937432c77" read|notify -const ble_uuid128_t update_result_uuid = - BLE_UUID128_INIT(0x77, 0x2c, 0x43, 0x37, 0x09, 0x21, 0x4a, 0xac, 0x24, 0x44, 0x11, 0x74, 0x62, 0x48, 0x13, 0x5e); - -// "5e134862-7411-4424-ac4a-210937432c67" write -const ble_uuid128_t update_region_uuid = - BLE_UUID128_INIT(0x67, 0x2c, 0x43, 0x37, 0x09, 0x21, 0x4a, 0xac, 0x24, 0x44, 0x11, 0x74, 0x62, 0x48, 0x13, 0x5e); - -const struct ble_gatt_svc_def gatt_update_svcs[] = { - { - /*** Service: Security test. */ - .type = BLE_GATT_SVC_TYPE_PRIMARY, - .uuid = &update_service_uuid.u, - .characteristics = - (struct ble_gatt_chr_def[]){{ - .uuid = &update_size_uuid.u, - .access_cb = update_size_callback, - .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN | BLE_GATT_CHR_F_READ | - BLE_GATT_CHR_F_READ_AUTHEN, - }, - { - .uuid = &update_data_uuid.u, - .access_cb = update_data_callback, - .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN, - }, - { - .uuid = &update_crc32_uuid.u, - .access_cb = update_crc32_callback, - .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN, - }, - { - .uuid = &update_result_uuid.u, - .access_cb = update_result_callback, - .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN | BLE_GATT_CHR_F_NOTIFY, - }, - { - .uuid = &update_region_uuid.u, - .access_cb = update_region_callback, - .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN, - }, - { - 0, /* No more characteristics in this service. */ - }}, - }, - - { - 0, /* No more services. */ - }, -}; - -#endif //#ifndef USE_NEW_ESP32_BLUETOOTH \ No newline at end of file diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index caec3e446..b408daf98 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -6,6 +6,9 @@ // defaults for ESP32 architecture // +#ifndef HAS_BLUETOOTH + #define HAS_BLUETOOTH 1 +#endif #ifndef HAS_WIFI #define HAS_WIFI 1 #endif diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp index fce405a01..cd370e9ac 100644 --- a/src/platform/esp32/main-esp32.cpp +++ b/src/platform/esp32/main-esp32.cpp @@ -1,15 +1,10 @@ -#include "BluetoothSoftwareUpdate.h" #include "PowerFSM.h" #include "configuration.h" #include "esp_task_wdt.h" #include "main.h" -#ifdef USE_NEW_ESP32_BLUETOOTH -#include "ESP32Bluetooth.h" +#include "nimble/NimbleBluetooth.h" #include "mesh/http/WiFiAPClient.h" -#else -#include "nimble/BluetoothUtil.h" -#endif #include "sleep.h" #include "target_specific.h" @@ -19,41 +14,26 @@ #include #include -#ifdef USE_NEW_ESP32_BLUETOOTH -ESP32Bluetooth *esp32Bluetooth; -#endif +NimbleBluetooth *nimbleBluetooth; void getMacAddr(uint8_t *dmac) { assert(esp_efuse_mac_get_default(dmac) == ESP_OK); } -/* -static void printBLEinfo() { - int dev_num = esp_ble_get_bond_device_num(); - - esp_ble_bond_dev_t *dev_list = (esp_ble_bond_dev_t *)malloc(sizeof(esp_ble_bond_dev_t) * dev_num); - esp_ble_get_bond_device_list(&dev_num, dev_list); - for (int i = 0; i < dev_num; i++) { - // esp_ble_remove_bond_device(dev_list[i].bd_addr); - } - -} */ -#ifdef USE_NEW_ESP32_BLUETOOTH void setBluetoothEnable(bool on) { - if (!isWifiAvailable()) { - if (!esp32Bluetooth) { - esp32Bluetooth = new ESP32Bluetooth(); + if (!isWifiAvailable() && config.bluetooth.enabled == true) { + if (!nimbleBluetooth) { + nimbleBluetooth = new NimbleBluetooth(); } - if (on && !esp32Bluetooth->isActive()) { - esp32Bluetooth->setup(); + if (on && !nimbleBluetooth->isActive()) { + nimbleBluetooth->setup(); } else { - esp32Bluetooth->shutdown(); + nimbleBluetooth->shutdown(); } } } -#endif void esp32Setup() { @@ -123,7 +103,6 @@ Periodic axpDebugOutput(axpDebugRead); void esp32Loop() { esp_task_wdt_reset(); // service our app level watchdog - //loopBLE(); // for debug printing // radio.radioIf.canSleep(); diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index ede288b27..8a08c7448 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -1,6 +1,8 @@ +#include #include "configuration.h" #include "NRF52Bluetooth.h" #include "BluetoothCommon.h" +#include "PowerFSM.h" #include "main.h" #include "mesh/PhoneAPI.h" #include "mesh/mesh-pb-constants.h" @@ -22,7 +24,7 @@ static BLEDfu bledfu; // DFU software update helper service static uint8_t fromRadioBytes[FromRadio_size]; static uint8_t toRadioBytes[ToRadio_size]; -static bool bleConnected; +static uint16_t connectionHandle; class BluetoothPhoneAPI : public PhoneAPI { @@ -39,13 +41,14 @@ class BluetoothPhoneAPI : public PhoneAPI /// Check the current underlying physical link to see if the client is currently connected virtual bool checkIsConnected() override { - return bleConnected; + BLEConnection *connection = Bluefruit.Connection(connectionHandle); + return connection->connected(); } }; static BluetoothPhoneAPI *bluetoothPhoneAPI; -void connect_callback(uint16_t conn_handle) +void onConnect(uint16_t conn_handle) { // Get the reference to current connection BLEConnection *connection = Bluefruit.Connection(conn_handle); @@ -54,7 +57,7 @@ void connect_callback(uint16_t conn_handle) connection->getPeerName(central_name, sizeof(central_name)); DEBUG_MSG("BLE Connected to %s\n", central_name); - bleConnected = true; + // bluetoothPhoneAPI->setInitialState(); } /** @@ -62,15 +65,12 @@ void connect_callback(uint16_t conn_handle) * @param conn_handle connection where this event happens * @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h */ -void disconnect_callback(uint16_t conn_handle, uint8_t reason) +void onDisconnect(uint16_t conn_handle, uint8_t reason) { - // FIXME - we currently assume only one active connection - bleConnected = false; - DEBUG_MSG("BLE Disconnected, reason = 0x%x\n", reason); } -void cccd_callback(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value) +void onCCCD(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value) { // Display the raw request packet DEBUG_MSG("CCCD Updated: %u\n", cccd_value); @@ -126,7 +126,7 @@ static void authorizeRead(uint16_t conn_hdl) /** * client is starting read, pull the bytes from our API class */ -void fromRadioAuthorizeCb(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_read_t *request) +void onFromRadio(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_read_t *request) { if (request->offset == 0) { // If the read is long, we will get multiple authorize invocations - we only populate data on the first @@ -141,7 +141,7 @@ void fromRadioAuthorizeCb(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_e authorizeRead(conn_hdl); } -void toRadioWriteCb(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data, uint16_t len) +void onToRadio(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data, uint16_t len) { DEBUG_MSG("toRadioWriteCb data %p, len %u\n", data, len); @@ -151,7 +151,7 @@ void toRadioWriteCb(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data, ui /** * client is starting read, pull the bytes from our API class */ -void fromNumAuthorizeCb(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_read_t *request) +void onFromNumAuthorize(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_read_t *request) { DEBUG_MSG("fromNumAuthorizeCb\n"); @@ -168,39 +168,37 @@ void setupMeshService(void) // any characteristic(s) within that service definition.. Calling .begin() on // a BLECharacteristic will cause it to be added to the last BLEService that // was 'begin()'ed! - - fromNum.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ); - fromNum.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // FIXME, secure this!!! fromNum.setFixedLen(0); // Variable len (either 0 or 4) FIXME consider changing protocol so it is fixed 4 byte len, where 0 means empty fromNum.setMaxLen(4); - fromNum.setCccdWriteCallback(cccd_callback); // Optionally capture CCCD updates + fromNum.setCccdWriteCallback(onCCCD); // Optionally capture CCCD updates // We don't yet need to hook the fromNum auth callback // fromNum.setReadAuthorizeCallback(fromNumAuthorizeCb); fromNum.write32(0); // Provide default fromNum of 0 fromNum.begin(); fromRadio.setProperties(CHR_PROPS_READ); - fromRadio.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // FIXME secure this! + // fromRadio.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // FIXME secure this! fromRadio.setMaxLen(sizeof(fromRadioBytes)); fromRadio.setReadAuthorizeCallback( - fromRadioAuthorizeCb, + onFromRadio, false); // We don't call this callback via the adafruit queue, because we can safely run in the BLE context fromRadio.setBuffer(fromRadioBytes, sizeof(fromRadioBytes)); // we preallocate our fromradio buffer so we won't waste space // for two copies fromRadio.begin(); toRadio.setProperties(CHR_PROPS_WRITE); - toRadio.setPermission(SECMODE_OPEN, SECMODE_OPEN); // FIXME secure this! + // toRadio.setPermission(SECMODE_OPEN, SECMODE_OPEN); // FIXME secure this! toRadio.setFixedLen(0); toRadio.setMaxLen(512); toRadio.setBuffer(toRadioBytes, sizeof(toRadioBytes)); // We don't call this callback via the adafruit queue, because we can safely run in the BLE context - toRadio.setWriteCallback(toRadioWriteCb, false); + toRadio.setWriteCallback(onToRadio, false); toRadio.begin(); } // FIXME, turn off soft device access for debugging static bool isSoftDeviceAllowed = true; +static uint32_t configuredPasskey; void NRF52Bluetooth::shutdown() { @@ -211,9 +209,14 @@ void NRF52Bluetooth::shutdown() void NRF52Bluetooth::setup() { + // Uncomment for testing + // Bluefruit.Periph.clearBonds(); + // Bluefruit.Central.clearBonds(); + // Initialise the Bluefruit module DEBUG_MSG("Initialise the Bluefruit nRF52 module\n"); Bluefruit.autoConnLed(false); + Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); Bluefruit.begin(); // Clear existing data. @@ -221,12 +224,29 @@ void NRF52Bluetooth::setup() Bluefruit.Advertising.clearData(); Bluefruit.ScanResponse.clearData(); + if (config.bluetooth.mode != Config_BluetoothConfig_PairingMode_NoPin) { + configuredPasskey = config.bluetooth.mode == Config_BluetoothConfig_PairingMode_FixedPin ? + config.bluetooth.fixed_pin : random(100000, 999999); + auto pinString = std::to_string(configuredPasskey); + DEBUG_MSG("Bluetooth pin set to '%i'\n", configuredPasskey); + Bluefruit.Security.setPIN(pinString.c_str()); + Bluefruit.Security.setIOCaps(true, false, false); + Bluefruit.Security.setPairPasskeyCallback(NRF52Bluetooth::onPairingPasskey); + Bluefruit.Security.setPairCompleteCallback(NRF52Bluetooth::onPairingCompleted); + Bluefruit.Security.setSecuredCallback(NRF52Bluetooth::onConnectionSecured); + meshBleService.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM); + } + else { + Bluefruit.Security.setIOCaps(false, false, false); + meshBleService.setPermission(SECMODE_OPEN, SECMODE_OPEN); + } + // Set the advertised device name (keep it short!) Bluefruit.setName(getDeviceName()); // Set the connect/disconnect callback handlers - Bluefruit.Periph.setConnectCallback(connect_callback); - Bluefruit.Periph.setDisconnectCallback(disconnect_callback); + Bluefruit.Periph.setConnectCallback(onConnect); + Bluefruit.Periph.setDisconnectCallback(onDisconnect); // Configure and Start the Device Information Service DEBUG_MSG("Configuring the Device Information Service\n"); @@ -251,7 +271,6 @@ void NRF52Bluetooth::setup() // Setup the advertising packet(s) DEBUG_MSG("Setting up the advertising payload(s)\n"); startAdv(); - DEBUG_MSG("Advertising\n"); } } @@ -271,3 +290,41 @@ void NRF52Bluetooth::clearBonds() Bluefruit.Periph.clearBonds(); Bluefruit.Central.clearBonds(); } + +void NRF52Bluetooth::onConnectionSecured(uint16_t conn_handle) +{ + DEBUG_MSG("BLE connection secured\n"); + BLEConnection* connection = Bluefruit.Connection(conn_handle); + + if (!connection->secured()) + { + connection->requestPairing(); + } +} + +bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passkey[6], bool match_request) +{ + DEBUG_MSG("BLE pairing process started with passkey %.3s %.3s\n", passkey, passkey+3); + screen->startBluetoothPinScreen(configuredPasskey); + + if (match_request) + { + uint32_t start_time = millis(); + while(millis() < start_time + 30000) + { + if (!Bluefruit.connected(conn_handle)) break; + } + } + DEBUG_MSG("BLE passkey pairing: match_request=%i\n", match_request); + return true; +} + +void NRF52Bluetooth::onPairingCompleted(uint16_t conn_handle, uint8_t auth_status) +{ + if (auth_status == BLE_GAP_SEC_STATUS_SUCCESS) + DEBUG_MSG("BLE pairing success\n"); + else + DEBUG_MSG("BLE pairing failed\n"); + + screen->stopBluetoothPinScreen(); +} diff --git a/src/platform/nrf52/NRF52Bluetooth.h b/src/platform/nrf52/NRF52Bluetooth.h index d072cdfe7..025e9e4c8 100644 --- a/src/platform/nrf52/NRF52Bluetooth.h +++ b/src/platform/nrf52/NRF52Bluetooth.h @@ -6,5 +6,10 @@ class NRF52Bluetooth void setup(); void shutdown(); void clearBonds(); -}; + private: + static void onConnectionSecured(uint16_t conn_handle); + void convertToUint8(uint8_t target[4], uint32_t source); + static bool onPairingPasskey(uint16_t conn_handle, uint8_t const passkey[6], bool match_request); + static void onPairingCompleted(uint16_t conn_handle, uint8_t auth_status); +}; diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index 5ef37cc44..63e173434 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -5,7 +5,9 @@ // // defaults for NRF52 architecture // - +#ifndef HAS_BLUETOOTH + #define HAS_BLUETOOTH 1 +#endif #ifndef HAS_SCREEN #define HAS_SCREEN 1 #endif diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp index 668565650..589e4ae59 100644 --- a/src/platform/nrf52/main-nrf52.cpp +++ b/src/platform/nrf52/main-nrf52.cpp @@ -7,6 +7,7 @@ #include #include // #include +#include "NodeDB.h" #include "NRF52Bluetooth.h" #include "error.h" @@ -68,7 +69,7 @@ static const bool useSoftDevice = true; // Set to false for easier debugging void setBluetoothEnable(bool on) { - if (on != bleOn) { + if (on != bleOn && config.bluetooth.enabled == true) { if (on) { if (!nrf52Bluetooth) { if (!useSoftDevice) @@ -81,9 +82,8 @@ void setBluetoothEnable(bool on) initBrownout(); } } - } else { - if (nrf52Bluetooth) - nrf52Bluetooth->shutdown(); + } else if (nrf52Bluetooth) { + nrf52Bluetooth->shutdown(); } bleOn = on; } diff --git a/src/sleep.cpp b/src/sleep.cpp index 06e0fa82d..bd908357c 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -16,8 +16,6 @@ #include #include -#include "nimble/BluetoothUtil.h" - esp_sleep_source_t wakeCause; // the reason we booted this time #endif