/* TTN module Wrapper to use TTN with the LMIC library Copyright (C) 2018 by Xose PĂ©rez This code requires the MCCI LoRaWAN LMIC library by IBM, Matthis Kooijman, Terry Moore, ChaeHee Won, Frank Rose https://github.com/mcci-catena/arduino-lmic This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include "configuration.h" #include "credentials.h" // ----------------------------------------------------------------------------- // Globals // ----------------------------------------------------------------------------- // LMIC GPIO configuration const lmic_pinmap lmic_pins = { .nss = NSS_GPIO, .rxtx = LMIC_UNUSED_PIN, .rst = RESET_GPIO, .dio = {DIO0_GPIO, DIO1_GPIO, DIO2_GPIO}, }; // Message counter, stored in RTC memory, survives deep sleep. static RTC_DATA_ATTR uint32_t count = 0; #ifdef USE_ABP // These callbacks are only used in over-the-air activation, so they are // left empty here (we cannot leave them out completely unless // DISABLE_JOIN is set in config.h, otherwise the linker will complain). void os_getArtEui (u1_t* buf) { } void os_getDevEui (u1_t* buf) { } void os_getDevKey (u1_t* buf) { } #endif #ifdef USE_OTAA void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8); } void os_getDevEui (u1_t* buf) { memcpy(buf, DEVEUI, 8); } void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16); } #endif std::vector _lmic_callbacks; // ----------------------------------------------------------------------------- // Private methods // ----------------------------------------------------------------------------- void _ttn_callback(uint8_t message) { for (uint8_t i=0; i<_lmic_callbacks.size(); i++) { (_lmic_callbacks[i])(message); } } void forceTxSingleChannelDr() { // Disables all channels, except for the one defined by SINGLE_CHANNEL_GATEWAY // This only affects uplinks; for downlinks the default // channels or the configuration from the OTAA Join Accept are used. #ifdef SINGLE_CHANNEL_GATEWAY for(int i=0; i<9; i++) { // For EU; for US use i<71 if(i != SINGLE_CHANNEL_GATEWAY) { LMIC_disableChannel(i); } } #endif // Set data rate (SF) and transmit power for uplink ttn_sf(LORAWAN_SF); } // DevEUI generator using devices's MAC address - from https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/lorawan.cpp void gen_lora_deveui(uint8_t *pdeveui) { 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]; } } static void printHex2(unsigned v) { v &= 0xff; if (v < 16) Serial.print('0'); Serial.print(v, HEX); } #ifdef USE_OTAA // generate DevEUI from macaddr if needed void initDevEUI() { bool needInit = true; for(int i = 0; i < sizeof(DEVEUI); i++) if(DEVEUI[i]) needInit = false; if(needInit) gen_lora_deveui(DEVEUI); Serial.print("DevEUI: "); for(int i = 0; i < sizeof(DEVEUI); i++) { if (i != 0) Serial.print("-"); printHex2(DEVEUI[i]); } Serial.println(); } #endif // LMIC library will call this method when an event is fired void onEvent(ev_t event) { switch(event) { case EV_JOINED: { #ifdef SINGLE_CHANNEL_GATEWAY forceTxSingleChannelDr(); #endif // 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. if(!LORAWAN_ADR){ LMIC_setLinkCheckMode(0); // Link check problematic if not using ADR. Must be set after join } Serial.println(F("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 5 * 60 * 1000L) { // write if we roll over (50 days) or 5 mins lastWriteMsec = now; Preferences p; if(p.begin("lora", false)) { p.putUInt("count", count); p.end(); } } } /// Blow away our prefs (i.e. to rejoin from scratch) void ttn_erase_prefs() { Preferences p; if(p.begin("lora", false)) { p.clear(); p.end(); } } void ttn_send(uint8_t * data, uint8_t data_size, uint8_t port, bool confirmed){ ttn_set_cnt(); // we are about to send using the current packet count // Check if there is not a current TX/RX job running if (LMIC.opmode & OP_TXRXPEND) { _ttn_callback(EV_PENDING); return; } // Prepare upstream data transmission at the next possible time. // Parameters are port, data, length, confirmed LMIC_setTxData2(port, data, data_size, confirmed ? 1 : 0); _ttn_callback(EV_QUEUED); count++; } void ttn_loop() { os_runloop_once(); }