Merge pull request #46 from geeksville/master

a quick window to sneak in and fix formatting
This commit is contained in:
Kevin Hester 2020-03-18 19:19:18 -07:00 committed by GitHub
commit 992b525588
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1138 additions and 1262 deletions

View File

@ -1,20 +1,16 @@
#include "CustomRF95.h" #include "CustomRF95.h"
#include <pb_encode.h>
#include <pb_decode.h>
#include "configuration.h"
#include "assert.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "assert.h"
#include "configuration.h"
#include <pb_decode.h>
#include <pb_encode.h>
/// A temporary buffer used for sending/receving packets, sized to hold the biggest buffer we might need /// A temporary buffer used for sending/receving packets, sized to hold the biggest buffer we might need
#define MAX_RHPACKETLEN 251 #define MAX_RHPACKETLEN 251
static uint8_t radiobuf[MAX_RHPACKETLEN]; static uint8_t radiobuf[MAX_RHPACKETLEN];
CustomRF95::CustomRF95(MemoryPool<MeshPacket> &_pool, PointerQueue<MeshPacket> &_rxDest) CustomRF95::CustomRF95(MemoryPool<MeshPacket> &_pool, PointerQueue<MeshPacket> &_rxDest)
: RH_RF95(NSS_GPIO, DIO0_GPIO), : RH_RF95(NSS_GPIO, DIO0_GPIO), pool(_pool), rxDest(_rxDest), txQueue(MAX_TX_QUEUE), sendingPacket(NULL)
pool(_pool),
rxDest(_rxDest),
txQueue(MAX_TX_QUEUE),
sendingPacket(NULL)
{ {
} }
@ -49,15 +45,12 @@ ErrorCode CustomRF95::send(MeshPacket *p)
// We wait _if_ we are partially though receiving a packet (rather than just merely waiting for one). // We wait _if_ we are partially though receiving a packet (rather than just merely waiting for one).
// To do otherwise would be doubly bad because not only would we drop the packet that was on the way in, // To do otherwise would be doubly bad because not only would we drop the packet that was on the way in,
// we almost certainly guarantee no one outside will like the packet we are sending. // we almost certainly guarantee no one outside will like the packet we are sending.
if (_mode == RHModeIdle || (_mode == RHModeRx && !_isReceiving)) if (_mode == RHModeIdle || (_mode == RHModeRx && !_isReceiving)) {
{
// if the radio is idle, we can send right away // if the radio is idle, we can send right away
DEBUG_MSG("immedate send on mesh (txGood=%d,rxGood=%d,rxBad=%d)\n", txGood(), rxGood(), rxBad()); DEBUG_MSG("immedate send on mesh (txGood=%d,rxGood=%d,rxBad=%d)\n", txGood(), rxGood(), rxBad());
startSend(p); startSend(p);
return ERRNO_OK; return ERRNO_OK;
} } else {
else
{
DEBUG_MSG("enquing packet for send from=0x%x, to=0x%x\n", p->from, p->to); DEBUG_MSG("enquing packet for send from=0x%x, to=0x%x\n", p->from, p->to);
ErrorCode res = txQueue.enqueue(p, 0) ? ERRNO_OK : ERRNO_UNKNOWN; ErrorCode res = txQueue.enqueue(p, 0) ? ERRNO_OK : ERRNO_UNKNOWN;
@ -68,7 +61,8 @@ ErrorCode CustomRF95::send(MeshPacket *p)
} }
} }
// After doing standard behavior, check to see if a new packet arrived or one was sent and start a new send or receive as necessary // After doing standard behavior, check to see if a new packet arrived or one was sent and start a new send or receive as
// necessary
void CustomRF95::handleInterrupt() void CustomRF95::handleInterrupt()
{ {
RH_RF95::handleInterrupt(); RH_RF95::handleInterrupt();
@ -85,8 +79,7 @@ void CustomRF95::handleInterrupt()
} }
// If we just finished receiving a packet, forward it into a queue // If we just finished receiving a packet, forward it into a queue
if (_rxBufValid) if (_rxBufValid) {
{
// We received a packet // We received a packet
// Skip the 4 headers that are at the beginning of the rxBuf // Skip the 4 headers that are at the beginning of the rxBuf
@ -111,18 +104,14 @@ void CustomRF95::handleInterrupt()
// Note: we can't create it at this point, because it might be a bogus User node allocation. But odds are we will // Note: we can't create it at this point, because it might be a bogus User node allocation. But odds are we will
// already have a record we can hide this debugging info in. // already have a record we can hide this debugging info in.
NodeInfo *info = nodeDB.getNode(mp->from); NodeInfo *info = nodeDB.getNode(mp->from);
if (info) if (info) {
{
info->snr = snr; info->snr = snr;
info->frequency_error = freqerr; info->frequency_error = freqerr;
} }
if (!pb_decode_from_bytes(payload, payloadLen, SubPacket_fields, p)) if (!pb_decode_from_bytes(payload, payloadLen, SubPacket_fields, p)) {
{
pool.releaseFromISR(mp, &higherPriWoken); pool.releaseFromISR(mp, &higherPriWoken);
} } else {
else
{
// parsing was successful, queue for our recipient // parsing was successful, queue for our recipient
mp->has_payload = true; mp->has_payload = true;
@ -152,8 +141,7 @@ bool CustomRF95::handleIdleISR()
MeshPacket *txp = txQueue.dequeuePtrFromISR(0); MeshPacket *txp = txQueue.dequeuePtrFromISR(0);
if (txp) if (txp)
startSend(txp); startSend(txp);
else else {
{
// Nothing to send, let's switch back to receive mode // Nothing to send, let's switch back to receive mode
setModeRx(); setModeRx();
} }

View File

@ -1,15 +1,14 @@
#pragma once #pragma once
#include <RH_RF95.h>
#include <RHMesh.h>
#include "MemoryPool.h" #include "MemoryPool.h"
#include "mesh.pb.h"
#include "PointerQueue.h"
#include "MeshTypes.h" #include "MeshTypes.h"
#include "PointerQueue.h"
#include "mesh.pb.h"
#include <RHMesh.h>
#include <RH_RF95.h>
#define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission #define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission
/** /**
* A version of the RF95 driver which is smart enough to manage packets via queues (no polling or blocking in user threads!) * A version of the RF95 driver which is smart enough to manage packets via queues (no polling or blocking in user threads!)
*/ */
@ -46,7 +45,8 @@ public:
bool init(); bool init();
protected: protected:
// After doing standard behavior, check to see if a new packet arrived or one was sent and start a new send or receive as necessary // After doing standard behavior, check to see if a new packet arrived or one was sent and start a new send or receive as
// necessary
virtual void handleInterrupt(); virtual void handleInterrupt();
private: private:

View File

@ -35,7 +35,8 @@ void GPS::setup()
isConnected = ublox.begin(_serial_gps); isConnected = ublox.begin(_serial_gps);
// try a second time, the ublox lib serial parsing is buggy? // try a second time, the ublox lib serial parsing is buggy?
if(!isConnected) isConnected = ublox.begin(_serial_gps); if (!isConnected)
isConnected = ublox.begin(_serial_gps);
if (isConnected) { if (isConnected) {
DEBUG_MSG("Connected to GPS successfully, TXpin=%d\n", GPS_TX_PIN); DEBUG_MSG("Connected to GPS successfully, TXpin=%d\n", GPS_TX_PIN);

View File

@ -1,9 +1,9 @@
#pragma once #pragma once
#include "PeriodicTask.h"
#include "Observer.h" #include "Observer.h"
#include "sys/time.h" #include "PeriodicTask.h"
#include "SparkFun_Ublox_Arduino_Library.h" #include "SparkFun_Ublox_Arduino_Library.h"
#include "sys/time.h"
/** /**
* A gps class that only reads from the GPS periodically (and FIXME - eventually keeps the gps powered down except when reading) * A gps class that only reads from the GPS periodically (and FIXME - eventually keeps the gps powered down except when reading)
@ -50,4 +50,3 @@ private:
}; };
extern GPS gps; extern GPS gps;

View File

@ -10,8 +10,7 @@
* *
* Eventually this routine will even be safe for ISR use... * Eventually this routine will even be safe for ISR use...
*/ */
template <class T> template <class T> class MemoryPool
class MemoryPool
{ {
PointerQueue<T> dead; PointerQueue<T> dead;
@ -29,10 +28,7 @@ public:
release(&buf[i]); release(&buf[i]);
} }
~MemoryPool() ~MemoryPool() { delete[] buf; }
{
delete[] buf;
}
/// Return a queable object which has been prefilled with zeros. Panic if no buffer is available /// Return a queable object which has been prefilled with zeros. Panic if no buffer is available
T *allocZeroed() T *allocZeroed()
@ -43,7 +39,8 @@ public:
return p; return p;
} }
/// Return a queable object which has been prefilled with zeros - allow timeout to wait for available buffers (you probably don't want this version) /// Return a queable object which has been prefilled with zeros - allow timeout to wait for available buffers (you probably
/// don't want this version)
T *allocZeroed(TickType_t maxWait) T *allocZeroed(TickType_t maxWait)
{ {
T *p = dead.dequeuePtr(maxWait); T *p = dead.dequeuePtr(maxWait);
@ -67,13 +64,17 @@ public:
void release(T *p) void release(T *p)
{ {
assert(dead.enqueue(p, 0)); assert(dead.enqueue(p, 0));
assert(p >= buf && (p - buf) < maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool assert(p >= buf &&
(p - buf) <
maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
} }
/// Return a buffer from an ISR, if higherPriWoken is set to true you have some work to do ;-) /// Return a buffer from an ISR, if higherPriWoken is set to true you have some work to do ;-)
void releaseFromISR(T *p, BaseType_t *higherPriWoken) void releaseFromISR(T *p, BaseType_t *higherPriWoken)
{ {
assert(dead.enqueueFromISR(p, higherPriWoken)); assert(dead.enqueueFromISR(p, higherPriWoken));
assert(p >= buf && (p - buf) < maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool assert(p >= buf &&
(p - buf) <
maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
} }
}; };

View File

@ -1,21 +1,22 @@
#include "BluetoothUtil.h"
#include "MeshBluetoothService.h" #include "MeshBluetoothService.h"
#include <esp_gatt_defs.h> #include "BluetoothUtil.h"
#include <BLE2902.h>
#include <Arduino.h> #include <Arduino.h>
#include <BLE2902.h>
#include <assert.h> #include <assert.h>
#include <esp_gatt_defs.h>
#include "mesh.pb.h"
#include "MeshService.h"
#include "mesh-pb-constants.h"
#include "NodeDB.h"
#include "configuration.h"
#include "PowerFSM.h"
#include "CallbackCharacteristic.h" #include "CallbackCharacteristic.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
#include "mesh.pb.h"
#include "GPS.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 // 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 uint8_t trBytes[_max(_max(_max(_max(ToRadio_size, RadioConfig_size), User_size), MyNodeInfo_size), FromRadio_size)];
class ProtobufCharacteristic : public CallbackCharacteristic class ProtobufCharacteristic : public CallbackCharacteristic
@ -25,9 +26,7 @@ class ProtobufCharacteristic : public CallbackCharacteristic
public: public:
ProtobufCharacteristic(const char *uuid, uint32_t btprops, const pb_msgdesc_t *_fields, void *_my_struct) ProtobufCharacteristic(const char *uuid, uint32_t btprops, const pb_msgdesc_t *_fields, void *_my_struct)
: CallbackCharacteristic(uuid, btprops), : CallbackCharacteristic(uuid, btprops), fields(_fields), my_struct(_my_struct)
fields(_fields),
my_struct(_my_struct)
{ {
setCallbacks(this); setCallbacks(this);
} }
@ -63,7 +62,8 @@ class NodeInfoCharacteristic : public BLECharacteristic, public BLEKeepAliveCall
{ {
public: public:
NodeInfoCharacteristic() NodeInfoCharacteristic()
: BLECharacteristic("d31e02e0-c8ab-4d3f-9cc9-0b8466bdabe8", BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ) : BLECharacteristic("d31e02e0-c8ab-4d3f-9cc9-0b8466bdabe8",
BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ)
{ {
setCallbacks(this); setCallbacks(this);
} }
@ -74,14 +74,12 @@ public:
const NodeInfo *info = nodeDB.readNextInfo(); const NodeInfo *info = nodeDB.readNextInfo();
if (info) if (info) {
{ DEBUG_MSG("Sending nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", info->num, info->position.time, info->user.id,
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); info->user.long_name);
size_t numbytes = pb_encode_to_bytes(trBytes, sizeof(trBytes), NodeInfo_fields, info); size_t numbytes = pb_encode_to_bytes(trBytes, sizeof(trBytes), NodeInfo_fields, info);
c->setValue(trBytes, numbytes); c->setValue(trBytes, numbytes);
} } else {
else
{
c->setValue(trBytes, 0); // Send an empty response c->setValue(trBytes, 0); // Send an empty response
DEBUG_MSG("Done sending nodeinfos\n"); DEBUG_MSG("Done sending nodeinfos\n");
} }
@ -95,13 +93,14 @@ public:
} }
}; };
// wrap our protobuf version with something that forces the service to reload the config // wrap our protobuf version with something that forces the service to reload the config
class RadioCharacteristic : public ProtobufCharacteristic class RadioCharacteristic : public ProtobufCharacteristic
{ {
public: public:
RadioCharacteristic() RadioCharacteristic()
: ProtobufCharacteristic("b56786c8-839a-44a1-b98e-a1724c4a0262", BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ, RadioConfig_fields, &radioConfig) : ProtobufCharacteristic("b56786c8-839a-44a1-b98e-a1724c4a0262",
BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ, RadioConfig_fields,
&radioConfig)
{ {
} }
@ -127,31 +126,29 @@ class OwnerCharacteristic : public ProtobufCharacteristic
{ {
public: public:
OwnerCharacteristic() OwnerCharacteristic()
: ProtobufCharacteristic("6ff1d8b6-e2de-41e3-8c0b-8fa384f64eb6", BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ, User_fields, &owner) : ProtobufCharacteristic("6ff1d8b6-e2de-41e3-8c0b-8fa384f64eb6",
BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ, User_fields, &owner)
{ {
} }
void onWrite(BLECharacteristic *c) void onWrite(BLECharacteristic *c)
{ {
BLEKeepAliveCallbacks::onWrite(c); // NOTE: We do not call the standard ProtobufCharacteristic superclass, because we want custom write behavior BLEKeepAliveCallbacks::onWrite(
c); // NOTE: We do not call the standard ProtobufCharacteristic superclass, because we want custom write behavior
static User o; // if the phone doesn't set ID we are careful to keep ours, we also always keep our macaddr 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)) if (writeToDest(c, &o)) {
{
int changed = 0; int changed = 0;
if (*o.long_name) if (*o.long_name) {
{
changed |= strcmp(owner.long_name, o.long_name); changed |= strcmp(owner.long_name, o.long_name);
strcpy(owner.long_name, o.long_name); strcpy(owner.long_name, o.long_name);
} }
if (*o.short_name) if (*o.short_name) {
{
changed |= strcmp(owner.short_name, o.short_name); changed |= strcmp(owner.short_name, o.short_name);
strcpy(owner.short_name, o.short_name); strcpy(owner.short_name, o.short_name);
} }
if (*o.id) if (*o.id) {
{
changed |= strcmp(owner.id, o.id); changed |= strcmp(owner.id, o.id);
strcpy(owner.id, o.id); strcpy(owner.id, o.id);
} }
@ -165,10 +162,7 @@ public:
class ToRadioCharacteristic : public CallbackCharacteristic class ToRadioCharacteristic : public CallbackCharacteristic
{ {
public: public:
ToRadioCharacteristic() ToRadioCharacteristic() : CallbackCharacteristic("f75c76d2-129e-4dad-a1dd-7866124401e7", BLECharacteristic::PROPERTY_WRITE) {}
: CallbackCharacteristic("f75c76d2-129e-4dad-a1dd-7866124401e7", BLECharacteristic::PROPERTY_WRITE)
{
}
void onWrite(BLECharacteristic *c) void onWrite(BLECharacteristic *c)
{ {
@ -182,8 +176,7 @@ public:
class FromRadioCharacteristic : public CallbackCharacteristic class FromRadioCharacteristic : public CallbackCharacteristic
{ {
public: public:
FromRadioCharacteristic() FromRadioCharacteristic() : CallbackCharacteristic("8ba2bcc2-ee02-4a55-a531-c525c5e454d5", BLECharacteristic::PROPERTY_READ)
: CallbackCharacteristic("8ba2bcc2-ee02-4a55-a531-c525c5e454d5", BLECharacteristic::PROPERTY_READ)
{ {
} }
@ -194,13 +187,10 @@ public:
// Someone is going to read our value as soon as this callback returns. So fill it with the next message in the queue // 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 // or make empty if the queue is empty
if (!mp) if (!mp) {
{
DEBUG_MSG("toPhone queue is empty\n"); DEBUG_MSG("toPhone queue is empty\n");
c->setValue((uint8_t *)"", 0); c->setValue((uint8_t *)"", 0);
} } else {
else
{
static FromRadio fRadio; static FromRadio fRadio;
// Encapsulate as a FromRadio packet // Encapsulate as a FromRadio packet
@ -221,8 +211,9 @@ class FromNumCharacteristic : public CallbackCharacteristic
{ {
public: public:
FromNumCharacteristic() FromNumCharacteristic()
: CallbackCharacteristic("ed9da18c-a800-4f66-a670-aa7547e34453", : CallbackCharacteristic("ed9da18c-a800-4f66-a670-aa7547e34453", BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY) BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY)
{ {
} }
@ -240,8 +231,7 @@ FromNumCharacteristic *meshFromNumCharacteristic;
*/ */
void bluetoothNotifyFromNum(uint32_t newValue) void bluetoothNotifyFromNum(uint32_t newValue)
{ {
if (meshFromNumCharacteristic) if (meshFromNumCharacteristic) {
{
// if bt not running ignore // if bt not running ignore
meshFromNumCharacteristic->setValue(newValue); meshFromNumCharacteristic->setValue(newValue);
meshFromNumCharacteristic->notify(); meshFromNumCharacteristic->notify();
@ -265,7 +255,10 @@ BLEService *createMeshBluetoothService(BLEServer *server)
addWithDesc(service, new ToRadioCharacteristic, "toRadio"); addWithDesc(service, new ToRadioCharacteristic, "toRadio");
addWithDesc(service, new FromRadioCharacteristic, "fromNum"); addWithDesc(service, new FromRadioCharacteristic, "fromNum");
addWithDesc(service, new ProtobufCharacteristic("ea9f3f82-8dc4-4733-9452-1f6da28892a2", BLECharacteristic::PROPERTY_READ, MyNodeInfo_fields, &myNodeInfo), "myNode"); addWithDesc(service,
new ProtobufCharacteristic("ea9f3f82-8dc4-4733-9452-1f6da28892a2", BLECharacteristic::PROPERTY_READ,
MyNodeInfo_fields, &myNodeInfo),
"myNode");
addWithDesc(service, new RadioCharacteristic, "radio"); addWithDesc(service, new RadioCharacteristic, "radio");
addWithDesc(service, new OwnerCharacteristic, "owner"); addWithDesc(service, new OwnerCharacteristic, "owner");
addWithDesc(service, new NodeInfoCharacteristic, "nodeinfo"); addWithDesc(service, new NodeInfoCharacteristic, "nodeinfo");
@ -276,8 +269,7 @@ BLEService *createMeshBluetoothService(BLEServer *server)
// We only add to advertisting once, because the ESP32 arduino code is dumb and that object never dies // We only add to advertisting once, because the ESP32 arduino code is dumb and that object never dies
static bool firstTime = true; static bool firstTime = true;
if (firstTime) if (firstTime) {
{
firstTime = false; firstTime = false;
server->getAdvertising()->addServiceUUID(service->getUUID()); server->getAdvertising()->addServiceUUID(service->getUUID());
} }
@ -295,8 +287,6 @@ void stopMeshBluetoothService()
meshService->stop(); meshService->stop();
} }
void destroyMeshBluetoothService() void destroyMeshBluetoothService()
{ {
assert(meshService); assert(meshService);

View File

@ -1,8 +1,8 @@
#pragma once #pragma once
#include <BLEService.h>
#include <BLEServer.h>
#include <Arduino.h> #include <Arduino.h>
#include <BLEServer.h>
#include <BLEService.h>
BLEService *createMeshBluetoothService(BLEServer *server); BLEService *createMeshBluetoothService(BLEServer *server);
void destroyMeshBluetoothService(); void destroyMeshBluetoothService();

View File

@ -1,16 +1,17 @@
#include <SPI.h>
#include "RH_RF95.h" #include "RH_RF95.h"
#include <RHMesh.h> #include <RHMesh.h>
#include <SPI.h>
#include <assert.h> #include <assert.h>
#include <pb_encode.h>
#include <pb_decode.h>
#include "MeshRadio.h" #include "MeshRadio.h"
#include "configuration.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "configuration.h"
#include <pb_decode.h>
#include <pb_encode.h>
/// 16 bytes of random PSK for our _public_ default channel that all devices power up on /// 16 bytes of random PSK for our _public_ default channel that all devices power up on
static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59, 0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0xbf}; static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0xbf};
/** /**
* ## LoRaWAN for North America * ## LoRaWAN for North America
@ -19,21 +20,20 @@ LoRaWAN defines 64, 125 kHz channels from 902.3 to 914.9 MHz increments.
The maximum output power for North America is +30 dBM. The maximum output power for North America is +30 dBM.
The band is from 902 to 928 MHz. It mentions channel number and its respective channel frequency. All the 13 channels are separated by 2.16 MHz with respect to the adjacent channels. The band is from 902 to 928 MHz. It mentions channel number and its respective channel frequency. All the 13 channels are
Channel zero starts at 903.08 MHz center frequency. separated by 2.16 MHz with respect to the adjacent channels. Channel zero starts at 903.08 MHz center frequency.
*/ */
/// Sometimes while debugging it is useful to set this false, to disable rf95 accesses /// Sometimes while debugging it is useful to set this false, to disable rf95 accesses
bool useHardware = true; bool useHardware = true;
MeshRadio::MeshRadio(MemoryPool<MeshPacket> &_pool, PointerQueue<MeshPacket> &_rxDest) MeshRadio::MeshRadio(MemoryPool<MeshPacket> &_pool, PointerQueue<MeshPacket> &_rxDest) : rf95(_pool, _rxDest), manager(rf95)
: rf95(_pool, _rxDest),
manager(rf95)
{ {
myNodeInfo.num_channels = NUM_CHANNELS; myNodeInfo.num_channels = NUM_CHANNELS;
// radioConfig.modem_config = RadioConfig_ModemConfig_Bw125Cr45Sf128; // medium range and fast // radioConfig.modem_config = RadioConfig_ModemConfig_Bw125Cr45Sf128; // medium range and fast
//channelSettings.modem_config = ChannelSettings_ModemConfig_Bw500Cr45Sf128; // short range and fast, but wide bandwidth so incompatible radios can talk together // channelSettings.modem_config = ChannelSettings_ModemConfig_Bw500Cr45Sf128; // short range and fast, but wide bandwidth so
// incompatible radios can talk together
channelSettings.modem_config = ChannelSettings_ModemConfig_Bw125Cr48Sf4096; // slow and long range channelSettings.modem_config = ChannelSettings_ModemConfig_Bw125Cr48Sf4096; // slow and long range
channelSettings.tx_power = 23; channelSettings.tx_power = 23;
@ -61,10 +61,10 @@ bool MeshRadio::init()
delay(10); delay(10);
#endif #endif
manager.setThisAddress(nodeDB.getNodeNum()); // Note: we must do this here, because the nodenum isn't inited at constructor time. manager.setThisAddress(
nodeDB.getNodeNum()); // Note: we must do this here, because the nodenum isn't inited at constructor time.
if (!manager.init()) if (!manager.init()) {
{
DEBUG_MSG("LoRa radio init failed\n"); DEBUG_MSG("LoRa radio init failed\n");
DEBUG_MSG("Uncomment '#define SERIAL_DEBUG' in RH_RF95.cpp for detailed debug info\n"); DEBUG_MSG("Uncomment '#define SERIAL_DEBUG' in RH_RF95.cpp for detailed debug info\n");
return false; return false;
@ -100,15 +100,15 @@ void MeshRadio::reloadConfig()
// Set up default configuration // Set up default configuration
// No Sync Words in LORA mode. // No Sync Words in LORA mode.
rf95.setModemConfig((RH_RF95::ModemConfigChoice)channelSettings.modem_config); // Radio default rf95.setModemConfig(
(RH_RF95::ModemConfigChoice)channelSettings.modem_config); // Radio default
// setModemConfig(Bw125Cr48Sf4096); // slow and reliable? // setModemConfig(Bw125Cr48Sf4096); // slow and reliable?
// rf95.setPreambleLength(8); // Default is 8 // rf95.setPreambleLength(8); // Default is 8
// Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
int channel_num = hash(channelSettings.name) % NUM_CHANNELS; int channel_num = hash(channelSettings.name) % NUM_CHANNELS;
float center_freq = CH0 + CH_SPACING * channel_num; float center_freq = CH0 + CH_SPACING * channel_num;
if (!rf95.setFrequency(center_freq)) if (!rf95.setFrequency(center_freq)) {
{
DEBUG_MSG("setFrequency failed\n"); DEBUG_MSG("setFrequency failed\n");
assert(0); // fixme panic assert(0); // fixme panic
} }
@ -121,7 +121,8 @@ void MeshRadio::reloadConfig()
// FIXME - can we do this? It seems to be in the Heltec board. // FIXME - can we do this? It seems to be in the Heltec board.
rf95.setTxPower(channelSettings.tx_power, false); rf95.setTxPower(channelSettings.tx_power, false);
DEBUG_MSG("Set radio: name=%s. config=%u, ch=%d, txpower=%d\n", channelSettings.name, channelSettings.modem_config, channel_num, channelSettings.tx_power); DEBUG_MSG("Set radio: name=%s. config=%u, ch=%d, txpower=%d\n", channelSettings.name, channelSettings.modem_config,
channel_num, channelSettings.tx_power);
// Done with init tell radio to start receiving // Done with init tell radio to start receiving
rf95.setModeRx(); rf95.setModeRx();
@ -131,8 +132,7 @@ ErrorCode MeshRadio::send(MeshPacket *p)
{ {
if (useHardware) if (useHardware)
return rf95.send(p); return rf95.send(p);
else else {
{
rf95.pool.release(p); rf95.pool.release(p);
return ERRNO_OK; return ERRNO_OK;
} }

View File

@ -1,12 +1,12 @@
#pragma once #pragma once
#include "CustomRF95.h" #include "CustomRF95.h"
#include <RHMesh.h>
#include "MemoryPool.h" #include "MemoryPool.h"
#include "mesh.pb.h"
#include "PointerQueue.h"
#include "MeshTypes.h" #include "MeshTypes.h"
#include "PointerQueue.h"
#include "configuration.h" #include "configuration.h"
#include "mesh.pb.h"
#include <RHMesh.h>
// US channel settings // US channel settings
#define CH0_US 903.08f // MHz #define CH0_US 903.08f // MHz
@ -58,13 +58,14 @@
#error "HW_VERSION not set" #error "HW_VERSION not set"
#endif #endif
/** /**
* A raw low level interface to our mesh. Only understands nodenums and bytes (not protobufs or node ids) * A raw low level interface to our mesh. Only understands nodenums and bytes (not protobufs or node ids)
*/ */
class MeshRadio { class MeshRadio
{
public: public:
CustomRF95 rf95; // the raw radio interface - for now I'm leaving public - because this class is shrinking to be almost nothing CustomRF95
rf95; // the raw radio interface - for now I'm leaving public - because this class is shrinking to be almost nothing
/** pool is the pool we will alloc our rx packets from /** pool is the pool we will alloc our rx packets from
* rxDest is where we will send any rx packets, it becomes receivers responsibility to return packet to the pool * rxDest is where we will send any rx packets, it becomes receivers responsibility to return packet to the pool
@ -86,7 +87,6 @@ public:
void reloadConfig(); void reloadConfig();
private: private:
// RHDatagram manager; // RHDatagram manager;
// RHReliableDatagram manager; // don't use mesh yet // RHReliableDatagram manager; // don't use mesh yet
RHMesh manager; RHMesh manager;
@ -98,5 +98,3 @@ private:
/// enqueue a received packet in rxDest /// enqueue a received packet in rxDest
void handleReceive(MeshPacket *p); void handleReceive(MeshPacket *p);
}; };

View File

@ -2,37 +2,41 @@
#include <Arduino.h> #include <Arduino.h>
#include <assert.h> #include <assert.h>
#include "main.h"
#include "mesh-pb-constants.h"
#include "MeshService.h"
#include "MeshBluetoothService.h"
#include "NodeDB.h"
#include "GPS.h" #include "GPS.h"
#include "MeshBluetoothService.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "Periodic.h" #include "Periodic.h"
#include "PowerFSM.h" #include "PowerFSM.h"
#include "main.h"
#include "mesh-pb-constants.h"
/* /*
receivedPacketQueue - this is a queue of messages we've received from the mesh, which we are keeping to deliver to the phone. 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 alloced with new). It is implemented with a FreeRTos queue (wrapped with a little RTQueue class) of pointers to MeshPacket protobufs (which were
After a packet ptr is removed from the queue and processed it should be deleted. (eventually we should move sent packets into a 'sentToPhone' queue alloced with new). After a packet ptr is removed from the queue and processed it should be deleted. (eventually we should move
of packets we can delete just as soon as we are sure the phone has acked those packets - when the phone writes to FromNum) sent packets into a 'sentToPhone' queue of packets we can delete just as soon as we are sure the phone has acked those packets -
when the phone writes to FromNum)
mesh - an instance of Mesh class. Which manages the interface to the mesh radio library, reception of packets from other nodes, arbitrating to select mesh - an instance of Mesh class. Which manages the interface to the mesh radio library, reception of packets from other nodes,
a node number and keeping the current nodedb. arbitrating to select a node number and keeping the current nodedb.
*/ */
/* Broadcast when a newly powered mesh node wants to find a node num it can use /* Broadcast when a newly powered mesh node wants to find a node num it can use
The algoritm is as follows: The algoritm is as follows:
* when a node starts up, it broadcasts their user and the normal flow is for all other nodes to reply with their User as well (so the new node can build its node db) * when a node starts up, it broadcasts their user and the normal flow is for all other nodes to reply with their User as well (so
* If a node ever receives a User (not just the first broadcast) message where the sender node number equals our node number, that indicates a collision has occurred and the following steps should happen: the new node can build its node db)
* If a node ever receives a User (not just the first broadcast) message where the sender node number equals our node number, that
indicates a collision has occurred and the following steps should happen:
If the receiving node (that was already in the mesh)'s macaddr is LOWER than the new User who just tried to sign in: it gets to keep its nodenum. We send a broadcast message If the receiving node (that was already in the mesh)'s macaddr is LOWER than the new User who just tried to sign in: it gets to
of OUR User (we use a broadcast so that the other node can receive our message, considering we have the same id - it also serves to let observers correct their nodedb) - this case is rare so it should be okay. keep its nodenum. We send a broadcast message of OUR User (we use a broadcast so that the other node can receive our message,
considering we have the same id - it also serves to let observers correct their nodedb) - this case is rare so it should be okay.
If any node receives a User where the macaddr is GTE than their local macaddr, they have been vetoed and should pick a new random nodenum (filtering against whatever it knows about the nodedb) and If any node receives a User where the macaddr is GTE than their local macaddr, they have been vetoed and should pick a new random
rebroadcast their User. nodenum (filtering against whatever it knows about the nodedb) and rebroadcast their User.
FIXME in the initial proof of concept we just skip the entire want/deny flow and just hand pick node numbers at first. FIXME in the initial proof of concept we just skip the entire want/deny flow and just hand pick node numbers at first.
*/ */
@ -40,15 +44,15 @@ FIXME in the initial proof of concept we just skip the entire want/deny flow and
MeshService service; MeshService service;
// I think this is right, one packet for each of the three fifos + one packet being currently assembled for TX or RX // I think this is right, one packet for each of the three fifos + one packet being currently assembled for TX or RX
#define MAX_PACKETS (MAX_RX_TOPHONE + MAX_RX_FROMRADIO + MAX_TX_QUEUE + 2) // max number of packets which can be in flight (either queued from reception or queued for sending) #define MAX_PACKETS \
(MAX_RX_TOPHONE + MAX_RX_FROMRADIO + MAX_TX_QUEUE + \
2) // max number of packets which can be in flight (either queued from reception or queued for sending)
#define MAX_RX_FROMRADIO 4 // max number of packets destined to our queue, we dispatch packets quickly so it doesn't need to be big #define MAX_RX_FROMRADIO \
4 // max number of packets destined to our queue, we dispatch packets quickly so it doesn't need to be big
MeshService::MeshService() MeshService::MeshService()
: packetPool(MAX_PACKETS), : packetPool(MAX_PACKETS), toPhoneQueue(MAX_RX_TOPHONE), fromRadioQueue(MAX_RX_FROMRADIO), fromNum(0),
toPhoneQueue(MAX_RX_TOPHONE),
fromRadioQueue(MAX_RX_FROMRADIO),
fromNum(0),
radio(packetPool, fromRadioQueue) radio(packetPool, fromRadioQueue)
{ {
// assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro // assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro
@ -88,29 +92,25 @@ MeshPacket *MeshService::handleFromRadioUser(MeshPacket *mp)
// we win if we have a lower macaddr // we win if we have a lower macaddr
bool weWin = memcmp(&owner.macaddr, &mp->payload.variant.user.macaddr, sizeof(owner.macaddr)) < 0; bool weWin = memcmp(&owner.macaddr, &mp->payload.variant.user.macaddr, sizeof(owner.macaddr)) < 0;
if (isCollision) if (isCollision) {
{ if (weWin) {
if (weWin)
{
DEBUG_MSG("NOTE! Received a nodenum collision and we are vetoing\n"); DEBUG_MSG("NOTE! Received a nodenum collision and we are vetoing\n");
packetPool.release(mp); // discard it packetPool.release(mp); // discard it
mp = NULL; mp = NULL;
sendOurOwner(); // send our owner as a _broadcast_ because that other guy is mistakenly using our nodenum sendOurOwner(); // send our owner as a _broadcast_ because that other guy is mistakenly using our nodenum
} } else {
else
{
// we lost, we need to try for a new nodenum! // we lost, we need to try for a new nodenum!
DEBUG_MSG("NOTE! Received a nodenum collision we lost, so picking a new nodenum\n"); DEBUG_MSG("NOTE! Received a nodenum collision we lost, so picking a new nodenum\n");
nodeDB.updateFrom(*mp); // update the DB early - before trying to repick (so we don't select the same node number again) nodeDB.updateFrom(
*mp); // update the DB early - before trying to repick (so we don't select the same node number again)
nodeDB.pickNewNodeNum(); nodeDB.pickNewNodeNum();
sendOurOwner(); // broadcast our new attempt at a node number sendOurOwner(); // broadcast our new attempt at a node number
} }
} } else if (wasBroadcast) {
else if (wasBroadcast) // If we haven't yet abandoned the packet and it was a broadcast, reply (just to them) with our User record so they can
{ // build their DB
// If we haven't yet abandoned the packet and it was a broadcast, reply (just to them) with our User record so they can build their DB
// Someone just sent us a User, reply with our Owner // Someone just sent us a User, reply with our Owner
DEBUG_MSG("Received broadcast Owner from 0x%x, replying with our owner\n", mp->from); DEBUG_MSG("Received broadcast Owner from 0x%x, replying with our owner\n", mp->from);
@ -126,12 +126,10 @@ MeshPacket *MeshService::handleFromRadioUser(MeshPacket *mp)
void MeshService::handleIncomingPosition(MeshPacket *mp) void MeshService::handleIncomingPosition(MeshPacket *mp)
{ {
if (mp->has_payload && mp->payload.which_variant == SubPacket_position_tag) if (mp->has_payload && mp->payload.which_variant == SubPacket_position_tag) {
{
DEBUG_MSG("handled incoming position time=%u\n", mp->payload.variant.position.time); DEBUG_MSG("handled incoming position time=%u\n", mp->payload.variant.position.time);
if (mp->payload.variant.position.time) if (mp->payload.variant.position.time) {
{
struct timeval tv; struct timeval tv;
uint32_t secs = mp->payload.variant.position.time; uint32_t secs = mp->payload.variant.position.time;
@ -153,21 +151,18 @@ void MeshService::handleFromRadio(MeshPacket *mp)
if (!myNodeInfo.has_gps) if (!myNodeInfo.has_gps)
handleIncomingPosition(mp); handleIncomingPosition(mp);
if (mp->has_payload && mp->payload.which_variant == SubPacket_user_tag) if (mp->has_payload && mp->payload.which_variant == SubPacket_user_tag) {
{
mp = handleFromRadioUser(mp); mp = handleFromRadioUser(mp);
} }
// If we veto a received User packet, we don't put it into the DB or forward it to the phone (to prevent confusing it) // If we veto a received User packet, we don't put it into the DB or forward it to the phone (to prevent confusing it)
if (mp) if (mp) {
{
DEBUG_MSG("Forwarding to phone, from=0x%x, rx_time=%u\n", mp->from, mp->rx_time); DEBUG_MSG("Forwarding to phone, from=0x%x, rx_time=%u\n", mp->from, mp->rx_time);
nodeDB.updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio nodeDB.updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio
fromNum++; fromNum++;
if (toPhoneQueue.numFree() == 0) if (toPhoneQueue.numFree() == 0) {
{
DEBUG_MSG("NOTE: tophone queue is full, discarding oldest\n"); DEBUG_MSG("NOTE: tophone queue is full, discarding oldest\n");
MeshPacket *d = toPhoneQueue.dequeuePtr(0); MeshPacket *d = toPhoneQueue.dequeuePtr(0);
if (d) if (d)
@ -177,8 +172,7 @@ void MeshService::handleFromRadio(MeshPacket *mp)
if (mp->payload.want_response) if (mp->payload.want_response)
sendNetworkPing(mp->from); sendNetworkPing(mp->from);
} } else
else
DEBUG_MSG("Dropping vetoed User message\n"); DEBUG_MSG("Dropping vetoed User message\n");
} }
@ -186,8 +180,7 @@ void MeshService::handleFromRadio()
{ {
MeshPacket *mp; MeshPacket *mp;
uint32_t oldFromNum = fromNum; uint32_t oldFromNum = fromNum;
while ((mp = fromRadioQueue.dequeuePtr(0)) != NULL) while ((mp = fromRadioQueue.dequeuePtr(0)) != NULL) {
{
handleFromRadio(mp); handleFromRadio(mp);
} }
if (oldFromNum != fromNum) // We don't want to generate extra notifies for multiple new packets if (oldFromNum != fromNum) // We don't want to generate extra notifies for multiple new packets
@ -227,23 +220,20 @@ void MeshService::handleToRadio(std::string s)
{ {
static ToRadio r; // this is a static scratch object, any data must be copied elsewhere before returning static ToRadio r; // this is a static scratch object, any data must be copied elsewhere before returning
if (pb_decode_from_bytes((const uint8_t *)s.c_str(), s.length(), ToRadio_fields, &r)) if (pb_decode_from_bytes((const uint8_t *)s.c_str(), s.length(), ToRadio_fields, &r)) {
{ switch (r.which_variant) {
switch (r.which_variant) case ToRadio_packet_tag: {
{
case ToRadio_packet_tag:
{
// If our phone is sending a position, see if we can use it to set our RTC // If our phone is sending a position, see if we can use it to set our RTC
handleIncomingPosition(&r.variant.packet); // If it is a position packet, perhaps set our clock handleIncomingPosition(&r.variant.packet); // If it is a position packet, perhaps set our clock
r.variant.packet.rx_time = gps.getValidTime(); // Record the time the packet arrived from the phone (so we update our nodedb for the local node) r.variant.packet.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(r.variant.packet)); sendToMesh(packetPool.allocCopy(r.variant.packet));
bool loopback = false; // if true send any packet the phone sends back itself (for testing) bool loopback = false; // if true send any packet the phone sends back itself (for testing)
if (loopback) if (loopback) {
{
MeshPacket *mp = packetPool.allocCopy(r.variant.packet); MeshPacket *mp = packetPool.allocCopy(r.variant.packet);
handleFromRadio(mp); handleFromRadio(mp);
bluetoothNotifyFromNum(fromNum); // tell the phone a new packet arrived bluetoothNotifyFromNum(fromNum); // tell the phone a new packet arrived
@ -254,8 +244,7 @@ void MeshService::handleToRadio(std::string s)
DEBUG_MSG("Error: unexpected ToRadio variant\n"); DEBUG_MSG("Error: unexpected ToRadio variant\n");
break; break;
} }
} } else {
else {
DEBUG_MSG("Error: ignoring malformed toradio\n"); DEBUG_MSG("Error: ignoring malformed toradio\n");
} }
} }
@ -264,10 +253,10 @@ void MeshService::sendToMesh(MeshPacket *p)
{ {
nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...) nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...)
// Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other nodes shouldn't trust it anyways) // Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other
// Note: for now, we allow a device with a local GPS to include the time, so that gpsless devices can get time. // nodes shouldn't trust it anyways) Note: for now, we allow a device with a local GPS to include the time, so that gpsless
if (p->has_payload && p->payload.which_variant == SubPacket_position_tag) // devices can get time.
{ if (p->has_payload && p->payload.which_variant == SubPacket_position_tag) {
if (!myNodeInfo.has_gps) if (!myNodeInfo.has_gps)
p->payload.variant.position.time = 0; p->payload.variant.position.time = 0;
else else
@ -277,8 +266,7 @@ void MeshService::sendToMesh(MeshPacket *p)
// If the phone sent a packet just to us, don't send it out into the network // If the phone sent a packet just to us, don't send it out into the network
if (p->to == nodeDB.getNodeNum()) if (p->to == nodeDB.getNodeNum())
DEBUG_MSG("Dropping locally processed message\n"); DEBUG_MSG("Dropping locally processed message\n");
else else {
{
// Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it // Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it
if (radio.send(p) != ERRNO_OK) if (radio.send(p) != ERRNO_OK)
DEBUG_MSG("Dropped packet because send queue was full!\n"); DEBUG_MSG("Dropped packet because send queue was full!\n");
@ -319,7 +307,8 @@ void MeshService::sendOurPosition(NodeNum dest)
p->to = dest; p->to = dest;
p->payload.which_variant = SubPacket_position_tag; p->payload.which_variant = SubPacket_position_tag;
p->payload.variant.position = node->position; p->payload.variant.position = node->position;
p->payload.variant.position.time = gps.getValidTime(); // This nodedb timestamp might be stale, so update it if our clock is valid. p->payload.variant.position.time =
gps.getValidTime(); // This nodedb timestamp might be stale, so update it if our clock is valid.
sendToMesh(p); sendToMesh(p);
} }
@ -342,15 +331,12 @@ void MeshService::onGPSChanged()
// We limit our GPS broadcasts to a max rate // We limit our GPS broadcasts to a max rate
static uint32_t lastGpsSend; static uint32_t lastGpsSend;
uint32_t now = millis(); uint32_t now = millis();
if (lastGpsSend == 0 || now - lastGpsSend > radioConfig.preferences.position_broadcast_secs * 1000) if (lastGpsSend == 0 || now - lastGpsSend > radioConfig.preferences.position_broadcast_secs * 1000) {
{
lastGpsSend = now; lastGpsSend = now;
DEBUG_MSG("Sending position to mesh\n"); DEBUG_MSG("Sending position to mesh\n");
sendToMesh(p); sendToMesh(p);
} } else {
else
{
// We don't need to send this packet to anyone else, but it still serves as a nice uniform way to update our local state // We don't need to send this packet to anyone else, but it still serves as a nice uniform way to update our local state
nodeDB.updateFrom(*p); nodeDB.updateFrom(*p);

View File

@ -3,11 +3,11 @@
#include <Arduino.h> #include <Arduino.h>
#include <assert.h> #include <assert.h>
#include "mesh.pb.h"
#include "MeshRadio.h"
#include "PointerQueue.h"
#include "MemoryPool.h" #include "MemoryPool.h"
#include "MeshRadio.h"
#include "Observer.h" #include "Observer.h"
#include "PointerQueue.h"
#include "mesh.pb.h"
/** /**
* Top level app for this service. keeps the mesh, the radio config and the queue of received packets. * Top level app for this service. keeps the mesh, the radio config and the queue of received packets.
@ -31,7 +31,6 @@ class MeshService: private Observer
uint32_t fromNum; uint32_t fromNum;
public: public:
MeshRadio radio; MeshRadio radio;
MeshService(); MeshService();
@ -60,17 +59,20 @@ public:
/// Allocate and return a meshpacket which defaults as send to broadcast from the current node. /// Allocate and return a meshpacket which defaults as send to broadcast from the current node.
MeshPacket *allocForSending(); MeshPacket *allocForSending();
/// Called when the user wakes up our GUI, normally sends our latest location to the mesh (if we have it), otherwise at least sends our owner /// Called when the user wakes up our GUI, normally sends our latest location to the mesh (if we have it), otherwise at least
/// sends our owner
void sendNetworkPing(NodeNum dest = NODENUM_BROADCAST); void sendNetworkPing(NodeNum dest = NODENUM_BROADCAST);
/// Send our owner info to a particular node /// Send our owner info to a particular node
void sendOurOwner(NodeNum dest = NODENUM_BROADCAST); void sendOurOwner(NodeNum dest = NODENUM_BROADCAST);
private: private:
/// Broadcasts our last known position /// Broadcasts our last known position
void sendOurPosition(NodeNum dest = NODENUM_BROADCAST); void sendOurPosition(NodeNum dest = NODENUM_BROADCAST);
/// Send a packet into the mesh - note p must have been allocated from packetPool. We will return it to that pool after sending. /// Send a packet into the mesh - note p must have been allocated from packetPool. We will return it to that pool after
/// This is the ONLY function you should use for sending messages into the mesh, because it also updates the nodedb cache /// sending. This is the ONLY function you should use for sending messages into the mesh, because it also updates the nodedb
/// cache
void sendToMesh(MeshPacket *p); void sendToMesh(MeshPacket *p);
/// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh /// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh
@ -92,4 +94,3 @@ private:
}; };
extern MeshService service; extern MeshService service;

View File

@ -5,13 +5,13 @@
#include "FS.h" #include "FS.h"
#include "SPIFFS.h" #include "SPIFFS.h"
#include <pb_encode.h> #include "GPS.h"
#include <pb_decode.h> #include "NodeDB.h"
#include "PowerFSM.h"
#include "configuration.h" #include "configuration.h"
#include "mesh-pb-constants.h" #include "mesh-pb-constants.h"
#include "NodeDB.h" #include <pb_decode.h>
#include "GPS.h" #include <pb_encode.h>
#include "PowerFSM.h"
NodeDB nodeDB; NodeDB nodeDB;
@ -41,9 +41,7 @@ User &owner = devicestate.owner;
static uint8_t ourMacAddr[6]; static uint8_t ourMacAddr[6];
NodeDB::NodeDB() : nodes(devicestate.node_db), numNodes(&devicestate.node_db_count) NodeDB::NodeDB() : nodes(devicestate.node_db), numNodes(&devicestate.node_db_count) {}
{
}
void NodeDB::init() void NodeDB::init()
{ {
@ -78,8 +76,8 @@ void NodeDB::init()
// Init our blank owner info to reasonable defaults // Init our blank owner info to reasonable defaults
esp_efuse_mac_get_default(ourMacAddr); esp_efuse_mac_get_default(ourMacAddr);
sprintf(owner.id, "!%02x%02x%02x%02x%02x%02x", ourMacAddr[0], sprintf(owner.id, "!%02x%02x%02x%02x%02x%02x", ourMacAddr[0], ourMacAddr[1], ourMacAddr[2], ourMacAddr[3], ourMacAddr[4],
ourMacAddr[1], ourMacAddr[2], ourMacAddr[3], ourMacAddr[4], ourMacAddr[5]); ourMacAddr[5]);
memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr)); memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr));
// make each node start with ad different random seed (but okay that the sequence is the same each boot) // make each node start with ad different random seed (but okay that the sequence is the same each boot)
@ -122,8 +120,7 @@ void NodeDB::pickNewNodeNum()
r = NUM_RESERVED; // don't pick a reserved node number r = NUM_RESERVED; // don't pick a reserved node number
NodeInfo *found; NodeInfo *found;
while ((found = getNode(r)) && memcmp(found->user.macaddr, owner.macaddr, sizeof(owner.macaddr))) while ((found = getNode(r)) && memcmp(found->user.macaddr, owner.macaddr, sizeof(owner.macaddr))) {
{
NodeNum n = random(NUM_RESERVED, NODENUM_BROADCAST); // try a new random choice NodeNum n = random(NUM_RESERVED, NODENUM_BROADCAST); // try a new random choice
DEBUG_MSG("NOTE! Our desired nodenum 0x%x is in use, so trying for 0x%x\n", r, n); DEBUG_MSG("NOTE! Our desired nodenum 0x%x is in use, so trying for 0x%x\n", r, n);
r = n; r = n;
@ -140,25 +137,20 @@ void NodeDB::loadFromDisk()
static DeviceState scratch; static DeviceState scratch;
File f = FS.open(preffile); File f = FS.open(preffile);
if (f) if (f) {
{
DEBUG_MSG("Loading saved preferences\n"); DEBUG_MSG("Loading saved preferences\n");
pb_istream_t stream = {&readcb, &f, DeviceState_size}; pb_istream_t stream = {&readcb, &f, DeviceState_size};
// DEBUG_MSG("Preload channel name=%s\n", channelSettings.name); // DEBUG_MSG("Preload channel name=%s\n", channelSettings.name);
memset(&scratch, 0, sizeof(scratch)); memset(&scratch, 0, sizeof(scratch));
if (!pb_decode(&stream, DeviceState_fields, &scratch)) if (!pb_decode(&stream, DeviceState_fields, &scratch)) {
{
DEBUG_MSG("Error: can't decode protobuf %s\n", PB_GET_ERROR(&stream)); DEBUG_MSG("Error: can't decode protobuf %s\n", PB_GET_ERROR(&stream));
// FIXME - report failure to phone // FIXME - report failure to phone
} } else {
else
{
if (scratch.version < DEVICESTATE_MIN_VER) if (scratch.version < DEVICESTATE_MIN_VER)
DEBUG_MSG("Warn: devicestate is old, discarding\n"); DEBUG_MSG("Warn: devicestate is old, discarding\n");
else else {
{
DEBUG_MSG("Loaded saved preferences version %d\n", scratch.version); DEBUG_MSG("Loaded saved preferences version %d\n", scratch.version);
devicestate = scratch; devicestate = scratch;
} }
@ -167,9 +159,7 @@ void NodeDB::loadFromDisk()
} }
f.close(); f.close();
} } else {
else
{
DEBUG_MSG("No saved preferences found\n"); DEBUG_MSG("No saved preferences found\n");
} }
} }
@ -177,8 +167,7 @@ void NodeDB::loadFromDisk()
void NodeDB::saveToDisk() void NodeDB::saveToDisk()
{ {
File f = FS.open(preftmp, "w"); File f = FS.open(preftmp, "w");
if (f) if (f) {
{
DEBUG_MSG("Writing preferences\n"); DEBUG_MSG("Writing preferences\n");
pb_ostream_t stream = {&writecb, &f, SIZE_MAX, 0}; pb_ostream_t stream = {&writecb, &f, SIZE_MAX, 0};
@ -186,8 +175,7 @@ void NodeDB::saveToDisk()
// DEBUG_MSG("Presave channel name=%s\n", channelSettings.name); // DEBUG_MSG("Presave channel name=%s\n", channelSettings.name);
devicestate.version = DEVICESTATE_CUR_VER; devicestate.version = DEVICESTATE_CUR_VER;
if (!pb_encode(&stream, DeviceState_fields, &devicestate)) if (!pb_encode(&stream, DeviceState_fields, &devicestate)) {
{
DEBUG_MSG("Error: can't write protobuf %s\n", PB_GET_ERROR(&stream)); DEBUG_MSG("Error: can't write protobuf %s\n", PB_GET_ERROR(&stream));
// FIXME - report failure to phone // FIXME - report failure to phone
} }
@ -199,9 +187,7 @@ void NodeDB::saveToDisk()
DEBUG_MSG("Warning: Can't remove old pref file\n"); DEBUG_MSG("Warning: Can't remove old pref file\n");
if (!FS.rename(preftmp, preffile)) if (!FS.rename(preftmp, preffile))
DEBUG_MSG("Error: can't rename new pref file\n"); DEBUG_MSG("Error: can't rename new pref file\n");
} } else {
else
{
DEBUG_MSG("ERROR: can't write prefs\n"); // FIXME report to app DEBUG_MSG("ERROR: can't write prefs\n"); // FIXME report to app
} }
} }
@ -245,8 +231,7 @@ size_t NodeDB::getNumOnlineNodes()
/// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw /// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw
void NodeDB::updateFrom(const MeshPacket &mp) void NodeDB::updateFrom(const MeshPacket &mp)
{ {
if (mp.has_payload) if (mp.has_payload) {
{
const SubPacket &p = mp.payload; const SubPacket &p = mp.payload;
DEBUG_MSG("Update DB node 0x%x for variant %d, rx_time=%u\n", mp.from, p.which_variant, mp.rx_time); DEBUG_MSG("Update DB node 0x%x for variant %d, rx_time=%u\n", mp.from, p.which_variant, mp.rx_time);
@ -256,16 +241,13 @@ void NodeDB::updateFrom(const MeshPacket &mp)
if (oldNumNodes != *numNodes) if (oldNumNodes != *numNodes)
updateGUI = true; // we just created a nodeinfo updateGUI = true; // we just created a nodeinfo
if (mp.rx_time) if (mp.rx_time) { // if the packet has a valid timestamp use it to update our last_seen
{ // if the packet has a valid timestamp use it to update our last_seen
info->has_position = true; // at least the time is valid info->has_position = true; // at least the time is valid
info->position.time = mp.rx_time; info->position.time = mp.rx_time;
} }
switch (p.which_variant) switch (p.which_variant) {
{ case SubPacket_position_tag: {
case SubPacket_position_tag:
{
// we carefully preserve the old time, because we always trust our local timestamps more // we carefully preserve the old time, because we always trust our local timestamps more
uint32_t oldtime = info->position.time; uint32_t oldtime = info->position.time;
info->position = p.variant.position; info->position = p.variant.position;
@ -275,14 +257,12 @@ void NodeDB::updateFrom(const MeshPacket &mp)
break; break;
} }
case SubPacket_data_tag: case SubPacket_data_tag: {
{
// Keep a copy of the most recent text message. // Keep a copy of the most recent text message.
if (p.variant.data.typ == Data_Type_CLEAR_TEXT) if (p.variant.data.typ == Data_Type_CLEAR_TEXT) {
{ DEBUG_MSG("Received text msg from=0%0x, msg=%.*s\n", mp.from, p.variant.data.payload.size,
DEBUG_MSG("Received text msg from=0%0x, msg=%.*s\n", mp.from, p.variant.data.payload.size, p.variant.data.payload.bytes); p.variant.data.payload.bytes);
if (mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum()) if (mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum()) {
{
// We only store/display messages destined for us. // We only store/display messages destined for us.
devicestate.rx_text_message = mp; devicestate.rx_text_message = mp;
devicestate.has_rx_text_message = true; devicestate.has_rx_text_message = true;
@ -293,18 +273,17 @@ void NodeDB::updateFrom(const MeshPacket &mp)
break; break;
} }
case SubPacket_user_tag: case SubPacket_user_tag: {
{
DEBUG_MSG("old user %s/%s/%s\n", info->user.id, info->user.long_name, info->user.short_name); DEBUG_MSG("old user %s/%s/%s\n", info->user.id, info->user.long_name, info->user.short_name);
bool changed = memcmp(&info->user, &p.variant.user, sizeof(info->user)); // Both of these blocks start as filled with zero so I think this is okay bool changed = memcmp(&info->user, &p.variant.user,
sizeof(info->user)); // Both of these blocks start as filled with zero so I think this is okay
info->user = p.variant.user; info->user = p.variant.user;
DEBUG_MSG("updating changed=%d user %s/%s/%s\n", changed, info->user.id, info->user.long_name, info->user.short_name); DEBUG_MSG("updating changed=%d user %s/%s/%s\n", changed, info->user.id, info->user.long_name, info->user.short_name);
info->has_user = true; info->has_user = true;
if (changed) if (changed) {
{
updateGUIforNode = info; updateGUIforNode = info;
powerFSM.trigger(EVENT_NODEDB_UPDATED); powerFSM.trigger(EVENT_NODEDB_UPDATED);
@ -337,8 +316,7 @@ NodeInfo *NodeDB::getOrCreateNode(NodeNum n)
{ {
NodeInfo *info = getNode(n); NodeInfo *info = getNode(n);
if (!info) if (!info) {
{
// add the node // add the node
assert(*numNodes < MAX_NUM_NODES); assert(*numNodes < MAX_NUM_NODES);
info = &nodes[(*numNodes)++]; info = &nodes[(*numNodes)++];

View File

@ -3,8 +3,8 @@
#include <Arduino.h> #include <Arduino.h>
#include <assert.h> #include <assert.h>
#include "mesh-pb-constants.h"
#include "MeshTypes.h" #include "MeshTypes.h"
#include "mesh-pb-constants.h"
extern DeviceState devicestate; extern DeviceState devicestate;
extern MyNodeInfo &myNodeInfo; extern MyNodeInfo &myNodeInfo;
@ -56,10 +56,11 @@ public:
// bool handleWantNodeNum(NodeNum n); // bool handleWantNodeNum(NodeNum n);
/* void handleDenyNodeNum(NodeNum FIXME read mesh proto docs, perhaps picking a random node num is not a great idea /* void handleDenyNodeNum(NodeNum FIXME read mesh proto docs, perhaps picking a random node num is not a great idea
and instead we should use a special 'im unconfigured node number' and include our desired node number in the wantnum message. the and instead we should use a special 'im unconfigured node number' and include our desired node number in the wantnum message.
unconfigured node num would only be used while initially joining the mesh so low odds of conflicting (especially if we randomly select the unconfigured node num would only be used while initially joining the mesh so low odds of conflicting (especially if we
from a small number of nodenums which can be used temporarily for this operation). figure out what the lower level randomly select from a small number of nodenums which can be used temporarily for this operation). figure out what the lower
mesh sw does if it does conflict? would it be better for people who are replying with denynode num to just broadcast their denial?) level mesh sw does if it does conflict? would it be better for people who are replying with denynode num to just broadcast
their denial?)
*/ */
/// Called from bluetooth when the user wants to start reading the node DB from scratch. /// Called from bluetooth when the user wants to start reading the node DB from scratch.
@ -74,13 +75,16 @@ public:
/// Find a node in our DB, return null for missing /// Find a node in our DB, return null for missing
NodeInfo *getNode(NodeNum n); NodeInfo *getNode(NodeNum n);
NodeInfo *getNodeByIndex(size_t x) { assert(x < *numNodes); return &nodes[x]; } NodeInfo *getNodeByIndex(size_t x)
{
assert(x < *numNodes);
return &nodes[x];
}
/// Return the number of nodes we've heard from recently (within the last 2 hrs?) /// Return the number of nodes we've heard from recently (within the last 2 hrs?)
size_t getNumOnlineNodes(); size_t getNumOnlineNodes();
private: private:
/// Find a node in our DB, create an empty NodeInfo if missing /// Find a node in our DB, create an empty NodeInfo if missing
NodeInfo *getOrCreateNode(NodeNum n); NodeInfo *getOrCreateNode(NodeNum n);
@ -89,4 +93,3 @@ private:
}; };
extern NodeDB nodeDB; extern NodeDB nodeDB;

View File

@ -30,20 +30,12 @@ class Observable
public: public:
void notifyObservers() void notifyObservers()
{ {
for (std::list<Observer *>::const_iterator iterator = observers.begin(); iterator != observers.end(); ++iterator) for (std::list<Observer *>::const_iterator iterator = observers.begin(); iterator != observers.end(); ++iterator) {
{
(*iterator)->onNotify(this); (*iterator)->onNotify(this);
} }
} }
void addObserver(Observer *o) void addObserver(Observer *o) { observers.push_back(o); }
{
observers.push_back(o);
}
void removeObserver(Observer *o) void removeObserver(Observer *o) { observers.remove(o); }
{
observers.remove(o);
}
}; };

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <Arduino.h>
#include "PeriodicTask.h" #include "PeriodicTask.h"
#include <Arduino.h>
/** /**
* Periodically invoke a callback. * Periodically invoke a callback.
@ -17,6 +17,5 @@ public:
Periodic(uint32_t (*_callback)()) : callback(_callback) {} Periodic(uint32_t (*_callback)()) : callback(_callback) {}
protected: protected:
void doTask(); void doTask();
}; };

View File

@ -5,13 +5,10 @@
/** /**
* A wrapper for freertos queues that assumes each element is a pointer * A wrapper for freertos queues that assumes each element is a pointer
*/ */
template <class T> template <class T> class PointerQueue : public TypedQueue<T *>
class PointerQueue : public TypedQueue<T *>
{ {
public: public:
PointerQueue(int maxElements) : TypedQueue<T *>(maxElements) PointerQueue(int maxElements) : TypedQueue<T *>(maxElements) {}
{
}
// returns a ptr or null if the queue was empty // returns a ptr or null if the queue was empty
T *dequeuePtr(TickType_t maxWait = portMAX_DELAY) T *dequeuePtr(TickType_t maxWait = portMAX_DELAY)

View File

@ -124,9 +124,7 @@ static void screenPress()
screen.onPress(); screen.onPress();
} }
static void bootEnter() {}
static void bootEnter() {
}
State stateSDS(sdsEnter, NULL, NULL, "SDS"); State stateSDS(sdsEnter, NULL, NULL, "SDS");
State stateLS(lsEnter, lsIdle, lsExit, "LS"); State stateLS(lsEnter, lsIdle, lsExit, "LS");
@ -138,8 +136,7 @@ Fsm powerFSM(&stateBOOT);
void PowerFSM_setup() void PowerFSM_setup()
{ {
powerFSM.add_timed_transition(&stateBOOT, &stateON, 3 * 1000, NULL, powerFSM.add_timed_transition(&stateBOOT, &stateON, 3 * 1000, NULL, "boot timeout");
"boot timeout");
powerFSM.add_transition(&stateLS, &stateDARK, EVENT_WAKE_TIMER, wakeForPing, "Wake timer"); powerFSM.add_transition(&stateLS, &stateDARK, EVENT_WAKE_TIMER, wakeForPing, "Wake timer");

View File

@ -10,8 +10,7 @@
* A wrapper for freertos queues. Note: each element object should be small * A wrapper for freertos queues. Note: each element object should be small
* and POD (Plain Old Data type) as elements are memcpied by value. * and POD (Plain Old Data type) as elements are memcpied by value.
*/ */
template <class T> template <class T> class TypedQueue
class TypedQueue
{ {
static_assert(std::is_pod<T>::value, "T must be pod"); static_assert(std::is_pod<T>::value, "T must be pod");
QueueHandle_t h; QueueHandle_t h;
@ -23,38 +22,17 @@ class TypedQueue
assert(h); assert(h);
} }
~TypedQueue() ~TypedQueue() { vQueueDelete(h); }
{
vQueueDelete(h);
}
int numFree() int numFree() { return uxQueueSpacesAvailable(h); }
{
return uxQueueSpacesAvailable(h);
}
bool isEmpty() bool isEmpty() { return uxQueueMessagesWaiting(h) == 0; }
{
return uxQueueMessagesWaiting(h) == 0;
}
bool enqueue(T x, TickType_t maxWait = portMAX_DELAY) bool enqueue(T x, TickType_t maxWait = portMAX_DELAY) { return xQueueSendToBack(h, &x, maxWait) == pdTRUE; }
{
return xQueueSendToBack(h, &x, maxWait) == pdTRUE;
}
bool enqueueFromISR(T x, BaseType_t *higherPriWoken) bool enqueueFromISR(T x, BaseType_t *higherPriWoken) { return xQueueSendToBackFromISR(h, &x, higherPriWoken) == pdTRUE; }
{
return xQueueSendToBackFromISR(h, &x, higherPriWoken) == pdTRUE;
}
bool dequeue(T *p, TickType_t maxWait = portMAX_DELAY) bool dequeue(T *p, TickType_t maxWait = portMAX_DELAY) { return xQueueReceive(h, p, maxWait) == pdTRUE; }
{
return xQueueReceive(h, p, maxWait) == pdTRUE;
}
bool dequeueFromISR(T *p, BaseType_t *higherPriWoken) bool dequeueFromISR(T *p, BaseType_t *higherPriWoken) { return xQueueReceiveFromISR(h, p, higherPriWoken); }
{
return xQueueReceiveFromISR(h, p, higherPriWoken);
}
}; };

View File

@ -41,10 +41,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Select which board is being used. If the outside build environment has sent a choice, just use that // Select which board is being used. If the outside build environment has sent a choice, just use that
#if !defined(T_BEAM_V10) && !defined(HELTEC_LORA32) #if !defined(T_BEAM_V10) && !defined(HELTEC_LORA32)
// #define T_BEAM_V10 // AKA Rev1 (second board released) #define T_BEAM_V10 // AKA Rev1 (second board released)
#define HELTEC_LORA32 // #define HELTEC_LORA32
#define HW_VERSION_US // We encode the hardware freq range in the hw version string, so sw update can eventually install the correct build #define HW_VERSION_US // We encode the hardware freq range in the hw version string, so sw update can eventually install the
// correct build
#endif #endif
// If we are using the JTAG port for debugging, some pins must be left free for that (and things like GPS have to be disabled) // If we are using the JTAG port for debugging, some pins must be left free for that (and things like GPS have to be disabled)

View File

@ -1,11 +1,8 @@
#define SATELLITE_IMAGE_WIDTH 16 #define SATELLITE_IMAGE_WIDTH 16
#define SATELLITE_IMAGE_HEIGHT 15 #define SATELLITE_IMAGE_HEIGHT 15
const uint8_t SATELLITE_IMAGE[] PROGMEM = { const uint8_t SATELLITE_IMAGE[] PROGMEM = {0x00, 0x08, 0x00, 0x1C, 0x00, 0x0E, 0x20, 0x07, 0x70, 0x02,
0x00, 0x08, 0x00, 0x1C, 0x00, 0x0E, 0x20, 0x07, 0x70, 0x02, 0xF8, 0x00, 0xF8, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC8, 0x01, 0x9C, 0x54,
0xF0, 0x01, 0xE0, 0x03, 0xC8, 0x01, 0x9C, 0x54, 0x0E, 0x52, 0x07, 0x48, 0x0E, 0x52, 0x07, 0x48, 0x02, 0x26, 0x00, 0x10, 0x00, 0x0E};
0x02, 0x26, 0x00, 0x10, 0x00, 0x0E
};
const const
#include "icon.xbm" #include "icon.xbm"

View File

@ -21,22 +21,22 @@
*/ */
#include "configuration.h"
#include "rom/rtc.h"
#include <driver/rtc_io.h>
#include <Wire.h>
#include "BluetoothUtil.h" #include "BluetoothUtil.h"
#include "MeshBluetoothService.h"
#include "MeshService.h"
#include "GPS.h" #include "GPS.h"
#include "screen.h" #include "MeshBluetoothService.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "Periodic.h" #include "Periodic.h"
#include "PowerFSM.h"
#include "configuration.h"
#include "esp32/pm.h" #include "esp32/pm.h"
#include "esp_pm.h" #include "esp_pm.h"
#include "MeshRadio.h" #include "rom/rtc.h"
#include "screen.h"
#include "sleep.h" #include "sleep.h"
#include "PowerFSM.h" #include <Wire.h>
#include <driver/rtc_io.h>
#ifdef T_BEAM_V10 #ifdef T_BEAM_V10
#include "axp20x.h" #include "axp20x.h"
@ -69,31 +69,25 @@ void scanI2Cdevice(void)
{ {
byte err, addr; byte err, addr;
int nDevices = 0; int nDevices = 0;
for (addr = 1; addr < 127; addr++) for (addr = 1; addr < 127; addr++) {
{
Wire.beginTransmission(addr); Wire.beginTransmission(addr);
err = Wire.endTransmission(); err = Wire.endTransmission();
if (err == 0) if (err == 0) {
{
DEBUG_MSG("I2C device found at address 0x%x\n", addr); DEBUG_MSG("I2C device found at address 0x%x\n", addr);
nDevices++; nDevices++;
if (addr == SSD1306_ADDRESS) if (addr == SSD1306_ADDRESS) {
{
ssd1306_found = true; ssd1306_found = true;
DEBUG_MSG("ssd1306 display found\n"); DEBUG_MSG("ssd1306 display found\n");
} }
#ifdef T_BEAM_V10 #ifdef T_BEAM_V10
if (addr == AXP192_SLAVE_ADDRESS) if (addr == AXP192_SLAVE_ADDRESS) {
{
axp192_found = true; axp192_found = true;
DEBUG_MSG("axp192 PMU found\n"); DEBUG_MSG("axp192 PMU found\n");
} }
#endif #endif
} } else if (err == 4) {
else if (err == 4)
{
DEBUG_MSG("Unknow error at address 0x%x\n", addr); DEBUG_MSG("Unknow error at address 0x%x\n", addr);
} }
} }
@ -107,20 +101,16 @@ void scanI2Cdevice(void)
* Init the power manager chip * Init the power manager chip
* *
* axp192 power * axp192 power
DCDC1 0.7-3.5V @ 1200mA max -> OLED // If you turn this off you'll lose comms to the axp192 because the OLED and the axp192 share the same i2c bus, instead use ssd1306 sleep mode DCDC1 0.7-3.5V @ 1200mA max -> OLED // If you turn this off you'll lose comms to the axp192 because the OLED and the axp192
DCDC2 -> unused share the same i2c bus, instead use ssd1306 sleep mode DCDC2 -> unused DCDC3 0.7-3.5V @ 700mA max -> ESP32 (keep this on!) LDO1
DCDC3 0.7-3.5V @ 700mA max -> ESP32 (keep this on!) 30mA -> charges GPS backup battery // charges the tiny J13 battery by the GPS to power the GPS ram (for a couple of days), can
LDO1 30mA -> charges GPS backup battery // charges the tiny J13 battery by the GPS to power the GPS ram (for a couple of days), can not be turned off not be turned off LDO2 200mA -> LORA LDO3 200mA -> GPS
LDO2 200mA -> LORA
LDO3 200mA -> GPS
*/ */
void axp192Init() void axp192Init()
{ {
#ifdef T_BEAM_V10 #ifdef T_BEAM_V10
if (axp192_found) if (axp192_found) {
{ if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) {
if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS))
{
DEBUG_MSG("AXP192 Begin PASS\n"); DEBUG_MSG("AXP192 Begin PASS\n");
// axp.setChgLEDMode(LED_BLINK_4HZ); // axp.setChgLEDMode(LED_BLINK_4HZ);
@ -169,26 +159,21 @@ void axp192Init()
#ifdef PMU_IRQ #ifdef PMU_IRQ
pinMode(PMU_IRQ, INPUT_PULLUP); pinMode(PMU_IRQ, INPUT_PULLUP);
attachInterrupt(PMU_IRQ, [] { attachInterrupt(
pmu_irq = true; PMU_IRQ, [] { pmu_irq = true; }, RISING);
},
RISING);
axp.adc1Enable(AXP202_BATT_CUR_ADC1, 1); axp.adc1Enable(AXP202_BATT_CUR_ADC1, 1);
axp.enableIRQ(AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ, 1); axp.enableIRQ(AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ,
1);
axp.clearIRQ(); axp.clearIRQ();
#endif #endif
isCharging = axp.isChargeing() ? 1 : 0; isCharging = axp.isChargeing() ? 1 : 0;
isUSBPowered = axp.isVBUSPlug() ? 1 : 0; isUSBPowered = axp.isVBUSPlug() ? 1 : 0;
} } else {
else
{
DEBUG_MSG("AXP192 Begin FAIL\n"); DEBUG_MSG("AXP192 Begin FAIL\n");
} }
} } else {
else
{
DEBUG_MSG("AXP192 not found\n"); DEBUG_MSG("AXP192 not found\n");
} }
#endif #endif
@ -279,8 +264,8 @@ void initBluetooth()
powerFSM.trigger(EVENT_BLUETOOTH_PAIR); powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
screen.startBluetoothPinScreen(pin); screen.startBluetoothPinScreen(pin);
}, },
[]() { screen.stopBluetoothPinScreen(); }, []() { screen.stopBluetoothPinScreen(); }, getDeviceName(), HW_VENDOR, xstr(APP_VERSION),
getDeviceName(), HW_VENDOR, xstr(APP_VERSION), xstr(HW_VERSION)); // FIXME, use a real name based on the macaddr xstr(HW_VERSION)); // FIXME, use a real name based on the macaddr
createMeshBluetoothService(serve); createMeshBluetoothService(serve);
// Start advertising - this must be done _after_ creating all services // Start advertising - this must be done _after_ creating all services
@ -289,19 +274,15 @@ void initBluetooth()
void setBluetoothEnable(bool on) void setBluetoothEnable(bool on)
{ {
if (on != bluetoothOn) if (on != bluetoothOn) {
{
DEBUG_MSG("Setting bluetooth enable=%d\n", on); DEBUG_MSG("Setting bluetooth enable=%d\n", on);
bluetoothOn = on; bluetoothOn = on;
if (on) if (on) {
{
Serial.printf("Pre BT: %u heap size\n", ESP.getFreeHeap()); Serial.printf("Pre BT: %u heap size\n", ESP.getFreeHeap());
// ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) ); // ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
initBluetooth(); initBluetooth();
} } else {
else
{
// We have to totally teardown our bluetooth objects to prevent leaks // We have to totally teardown our bluetooth objects to prevent leaks
stopMeshBluetoothService(); // Must do before shutting down bluetooth stopMeshBluetoothService(); // Must do before shutting down bluetooth
deinitBLE(); deinitBLE();
@ -360,11 +341,9 @@ void loop()
// service.radio.rf95.canSleep(); // service.radio.rf95.canSleep();
#ifdef T_BEAM_V10 #ifdef T_BEAM_V10
if (axp192_found) if (axp192_found) {
{
#ifdef PMU_IRQ #ifdef PMU_IRQ
if (pmu_irq) if (pmu_irq) {
{
pmu_irq = false; pmu_irq = false;
axp.readIRQ(); axp.readIRQ();
@ -384,14 +363,12 @@ void loop()
#endif #endif
#ifdef BUTTON_PIN #ifdef BUTTON_PIN
// if user presses button for more than 3 secs, discard our network prefs and reboot (FIXME, use a debounce lib instead of this boilerplate) // if user presses button for more than 3 secs, discard our network prefs and reboot (FIXME, use a debounce lib instead of
// this boilerplate)
static bool wasPressed = false; static bool wasPressed = false;
if (!digitalRead(BUTTON_PIN)) {
if (!digitalRead(BUTTON_PIN)) if (!wasPressed) { // just started a new press
{
if (!wasPressed)
{ // just started a new press
DEBUG_MSG("pressing\n"); DEBUG_MSG("pressing\n");
// doLightSleep(); // doLightSleep();
@ -400,9 +377,7 @@ void loop()
powerFSM.trigger(EVENT_PRESS); powerFSM.trigger(EVENT_PRESS);
} }
} } else if (wasPressed) {
else if (wasPressed)
{
// we just did a release // we just did a release
wasPressed = false; wasPressed = false;
} }
@ -410,8 +385,7 @@ void loop()
// Show boot screen for first 3 seconds, then switch to normal operation. // Show boot screen for first 3 seconds, then switch to normal operation.
static bool showingBootScreen = true; static bool showingBootScreen = true;
if (showingBootScreen && (millis() > 3000)) if (showingBootScreen && (millis() > 3000)) {
{
screen.stopBootScreen(); screen.stopBootScreen();
showingBootScreen = false; showingBootScreen = false;
} }
@ -420,7 +394,8 @@ void loop()
// i.e. don't just keep spinning in loop as fast as we can. // i.e. don't just keep spinning in loop as fast as we can.
// DEBUG_MSG("msecs %d\n", msecstosleep); // DEBUG_MSG("msecs %d\n", msecstosleep);
// FIXME - until button press handling is done by interrupt (see polling above) we can't sleep very long at all or buttons feel slow // FIXME - until button press handling is done by interrupt (see polling above) we can't sleep very long at all or buttons
// feel slow
msecstosleep = 10; msecstosleep = 10;
delay(msecstosleep); delay(msecstosleep);

View File

@ -1,10 +1,10 @@
#include <Arduino.h>
#include "configuration.h"
#include "mesh-pb-constants.h" #include "mesh-pb-constants.h"
#include <pb_encode.h>
#include <pb_decode.h>
#include <assert.h>
#include "FS.h" #include "FS.h"
#include "configuration.h"
#include <Arduino.h>
#include <assert.h>
#include <pb_decode.h>
#include <pb_encode.h>
/// helper function for encoding a record as a protobuf, any failures to encode are fatal and we will panic /// helper function for encoding a record as a protobuf, any failures to encode are fatal and we will panic
/// returns the encoded packet size /// returns the encoded packet size
@ -12,42 +12,33 @@ size_t pb_encode_to_bytes(uint8_t *destbuf, size_t destbufsize, const pb_msgdesc
{ {
pb_ostream_t stream = pb_ostream_from_buffer(destbuf, destbufsize); pb_ostream_t stream = pb_ostream_from_buffer(destbuf, destbufsize);
if (!pb_encode(&stream, fields, src_struct)) if (!pb_encode(&stream, fields, src_struct)) {
{
DEBUG_MSG("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream)); DEBUG_MSG("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream));
assert(0); // FIXME - panic assert(0); // FIXME - panic
} } else {
else
{
return stream.bytes_written; return stream.bytes_written;
} }
} }
/// helper function for decoding a record as a protobuf, we will return false if the decoding failed /// helper function for decoding a record as a protobuf, we will return false if the decoding failed
bool pb_decode_from_bytes(const uint8_t *srcbuf, size_t srcbufsize, const pb_msgdesc_t *fields, void *dest_struct) bool pb_decode_from_bytes(const uint8_t *srcbuf, size_t srcbufsize, const pb_msgdesc_t *fields, void *dest_struct)
{ {
pb_istream_t stream = pb_istream_from_buffer(srcbuf, srcbufsize); pb_istream_t stream = pb_istream_from_buffer(srcbuf, srcbufsize);
if (!pb_decode(&stream, fields, dest_struct)) if (!pb_decode(&stream, fields, dest_struct)) {
{
DEBUG_MSG("Error: can't decode protobuf %s, pb_msgdesc 0x%p\n", PB_GET_ERROR(&stream), fields); DEBUG_MSG("Error: can't decode protobuf %s, pb_msgdesc 0x%p\n", PB_GET_ERROR(&stream), fields);
return false; return false;
} } else {
else
{
return true; return true;
} }
} }
/// Read from an Arduino File /// Read from an Arduino File
bool readcb(pb_istream_t *stream, uint8_t *buf, size_t count) bool readcb(pb_istream_t *stream, uint8_t *buf, size_t count)
{ {
File *file = (File *)stream->state; File *file = (File *)stream->state;
bool status; bool status;
if (buf == NULL) if (buf == NULL) {
{
while (count-- && file->read() != EOF) while (count-- && file->read() != EOF)
; ;
return count == 0; return count == 0;
@ -61,7 +52,6 @@ bool readcb(pb_istream_t *stream, uint8_t *buf, size_t count)
return status; return status;
} }
/// Write to an arduino file /// Write to an arduino file
bool writecb(pb_ostream_t *stream, const uint8_t *buf, size_t count) bool writecb(pb_ostream_t *stream, const uint8_t *buf, size_t count)
{ {

View File

@ -13,7 +13,6 @@
/// max number of nodes allowed in the mesh /// max number of nodes allowed in the mesh
#define MAX_NUM_NODES (member_size(DeviceState, node_db) / member_size(DeviceState, node_db[0])) #define MAX_NUM_NODES (member_size(DeviceState, node_db) / member_size(DeviceState, node_db[0]))
/// helper function for encoding a record as a protobuf, any failures to encode are fatal and we will panic /// helper function for encoding a record as a protobuf, any failures to encode are fatal and we will panic
/// returns the encoded packet size /// returns the encoded packet size
size_t pb_encode_to_bytes(uint8_t *destbuf, size_t destbufsize, const pb_msgdesc_t *fields, const void *src_struct); size_t pb_encode_to_bytes(uint8_t *destbuf, size_t destbufsize, const pb_msgdesc_t *fields, const void *src_struct);

View File

@ -96,8 +96,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
// the max length of this buffer is much longer than we can possibly print // the max length of this buffer is much longer than we can possibly print
static char tempBuf[96]; static char tempBuf[96];
snprintf(tempBuf, sizeof(tempBuf), " %s", snprintf(tempBuf, sizeof(tempBuf), " %s", mp.payload.variant.data.payload.bytes);
mp.payload.variant.data.payload.bytes);
display->drawStringMaxWidth(4 + x, 10 + y, 128, tempBuf); display->drawStringMaxWidth(4 + x, 10 + y, 128, tempBuf);
} }

View File

@ -1,18 +1,18 @@
#include "configuration.h" #include "sleep.h"
#include "rom/rtc.h"
#include <driver/rtc_io.h>
#include <Wire.h>
#include "BluetoothUtil.h" #include "BluetoothUtil.h"
#include "MeshBluetoothService.h"
#include "MeshService.h"
#include "GPS.h" #include "GPS.h"
#include "MeshBluetoothService.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "Periodic.h" #include "Periodic.h"
#include "configuration.h"
#include "esp32/pm.h" #include "esp32/pm.h"
#include "esp_pm.h" #include "esp_pm.h"
#include "MeshRadio.h"
#include "main.h" #include "main.h"
#include "sleep.h" #include "rom/rtc.h"
#include <Wire.h>
#include <driver/rtc_io.h>
#ifdef T_BEAM_V10 #ifdef T_BEAM_V10
#include "axp20x.h" #include "axp20x.h"
@ -49,8 +49,7 @@ void setLed(bool ledOn)
#endif #endif
#ifdef T_BEAM_V10 #ifdef T_BEAM_V10
if (axp192_found) if (axp192_found) {
{
// blink the axp led // blink the axp led
axp.setChgLEDMode(ledOn ? AXP20X_LED_LOW_LEVEL : AXP20X_LED_OFF); axp.setChgLEDMode(ledOn ? AXP20X_LED_LOW_LEVEL : AXP20X_LED_OFF);
} }
@ -76,8 +75,8 @@ void initDeepSleep()
Not using yet because we are using wake on all buttons being low Not using yet because we are using wake on all buttons being low
wakeButtons = esp_sleep_get_ext1_wakeup_status(); // If one of these buttons is set it was the reason we woke wakeButtons = esp_sleep_get_ext1_wakeup_status(); // If one of these buttons is set it was the reason we woke
if (wakeCause == ESP_SLEEP_WAKEUP_EXT1 && !wakeButtons) // we must have been using the 'all buttons rule for waking' to support busted boards, assume button one was pressed if (wakeCause == ESP_SLEEP_WAKEUP_EXT1 && !wakeButtons) // we must have been using the 'all buttons rule for waking' to
wakeButtons = ((uint64_t)1) << buttons.gpios[0]; support busted boards, assume button one was pressed wakeButtons = ((uint64_t)1) << buttons.gpios[0];
*/ */
// If we booted because our timer ran out or the user pressed reset, send those as fake events // If we booted because our timer ran out or the user pressed reset, send those as fake events
@ -126,8 +125,7 @@ void doDeepSleep(uint64_t msecToWake)
setLed(false); setLed(false);
#ifdef T_BEAM_V10 #ifdef T_BEAM_V10
if (axp192_found) if (axp192_found) {
{
// No need to turn this off if the power draw in sleep mode really is just 0.2uA and turning it off would // No need to turn this off if the power draw in sleep mode really is just 0.2uA and turning it off would
// leave floating input for the IRQ line // leave floating input for the IRQ line
@ -157,10 +155,18 @@ void doDeepSleep(uint64_t msecToWake)
static const uint8_t rtcGpios[] = {/* 0, */ 2, static const uint8_t rtcGpios[] = {/* 0, */ 2,
/* 4, */ /* 4, */
#ifndef USE_JTAG #ifndef USE_JTAG
12, 13, /* 14, */ /* 15, */ 12,
13,
/* 14, */ /* 15, */
#endif #endif
/* 25, */ 26, /* 27, */ /* 25, */ 26, /* 27, */
32, 33, 34, 35, 36, 37, /* 38, */ 39}; 32,
33,
34,
35,
36,
37,
/* 38, */ 39};
for (int i = 0; i < sizeof(rtcGpios); i++) for (int i = 0; i < sizeof(rtcGpios); i++)
rtc_gpio_isolate((gpio_num_t)rtcGpios[i]); rtc_gpio_isolate((gpio_num_t)rtcGpios[i]);
@ -180,8 +186,8 @@ void doDeepSleep(uint64_t msecToWake)
#endif #endif
// Not needed because both of the current boards have external pullups // Not needed because both of the current boards have external pullups
// FIXME change polarity in hw so we can wake on ANY_HIGH instead - that would allow us to use all three buttons (instead of just the first) // FIXME change polarity in hw so we can wake on ANY_HIGH instead - that would allow us to use all three buttons (instead of
// gpio_pullup_en((gpio_num_t)BUTTON_PIN); // just the first) gpio_pullup_en((gpio_num_t)BUTTON_PIN);
esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ALL_LOW); esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ALL_LOW);
#endif #endif
@ -221,7 +227,8 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
assert(esp_sleep_enable_gpio_wakeup() == ESP_OK); assert(esp_sleep_enable_gpio_wakeup() == ESP_OK);
assert(esp_sleep_enable_timer_wakeup(sleepUsec) == ESP_OK); assert(esp_sleep_enable_timer_wakeup(sleepUsec) == ESP_OK);
assert(esp_light_sleep_start() == ESP_OK); assert(esp_light_sleep_start() == ESP_OK);
//DEBUG_MSG("Exit light sleep b=%d, rf95=%d, pmu=%d\n", digitalRead(BUTTON_PIN), digitalRead(DIO0_GPIO), digitalRead(PMU_IRQ)); // DEBUG_MSG("Exit light sleep b=%d, rf95=%d, pmu=%d\n", digitalRead(BUTTON_PIN), digitalRead(DIO0_GPIO),
// digitalRead(PMU_IRQ));
return esp_sleep_get_wakeup_cause(); return esp_sleep_get_wakeup_cause();
} }