esp32 TTGO Lora OLED
Links
Getting started
- Add the ESP32 board to the Arduino IDE. Go to "File -> Preferences" and add "https://dl.espressif.com/dl/package_esp32_index.json" to the "Additional Boards Manager URLs"
- Go to "Tools -> Board -> Boards Manager..." and install the esp32 board
- Install the "Adafruit SSD1306" oled driver library and the "Adafruit GFX Library" and the "LoRa" library through "Sketch > Include Library > Manage Libraries"
- Install the "LoRa" and "MCCI LoRaWAN LMIC library" libraries
- Install the "U8g2" library
- Edit "Arduino/libraries/MCCI_LoRaWAN_LMIC_library/project_config/lmic_project_config.h" to meet your environment. The default is CFG_us915 but in Europe we need CFG_eu868
Some code
#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
#include <U8x8lib.h>
/**
* !!! IMPORTANT !!!
*
* Make sure to configure ".../Arduino/libraries/MCCI_LoRaWAN_LMIC_library/project_config/lmic_project_config.h"
*
*/
#define BUILTIN_LED 2
// the OLED used
U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ 15, /* data=*/ 4, /* reset=*/ 16);
// This EUI must be in little-endian format, so least-significant-byte
// first. When copying an EUI from ttnctl output, this means to reverse
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
// 0x70.
static const u1_t PROGMEM APPEUI[8]={ 0x24, 0xFA, 0x00, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 };
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}
// This should also be in little endian format, see above.
static const u1_t PROGMEM DEVEUI[8]={ 0x35, 0xA1, 0x44, 0x41, 0x89, 0xE1, 0x15, 0x00 };
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}
// This key should be in big endian format (or, since it is not really a
// number but a block of memory, endianness does not really apply). In
// practice, a key taken from ttnctl can be copied as-is.
static const u1_t PROGMEM APPKEY[16] = { 0x79, 0x15, 0xDB, 0x3A, 0x1C, 0x37, 0x3E, 0x1E, 0x37, 0x02, 0x1C, 0xCC, 0x49, 0x49, 0x85, 0x53 };
void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);}
static uint8_t mydata[] = "Hello, world!";
static osjob_t sendjob;
// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 30;
// Pin mapping
const lmic_pinmap lmic_pins = {
.nss = 18,
.rxtx = LMIC_UNUSED_PIN,
.rst = 14,
.dio = {26, 33, 32},
};
void printHex2(unsigned v) {
v &= 0xff;
if (v < 16)
Serial.print('0');
Serial.print(v, HEX);
}
void oledPrintf(int col, int row, const char* fmt, ...) {
char msg[50];
va_list args;
va_start(args, fmt);
vsprintf(msg, fmt, args);
va_end(args);
u8x8.setCursor(col, row);
Serial.println(msg);
strcat(msg, " ");
msg[16] = 0; // Mark end of string.
u8x8.print(msg);
}
void onEvent (ev_t ev) {
long now = os_getTime();
oledPrintf(0, 5, "Time %lu", now);
// clear the lines
oledPrintf(0, 3, "");
oledPrintf(0, 6, "");
oledPrintf(0, 7, "");
switch(ev) {
case EV_SCAN_TIMEOUT:
oledPrintf(0, 7, "EV_SCAN_TIMEOUT");
break;
case EV_BEACON_FOUND:
oledPrintf(0, 7, "EV_BEACON_FOUND");
break;
case EV_BEACON_MISSED:
oledPrintf(0, 7, "EV_BEACON_MISSED");
break;
case EV_BEACON_TRACKED:
oledPrintf(0, 7, "EV_BEACON_TRACKED");
break;
case EV_JOINING:
oledPrintf(0, 7, "EV_JOINING");
break;
case EV_JOINED:
oledPrintf(0, 7, "EV_JOINED");
{
u4_t netid = 0;
devaddr_t devaddr = 0;
u1_t nwkKey[16];
u1_t artKey[16];
LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
Serial.print("netid: ");
Serial.println(netid, DEC);
Serial.print("devaddr: ");
Serial.println(devaddr, HEX);
Serial.print("AppSKey: ");
for (size_t i=0; i<sizeof(artKey); ++i) {
if (i != 0) Serial.print("-");
printHex2(artKey[i]);
}
Serial.println("");
Serial.print("NwkSKey: ");
for (size_t i=0; i<sizeof(nwkKey); ++i) {
if (i != 0) Serial.print("-");
printHex2(nwkKey[i]);
}
Serial.println();
}
// Disable link check validation (automatically enabled
// during join, but because slow data rates change max TX
// size, we don't use it in this example.
LMIC_setLinkCheckMode(0);
break;
case EV_RFU1:
oledPrintf(0, 7, "EV_RFU1");
break;
case EV_JOIN_FAILED:
oledPrintf(0, 7, "EV_JOIN_FAILED");
break;
case EV_REJOIN_FAILED:
oledPrintf(0, 7, "EV_REJOIN_FAILED");
break;
case EV_TXCOMPLETE:
oledPrintf(0, 7, "EV_TXCOMPLETE");
digitalWrite(BUILTIN_LED, LOW);
if (LMIC.txrxFlags & TXRX_ACK) {
oledPrintf(0, 3, "rssi:%d, snr:%1d", LMIC.rssi, LMIC.snr);
oledPrintf(0, 6, "Received ack");
}
if (LMIC.dataLen) {
oledPrintf(0, 3, "rssi:%d, snr:%1d", LMIC.rssi, LMIC.snr);
oledPrintf(0, 6, "Received %d", LMIC.dataLen);
Serial.print("Data:");
for(size_t i=0; i<LMIC.dataLen; i++) {
Serial.print(" ");
printHex2(LMIC.frame[i + LMIC.dataBeg]);
}
Serial.println();
}
// Schedule next transmission
os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
break;
case EV_LOST_TSYNC:
oledPrintf(0, 7, "EV_LOST_TSYNC");
break;
case EV_RESET:
oledPrintf(0, 7, "EV_RESET");
break;
case EV_RXCOMPLETE:
// data received in ping slot
oledPrintf(0, 7, "EV_RXCOMPLETE");
break;
case EV_LINK_DEAD:
oledPrintf(0, 7, "EV_LINK_DEAD");
break;
case EV_LINK_ALIVE:
oledPrintf(0, 7, "EV_LINK_ALIVE");
break;
case EV_SCAN_FOUND:
oledPrintf(0, 7, "EV_SCAN_FOUND");
break;
case EV_TXSTART:
oledPrintf(0, 7, "EV_TXSTART");
break;
case EV_TXCANCELED:
oledPrintf(0, 7, "EV_TXCANCELED");
break;
case EV_RXSTART:
oledPrintf(0, 7, "EV_RXSTART");
break;
case EV_JOIN_TXCOMPLETE:
oledPrintf(0, 7, "EV_JOIN_TXCOMPLETE");
break;
default:
oledPrintf(0, 7, "Unknown event %ud", ev);
break;
}
}
void do_send(osjob_t* j){
// Check if there is not a current TX/RX job running
if (LMIC.opmode & OP_TXRXPEND) {
Serial.println(F("OP_TXRXPEND, not sending"));
} else {
// Prepare upstream data transmission at the next possible time.
u1_t confirmed = 1;
LMIC_setTxData2(1, mydata, sizeof(mydata)-1, confirmed);
Serial.println(F("Packet queued"));
digitalWrite(BUILTIN_LED, HIGH);
}
// Next TX is scheduled after TX_COMPLETE event.
}
void setup() {
Serial.begin(115200);
Serial.println(F("Starting"));
u8x8.begin();
u8x8.setFont(u8x8_font_chroma48medium8_r);
oledPrintf(0, 0, "Milo's OLED 2020");
// LMIC init
os_init();
// Reset the MAC state. Session and pending data transfers will be discarded.
LMIC_reset();
LMIC_setAdrMode(false);
// Start job (sending automatically starts OTAA too)
do_send(&sendjob);
pinMode(BUILTIN_LED, OUTPUT);
digitalWrite(BUILTIN_LED, LOW);
}
void loop() {
os_runloop_once();
}