From 9232dfcccf108b9487a59ebe12b11047b9e9fb3e Mon Sep 17 00:00:00 2001 From: geeksville Date: Mon, 20 Apr 2020 18:03:13 -0700 Subject: [PATCH 1/9] WIP - add new baseclass for all api endpoints (serial, bluetooth, udp) https://github.com/meshtastic/Meshtastic-esp32/issues/69 --- proto | 2 +- src/PhoneAPI.cpp | 53 +++++++++++++++++++++++++++++++++ src/PhoneAPI.h | 76 ++++++++++++++++++++++++++++++++++++++++++++++++ src/mesh.pb.c | 3 ++ src/mesh.pb.h | 53 +++++++++++++++++++++++++++++++-- 5 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 src/PhoneAPI.cpp create mode 100644 src/PhoneAPI.h diff --git a/proto b/proto index e06645d8d..0cef75501 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit e06645d8db16b9e4f23e74a931b8d5cd07bcbe3c +Subproject commit 0cef75501578a2c4adf63da09fdc34db20b3d862 diff --git a/src/PhoneAPI.cpp b/src/PhoneAPI.cpp new file mode 100644 index 000000000..f9c2d9406 --- /dev/null +++ b/src/PhoneAPI.cpp @@ -0,0 +1,53 @@ +#include "PhoneAPI.h" +#include + +PhoneAPI::PhoneAPI() +{ + // Make sure that we never let our packets grow too large for one BLE packet + assert(FromRadio_size <= 512); + assert(ToRadio_size <= 512); +} + +/** + * Handle a ToRadio protobuf + */ +void PhoneAPI::handleToRadio(const char *buf, size_t len) +{ + // FIXME +} + +/** + * Get the next packet we want to send to the phone, or NULL if no such packet is available. + * + * We assume buf is at least FromRadio_size bytes long. + */ +bool PhoneAPI::getFromRadio(char *buf) +{ + return false; // FIXME +} + +/** + * Return true if we have data available to send to the phone + */ +bool PhoneAPI::available() +{ + return true; // FIXME +} + +// +// The following routines are only public for now - until the rev1 bluetooth API is removed +// + +void PhoneAPI::handleSetOwner(const User &o) {} + +void PhoneAPI::handleSetRadio(const RadioConfig &r) {} + +/** + * The client wants to start a new set of config reads + */ +void PhoneAPI::handleWantConfig(uint32_t nonce) {} + +/** + * Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool + */ +void PhoneAPI::handleToRadioPacket(MeshPacket *p) {} \ No newline at end of file diff --git a/src/PhoneAPI.h b/src/PhoneAPI.h new file mode 100644 index 000000000..e27fab772 --- /dev/null +++ b/src/PhoneAPI.h @@ -0,0 +1,76 @@ +#pragma once + +#include "mesh-pb-constants.h" +#include "mesh.pb.h" +#include + +/** + * Provides our protobuf based API which phone/PC clients can use to talk to our device + * over UDP, bluetooth or serial. + * + * Eventually there should be once instance of this class for each live connection (because it has a bit of state + * for that connection) + */ +class PhoneAPI +{ + enum State { + STATE_SEND_NOTHING, // Initial state, don't send anything until the client starts asking for config + STATE_SEND_MY_NODEINFO, + STATE_SEND_OWNER, + STATE_SEND_RADIO, + STATE_SEND_COMPLETE_ID, + STATE_SEND_PACKETS // send packets or debug strings + }; + + State state = STATE_SEND_NOTHING; + + /** + * Each packet sent to the phone has an incrementing count + */ + uint32_t fromRadioNum = 0; + + public: + PhoneAPI(); + + /** + * Handle a ToRadio protobuf + */ + void handleToRadio(const char *buf, size_t len); + + /** + * Get the next packet we want to send to the phone, or NULL if no such packet is available. + * + * We assume buf is at least FromRadio_size bytes long. + */ + bool getFromRadio(char *buf); + + /** + * Return true if we have data available to send to the phone + */ + bool available(); + + // + // The following routines are only public for now - until the rev1 bluetooth API is removed + // + + void handleSetOwner(const User &o); + void handleSetRadio(const RadioConfig &r); + + protected: + + /** + * Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies) + */ + void onNowHasData(uint32_t fromRadioNum) {} + + private: + /** + * The client wants to start a new set of config reads + */ + void handleWantConfig(uint32_t nonce); + + /** + * Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool + */ + void handleToRadioPacket(MeshPacket *p); +}; diff --git a/src/mesh.pb.c b/src/mesh.pb.c index b4e21ebde..096f28896 100644 --- a/src/mesh.pb.c +++ b/src/mesh.pb.c @@ -42,6 +42,9 @@ PB_BIND(MyNodeInfo, MyNodeInfo, AUTO) PB_BIND(DeviceState, DeviceState, 4) +PB_BIND(DebugString, DebugString, 2) + + PB_BIND(FromRadio, FromRadio, 2) diff --git a/src/mesh.pb.h b/src/mesh.pb.h index dda10e710..67ca16227 100644 --- a/src/mesh.pb.h +++ b/src/mesh.pb.h @@ -49,6 +49,10 @@ typedef struct _Data { Data_payload_t payload; } Data; +typedef struct _DebugString { + char message[256]; +} DebugString; + typedef struct _MyNodeInfo { int32_t my_node_num; bool has_gps; @@ -150,6 +154,12 @@ typedef struct _FromRadio { pb_size_t which_variant; union { MeshPacket packet; + MyNodeInfo my_info; + NodeInfo node_info; + User owner; + RadioConfig radio; + DebugString debug_string; + uint32_t config_complete_id; } variant; } FromRadio; @@ -157,6 +167,9 @@ typedef struct _ToRadio { pb_size_t which_variant; union { MeshPacket packet; + uint32_t want_config_id; + RadioConfig set_radio; + User set_owner; } variant; } ToRadio; @@ -188,6 +201,7 @@ typedef struct _ToRadio { #define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0} #define MyNodeInfo_init_default {0, 0, 0, "", "", "", 0, 0, 0} #define DeviceState_init_default {false, RadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default}, false, MeshPacket_init_default, 0} +#define DebugString_init_default {""} #define FromRadio_init_default {0, 0, {MeshPacket_init_default}} #define ToRadio_init_default {0, {MeshPacket_init_default}} #define Position_init_zero {0, 0, 0, 0, 0} @@ -202,6 +216,7 @@ typedef struct _ToRadio { #define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0} #define MyNodeInfo_init_zero {0, 0, 0, "", "", "", 0, 0, 0} #define DeviceState_init_zero {false, RadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero}, false, MeshPacket_init_zero, 0} +#define DebugString_init_zero {""} #define FromRadio_init_zero {0, 0, {MeshPacket_init_zero}} #define ToRadio_init_zero {0, {MeshPacket_init_zero}} @@ -213,6 +228,7 @@ typedef struct _ToRadio { #define ChannelSettings_name_tag 5 #define Data_typ_tag 1 #define Data_payload_tag 2 +#define DebugString_message_tag 1 #define MyNodeInfo_my_node_num_tag 1 #define MyNodeInfo_has_gps_tag 2 #define MyNodeInfo_num_channels_tag 3 @@ -269,8 +285,17 @@ typedef struct _ToRadio { #define DeviceState_version_tag 8 #define DeviceState_rx_text_message_tag 7 #define FromRadio_packet_tag 2 +#define FromRadio_my_info_tag 3 +#define FromRadio_node_info_tag 4 +#define FromRadio_owner_tag 5 +#define FromRadio_radio_tag 6 +#define FromRadio_debug_string_tag 7 +#define FromRadio_config_complete_id_tag 8 #define FromRadio_num_tag 1 #define ToRadio_packet_tag 1 +#define ToRadio_want_config_id_tag 100 +#define ToRadio_set_radio_tag 101 +#define ToRadio_set_owner_tag 102 /* Struct field encoding specification for nanopb */ #define Position_FIELDLIST(X, a) \ @@ -397,18 +422,39 @@ X(a, STATIC, SINGULAR, UINT32, version, 8) #define DeviceState_receive_queue_MSGTYPE MeshPacket #define DeviceState_rx_text_message_MSGTYPE MeshPacket +#define DebugString_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, message, 1) +#define DebugString_CALLBACK NULL +#define DebugString_DEFAULT NULL + #define FromRadio_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, num, 1) \ -X(a, STATIC, ONEOF, MESSAGE, (variant,packet,variant.packet), 2) +X(a, STATIC, ONEOF, MESSAGE, (variant,packet,variant.packet), 2) \ +X(a, STATIC, ONEOF, MESSAGE, (variant,my_info,variant.my_info), 3) \ +X(a, STATIC, ONEOF, MESSAGE, (variant,node_info,variant.node_info), 4) \ +X(a, STATIC, ONEOF, MESSAGE, (variant,owner,variant.owner), 5) \ +X(a, STATIC, ONEOF, MESSAGE, (variant,radio,variant.radio), 6) \ +X(a, STATIC, ONEOF, MESSAGE, (variant,debug_string,variant.debug_string), 7) \ +X(a, STATIC, ONEOF, UINT32, (variant,config_complete_id,variant.config_complete_id), 8) #define FromRadio_CALLBACK NULL #define FromRadio_DEFAULT NULL #define FromRadio_variant_packet_MSGTYPE MeshPacket +#define FromRadio_variant_my_info_MSGTYPE MyNodeInfo +#define FromRadio_variant_node_info_MSGTYPE NodeInfo +#define FromRadio_variant_owner_MSGTYPE User +#define FromRadio_variant_radio_MSGTYPE RadioConfig +#define FromRadio_variant_debug_string_MSGTYPE DebugString #define ToRadio_FIELDLIST(X, a) \ -X(a, STATIC, ONEOF, MESSAGE, (variant,packet,variant.packet), 1) +X(a, STATIC, ONEOF, MESSAGE, (variant,packet,variant.packet), 1) \ +X(a, STATIC, ONEOF, UINT32, (variant,want_config_id,variant.want_config_id), 100) \ +X(a, STATIC, ONEOF, MESSAGE, (variant,set_radio,variant.set_radio), 101) \ +X(a, STATIC, ONEOF, MESSAGE, (variant,set_owner,variant.set_owner), 102) #define ToRadio_CALLBACK NULL #define ToRadio_DEFAULT NULL #define ToRadio_variant_packet_MSGTYPE MeshPacket +#define ToRadio_variant_set_radio_MSGTYPE RadioConfig +#define ToRadio_variant_set_owner_MSGTYPE User extern const pb_msgdesc_t Position_msg; extern const pb_msgdesc_t Data_msg; @@ -422,6 +468,7 @@ extern const pb_msgdesc_t RadioConfig_UserPreferences_msg; extern const pb_msgdesc_t NodeInfo_msg; extern const pb_msgdesc_t MyNodeInfo_msg; extern const pb_msgdesc_t DeviceState_msg; +extern const pb_msgdesc_t DebugString_msg; extern const pb_msgdesc_t FromRadio_msg; extern const pb_msgdesc_t ToRadio_msg; @@ -438,6 +485,7 @@ extern const pb_msgdesc_t ToRadio_msg; #define NodeInfo_fields &NodeInfo_msg #define MyNodeInfo_fields &MyNodeInfo_msg #define DeviceState_fields &DeviceState_msg +#define DebugString_fields &DebugString_msg #define FromRadio_fields &FromRadio_msg #define ToRadio_fields &ToRadio_msg @@ -454,6 +502,7 @@ extern const pb_msgdesc_t ToRadio_msg; #define NodeInfo_size 155 #define MyNodeInfo_size 85 #define DeviceState_size 19502 +#define DebugString_size 258 #define FromRadio_size 435 #define ToRadio_size 429 From 31f735ae1fb0e81150060a3effa4bcddc409dfae Mon Sep 17 00:00:00 2001 From: geeksville Date: Mon, 20 Apr 2020 19:30:41 -0700 Subject: [PATCH 2/9] minor status update --- docs/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 65d616bce..e3bbff93b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -36,8 +36,9 @@ This software is 100% open source and developed by a group of hobbyist experimen # Updates -Note: Updates are happening almost daily, only major updates are listed below. For more details see our chat, github releases or the Android alpha tester emails. +Note: Updates are happening almost daily, only major updates are listed below. For more details see our forum. +- 04/20/2020 - 0.4.3 Pretty solid now both for the android app and the device code. Many people have donated translations and code. Probably going to call it a beta soon. - 03/03/2020 - 0.0.9 of the Android app and device code is released. Still an alpha but fairly functional. - 02/25/2020 - 0.0.4 of the Android app is released. This is a very early alpha, see below to join the alpha-testers group. - 02/23/2020 - 0.0.4 release. Still very bleeding edge but much closer to the final power management, a charged T-BEAM should run for many days with this load. If you'd like to try it, we'd love your feedback. Click [here](https://github.com/meshtastic/Meshtastic-esp32/blob/master/README.md) for instructions. From e40524baf049cd8fa39bf43c2c85fe4dabb9df26 Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 22 Apr 2020 14:55:36 -0700 Subject: [PATCH 3/9] begin moving comms glue from the old crufty BLE code to the new cleaner PhoneAPI class --- proto | 2 +- src/MeshService.cpp | 53 +++++++----------- src/MeshService.h | 8 ++- src/PhoneAPI.cpp | 87 ++++++++++++++++++++++++++--- src/PhoneAPI.h | 28 ++++++++-- src/esp32/MeshBluetoothService.cpp | 90 +++++++++++++----------------- 6 files changed, 168 insertions(+), 100 deletions(-) diff --git a/proto b/proto index 0cef75501..083ba7931 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 0cef75501578a2c4adf63da09fdc34db20b3d862 +Subproject commit 083ba793108c34044e6abc8c94a5f250343b4f32 diff --git a/src/MeshService.cpp b/src/MeshService.cpp index 4331f939f..d82f46b55 100644 --- a/src/MeshService.cpp +++ b/src/MeshService.cpp @@ -214,46 +214,33 @@ void MeshService::reloadConfig() nodeDB.saveToDisk(); } -/// Given a ToRadio buffer parse it and properly handle it (setup radio, owner or send packet into the mesh) -void MeshService::handleToRadio(std::string s) +/** + * Given a ToRadio buffer parse it and properly handle it (setup radio, owner or send packet into the mesh) + * Called by PhoneAPI.handleToRadio. Note: p is a scratch buffer, this function is allowed to write to it but it can not keep a reference + */ +void MeshService::handleToRadio(MeshPacket &p) { - static ToRadio r; // this is a static scratch object, any data must be copied elsewhere before returning + handleIncomingPosition(&p); // If it is a position packet, perhaps set our clock - if (pb_decode_from_bytes((const uint8_t *)s.c_str(), s.length(), ToRadio_fields, &r)) { - switch (r.which_variant) { - case ToRadio_packet_tag: { - // If our phone is sending a position, see if we can use it to set our RTC - MeshPacket &p = r.variant.packet; - handleIncomingPosition(&p); // If it is a position packet, perhaps set our clock + if (p.from == 0) // If the phone didn't set a sending node ID, use ours + p.from = nodeDB.getNodeNum(); - if (p.from == 0) // If the phone didn't set a sending node ID, use ours - p.from = nodeDB.getNodeNum(); + if (p.id == 0) + p.id = generatePacketId(); // If the phone didn't supply one, then pick one - if (p.id == 0) - p.id = generatePacketId(); // If the phone didn't supply one, then pick one + p.rx_time = gps.getValidTime(); // Record the time the packet arrived from the phone + // (so we update our nodedb for the local node) - p.rx_time = gps.getValidTime(); // Record the time the packet arrived from the phone - // (so we update our nodedb for the local node) + // Send the packet into the mesh - // Send the packet into the mesh + sendToMesh(packetPool.allocCopy(p)); - sendToMesh(packetPool.allocCopy(p)); - - bool loopback = false; // if true send any packet the phone sends back itself (for testing) - if (loopback) { - // no need to copy anymore because handle from radio assumes it should _not_ delete - // packetPool.allocCopy(r.variant.packet); - handleFromRadio(&p); - // handleFromRadio will tell the phone a new packet arrived - } - break; - } - default: - DEBUG_MSG("Error: unexpected ToRadio variant\n"); - break; - } - } else { - DEBUG_MSG("Error: ignoring malformed toradio\n"); + bool loopback = false; // if true send any packet the phone sends back itself (for testing) + if (loopback) { + // no need to copy anymore because handle from radio assumes it should _not_ delete + // packetPool.allocCopy(r.variant.packet); + handleFromRadio(&p); + // handleFromRadio will tell the phone a new packet arrived } } diff --git a/src/MeshService.h b/src/MeshService.h index f1e45d0bb..f3328225b 100644 --- a/src/MeshService.h +++ b/src/MeshService.h @@ -54,8 +54,12 @@ class MeshService /// Allows the bluetooth handler to free packets after they have been sent void releaseToPool(MeshPacket *p) { packetPool.release(p); } - /// Given a ToRadio buffer (from bluetooth) parse it and properly handle it (setup radio, owner or send packet into the mesh) - void handleToRadio(std::string s); + /** + * Given a ToRadio buffer parse it and properly handle it (setup radio, owner or send packet into the mesh) + * Called by PhoneAPI.handleToRadio. Note: p is a scratch buffer, this function is allowed to write to it but it can not keep + * a reference + */ + void handleToRadio(MeshPacket &p); /// The radioConfig object just changed, call this to force the hw to change to the new settings void reloadConfig(); diff --git a/src/PhoneAPI.cpp b/src/PhoneAPI.cpp index f9c2d9406..66524b2cf 100644 --- a/src/PhoneAPI.cpp +++ b/src/PhoneAPI.cpp @@ -1,4 +1,6 @@ #include "PhoneAPI.h" +#include "MeshService.h" +#include "NodeDB.h" #include PhoneAPI::PhoneAPI() @@ -8,12 +10,31 @@ PhoneAPI::PhoneAPI() assert(ToRadio_size <= 512); } +void PhoneAPI::init() +{ + observe(&service.fromNumChanged); +} + /** * Handle a ToRadio protobuf */ -void PhoneAPI::handleToRadio(const char *buf, size_t len) +void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) { - // FIXME + if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) { + switch (toRadioScratch.which_variant) { + case ToRadio_packet_tag: { + // If our phone is sending a position, see if we can use it to set our RTC + MeshPacket &p = toRadioScratch.variant.packet; + service.handleToRadio(p); + break; + } + default: + DEBUG_MSG("Error: unexpected ToRadio variant\n"); + break; + } + } else { + DEBUG_MSG("Error: ignoring malformed toradio\n"); + } } /** @@ -21,9 +42,28 @@ void PhoneAPI::handleToRadio(const char *buf, size_t len) * * We assume buf is at least FromRadio_size bytes long. */ -bool PhoneAPI::getFromRadio(char *buf) +size_t PhoneAPI::getFromRadio(uint8_t *buf) { - return false; // FIXME + if (!available()) + return false; + + // Do we have a message from the mesh? + if (packetForPhone) { + // Encapsulate as a FromRadio packet + memset(&fromRadioScratch, 0, sizeof(fromRadioScratch)); + fromRadioScratch.which_variant = FromRadio_packet_tag; + fromRadioScratch.variant.packet = *packetForPhone; + + size_t numbytes = pb_encode_to_bytes(buf, sizeof(FromRadio_size), FromRadio_fields, &fromRadioScratch); + DEBUG_MSG("delivering toPhone packet to phone %d bytes\n", numbytes); + + service.releaseToPool(packetForPhone); // we just copied the bytes, so don't need this buffer anymore + packetForPhone = NULL; + return numbytes; + } + + DEBUG_MSG("toPhone queue is empty\n"); + return 0; } /** @@ -31,6 +71,8 @@ bool PhoneAPI::getFromRadio(char *buf) */ bool PhoneAPI::available() { + packetForPhone = service.getForPhone(); + return true; // FIXME } @@ -38,9 +80,33 @@ bool PhoneAPI::available() // The following routines are only public for now - until the rev1 bluetooth API is removed // -void PhoneAPI::handleSetOwner(const User &o) {} +void PhoneAPI::handleSetOwner(const User &o) +{ + int changed = 0; -void PhoneAPI::handleSetRadio(const RadioConfig &r) {} + if (*o.long_name) { + changed |= strcmp(owner.long_name, o.long_name); + strcpy(owner.long_name, o.long_name); + } + if (*o.short_name) { + changed |= strcmp(owner.short_name, o.short_name); + strcpy(owner.short_name, o.short_name); + } + if (*o.id) { + changed |= strcmp(owner.id, o.id); + strcpy(owner.id, o.id); + } + + if (changed) // If nothing really changed, don't broadcast on the network or write to flash + service.reloadOwner(); +} + +void PhoneAPI::handleSetRadio(const RadioConfig &r) +{ + radioConfig = r; + + service.reloadConfig(); +} /** * The client wants to start a new set of config reads @@ -50,4 +116,11 @@ void PhoneAPI::handleWantConfig(uint32_t nonce) {} /** * Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool */ -void PhoneAPI::handleToRadioPacket(MeshPacket *p) {} \ No newline at end of file +void PhoneAPI::handleToRadioPacket(MeshPacket *p) {} + +/// If the mesh service tells us fromNum has changed, tell the phone +int PhoneAPI::onNotify(uint32_t newValue) +{ + onNowHasData(newValue); + return 0; +} \ No newline at end of file diff --git a/src/PhoneAPI.h b/src/PhoneAPI.h index e27fab772..d8c129cce 100644 --- a/src/PhoneAPI.h +++ b/src/PhoneAPI.h @@ -1,5 +1,6 @@ #pragma once +#include "Observer.h" #include "mesh-pb-constants.h" #include "mesh.pb.h" #include @@ -8,10 +9,13 @@ * Provides our protobuf based API which phone/PC clients can use to talk to our device * over UDP, bluetooth or serial. * + * Subclass to customize behavior for particular type of transport (BLE, UDP, TCP, serial) + * * Eventually there should be once instance of this class for each live connection (because it has a bit of state * for that connection) */ class PhoneAPI + : public Observer // FIXME, we shouldn't be inheriting from Observer, instead use CallbackObserver as a member { enum State { STATE_SEND_NOTHING, // Initial state, don't send anything until the client starts asking for config @@ -27,22 +31,34 @@ class PhoneAPI /** * Each packet sent to the phone has an incrementing count */ - uint32_t fromRadioNum = 0; + uint32_t fromRadioNum = 0; + + /// We temporarily keep the packet here between the call to available and getFromRadio + MeshPacket *packetForPhone = NULL; + + /// Our fromradio packet while it is being assembled + FromRadio fromRadioScratch; + + ToRadio toRadioScratch; // this is a static scratch object, any data must be copied elsewhere before returning public: PhoneAPI(); + /// Do late init that can't happen at constructor time + void init(); + /** * Handle a ToRadio protobuf */ - void handleToRadio(const char *buf, size_t len); + void handleToRadio(const uint8_t *buf, size_t len); /** - * Get the next packet we want to send to the phone, or NULL if no such packet is available. + * Get the next packet we want to send to the phone * * We assume buf is at least FromRadio_size bytes long. + * Returns number of bytes in the FromRadio packet (or 0 if no packet available) */ - bool getFromRadio(char *buf); + size_t getFromRadio(uint8_t *buf); /** * Return true if we have data available to send to the phone @@ -57,7 +73,6 @@ class PhoneAPI void handleSetRadio(const RadioConfig &r); protected: - /** * Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies) */ @@ -73,4 +88,7 @@ class PhoneAPI * Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool */ void handleToRadioPacket(MeshPacket *p); + + /// If the mesh service tells us fromNum has changed, tell the phone + virtual int onNotify(uint32_t newValue); }; diff --git a/src/esp32/MeshBluetoothService.cpp b/src/esp32/MeshBluetoothService.cpp index 086f0afe6..cadf60121 100644 --- a/src/esp32/MeshBluetoothService.cpp +++ b/src/esp32/MeshBluetoothService.cpp @@ -6,19 +6,41 @@ #include #include "CallbackCharacteristic.h" +#include "GPS.h" #include "MeshService.h" #include "NodeDB.h" +#include "PhoneAPI.h" #include "PowerFSM.h" #include "configuration.h" #include "mesh-pb-constants.h" #include "mesh.pb.h" -#include "GPS.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[_max(_max(_max(_max(ToRadio_size, RadioConfig_size), User_size), MyNodeInfo_size), FromRadio_size)]; +static CallbackCharacteristic *meshFromNumCharacteristic; + +BLEService *meshService; + +class BluetoothPhoneAPI : public PhoneAPI +{ + /** + * Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies) + */ + virtual void onNowHasData(uint32_t fromRadioNum) + { + PhoneAPI::onNowHasData(fromRadioNum); + + if (meshFromNumCharacteristic) { // this ptr might change from sleep to sleep, or even be null + meshFromNumCharacteristic->setValue(fromRadioNum); + meshFromNumCharacteristic->notify(); + } + } +}; + +BluetoothPhoneAPI *bluetoothPhoneAPI; + class ProtobufCharacteristic : public CallbackCharacteristic { const pb_msgdesc_t *fields; @@ -114,7 +136,7 @@ class RadioCharacteristic : public ProtobufCharacteristic { DEBUG_MSG("Writing radio config\n"); ProtobufCharacteristic::onWrite(c); - service.reloadConfig(); + bluetoothPhoneAPI->handleSetRadio(radioConfig); } }; @@ -135,23 +157,7 @@ class OwnerCharacteristic : public ProtobufCharacteristic static User o; // if the phone doesn't set ID we are careful to keep ours, we also always keep our macaddr if (writeToDest(c, &o)) { - int changed = 0; - - if (*o.long_name) { - changed |= strcmp(owner.long_name, o.long_name); - strcpy(owner.long_name, o.long_name); - } - if (*o.short_name) { - changed |= strcmp(owner.short_name, o.short_name); - strcpy(owner.short_name, o.short_name); - } - if (*o.id) { - changed |= strcmp(owner.id, o.id); - strcpy(owner.id, o.id); - } - - if (changed) // If nothing really changed, don't broadcast on the network or write to flash - service.reloadOwner(); + bluetoothPhoneAPI->handleSetOwner(o); } } }; @@ -166,7 +172,7 @@ class ToRadioCharacteristic : public CallbackCharacteristic BLEKeepAliveCallbacks::onWrite(c); DEBUG_MSG("Got on write\n"); - service.handleToRadio(c->getValue()); + bluetoothPhoneAPI->handleToRadio(c->getData(), c->getValue().length()); } }; @@ -180,31 +186,17 @@ class FromRadioCharacteristic : public CallbackCharacteristic void onRead(BLECharacteristic *c) { BLEKeepAliveCallbacks::onRead(c); - MeshPacket *mp = service.getForPhone(); + size_t numBytes = bluetoothPhoneAPI->getFromRadio(trBytes); // 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 - if (!mp) { - DEBUG_MSG("toPhone queue is empty\n"); - c->setValue((uint8_t *)"", 0); - } else { - static FromRadio fRadio; - - // Encapsulate as a FromRadio packet - memset(&fRadio, 0, sizeof(fRadio)); - fRadio.which_variant = FromRadio_packet_tag; - fRadio.variant.packet = *mp; - - size_t numbytes = pb_encode_to_bytes(trBytes, sizeof(trBytes), FromRadio_fields, &fRadio); - DEBUG_MSG("delivering toPhone packet to phone %d bytes\n", numbytes); - c->setValue(trBytes, numbytes); - - service.releaseToPool(mp); // we just copied the bytes, so don't need this buffer anymore + if (numBytes) { + c->setValue(trBytes, numBytes); } } }; -class FromNumCharacteristic : public CallbackCharacteristic, public Observer +class FromNumCharacteristic : public CallbackCharacteristic { public: FromNumCharacteristic() @@ -212,7 +204,7 @@ class FromNumCharacteristic : public CallbackCharacteristic, public Observerinit(); + } + // Create the BLE Service, we need more than the default of 15 handles BLEService *service = server->createService(BLEUUID("6ba1b218-15a8-461f-9fa8-5dcae273eafd"), 30, 0); From bd77d47215acc4173ddd88ca7c3470b40c2f0f89 Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 22 Apr 2020 14:58:35 -0700 Subject: [PATCH 4/9] change serial baud rate to 921600 --- bin/start-terminal0.sh | 2 +- bin/start-terminal1.sh | 2 +- platformio.ini | 2 +- src/configuration.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/start-terminal0.sh b/bin/start-terminal0.sh index 895fb94b5..3ad4e7822 100755 --- a/bin/start-terminal0.sh +++ b/bin/start-terminal0.sh @@ -1 +1 @@ -pio device monitor -b 115200 +pio device monitor -b 921600 diff --git a/bin/start-terminal1.sh b/bin/start-terminal1.sh index c5236bbe3..3433501ea 100755 --- a/bin/start-terminal1.sh +++ b/bin/start-terminal1.sh @@ -1 +1 @@ -pio device monitor -p /dev/ttyUSB1 -b 115200 +pio device monitor -p /dev/ttyUSB1 -b 921600 diff --git a/platformio.ini b/platformio.ini index fbfb3a459..d4b105129 100644 --- a/platformio.ini +++ b/platformio.ini @@ -51,7 +51,7 @@ build_flags = -Wno-missing-field-initializers -Isrc -Isrc/rf95 -Os -Wl,-Map,.pio ; the default is esptool ; upload_protocol = esp-prog -monitor_speed = 115200 +monitor_speed = 921600 # debug_tool = esp-prog # debug_port = /dev/ttyACM0 diff --git a/src/configuration.h b/src/configuration.h index 4ef859aa3..733e40a76 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -47,7 +47,7 @@ along with this program. If not, see . #endif #define DEBUG_PORT Serial // Serial debug port -#define SERIAL_BAUD 115200 // Serial debug baud rate +#define SERIAL_BAUD 921600 // Serial debug baud rate #define REQUIRE_RADIO true // If true, we will fail to start if the radio is not found From 169d85d0fa2a1506c2f106da14a36f84b8ae2685 Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 22 Apr 2020 15:13:05 -0700 Subject: [PATCH 5/9] handle the new set_owner and set_radio messages --- src/PhoneAPI.cpp | 16 ++++++++++++++++ src/PhoneAPI.h | 3 +++ 2 files changed, 19 insertions(+) diff --git a/src/PhoneAPI.cpp b/src/PhoneAPI.cpp index 66524b2cf..18a05228e 100644 --- a/src/PhoneAPI.cpp +++ b/src/PhoneAPI.cpp @@ -28,6 +28,22 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) service.handleToRadio(p); break; } + case ToRadio_want_config_id_tag: + config_nonce = toRadioScratch.variant.want_config_id; + DEBUG_MSG("Client wants config, nonce=%u\n", config_nonce); + state = STATE_SEND_MY_NODEINFO; + break; + + case ToRadio_set_owner_tag: + DEBUG_MSG("Client is setting owner\n"); + handleSetOwner(toRadioScratch.variant.set_owner); + break; + + case ToRadio_set_radio_tag: + DEBUG_MSG("Client is setting radio\n"); + handleSetRadio(toRadioScratch.variant.set_radio); + break; + default: DEBUG_MSG("Error: unexpected ToRadio variant\n"); break; diff --git a/src/PhoneAPI.h b/src/PhoneAPI.h index d8c129cce..3439730fa 100644 --- a/src/PhoneAPI.h +++ b/src/PhoneAPI.h @@ -41,6 +41,9 @@ class PhoneAPI ToRadio toRadioScratch; // this is a static scratch object, any data must be copied elsewhere before returning + /// Use to ensure that clients don't get confused about old messages from the radio + uint32_t config_nonce = 0; + public: PhoneAPI(); From 562b227c737dc345624037657f409d4f3072b6a1 Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 22 Apr 2020 16:03:54 -0700 Subject: [PATCH 6/9] new API now seems fully implemented - now on to testing. #69 --- src/PhoneAPI.cpp | 138 ++++++++++++++++++++++++++++++++++++++++------- src/PhoneAPI.h | 15 +++--- src/error.h | 2 +- 3 files changed, 127 insertions(+), 28 deletions(-) diff --git a/src/PhoneAPI.cpp b/src/PhoneAPI.cpp index 18a05228e..5649d9165 100644 --- a/src/PhoneAPI.cpp +++ b/src/PhoneAPI.cpp @@ -31,7 +31,11 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) case ToRadio_want_config_id_tag: config_nonce = toRadioScratch.variant.want_config_id; DEBUG_MSG("Client wants config, nonce=%u\n", config_nonce); - state = STATE_SEND_MY_NODEINFO; + state = STATE_SEND_MY_INFO; + + DEBUG_MSG("Reset nodeinfo read pointer\n"); + nodeDB.resetReadPointer(); // FIXME, this read pointer should be moved out of nodeDB and into this class - because + // this will break once we have multiple instances of PhoneAPI running independently break; case ToRadio_set_owner_tag: @@ -57,28 +61,97 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) * Get the next packet we want to send to the phone, or NULL if no such packet is available. * * We assume buf is at least FromRadio_size bytes long. + * + * Our sending states progress in the following sequence: + * STATE_SEND_MY_INFO, // send our my info record + STATE_SEND_RADIO, + STATE_SEND_OWNER, + STATE_SEND_NODEINFO, // states progress in this order as the device sends to to the client + STATE_SEND_COMPLETE_ID, + STATE_SEND_PACKETS // send packets or debug strings */ size_t PhoneAPI::getFromRadio(uint8_t *buf) { if (!available()) return false; + // In case we send a FromRadio packet + memset(&fromRadioScratch, 0, sizeof(fromRadioScratch)); + + // Advance states as needed + switch (state) { + case STATE_SEND_NOTHING: + break; + + case STATE_SEND_MY_INFO: + fromRadioScratch.which_variant = FromRadio_my_info_tag; + fromRadioScratch.variant.my_info = myNodeInfo; + state = STATE_SEND_RADIO; + break; + + case STATE_SEND_RADIO: + fromRadioScratch.which_variant = FromRadio_radio_tag; + fromRadioScratch.variant.radio = radioConfig; + state = STATE_SEND_OWNER; + break; + + case STATE_SEND_OWNER: + fromRadioScratch.which_variant = FromRadio_owner_tag; + fromRadioScratch.variant.owner = owner; + state = STATE_SEND_NODEINFO; + break; + + case STATE_SEND_NODEINFO: { + const NodeInfo *info = nodeDB.readNextInfo(); + + if (info) { + DEBUG_MSG("Sending nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", info->num, info->position.time, info->user.id, + info->user.long_name); + fromRadioScratch.which_variant = FromRadio_node_info_tag; + fromRadioScratch.variant.node_info = *info; + // Stay in current state until done sending nodeinfos + } else { + DEBUG_MSG("Done sending nodeinfos\n"); + state = STATE_SEND_COMPLETE_ID; + // Go ahead and send that ID right now + return getFromRadio(buf); + } + break; + } + + case STATE_SEND_COMPLETE_ID: + fromRadioScratch.which_variant = FromRadio_config_complete_id_tag; + fromRadioScratch.variant.config_complete_id = config_nonce; + config_nonce = 0; + state = STATE_SEND_PACKETS; + break; + + case STATE_LEGACY: // Treat as the same as send packets + case STATE_SEND_PACKETS: + // Do we have a message from the mesh? + if (packetForPhone) { + // Encapsulate as a FromRadio packet + fromRadioScratch.which_variant = FromRadio_packet_tag; + fromRadioScratch.variant.packet = *packetForPhone; + + service.releaseToPool(packetForPhone); // we just copied the bytes, so don't need this buffer anymore + packetForPhone = NULL; + } + break; + + default: + assert(0); // unexpected state - FIXME, make an error code and reboot + } + // Do we have a message from the mesh? - if (packetForPhone) { + if (fromRadioScratch.which_variant != 0) { // Encapsulate as a FromRadio packet - memset(&fromRadioScratch, 0, sizeof(fromRadioScratch)); - fromRadioScratch.which_variant = FromRadio_packet_tag; - fromRadioScratch.variant.packet = *packetForPhone; - size_t numbytes = pb_encode_to_bytes(buf, sizeof(FromRadio_size), FromRadio_fields, &fromRadioScratch); - DEBUG_MSG("delivering toPhone packet to phone %d bytes\n", numbytes); - - service.releaseToPool(packetForPhone); // we just copied the bytes, so don't need this buffer anymore - packetForPhone = NULL; + DEBUG_MSG("delivering toPhone packet to phone variant=%d, %d bytes\n", fromRadioScratch.which_variant, numbytes); return numbytes; } - DEBUG_MSG("toPhone queue is empty\n"); + DEBUG_MSG("no FromRadio packet available\n"); return 0; } @@ -87,9 +160,37 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) */ bool PhoneAPI::available() { - packetForPhone = service.getForPhone(); + switch (state) { + case STATE_SEND_NOTHING: + return false; - return true; // FIXME + case STATE_SEND_MY_INFO: + return true; + + case STATE_SEND_NODEINFO: + return true; + + case STATE_SEND_OWNER: + return true; + + case STATE_SEND_RADIO: + return true; + + case STATE_SEND_COMPLETE_ID: + return true; + + case STATE_LEGACY: // Treat as the same as send packets + case STATE_SEND_PACKETS: + // Try to pull a new packet from the service (if we haven't already) + if (!packetForPhone) + packetForPhone = service.getForPhone(); + return !!packetForPhone; + + default: + assert(0); // unexpected state - FIXME, make an error code and reboot + } + + return false; } // @@ -124,10 +225,6 @@ void PhoneAPI::handleSetRadio(const RadioConfig &r) service.reloadConfig(); } -/** - * The client wants to start a new set of config reads - */ -void PhoneAPI::handleWantConfig(uint32_t nonce) {} /** * Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool @@ -137,6 +234,11 @@ void PhoneAPI::handleToRadioPacket(MeshPacket *p) {} /// If the mesh service tells us fromNum has changed, tell the phone int PhoneAPI::onNotify(uint32_t newValue) { - onNowHasData(newValue); + if (state == STATE_SEND_PACKETS || state == STATE_LEGACY) { + DEBUG_MSG("Telling client we have new packets %u\n", newValue); + onNowHasData(newValue); + } else + DEBUG_MSG("(Client not yet interested in packets)\n"); + return 0; } \ No newline at end of file diff --git a/src/PhoneAPI.h b/src/PhoneAPI.h index 3439730fa..8f9af78df 100644 --- a/src/PhoneAPI.h +++ b/src/PhoneAPI.h @@ -18,15 +18,17 @@ class PhoneAPI : public Observer // FIXME, we shouldn't be inheriting from Observer, instead use CallbackObserver as a member { enum State { - STATE_SEND_NOTHING, // Initial state, don't send anything until the client starts asking for config - STATE_SEND_MY_NODEINFO, - STATE_SEND_OWNER, + STATE_LEGACY, // Temporary default state - until Android apps are all updated, uses the old BLE API + STATE_SEND_NOTHING, // (Eventual) Initial state, don't send anything until the client starts asking for config + STATE_SEND_MY_INFO, // send our my info record STATE_SEND_RADIO, + STATE_SEND_OWNER, + STATE_SEND_NODEINFO, // states progress in this order as the device sends to to the client STATE_SEND_COMPLETE_ID, STATE_SEND_PACKETS // send packets or debug strings }; - State state = STATE_SEND_NOTHING; + State state = STATE_LEGACY; /** * Each packet sent to the phone has an incrementing count @@ -82,11 +84,6 @@ class PhoneAPI void onNowHasData(uint32_t fromRadioNum) {} private: - /** - * The client wants to start a new set of config reads - */ - void handleWantConfig(uint32_t nonce); - /** * Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool */ diff --git a/src/error.h b/src/error.h index 4fae3825b..6adcc0ecb 100644 --- a/src/error.h +++ b/src/error.h @@ -3,7 +3,7 @@ #include /// Error codes for critical error -enum CriticalErrorCode { NoError, ErrTxWatchdog, ErrSleepEnterWait, ErrNoRadio }; +enum CriticalErrorCode { NoError, ErrTxWatchdog, ErrSleepEnterWait, ErrNoRadio, ErrUnspecified }; /// Record an error that should be reported via analytics void recordCriticalError(CriticalErrorCode code, uint32_t address = 0); From c67b53b96993eeaa4b3910deecac7a122f551f5a Mon Sep 17 00:00:00 2001 From: geeksville Date: Thu, 23 Apr 2020 10:30:14 -0700 Subject: [PATCH 7/9] remove owner from ToRadio --- proto | 2 +- src/PhoneAPI.cpp | 20 ++++++-------------- src/PhoneAPI.h | 8 ++++++-- src/mesh.pb.h | 4 ---- 4 files changed, 13 insertions(+), 21 deletions(-) diff --git a/proto b/proto index 083ba7931..79b2cf728 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 083ba793108c34044e6abc8c94a5f250343b4f32 +Subproject commit 79b2cf728c08007284542b32d9d075d01e8153d8 diff --git a/src/PhoneAPI.cpp b/src/PhoneAPI.cpp index 5649d9165..4776cd969 100644 --- a/src/PhoneAPI.cpp +++ b/src/PhoneAPI.cpp @@ -34,6 +34,7 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) state = STATE_SEND_MY_INFO; DEBUG_MSG("Reset nodeinfo read pointer\n"); + nodeInfoForPhone = NULL; // Don't keep returning old nodeinfos nodeDB.resetReadPointer(); // FIXME, this read pointer should be moved out of nodeDB and into this class - because // this will break once we have multiple instances of PhoneAPI running independently break; @@ -62,10 +63,9 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) * * We assume buf is at least FromRadio_size bytes long. * - * Our sending states progress in the following sequence: + * Our sending states progress in the following sequence (the client app ASSUMES THIS SEQUENCE, DO NOT CHANGE IT): * STATE_SEND_MY_INFO, // send our my info record STATE_SEND_RADIO, - STATE_SEND_OWNER, STATE_SEND_NODEINFO, // states progress in this order as the device sends to to the client STATE_SEND_COMPLETE_ID, STATE_SEND_PACKETS // send packets or debug strings @@ -92,17 +92,11 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) case STATE_SEND_RADIO: fromRadioScratch.which_variant = FromRadio_radio_tag; fromRadioScratch.variant.radio = radioConfig; - state = STATE_SEND_OWNER; - break; - - case STATE_SEND_OWNER: - fromRadioScratch.which_variant = FromRadio_owner_tag; - fromRadioScratch.variant.owner = owner; state = STATE_SEND_NODEINFO; break; case STATE_SEND_NODEINFO: { - const NodeInfo *info = nodeDB.readNextInfo(); + const NodeInfo *info = nodeInfoForPhone; if (info) { DEBUG_MSG("Sending nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", info->num, info->position.time, info->user.id, @@ -168,10 +162,9 @@ bool PhoneAPI::available() return true; case STATE_SEND_NODEINFO: - return true; - - case STATE_SEND_OWNER: - return true; + if (!nodeInfoForPhone) + nodeInfoForPhone = nodeDB.readNextInfo(); + return true; // Always say we have something, because we might need to advance our state machine case STATE_SEND_RADIO: return true; @@ -225,7 +218,6 @@ void PhoneAPI::handleSetRadio(const RadioConfig &r) service.reloadConfig(); } - /** * Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool */ diff --git a/src/PhoneAPI.h b/src/PhoneAPI.h index 8f9af78df..56eac067d 100644 --- a/src/PhoneAPI.h +++ b/src/PhoneAPI.h @@ -22,7 +22,7 @@ class PhoneAPI STATE_SEND_NOTHING, // (Eventual) Initial state, don't send anything until the client starts asking for config STATE_SEND_MY_INFO, // send our my info record STATE_SEND_RADIO, - STATE_SEND_OWNER, + // STATE_SEND_OWNER, no need to send Owner specially, it is just part of the nodedb STATE_SEND_NODEINFO, // states progress in this order as the device sends to to the client STATE_SEND_COMPLETE_ID, STATE_SEND_PACKETS // send packets or debug strings @@ -35,9 +35,13 @@ class PhoneAPI */ uint32_t fromRadioNum = 0; - /// We temporarily keep the packet here between the call to available and getFromRadio + /// We temporarily keep the packet here between the call to available and getFromRadio. We will free it after the phone + /// downloads it MeshPacket *packetForPhone = NULL; + /// We temporarily keep the nodeInfo here between the call to available and getFromRadio + const NodeInfo *nodeInfoForPhone = NULL; + /// Our fromradio packet while it is being assembled FromRadio fromRadioScratch; diff --git a/src/mesh.pb.h b/src/mesh.pb.h index 67ca16227..f18b5c4f4 100644 --- a/src/mesh.pb.h +++ b/src/mesh.pb.h @@ -156,7 +156,6 @@ typedef struct _FromRadio { MeshPacket packet; MyNodeInfo my_info; NodeInfo node_info; - User owner; RadioConfig radio; DebugString debug_string; uint32_t config_complete_id; @@ -287,7 +286,6 @@ typedef struct _ToRadio { #define FromRadio_packet_tag 2 #define FromRadio_my_info_tag 3 #define FromRadio_node_info_tag 4 -#define FromRadio_owner_tag 5 #define FromRadio_radio_tag 6 #define FromRadio_debug_string_tag 7 #define FromRadio_config_complete_id_tag 8 @@ -432,7 +430,6 @@ X(a, STATIC, SINGULAR, UINT32, num, 1) \ X(a, STATIC, ONEOF, MESSAGE, (variant,packet,variant.packet), 2) \ X(a, STATIC, ONEOF, MESSAGE, (variant,my_info,variant.my_info), 3) \ X(a, STATIC, ONEOF, MESSAGE, (variant,node_info,variant.node_info), 4) \ -X(a, STATIC, ONEOF, MESSAGE, (variant,owner,variant.owner), 5) \ X(a, STATIC, ONEOF, MESSAGE, (variant,radio,variant.radio), 6) \ X(a, STATIC, ONEOF, MESSAGE, (variant,debug_string,variant.debug_string), 7) \ X(a, STATIC, ONEOF, UINT32, (variant,config_complete_id,variant.config_complete_id), 8) @@ -441,7 +438,6 @@ X(a, STATIC, ONEOF, UINT32, (variant,config_complete_id,variant.config_co #define FromRadio_variant_packet_MSGTYPE MeshPacket #define FromRadio_variant_my_info_MSGTYPE MyNodeInfo #define FromRadio_variant_node_info_MSGTYPE NodeInfo -#define FromRadio_variant_owner_MSGTYPE User #define FromRadio_variant_radio_MSGTYPE RadioConfig #define FromRadio_variant_debug_string_MSGTYPE DebugString From 3673f95fe573d3992511c5dce2313b9d39294fc6 Mon Sep 17 00:00:00 2001 From: geeksville Date: Thu, 23 Apr 2020 11:02:14 -0700 Subject: [PATCH 8/9] woot! using new BLE api approximately works for reading --- lib/BluetoothOTA/src/BluetoothUtil.cpp | 362 ++++++++++++------------- src/PhoneAPI.cpp | 6 +- src/esp32/MeshBluetoothService.cpp | 2 + 3 files changed, 184 insertions(+), 186 deletions(-) diff --git a/lib/BluetoothOTA/src/BluetoothUtil.cpp b/lib/BluetoothOTA/src/BluetoothUtil.cpp index 682db0b9e..0da8df85d 100644 --- a/lib/BluetoothOTA/src/BluetoothUtil.cpp +++ b/lib/BluetoothOTA/src/BluetoothUtil.cpp @@ -1,70 +1,68 @@ #include "BluetoothUtil.h" #include "BluetoothSoftwareUpdate.h" -#include -#include -#include -#include #include "configuration.h" +#include +#include +#include +#include SimpleAllocator btPool; /** * Create standard device info service **/ -BLEService *createDeviceInfomationService(BLEServer *server, std::string hwVendor, std::string swVersion, std::string hwVersion = "") +BLEService *createDeviceInfomationService(BLEServer *server, std::string hwVendor, std::string swVersion, + std::string hwVersion = "") { - BLEService *deviceInfoService = server->createService(BLEUUID((uint16_t)ESP_GATT_UUID_DEVICE_INFO_SVC)); + BLEService *deviceInfoService = server->createService(BLEUUID((uint16_t)ESP_GATT_UUID_DEVICE_INFO_SVC)); - BLECharacteristic *swC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_SW_VERSION_STR), BLECharacteristic::PROPERTY_READ); - BLECharacteristic *mfC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_MANU_NAME), BLECharacteristic::PROPERTY_READ); - // BLECharacteristic SerialNumberCharacteristic(BLEUUID((uint16_t) ESP_GATT_UUID_SERIAL_NUMBER_STR), BLECharacteristic::PROPERTY_READ); + BLECharacteristic *swC = + new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_SW_VERSION_STR), BLECharacteristic::PROPERTY_READ); + BLECharacteristic *mfC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_MANU_NAME), BLECharacteristic::PROPERTY_READ); + // BLECharacteristic SerialNumberCharacteristic(BLEUUID((uint16_t) ESP_GATT_UUID_SERIAL_NUMBER_STR), + // BLECharacteristic::PROPERTY_READ); - /* - * Mandatory characteristic for device info service? - - BLECharacteristic *m_pnpCharacteristic = m_deviceInfoService->createCharacteristic(ESP_GATT_UUID_PNP_ID, BLECharacteristic::PROPERTY_READ); + /* + * Mandatory characteristic for device info service? - uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version; - uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >> 8), (uint8_t) version }; - m_pnpCharacteristic->setValue(pnp, sizeof(pnp)); - */ - swC->setValue(swVersion); - deviceInfoService->addCharacteristic(addBLECharacteristic(swC)); - mfC->setValue(hwVendor); - deviceInfoService->addCharacteristic(addBLECharacteristic(mfC)); - if (!hwVersion.empty()) - { - BLECharacteristic *hwvC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_HW_VERSION_STR), BLECharacteristic::PROPERTY_READ); - hwvC->setValue(hwVersion); - deviceInfoService->addCharacteristic(addBLECharacteristic(hwvC)); - } - //SerialNumberCharacteristic.setValue("FIXME"); - //deviceInfoService->addCharacteristic(&SerialNumberCharacteristic); + BLECharacteristic *m_pnpCharacteristic = m_deviceInfoService->createCharacteristic(ESP_GATT_UUID_PNP_ID, + BLECharacteristic::PROPERTY_READ); - // m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, BLECharacteristic::PROPERTY_READ); - // m_manufacturerCharacteristic->setValue(name); + uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version; + uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >> + 8), (uint8_t) version }; m_pnpCharacteristic->setValue(pnp, sizeof(pnp)); + */ + swC->setValue(swVersion); + deviceInfoService->addCharacteristic(addBLECharacteristic(swC)); + mfC->setValue(hwVendor); + deviceInfoService->addCharacteristic(addBLECharacteristic(mfC)); + if (!hwVersion.empty()) { + BLECharacteristic *hwvC = + new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_HW_VERSION_STR), BLECharacteristic::PROPERTY_READ); + hwvC->setValue(hwVersion); + deviceInfoService->addCharacteristic(addBLECharacteristic(hwvC)); + } + // SerialNumberCharacteristic.setValue("FIXME"); + // deviceInfoService->addCharacteristic(&SerialNumberCharacteristic); - /* add these later? - ESP_GATT_UUID_SYSTEM_ID - */ + // m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, + // BLECharacteristic::PROPERTY_READ); m_manufacturerCharacteristic->setValue(name); - // caller must call service->start(); - return deviceInfoService; + /* add these later? + ESP_GATT_UUID_SYSTEM_ID + */ + + // caller must call service->start(); + return deviceInfoService; } bool _BLEClientConnected = false; class MyServerCallbacks : public BLEServerCallbacks { - void onConnect(BLEServer *pServer) - { - _BLEClientConnected = true; - }; + void onConnect(BLEServer *pServer) { _BLEClientConnected = true; }; - void onDisconnect(BLEServer *pServer) - { - _BLEClientConnected = false; - } + void onDisconnect(BLEServer *pServer) { _BLEClientConnected = false; } }; #define MAX_DESCRIPTORS 32 @@ -78,34 +76,34 @@ static size_t numDescs; /// Add a characteristic that we will delete when we restart BLECharacteristic *addBLECharacteristic(BLECharacteristic *c) { - assert(numChars < MAX_CHARACTERISTICS); - chars[numChars++] = c; - return c; + assert(numChars < MAX_CHARACTERISTICS); + chars[numChars++] = c; + return c; } /// Add a characteristic that we will delete when we restart BLEDescriptor *addBLEDescriptor(BLEDescriptor *c) { - assert(numDescs < MAX_DESCRIPTORS); - descs[numDescs++] = c; + assert(numDescs < MAX_DESCRIPTORS); + descs[numDescs++] = c; - return c; + return c; } // Help routine to add a description to any BLECharacteristic and add it to the service // We default to require an encrypted BOND for all these these characterstics void addWithDesc(BLEService *service, BLECharacteristic *c, const char *description) { - c->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + c->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); - BLEDescriptor *desc = new BLEDescriptor(BLEUUID((uint16_t)ESP_GATT_UUID_CHAR_DESCRIPTION), strlen(description) + 1); - assert(desc); - desc->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); - desc->setValue(description); - c->addDescriptor(desc); - service->addCharacteristic(c); - addBLECharacteristic(c); - addBLEDescriptor(desc); + BLEDescriptor *desc = new BLEDescriptor(BLEUUID((uint16_t)ESP_GATT_UUID_CHAR_DESCRIPTION), strlen(description) + 1); + assert(desc); + desc->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + desc->setValue(description); + c->addDescriptor(desc); + service->addCharacteristic(c); + addBLECharacteristic(c); + addBLEDescriptor(desc); } static BLECharacteristic *batteryLevelC; @@ -115,19 +113,20 @@ static BLECharacteristic *batteryLevelC; */ BLEService *createBatteryService(BLEServer *server) { - // Create the BLE Service - BLEService *pBattery = server->createService(BLEUUID((uint16_t)0x180F)); + // 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); + 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 + 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 - // server->getAdvertising()->addServiceUUID(pBattery->getUUID()); - pBattery->start(); + // I don't think we need to advertise this + // server->getAdvertising()->addServiceUUID(pBattery->getUUID()); + pBattery->start(); - return pBattery; + return pBattery; } /** @@ -136,87 +135,82 @@ BLEService *createBatteryService(BLEServer *server) */ void updateBatteryLevel(uint8_t level) { - // Pretend to update battery levels - fixme do elsewhere - if (batteryLevelC) - { - batteryLevelC->setValue(&level, 1); - batteryLevelC->notify(); - } + // Pretend to update battery levels - fixme do elsewhere + if (batteryLevelC) { + batteryLevelC->setValue(&level, 1); + batteryLevelC->notify(); + } } void dumpCharacteristic(BLECharacteristic *c) { - std::string value = c->getValue(); + std::string value = c->getValue(); - if (value.length() > 0) - { - DEBUG_MSG("New value: "); - for (int i = 0; i < value.length(); i++) - DEBUG_MSG("%c", value[i]); + if (value.length() > 0) { + DEBUG_MSG("New value: "); + for (int i = 0; i < value.length(); i++) + DEBUG_MSG("%c", value[i]); - DEBUG_MSG("\n"); - } + DEBUG_MSG("\n"); + } } /** converting endianness pull out a 32 bit value */ uint32_t getValue32(BLECharacteristic *c, uint32_t defaultValue) { - std::string value = c->getValue(); - uint32_t r = defaultValue; + std::string value = c->getValue(); + uint32_t r = defaultValue; - if (value.length() == 4) - r = value[0] | (value[1] << 8UL) | (value[2] << 16UL) | (value[3] << 24UL); + if (value.length() == 4) + r = value[0] | (value[1] << 8UL) | (value[2] << 16UL) | (value[3] << 24UL); - return r; + return r; } class MySecurity : public BLESecurityCallbacks { - protected: - bool onConfirmPIN(uint32_t pin) - { - Serial.printf("onConfirmPIN %u\n", pin); - return false; - } - - uint32_t onPassKeyRequest() - { - Serial.println("onPassKeyRequest"); - return 123511; // not used - } - - void onPassKeyNotify(uint32_t pass_key) - { - Serial.printf("onPassKeyNotify %u\n", pass_key); - startCb(pass_key); - } - - bool onSecurityRequest() - { - Serial.println("onSecurityRequest"); - return true; - } - - void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl) - { - if (cmpl.success) + protected: + bool onConfirmPIN(uint32_t pin) { - uint16_t length; - esp_ble_gap_get_whitelist_size(&length); - Serial.printf(" onAuthenticationComplete -> success size: %d\n", length); - } - else - { - Serial.printf("onAuthenticationComplete -> fail %d\n", cmpl.fail_reason); + Serial.printf("onConfirmPIN %u\n", pin); + return false; } - // Remove our custom PIN request screen. - stopCb(); - } + uint32_t onPassKeyRequest() + { + Serial.println("onPassKeyRequest"); + return 123511; // not used + } - public: - StartBluetoothPinScreenCallback startCb; - StopBluetoothPinScreenCallback stopCb; + void onPassKeyNotify(uint32_t pass_key) + { + Serial.printf("onPassKeyNotify %u\n", pass_key); + startCb(pass_key); + } + + bool onSecurityRequest() + { + Serial.println("onSecurityRequest"); + return true; + } + + void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl) + { + if (cmpl.success) { + uint16_t length; + esp_ble_gap_get_whitelist_size(&length); + Serial.printf(" authenticated and connected to phone\n"); + } else { + Serial.printf("phone authenticate failed %d\n", cmpl.fail_reason); + } + + // Remove our custom PIN request screen. + stopCb(); + } + + public: + StartBluetoothPinScreenCallback startCb; + StopBluetoothPinScreenCallback stopCb; }; BLEServer *pServer; @@ -225,88 +219,88 @@ BLEService *pDevInfo, *pUpdate; void deinitBLE() { - assert(pServer); + assert(pServer); - pServer->getAdvertising()->stop(); + pServer->getAdvertising()->stop(); - destroyUpdateService(); + destroyUpdateService(); - pUpdate->stop(); - pDevInfo->stop(); - pUpdate->stop(); // we delete them below + pUpdate->stop(); + pDevInfo->stop(); + pUpdate->stop(); // we delete them below - // First shutdown bluetooth - BLEDevice::deinit(false); + // First shutdown bluetooth + BLEDevice::deinit(false); - // do not delete this - it is dynamically allocated, but only once - statically in BLEDevice - // delete pServer->getAdvertising(); + // do not delete this - it is dynamically allocated, but only once - statically in BLEDevice + // delete pServer->getAdvertising(); - delete pUpdate; - delete pDevInfo; - delete pServer; + delete pUpdate; + delete pDevInfo; + delete pServer; - batteryLevelC = NULL; // Don't let anyone generate bogus notifies + batteryLevelC = NULL; // Don't let anyone generate bogus notifies - for (int i = 0; i < numChars; i++) - delete chars[i]; - numChars = 0; + for (int i = 0; i < numChars; i++) + delete chars[i]; + numChars = 0; - for (int i = 0; i < numDescs; i++) - delete descs[i]; - numDescs = 0; + for (int i = 0; i < numDescs; i++) + delete descs[i]; + numDescs = 0; - btPool.reset(); + btPool.reset(); } -BLEServer *initBLE( - StartBluetoothPinScreenCallback startBtPinScreen, - StopBluetoothPinScreenCallback stopBtPinScreen, - std::string deviceName, std::string hwVendor, std::string swVersion, std::string hwVersion) +BLEServer *initBLE(StartBluetoothPinScreenCallback startBtPinScreen, StopBluetoothPinScreenCallback stopBtPinScreen, + std::string deviceName, std::string hwVendor, std::string swVersion, std::string hwVersion) { - BLEDevice::init(deviceName); - BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + BLEDevice::init(deviceName); + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); - /* - * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation - */ - static MySecurity mySecurity; - mySecurity.startCb = startBtPinScreen; - mySecurity.stopCb = stopBtPinScreen; - BLEDevice::setSecurityCallbacks(&mySecurity); + /* + * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation + */ + static MySecurity mySecurity; + mySecurity.startCb = startBtPinScreen; + mySecurity.stopCb = stopBtPinScreen; + BLEDevice::setSecurityCallbacks(&mySecurity); - // Create the BLE Server - pServer = BLEDevice::createServer(); - static MyServerCallbacks myCallbacks; - pServer->setCallbacks(&myCallbacks); + // Create the BLE Server + pServer = BLEDevice::createServer(); + static MyServerCallbacks myCallbacks; + pServer->setCallbacks(&myCallbacks); - pDevInfo = createDeviceInfomationService(pServer, hwVendor, swVersion, hwVersion); + pDevInfo = createDeviceInfomationService(pServer, hwVendor, swVersion, hwVersion); - // We now let users create the battery service only if they really want (not all devices have a battery) - // BLEService *pBattery = createBatteryService(pServer); + // We now let users create the battery service only if they really want (not all devices have a battery) + // BLEService *pBattery = createBatteryService(pServer); - pUpdate = createUpdateService(pServer, hwVendor, swVersion, hwVersion); // We need to advertise this so our android ble scan operation can see it + pUpdate = createUpdateService(pServer, hwVendor, swVersion, + hwVersion); // We need to advertise this so our android ble scan operation can see it - // It seems only one service can be advertised - so for now don't advertise our updater - // pServer->getAdvertising()->addServiceUUID(pUpdate->getUUID()); + // It seems only one service can be advertised - so for now don't advertise our updater + // pServer->getAdvertising()->addServiceUUID(pUpdate->getUUID()); - // start all our services (do this after creating all of them) - pDevInfo->start(); - pUpdate->start(); + // start all our services (do this after creating all of them) + pDevInfo->start(); + pUpdate->start(); - // FIXME turn on this restriction only after the device is paired with a phone - // advert->setScanFilter(false, true); // We let anyone scan for us (FIXME, perhaps only allow that until we are paired with a phone and configured) but only let whitelist phones connect + // FIXME turn on this restriction only after the device is paired with a phone + // advert->setScanFilter(false, true); // We let anyone scan for us (FIXME, perhaps only allow that until we are paired with a + // phone and configured) but only let whitelist phones connect - static BLESecurity security; // static to avoid allocs - BLESecurity *pSecurity = &security; - pSecurity->setCapability(ESP_IO_CAP_OUT); - pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND); - pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + static BLESecurity security; // static to avoid allocs + BLESecurity *pSecurity = &security; + pSecurity->setCapability(ESP_IO_CAP_OUT); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND); + pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); - return pServer; + return pServer; } // Called from loop void loopBLE() { - bluetoothRebootCheck(); + bluetoothRebootCheck(); } diff --git a/src/PhoneAPI.cpp b/src/PhoneAPI.cpp index 4776cd969..043d1fb36 100644 --- a/src/PhoneAPI.cpp +++ b/src/PhoneAPI.cpp @@ -97,6 +97,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) case STATE_SEND_NODEINFO: { const NodeInfo *info = nodeInfoForPhone; + nodeInfoForPhone = NULL; // We just consumed a nodeinfo, will need a new one next time if (info) { DEBUG_MSG("Sending nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", info->num, info->position.time, info->user.id, @@ -140,8 +141,9 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) // Do we have a message from the mesh? if (fromRadioScratch.which_variant != 0) { // Encapsulate as a FromRadio packet - size_t numbytes = pb_encode_to_bytes(buf, sizeof(FromRadio_size), FromRadio_fields, &fromRadioScratch); - DEBUG_MSG("delivering toPhone packet to phone variant=%d, %d bytes\n", fromRadioScratch.which_variant, numbytes); + DEBUG_MSG("encoding toPhone packet to phone variant=%d", fromRadioScratch.which_variant); + size_t numbytes = pb_encode_to_bytes(buf, FromRadio_size, FromRadio_fields, &fromRadioScratch); + DEBUG_MSG(", %d bytes\n", numbytes); return numbytes; } diff --git a/src/esp32/MeshBluetoothService.cpp b/src/esp32/MeshBluetoothService.cpp index cadf60121..024f2d532 100644 --- a/src/esp32/MeshBluetoothService.cpp +++ b/src/esp32/MeshBluetoothService.cpp @@ -192,6 +192,8 @@ class FromRadioCharacteristic : public CallbackCharacteristic // or make empty if the queue is empty if (numBytes) { c->setValue(trBytes, numBytes); + } else { + c->setValue((uint8_t *)"", 0); } } }; From a0b6d57591e1df6bc3a5a928c71713c1c8507e4d Mon Sep 17 00:00:00 2001 From: geeksville Date: Thu, 23 Apr 2020 11:41:30 -0700 Subject: [PATCH 9/9] Fix #69 - new BLE API is in and tested from android --- bin/version.sh | 2 +- docs/software/bluetooth-api.md | 86 +++++++++++++----------------- src/esp32/MeshBluetoothService.cpp | 50 +++++++++-------- 3 files changed, 67 insertions(+), 71 deletions(-) diff --git a/bin/version.sh b/bin/version.sh index 6733e32e6..6c7385723 100644 --- a/bin/version.sh +++ b/bin/version.sh @@ -1,3 +1,3 @@ -export VERSION=0.4.3 \ No newline at end of file +export VERSION=0.5.5 \ No newline at end of file diff --git a/docs/software/bluetooth-api.md b/docs/software/bluetooth-api.md index 69f808616..cc0df5a1d 100644 --- a/docs/software/bluetooth-api.md +++ b/docs/software/bluetooth-api.md @@ -1,30 +1,30 @@ # Bluetooth API -The Bluetooth API is design to have only a few characteristics and most polymorphism comes from the flexible set of Google Protocol Buffers which are sent over the wire. We use protocol buffers extensively both for the bluetooth API and for packets inside the mesh or when providing packets to other applications on the phone. +The Bluetooth API is design to have only a few characteristics and most polymorphism comes from the flexible set of Google Protocol Buffers which are sent over the wire. We use protocol buffers extensively both for the bluetooth API and for packets inside the mesh or when providing packets to other applications on the phone. ## A note on MTU sizes -This device will work with any MTU size, but it is highly recommended that you call your phone's "setMTU function to increase MTU to 512 bytes" as soon as you connect to a service. This will dramatically improve performance when reading/writing packets. +This device will work with any MTU size, but it is highly recommended that you call your phone's "setMTU function to increase MTU to 512 bytes" as soon as you connect to a service. This will dramatically improve performance when reading/writing packets. -## MeshBluetoothService +## MeshBluetoothService -This is the main bluetooth service for the device and provides the API your app should use to get information about the mesh, send packets or provision the radio. +This is the main bluetooth service for the device and provides the API your app should use to get information about the mesh, send packets or provision the radio. -For a reference implementation of a client that uses this service see [RadioInterfaceService](https://github.com/meshtastic/Meshtastic-Android/blob/master/app/src/main/java/com/geeksville/mesh/service/RadioInterfaceService.kt). Typical flow when +For a reference implementation of a client that uses this service see [RadioInterfaceService](https://github.com/meshtastic/Meshtastic-Android/blob/master/app/src/main/java/com/geeksville/mesh/service/RadioInterfaceService.kt). Typical flow when a phone connects to the device should be the following: -* SetMTU size to 512 -* Read a RadioConfig from "radio" - used to get the channel and radio settings -* Read (and write if incorrect) a User from "user" - to get the username for this node -* Read a MyNodeInfo from "mynode" to get information about this local device -* Write an empty record to "nodeinfo" to restart the nodeinfo reading state machine -* Read from "nodeinfo" until it returns empty to build the phone's copy of the current NodeDB for the mesh -* Read from "fromradio" until it returns empty to get any messages that arrived for this node while the phone was away -* Subscribe to notify on "fromnum" to get notified whenever the device has a new received packet -* Read that new packet from "fromradio" -* Whenever the phone has a packet to send write to "toradio" +- SetMTU size to 512 +- Read a RadioConfig from "radio" - used to get the channel and radio settings +- Read (and write if incorrect) a User from "user" - to get the username for this node +- Read a MyNodeInfo from "mynode" to get information about this local device +- Write an empty record to "nodeinfo" to restart the nodeinfo reading state machine +- Read from "nodeinfo" until it returns empty to build the phone's copy of the current NodeDB for the mesh +- Read from "fromradio" until it returns empty to get any messages that arrived for this node while the phone was away +- Subscribe to notify on "fromnum" to get notified whenever the device has a new received packet +- Read that new packet from "fromradio" +- Whenever the phone has a packet to send write to "toradio" -For definitions (and documentation) on FromRadio, ToRadio, MyNodeInfo, NodeInfo and User protocol buffers see [mesh.proto](https://github.com/meshtastic/Meshtastic-protobufs/blob/master/mesh.proto) +For definitions (and documentation) on FromRadio, ToRadio, MyNodeInfo, NodeInfo and User protocol buffers see [mesh.proto](https://github.com/meshtastic/Meshtastic-protobufs/blob/master/mesh.proto) UUID for the service: 6ba1b218-15a8-461f-9fa8-5dcae273eafd @@ -37,7 +37,7 @@ Description (including human readable name) 8ba2bcc2-ee02-4a55-a531-c525c5e454d5 read fromradio - contains a newly received FromRadio packet destined towards the phone (up to MAXPACKET bytes per packet). -After reading the esp32 will put the next packet in this mailbox. If the FIFO is empty it will put an empty packet in this +After reading the esp32 will put the next packet in this mailbox. If the FIFO is empty it will put an empty packet in this mailbox. f75c76d2-129e-4dad-a1dd-7866124401e7 @@ -49,34 +49,22 @@ read,notify,write fromnum - the current packet # in the message waiting inside fromradio, if the phone sees this notify it should read messages until it catches up with this number. -The phone can write to this register to go backwards up to FIXME packets, to handle the rare case of a fromradio packet was dropped after the esp32 callback was called, but before it arrives at the phone. If the phone writes to this register the esp32 will discard older packets and put the next packet >= fromnum in fromradio. +The phone can write to this register to go backwards up to FIXME packets, to handle the rare case of a fromradio packet was dropped after the esp32 callback was called, but before it arrives at the phone. If the phone writes to this register the esp32 will discard older packets and put the next packet >= fromnum in fromradio. When the esp32 advances fromnum, it will delay doing the notify by 100ms, in the hopes that the notify will never actally need to be sent if the phone is already pulling from fromradio. Note: that if the phone ever sees this number decrease, it means the esp32 has rebooted. -ea9f3f82-8dc4-4733-9452-1f6da28892a2 -read -mynode - read this to access a MyNodeInfo protobuf - -d31e02e0-c8ab-4d3f-9cc9-0b8466bdabe8 -read, write -nodeinfo - read this to get a series of NodeInfos (ending with a null empty record), write to this to restart the read statemachine that returns all the node infos - -b56786c8-839a-44a1-b98e-a1724c4a0262 -read,write -radio - read/write this to access a RadioConfig protobuf - -6ff1d8b6-e2de-41e3-8c0b-8fa384f64eb6 -read,write -owner - read/write this to access a User protobuf - Re: queue management Not all messages are kept in the fromradio queue (filtered based on SubPacket): -* only the most recent Position and User messages for a particular node are kept -* all Data SubPackets are kept -* No WantNodeNum / DenyNodeNum messages are kept -A variable keepAllPackets, if set to true will suppress this behavior and instead keep everything for forwarding to the phone (for debugging) +- only the most recent Position and User messages for a particular node are kept +- all Data SubPackets are kept +- No WantNodeNum / DenyNodeNum messages are kept + A variable keepAllPackets, if set to true will suppress this behavior and instead keep everything for forwarding to the phone (for debugging) + +## Protobuf API + +On connect, you should send a want_config_id protobuf to the device. This will cause the device to send its node DB and radio config via the fromradio endpoint. After sending the full DB, the radio will send a want_config_id to indicate it is done sending the configuration. ## Other bluetooth services @@ -85,21 +73,21 @@ provided by the device. ### BluetoothSoftwareUpdate -The software update service. For a sample function that performs a software update using this API see [startUpdate](https://github.com/meshtastic/Meshtastic-Android/blob/master/app/src/main/java/com/geeksville/mesh/service/SoftwareUpdateService.kt). +The software update service. For a sample function that performs a software update using this API see [startUpdate](https://github.com/meshtastic/Meshtastic-Android/blob/master/app/src/main/java/com/geeksville/mesh/service/SoftwareUpdateService.kt). SoftwareUpdateService UUID cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30 Characteristics -| UUID | properties | description| -|--------------------------------------|------------------|------------| -| e74dd9c0-a301-4a6f-95a1-f0e1dbea8e1e | write,read | total image size, 32 bit, write this first, then read read back to see if it was acceptable (0 mean not accepted) | -| e272ebac-d463-4b98-bc84-5cc1a39ee517 | write | data, variable sized, recommended 512 bytes, write one for each block of file | -| 4826129c-c22a-43a3-b066-ce8f0d5bacc6 | write | crc32, write last - writing this will complete the OTA operation, now you can read result | -| 5e134862-7411-4424-ac4a-210937432c77 | read,notify | result code, readable but will notify when the OTA operation completes | -| GATT_UUID_SW_VERSION_STR/0x2a28 | read | We also implement these standard GATT entries because SW update probably needs them: | -| GATT_UUID_MANU_NAME/0x2a29 | read | | -| GATT_UUID_HW_VERSION_STR/0x2a27 | read | | +| UUID | properties | description | +| ------------------------------------ | ----------- | ----------------------------------------------------------------------------------------------------------------- | +| e74dd9c0-a301-4a6f-95a1-f0e1dbea8e1e | write,read | total image size, 32 bit, write this first, then read read back to see if it was acceptable (0 mean not accepted) | +| e272ebac-d463-4b98-bc84-5cc1a39ee517 | write | data, variable sized, recommended 512 bytes, write one for each block of file | +| 4826129c-c22a-43a3-b066-ce8f0d5bacc6 | write | crc32, write last - writing this will complete the OTA operation, now you can read result | +| 5e134862-7411-4424-ac4a-210937432c77 | read,notify | result code, readable but will notify when the OTA operation completes | +| GATT_UUID_SW_VERSION_STR/0x2a28 | read | We also implement these standard GATT entries because SW update probably needs them: | +| GATT_UUID_MANU_NAME/0x2a29 | read | | +| GATT_UUID_HW_VERSION_STR/0x2a27 | read | | ### DeviceInformationService @@ -107,4 +95,4 @@ Implements the standard BLE contract for this service (has software version, har ### BatteryLevelService -Implements the standard BLE contract service, provides battery level in a way that most client devices should automatically understand (i.e. it should show in the bluetooth devices screen automatically) \ No newline at end of file +Implements the standard BLE contract service, provides battery level in a way that most client devices should automatically understand (i.e. it should show in the bluetooth devices screen automatically) diff --git a/src/esp32/MeshBluetoothService.cpp b/src/esp32/MeshBluetoothService.cpp index 024f2d532..9b3f14804 100644 --- a/src/esp32/MeshBluetoothService.cpp +++ b/src/esp32/MeshBluetoothService.cpp @@ -23,6 +23,9 @@ static CallbackCharacteristic *meshFromNumCharacteristic; BLEService *meshService; +// If defined we will also support the old API +#define SUPPORT_OLD_BLE_API + class BluetoothPhoneAPI : public PhoneAPI { /** @@ -80,6 +83,7 @@ class ProtobufCharacteristic : public CallbackCharacteristic } }; +#ifdef SUPPORT_OLD_BLE_API class NodeInfoCharacteristic : public BLECharacteristic, public BLEKeepAliveCallbacks { public: @@ -162,6 +166,29 @@ class OwnerCharacteristic : public ProtobufCharacteristic } }; +class MyNodeInfoCharacteristic : public ProtobufCharacteristic +{ + public: + MyNodeInfoCharacteristic() + : ProtobufCharacteristic("ea9f3f82-8dc4-4733-9452-1f6da28892a2", BLECharacteristic::PROPERTY_READ, MyNodeInfo_fields, + &myNodeInfo) + { + } + + void onRead(BLECharacteristic *c) + { + // update gps connection state + myNodeInfo.has_gps = gps.isConnected; + + ProtobufCharacteristic::onRead(c); + + myNodeInfo.error_code = 0; // The phone just read us, so throw it away + myNodeInfo.error_address = 0; + } +}; + +#endif + class ToRadioCharacteristic : public CallbackCharacteristic { public: @@ -216,27 +243,6 @@ class FromNumCharacteristic : public CallbackCharacteristic } }; -class MyNodeInfoCharacteristic : public ProtobufCharacteristic -{ - public: - MyNodeInfoCharacteristic() - : ProtobufCharacteristic("ea9f3f82-8dc4-4733-9452-1f6da28892a2", BLECharacteristic::PROPERTY_READ, MyNodeInfo_fields, - &myNodeInfo) - { - } - - void onRead(BLECharacteristic *c) - { - // update gps connection state - myNodeInfo.has_gps = gps.isConnected; - - ProtobufCharacteristic::onRead(c); - - myNodeInfo.error_code = 0; // The phone just read us, so throw it away - myNodeInfo.error_address = 0; - } -}; - /* See bluetooth-api.md for documentation. */ @@ -257,10 +263,12 @@ BLEService *createMeshBluetoothService(BLEServer *server) addWithDesc(service, meshFromNumCharacteristic, "fromRadio"); addWithDesc(service, new ToRadioCharacteristic, "toRadio"); addWithDesc(service, new FromRadioCharacteristic, "fromNum"); +#ifdef SUPPORT_OLD_BLE_API addWithDesc(service, new MyNodeInfoCharacteristic, "myNode"); addWithDesc(service, new RadioCharacteristic, "radio"); addWithDesc(service, new OwnerCharacteristic, "owner"); addWithDesc(service, new NodeInfoCharacteristic, "nodeinfo"); +#endif meshFromNumCharacteristic->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification