sforkowany z mirror/meshtastic-firmware
mesh sending and receving now much more real
rodzic
4051bf8465
commit
9aed5056ad
3
TODO.md
3
TODO.md
|
@ -5,6 +5,7 @@
|
|||
* solder debug headers to board
|
||||
* make message send from android go to service, then to mesh radio
|
||||
* make message receive from radio go through to android
|
||||
* test loopback tx/rx path code without using radio
|
||||
* have MeshService keep a node DB by sniffing user messages
|
||||
* have meshservice send location data on mesh (if device has a GPS)
|
||||
|
||||
|
@ -20,6 +21,8 @@
|
|||
* sendToMesh can currently block for a long time, instead have it just queue a packet for a radio freertos thread
|
||||
* see section 7.3 of https://cdn.sparkfun.com/assets/learn_tutorials/8/0/4/RFM95_96_97_98W.pdf and have hope radio wake only when a valid packet is received. Possibly even wake the ESP32 from deep sleep via GPIO.
|
||||
* fix the logo
|
||||
* do debug logging to android over bluetooth
|
||||
* break out my bluetooth OTA software as a seperate library so others can use it
|
||||
|
||||
# Pre-beta priority
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <Arduino.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "PointerQueue.h"
|
||||
|
||||
/**
|
||||
* A pool based allocator
|
||||
|
@ -10,12 +11,13 @@
|
|||
* Eventually this routine will even be safe for ISR use...
|
||||
*/
|
||||
template <class T> class MemoryPool {
|
||||
TypedQueue<T *> dead;
|
||||
PointerQueue<T> dead;
|
||||
|
||||
T *buf; // our large raw block of memory
|
||||
|
||||
size_t maxElements;
|
||||
public:
|
||||
MemoryPool(int maxElements): queued(maxElements), dead(maxElements) {
|
||||
MemoryPool(size_t _maxElements): dead(_maxElements), maxElements(_maxElements) {
|
||||
buf = new T[maxElements];
|
||||
|
||||
// prefill dead
|
||||
|
@ -29,19 +31,28 @@ public:
|
|||
|
||||
/// Return a queable object which has been prefilled with zeros
|
||||
T *allocZeroed(TickType_t maxWait = portMAX_DELAY) {
|
||||
T *p;
|
||||
|
||||
if(dead.dequeue(&p, maxWait) != pdTRUE)
|
||||
return NULL;
|
||||
|
||||
memset(p, 0, sizeof(T));
|
||||
T *p = dead.dequeuePtr(maxWait);
|
||||
|
||||
if(p)
|
||||
memset(p, 0, sizeof(T));
|
||||
return p;
|
||||
}
|
||||
|
||||
/// Return a queable object which is a copy of some other object
|
||||
T *allocCopy(const T &src, TickType_t maxWait = portMAX_DELAY) {
|
||||
T *p = dead.dequeuePtr(maxWait);
|
||||
|
||||
if(p)
|
||||
memcpy(p, &src, sizeof(T));
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/// Return a buffer for use by others
|
||||
void free(T *p) {
|
||||
void release(T *p) {
|
||||
int res = dead.enqueue(p, 0);
|
||||
assert(res == pdTRUE);
|
||||
assert(p >= buf && (p - buf) < maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -8,148 +8,8 @@
|
|||
#include <pb_encode.h>
|
||||
#include <pb_decode.h>
|
||||
#include "mesh.pb.h"
|
||||
#include "MeshRadio.h"
|
||||
#include "TypedQueue.h"
|
||||
#include "MemoryPool.h"
|
||||
#include "MeshService.h"
|
||||
|
||||
/*
|
||||
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).
|
||||
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
|
||||
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
|
||||
a node number and keeping the current nodedb.
|
||||
|
||||
|
||||
typedef in32_t NodeNum;
|
||||
|
||||
class NodeInfo {
|
||||
position;
|
||||
last_seen
|
||||
user
|
||||
};
|
||||
|
||||
class NodeDB {
|
||||
NodeNum provisionalNodeNum; // if we are trying to find a node num this is our current attempt
|
||||
|
||||
NodeNum ourNodeNum; // -1 if not yet found
|
||||
|
||||
HashMap<NodeNum, NodeInfo> nodes;
|
||||
public:
|
||||
/// don't do mesh based algoritm for node id assignment (initially) - instead just store in flash - possibly even in the initial alpha release do this hack
|
||||
|
||||
/// if returns false, that means our node should send a DenyNodeNum response. If true, we think the number is okay for use
|
||||
// bool handleWantNodeNum(NodeNum n);
|
||||
|
||||
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
|
||||
unconfigured node num would only be used while initially joining the mesh so low odds of conflicting (especially if we randomly select
|
||||
from a small number of nodenums which can be used temporarily for this operation). figure out what the lower 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?)
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#define MAX_PACKETS 32 // max number of packets which can be in flight (either queued from reception or queued for sending)
|
||||
#define MAX_RX_TOPHONE 16 // max number of packets which can be waiting for delivery to android
|
||||
|
||||
/// A temporary buffer used for sending packets, sized to hold the biggest buffer we might need
|
||||
static uint8_t outbuf[MeshPacket_size];
|
||||
|
||||
/**
|
||||
* Top level app for this service. keeps the mesh, the radio config and the queue of received packets.
|
||||
*
|
||||
*/
|
||||
class MeshService
|
||||
{
|
||||
MemoryPool<MeshPacket> packetPool;
|
||||
|
||||
/// received packets waiting for the phone to process them
|
||||
/// FIXME, change to a DropOldestQueue and keep a count of the number of dropped packets to ensure
|
||||
/// we never hang because android hasn't been there in a while
|
||||
PointerQueue<MeshPacket> toPhoneQueue;
|
||||
|
||||
/// Packets which have just arrived from the radio, ready to be processed by this service and possibly
|
||||
/// forwarded to the phone. Note: not using yet - seeing if I can just handle everything asap in handleFromRadio
|
||||
// PointerQueue<MeshPacket> fromRadioQueue;
|
||||
|
||||
public:
|
||||
MeshService() : packetPool(MAX_PACKETS), toPhoneQueue(MAX_RX_TOPHONE) {
|
||||
|
||||
}
|
||||
|
||||
/// Do idle processing (mostly processing messages which have been queued from the radio)
|
||||
// void loop() { }
|
||||
|
||||
/**
|
||||
* handle an incoming MeshPacket from the radio, update DB state and queue it for the phone
|
||||
*/
|
||||
void handleFromRadio(NodeNum from, NodeNum to, const uint8_t *buf, size_t len) {
|
||||
MeshPacket *p = packetPool.allocZeroed();
|
||||
assert(p);
|
||||
|
||||
pb_istream_t stream = pb_istream_from_buffer(buf, len);
|
||||
if (!pb_decode(&stream, MeshPacket_fields, p) || !p->has_payload)
|
||||
{
|
||||
Serial.printf("Error: can't decode MeshPacket %s\n", PB_GET_ERROR(&stream));
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME - update DB state based on payload and show recevied texts
|
||||
|
||||
toPhoneQueue.enqueue(p);
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a ToRadio buffer parse it and properly handle it (setup radio, owner or send packet into the mesh)
|
||||
void handleToRadio(std::string s)
|
||||
{
|
||||
static ToRadio r; // new ToRadio(); FIXME dynamically allocate
|
||||
|
||||
pb_istream_t stream = pb_istream_from_buffer((const uint8_t *)s.c_str(), s.length());
|
||||
if (!pb_decode(&stream, ToRadio_fields, &r))
|
||||
{
|
||||
Serial.printf("Error: can't decode ToRadio %s\n", PB_GET_ERROR(&stream));
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (r.which_variant)
|
||||
{
|
||||
case ToRadio_packet_tag:
|
||||
sendToMesh(r.variant.packet);
|
||||
break;
|
||||
|
||||
default:
|
||||
Serial.println("Error: unexpected ToRadio variant");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/// Send a packet into the mesh
|
||||
void sendToMesh(const MeshPacket &p)
|
||||
{
|
||||
assert(p.has_payload);
|
||||
|
||||
pb_ostream_t stream = pb_ostream_from_buffer(outbuf, sizeof(outbuf));
|
||||
if (!pb_encode(&stream, MeshPacket_fields, &p))
|
||||
{
|
||||
Serial.printf("Error: can't encode MeshPacket %s\n", PB_GET_ERROR(&stream));
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(radio.sendTo(p.to, outbuf, stream.bytes_written) == ERRNO_OK);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MeshService service;
|
||||
|
||||
static BLECharacteristic meshFromRadioCharacteristic("8ba2bcc2-ee02-4a55-a531-c525c5e454d5", BLECharacteristic::PROPERTY_READ);
|
||||
static BLECharacteristic meshToRadioCharacteristic("f75c76d2-129e-4dad-a1dd-7866124401e7", BLECharacteristic::PROPERTY_WRITE);
|
||||
|
|
|
@ -3,103 +3,127 @@
|
|||
#include <RHMesh.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <pb_encode.h>
|
||||
#include <pb_decode.h>
|
||||
#include "MeshRadio.h"
|
||||
#include "configuration.h"
|
||||
|
||||
|
||||
|
||||
// Change to 434.0 or other frequency, must match RX's freq!
|
||||
#define RF95_FREQ 915.0
|
||||
|
||||
MeshRadio radio;
|
||||
|
||||
/**
|
||||
* get our starting (provisional) nodenum from flash. But check first if anyone else is using it, by trying to send a message to it (arping)
|
||||
*/
|
||||
NodeNum getDesiredNodeNum() {
|
||||
uint8_t dmac[6];
|
||||
esp_efuse_mac_get_default(dmac);
|
||||
|
||||
// FIXME not the right way to guess node numes
|
||||
uint8_t r = dmac[5];
|
||||
assert(r != 0xff); // It better not be the broadcast address
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
MeshRadio::MeshRadio() : rf95(NSS_GPIO, DIO0_GPIO), manager(rf95, getDesiredNodeNum()) {
|
||||
|
||||
}
|
||||
|
||||
bool MeshRadio::init() {
|
||||
pinMode(RESET_GPIO, OUTPUT); // Deassert reset
|
||||
digitalWrite(RESET_GPIO, HIGH);
|
||||
|
||||
// pulse reset
|
||||
digitalWrite(RESET_GPIO, LOW);
|
||||
delay(10);
|
||||
digitalWrite(RESET_GPIO, HIGH);
|
||||
delay(10);
|
||||
|
||||
if (!manager.init()) {
|
||||
Serial.println("LoRa radio init failed");
|
||||
Serial.println("Uncomment '#define SERIAL_DEBUG' in RH_RF95.cpp for detailed debug info");
|
||||
return false;
|
||||
}
|
||||
|
||||
Serial.println("LoRa radio init OK!");
|
||||
|
||||
// Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
|
||||
if (!rf95.setFrequency(RF95_FREQ)) {
|
||||
Serial.println("setFrequency failed");
|
||||
while (1);
|
||||
}
|
||||
Serial.print("Set Freq to: "); Serial.println(RF95_FREQ);
|
||||
|
||||
// Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
|
||||
|
||||
// The default transmitter power is 13dBm, using PA_BOOST.
|
||||
// If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then
|
||||
// you can set transmitter powers from 5 to 23 dBm:
|
||||
// FIXME - can we do this? It seems to be in the Heltec board.
|
||||
rf95.setTxPower(23, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
ErrorCode MeshRadio::sendTo(NodeNum dest, const uint8_t *buf, size_t len) {
|
||||
Serial.printf("mesh sendTo %d bytes to %d\n", len, dest);
|
||||
// FIXME - for now we do all packets as broadcast
|
||||
dest = NODENUM_BROADCAST;
|
||||
|
||||
// Note: we don't use sendToWait here because we don't want to wait and for the time being don't require
|
||||
// reliable delivery
|
||||
// return manager.sendtoWait((uint8_t *) buf, len, dest);
|
||||
return manager.sendto((uint8_t *) buf, len, dest) ? ERRNO_OK : ERRNO_UNKNOWN;
|
||||
}
|
||||
|
||||
void MeshRadio::loop() {
|
||||
// FIXME read from radio with recvfromAckTimeout
|
||||
}
|
||||
|
||||
void mesh_init() {
|
||||
while (!radio.init()) {
|
||||
Serial.println("radio init failed");
|
||||
while (1);
|
||||
}
|
||||
}
|
||||
|
||||
int16_t packetnum = 0; // packet counter, we increment per xmission
|
||||
|
||||
void mesh_loop()
|
||||
NodeNum getDesiredNodeNum()
|
||||
{
|
||||
radio.loop();
|
||||
uint8_t dmac[6];
|
||||
esp_efuse_mac_get_default(dmac);
|
||||
|
||||
delay(1000); // Wait 1 second between transmits, could also 'sleep' here!
|
||||
// FIXME not the right way to guess node numes
|
||||
uint8_t r = dmac[5];
|
||||
assert(r != 0xff); // It better not be the broadcast address
|
||||
return r;
|
||||
}
|
||||
|
||||
MeshRadio::MeshRadio(MemoryPool<MeshPacket> &_pool, PointerQueue<MeshPacket> &_rxDest)
|
||||
: rf95(NSS_GPIO, DIO0_GPIO),
|
||||
manager(rf95, getDesiredNodeNum()),
|
||||
pool(_pool),
|
||||
rxDest(_rxDest),
|
||||
txQueue(MAX_TX_QUEUE)
|
||||
{
|
||||
}
|
||||
|
||||
bool MeshRadio::init()
|
||||
{
|
||||
pinMode(RESET_GPIO, OUTPUT); // Deassert reset
|
||||
digitalWrite(RESET_GPIO, HIGH);
|
||||
|
||||
// pulse reset
|
||||
digitalWrite(RESET_GPIO, LOW);
|
||||
delay(10);
|
||||
digitalWrite(RESET_GPIO, HIGH);
|
||||
delay(10);
|
||||
|
||||
if (!manager.init())
|
||||
{
|
||||
Serial.println("LoRa radio init failed");
|
||||
Serial.println("Uncomment '#define SERIAL_DEBUG' in RH_RF95.cpp for detailed debug info");
|
||||
return false;
|
||||
}
|
||||
|
||||
Serial.println("LoRa radio init OK!");
|
||||
|
||||
// Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
|
||||
if (!rf95.setFrequency(RF95_FREQ))
|
||||
{
|
||||
Serial.println("setFrequency failed");
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
Serial.print("Set Freq to: ");
|
||||
Serial.println(RF95_FREQ);
|
||||
|
||||
// Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
|
||||
|
||||
// The default transmitter power is 13dBm, using PA_BOOST.
|
||||
// If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then
|
||||
// you can set transmitter powers from 5 to 23 dBm:
|
||||
// FIXME - can we do this? It seems to be in the Heltec board.
|
||||
rf95.setTxPower(23, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ErrorCode MeshRadio::send(MeshPacket *p)
|
||||
{
|
||||
int res = ERRNO_UNKNOWN;
|
||||
|
||||
/// A temporary buffer used for sending packets, sized to hold the biggest buffer we might need
|
||||
static uint8_t outbuf[SubPacket_size];
|
||||
|
||||
assert(p->has_payload);
|
||||
|
||||
pb_ostream_t stream = pb_ostream_from_buffer(outbuf, sizeof(outbuf));
|
||||
if (!pb_encode(&stream, SubPacket_fields, &p->payload))
|
||||
{
|
||||
Serial.printf("Error: can't encode SubPacket %s\n", PB_GET_ERROR(&stream));
|
||||
}
|
||||
else
|
||||
{
|
||||
res = sendTo(p->to, outbuf, stream.bytes_written);
|
||||
}
|
||||
|
||||
pool.release(p);
|
||||
return res;
|
||||
}
|
||||
|
||||
ErrorCode MeshRadio::sendTo(NodeNum dest, const uint8_t *buf, size_t len)
|
||||
{
|
||||
Serial.printf("mesh sendTo %d bytes to %d\n", len, dest);
|
||||
// FIXME - for now we do all packets as broadcast
|
||||
dest = NODENUM_BROADCAST;
|
||||
|
||||
// Note: we don't use sendToWait here because we don't want to wait and for the time being don't require
|
||||
// reliable delivery
|
||||
// return manager.sendtoWait((uint8_t *) buf, len, dest);
|
||||
return manager.sendto((uint8_t *)buf, len, dest) ? ERRNO_OK : ERRNO_UNKNOWN;
|
||||
}
|
||||
|
||||
void MeshRadio::loop()
|
||||
{
|
||||
// FIXME read from radio with recvfromAckTimeout
|
||||
|
||||
#if 0
|
||||
static int16_t packetnum = 0; // packet counter, we increment per xmission
|
||||
|
||||
char radiopacket[20] = "Hello World # ";
|
||||
sprintf(radiopacket, "hello %d", packetnum++);
|
||||
|
||||
assert(radio.sendTo(NODENUM_BROADCAST, (uint8_t *)radiopacket, sizeof(radiopacket)) == ERRNO_OK);
|
||||
assert(sendTo(NODENUM_BROADCAST, (uint8_t *)radiopacket, sizeof(radiopacket)) == ERRNO_OK);
|
||||
#endif
|
||||
|
||||
// manager.recvfromAckTimeout()
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
#include <RH_RF95.h>
|
||||
#include <RHMesh.h>
|
||||
#include "MemoryPool.h"
|
||||
#include "mesh.pb.h"
|
||||
#include "PointerQueue.h"
|
||||
|
||||
#define NODENUM_BROADCAST 255
|
||||
#define ERRNO_OK 0
|
||||
|
@ -10,37 +13,42 @@
|
|||
typedef int ErrorCode;
|
||||
typedef uint8_t NodeNum;
|
||||
|
||||
/// Callback for a receive packet, the callee must copy/queue the payload elsewhere before returning
|
||||
typedef void (*MeshRXHandler)(NodeNum from, NodeNum to, const uint8_t *buf, size_t len);
|
||||
#define MAX_TX_QUEUE 4 // max number of packets which can be waiting for transmission
|
||||
|
||||
|
||||
/**
|
||||
* A raw low level interface to our mesh. Only understands nodenums and bytes (not protobufs or node ids)
|
||||
*/
|
||||
class MeshRadio {
|
||||
public:
|
||||
MeshRadio();
|
||||
/** 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
|
||||
*/
|
||||
MeshRadio(MemoryPool<MeshPacket> &pool, PointerQueue<MeshPacket> &rxDest);
|
||||
|
||||
bool init();
|
||||
|
||||
/// Prepare the radio to enter sleep mode, where it should draw only 0.2 uA
|
||||
void sleep() { rf95.sleep(); }
|
||||
|
||||
/// Send a packet - the current implementation blocks for a while possibly (FIXME)
|
||||
ErrorCode sendTo(NodeNum dest, const uint8_t *buf, size_t len);
|
||||
/// Send a packet (possibly by enquing in a private fifo). This routine will
|
||||
/// later free() the packet to pool.
|
||||
ErrorCode send(MeshPacket *p);
|
||||
|
||||
/// Do loop callback operations (we currently FIXME poll the receive mailbox here)
|
||||
/// for received packets it will call the rx handler
|
||||
void loop();
|
||||
|
||||
void setRXHandler(MeshRXHandler h) { rxHandler = h; }
|
||||
|
||||
private:
|
||||
RH_RF95 rf95; // the raw radio interface
|
||||
RHMesh manager;
|
||||
MeshRXHandler rxHandler;
|
||||
// MeshRXHandler rxHandler;
|
||||
|
||||
MemoryPool<MeshPacket> &pool;
|
||||
PointerQueue<MeshPacket> &rxDest;
|
||||
PointerQueue<MeshPacket> txQueue;
|
||||
|
||||
/// low level send, might block for mutiple seconds
|
||||
ErrorCode sendTo(NodeNum dest, const uint8_t *buf, size_t len);
|
||||
};
|
||||
|
||||
extern MeshRadio radio;
|
||||
|
||||
void mesh_init();
|
||||
void mesh_loop();
|
|
@ -0,0 +1,103 @@
|
|||
|
||||
#include <Arduino.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <pb_encode.h>
|
||||
#include <pb_decode.h>
|
||||
#include "mesh.pb.h"
|
||||
#include "MeshService.h"
|
||||
|
||||
/*
|
||||
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).
|
||||
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
|
||||
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
|
||||
a node number and keeping the current nodedb.
|
||||
|
||||
|
||||
typedef in32_t NodeNum;
|
||||
|
||||
class NodeInfo {
|
||||
position;
|
||||
last_seen
|
||||
user
|
||||
};
|
||||
|
||||
class NodeDB {
|
||||
NodeNum provisionalNodeNum; // if we are trying to find a node num this is our current attempt
|
||||
|
||||
NodeNum ourNodeNum; // -1 if not yet found
|
||||
|
||||
HashMap<NodeNum, NodeInfo> nodes;
|
||||
public:
|
||||
/// don't do mesh based algoritm for node id assignment (initially) - instead just store in flash - possibly even in the initial alpha release do this hack
|
||||
|
||||
/// if returns false, that means our node should send a DenyNodeNum response. If true, we think the number is okay for use
|
||||
// bool handleWantNodeNum(NodeNum n);
|
||||
|
||||
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
|
||||
unconfigured node num would only be used while initially joining the mesh so low odds of conflicting (especially if we randomly select
|
||||
from a small number of nodenums which can be used temporarily for this operation). figure out what the lower 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?)
|
||||
};
|
||||
|
||||
*/
|
||||
|
||||
MeshService service;
|
||||
|
||||
#define MAX_PACKETS 32 // max number of packets which can be in flight (either queued from reception or queued for sending)
|
||||
#define MAX_RX_TOPHONE 16 // max number of packets which can be waiting for delivery to android
|
||||
|
||||
MeshService::MeshService() : packetPool(MAX_PACKETS), toPhoneQueue(MAX_RX_TOPHONE), radio(packetPool, toPhoneQueue)
|
||||
{
|
||||
}
|
||||
|
||||
void MeshService::init()
|
||||
{
|
||||
if (!radio.init())
|
||||
Serial.println("radio init failed");
|
||||
}
|
||||
|
||||
/// Do idle processing (mostly processing messages which have been queued from the radio)
|
||||
void MeshService::loop()
|
||||
{
|
||||
radio.loop(); // FIXME, possibly move radio interaction to own thread
|
||||
}
|
||||
|
||||
/// Given a ToRadio buffer parse it and properly handle it (setup radio, owner or send packet into the mesh)
|
||||
void MeshService::handleToRadio(std::string s)
|
||||
{
|
||||
static ToRadio r; // this is a static scratch object, any data must be copied elsewhere before returning
|
||||
|
||||
pb_istream_t stream = pb_istream_from_buffer((const uint8_t *)s.c_str(), s.length());
|
||||
if (!pb_decode(&stream, ToRadio_fields, &r))
|
||||
{
|
||||
Serial.printf("Error: can't decode ToRadio %s\n", PB_GET_ERROR(&stream));
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (r.which_variant)
|
||||
{
|
||||
case ToRadio_packet_tag:
|
||||
sendToMesh(r.variant.packet);
|
||||
break;
|
||||
|
||||
default:
|
||||
Serial.println("Error: unexpected ToRadio variant");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a packet into the mesh - note p is read only and should be copied into a pool based MeshPacket before
|
||||
/// sending.
|
||||
void MeshService::sendToMesh(const MeshPacket &pIn)
|
||||
{
|
||||
MeshPacket *pOut = packetPool.allocCopy(pIn);
|
||||
assert(pOut); // FIXME
|
||||
|
||||
assert(radio.send(pOut) == pdTRUE);
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "mesh.pb.h"
|
||||
#include "MeshRadio.h"
|
||||
#include "PointerQueue.h"
|
||||
#include "MemoryPool.h"
|
||||
|
||||
/**
|
||||
* Top level app for this service. keeps the mesh, the radio config and the queue of received packets.
|
||||
*
|
||||
*/
|
||||
class MeshService
|
||||
{
|
||||
MemoryPool<MeshPacket> packetPool;
|
||||
|
||||
/// received packets waiting for the phone to process them
|
||||
/// FIXME, change to a DropOldestQueue and keep a count of the number of dropped packets to ensure
|
||||
/// we never hang because android hasn't been there in a while
|
||||
PointerQueue<MeshPacket> toPhoneQueue;
|
||||
|
||||
/// Packets which have just arrived from the radio, ready to be processed by this service and possibly
|
||||
/// forwarded to the phone. Note: not using yet - seeing if I can just handle everything asap in handleFromRadio
|
||||
// PointerQueue<MeshPacket> fromRadioQueue;
|
||||
|
||||
public:
|
||||
|
||||
MeshRadio radio;
|
||||
|
||||
MeshService();
|
||||
|
||||
void init();
|
||||
|
||||
/// Do idle processing (mostly processing messages which have been queued from the radio)
|
||||
void loop();
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* handle an incoming MeshPacket from the radio, update DB state and queue it for the phone
|
||||
*/
|
||||
void handleFromRadio(NodeNum from, NodeNum to, const uint8_t *buf, size_t len) {
|
||||
MeshPacket *p = packetPool.allocZeroed();
|
||||
assert(p);
|
||||
|
||||
pb_istream_t stream = pb_istream_from_buffer(buf, len);
|
||||
if (!pb_decode(&stream, MeshPacket_fields, p) || !p->has_payload)
|
||||
{
|
||||
Serial.printf("Error: can't decode MeshPacket %s\n", PB_GET_ERROR(&stream));
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME - update DB state based on payload and show recevied texts
|
||||
|
||||
toPhoneQueue.enqueue(p);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Given a ToRadio buffer (from bluetooth) parse it and properly handle it (setup radio, owner or send packet into the mesh)
|
||||
void handleToRadio(std::string s);
|
||||
|
||||
private:
|
||||
|
||||
/// Send a packet into the mesh - note p is read only and should be copied into a pool based MeshPacket before
|
||||
/// sending.
|
||||
void sendToMesh(const MeshPacket &p);
|
||||
|
||||
};
|
||||
|
||||
extern MeshService service;
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include "TypedQueue.h"
|
||||
|
||||
|
||||
/**
|
||||
* A wrapper for freertos queues that assumes each element is a pointer
|
||||
*/
|
||||
template <class T> class PointerQueue: public TypedQueue<T *> {
|
||||
public:
|
||||
PointerQueue(int maxElements) : TypedQueue<T *>(maxElements) {
|
||||
}
|
||||
|
||||
// preturns a ptr or null if the queue was empty
|
||||
T *dequeuePtr(TickType_t maxWait = portMAX_DELAY) {
|
||||
T *p;
|
||||
|
||||
return this->dequeue(&p, maxWait) == pdTRUE ? p : NULL;
|
||||
}
|
||||
};
|
||||
|
|
@ -29,22 +29,3 @@ public:
|
|||
return xQueueReceive(h, p, maxWait);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A wrapper for freertos queues that assumes each element is a pointer
|
||||
*/
|
||||
template <class T> class PointerQueue: public TypedQueue<T *> {
|
||||
TypedQueue h;
|
||||
public:
|
||||
PointerQueue(int maxElements) : TypedQueue(maxElements) {
|
||||
}
|
||||
|
||||
// preturns a ptr or null if the queue was empty
|
||||
T *dequeuePtr(TickType_t maxWait = portMAX_DELAY) {
|
||||
T *p;
|
||||
|
||||
return dequeue(&p, maxWait) == pdTRUE ? p : NULL;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include <Wire.h>
|
||||
#include "BluetoothUtil.h"
|
||||
#include "MeshBluetoothService.h"
|
||||
#include "MeshRadio.h"
|
||||
#include "MeshService.h"
|
||||
|
||||
#ifdef T_BEAM_V10
|
||||
#include "axp20x.h"
|
||||
|
@ -62,7 +62,7 @@ void doDeepSleep(uint64_t msecToWake)
|
|||
screen_off(); // datasheet says this will draw only 10ua
|
||||
|
||||
// Put radio in sleep mode (will still draw power but only 0.2uA)
|
||||
radio.sleep();
|
||||
service.radio.sleep();
|
||||
|
||||
#ifdef RESET_OLED
|
||||
digitalWrite(RESET_OLED, 1); // put the display in reset before killing its power
|
||||
|
@ -345,7 +345,7 @@ void setup()
|
|||
delay(LOGO_DELAY);
|
||||
//}
|
||||
|
||||
mesh_init();
|
||||
service.init();
|
||||
BLEServer *serve = initBLE("KHBT Test"); // FIXME, use a real name based on the macaddr
|
||||
BLEService *bts = createMeshBluetoothService(serve);
|
||||
bts->start();
|
||||
|
@ -356,7 +356,7 @@ void loop()
|
|||
{
|
||||
gps_loop();
|
||||
screen_loop();
|
||||
mesh_loop();
|
||||
service.loop();
|
||||
loopBLE();
|
||||
|
||||
#ifdef LED_PIN
|
||||
|
|
Ładowanie…
Reference in New Issue