move bluetooth code into something that is architecture specific...

because the ESP32 implementation will be different from NRF52
to make this possible I needed to decouple knowlege about bluetooth from
our mesh service.  Instead mesh service now uses the Obserable pattern
to let any interested consumer get notified of important mesh changes
(currently that is only bluetooth, but really we should do the same
thing for decoupling the GUI 'app' from the mesh service)

@girtsf would you mind reviewing my Observer changes? I haven't written
C++ code in a long time ;-)
This commit is contained in:
geeksville 2020-04-10 12:18:48 -07:00
parent 93a06906cb
commit 6ad451eb5f
13 changed files with 104 additions and 108 deletions

View File

@ -192,7 +192,7 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0
if (hasValidLocation) {
wantNewLocation = false;
notifyObservers();
notifyObservers(NULL);
// ublox.powerOff();
}
} else // we didn't get a location update, go back to sleep and hope the characters show up

View File

@ -10,7 +10,7 @@
*
* When new data is available it will notify observers.
*/
class GPS : public PeriodicTask, public Observable
class GPS : public PeriodicTask, public Observable<void *>
{
SFE_UBLOX_GPS ublox;

View File

@ -3,7 +3,7 @@
#include <assert.h>
#include "GPS.h"
#include "MeshBluetoothService.h"
//#include "MeshBluetoothService.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "Periodic.h"
@ -52,8 +52,7 @@ MeshService service;
4 // max number of packets destined to our queue, we dispatch packets quickly so it doesn't need to be big
MeshService::MeshService()
: packetPool(MAX_PACKETS), toPhoneQueue(MAX_RX_TOPHONE), fromRadioQueue(MAX_RX_FROMRADIO), fromNum(0),
radio(packetPool, fromRadioQueue)
: packetPool(MAX_PACKETS), toPhoneQueue(MAX_RX_TOPHONE), fromRadioQueue(MAX_RX_FROMRADIO), radio(packetPool, fromRadioQueue)
{
// assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro
}
@ -65,7 +64,7 @@ void MeshService::init()
if (!radio.init())
DEBUG_MSG("radio init failed\n");
gps.addObserver(this);
gpsObserver.observe(&gps);
// No need to call this here, our periodic task will fire quite soon
// sendOwnerPeriod();
@ -191,7 +190,7 @@ void MeshService::handleFromRadio()
handleFromRadio(mp);
}
if (oldFromNum != fromNum) // We don't want to generate extra notifies for multiple new packets
bluetoothNotifyFromNum(fromNum);
fromNumChanged.notifyObservers(fromNum);
}
uint32_t sendOwnerCb()
@ -244,7 +243,7 @@ void MeshService::handleToRadio(std::string s)
if (loopback) {
MeshPacket *mp = packetPool.allocCopy(r.variant.packet);
handleFromRadio(mp);
bluetoothNotifyFromNum(fromNum); // tell the phone a new packet arrived
// handleFromRadio will tell the phone a new packet arrived
}
break;
}
@ -323,8 +322,10 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies)
sendToMesh(p);
}
void MeshService::onGPSChanged()
void MeshService::onGPSChanged(void *unused)
{
DEBUG_MSG("got gps notify\n");
// Update our local node info with our position (even if we don't decide to update anyone else)
MeshPacket *p = allocForSending();
p->payload.which_variant = SubPacket_position_tag;
@ -354,9 +355,3 @@ void MeshService::onGPSChanged()
releaseToPool(p);
}
}
void MeshService::onNotify(Observable *o)
{
DEBUG_MSG("got gps notify\n");
onGPSChanged();
}

View File

