ESP32-PaxCounter/src/lorawan.cpp

350 lines
9.4 KiB
C++
Raw Normal View History

2018-07-14 20:07:33 +02:00
#ifdef HAS_LORA
2018-03-18 19:45:17 +01:00
// Basic Config
#include "lorawan.h"
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
2018-10-03 16:24:45 +02:00
osjob_t sendjob;
QueueHandle_t LoraSendQueue;
2018-07-23 13:20:06 +02:00
// LMIC enhanced Pin mapping
const lmic_pinmap lmic_pins = {
.nss = SS,
.rxtx = LMIC_UNUSED_PIN,
.rst = RST,
.dio = {DIO0, DIO1, DIO2}
// 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-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
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
}
2018-03-18 19:45:17 +01:00
#ifdef VERBOSE
// 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] = "";
switch (ev) {
2018-06-12 12:52:48 +02:00
case EV_SCAN_TIMEOUT:
strcpy_P(buff, PSTR("SCAN TIMEOUT"));
break;
2018-06-12 12:52:48 +02:00
case EV_BEACON_FOUND:
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:
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:
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"));
2018-07-14 20:07:33 +02:00
sprintf(display_line6, " "); // clear previous lmic status
// set data rate adaptation according to saved setting
2018-06-12 12:52:48 +02:00
LMIC_setAdrMode(cfg.adrmode);
// set cyclic lmic link check to off if no ADR because is not supported by
// ttn (but enabled by lmic after join)
LMIC_setLinkCheckMode(cfg.adrmode);
2018-06-12 12:52:48 +02:00
// Set data rate and transmit power (note: txpower seems to be ignored by
// the library)
switch_lora(cfg.lorasf, cfg.txpower);
2018-10-03 16:24:45 +02:00
// kickoff first send job
os_setCallback(&sendjob, lora_send);
2018-06-12 12:52:48 +02:00
// show effective LoRa parameters after join
ESP_LOGI(TAG, "ADR=%d, SF=%d, TXPOWER=%d", cfg.adrmode, cfg.lorasf,
cfg.txpower);
break;
case EV_JOIN_FAILED:
strcpy_P(buff, PSTR("JOIN_FAILED"));
break;
case EV_REJOIN_FAILED:
strcpy_P(buff, PSTR("REJOIN_FAILED"));
break;
2018-06-12 12:52:48 +02:00
case EV_TXCOMPLETE:
2018-06-12 12:52:48 +02:00
strcpy_P(buff, (LMIC.txrxFlags & TXRX_ACK) ? PSTR("RECEIVED ACK")
: PSTR("TX COMPLETE"));
2018-07-14 20:07:33 +02:00
sprintf(display_line6, " "); // clear previous lmic status
2018-06-12 12:52:48 +02:00
if (LMIC.dataLen) {
ESP_LOGI(TAG, "Received %d bytes of payload, RSSI %d SNR %d",
LMIC.dataLen, (signed char)LMIC.rssi, (signed char)LMIC.snr);
sprintf(display_line6, "RSSI %d SNR %d", (signed char)LMIC.rssi,
2018-09-15 18:59:20 +02:00
(signed char)LMIC.snr);
2018-06-12 12:52:48 +02:00
2018-07-31 00:00:24 +02:00
// check if command is received on command port, then call interpreter
2018-06-12 12:52:48 +02:00
if ((LMIC.txrxFlags & TXRX_PORT) &&
2018-07-31 00:00:24 +02:00
(LMIC.frame[LMIC.dataBeg - 1] == RCMDPORT))
rcommand(LMIC.frame + LMIC.dataBeg, LMIC.dataLen);
}
2018-06-12 12:52:48 +02:00
break;
case EV_LOST_TSYNC:
strcpy_P(buff, PSTR("LOST_TSYNC"));
break;
case EV_RESET:
strcpy_P(buff, PSTR("RESET"));
break;
case EV_RXCOMPLETE:
// data received in ping slot
strcpy_P(buff, PSTR("RX COMPLETE"));
break;
case EV_LINK_DEAD:
strcpy_P(buff, PSTR("LINK DEAD"));
break;
case EV_LINK_ALIVE:
strcpy_P(buff, PSTR("LINK ALIVE"));
break;
case EV_TXSTART:
strcpy_P(buff, PSTR("TX START"));
break;
/*
|| This event is defined but not used in the code. No
|| point in wasting codespace on it.
||
|| case EV_SCAN_FOUND:
|| Serial.println(F("EV_SCAN_FOUND"));
|| break;
*/
/*
|| This event is defined but not used in the code. No
|| point in wasting codespace on it.
||
|| case EV_RFU1:
|| Serial.println(F("EV_RFU1"));
|| break;
*/
2018-06-12 12:52:48 +02:00
default:
sprintf_P(buff, PSTR("UNKNOWN EVENT %d"), ev);
break;
}
2018-06-12 12:52:48 +02:00
// Log & Display if asked
if (*buff) {
ESP_LOGI(TAG, "EV_%s", buff);
2018-07-14 20:07:33 +02:00
sprintf(display_line7, buff);
2018-06-12 12:52:48 +02:00
}
}
2018-07-14 20:07:33 +02:00
2018-08-05 12:16:54 +02:00
// helper function to assign LoRa datarates to numeric spreadfactor values
void switch_lora(uint8_t sf, uint8_t tx) {
if (tx > 20)
return;
cfg.txpower = tx;
switch (sf) {
case 7:
LMIC_setDrTxpow(DR_SF7, tx);
cfg.lorasf = sf;
break;
case 8:
LMIC_setDrTxpow(DR_SF8, tx);
cfg.lorasf = sf;
break;
case 9:
LMIC_setDrTxpow(DR_SF9, tx);
cfg.lorasf = sf;
break;
case 10:
LMIC_setDrTxpow(DR_SF10, tx);
cfg.lorasf = sf;
break;
case 11:
#if defined(CFG_eu868)
LMIC_setDrTxpow(DR_SF11, tx);
cfg.lorasf = sf;
break;
#elif defined(CFG_us915)
LMIC_setDrTxpow(DR_SF11CR, tx);
cfg.lorasf = sf;
break;
#endif
case 12:
#if defined(CFG_eu868)
LMIC_setDrTxpow(DR_SF12, tx);
cfg.lorasf = sf;
break;
#elif defined(CFG_us915)
LMIC_setDrTxpow(DR_SF12CR, tx);
cfg.lorasf = sf;
break;
#endif
default:
break;
}
}
2018-10-03 16:24:45 +02:00
void lora_send(osjob_t *job) {
MessageBuffer_t SendBuffer;
// Check if there is a pending TX/RX job running, if yes don't eat data
// since it cannot be sent right now
if ((LMIC.opmode & (OP_JOINING | OP_REJOIN | OP_TXDATA | OP_POLL)) != 0) {
// waiting for LoRa getting ready
} else {
if (xQueueReceive(LoraSendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
// SendBuffer gets struct MessageBuffer with next payload from queue
LMIC_setTxData2(SendBuffer.MessagePort, SendBuffer.Message,
SendBuffer.MessageSize, (cfg.countermode & 0x02));
ESP_LOGI(TAG, "%d bytes sent to LoRa", SendBuffer.MessageSize);
//sprintf(display_line7, "PACKET QUEUED");
2018-10-03 16:24:45 +02:00
}
}
// reschedule job every 0,5 - 1 sec. including a bit of random to prevent
// systematic collisions
os_setTimedCallback(job, os_getTime() + 500 + ms2osticks(random(500)),
lora_send);
}
2018-07-14 20:07:33 +02:00
#endif // HAS_LORA