2020-05-01 04:42:11 +00:00
|
|
|
|
|
|
|
#include "RadioInterface.h"
|
2020-05-09 23:32:26 +00:00
|
|
|
#include "MeshRadio.h"
|
|
|
|
#include "MeshService.h"
|
2020-04-01 04:56:35 +00:00
|
|
|
#include "NodeDB.h"
|
|
|
|
#include "assert.h"
|
|
|
|
#include "configuration.h"
|
2020-05-09 23:32:26 +00:00
|
|
|
#include "sleep.h"
|
2020-07-06 08:45:55 +00:00
|
|
|
#include "timing.h"
|
2020-04-17 16:48:54 +00:00
|
|
|
#include <assert.h>
|
2020-04-01 04:56:35 +00:00
|
|
|
#include <pb_decode.h>
|
|
|
|
#include <pb_encode.h>
|
|
|
|
|
2020-05-09 23:32:26 +00:00
|
|
|
/**
|
|
|
|
* ## LoRaWAN for North America
|
|
|
|
|
|
|
|
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 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. Channel zero starts at 903.08 MHz center frequency.
|
|
|
|
*/
|
|
|
|
|
2020-05-01 19:31:36 +00:00
|
|
|
// 1kb was too small
|
2020-05-01 19:11:04 +00:00
|
|
|
#define RADIO_STACK_SIZE 4096
|
|
|
|
|
2020-06-14 22:30:42 +00:00
|
|
|
void printPacket(const char *prefix, const MeshPacket *p)
|
|
|
|
{
|
|
|
|
DEBUG_MSG("%s (id=0x%08x Fr0x%02x To0x%02x, WantAck%d, HopLim%d", prefix, p->id, p->from & 0xff, p->to & 0xff, p->want_ack,
|
|
|
|
p->hop_limit);
|
|
|
|
if (p->which_payload == MeshPacket_decoded_tag) {
|
|
|
|
auto &s = p->decoded;
|
|
|
|
switch (s.which_payload) {
|
|
|
|
case SubPacket_data_tag:
|
|
|
|
DEBUG_MSG(" Payload:Data");
|
|
|
|
break;
|
|
|
|
case SubPacket_position_tag:
|
|
|
|
DEBUG_MSG(" Payload:Position");
|
|
|
|
break;
|
|
|
|
case SubPacket_user_tag:
|
|
|
|
DEBUG_MSG(" Payload:User");
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
DEBUG_MSG(" Payload:None");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DEBUG_MSG(" Payload:%d", s.which_payload);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (s.want_response)
|
|
|
|
DEBUG_MSG(" WANTRESP");
|
|
|
|
|
|
|
|
if (s.source != 0)
|
|
|
|
DEBUG_MSG(" source=%08x", s.source);
|
|
|
|
|
|
|
|
if (s.dest != 0)
|
|
|
|
DEBUG_MSG(" dest=%08x", s.dest);
|
|
|
|
|
|
|
|
if (s.which_ack == SubPacket_success_id_tag)
|
|
|
|
DEBUG_MSG(" successId=%08x", s.ack.success_id);
|
|
|
|
else if (s.which_ack == SubPacket_fail_id_tag)
|
|
|
|
DEBUG_MSG(" failId=%08x", s.ack.fail_id);
|
|
|
|
} else {
|
|
|
|
DEBUG_MSG(" encrypted");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p->rx_time != 0) {
|
|
|
|
DEBUG_MSG(" rxtime=%u", p->rx_time);
|
|
|
|
}
|
|
|
|
DEBUG_MSG(")\n");
|
|
|
|
}
|
|
|
|
|
2020-06-09 22:47:05 +00:00
|
|
|
RadioInterface::RadioInterface()
|
2020-04-30 16:44:16 +00:00
|
|
|
{
|
2020-06-03 20:46:31 +00:00
|
|
|
assert(sizeof(PacketHeader) == 4 || sizeof(PacketHeader) == 16); // make sure the compiler did what we expected
|
2020-05-09 23:32:26 +00:00
|
|
|
|
|
|
|
myNodeInfo.num_channels = NUM_CHANNELS;
|
|
|
|
|
|
|
|
// Can't print strings this early - serial not setup yet
|
|
|
|
// DEBUG_MSG("Set meshradio defaults name=%s\n", channelSettings.name);
|
2020-04-30 16:44:16 +00:00
|
|
|
}
|
2020-04-01 04:56:35 +00:00
|
|
|
|
2020-05-01 19:11:04 +00:00
|
|
|
bool RadioInterface::init()
|
|
|
|
{
|
2020-05-09 23:32:26 +00:00
|
|
|
DEBUG_MSG("Starting meshradio init...\n");
|
|
|
|
|
|
|
|
configChangedObserver.observe(&service.configChanged);
|
|
|
|
preflightSleepObserver.observe(&preflightSleep);
|
|
|
|
notifyDeepSleepObserver.observe(¬ifyDeepSleep);
|
|
|
|
|
|
|
|
// we now expect interfaces to operate in promiscous mode
|
|
|
|
// radioIf.setThisAddress(nodeDB.getNodeNum()); // Note: we must do this here, because the nodenum isn't inited at constructor
|
|
|
|
// time.
|
|
|
|
|
|
|
|
// we want this thread to run at very high priority, because it is effectively running as a user space ISR
|
2020-05-03 02:51:25 +00:00
|
|
|
start("radio", RADIO_STACK_SIZE, configMAX_PRIORITIES - 1); // Start our worker thread
|
2020-05-01 19:11:04 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-05-09 23:32:26 +00:00
|
|
|
/** hash a string into an integer
|
|
|
|
*
|
|
|
|
* djb2 by Dan Bernstein.
|
|
|
|
* http://www.cse.yorku.ca/~oz/hash.html
|
|
|
|
*/
|
|
|
|
unsigned long hash(char *str)
|
|
|
|
{
|
|
|
|
unsigned long hash = 5381;
|
|
|
|
int c;
|
|
|
|
|
|
|
|
while ((c = *str++) != 0)
|
|
|
|
hash = ((hash << 5) + hash) + (unsigned char)c; /* hash * 33 + c */
|
|
|
|
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
2020-08-12 18:04:03 +00:00
|
|
|
|
|
|
|
|
2020-08-12 17:42:25 +00:00
|
|
|
#define POWER_DEFAULT 17
|
|
|
|
|
2020-05-09 23:32:26 +00:00
|
|
|
/**
|
|
|
|
* Pull our channel settings etc... from protobufs to the dumb interface settings
|
|
|
|
*/
|
|
|
|
void RadioInterface::applyModemConfig()
|
|
|
|
{
|
|
|
|
// Set up default configuration
|
2020-08-12 17:42:25 +00:00
|
|
|
// No Sync Words in LORA mode
|
|
|
|
|
|
|
|
power = channelSettings.tx_power;
|
|
|
|
if (power == 0)
|
|
|
|
power = POWER_DEFAULT;
|
2020-05-09 23:32:26 +00:00
|
|
|
|
2020-08-12 17:46:44 +00:00
|
|
|
// If user has manually specified a channel num, then use that, otherwise generate one by hashing the name
|
|
|
|
int channel_num = (channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelSettings.name)) % NUM_CHANNELS;
|
2020-05-09 23:32:26 +00:00
|
|
|
freq = CH0 + CH_SPACING * channel_num;
|
|
|
|
|
|
|
|
DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelSettings.name, channelSettings.modem_config, channel_num,
|
2020-08-12 17:42:25 +00:00
|
|
|
power);
|
2020-05-09 23:32:26 +00:00
|
|
|
}
|
|
|
|
|
2020-04-01 04:56:35 +00:00
|
|
|
ErrorCode SimRadio::send(MeshPacket *p)
|
|
|
|
{
|
|
|
|
DEBUG_MSG("SimRadio.send\n");
|
2020-04-17 16:48:54 +00:00
|
|
|
packetPool.release(p);
|
2020-04-01 04:56:35 +00:00
|
|
|
return ERRNO_OK;
|
2020-04-17 16:48:54 +00:00
|
|
|
}
|
|
|
|
|
2020-04-18 21:22:24 +00:00
|
|
|
void RadioInterface::deliverToReceiver(MeshPacket *p)
|
2020-04-17 16:48:54 +00:00
|
|
|
{
|
|
|
|
assert(rxDest);
|
2020-04-18 21:22:24 +00:00
|
|
|
assert(rxDest->enqueue(p, 0)); // NOWAIT - fixme, if queue is full, delete older messages
|
2020-04-30 01:46:32 +00:00
|
|
|
}
|
|
|
|
|
2020-04-30 16:44:16 +00:00
|
|
|
/***
|
|
|
|
* given a packet set sendingPacket and decode the protobufs into radiobuf. Returns # of payload bytes to send
|
|
|
|
*/
|
|
|
|
size_t RadioInterface::beginSending(MeshPacket *p)
|
|
|
|
{
|
|
|
|
assert(!sendingPacket);
|
|
|
|
|
|
|
|
// DEBUG_MSG("sending queued packet on mesh (txGood=%d,rxGood=%d,rxBad=%d)\n", rf95.txGood(), rf95.rxGood(), rf95.rxBad());
|
2020-05-10 00:51:20 +00:00
|
|
|
assert(p->which_payload == MeshPacket_encrypted_tag); // It should have already been encoded by now
|
2020-04-30 16:44:16 +00:00
|
|
|
|
2020-07-06 08:45:55 +00:00
|
|
|
lastTxStart = timing::millis();
|
2020-04-30 16:44:16 +00:00
|
|
|
|
|
|
|
PacketHeader *h = (PacketHeader *)radiobuf;
|
|
|
|
|
|
|
|
h->from = p->from;
|
|
|
|
h->to = p->to;
|
|
|
|
h->id = p->id;
|
2020-05-19 00:35:23 +00:00
|
|
|
assert(p->hop_limit <= HOP_MAX);
|
2020-05-19 17:27:28 +00:00
|
|
|
h->flags = p->hop_limit | (p->want_ack ? PACKET_FLAGS_WANT_ACK_MASK : 0);
|
2020-04-30 16:44:16 +00:00
|
|
|
|
|
|
|
// if the sender nodenum is zero, that means uninitialized
|
|
|
|
assert(h->from);
|
|
|
|
|
2020-05-10 00:51:20 +00:00
|
|
|
memcpy(radiobuf + sizeof(PacketHeader), p->encrypted.bytes, p->encrypted.size);
|
2020-04-30 16:44:16 +00:00
|
|
|
|
|
|
|
sendingPacket = p;
|
2020-05-10 00:51:20 +00:00
|
|
|
return p->encrypted.size + sizeof(PacketHeader);
|
2020-04-30 16:44:16 +00:00
|
|
|
}
|