ESP32-PaxCounter/src/lorawan.cpp

660 lines
20 KiB
C++
Raw Normal View History

2018-03-18 19:45:17 +01:00
// Basic Config
2019-03-14 19:52:10 +01:00
#if (HAS_LORA)
#include "lorawan.h"
#endif
2018-03-18 19:45:17 +01:00
// Local logging Tag
2018-06-02 18:28:01 +02:00
static const char TAG[] = "lora";
2018-03-18 19:45:17 +01:00
2019-03-14 19:52:10 +01:00
#if (HAS_LORA)
2018-11-03 20:44:54 +01:00
2019-02-16 15:02:07 +01:00
#if CLOCK_ERROR_PROCENTAGE > 7
#warning CLOCK_ERROR_PROCENTAGE value in lmic_config.h is too high; values > 7 will cause side effects
#endif
2019-03-31 15:47:19 +02:00
#if (TIME_SYNC_LORAWAN)
#ifndef LMIC_ENABLE_DeviceTimeReq
#define LMIC_ENABLE_DeviceTimeReq 1
#endif
#endif
2018-10-03 16:24:45 +02:00
QueueHandle_t LoraSendQueue;
TaskHandle_t lmicTask = NULL, lorasendTask = NULL;
2018-10-03 16:24:45 +02:00
// table of LORAWAN MAC messages sent by the network to the device
// format: opcode, cmdname (max 19 chars), #bytes params
// source: LoRaWAN 1.1 Specification (October 11, 2017)
2019-09-07 14:52:18 +02:00
static const mac_t MACdn_table[] = {
{0x01, "ResetConf", 1}, {0x02, "LinkCheckAns", 2},
{0x03, "LinkADRReq", 4}, {0x04, "DutyCycleReq", 1},
{0x05, "RXParamSetupReq", 4}, {0x06, "DevStatusReq", 0},
{0x07, "NewChannelReq", 5}, {0x08, "RxTimingSetupReq", 1},
{0x09, "TxParamSetupReq", 1}, {0x0A, "DlChannelReq", 4},
{0x0B, "RekeyConf", 1}, {0x0C, "ADRParamSetupReq", 1},
{0x0D, "DeviceTimeAns", 5}, {0x0E, "ForceRejoinReq", 2},
{0x0F, "RejoinParamSetupReq", 1}};
2019-09-07 14:52:18 +02:00
// table of LORAWAN MAC messages sent by the device to the network
static const mac_t MACup_table[] = {
{0x01, "ResetInd", 1}, {0x02, "LinkCheckReq", 0},
{0x03, "LinkADRAns", 1}, {0x04, "DutyCycleAns", 0},
{0x05, "RXParamSetupAns", 1}, {0x06, "DevStatusAns", 2},
{0x07, "NewChannelAns", 1}, {0x08, "RxTimingSetupAns", 0},
{0x09, "TxParamSetupAns", 0}, {0x0A, "DlChannelAns", 1},
{0x0B, "RekeyInd", 1}, {0x0C, "ADRParamSetupAns", 0},
{0x0D, "DeviceTimeReq", 0}, {0x0F, "RejoinParamSetupAns", 1}};
2018-11-27 10:10:20 +01:00
class MyHalConfig_t : public Arduino_LMIC::HalConfiguration_t {
public:
2018-11-27 10:10:20 +01:00
MyHalConfig_t(){};
virtual void begin(void) override {
SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
}
};
2018-11-27 10:10:20 +01:00
MyHalConfig_t myHalConfig{};
2018-11-27 10:10:20 +01:00
// LMIC pin mapping
2018-11-26 23:56:25 +01:00
const lmic_pinmap lmic_pins = {
.nss = LORA_CS,
.rxtx = LMIC_UNUSED_PIN,
.rst = LORA_RST == NOT_A_PIN ? LMIC_UNUSED_PIN : LORA_RST,
.dio = {LORA_IRQ, LORA_IO1,
LORA_IO2 == NOT_A_PIN ? LMIC_UNUSED_PIN : LORA_IO2},
// optional: set polarity of rxtx pin.
.rxtx_rx_active = 0,
// optional: set RSSI cal for listen-before-talk
// this value is in dB, and is added to RSSI
// measured prior to decision.
// Must include noise guardband! Ignored in US,
// EU, IN, other markets where LBT is not required.
.rssi_cal = 0,
// optional: override LMIC_SPI_FREQ if non-zero
.spi_freq = 0,
2018-11-26 23:56:25 +01:00
.pConfig = &myHalConfig};
2018-07-23 13:20:06 +02:00
2018-03-18 19:45:17 +01:00
// DevEUI generator using devices's MAC address
void gen_lora_deveui(uint8_t *pdeveui) {
2018-06-12 12:52:48 +02:00
uint8_t *p = pdeveui, dmac[6];
int i = 0;
esp_efuse_mac_get_default(dmac);
// deveui is LSB, we reverse it so TTN DEVEUI display
// will remain the same as MAC address
// MAC is 6 bytes, devEUI 8, set first 2 ones
// with an arbitrary value
*p++ = 0xFF;
*p++ = 0xFE;
// Then next 6 bytes are mac address reversed
for (i = 0; i < 6; i++) {
*p++ = dmac[5 - i];
}
2018-03-18 19:45:17 +01:00
}
/* new version, does it with well formed mac according IEEE spec, but is
breaking change
// DevEUI generator using devices's MAC address
void gen_lora_deveui(uint8_t *pdeveui) {
uint8_t *p = pdeveui, dmac[6];
ESP_ERROR_CHECK(esp_efuse_mac_get_default(dmac));
// deveui is LSB, we reverse it so TTN DEVEUI display
2018-09-20 12:53:58 +02:00
// will remain the same as MAC address
// MAC is 6 bytes, devEUI 8, set middle 2 ones
// to an arbitrary value
*p++ = dmac[5];
*p++ = dmac[4];
*p++ = dmac[3];
*p++ = 0xfe;
*p++ = 0xff;
*p++ = dmac[2];
*p++ = dmac[1];
*p++ = dmac[0];
}
*/
2018-03-21 22:32:59 +01:00
// Function to do a byte swap in a byte array
2018-06-12 12:52:48 +02:00
void RevBytes(unsigned char *b, size_t c) {
2018-03-21 22:32:59 +01:00
u1_t i;
2018-06-12 12:52:48 +02:00
for (i = 0; i < c / 2; i++) {
unsigned char t = b[i];
2018-03-21 22:32:59 +01:00
b[i] = b[c - 1 - i];
2018-06-12 12:52:48 +02:00
b[c - 1 - i] = t;
}
2018-03-21 22:32:59 +01:00
}
2018-07-15 14:28:05 +02:00
// LMIC callback functions
void os_getDevKey(u1_t *buf) { memcpy(buf, APPKEY, 16); }
void os_getArtEui(u1_t *buf) {
memcpy(buf, APPEUI, 8);
RevBytes(buf, 8); // TTN requires it in LSB First order, so we swap bytes
}
void os_getDevEui(u1_t *buf) {
int i = 0, k = 0;
memcpy(buf, DEVEUI, 8); // get fixed DEVEUI from loraconf.h
for (i = 0; i < 8; i++) {
k += buf[i];
}
if (k) {
RevBytes(buf, 8); // use fixed DEVEUI and swap bytes to LSB format
} else {
gen_lora_deveui(buf); // generate DEVEUI from device's MAC
}
// Get MCP 24AA02E64 hardware DEVEUI (override default settings if found)
#ifdef MCP_24AA02E64_I2C_ADDRESS
get_hard_deveui(buf);
RevBytes(buf, 8); // swap bytes to LSB format
#endif
}
void get_hard_deveui(uint8_t *pdeveui) {
2018-06-12 12:52:48 +02:00
// read DEVEUI from Microchip 24AA02E64 2Kb serial eeprom if present
#ifdef MCP_24AA02E64_I2C_ADDRESS
uint8_t i2c_ret;
2018-06-12 12:52:48 +02:00
// Init this just in case, no more to 100KHz
2019-09-02 16:08:12 +02:00
Wire.begin(SDA, SCL, 100000);
2018-06-12 12:52:48 +02:00
Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS);
Wire.write(MCP_24AA02E64_MAC_ADDRESS);
i2c_ret = Wire.endTransmission();
// check if device was seen on i2c bus
if (i2c_ret == 0) {
2018-06-12 12:52:48 +02:00
char deveui[32] = "";
uint8_t data;
Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS);
2018-06-12 12:52:48 +02:00
Wire.write(MCP_24AA02E64_MAC_ADDRESS);
Wire.endTransmission();
Wire.requestFrom(MCP_24AA02E64_I2C_ADDRESS, 8);
while (Wire.available()) {
data = Wire.read();
sprintf(deveui + strlen(deveui), "%02X ", data);
*pdeveui++ = data;
}
ESP_LOGI(TAG, "Serial EEPROM found, read DEVEUI %s", deveui);
} else
ESP_LOGI(TAG, "Could not read DEVEUI from serial EEPROM");
2018-06-12 12:52:48 +02:00
// Set back to 400KHz to speed up OLED
Wire.setClock(400000);
#endif // MCP 24AA02E64
}
2019-03-09 00:53:11 +01:00
#if (VERBOSE)
2018-03-18 19:45:17 +01:00
// Display OTAA keys
2018-08-03 23:50:04 +02:00
void showLoraKeys(void) {
2018-06-12 12:52:48 +02:00
// LMIC may not have used callback to fill
// all EUI buffer so we do it here to a temp
// buffer to be able to display them
uint8_t buf[32];
os_getDevEui((u1_t *)buf);
printKey("DevEUI", buf, 8, true);
os_getArtEui((u1_t *)buf);
printKey("AppEUI", buf, 8, true);
os_getDevKey((u1_t *)buf);
printKey("AppKey", buf, 16, false);
2018-03-18 19:45:17 +01:00
}
#endif // VERBOSE
2018-06-12 12:52:48 +02:00
void onEvent(ev_t ev) {
char buff[24] = "";
2019-03-09 15:25:44 +01:00
2018-06-12 12:52:48 +02:00
switch (ev) {
2018-06-12 12:52:48 +02:00
case EV_SCAN_TIMEOUT:
2019-03-23 11:01:49 +01:00
strcpy_P(buff, PSTR("SCAN TIMEOUT"));
2018-06-12 12:52:48 +02:00
break;
2018-06-12 12:52:48 +02:00
case EV_BEACON_FOUND:
2019-03-23 11:01:49 +01:00
strcpy_P(buff, PSTR("BEACON FOUND"));
2018-06-12 12:52:48 +02:00
break;
2018-06-12 12:52:48 +02:00
case EV_BEACON_MISSED:
2019-03-23 11:01:49 +01:00
strcpy_P(buff, PSTR("BEACON MISSED"));
2018-06-12 12:52:48 +02:00
break;
2018-06-12 12:52:48 +02:00
case EV_BEACON_TRACKED:
2019-03-23 11:01:49 +01:00
strcpy_P(buff, PSTR("BEACON TRACKED"));
2018-06-12 12:52:48 +02:00
break;
2018-06-12 12:52:48 +02:00
case EV_JOINING:
strcpy_P(buff, PSTR("JOINING"));
break;
2018-06-10 21:03:16 +02:00
2018-06-12 12:52:48 +02:00
case EV_JOINED:
strcpy_P(buff, PSTR("JOINED"));
// set data rate adaptation according to saved setting
2018-06-12 12:52:48 +02:00
LMIC_setAdrMode(cfg.adrmode);
2019-09-20 13:22:45 +02:00
// set data rate and transmit power to defaults only if we have no ADR
2019-09-19 17:06:37 +02:00
if (!cfg.adrmode)
2019-09-20 13:22:45 +02:00
LMIC_setDrTxpow(assertDR(cfg.loradr), cfg.txpower);
2019-09-19 17:06:37 +02:00
// show current devaddr
2019-04-13 13:59:30 +02:00
ESP_LOGI(TAG, "DEVaddr=%08X", LMIC.devaddr);
2019-09-20 13:22:45 +02:00
ESP_LOGI(TAG, "Radio parameters %s / %s / %s",
getSfName(updr2rps(LMIC.datarate)),
getBwName(updr2rps(LMIC.datarate)),
getCrName(updr2rps(LMIC.datarate)));
2018-06-12 12:52:48 +02:00
break;
2019-03-23 11:01:49 +01:00
case EV_RFU1:
strcpy_P(buff, PSTR("RFU1"));
break;
case EV_JOIN_FAILED:
2019-03-23 11:01:49 +01:00
strcpy_P(buff, PSTR("JOIN FAILED"));
break;
case EV_REJOIN_FAILED:
2019-03-23 11:01:49 +01:00
strcpy_P(buff, PSTR("REJOIN FAILED"));
break;
2018-06-12 12:52:48 +02:00
case EV_TXCOMPLETE:
2019-09-20 19:45:37 +02:00
strcpy_P(buff, PSTR("TX COMPLETE"));
2018-06-12 12:52:48 +02:00
break;
case EV_LOST_TSYNC:
2019-03-23 11:01:49 +01:00
strcpy_P(buff, PSTR("LOST TSYNC"));
break;
case EV_RESET:
strcpy_P(buff, PSTR("RESET"));
break;
case EV_RXCOMPLETE:
// data received in ping slot
2019-03-23 11:01:49 +01:00
strcpy_P(buff, PSTR("RX COMPLETE"));
break;
case EV_LINK_DEAD:
2019-03-23 11:01:49 +01:00
strcpy_P(buff, PSTR("LINK DEAD"));
break;
case EV_LINK_ALIVE:
2018-11-25 20:56:14 +01:00
strcpy_P(buff, PSTR("LINK_ALIVE"));
break;
2019-03-23 11:01:49 +01:00
case EV_SCAN_FOUND:
strcpy_P(buff, PSTR("SCAN FOUND"));
break;
case EV_TXSTART:
2019-07-22 22:00:39 +02:00
if (!(LMIC.opmode & OP_JOINING)) {
2019-08-29 10:15:11 +02:00
strcpy_P(buff, PSTR("TX START"));
2019-07-22 22:00:39 +02:00
}
break;
2019-03-23 11:01:49 +01:00
case EV_TXCANCELED:
strcpy_P(buff, PSTR("TX CANCELLED"));
2018-10-22 19:42:27 +02:00
break;
2019-03-23 11:01:49 +01:00
case EV_RXSTART:
strcpy_P(buff, PSTR("RX START"));
break;
case EV_JOIN_TXCOMPLETE:
2019-03-24 16:23:34 +01:00
strcpy_P(buff, PSTR("JOIN WAIT"));
2018-10-22 19:42:27 +02:00
break;
2018-06-12 12:52:48 +02:00
default:
2019-03-23 11:01:49 +01:00
sprintf_P(buff, PSTR("LMIC EV %d"), ev);
2018-06-12 12:52:48 +02:00
break;
}
2018-06-12 12:52:48 +02:00
// Log & Display if asked
if (*buff) {
2019-03-23 11:01:49 +01:00
ESP_LOGI(TAG, "%s", buff);
2019-09-20 19:45:37 +02:00
sprintf(lmic_event_msg, buff);
2018-06-12 12:52:48 +02:00
}
}
2018-07-14 20:07:33 +02:00
// LMIC send task
void lora_send(void *pvParameters) {
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
2019-08-28 21:55:50 +02:00
MessageBuffer_t SendBuffer;
2019-08-28 21:55:50 +02:00
while (1) {
2019-08-30 14:06:30 +02:00
// postpone until we are joined if we are not
2019-08-30 14:06:30 +02:00
while (!LMIC.devaddr) {
vTaskDelay(pdMS_TO_TICKS(500));
}
2019-08-29 20:23:22 +02:00
// fetch next or wait for payload to send from queue
2019-08-30 14:06:30 +02:00
if (xQueueReceive(LoraSendQueue, &SendBuffer, portMAX_DELAY) != pdTRUE) {
ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!");
continue;
}
2019-08-30 14:06:30 +02:00
// attempt to transmit payload
else {
switch (LMIC_sendWithCallback_strict(
2019-08-31 15:19:49 +02:00
SendBuffer.MessagePort, SendBuffer.Message, SendBuffer.MessageSize,
(cfg.countermode & 0x02), myTxCallback, NULL)) {
case LMIC_ERROR_SUCCESS:
2019-08-31 18:36:49 +02:00
ESP_LOGI(TAG, "%d byte(s) sent to LORA", SendBuffer.MessageSize);
2019-08-30 14:06:30 +02:00
break;
case LMIC_ERROR_TX_BUSY: // LMIC already has a tx message pending
case LMIC_ERROR_TX_FAILED: // message was not sent
// ESP_LOGD(TAG, "LMIC busy, message re-enqueued"); // very noisy
2019-08-30 14:06:30 +02:00
vTaskDelay(pdMS_TO_TICKS(1000 + random(500))); // wait a while
lora_enqueuedata(&SendBuffer); // re-enqueue the undelivered message
2019-08-30 14:06:30 +02:00
break;
case LMIC_ERROR_TX_TOO_LARGE: // message size exceeds LMIC buffer size
case LMIC_ERROR_TX_NOT_FEASIBLE: // message too large for current datarate
ESP_LOGI(TAG,
"Message too large to send, message not sent and deleted");
// we need some kind of error handling here -> to be done
2019-08-30 14:06:30 +02:00
break;
default: // other LMIC return code
ESP_LOGE(TAG, "LMIC error, message not sent and deleted");
2019-08-30 14:06:30 +02:00
} // switch
}
delay(2); // yield to CPU
2018-10-03 16:24:45 +02:00
}
}
2018-11-03 20:29:02 +01:00
esp_err_t lora_stack_init() {
assert(SEND_QUEUE_SIZE);
2018-11-03 20:29:02 +01:00
LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
if (LoraSendQueue == 0) {
ESP_LOGE(TAG, "Could not create LORA send queue. Aborting.");
return ESP_FAIL;
}
ESP_LOGI(TAG, "LORA send queue created, size %d Bytes",
2019-07-14 16:52:18 +02:00
SEND_QUEUE_SIZE * sizeof(MessageBuffer_t));
2018-11-03 20:29:02 +01:00
2019-08-29 20:23:22 +02:00
// start lorawan stack
2018-11-03 20:29:02 +01:00
ESP_LOGI(TAG, "Starting LMIC...");
xTaskCreatePinnedToCore(lmictask, // task function
"lmictask", // name of task
4096, // stack size of task
(void *)1, // parameter of the task
2019-09-17 14:32:22 +02:00
5, // priority of the task
&lmicTask, // task handle
1); // CPU core
2018-11-03 20:29:02 +01:00
2019-08-30 14:06:30 +02:00
if (!LMIC_startJoining()) { // start joining
ESP_LOGI(TAG, "Already joined");
}
2019-08-29 20:23:22 +02:00
// start lmic send task
xTaskCreatePinnedToCore(lora_send, // task function
"lorasendtask", // name of task
2019-08-30 18:59:45 +02:00
3072, // stack size of task
(void *)1, // parameter of the task
2019-08-29 20:23:22 +02:00
1, // priority of the task
&lorasendTask, // task handle
1); // CPU core
2019-08-29 20:23:22 +02:00
return ESP_OK;
2018-11-03 20:29:02 +01:00
}
void lora_enqueuedata(MessageBuffer_t *message) {
2018-11-03 20:29:02 +01:00
// enqueue message in LORA send queue
2019-08-28 21:55:50 +02:00
BaseType_t ret = pdFALSE;
2019-03-16 21:01:43 +01:00
MessageBuffer_t DummyBuffer;
sendprio_t prio = message->MessagePrio;
switch (prio) {
case prio_high:
2019-08-28 21:55:50 +02:00
// clear some space in queue if full, then fallthrough to prio_normal
if (uxQueueSpacesAvailable(LoraSendQueue) == 0) {
2019-03-16 21:01:43 +01:00
xQueueReceive(LoraSendQueue, &DummyBuffer, (TickType_t)0);
2019-08-28 21:55:50 +02:00
ESP_LOGW(TAG, "LORA sendqueue purged, data is lost");
}
2019-03-16 21:01:43 +01:00
case prio_normal:
2019-02-16 15:02:07 +01:00
ret = xQueueSendToFront(LoraSendQueue, (void *)message, (TickType_t)0);
break;
case prio_low:
default:
ret = xQueueSendToBack(LoraSendQueue, (void *)message, (TickType_t)0);
break;
}
2019-08-30 14:06:30 +02:00
if (ret != pdTRUE)
2018-11-03 20:29:02 +01:00
ESP_LOGW(TAG, "LORA sendqueue is full");
}
2019-02-27 00:52:27 +01:00
void lora_queuereset(void) { xQueueReset(LoraSendQueue); }
2018-11-03 20:29:02 +01:00
2019-03-31 15:32:22 +02:00
#if (TIME_SYNC_LORAWAN)
2019-03-28 14:19:02 +01:00
void IRAM_ATTR user_request_network_time_callback(void *pVoidUserUTCTime,
int flagSuccess) {
// Explicit conversion from void* to uint32_t* to avoid compiler errors
2019-02-23 23:39:45 +01:00
time_t *pUserUTCTime = (time_t *)pVoidUserUTCTime;
2019-02-27 00:52:27 +01:00
// A struct that will be populated by LMIC_getNetworkTimeReference.
// It contains the following fields:
// - tLocal: the value returned by os_GetTime() when the time
// request was sent to the gateway, and
// - tNetwork: the seconds between the GPS epoch and the time
// the gateway received the time request
lmic_time_reference_t lmicTimeReference;
if (flagSuccess != 1) {
2018-11-17 18:30:19 +01:00
ESP_LOGW(TAG, "LoRaWAN network did not answer time request");
return;
}
// Populate lmic_time_reference
flagSuccess = LMIC_getNetworkTimeReference(&lmicTimeReference);
if (flagSuccess != 1) {
2018-11-17 18:30:19 +01:00
ESP_LOGW(TAG, "LoRaWAN time request failed");
return;
}
// mask application irq to ensure accurate timing
2019-07-23 20:43:10 +02:00
mask_user_IRQ();
2019-03-28 14:19:02 +01:00
// Update userUTCTime, considering the difference between the GPS and UTC
2019-03-09 15:25:44 +01:00
// time, and the leap seconds until year 2019
*pUserUTCTime = lmicTimeReference.tNetwork + 315964800;
// Current time, in ticks
ostime_t ticksNow = os_getTime();
// Time when the request was sent, in ticks
ostime_t ticksRequestSent = lmicTimeReference.tLocal;
// Add the delay between the instant the time was transmitted and
// the current time
2019-02-23 23:39:45 +01:00
time_t requestDelaySec = osticks2ms(ticksNow - ticksRequestSent) / 1000;
2019-01-28 23:59:52 +01:00
// Update system time with time read from the network
2019-08-03 12:27:24 +02:00
setMyTime(*pUserUTCTime + requestDelaySec, 0, _lora);
2019-03-25 19:06:54 +01:00
finish:
// end of time critical section: release app irq lock
2019-03-31 19:13:06 +02:00
unmask_user_IRQ();
2019-03-28 14:19:02 +01:00
2019-03-23 11:01:49 +01:00
} // user_request_network_time_callback
2019-03-31 15:32:22 +02:00
#endif // TIME_SYNC_LORAWAN
2019-02-27 00:52:27 +01:00
// LMIC lorawan stack task
void lmictask(void *pvParameters) {
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
2019-08-03 12:27:24 +02:00
os_init(); // initialize lmic run-time environment
LMIC_reset(); // initialize lmic MAC
2019-10-05 23:08:13 +02:00
// pre-join settings
LMIC_setDrTxpow(assertDR(LORADRDEFAULT), LORATXPOWDEFAULT);
2019-09-19 17:06:37 +02:00
LMIC_setLinkCheckMode(0);
// This tells LMIC to make the receive windows bigger, in case your clock is
// faster or slower. This causes the transceiver to be earlier switched on,
// so consuming more power. You may sharpen (reduce) CLOCK_ERROR_PERCENTAGE
// in src/lmic_config.h if you are limited on battery.
#ifdef CLOCK_ERROR_PROCENTAGE
LMIC_setClockError(MAX_CLOCK_ERROR * CLOCK_ERROR_PROCENTAGE / 100);
#endif
2019-09-19 17:06:37 +02:00
#if CFG_LMIC_US_like
// in the US, with TTN, it saves join time if we start on subband 1
// (channels 8-15). This will get overridden after the join by parameters
// from the network. If working with other networks or in other regions,
// this will need to be changed.
LMIC_selectSubBand(1);
#endif
// register a callback for downlink messages. We aren't trying to write
// reentrant code, so pUserData is NULL.
LMIC_registerRxMessageCb(myRxCallback, NULL);
while (1) {
os_runloop_once(); // execute lmic scheduled jobs and events
delay(2); // yield to CPU
}
} // lmictask
2019-08-31 15:19:49 +02:00
// receive message handler
void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
size_t nMsg) {
2019-08-31 18:36:49 +02:00
// display type of received data
2019-08-31 15:19:49 +02:00
if (nMsg)
2019-09-07 14:52:18 +02:00
ESP_LOGI(TAG, "Received %u byte(s) of payload on port %u", nMsg, port);
2019-08-31 18:36:49 +02:00
else if (port)
ESP_LOGI(TAG, "Received empty message on port %u", port);
// list MAC messages, if any
uint8_t nMac = pMsg - &LMIC.frame[0];
if (port != MACPORT)
--nMac;
if (nMac) {
2019-09-07 14:52:18 +02:00
ESP_LOGI(TAG, "%u byte(s) downlink MAC commands", nMac);
2019-08-31 18:36:49 +02:00
// NOT WORKING YET
2019-09-07 14:52:18 +02:00
// whe need to unwrap the MAC command from LMIC.frame here
// mac_decode(LMIC.frame, nMac, MACdn_table, sizeof(MACdn_table) /
// sizeof(MACdn_table[0]));
}
if (LMIC.pendMacLen) {
2019-09-07 14:52:18 +02:00
ESP_LOGI(TAG, "%u byte(s) uplink MAC commands", LMIC.pendMacLen);
mac_decode(LMIC.pendMacData, LMIC.pendMacLen, MACup_table,
sizeof(MACup_table) / sizeof(MACup_table[0]));
2019-08-31 18:36:49 +02:00
}
2019-08-31 15:19:49 +02:00
switch (port) {
// ignore mac messages
case MACPORT:
break;
// rcommand received -> call interpreter
case RCMDPORT:
rcommand(pMsg, nMsg);
break;
default:
#if (TIME_SYNC_LORASERVER)
// valid timesync answer -> call timesync processor
if (port == TIMEPORT) {
recv_timesync_ans(pMsg, nMsg);
break;
}
2019-08-31 15:19:49 +02:00
#endif
// unknown port -> display info
2019-08-31 18:36:49 +02:00
ESP_LOGI(TAG, "Received data on unsupported port %u", port);
2019-08-31 15:19:49 +02:00
break;
} // switch
}
// transmit complete message handler
2019-08-31 18:36:49 +02:00
void myTxCallback(void *pUserData, int fSuccess) {
2019-09-20 19:45:37 +02:00
#if (TIME_SYNC_LORASERVER)
// if last packet sent was a timesync request, store TX timestamp
if (LMIC.pendTxPort == TIMEPORT)
store_time_sync_req(osticks2ms(LMIC.txend)); // milliseconds
#endif
2019-08-31 18:36:49 +02:00
}
// decode LORAWAN MAC message
2019-09-07 14:52:18 +02:00
void mac_decode(const uint8_t cmd[], const uint8_t cmdlen, const mac_t table[],
const uint8_t tablesize) {
2019-08-31 18:36:49 +02:00
2019-09-07 14:52:18 +02:00
if (!cmdlen)
2019-08-31 18:36:49 +02:00
return;
2019-09-07 14:52:18 +02:00
uint8_t foundcmd[cmdlen], cursor = 0;
while (cursor < cmdlen) {
2019-08-31 18:36:49 +02:00
2019-09-07 14:52:18 +02:00
int i = tablesize; // number of commands in table
2019-08-31 18:36:49 +02:00
while (i--) {
if (cmd[cursor] == table[i].opcode) { // lookup command in opcode table
cursor++; // strip 1 byte opcode
2019-09-07 14:52:18 +02:00
if ((cursor + table[i].params) <= cmdlen) {
2019-08-31 18:36:49 +02:00
memmove(foundcmd, cmd + cursor,
table[i].params); // strip opcode from cmd array
cursor += table[i].params;
2019-09-07 14:52:18 +02:00
ESP_LOGD(TAG, "MAC command %s", table[i].cmdname);
2019-08-31 18:36:49 +02:00
} else
2019-09-07 14:52:18 +02:00
ESP_LOGD(TAG, "MAC command 0x%02X with missing parameter(s)",
2019-08-31 18:36:49 +02:00
table[i].opcode);
break; // command found -> exit table lookup loop
} // end of command validation
} // end of command table lookup loop
if (i < 0) { // command not found -> skip it
2019-09-07 14:52:18 +02:00
ESP_LOGD(TAG, "Unknown MAC command 0x%02X", cmd[cursor]);
2019-08-31 18:36:49 +02:00
cursor++;
}
} // command parsing loop
} // mac_decode()
2019-08-31 15:19:49 +02:00
2019-09-20 09:04:24 +02:00
uint8_t getBattLevel() {
/*
return values:
MCMD_DEVS_EXT_POWER = 0x00, // external power supply
MCMD_DEVS_BATT_MIN = 0x01, // min battery value
MCMD_DEVS_BATT_MAX = 0xFE, // max battery value
MCMD_DEVS_BATT_NOINFO = 0xFF, // unknown battery level
*/
#if (defined HAS_PMU || defined BAT_MEASURE_ADC)
uint16_t voltage = read_voltage();
switch (voltage) {
case 0:
return MCMD_DEVS_BATT_NOINFO;
case 0xffff:
return MCMD_DEVS_EXT_POWER;
default:
return (voltage > OTA_MIN_BATT ? MCMD_DEVS_BATT_MAX : MCMD_DEVS_BATT_MIN);
}
#else // we don't have any info on battery level
return MCMD_DEVS_BATT_NOINFO;
#endif
} // getBattLevel()
2019-09-20 09:51:33 +02:00
// u1_t os_getBattLevel(void) { return getBattLevel(); };
const char *getSfName(rps_t rps) {
2019-10-05 23:08:13 +02:00
const char *const t[] = {"FSK", "SF7", "SF8", "SF9",
2019-09-20 20:00:31 +02:00
"SF10", "SF11", "SF12", "SF?"};
2019-09-20 09:51:33 +02:00
return t[getSf(rps)];
}
2019-09-20 09:04:24 +02:00
2019-09-20 13:22:45 +02:00
const char *getBwName(rps_t rps) {
2019-09-20 20:00:31 +02:00
const char *const t[] = {"BW125", "BW250", "BW500", "BW?"};
2019-09-20 13:22:45 +02:00
return t[getBw(rps)];
}
const char *getCrName(rps_t rps) {
const char *const t[] = {"CR 4/5", "CR 4/6", "CR 4/7", "CR 4/8"};
return t[getCr(rps)];
}
2019-08-31 15:19:49 +02:00
#endif // HAS_LORA