2018-07-14 20:07:33 +02:00
|
|
|
#ifdef HAS_LORA
|
|
|
|
|
2018-03-18 19:45:17 +01:00
|
|
|
// Basic Config
|
|
|
|
#include "globals.h"
|
2018-07-15 14:28:05 +02:00
|
|
|
#include "rcommand.h"
|
2018-03-18 19:45:17 +01:00
|
|
|
|
2018-04-04 01:26:05 +02:00
|
|
|
#ifdef MCP_24AA02E64_I2C_ADDRESS
|
2018-06-12 12:52:48 +02:00
|
|
|
#include <Wire.h> // Needed for 24AA02E64, does not hurt anything if included and not used
|
2018-04-04 01:26:05 +02:00
|
|
|
#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
|
|
|
|
2018-07-23 13:20:06 +02:00
|
|
|
// LMIC enhanced Pin mapping
|
|
|
|
const lmic_pinmap lmic_pins = {.mosi = PIN_SPI_MOSI,
|
|
|
|
.miso = PIN_SPI_MISO,
|
|
|
|
.sck = PIN_SPI_SCK,
|
|
|
|
.nss = PIN_SPI_SS,
|
|
|
|
.rxtx = LMIC_UNUSED_PIN,
|
|
|
|
.rst = RST,
|
|
|
|
.dio = {DIO0, DIO1, DIO2}};
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2018-04-04 01:26:05 +02:00
|
|
|
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
|
2018-04-04 01:26:05 +02:00
|
|
|
#ifdef MCP_24AA02E64_I2C_ADDRESS
|
2018-06-12 12:52:48 +02:00
|
|
|
uint8_t i2c_ret;
|
|
|
|
// Init this just in case, no more to 100KHz
|
|
|
|
Wire.begin(OLED_SDA, OLED_SCL, 100000);
|
|
|
|
Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS);
|
|
|
|
Wire.write(MCP_24AA02E64_MAC_ADDRESS);
|
|
|
|
i2c_ret = Wire.endTransmission();
|
|
|
|
// check if device seen on i2c bus
|
|
|
|
if (i2c_ret == 0) {
|
|
|
|
char deveui[32] = "";
|
|
|
|
uint8_t data;
|
2018-04-04 01:26:05 +02:00
|
|
|
Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS);
|
2018-06-12 12:52:48 +02:00
|
|
|
Wire.write(MCP_24AA02E64_MAC_ADDRESS);
|
|
|
|
Wire.requestFrom(MCP_24AA02E64_I2C_ADDRESS, 8);
|
|
|
|
while (Wire.available()) {
|
|
|
|
data = Wire.read();
|
|
|
|
sprintf(deveui + strlen(deveui), "%02X ", data);
|
|
|
|
*pdeveui++ = data;
|
2018-06-10 21:46:58 +02:00
|
|
|
}
|
2018-06-12 12:52:48 +02:00
|
|
|
i2c_ret = Wire.endTransmission();
|
|
|
|
ESP_LOGI(TAG, "Serial EEPROM 24AA02E64 found, read DEVEUI %s", deveui);
|
|
|
|
} else {
|
|
|
|
ESP_LOGI(TAG, "Serial EEPROM 24AA02E64 not found ret=%d", i2c_ret);
|
|
|
|
}
|
|
|
|
// Set back to 400KHz to speed up OLED
|
|
|
|
Wire.setClock(400000);
|
|
|
|
#endif // MCP 24AA02E64
|
2018-04-04 01:26:05 +02:00
|
|
|
}
|
|
|
|
|
2018-03-18 19:45:17 +01:00
|
|
|
#ifdef VERBOSE
|
|
|
|
|
|
|
|
// Display a key
|
2018-06-12 12:52:48 +02:00
|
|
|
void printKey(const char *name, const uint8_t *key, uint8_t len, bool lsb) {
|
|
|
|
const uint8_t *p;
|
|
|
|
char keystring[len + 1] = "", keybyte[3];
|
|
|
|
for (uint8_t i = 0; i < len; i++) {
|
|
|
|
p = lsb ? key + len - i - 1 : key + i;
|
|
|
|
sprintf(keybyte, "%02X", *p);
|
|
|
|
strncat(keystring, keybyte, 2);
|
|
|
|
}
|
2018-03-18 19:45:17 +01:00
|
|
|
ESP_LOGI(TAG, "%s: %s", name, keystring);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Display OTAA keys
|
|
|
|
void printKeys(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) {
|
|
|
|
case EV_SCAN_TIMEOUT:
|
|
|
|
strcpy_P(buff, PSTR("SCAN TIMEOUT"));
|
|
|
|
break;
|
|
|
|
case EV_BEACON_FOUND:
|
|
|
|
strcpy_P(buff, PSTR("BEACON FOUND"));
|
|
|
|
break;
|
|
|
|
case EV_BEACON_MISSED:
|
|
|
|
strcpy_P(buff, PSTR("BEACON MISSED"));
|
|
|
|
break;
|
|
|
|
case EV_BEACON_TRACKED:
|
|
|
|
strcpy_P(buff, PSTR("BEACON TRACKED"));
|
|
|
|
break;
|
|
|
|
case EV_JOINING:
|
|
|
|
strcpy_P(buff, PSTR("JOINING"));
|
|
|
|
break;
|
|
|
|
case EV_LOST_TSYNC:
|
|
|
|
strcpy_P(buff, PSTR("LOST TSYNC"));
|
|
|
|
break;
|
|
|
|
case EV_RESET:
|
|
|
|
strcpy_P(buff, PSTR("RESET"));
|
|
|
|
break;
|
|
|
|
case EV_RXCOMPLETE:
|
|
|
|
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_RFU1:
|
|
|
|
strcpy_P(buff, PSTR("RFUI"));
|
|
|
|
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-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
|
2018-06-12 12:52:48 +02:00
|
|
|
|
|
|
|
// set data rate adaptation
|
|
|
|
LMIC_setAdrMode(cfg.adrmode);
|
|
|
|
// Set data rate and transmit power (note: txpower seems to be ignored by
|
|
|
|
// the library)
|
|
|
|
switch_lora(cfg.lorasf, cfg.txpower);
|
|
|
|
|
|
|
|
// show effective LoRa parameters after join
|
|
|
|
ESP_LOGI(TAG, "ADR=%d, SF=%d, TXPOWER=%d", cfg.adrmode, cfg.lorasf,
|
|
|
|
cfg.txpower);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EV_TXCOMPLETE:
|
|
|
|
|
|
|
|
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, LMIC.rssi, (signed char)LMIC.snr / 4);
|
|
|
|
// LMIC.snr = SNR twos compliment [dB] * 4
|
|
|
|
// LMIC.rssi = RSSI [dBm] (-196...+63)
|
2018-07-14 20:07:33 +02:00
|
|
|
sprintf(display_line6, "RSSI %d SNR %d", LMIC.rssi,
|
2018-06-12 12:52:48 +02:00
|
|
|
(signed char)LMIC.snr / 4);
|
|
|
|
|
|
|
|
// check if payload received on command port, then call remote command
|
|
|
|
// interpreter
|
|
|
|
if ((LMIC.txrxFlags & TXRX_PORT) &&
|
|
|
|
(LMIC.frame[LMIC.dataBeg - 1] == RCMDPORT)) {
|
|
|
|
// caution: buffering LMIC values here because rcommand() can modify
|
|
|
|
// LMIC.frame
|
|
|
|
unsigned char *buffer = new unsigned char[MAX_LEN_FRAME];
|
|
|
|
memcpy(buffer, LMIC.frame, MAX_LEN_FRAME); // Copy data from cfg to
|
|
|
|
// char*
|
|
|
|
int i, k = LMIC.dataBeg, l = LMIC.dataBeg + LMIC.dataLen - 2;
|
|
|
|
for (i = k; i <= l; i += 2) {
|
|
|
|
rcommand(buffer[i], buffer[i + 1]);
|
|
|
|
}
|
|
|
|
delete[] buffer; // free memory
|
|
|
|
}
|
2018-06-10 21:46:58 +02:00
|
|
|
}
|
2018-06-12 12:52:48 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
sprintf_P(buff, PSTR("UNKNOWN EVENT %d"), ev);
|
|
|
|
break;
|
|
|
|
}
|
2018-06-10 21:46:58 +02:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
} // onEvent()
|
2018-07-14 20:07:33 +02:00
|
|
|
|
2018-07-23 13:20:06 +02:00
|
|
|
// LMIC FreeRTos Task
|
|
|
|
void lorawan_loop(void *pvParameters) {
|
|
|
|
|
|
|
|
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
os_runloop_once(); // execute LMIC jobs
|
|
|
|
vTaskDelay(1 / portTICK_PERIOD_MS); // reset watchdog
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-14 20:07:33 +02:00
|
|
|
#endif // HAS_LORA
|