/* 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" // This file should not be in any publig git repos, it contains your secret APPKEY #include "../credentials-private.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); } // 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(); } // 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(); }