@ -13,8 +13,10 @@
* Top level app for this service. keeps the mesh, the radio config and the queue of received packets.
*
*/
class MeshService : private Observer
class MeshService
{
CallbackObserver<MeshService, void *> gpsObserver = CallbackObserver<MeshService, void *>(this, &MeshService::onGPSChanged);
MemoryPool<MeshPacket> packetPool;
/// received packets waiting for the phone to process them
@ -28,11 +30,13 @@ class MeshService : private Observer
PointerQueue<MeshPacket> fromRadioQueue;
/// The current nonce for the newest packet which has been queued for the phone
uint32_t fromNum;
uint32_t fromNum = 0;
public:
MeshRadio radio;
Observable<uint32_t> fromNumChanged;
MeshService();
void init();
@ -76,14 +80,13 @@ class MeshService : private Observer
void sendToMesh(MeshPacket *p);
/// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh
void onGPSChanged();
virtual void onNotify(Observable *o);
void onGPSChanged(void *arg);
/// handle all the packets that just arrived from the mesh radio
void handleFromRadio();
/// Handle a packet that just arrived from the radio. We will either eventually enqueue the message to the phone or return it to the free pool
/// Handle a packet that just arrived from the radio. We will either eventually enqueue the message to the phone or return it
/// to the free pool
void handleFromRadio(MeshPacket *p);
/// handle a user packet that just arrived on the radio, return NULL if we should not process this packet at all

View File

@ -1,13 +1,2 @@
#include "Observer.h"
Observer::~Observer()
{
if (observed)
observed->removeObserver(this);
observed = NULL;
}
void Observer::observe(Observable *o)
{
o->addObserver(this);
}

View File

@ -4,38 +4,83 @@
#include <list>
class Observable;
template <class T> class Observable;
class Observer
/**
* An observer which can be mixed in as a baseclass. Implement onNotify as a method in your class.
*/
template <class T> class Observer
{
Observable *observed;
Observable<T> *observed;
public:
Observer() : observed(NULL) {}
virtual ~Observer();
void observe(Observable *o);
void observe(Observable<T> *o);
private:
friend class Observable;
friend class Observable<T>;
virtual void onNotify(Observable *o) = 0;
protected:
virtual void onNotify(T arg) = 0;
};
class Observable
/**
* An observer that calls an arbitrary method
*/
template <class Callback, class T> class CallbackObserver : public Observer<T>
{
std::list<Observer *> observers;
typedef void (Callback::*ObserverCallback)(T arg);
Callback *objPtr;
ObserverCallback method;
public:
void notifyObservers()
CallbackObserver(Callback *_objPtr, ObserverCallback _method) : objPtr(_objPtr), method(_method) {}
protected:
virtual void onNotify(T arg) { (objPtr->*method)(arg); }
};
/**
* An observable class that will notify observers anytime notifyObservers is called. Argument type T can be any type, but for
* performance reasons a pointer or word sized object is recommended.
*/
template <class T> class Observable
{
std::list<Observer<T> *> observers;
public:
/**
* Tell all observers about a change, observers can process arg as they wish
*/
void notifyObservers(T arg)
{
for (std::list<Observer *>::const_iterator iterator = observers.begin(); iterator != observers.end(); ++iterator) {
(*iterator)->onNotify(this);
for (typename std::list<Observer<T> *>::const_iterator iterator = observers.begin(); iterator != observers.end();
++iterator) {
(*iterator)->onNotify(arg);
}
}
void addObserver(Observer *o) { observers.push_back(o); }
private:
friend class Observer<T>;
void removeObserver(Observer *o) { observers.remove(o); }
// Not called directly, instead call observer.observe
void addObserver(Observer<T> *o) { observers.push_back(o); }
void removeObserver(Observer<T> *o) { observers.remove(o); }
};
template <class T> Observer<T>::~Observer()
{
if (observed)
observed->removeObserver(this);
observed = NULL;
}
template <class T> void Observer<T>::observe(Observable<T> *o)
{
o->addObserver(this);
}

View File

@ -7,6 +7,7 @@
#include "main.h"
#include "screen.h"
#include "sleep.h"
#include "target_specific.h"
static void sdsEnter()
{

View File

@ -204,7 +204,7 @@ class FromRadioCharacteristic : public CallbackCharacteristic
}
};
class FromNumCharacteristic : public CallbackCharacteristic
class FromNumCharacteristic : public CallbackCharacteristic, public Observer<uint32_t>
{
public:
FromNumCharacteristic()
@ -212,6 +212,7 @@ class FromNumCharacteristic : public CallbackCharacteristic
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY)
{
observe(&service.fromNumChanged);
}
void onRead(BLECharacteristic *c)
@ -219,6 +220,13 @@ class FromNumCharacteristic : public CallbackCharacteristic
BLEKeepAliveCallbacks::onRead(c);
DEBUG_MSG("FIXME implement fromnum read\n");
}
/// If the mesh service tells us fromNum has changed, tell the phone
virtual void onNotify(uint32_t newValue)
{
setValue(newValue);
notify();
}
};
class MyNodeInfoCharacteristic : public ProtobufCharacteristic
@ -244,18 +252,6 @@ class MyNodeInfoCharacteristic : public ProtobufCharacteristic
FromNumCharacteristic *meshFromNumCharacteristic;
/**
* Tell any bluetooth clients that the number of rx packets has changed
*/
void bluetoothNotifyFromNum(uint32_t newValue)
{
if (meshFromNumCharacteristic) {
// if bt not running ignore
meshFromNumCharacteristic->setValue(newValue);
meshFromNumCharacteristic->notify();
}
}
BLEService *meshService;
/*

View File

@ -21,9 +21,7 @@
*/
#include "BluetoothUtil.h"
#include "GPS.h"
#include "MeshBluetoothService.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h"
@ -39,6 +37,10 @@
#include <Wire.h>
#include <driver/rtc_io.h>
#ifndef NO_ESP32
#include "BluetoothUtil.h"
#endif
#ifdef TBEAM_V10
#include "axp20x.h"
AXP20X_Class axp;
@ -59,8 +61,6 @@ static meshtastic::PowerStatus powerStatus;
bool ssd1306_found;
bool axp192_found;
bool bluetoothOn;
// -----------------------------------------------------------------------------
// Application
// -----------------------------------------------------------------------------
@ -266,49 +266,6 @@ void setup()
setCPUFast(false); // 80MHz is fine for our slow peripherals
}
void initBluetooth()
{
DEBUG_MSG("Starting bluetooth\n");
// FIXME - we are leaking like crazy
// AllocatorScope scope(btPool);
// Note: these callbacks might be coming in from a different thread.
BLEServer *serve = initBLE(
[](uint32_t pin) {
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
screen.startBluetoothPinScreen(pin);
},
[]() { screen.stopBluetoothPinScreen(); }, getDeviceName(), HW_VENDOR, xstr(APP_VERSION),
xstr(HW_VERSION)); // FIXME, use a real name based on the macaddr
createMeshBluetoothService(serve);
// Start advertising - this must be done _after_ creating all services
serve->getAdvertising()->start();
}
void setBluetoothEnable(bool on)
{
if (on != bluetoothOn) {
DEBUG_MSG("Setting bluetooth enable=%d\n", on);
bluetoothOn = on;
if (on) {
Serial.printf("Pre BT: %u heap size\n", ESP.getFreeHeap());
// ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
initBluetooth();
} else {
// We have to totally teardown our bluetooth objects to prevent leaks
stopMeshBluetoothService(); // Must do before shutting down bluetooth
deinitBLE();
destroyMeshBluetoothService(); // must do after deinit, because it frees our service
Serial.printf("Shutdown BT: %u heap size\n", ESP.getFreeHeap());
// ESP_ERROR_CHECK( heap_trace_stop() );
// heap_trace_dump();
}
}
}
uint32_t ledBlinker()
{
static bool ledOn;
@ -352,7 +309,10 @@ void loop()
ledPeriodic.loop();
// axpDebugOutput.loop();
#ifndef NO_ESP32
loopBLE();
#endif
// for debug printing
// service.radio.radioIf.canSleep();

View File

@ -9,3 +9,6 @@ extern bool isUSBPowered;
// Global Screen singleton.
extern meshtastic::Screen screen;
// Return a human readable string of the form "Meshtastic_ab13"
const char *getDeviceName();

View File

@ -1,7 +1,5 @@
#include "sleep.h"
#include "BluetoothUtil.h"
#include "GPS.h"
#include "MeshBluetoothService.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h"
@ -11,9 +9,14 @@
#include "esp_pm.h"
#include "main.h"
#include "rom/rtc.h"
#include "target_specific.h"
#include <Wire.h>
#include <driver/rtc_io.h>
#ifndef NO_ESP32
#include "BluetoothUtil.h"
#endif
#ifdef TBEAM_V10
#include "axp20x.h"
extern AXP20X_Class axp;
@ -105,7 +108,9 @@ void doDeepSleep(uint64_t msecToWake)
// not using wifi yet, but once we are this is needed to shutoff the radio hw
// esp_wifi_stop();
#ifndef NO_ESP32
BLEDevice::deinit(false); // We are required to shutdown bluetooth before deep or light sleep
#endif
screen.setOn(false); // datasheet says this will draw only 10ua

View File

@ -5,7 +5,6 @@
void doDeepSleep(uint64_t msecToWake);
esp_sleep_wakeup_cause_t doLightSleep(uint64_t msecToWake);
void setBluetoothEnable(bool on);
void setGPSPower(bool on);
// Perform power on init that we do on each wake from deep sleep