├── T3_V1.6.pdf ├── img ├── image1.jpg └── Paxcounter-title.jpg ├── lib ├── arduino-lmic-1.5.0-arduino-2-tweaked │ ├── doc │ │ ├── LMiC-v1.5.pdf │ │ ├── README.txt │ │ └── release-notes.txt │ ├── src │ │ ├── lmic.h │ │ ├── hal │ │ │ ├── hal.h │ │ │ └── hal.cpp │ │ ├── lmic │ │ │ ├── hal.h │ │ │ ├── config.h │ │ │ ├── oslmic.c │ │ │ ├── oslmic.h │ │ │ ├── lmic.h │ │ │ └── lorabase.h │ │ └── aes │ │ │ ├── other.c │ │ │ └── ideetron │ │ │ └── AES-128_V10.cpp │ ├── library.properties │ ├── examples │ │ ├── raw │ │ │ └── raw.ino │ │ ├── ttn-otaa │ │ │ └── ttn-otaa.ino │ │ └── ttn-abp │ │ │ └── ttn-abp.ino │ └── README.md └── readme.txt ├── .gitignore ├── src ├── hal │ ├── lopy.h │ ├── lopy4.h │ ├── heltec.h │ ├── ttgov1.h │ ├── lolin32lite_lora.h │ ├── lolin32_lora.h │ └── ttgov2.h ├── rgb_led.h ├── macsniff.h ├── loraconf.h ├── antenna.cpp ├── globals.h ├── rgb_led.cpp ├── rokkithash.cpp ├── macsniff.cpp ├── main.h ├── lorawan.cpp ├── rcommand.cpp ├── blecsan.cpp └── configmanager.cpp ├── platformio.ini └── README.md /T3_V1.6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilyGO/ESP32-Paxcounter/HEAD/T3_V1.6.pdf -------------------------------------------------------------------------------- /img/image1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilyGO/ESP32-Paxcounter/HEAD/img/image1.jpg -------------------------------------------------------------------------------- /img/Paxcounter-title.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilyGO/ESP32-Paxcounter/HEAD/img/Paxcounter-title.jpg -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/doc/LMiC-v1.5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilyGO/ESP32-Paxcounter/HEAD/lib/arduino-lmic-1.5.0-arduino-2-tweaked/doc/LMiC-v1.5.pdf -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | extern "C"{ 3 | #endif 4 | 5 | #include "lmic/lmic.h" 6 | 7 | #ifdef __cplusplus 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/doc/README.txt: -------------------------------------------------------------------------------- 1 | DISCLAIMER: 2 | Please note that the software is provided AS IS and we cannot 3 | provide support for optimizations, adaptations, integration, 4 | ports to other platforms or device drivers! 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pioenvs 2 | .piolibdeps 3 | .vscode 4 | .travis.yml 5 | .vscode/c_cpp_properties.json 6 | .vscode/settings.json 7 | .vscode/launch.json 8 | src/loraconf.h 9 | .vscode/*.db 10 | .vscode/.browse.c_cpp.db* 11 | .clang_complete 12 | .gcc-flags.json 13 | -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/library.properties: -------------------------------------------------------------------------------- 1 | name=IBM LMIC framework 2 | version=1.5.0+arduino-2 3 | author=IBM 4 | maintainer=Matthijs Kooijman 5 | sentence=Arduino port of the LMIC (LoraWAN-in-C, formerly LoraMAC-in-C) framework provided by IBM. 6 | paragraph=Supports SX1272/SX1276 and HopeRF RFM92/RFM95 tranceivers 7 | category=Communication 8 | url=http://www.research.ibm.com/labs/zurich/ics/lrsc/lmic.html 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/hal/lopy.h: -------------------------------------------------------------------------------- 1 | // Hardware related definitions for Pycom LoPy Board (not: LoPy4) 2 | 3 | #define CFG_sx1272_radio 1 4 | #define HAS_LED NOT_A_PIN // LoPy4 has no on board LED, so we use RGB LED on LoPy4 5 | #define HAS_RGB_LED 0 // WS2812B RGB LED on GPIO0 6 | 7 | // Hardware pin definitions for Pycom LoPy board 8 | #define PIN_SPI_SS 17 9 | #define PIN_SPI_MOSI 27 10 | #define PIN_SPI_MISO 19 11 | #define PIN_SPI_SCK 5 12 | #define RST 18 13 | #define DIO0 23 // LoRa IRQ 14 | #define DIO1 23 // workaround 15 | #define DIO2 23 // workaround 16 | 17 | // select WIFI antenna (internal = onboard / external = u.fl socket) 18 | #define HAS_ANTENNA_SWITCH 16 // pin for switching wifi antenna 19 | #define WIFI_ANTENNA 0 // 0 = internal, 1 = external 20 | -------------------------------------------------------------------------------- /src/hal/lopy4.h: -------------------------------------------------------------------------------- 1 | // Hardware related definitions for Pycom LoPy Board (not: LoPy4) 2 | 3 | #define CFG_sx1276_radio 1 4 | #define HAS_LED NOT_A_PIN // LoPy4 has no on board LED, so we use RGB LED on LoPy4 5 | #define HAS_RGB_LED 0 // WS2812B RGB LED on GPIO0 6 | 7 | // Hardware pin definitions for Pycom LoPy4 board 8 | #define PIN_SPI_SS 18 9 | #define PIN_SPI_MOSI 27 10 | #define PIN_SPI_MISO 19 11 | #define PIN_SPI_SCK 5 12 | #define RST LMIC_UNUSED_PIN 13 | #define DIO0 23 // LoRa IRQ 14 | #define DIO1 23 // workaround 15 | #define DIO2 23 // workaround 16 | 17 | // select WIFI antenna (internal = onboard / external = u.fl socket) 18 | #define HAS_ANTENNA_SWITCH 21 // pin for switching wifi antenna 19 | #define WIFI_ANTENNA 0 // 0 = internal, 1 = external -------------------------------------------------------------------------------- /src/rgb_led.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | // value for HSL color 5 | // see http://www.workwithcolor.com/blue-color-hue-range-01.htm 6 | #define COLOR_RED 0 7 | #define COLOR_ORANGE 30 8 | #define COLOR_ORANGE_YELLOW 45 9 | #define COLOR_YELLOW 60 10 | #define COLOR_YELLOW_GREEN 90 11 | #define COLOR_GREEN 120 12 | #define COLOR_GREEN_CYAN 165 13 | #define COLOR_CYAN 180 14 | #define COLOR_CYAN_BLUE 210 15 | #define COLOR_BLUE 240 16 | #define COLOR_BLUE_MAGENTA 275 17 | #define COLOR_MAGENTA 300 18 | #define COLOR_PINK 350 19 | #define COLOR_WHITE 360 20 | #define COLOR_NONE 999 21 | 22 | struct RGBColor 23 | { 24 | uint8_t R; 25 | uint8_t G; 26 | uint8_t B; 27 | }; 28 | 29 | // Exported Functions 30 | void rgb_set_color(uint16_t hue); 31 | -------------------------------------------------------------------------------- /src/macsniff.h: -------------------------------------------------------------------------------- 1 | // ESP32 Functions 2 | #include 3 | 4 | #define MAC_SNIFF_WIFI 0 5 | #define MAC_SNIFF_BLE 1 6 | 7 | typedef struct { 8 | unsigned frame_ctrl:16; 9 | unsigned duration_id:16; 10 | uint8_t addr1[6]; /* receiver address */ 11 | uint8_t addr2[6]; /* sender address */ 12 | uint8_t addr3[6]; /* filtering address */ 13 | unsigned sequence_ctrl:16; 14 | uint8_t addr4[6]; /* optional */ 15 | } wifi_ieee80211_mac_hdr_t; 16 | 17 | typedef struct { 18 | wifi_ieee80211_mac_hdr_t hdr; 19 | uint8_t payload[0]; /* network data ended with 4 bytes csum (CRC32) */ 20 | } wifi_ieee80211_packet_t; 21 | 22 | uint16_t salt_reset(void); 23 | void wifi_sniffer_init(void); 24 | void wifi_sniffer_set_channel(uint8_t channel); 25 | void wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type); 26 | 27 | // function defined in rokkithash.cpp 28 | uint32_t rokkit(const char * , int ); 29 | -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/doc/release-notes.txt: -------------------------------------------------------------------------------- 1 | ============================================================================== 2 | LMIC VERSION 1.4 (17-Mar-2015) 3 | ------------------------------- 4 | 5 | - changed API: inverted port indicator flag in LMIC.txrxFlags 6 | (now TXRX_PORT, previously TXRX_NOPORT) 7 | 8 | - fixed offset OFF_CFLIST constant 9 | 10 | - changed CRC-16 algorithm for beacons to CCITT(XMODEM) polynomial 11 | 12 | - fixed radio driver (low data rate optimization for SF11+SF12 only for BW125) 13 | 14 | - fixed timer rollover handling in job queue 15 | 16 | ============================================================================== 17 | LMIC VERSION 1.5 (8-May-2015) 18 | ------------------------------ 19 | 20 | - fixed condition in convFreq() 21 | 22 | - fixed freq*100 bug and freq==0 bug for CFList 23 | 24 | - fixed TX scheduling bug 25 | 26 | - better support for GNU compiler toolchain 27 | 28 | ============================================================================== 29 | -------------------------------------------------------------------------------- /lib/readme.txt: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for the project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link to executable file. 4 | 5 | The source code of each library should be placed in separate directory, like 6 | "lib/private_lib/[here are source files]". 7 | 8 | For example, see how can be organized `Foo` and `Bar` libraries: 9 | 10 | |--lib 11 | | |--Bar 12 | | | |--docs 13 | | | |--examples 14 | | | |--src 15 | | | |- Bar.c 16 | | | |- Bar.h 17 | | |--Foo 18 | | | |- Foo.c 19 | | | |- Foo.h 20 | | |- readme.txt --> THIS FILE 21 | |- platformio.ini 22 | |--src 23 | |- main.c 24 | 25 | Then in `src/main.c` you should use: 26 | 27 | #include 28 | #include 29 | 30 | // rest H/C/CPP code 31 | 32 | PlatformIO will find your libraries automatically, configure preprocessor's 33 | include paths and build them. 34 | 35 | More information about PlatformIO Library Dependency Finder 36 | - http://docs.platformio.org/page/librarymanager/ldf.html 37 | -------------------------------------------------------------------------------- /src/loraconf.h: -------------------------------------------------------------------------------- 1 | /************************************************************ 2 | * LMIC LoRaWAN configuration 3 | * 4 | * Read the values from TTN console (or whatever applies) 5 | * 6 | ************************************************************/ 7 | 8 | #include 9 | 10 | 11 | 12 | // Set your DEVEUI here, if you have one. You can leave this untouched, 13 | // then the DEVEUI will be generated during runtime from device's MAC adress 14 | // Note: Use same format as in TTN console (cut & paste, for your convenience) 15 | // *** Take care : If Using a board with Microchip 24AA02E64 Uinique ID for deveui, ** 16 | // *** this DEVEUI will be overwriten by the one contained in the Microchip module *** 17 | static const u1_t DEVEUI[8]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 18 | 19 | // Note: Use msb format for APPEUI as in TTN console (cut & paste, for your convenience) 20 | // For TTN, APPEUI always starts with 0x70, 0xB3, 0xD5 21 | static const u1_t APPEUI[8]={ 0x70, 0xB3, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x00 }; 22 | 23 | // Note: Use msb format for APPEUI as in TTN console (cut & paste, for your convenience) 24 | static const u1_t APPKEY[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 25 | 26 | -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/hal/hal.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Matthijs Kooijman 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * This the HAL to run LMIC on top of the Arduino environment. 9 | *******************************************************************************/ 10 | #ifndef _hal_hal_h_ 11 | #define _hal_hal_h_ 12 | 13 | static const int NUM_DIO = 3; 14 | 15 | #if defined(ESP32) || defined(NRF51) 16 | #define LMIC_SPI_PINS_IN_MAPPING 17 | struct lmic_pinmap { 18 | u1_t mosi; 19 | u1_t miso; 20 | u1_t sck; 21 | u1_t nss; 22 | u1_t rxtx; 23 | u1_t rst; 24 | u1_t dio[NUM_DIO]; 25 | }; 26 | #else 27 | struct lmic_pinmap { 28 | u1_t nss; 29 | u1_t rxtx; 30 | u1_t rst; 31 | u1_t dio[NUM_DIO]; 32 | }; 33 | #endif 34 | 35 | // Use this for any unused pins. 36 | const u1_t LMIC_UNUSED_PIN = 0xff; 37 | 38 | // Declared here, to be defined an initialized by the application 39 | extern const lmic_pinmap lmic_pins; 40 | 41 | #endif // _hal_hal_h_ 42 | -------------------------------------------------------------------------------- /src/antenna.cpp: -------------------------------------------------------------------------------- 1 | /* switches wifi antenna, if board has switch to select internal and external antenna */ 2 | 3 | #ifdef HAS_ANTENNA_SWITCH 4 | 5 | #include 6 | 7 | // Local logging tag 8 | static const char *TAG = "antenna"; 9 | 10 | typedef enum { 11 | ANTENNA_INT = 0, 12 | ANTENNA_EXT 13 | } antenna_type_t; 14 | 15 | void antenna_init(void) { 16 | gpio_config_t gpioconf = {.pin_bit_mask = 1ull << HAS_ANTENNA_SWITCH, 17 | .mode = GPIO_MODE_OUTPUT, 18 | .pull_up_en = GPIO_PULLUP_DISABLE, 19 | .pull_down_en = GPIO_PULLDOWN_DISABLE, 20 | .intr_type = GPIO_INTR_DISABLE}; 21 | gpio_config(&gpioconf); 22 | } 23 | 24 | void antenna_select (const int8_t _ant) { 25 | if (HAS_ANTENNA_SWITCH < 32) { 26 | if (_ant == ANTENNA_EXT) { 27 | GPIO_REG_WRITE(GPIO_OUT_W1TS_REG, 1 << HAS_ANTENNA_SWITCH); 28 | } else { 29 | GPIO_REG_WRITE(GPIO_OUT_W1TC_REG, 1 << HAS_ANTENNA_SWITCH); 30 | } 31 | } else { 32 | if (_ant == ANTENNA_EXT) { 33 | GPIO_REG_WRITE(GPIO_OUT1_W1TS_REG, 1 << (HAS_ANTENNA_SWITCH & 31)); 34 | } else { 35 | GPIO_REG_WRITE(GPIO_OUT1_W1TC_REG, 1 << (HAS_ANTENNA_SWITCH & 31)); 36 | } 37 | } 38 | ESP_LOGI(TAG, "Wifi Antenna switched to %s", _ant ? "external" : "internal"); 39 | } 40 | 41 | #endif -------------------------------------------------------------------------------- /src/hal/heltec.h: -------------------------------------------------------------------------------- 1 | // Hardware related definitions for Heltec LoRa-32 Board 2 | 3 | #define CFG_sx1276_radio 1 4 | 5 | #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board 6 | //#define DISPLAY_FLIP 1 // uncomment this for rotated display 7 | #define HAS_LED GPIO_NUM_25 // white LED on board 8 | #define HAS_BUTTON GPIO_NUM_0 // button "PROG" on board 9 | 10 | // re-define pin definitions of pins_arduino.h 11 | #define PIN_SPI_SS 18 // ESP32 GPIO18 (Pin18) -- SX1276 NSS (Pin19) SPI Chip Select Input 12 | #define PIN_SPI_MOSI 27 // ESP32 GPIO27 (Pin27) -- SX1276 MOSI (Pin18) SPI Data Input 13 | #define PIN_SPI_MISO 19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output 14 | #define PIN_SPI_SCK 5 // ESP32 GPIO5 (Pin5) -- SX1276 SCK (Pin16) SPI Clock Input 15 | 16 | // non arduino pin definitions 17 | #define RST 14 // ESP32 GPIO14 (Pin14) -- SX1276 NRESET (Pin7) Reset Trigger Input 18 | #define DIO0 26 // ESP32 GPIO26 (Pin15) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done 19 | #define DIO1 33 // ESP32 GPIO33 (Pin13) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout 20 | #define DIO2 32 // ESP32 GPIO32 (Pin12) -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only) 21 | 22 | // Hardware pin definitions for Heltec LoRa-32 Board with OLED SSD1306 I2C Display 23 | #define OLED_RST 16 // ESP32 GPIO16 (Pin16) -- SD1306 RST 24 | #define OLED_SDA 4 // ESP32 GPIO4 (Pin4) -- SD1306 D1+D2 25 | #define OLED_SCL 15 // ESP32 GPIO15 (Pin15) -- SD1306 D0 26 | -------------------------------------------------------------------------------- /src/hal/ttgov1.h: -------------------------------------------------------------------------------- 1 | // Hardware related definitions for TTGOv1 board 2 | 3 | #define CFG_sx1276_radio 1 4 | 5 | #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board 6 | //#define DISPLAY_FLIP 1 // uncomment this for rotated display 7 | #define HAS_LED GPIO_NUM_2 // white LED on board 8 | #define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW 9 | #define HAS_BUTTON GPIO_NUM_0 // button "PRG" on board 10 | 11 | // re-define pin definitions of pins_arduino.h 12 | #define PIN_SPI_SS 18 // ESP32 GPIO18 (Pin18) -- SX1276 NSS (Pin19) SPI Chip Select Input 13 | #define PIN_SPI_MOSI 27 // ESP32 GPIO27 (Pin27) -- SX1276 MOSI (Pin18) SPI Data Input 14 | #define PIN_SPI_MISO 19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output 15 | #define PIN_SPI_SCK 5 // ESP32 GPIO5 (Pin5) -- SX1276 SCK (Pin16) SPI Clock Input 16 | 17 | // non arduino pin definitions 18 | #define RST 14 // ESP32 GPIO14 (Pin14) -- SX1276 NRESET (Pin7) Reset Trigger Input 19 | #define DIO0 26 // ESP32 GPIO26 (Pin15) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done 20 | #define DIO1 33 // ESP32 GPIO33 (Pin13) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout 21 | #define DIO2 32 // ESP32 GPIO32 (Pin12) -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only) 22 | 23 | // Hardware pin definitions for TTGOv1 Board with OLED SSD1306 I2C Display 24 | #define OLED_RST 16 // ESP32 GPIO16 (Pin16) -- SD1306 Reset 25 | #define OLED_SDA 4 // ESP32 GPIO4 (Pin4) -- SD1306 Data 26 | #define OLED_SCL 15 // ESP32 GPIO15 (Pin15) -- SD1306 Clock 27 | -------------------------------------------------------------------------------- /src/globals.h: -------------------------------------------------------------------------------- 1 | // The mother of all embedded development... 2 | #include 3 | 4 | // std::set for unified array functions 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef HAS_DISPLAY 10 | // OLED Display 11 | #include 12 | #endif 13 | 14 | // LMIC-Arduino LoRaWAN Stack 15 | #include 16 | #include 17 | 18 | // LED controls 19 | #ifdef HAS_RGB_LED 20 | #include 21 | #endif 22 | 23 | #include "rgb_led.h" 24 | #include "macsniff.h" 25 | 26 | // Struct holding devices's runtime configuration 27 | typedef struct { 28 | int8_t lorasf; // 7-12, lora spreadfactor 29 | int8_t txpower; // 2-15, lora tx power 30 | int8_t adrmode; // 0=disabled, 1=enabled 31 | int8_t screensaver; // 0=disabled, 1=enabled 32 | int8_t screenon; // 0=disabled, 1=enabled 33 | int8_t countermode; // 0=cyclic unconfirmed, 1=cumulative, 2=cyclic confirmed 34 | int16_t rssilimit; // threshold for rssilimiter, negative value! 35 | int8_t wifiscancycle; // wifi scan cycle [seconds/2] 36 | int8_t wifichancycle; // wifi channel switch cycle [seconds/100] 37 | int8_t blescantime; // BLE scan cycle duration [seconds] 38 | int8_t blescan; // 0=disabled, 1=enabled 39 | int8_t wifiant; // 0=internal, 1=external (for LoPy/LoPy4) 40 | int8_t vendorfilter; // 0=disabled, 1=enabled 41 | int8_t rgblum; // RGB Led luminosity (0..100%) 42 | char version[10]; // Firmware version 43 | } configData_t; 44 | 45 | extern configData_t cfg; 46 | extern uint8_t mydata[]; 47 | extern uint64_t uptimecounter; 48 | extern osjob_t sendjob; 49 | extern char display_lora[], display_lmic[]; 50 | extern int countermode, screensaver, adrmode, lorasf, txpower, rlim; 51 | extern bool joinstate; 52 | extern std::set wifis; 53 | extern std::set macs; 54 | 55 | #ifdef HAS_DISPLAY 56 | extern HAS_DISPLAY u8x8; 57 | #endif 58 | 59 | #ifdef BLECOUNTER 60 | extern int scanTime; 61 | extern std::set bles; 62 | #endif 63 | -------------------------------------------------------------------------------- /src/rgb_led.cpp: -------------------------------------------------------------------------------- 1 | // Basic Config 2 | #include "globals.h" 3 | 4 | #ifdef HAS_RGB_LED 5 | 6 | // RGB Led instance 7 | SmartLed rgb_led(LED_WS2812, 1, HAS_RGB_LED); 8 | 9 | float rgb_CalcColor(float p, float q, float t) 10 | { 11 | if (t < 0.0f) 12 | t += 1.0f; 13 | if (t > 1.0f) 14 | t -= 1.0f; 15 | 16 | if (t < 1.0f / 6.0f) 17 | return p + (q - p) * 6.0f * t; 18 | 19 | if (t < 0.5f) 20 | return q; 21 | 22 | if (t < 2.0f / 3.0f) 23 | return p + ((q - p) * (2.0f / 3.0f - t) * 6.0f); 24 | 25 | return p; 26 | } 27 | 28 | // ------------------------------------------------------------------------ 29 | // Hue, Saturation, Lightness color members 30 | // HslColor using H, S, L values (0.0 - 1.0) 31 | // L should be limited to between (0.0 - 0.5) 32 | // ------------------------------------------------------------------------ 33 | RGBColor rgb_hsl2rgb(float h, float s, float l) 34 | { 35 | RGBColor RGB_color; 36 | float r; 37 | float g; 38 | float b; 39 | 40 | if (s == 0.0f || l == 0.0f) 41 | { 42 | r = g = b = l; // achromatic or black 43 | } 44 | else 45 | { 46 | float q = l < 0.5f ? l * (1.0f + s) : l + s - (l * s); 47 | float p = 2.0f * l - q; 48 | r = rgb_CalcColor(p, q, h + 1.0f / 3.0f); 49 | g = rgb_CalcColor(p, q, h); 50 | b = rgb_CalcColor(p, q, h - 1.0f / 3.0f); 51 | } 52 | 53 | RGB_color.R = (uint8_t)(r * 255.0f); 54 | RGB_color.G = (uint8_t)(g * 255.0f); 55 | RGB_color.B = (uint8_t)(b * 255.0f); 56 | 57 | return RGB_color; 58 | } 59 | 60 | void rgb_set_color(uint16_t hue) { 61 | if (hue == COLOR_NONE) { 62 | // Off 63 | rgb_led[0] = Rgb(0,0,0); 64 | } else { 65 | // see http://www.workwithcolor.com/blue-color-hue-range-01.htm 66 | // H (is color from 0..360) should be between 0.0 and 1.0 67 | // S is saturation keep it to 1 68 | // L is brightness should be between 0.0 and 0.5 69 | // cfg.rgblum is between 0 and 100 (percent) 70 | RGBColor target = rgb_hsl2rgb( hue / 360.0f, 1.0f, 0.005f * cfg.rgblum); 71 | //uint32_t color = target.R<<16 | target.G<<8 | target.B; 72 | rgb_led[0] = Rgb(target.R, target.G, target.B); 73 | } 74 | // Show 75 | rgb_led.show(); 76 | } 77 | 78 | #else 79 | 80 | // No RGB LED empty functions 81 | void rgb_set_color(uint16_t hue) {} 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /src/hal/lolin32lite_lora.h: -------------------------------------------------------------------------------- 1 | // Hardware related definitions for lolin32 lite loraNode32 shield 2 | // See https://github.com/hallard/LoLin32-Lite-Lora 3 | 4 | // disable brownout detection (avoid unexpected reset on some boards) 5 | #define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature 6 | 7 | #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board 8 | //#define DISPLAY_FLIP 1 // uncomment this for rotated display 9 | #define HAS_LED 22 // ESP32 GPIO12 (pin22) On Board LED 10 | #define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW 11 | #define HAS_RGB_LED 13 // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED 12 | #define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield 13 | #define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown 14 | 15 | #define CFG_sx1276_radio 1 // RFM95 module 16 | 17 | // re-define pin definitions of pins_arduino.h 18 | #define PIN_SPI_SS 5 // ESP32 GPIO5 (Pin5) -- SX1276 NSS (Pin19) SPI Chip Select Input 19 | #define PIN_SPI_MOSI 23 // ESP32 GPIO23 (Pin23) -- SX1276 MOSI (Pin18) SPI Data Input 20 | #define PIN_SPI_MISO 19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output 21 | #define PIN_SPI_SCK 18 // ESP32 GPIO18 (Pin18 -- SX1276 SCK (Pin16) SPI Clock Input 22 | 23 | // non arduino pin definitions 24 | #define RST 25 // ESP32 GPIO25 (Pin25) -- SX1276 NRESET (Pin7) Reset Trigger Input 25 | #define DIO0 27 // ESP32 GPIO27 (Pin27) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done 26 | #define DIO1 26 // ESP32 GPIO26 (Pin26) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout 27 | #define DIO2 4 // ESP32 GPIO4 (Pin4) -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only) 28 | #define DIO5 35 // ESP32 GPIO35 (Pin35) -- SX1276 DIO5 not used by LMIC for LoRa (Timeout for FSK only) 29 | 30 | // Hardware pin definitions for LoRaNode32 Board with OLED I2C Display 31 | #define OLED_RST U8X8_PIN_NONE // Not reset pin 32 | #define OLED_SDA 14 // ESP32 GPIO14 (Pin14) -- OLED SDA 33 | #define OLED_SCL 12 // ESP32 GPIO12 (Pin12) -- OLED SCL 34 | 35 | // I2C config for Microchip 24AA02E64 DEVEUI unique address 36 | #define MCP_24AA02E64_I2C_ADDRESS 0x50 // I2C address for the 24AA02E64 37 | #define MCP_24AA02E64_MAC_ADDRESS 0xF8 // Memory adress of unique deveui 64 bits 38 | -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/hal.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | *******************************************************************************/ 11 | 12 | #ifndef _hal_hpp_ 13 | #define _hal_hpp_ 14 | 15 | #ifdef __cplusplus 16 | extern "C"{ 17 | #endif 18 | 19 | /* 20 | * initialize hardware (IO, SPI, TIMER, IRQ). 21 | */ 22 | void hal_init (void); 23 | 24 | /* 25 | * drive radio NSS pin (0=low, 1=high). 26 | */ 27 | void hal_pin_nss (u1_t val); 28 | 29 | /* 30 | * drive radio RX/TX pins (0=rx, 1=tx). 31 | */ 32 | void hal_pin_rxtx (u1_t val); 33 | 34 | /* 35 | * control radio RST pin (0=low, 1=high, 2=floating) 36 | */ 37 | void hal_pin_rst (u1_t val); 38 | 39 | /* 40 | * perform 8-bit SPI transaction with radio. 41 | * - write given byte 'outval' 42 | * - read byte and return value 43 | */ 44 | u1_t hal_spi (u1_t outval); 45 | 46 | /* 47 | * disable all CPU interrupts. 48 | * - might be invoked nested 49 | * - will be followed by matching call to hal_enableIRQs() 50 | */ 51 | void hal_disableIRQs (void); 52 | 53 | /* 54 | * enable CPU interrupts. 55 | */ 56 | void hal_enableIRQs (void); 57 | 58 | /* 59 | * put system and CPU in low-power mode, sleep until interrupt. 60 | */ 61 | void hal_sleep (void); 62 | 63 | /* 64 | * return 32-bit system time in ticks. 65 | */ 66 | u4_t hal_ticks (void); 67 | 68 | /* 69 | * busy-wait until specified timestamp (in ticks) is reached. 70 | */ 71 | void hal_waitUntil (u4_t time); 72 | 73 | /* 74 | * check and rewind timer for target time. 75 | * - return 1 if target time is close 76 | * - otherwise rewind timer for target time or full period and return 0 77 | */ 78 | u1_t hal_checkTimer (u4_t targettime); 79 | 80 | /* 81 | * perform fatal failure action. 82 | * - called by assertions 83 | * - action could be HALT or reboot 84 | */ 85 | void hal_failed (const char *file, u2_t line); 86 | 87 | #ifdef __cplusplus 88 | } // extern "C" 89 | #endif 90 | 91 | #endif // _hal_hpp_ 92 | -------------------------------------------------------------------------------- /src/hal/lolin32_lora.h: -------------------------------------------------------------------------------- 1 | // Hardware related definitions for lolin32 loraNode32 shield 2 | // See https://github.com/hallard/LoLin32-Lora 3 | 4 | // disable brownout detection (avoid unexpected reset on some boards) 5 | #define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature 6 | 7 | #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board 8 | //#define DISPLAY_FLIP 1 // uncomment this for rotated display 9 | #define HAS_LED NOT_A_PIN // Led os on same pin as Lora SS pin, to avoid problems, we don't use it 10 | #define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW 11 | // Anyway shield is on over the LoLin32 board, so we won't be able to see this LED 12 | #define HAS_RGB_LED 13 // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED 13 | #define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield 14 | #define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown 15 | 16 | #define CFG_sx1276_radio 1 // RFM95 module 17 | 18 | // re-define pin definitions of pins_arduino.h 19 | #define PIN_SPI_SS 5 // ESP32 GPIO5 (Pin5) -- SX1276 NSS (Pin19) SPI Chip Select Input 20 | #define PIN_SPI_MOSI 23 // ESP32 GPIO23 (Pin23) -- SX1276 MOSI (Pin18) SPI Data Input 21 | #define PIN_SPI_MISO 19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output 22 | #define PIN_SPI_SCK 18 // ESP32 GPIO18 (Pin18 -- SX1276 SCK (Pin16) SPI Clock Input 23 | 24 | // non arduino pin definitions 25 | #define RST 25 // ESP32 GPIO25 (Pin25) -- SX1276 NRESET (Pin7) Reset Trigger Input 26 | #define DIO0 27 // ESP32 GPIO27 (Pin27) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done 27 | #define DIO1 26 // ESP32 GPIO26 (Pin26) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout 28 | #define DIO2 4 // ESP32 GPIO4 (Pin4) -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only) 29 | #define DIO5 35 // ESP32 GPIO35 (Pin35) -- SX1276 DIO5 not used by LMIC for LoRa (Timeout for FSK only) 30 | 31 | // Hardware pin definitions for LoRaNode32 Board with OLED I2C Display 32 | #define OLED_RST U8X8_PIN_NONE // Not reset pin 33 | #define OLED_SDA 21 // ESP32 GPIO21 (Pin21) -- OLED SDA 34 | #define OLED_SCL 22 // ESP32 GPIO22 (Pin22) -- OLED SCL 35 | 36 | // I2C config for Microchip 24AA02E64 DEVEUI unique address 37 | #define MCP_24AA02E64_I2C_ADDRESS 0x50 // I2C address for the 24AA02E64 38 | #define MCP_24AA02E64_MAC_ADDRESS 0xF8 // Memory adress of unique deveui 64 bits 39 | -------------------------------------------------------------------------------- /src/hal/ttgov2.h: -------------------------------------------------------------------------------- 1 | // Hardware related definitions for TTGO V2 Board 2 | 3 | #define CFG_sx1276_radio 1 // HPD13A LoRa SoC 4 | 5 | #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C 6 | //#define DISPLAY_FLIP 1 // uncomment this for rotated display 7 | #define HAS_LED NOT_A_PIN // on-board LED is wired to SCL (used by display) therefore totally useless 8 | 9 | // disable brownout detection (needed on TTGOv2 for battery powered operation) 10 | #define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature 11 | 12 | // re-define pin definitions of pins_arduino.h 13 | #define PIN_SPI_SS 18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select Input 14 | #define PIN_SPI_MOSI 27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input 15 | #define PIN_SPI_MISO 19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output 16 | #define PIN_SPI_SCK 5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input 17 | 18 | // non arduino pin definitions 19 | #define RST LMIC_UNUSED_PIN // connected to ESP32 RST/EN 20 | #define DIO0 26 // ESP32 GPIO26 wired on PCB to HPD13A 21 | #define DIO1 33 // HPDIO1 on pcb, needs to be wired external to GPIO33 22 | #define DIO2 32 // HPDIO2 on pcb, needs to be wired external to GPIO32 (not necessary for LoRa, only FSK) 23 | 24 | // Hardware pin definitions for TTGO V2 Board with OLED SSD1306 0,96" I2C Display 25 | #define OLED_RST U8X8_PIN_NONE // connected to CPU RST/EN 26 | #define OLED_SDA 21 // ESP32 GPIO4 (Pin4) -- SD1306 D1+D2 27 | #define OLED_SCL 22 // ESP32 GPIO15 (Pin15) -- SD1306 D0 28 | 29 | 30 | /* 31 | ESP32 LoRa module (SPI) OLED display (I2C) 32 | --------- ----------------- ------------------ 33 | 5 SCK SCK 34 | 27 MOSI MOSI 35 | 19 MISO MISO 36 | 18 SS NSS 37 | 14 RST 38 | 26 DIO0 39 | 33 DIO1 (see note {1}) 40 | 32 DIO2 (see note {2}) 41 | 22 SCL SCL 42 | 21 SDA SDA 43 | 22 LED (useless, see note {3}) 44 | 45 | {1} Must be manually wired! 46 | DIO1 is wired to a separate pin but is not wired on-board to pin/GPIO33. 47 | Explicitly wire board pin labeled DIO1 to pin 33 (see TTGO V2.0 pinout). 48 | {2} Must be manually wired! 49 | DIO2 is wired to a separate pin but is not wired on-board to pin/GPIO32. 50 | Explicitly wire board pin labeled DIO2 to pin 32 (see TTGO V2.0 pinout). 51 | {3} The on-board LED is wired to SCL (used by display) therefore totally useless! 52 | */ 53 | 54 | -------------------------------------------------------------------------------- /src/rokkithash.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * RokkitHash - Arduino port for Paul Hsieh's "SuperFastHash" 3 | * 4 | * A very quick hash function, (c) Paul Hsieh 5 | * 6 | * See http://www.azillionmonkeys.com/qed/hash.html for more information 7 | * about its inner workings 8 | * 9 | * - Initial Arduino version: 2014 Alex K 10 | * - 8-bit improvements: robtillaart 11 | * - Current maintainer: SukkoPera 12 | * 13 | * See http://forum.arduino.cc/index.php?topic=226686.0 for some talk 14 | * about the various improvements. 15 | * 16 | * Permission is hereby granted, free of charge, to any person obtaining 17 | * a copy of this software and associated documentation files (the 18 | * "Software"), to deal in the Software without restriction, including 19 | * without limitation the rights to use, copy, modify, merge, publish, 20 | * distribute, sublicense, and/or sell copies of the Software, and to 21 | * permit persons to whom the Software is furnished to do so, subject to 22 | * the following conditions: 23 | * 24 | * The above copyright notice and this permission notice shall be 25 | * included in all copies or substantial portions of the Software. 26 | * 27 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 28 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 29 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 30 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 31 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 32 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 33 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 34 | * SOFTWARE. 35 | */ 36 | 37 | #include 38 | 39 | uint32_t rokkit(const char * data, int len) { 40 | uint32_t hash, tmp; 41 | int rem; 42 | 43 | if (len <= 0 || data == 0) return 0; 44 | hash = len; 45 | rem = len & 3; 46 | len >>= 2; 47 | 48 | /* Main loop */ 49 | while (len > 0) { 50 | hash += *((uint16_t*)data); 51 | tmp = (*((uint16_t*)(data+2)) << 11) ^ hash; 52 | hash = (hash << 16) ^ tmp; 53 | data += 2*2; 54 | hash += hash >> 11; 55 | len--; 56 | } 57 | 58 | /* Handle end cases */ 59 | switch (rem) { 60 | case 3: hash += *((uint16_t*)data); 61 | hash ^= hash << 16; 62 | hash ^= ((signed char)data[2]) << 18; 63 | hash += hash >> 11; 64 | break; 65 | case 2: hash += *((uint16_t*)data); 66 | hash ^= hash << 11; 67 | hash += hash >> 17; 68 | break; 69 | case 1: hash += (signed char)*data; 70 | hash ^= hash << 10; 71 | hash += hash >> 1; 72 | } 73 | 74 | /* Force "avalanching" of final 127 bits */ 75 | hash ^= hash << 3; 76 | hash += hash >> 5; 77 | hash ^= hash << 4; 78 | hash += hash >> 17; 79 | hash ^= hash << 25; 80 | hash += hash >> 6; 81 | 82 | return hash; 83 | } 84 | -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/config.h: -------------------------------------------------------------------------------- 1 | #ifndef _lmic_config_h_ 2 | #define _lmic_config_h_ 3 | 4 | // In the original LMIC code, these config values were defined on the 5 | // gcc commandline. Since Arduino does not allow easily modifying the 6 | // compiler commandline, use this file instead. 7 | 8 | #define CFG_eu868 1 9 | //#define CFG_us915 1 10 | // This is the SX1272/SX1273 radio, which is also used on the HopeRF 11 | // RFM92 boards. 12 | //#define CFG_sx1272_radio 1 13 | // This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on 14 | // the HopeRF RFM95 boards. 15 | #define CFG_sx1276_radio 1 16 | 17 | // 16 μs per tick 18 | // LMIC requires ticks to be 15.5μs - 100 μs long 19 | #define US_PER_OSTICK_EXPONENT 4 20 | #define US_PER_OSTICK (1 << US_PER_OSTICK_EXPONENT) 21 | #define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK) 22 | 23 | // Set this to 1 to enable some basic debug output (using printf) about 24 | // RF settings used during transmission and reception. Set to 2 to 25 | // enable more verbose output. Make sure that printf is actually 26 | // configured (e.g. on AVR it is not by default), otherwise using it can 27 | // cause crashing. 28 | #define LMIC_DEBUG_LEVEL 0 29 | 30 | // Enable this to allow using printf() to print to the given serial port 31 | // (or any other Print object). This can be easy for debugging. The 32 | // current implementation only works on AVR, though. 33 | //#define LMIC_PRINTF_TO Serial 34 | 35 | // Any runtime assertion failures are printed to this serial port (or 36 | // any other Print object). If this is unset, any failures just silently 37 | // halt execution. 38 | #define LMIC_FAILURE_TO Serial 39 | 40 | // Uncomment this to disable all code related to joining 41 | //#define DISABLE_JOIN 42 | // Uncomment this to disable all code related to ping 43 | //#define DISABLE_PING 44 | // Uncomment this to disable all code related to beacon tracking. 45 | // Requires ping to be disabled too 46 | //#define DISABLE_BEACONS 47 | 48 | // Uncomment these to disable the corresponding MAC commands. 49 | // Class A 50 | //#define DISABLE_MCMD_DCAP_REQ // duty cycle cap 51 | //#define DISABLE_MCMD_DN2P_SET // 2nd DN window param 52 | //#define DISABLE_MCMD_SNCH_REQ // set new channel 53 | // Class B 54 | //#define DISABLE_MCMD_PING_SET // set ping freq, automatically disabled by DISABLE_PING 55 | //#define DISABLE_MCMD_BCNI_ANS // next beacon start, automatical disabled by DISABLE_BEACON 56 | 57 | // In LoRaWAN, a gateway applies I/Q inversion on TX, and nodes do the 58 | // same on RX. This ensures that gateways can talk to nodes and vice 59 | // versa, but gateways will not hear other gateways and nodes will not 60 | // hear other nodes. By uncommenting this macro, this inversion is 61 | // disabled and this node can hear other nodes. If two nodes both have 62 | // this macro set, they can talk to each other (but they can no longer 63 | // hear gateways). This should probably only be used when debugging 64 | // and/or when talking to the radio directly (e.g. like in the "raw" 65 | // example). 66 | //#define DISABLE_INVERT_IQ_ON_RX 67 | 68 | // This allows choosing between multiple included AES implementations. 69 | // Make sure exactly one of these is uncommented. 70 | // 71 | // This selects the original AES implementation included LMIC. This 72 | // implementation is optimized for speed on 32-bit processors using 73 | // fairly big lookup tables, but it takes up big amounts of flash on the 74 | // AVR architecture. 75 | // #define USE_ORIGINAL_AES 76 | // 77 | // This selects the AES implementation written by Ideetroon for their 78 | // own LoRaWAN library. It also uses lookup tables, but smaller 79 | // byte-oriented ones, making it use a lot less flash space (but it is 80 | // also about twice as slow as the original). 81 | #define USE_IDEETRON_AES 82 | 83 | #endif // _lmic_config_h_ 84 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | 12 | ; ---> SELECT TARGET PLATFORM HERE! <--- 13 | [platformio] 14 | env_default = heltec_wifi_lora_32 15 | ;env_default = ttgov1 16 | ;env_default = ttgov2 17 | ;env_default = lopy 18 | ;env_default = lopy4 19 | ;env_default = lolin32lite_lora 20 | ;env_default = lolin32_lora 21 | ; 22 | description = Paxcounter is a proof-of-concept ESP32 device for metering passenger flows in realtime. It counts how many mobile devices are around. 23 | 24 | [common_env_data] 25 | lib_deps_display = 26 | U8g2@>=2.22.14 27 | lib_deps_rgbled = 28 | SmartLeds 29 | build_flags = 30 | ; we need build_flag for logging, otherwise we can't use ESP_LOGx in arduino framework 31 | ; ---> NOTE: For production run set DEBUG_LEVEL level to NONE! <--- 32 | ; otherwise device may crash in dense environments due to serial buffer overflow 33 | ; 34 | -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE 35 | ; -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_INFO 36 | ; -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE 37 | ; -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG 38 | ; 39 | ; override lora settings from LMiC library in lmic/config.h and use main.h instead 40 | -D_lmic_config_h_ 41 | -include "src/main.h" 42 | 43 | [env:heltec_wifi_lora_32] 44 | platform = espressif32 45 | framework = arduino 46 | board = heltec_wifi_lora_32 47 | monitor_baud = 115200 48 | upload_speed = 115200 49 | lib_deps = 50 | ${common_env_data.lib_deps_display} 51 | build_flags = 52 | ${common_env_data.build_flags} 53 | -Dheltec_wifi_lora_32 54 | -include "src/hal/heltec.h" 55 | 56 | [env:ttgov1] 57 | platform = espressif32 58 | framework = arduino 59 | board = esp32dev 60 | monitor_baud = 115200 61 | upload_speed = 115200 62 | lib_deps = 63 | ${common_env_data.lib_deps_display} 64 | build_flags = 65 | ${common_env_data.build_flags} 66 | -Dttgov1 67 | -include "src/hal/ttgov1.h" 68 | 69 | [env:ttgov2] 70 | platform = espressif32 71 | framework = arduino 72 | board = esp32dev 73 | monitor_baud = 115200 74 | upload_speed = 921600 75 | lib_deps = 76 | ${common_env_data.lib_deps_display} 77 | build_flags = 78 | ${common_env_data.build_flags} 79 | -Dttgov2 80 | -include "src/hal/ttgov2.h" 81 | 82 | [env:lopy] 83 | platform = espressif32 84 | framework = arduino 85 | board = esp32dev 86 | monitor_baud = 115200 87 | upload_speed = 921600 88 | lib_deps = 89 | ${common_env_data.lib_deps_rgbled} 90 | build_flags = 91 | ${common_env_data.build_flags} 92 | -Dlopy 93 | -include "src/hal/lopy.h" 94 | 95 | [env:lopy4] 96 | platform = espressif32 97 | framework = arduino 98 | board = esp32dev 99 | monitor_baud = 115200 100 | upload_speed = 921600 101 | lib_deps = 102 | ${common_env_data.lib_deps_rgbled} 103 | build_flags = 104 | ${common_env_data.build_flags} 105 | -Dlopy4 106 | -include "src/hal/lopy4.h" 107 | 108 | [env:lolin32lite_lora] 109 | platform = espressif32 110 | framework = arduino 111 | board = lolin32 112 | monitor_baud = 115200 113 | upload_speed = 256000 114 | lib_deps = 115 | ${common_env_data.lib_deps_rgbled} 116 | build_flags = 117 | ${common_env_data.build_flags} 118 | -Dlolin32lite_lora 119 | -include "src/hal/lolin32lite_lora.h" 120 | 121 | [env:lolin32_lora] 122 | platform = espressif32 123 | framework = arduino 124 | board = lolin32 125 | monitor_baud = 115200 126 | upload_speed = 921600 127 | lib_deps = 128 | ${common_env_data.lib_deps_rgbled} 129 | build_flags = 130 | ${common_env_data.build_flags} 131 | -Dlolin32_lora 132 | -include "src/hal/lolin32_lora.h" -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/oslmic.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | *******************************************************************************/ 11 | 12 | #include "lmic.h" 13 | #include 14 | 15 | // RUNTIME STATE 16 | static struct { 17 | osjob_t* scheduledjobs; 18 | osjob_t* runnablejobs; 19 | } OS; 20 | 21 | void os_init () { 22 | memset(&OS, 0x00, sizeof(OS)); 23 | hal_init(); 24 | radio_init(); 25 | LMIC_init(); 26 | } 27 | 28 | ostime_t os_getTime () { 29 | return hal_ticks(); 30 | } 31 | 32 | static u1_t unlinkjob (osjob_t** pnext, osjob_t* job) { 33 | for( ; *pnext; pnext = &((*pnext)->next)) { 34 | if(*pnext == job) { // unlink 35 | *pnext = job->next; 36 | return 1; 37 | } 38 | } 39 | return 0; 40 | } 41 | 42 | // clear scheduled job 43 | void os_clearCallback (osjob_t* job) { 44 | hal_disableIRQs(); 45 | u1_t res = unlinkjob(&OS.scheduledjobs, job) || unlinkjob(&OS.runnablejobs, job); 46 | hal_enableIRQs(); 47 | #if LMIC_DEBUG_LEVEL > 1 48 | if (res) 49 | lmic_printf("%lu: Cleared job %p\n", os_getTime(), job); 50 | #endif 51 | } 52 | 53 | // schedule immediately runnable job 54 | void os_setCallback (osjob_t* job, osjobcb_t cb) { 55 | osjob_t** pnext; 56 | hal_disableIRQs(); 57 | // remove if job was already queued 58 | os_clearCallback(job); 59 | // fill-in job 60 | job->func = cb; 61 | job->next = NULL; 62 | // add to end of run queue 63 | for(pnext=&OS.runnablejobs; *pnext; pnext=&((*pnext)->next)); 64 | *pnext = job; 65 | hal_enableIRQs(); 66 | #if LMIC_DEBUG_LEVEL > 1 67 | lmic_printf("%lu: Scheduled job %p, cb %p ASAP\n", os_getTime(), job, cb); 68 | #endif 69 | } 70 | 71 | // schedule timed job 72 | void os_setTimedCallback (osjob_t* job, ostime_t time, osjobcb_t cb) { 73 | osjob_t** pnext; 74 | hal_disableIRQs(); 75 | // remove if job was already queued 76 | os_clearCallback(job); 77 | // fill-in job 78 | job->deadline = time; 79 | job->func = cb; 80 | job->next = NULL; 81 | // insert into schedule 82 | for(pnext=&OS.scheduledjobs; *pnext; pnext=&((*pnext)->next)) { 83 | if((*pnext)->deadline - time > 0) { // (cmp diff, not abs!) 84 | // enqueue before next element and stop 85 | job->next = *pnext; 86 | break; 87 | } 88 | } 89 | *pnext = job; 90 | hal_enableIRQs(); 91 | #if LMIC_DEBUG_LEVEL > 1 92 | lmic_printf("%lu: Scheduled job %p, cb %p at %lu\n", os_getTime(), job, cb, time); 93 | #endif 94 | } 95 | 96 | // execute jobs from timer and from run queue 97 | void os_runloop () { 98 | while(1) { 99 | os_runloop_once(); 100 | } 101 | } 102 | 103 | void os_runloop_once() { 104 | #if LMIC_DEBUG_LEVEL > 1 105 | bool has_deadline = false; 106 | #endif 107 | osjob_t* j = NULL; 108 | hal_disableIRQs(); 109 | // check for runnable jobs 110 | if(OS.runnablejobs) { 111 | j = OS.runnablejobs; 112 | OS.runnablejobs = j->next; 113 | } else if(OS.scheduledjobs && hal_checkTimer(OS.scheduledjobs->deadline)) { // check for expired timed jobs 114 | j = OS.scheduledjobs; 115 | OS.scheduledjobs = j->next; 116 | #if LMIC_DEBUG_LEVEL > 1 117 | has_deadline = true; 118 | #endif 119 | } else { // nothing pending 120 | hal_sleep(); // wake by irq (timer already restarted) 121 | } 122 | hal_enableIRQs(); 123 | if(j) { // run job callback 124 | #if LMIC_DEBUG_LEVEL > 1 125 | lmic_printf("%lu: Running job %p, cb %p, deadline %lu\n", os_getTime(), j, j->func, has_deadline ? j->deadline : 0); 126 | #endif 127 | j->func(j); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/macsniff.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Basic Config 3 | #include "globals.h" 4 | 5 | #ifdef VENDORFILTER 6 | #include 7 | #include 8 | #include "vendor_array.h" 9 | #endif 10 | 11 | // Local logging tag 12 | static const char *TAG = "macsniff"; 13 | 14 | static wifi_country_t wifi_country = {.cc=WIFI_MY_COUNTRY, .schan=WIFI_CHANNEL_MIN, .nchan=WIFI_CHANNEL_MAX, .policy=WIFI_COUNTRY_POLICY_MANUAL}; 15 | 16 | uint16_t salt; 17 | 18 | uint16_t salt_reset(void) { 19 | salt = random(65536); // get new 16bit random for salting hashes 20 | return salt; 21 | } 22 | 23 | bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) { 24 | 25 | char buff[16]; // temporary buffer for printf 26 | bool added = false; 27 | uint32_t addr2int; 28 | uint32_t vendor2int; 29 | uint16_t hashedmac; 30 | 31 | // only last 3 MAC Address bytes are used for MAC Address Anonymization 32 | // but since it's uint32 we take 4 bytes to avoid 1st value to be 0 33 | addr2int = ( (uint32_t)paddr[2] ) | ( (uint32_t)paddr[3] << 8 ) | ( (uint32_t)paddr[4] << 16 ) | ( (uint32_t)paddr[5] << 24 ); 34 | 35 | #ifdef VENDORFILTER 36 | vendor2int = ( (uint32_t)paddr[2] ) | ( (uint32_t)paddr[1] << 8 ) | ( (uint32_t)paddr[0] << 16 ); 37 | // use OUI vendor filter list only on Wifi, not on BLE 38 | if ( (sniff_type==MAC_SNIFF_BLE) || std::find(vendors.begin(), vendors.end(), vendor2int) != vendors.end() ) { 39 | #endif 40 | 41 | // salt and hash MAC, and if new unique one, store identifier in container and increment counter on display 42 | // https://en.wikipedia.org/wiki/MAC_Address_Anonymization 43 | 44 | addr2int += (uint32_t) salt; // add 16-bit salt to pseudo MAC 45 | snprintf(buff, sizeof(buff), "%08X", addr2int); // convert unsigned 32-bit salted MAC to 8 digit hex string 46 | hashedmac = rokkit(&buff[3], 5); // hash MAC last string value, use 5 chars to fit hash in uint16_t container 47 | auto newmac = macs.insert(hashedmac); // add hashed MAC to total container if new unique 48 | added = newmac.second ? true:false; // true if hashed MAC is unique in container 49 | 50 | // Insert only if it was not found on global count 51 | if (added) { 52 | 53 | if (sniff_type == MAC_SNIFF_WIFI ) { 54 | rgb_set_color(COLOR_GREEN); 55 | wifis.insert(hashedmac); // add hashed MAC to wifi container 56 | } 57 | #ifdef BLECOUNTER 58 | else if (sniff_type == MAC_SNIFF_BLE ) { 59 | rgb_set_color(COLOR_MAGENTA); 60 | bles.insert(hashedmac); // add hashed MAC to BLE container 61 | } 62 | #endif 63 | 64 | // Not sure user will have time to see the LED 65 | // TBD do light off further in the code 66 | rgb_set_color(COLOR_NONE); 67 | } 68 | 69 | ESP_LOGI(TAG, "%s RSSI %ddBi -> MAC %s -> Hash %04X -> WiFi:%d BLTH:%d %s", 70 | sniff_type==MAC_SNIFF_WIFI ? "WiFi":"BLTH", 71 | rssi, buff, hashedmac, 72 | (int) wifis.size(), 73 | #ifdef BLECOUNTER 74 | (int) bles.size(), 75 | #else 76 | 0, 77 | #endif 78 | added ? "new" : "known"); 79 | 80 | #ifdef VENDORFILTER 81 | } else { 82 | // Very noisy 83 | // ESP_LOGD(TAG, "Filtered MAC %02X:%02X:%02X:%02X:%02X:%02X", paddr[0],paddr[1],paddr[2],paddr[3],paddr[5],paddr[5]); 84 | } 85 | #endif 86 | 87 | // True if MAC WiFi/BLE was new 88 | return added; // function returns bool if a new and unique Wifi or BLE mac was counted (true) or not (false) 89 | } 90 | 91 | void wifi_sniffer_init(void) { 92 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 93 | cfg.nvs_enable = 0; // we don't need any wifi settings from NVRAM 94 | wifi_promiscuous_filter_t filter = {.filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT}; // we need only MGMT frames 95 | ESP_ERROR_CHECK(esp_wifi_init(&cfg)); // configure Wifi with cfg 96 | ESP_ERROR_CHECK(esp_wifi_set_country(&wifi_country)); // set locales for RF and channels 97 | ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); // we don't need NVRAM 98 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL)); 99 | ESP_ERROR_CHECK(esp_wifi_set_promiscuous_filter(&filter)); // set MAC frame filter 100 | ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler)); 101 | ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); // now switch on monitor mode 102 | } 103 | 104 | void wifi_sniffer_set_channel(uint8_t channel) { 105 | esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); 106 | } 107 | 108 | void wifi_sniffer_packet_handler(void* buff, wifi_promiscuous_pkt_type_t type) { 109 | const wifi_promiscuous_pkt_t *ppkt = (wifi_promiscuous_pkt_t *)buff; 110 | const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)ppkt->payload; 111 | const wifi_ieee80211_mac_hdr_t *hdr = &ipkt->hdr; 112 | 113 | if (( cfg.rssilimit == 0 ) || (ppkt->rx_ctrl.rssi > cfg.rssilimit )) { // rssi is negative value 114 | uint8_t *p = (uint8_t *) hdr->addr2; 115 | mac_add(p, ppkt->rx_ctrl.rssi, MAC_SNIFF_WIFI) ; 116 | } else { 117 | ESP_LOGI(TAG, "WiFi RSSI %d -> ignoring (limit: %d)", ppkt->rx_ctrl.rssi, cfg.rssilimit); 118 | } 119 | } 120 | 121 | -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/aes/other.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2016 Matthijs Kooijman 3 | * 4 | * LICENSE 5 | * 6 | * Permission is hereby granted, free of charge, to anyone 7 | * obtaining a copy of this document and accompanying files, 8 | * to do whatever they want with them without any restriction, 9 | * including, but not limited to, copying, modification and 10 | * redistribution. 11 | * 12 | * NO WARRANTY OF ANY KIND IS PROVIDED. 13 | *******************************************************************************/ 14 | 15 | /* 16 | * The original LMIC AES implementation integrates raw AES encryption 17 | * with CMAC and AES-CTR in a single piece of code. Most other AES 18 | * implementations (only) offer raw single block AES encryption, so this 19 | * file contains an implementation of CMAC and AES-CTR, and offers the 20 | * same API through the os_aes() function as the original AES 21 | * implementation. This file assumes that there is an encryption 22 | * function available with this signature: 23 | * 24 | * extern "C" void lmic_aes_encrypt(u1_t *data, u1_t *key); 25 | * 26 | * That takes a single 16-byte buffer and encrypts it wit the given 27 | * 16-byte key. 28 | */ 29 | 30 | #include "../lmic/oslmic.h" 31 | 32 | #if !defined(USE_ORIGINAL_AES) 33 | 34 | // This should be defined elsewhere 35 | void lmic_aes_encrypt(u1_t *data, u1_t *key); 36 | 37 | // global area for passing parameters (aux, key) 38 | u4_t AESAUX[16/sizeof(u4_t)]; 39 | u4_t AESKEY[16/sizeof(u4_t)]; 40 | 41 | // Shift the given buffer left one bit 42 | static void shift_left(xref2u1_t buf, u1_t len) { 43 | while (len--) { 44 | u1_t next = len ? buf[1] : 0; 45 | 46 | u1_t val = (*buf << 1); 47 | if (next & 0x80) 48 | val |= 1; 49 | *buf++ = val; 50 | } 51 | } 52 | 53 | // Apply RFC4493 CMAC, using AESKEY as the key. If prepend_aux is true, 54 | // AESAUX is prepended to the message. AESAUX is used as working memory 55 | // in any case. The CMAC result is returned in AESAUX as well. 56 | static void os_aes_cmac(xref2u1_t buf, u2_t len, u1_t prepend_aux) { 57 | if (prepend_aux) 58 | lmic_aes_encrypt(AESaux, AESkey); 59 | else 60 | memset (AESaux, 0, 16); 61 | 62 | while (len > 0) { 63 | u1_t need_padding = 0; 64 | for (u1_t i = 0; i < 16; ++i, ++buf, --len) { 65 | if (len == 0) { 66 | // The message is padded with 0x80 and then zeroes. 67 | // Since zeroes are no-op for xor, we can just skip them 68 | // and leave AESAUX unchanged for them. 69 | AESaux[i] ^= 0x80; 70 | need_padding = 1; 71 | break; 72 | } 73 | AESaux[i] ^= *buf; 74 | } 75 | 76 | if (len == 0) { 77 | // Final block, xor with K1 or K2. K1 and K2 are calculated 78 | // by encrypting the all-zeroes block and then applying some 79 | // shifts and xor on that. 80 | u1_t final_key[16]; 81 | memset(final_key, 0, sizeof(final_key)); 82 | lmic_aes_encrypt(final_key, AESkey); 83 | 84 | // Calculate K1 85 | u1_t msb = final_key[0] & 0x80; 86 | shift_left(final_key, sizeof(final_key)); 87 | if (msb) 88 | final_key[sizeof(final_key)-1] ^= 0x87; 89 | 90 | // If the final block was not complete, calculate K2 from K1 91 | if (need_padding) { 92 | msb = final_key[0] & 0x80; 93 | shift_left(final_key, sizeof(final_key)); 94 | if (msb) 95 | final_key[sizeof(final_key)-1] ^= 0x87; 96 | } 97 | 98 | // Xor with K1 or K2 99 | for (u1_t i = 0; i < sizeof(final_key); ++i) 100 | AESaux[i] ^= final_key[i]; 101 | } 102 | 103 | lmic_aes_encrypt(AESaux, AESkey); 104 | } 105 | } 106 | 107 | // Run AES-CTR using the key in AESKEY and using AESAUX as the 108 | // counter block. The last byte of the counter block will be incremented 109 | // for every block. The given buffer will be encrypted in place. 110 | static void os_aes_ctr (xref2u1_t buf, u2_t len) { 111 | u1_t ctr[16]; 112 | while (len) { 113 | // Encrypt the counter block with the selected key 114 | memcpy(ctr, AESaux, sizeof(ctr)); 115 | lmic_aes_encrypt(ctr, AESkey); 116 | 117 | // Xor the payload with the resulting ciphertext 118 | for (u1_t i = 0; i < 16 && len > 0; i++, len--, buf++) 119 | *buf ^= ctr[i]; 120 | 121 | // Increment the block index byte 122 | AESaux[15]++; 123 | } 124 | } 125 | 126 | u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) { 127 | switch (mode & ~AES_MICNOAUX) { 128 | case AES_MIC: 129 | os_aes_cmac(buf, len, /* prepend_aux */ !(mode & AES_MICNOAUX)); 130 | return os_rmsbf4(AESaux); 131 | 132 | case AES_ENC: 133 | // TODO: Check / handle when len is not a multiple of 16 134 | for (u1_t i = 0; i < len; i += 16) 135 | lmic_aes_encrypt(buf+i, AESkey); 136 | break; 137 | 138 | case AES_CTR: 139 | os_aes_ctr(buf, len); 140 | break; 141 | } 142 | return 0; 143 | } 144 | 145 | #endif // !defined(USE_ORIGINAL_AES) 146 | -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/examples/raw/raw.ino: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Matthijs Kooijman 3 | * 4 | * Permission is hereby granted, free of charge, to anyone 5 | * obtaining a copy of this document and accompanying files, 6 | * to do whatever they want with them without any restriction, 7 | * including, but not limited to, copying, modification and redistribution. 8 | * NO WARRANTY OF ANY KIND IS PROVIDED. 9 | * 10 | * This example transmits data on hardcoded channel and receives data 11 | * when not transmitting. Running this sketch on two nodes should allow 12 | * them to communicate. 13 | *******************************************************************************/ 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #if !defined(DISABLE_INVERT_IQ_ON_RX) 20 | #error This example requires DISABLE_INVERT_IQ_ON_RX to be set. Update \ 21 | config.h in the lmic library to set it. 22 | #endif 23 | 24 | // How often to send a packet. Note that this sketch bypasses the normal 25 | // LMIC duty cycle limiting, so when you change anything in this sketch 26 | // (payload length, frequency, spreading factor), be sure to check if 27 | // this interval should not also be increased. 28 | // See this spreadsheet for an easy airtime and duty cycle calculator: 29 | // https://docs.google.com/spreadsheets/d/1voGAtQAjC1qBmaVuP1ApNKs1ekgUjavHuVQIXyYSvNc 30 | #define TX_INTERVAL 2000 31 | 32 | // Pin mapping 33 | const lmic_pinmap lmic_pins = { 34 | .nss = 6, 35 | .rxtx = LMIC_UNUSED_PIN, 36 | .rst = 5, 37 | .dio = {2, 3, 4}, 38 | }; 39 | 40 | 41 | // These callbacks are only used in over-the-air activation, so they are 42 | // left empty here (we cannot leave them out completely unless 43 | // DISABLE_JOIN is set in config.h, otherwise the linker will complain). 44 | void os_getArtEui (u1_t* buf) { } 45 | void os_getDevEui (u1_t* buf) { } 46 | void os_getDevKey (u1_t* buf) { } 47 | 48 | void onEvent (ev_t ev) { 49 | } 50 | 51 | osjob_t txjob; 52 | osjob_t timeoutjob; 53 | static void tx_func (osjob_t* job); 54 | 55 | // Transmit the given string and call the given function afterwards 56 | void tx(const char *str, osjobcb_t func) { 57 | os_radio(RADIO_RST); // Stop RX first 58 | delay(1); // Wait a bit, without this os_radio below asserts, apparently because the state hasn't changed yet 59 | LMIC.dataLen = 0; 60 | while (*str) 61 | LMIC.frame[LMIC.dataLen++] = *str++; 62 | LMIC.osjob.func = func; 63 | os_radio(RADIO_TX); 64 | Serial.println("TX"); 65 | } 66 | 67 | // Enable rx mode and call func when a packet is received 68 | void rx(osjobcb_t func) { 69 | LMIC.osjob.func = func; 70 | LMIC.rxtime = os_getTime(); // RX _now_ 71 | // Enable "continuous" RX (e.g. without a timeout, still stops after 72 | // receiving a packet) 73 | os_radio(RADIO_RXON); 74 | Serial.println("RX"); 75 | } 76 | 77 | static void rxtimeout_func(osjob_t *job) { 78 | digitalWrite(LED_BUILTIN, LOW); // off 79 | } 80 | 81 | static void rx_func (osjob_t* job) { 82 | // Blink once to confirm reception and then keep the led on 83 | digitalWrite(LED_BUILTIN, LOW); // off 84 | delay(10); 85 | digitalWrite(LED_BUILTIN, HIGH); // on 86 | 87 | // Timeout RX (i.e. update led status) after 3 periods without RX 88 | os_setTimedCallback(&timeoutjob, os_getTime() + ms2osticks(3*TX_INTERVAL), rxtimeout_func); 89 | 90 | // Reschedule TX so that it should not collide with the other side's 91 | // next TX 92 | os_setTimedCallback(&txjob, os_getTime() + ms2osticks(TX_INTERVAL/2), tx_func); 93 | 94 | Serial.print("Got "); 95 | Serial.print(LMIC.dataLen); 96 | Serial.println(" bytes"); 97 | Serial.write(LMIC.frame, LMIC.dataLen); 98 | Serial.println(); 99 | 100 | // Restart RX 101 | rx(rx_func); 102 | } 103 | 104 | static void txdone_func (osjob_t* job) { 105 | rx(rx_func); 106 | } 107 | 108 | // log text to USART and toggle LED 109 | static void tx_func (osjob_t* job) { 110 | // say hello 111 | tx("Hello, world!", txdone_func); 112 | // reschedule job every TX_INTERVAL (plus a bit of random to prevent 113 | // systematic collisions), unless packets are received, then rx_func 114 | // will reschedule at half this time. 115 | os_setTimedCallback(job, os_getTime() + ms2osticks(TX_INTERVAL + random(500)), tx_func); 116 | } 117 | 118 | // application entry point 119 | void setup() { 120 | Serial.begin(115200); 121 | Serial.println("Starting"); 122 | #ifdef VCC_ENABLE 123 | // For Pinoccio Scout boards 124 | pinMode(VCC_ENABLE, OUTPUT); 125 | digitalWrite(VCC_ENABLE, HIGH); 126 | delay(1000); 127 | #endif 128 | 129 | pinMode(LED_BUILTIN, OUTPUT); 130 | 131 | // initialize runtime env 132 | os_init(); 133 | 134 | // Set up these settings once, and use them for both TX and RX 135 | 136 | #if defined(CFG_eu868) 137 | // Use a frequency in the g3 which allows 10% duty cycling. 138 | LMIC.freq = 869525000; 139 | #elif defined(CFG_us915) 140 | LMIC.freq = 902300000; 141 | #endif 142 | 143 | // Maximum TX power 144 | LMIC.txpow = 27; 145 | // Use a medium spread factor. This can be increased up to SF12 for 146 | // better range, but then the interval should be (significantly) 147 | // lowered to comply with duty cycle limits as well. 148 | LMIC.datarate = DR_SF9; 149 | // This sets CR 4/5, BW125 (except for DR_SF7B, which uses BW250) 150 | LMIC.rps = updr2rps(LMIC.datarate); 151 | 152 | Serial.println("Started"); 153 | Serial.flush(); 154 | 155 | // setup initial job 156 | os_setCallback(&txjob, tx_func); 157 | } 158 | 159 | void loop() { 160 | // execute scheduled jobs and events 161 | os_runloop_once(); 162 | } 163 | -------------------------------------------------------------------------------- /src/main.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // program version - note: increment version after modifications to configData_t struct!! 4 | #define PROGVERSION "1.3.01" // use max 10 chars here! 5 | #define PROGNAME "PAXCNT" 6 | 7 | // Verbose enables serial output 8 | #define VERBOSE 1 // comment out to silence the device, for mute use build option 9 | 10 | // set this to include BLE counting and vendor filter functions 11 | #define VENDORFILTER 1 // comment out if you want to count things, not people 12 | #define BLECOUNTER 1 // comment out if you don't want BLE count 13 | 14 | // BLE scan parameters 15 | #define BLESCANTIME 11 // [seconds] scan duration, see note below 16 | #define BLESCANWINDOW 10 // [milliseconds] scan window, see below, 3 .. 10240, default 10 17 | #define BLESCANINTERVAL 10 // [milliseconds] how long to wait between scans, 3 .. 10240, default 10 18 | 19 | /* Note: guide for setting bluetooth parameters 20 | * 21 | * |< Scan Window > |< Scan Window > |< Scan Window > | 22 | * |< Scan Interval >|< Scan Interval >|< Scan Interval >| 23 | * |< Scan duration >| 24 | * 25 | * Scan duration sets how long scanning should be going on, interrupting a wifi scan cycle. 26 | * Scan window sets how much of the interval should be occupied by scanning. 27 | * Scan interval is how long scanning should be done on each channel. BLE uses 3 channels for advertising. 28 | * -> Adjust these values with power consumption in mind if power is limited. 29 | */ 30 | 31 | // WiFi scan parameters 32 | #define WIFI_CHANNEL_MIN 1 // start channel number where scan begings 33 | #define WIFI_CHANNEL_MAX 13 // total channel number to scan 34 | #define WIFI_MY_COUNTRY "EU" // select locale for Wifi RF settings 35 | #define WIFI_CHANNEL_SWITCH_INTERVAL 50 // [seconds/100] -> 0,5 sec. 36 | 37 | // LoRa payload send cycle 38 | #define SEND_SECS 120 // [seconds/2] -> 240 sec. 39 | //#define SEND_SECS 30 // [seconds/2] -> 60 sec. 40 | 41 | // Default LoRa Spreadfactor 42 | #define LORASFDEFAULT 9 // 7 ... 12 SF, according to LoRaWAN specs 43 | #define MAXLORARETRY 500 // maximum count of TX retries if LoRa busy 44 | #define RCMDPORT 2 // LoRaWAN Port on which device listenes for remote commands 45 | 46 | // Default RGB LED luminosity (in %) 47 | #define RGBLUMINOSITY 30 // 30% 48 | 49 | // OLED Display refresh cycle (in Milliseconds) 50 | #define DISPLAYFPS 5 // [fps] -> 5 Frames per second ps = 200ms refreseh cycle 51 | 52 | // LMIC settings 53 | // define hardware independent LMIC settings here, settings of standard library in /lmic/config.h will be ignored 54 | // define hardware specifics settings in platformio.ini as build_flag for hardware environment 55 | 56 | // Select frequency band here according to national regulations 57 | #define CFG_eu868 1 58 | //#define CFG_us915 1 59 | 60 | // This is the SX1272/SX1273 radio, which is also used on the HopeRF 61 | // RFM92 boards. 62 | //#define CFG_sx1272_radio 1 63 | // This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on 64 | // the HopeRF RFM95 boards. 65 | //#define CFG_sx1276_radio 1 66 | 67 | // 16 μs per tick 68 | // LMIC requires ticks to be 15.5μs - 100 μs long 69 | #define US_PER_OSTICK_EXPONENT 4 70 | #define US_PER_OSTICK (1 << US_PER_OSTICK_EXPONENT) 71 | #define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK) 72 | 73 | // Set this to 1 to enable some basic debug output (using printf) about 74 | // RF settings used during transmission and reception. Set to 2 to 75 | // enable more verbose output. Make sure that printf is actually 76 | // configured (e.g. on AVR it is not by default), otherwise using it can 77 | // cause crashing. 78 | //#define LMIC_DEBUG_LEVEL 1 79 | 80 | // Enable this to allow using printf() to print to the given serial port 81 | // (or any other Print object). This can be easy for debugging. The 82 | // current implementation only works on AVR, though. 83 | //#define LMIC_PRINTF_TO Serial 84 | 85 | // Any runtime assertion failures are printed to this serial port (or 86 | // any other Print object). If this is unset, any failures just silently 87 | // halt execution. 88 | #define LMIC_FAILURE_TO Serial 89 | 90 | // Uncomment this to disable all code related to joining 91 | //#define DISABLE_JOIN 92 | // Uncomment this to disable all code related to ping 93 | #define DISABLE_PING 94 | // Uncomment this to disable all code related to beacon tracking. 95 | // Requires ping to be disabled too 96 | #define DISABLE_BEACONS 97 | 98 | // Uncomment these to disable the corresponding MAC commands. 99 | // Class A 100 | //#define DISABLE_MCMD_DCAP_REQ // duty cycle cap 101 | //#define DISABLE_MCMD_DN2P_SET // 2nd DN window param 102 | //#define DISABLE_MCMD_SNCH_REQ // set new channel 103 | // Class B 104 | //#define DISABLE_MCMD_PING_SET // set ping freq, automatically disabled by DISABLE_PING 105 | //#define DISABLE_MCMD_BCNI_ANS // next beacon start, automatical disabled by DISABLE_BEACON 106 | 107 | // In LoRaWAN, a gateway applies I/Q inversion on TX, and nodes do the 108 | // same on RX. This ensures that gateways can talk to nodes and vice 109 | // versa, but gateways will not hear other gateways and nodes will not 110 | // hear other nodes. By uncommenting this macro, this inversion is 111 | // disabled and this node can hear other nodes. If two nodes both have 112 | // this macro set, they can talk to each other (but they can no longer 113 | // hear gateways). This should probably only be used when debugging 114 | // and/or when talking to the radio directly (e.g. like in the "raw" 115 | // example). 116 | //#define DISABLE_INVERT_IQ_ON_RX 117 | 118 | // This allows choosing between multiple included AES implementations. 119 | // Make sure exactly one of these is uncommented. 120 | // 121 | // This selects the original AES implementation included LMIC. This 122 | // implementation is optimized for speed on 32-bit processors using 123 | // fairly big lookup tables, but it takes up big amounts of flash on the 124 | // AVR architecture. 125 | #define USE_ORIGINAL_AES 126 | // 127 | // This selects the AES implementation written by Ideetroon for their 128 | // own LoRaWAN library. It also uses lookup tables, but smaller 129 | // byte-oriented ones, making it use a lot less flash space (but it is 130 | // also about twice as slow as the original). 131 | // #define USE_IDEETRON_AES 132 | -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/examples/ttn-otaa/ttn-otaa.ino: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman 3 | * 4 | * Permission is hereby granted, free of charge, to anyone 5 | * obtaining a copy of this document and accompanying files, 6 | * to do whatever they want with them without any restriction, 7 | * including, but not limited to, copying, modification and redistribution. 8 | * NO WARRANTY OF ANY KIND IS PROVIDED. 9 | * 10 | * This example sends a valid LoRaWAN packet with payload "Hello, 11 | * world!", using frequency and encryption settings matching those of 12 | * the The Things Network. 13 | * 14 | * This uses OTAA (Over-the-air activation), where where a DevEUI and 15 | * application key is configured, which are used in an over-the-air 16 | * activation procedure where a DevAddr and session keys are 17 | * assigned/generated for use with all further communication. 18 | * 19 | * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in 20 | * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably 21 | * violated by this sketch when left running for longer)! 22 | 23 | * To use this sketch, first register your application and device with 24 | * the things network, to set or generate an AppEUI, DevEUI and AppKey. 25 | * Multiple devices can use the same AppEUI, but each device has its own 26 | * DevEUI and AppKey. 27 | * 28 | * Do not forget to define the radio type correctly in config.h. 29 | * 30 | *******************************************************************************/ 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | // This EUI must be in little-endian format, so least-significant-byte 37 | // first. When copying an EUI from ttnctl output, this means to reverse 38 | // the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3, 39 | // 0x70. 40 | static const u1_t PROGMEM APPEUI[8]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 41 | void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);} 42 | 43 | // This should also be in little endian format, see above. 44 | static const u1_t PROGMEM DEVEUI[8]={ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 45 | void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);} 46 | 47 | // This key should be in big endian format (or, since it is not really a 48 | // number but a block of memory, endianness does not really apply). In 49 | // practice, a key taken from ttnctl can be copied as-is. 50 | // The key shown here is the semtech default key. 51 | static const u1_t PROGMEM APPKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }; 52 | void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);} 53 | 54 | static uint8_t mydata[] = "Hello, world!"; 55 | static osjob_t sendjob; 56 | 57 | // Schedule TX every this many seconds (might become longer due to duty 58 | // cycle limitations). 59 | const unsigned TX_INTERVAL = 60; 60 | 61 | // Pin mapping 62 | const lmic_pinmap lmic_pins = { 63 | .nss = 6, 64 | .rxtx = LMIC_UNUSED_PIN, 65 | .rst = 5, 66 | .dio = {2, 3, 4}, 67 | }; 68 | 69 | void onEvent (ev_t ev) { 70 | Serial.print(os_getTime()); 71 | Serial.print(": "); 72 | switch(ev) { 73 | case EV_SCAN_TIMEOUT: 74 | Serial.println(F("EV_SCAN_TIMEOUT")); 75 | break; 76 | case EV_BEACON_FOUND: 77 | Serial.println(F("EV_BEACON_FOUND")); 78 | break; 79 | case EV_BEACON_MISSED: 80 | Serial.println(F("EV_BEACON_MISSED")); 81 | break; 82 | case EV_BEACON_TRACKED: 83 | Serial.println(F("EV_BEACON_TRACKED")); 84 | break; 85 | case EV_JOINING: 86 | Serial.println(F("EV_JOINING")); 87 | break; 88 | case EV_JOINED: 89 | Serial.println(F("EV_JOINED")); 90 | 91 | // Disable link check validation (automatically enabled 92 | // during join, but not supported by TTN at this time). 93 | LMIC_setLinkCheckMode(0); 94 | break; 95 | case EV_RFU1: 96 | Serial.println(F("EV_RFU1")); 97 | break; 98 | case EV_JOIN_FAILED: 99 | Serial.println(F("EV_JOIN_FAILED")); 100 | break; 101 | case EV_REJOIN_FAILED: 102 | Serial.println(F("EV_REJOIN_FAILED")); 103 | break; 104 | break; 105 | case EV_TXCOMPLETE: 106 | Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); 107 | if (LMIC.txrxFlags & TXRX_ACK) 108 | Serial.println(F("Received ack")); 109 | if (LMIC.dataLen) { 110 | Serial.println(F("Received ")); 111 | Serial.println(LMIC.dataLen); 112 | Serial.println(F(" bytes of payload")); 113 | } 114 | // Schedule next transmission 115 | os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send); 116 | break; 117 | case EV_LOST_TSYNC: 118 | Serial.println(F("EV_LOST_TSYNC")); 119 | break; 120 | case EV_RESET: 121 | Serial.println(F("EV_RESET")); 122 | break; 123 | case EV_RXCOMPLETE: 124 | // data received in ping slot 125 | Serial.println(F("EV_RXCOMPLETE")); 126 | break; 127 | case EV_LINK_DEAD: 128 | Serial.println(F("EV_LINK_DEAD")); 129 | break; 130 | case EV_LINK_ALIVE: 131 | Serial.println(F("EV_LINK_ALIVE")); 132 | break; 133 | default: 134 | Serial.println(F("Unknown event")); 135 | break; 136 | } 137 | } 138 | 139 | void do_send(osjob_t* j){ 140 | // Check if there is not a current TX/RX job running 141 | if (LMIC.opmode & OP_TXRXPEND) { 142 | Serial.println(F("OP_TXRXPEND, not sending")); 143 | } else { 144 | // Prepare upstream data transmission at the next possible time. 145 | LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0); 146 | Serial.println(F("Packet queued")); 147 | } 148 | // Next TX is scheduled after TX_COMPLETE event. 149 | } 150 | 151 | void setup() { 152 | Serial.begin(9600); 153 | Serial.println(F("Starting")); 154 | 155 | #ifdef VCC_ENABLE 156 | // For Pinoccio Scout boards 157 | pinMode(VCC_ENABLE, OUTPUT); 158 | digitalWrite(VCC_ENABLE, HIGH); 159 | delay(1000); 160 | #endif 161 | 162 | // LMIC init 163 | os_init(); 164 | // Reset the MAC state. Session and pending data transfers will be discarded. 165 | LMIC_reset(); 166 | 167 | // Start job (sending automatically starts OTAA too) 168 | do_send(&sendjob); 169 | } 170 | 171 | void loop() { 172 | os_runloop_once(); 173 | } 174 | -------------------------------------------------------------------------------- /src/lorawan.cpp: -------------------------------------------------------------------------------- 1 | // Basic Config 2 | #include "globals.h" 3 | 4 | // LMIC-Arduino LoRaWAN Stack 5 | #include "loraconf.h" 6 | #include 7 | #include 8 | 9 | #ifdef MCP_24AA02E64_I2C_ADDRESS 10 | #include // Needed for 24AA02E64, does not hurt anything if included and not used 11 | #endif 12 | 13 | // Local logging Tag 14 | static const char *TAG = "lorawan"; 15 | 16 | // function defined in main.cpp 17 | void set_onboard_led(int state); 18 | 19 | // functions defined in rcommand.cpp 20 | void rcommand(int cmd, int arg); 21 | void switch_lora(int sf, int tx); 22 | 23 | // DevEUI generator using devices's MAC address 24 | void gen_lora_deveui(uint8_t *pdeveui) { 25 | uint8_t *p = pdeveui, dmac[6]; 26 | int i = 0; 27 | esp_efuse_mac_get_default(dmac); 28 | // deveui is LSB, we reverse it so TTN DEVEUI display 29 | // will remain the same as MAC address 30 | // MAC is 6 bytes, devEUI 8, set first 2 ones 31 | // with an arbitrary value 32 | *p++ = 0xFF; 33 | *p++ = 0xFE; 34 | // Then next 6 bytes are mac address reversed 35 | for ( i=0; i<6 ; i++) { 36 | *p++ = dmac[5-i]; 37 | } 38 | } 39 | 40 | // Function to do a byte swap in a byte array 41 | void RevBytes(unsigned char* b, size_t c) 42 | { 43 | u1_t i; 44 | for (i = 0; i < c / 2; i++) 45 | { unsigned char t = b[i]; 46 | b[i] = b[c - 1 - i]; 47 | b[c - 1 - i] = t; } 48 | } 49 | 50 | void get_hard_deveui(uint8_t *pdeveui) { 51 | // read DEVEUI from Microchip 24AA02E64 2Kb serial eeprom if present 52 | #ifdef MCP_24AA02E64_I2C_ADDRESS 53 | uint8_t i2c_ret; 54 | // Init this just in case, no more to 100KHz 55 | Wire.begin(OLED_SDA, OLED_SCL, 100000); 56 | Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS); 57 | Wire.write(MCP_24AA02E64_MAC_ADDRESS); 58 | i2c_ret = Wire.endTransmission(); 59 | // check if device seen on i2c bus 60 | if (i2c_ret == 0) { 61 | char deveui[32]=""; 62 | uint8_t data; 63 | Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS); 64 | Wire.write(MCP_24AA02E64_MAC_ADDRESS); 65 | Wire.requestFrom(MCP_24AA02E64_I2C_ADDRESS, 8); 66 | while (Wire.available()) { 67 | data = Wire.read(); 68 | sprintf(deveui+strlen(deveui), "%02X ", data); 69 | *pdeveui++ = data; 70 | } 71 | i2c_ret = Wire.endTransmission(); 72 | ESP_LOGI(TAG, "Serial EEPROM 24AA02E64 found, read DEVEUI %s", deveui); 73 | } else { 74 | ESP_LOGI(TAG, "Serial EEPROM 24AA02E64 not found ret=%d", i2c_ret); 75 | } 76 | // Set back to 400KHz to speed up OLED 77 | Wire.setClock(400000); 78 | #endif // MCP 24AA02E64 79 | } 80 | 81 | #ifdef VERBOSE 82 | 83 | // Display a key 84 | void printKey(const char * name, const uint8_t * key, uint8_t len, bool lsb) { 85 | uint8_t start=lsb?len:0; 86 | uint8_t end = lsb?0:len; 87 | const uint8_t * p ; 88 | char keystring[len+1] = "", keybyte[3]; 89 | for (uint8_t i=0; i> 8; 119 | mydata[1] = data & 0xff; 120 | 121 | #ifdef BLECOUNTER 122 | // Sum of unique BLE MACs seen 123 | data = (uint16_t) bles.size(); 124 | mydata[2] = (data & 0xff00) >> 8; 125 | mydata[3] = data & 0xff; 126 | #else 127 | mydata[2] = 0; 128 | mydata[3] = 0; 129 | #endif 130 | 131 | // Total BLE+WIFI unique MACs seen 132 | // TBD ? 133 | //data = (uint16_t) macs.size(); 134 | //mydata[4] = (data & 0xff00) >> 8; 135 | //mydata[5] = data & 0xff; 136 | 137 | // Check if there is not a current TX/RX job running 138 | if (LMIC.opmode & OP_TXRXPEND) { 139 | ESP_LOGI(TAG, "OP_TXRXPEND, not sending"); 140 | sprintf(display_lmic, "LORA BUSY"); 141 | } else { 142 | // Prepare upstream data transmission at the next possible time. 143 | LMIC_setTxData2(1, mydata, sizeof(mydata), (cfg.countermode & 0x02)); 144 | ESP_LOGI(TAG, "Packet queued"); 145 | sprintf(display_lmic, "PACKET QUEUED"); 146 | } 147 | // Next TX is scheduled after TX_COMPLETE event. 148 | } 149 | 150 | void onEvent (ev_t ev) { 151 | char buff[24]=""; 152 | 153 | switch(ev) { 154 | case EV_SCAN_TIMEOUT: strcpy_P(buff, PSTR("SCAN TIMEOUT")); break; 155 | case EV_BEACON_FOUND: strcpy_P(buff, PSTR("BEACON FOUND")); break; 156 | case EV_BEACON_MISSED: strcpy_P(buff, PSTR("BEACON MISSED")); break; 157 | case EV_BEACON_TRACKED: strcpy_P(buff, PSTR("BEACON TRACKED")); break; 158 | case EV_JOINING: strcpy_P(buff, PSTR("JOINING")); break; 159 | case EV_LOST_TSYNC: strcpy_P(buff, PSTR("LOST TSYNC")); break; 160 | case EV_RESET: strcpy_P(buff, PSTR("RESET")); break; 161 | case EV_RXCOMPLETE: strcpy_P(buff, PSTR("RX COMPLETE")); break; 162 | case EV_LINK_DEAD: strcpy_P(buff, PSTR("LINK DEAD")); break; 163 | case EV_LINK_ALIVE: strcpy_P(buff, PSTR("LINK ALIVE")); break; 164 | case EV_RFU1: strcpy_P(buff, PSTR("RFUI")); break; 165 | case EV_JOIN_FAILED: strcpy_P(buff, PSTR("JOIN FAILED")); break; 166 | case EV_REJOIN_FAILED: strcpy_P(buff, PSTR("REJOIN FAILED")); break; 167 | 168 | case EV_JOINED: 169 | strcpy_P(buff, PSTR("JOINED")); 170 | sprintf(display_lora, " "); // erase "Join Wait" message from display 171 | // Disable link check validation (automatically enabled 172 | // during join, but not supported by TTN at this time). 173 | LMIC_setLinkCheckMode(0); 174 | // set data rate adaptation 175 | LMIC_setAdrMode(cfg.adrmode); 176 | // Set data rate and transmit power (note: txpower seems to be ignored by the library) 177 | switch_lora(cfg.lorasf,cfg.txpower); 178 | joinstate=true; 179 | // show effective LoRa parameters after join 180 | ESP_LOGI(TAG, "ADR=%i, SF=%i, TXPOWER=%i", cfg.adrmode, cfg.lorasf, cfg.txpower); 181 | break; 182 | case EV_TXCOMPLETE: 183 | ESP_LOGI(TAG, "EV_TXCOMPLETE (includes waiting for RX windows)"); 184 | if (LMIC.txrxFlags & TXRX_ACK) { 185 | ESP_LOGI(TAG, "Received ack"); 186 | sprintf(display_lmic, "RECEIVED ACK"); 187 | 188 | } else { 189 | sprintf(display_lmic, "TX COMPLETE"); 190 | } 191 | if (LMIC.dataLen) { 192 | ESP_LOGI(TAG, "Received %d bytes of payload", LMIC.dataLen); 193 | sprintf(display_lora, "Rcvd %d bytes", LMIC.dataLen); 194 | 195 | // LMIC.snr = SNR twos compliment [dB] * 4 196 | // LMIC.rssi = RSSI [dBm] (-196...+63) 197 | sprintf(display_lmic, "RSSI %d SNR %d", LMIC.rssi, (signed char)LMIC.snr / 4 ); 198 | 199 | // check if payload received on command port, then call remote command interpreter 200 | if ( (LMIC.txrxFlags & TXRX_PORT) && (LMIC.frame[LMIC.dataBeg-1] == RCMDPORT ) ) { 201 | // caution: buffering LMIC values here because rcommand() can modify LMIC.frame 202 | unsigned char* buffer = new unsigned char[MAX_LEN_FRAME]; 203 | memcpy(buffer, LMIC.frame, MAX_LEN_FRAME); //Copy data from cfg to char* 204 | int i, k = LMIC.dataBeg, l = LMIC.dataBeg+LMIC.dataLen-2; 205 | for (i=k; i<=l; i+=2) 206 | rcommand(buffer[i], buffer[i+1]); 207 | delete[] buffer; //free memory 208 | } 209 | } 210 | break; 211 | default: sprintf_P(buff, PSTR("UNKNOWN EVENT %d"), ev); break; 212 | } 213 | 214 | // Log & Display if asked 215 | if (*buff) { 216 | ESP_LOGI(TAG, "EV_%s", buff); 217 | sprintf(display_lmic, buff); 218 | } 219 | 220 | 221 | } 222 | 223 | -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/examples/ttn-abp/ttn-abp.ino: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman 3 | * 4 | * Permission is hereby granted, free of charge, to anyone 5 | * obtaining a copy of this document and accompanying files, 6 | * to do whatever they want with them without any restriction, 7 | * including, but not limited to, copying, modification and redistribution. 8 | * NO WARRANTY OF ANY KIND IS PROVIDED. 9 | * 10 | * This example sends a valid LoRaWAN packet with payload "Hello, 11 | * world!", using frequency and encryption settings matching those of 12 | * the The Things Network. 13 | * 14 | * This uses ABP (Activation-by-personalisation), where a DevAddr and 15 | * Session keys are preconfigured (unlike OTAA, where a DevEUI and 16 | * application key is configured, while the DevAddr and session keys are 17 | * assigned/generated in the over-the-air-activation procedure). 18 | * 19 | * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in 20 | * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably 21 | * violated by this sketch when left running for longer)! 22 | * 23 | * To use this sketch, first register your application and device with 24 | * the things network, to set or generate a DevAddr, NwkSKey and 25 | * AppSKey. Each device should have their own unique values for these 26 | * fields. 27 | * 28 | * Do not forget to define the radio type correctly in config.h. 29 | * 30 | *******************************************************************************/ 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | // LoRaWAN NwkSKey, network session key 37 | // This is the default Semtech key, which is used by the early prototype TTN 38 | // network. 39 | static const PROGMEM u1_t NWKSKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }; 40 | 41 | // LoRaWAN AppSKey, application session key 42 | // This is the default Semtech key, which is used by the early prototype TTN 43 | // network. 44 | static const u1_t PROGMEM APPSKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }; 45 | 46 | // LoRaWAN end-device address (DevAddr) 47 | static const u4_t DEVADDR = 0x03FF0001 ; // <-- Change this address for every node! 48 | 49 | // These callbacks are only used in over-the-air activation, so they are 50 | // left empty here (we cannot leave them out completely unless 51 | // DISABLE_JOIN is set in config.h, otherwise the linker will complain). 52 | void os_getArtEui (u1_t* buf) { } 53 | void os_getDevEui (u1_t* buf) { } 54 | void os_getDevKey (u1_t* buf) { } 55 | 56 | static uint8_t mydata[] = "Hello, world!"; 57 | static osjob_t sendjob; 58 | 59 | // Schedule TX every this many seconds (might become longer due to duty 60 | // cycle limitations). 61 | const unsigned TX_INTERVAL = 60; 62 | 63 | // Pin mapping 64 | const lmic_pinmap lmic_pins = { 65 | .nss = 6, 66 | .rxtx = LMIC_UNUSED_PIN, 67 | .rst = 5, 68 | .dio = {2, 3, 4}, 69 | }; 70 | 71 | void onEvent (ev_t ev) { 72 | Serial.print(os_getTime()); 73 | Serial.print(": "); 74 | switch(ev) { 75 | case EV_SCAN_TIMEOUT: 76 | Serial.println(F("EV_SCAN_TIMEOUT")); 77 | break; 78 | case EV_BEACON_FOUND: 79 | Serial.println(F("EV_BEACON_FOUND")); 80 | break; 81 | case EV_BEACON_MISSED: 82 | Serial.println(F("EV_BEACON_MISSED")); 83 | break; 84 | case EV_BEACON_TRACKED: 85 | Serial.println(F("EV_BEACON_TRACKED")); 86 | break; 87 | case EV_JOINING: 88 | Serial.println(F("EV_JOINING")); 89 | break; 90 | case EV_JOINED: 91 | Serial.println(F("EV_JOINED")); 92 | break; 93 | case EV_RFU1: 94 | Serial.println(F("EV_RFU1")); 95 | break; 96 | case EV_JOIN_FAILED: 97 | Serial.println(F("EV_JOIN_FAILED")); 98 | break; 99 | case EV_REJOIN_FAILED: 100 | Serial.println(F("EV_REJOIN_FAILED")); 101 | break; 102 | case EV_TXCOMPLETE: 103 | Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); 104 | if (LMIC.txrxFlags & TXRX_ACK) 105 | Serial.println(F("Received ack")); 106 | if (LMIC.dataLen) { 107 | Serial.println(F("Received ")); 108 | Serial.println(LMIC.dataLen); 109 | Serial.println(F(" bytes of payload")); 110 | } 111 | // Schedule next transmission 112 | os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send); 113 | break; 114 | case EV_LOST_TSYNC: 115 | Serial.println(F("EV_LOST_TSYNC")); 116 | break; 117 | case EV_RESET: 118 | Serial.println(F("EV_RESET")); 119 | break; 120 | case EV_RXCOMPLETE: 121 | // data received in ping slot 122 | Serial.println(F("EV_RXCOMPLETE")); 123 | break; 124 | case EV_LINK_DEAD: 125 | Serial.println(F("EV_LINK_DEAD")); 126 | break; 127 | case EV_LINK_ALIVE: 128 | Serial.println(F("EV_LINK_ALIVE")); 129 | break; 130 | default: 131 | Serial.println(F("Unknown event")); 132 | break; 133 | } 134 | } 135 | 136 | void do_send(osjob_t* j){ 137 | // Check if there is not a current TX/RX job running 138 | if (LMIC.opmode & OP_TXRXPEND) { 139 | Serial.println(F("OP_TXRXPEND, not sending")); 140 | } else { 141 | // Prepare upstream data transmission at the next possible time. 142 | LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0); 143 | Serial.println(F("Packet queued")); 144 | } 145 | // Next TX is scheduled after TX_COMPLETE event. 146 | } 147 | 148 | void setup() { 149 | Serial.begin(115200); 150 | Serial.println(F("Starting")); 151 | 152 | #ifdef VCC_ENABLE 153 | // For Pinoccio Scout boards 154 | pinMode(VCC_ENABLE, OUTPUT); 155 | digitalWrite(VCC_ENABLE, HIGH); 156 | delay(1000); 157 | #endif 158 | 159 | // LMIC init 160 | os_init(); 161 | // Reset the MAC state. Session and pending data transfers will be discarded. 162 | LMIC_reset(); 163 | 164 | // Set static session parameters. Instead of dynamically establishing a session 165 | // by joining the network, precomputed session parameters are be provided. 166 | #ifdef PROGMEM 167 | // On AVR, these values are stored in flash and only copied to RAM 168 | // once. Copy them to a temporary buffer here, LMIC_setSession will 169 | // copy them into a buffer of its own again. 170 | uint8_t appskey[sizeof(APPSKEY)]; 171 | uint8_t nwkskey[sizeof(NWKSKEY)]; 172 | memcpy_P(appskey, APPSKEY, sizeof(APPSKEY)); 173 | memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY)); 174 | LMIC_setSession (0x1, DEVADDR, nwkskey, appskey); 175 | #else 176 | // If not running an AVR with PROGMEM, just use the arrays directly 177 | LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY); 178 | #endif 179 | 180 | #if defined(CFG_eu868) 181 | // Set up the channels used by the Things Network, which corresponds 182 | // to the defaults of most gateways. Without this, only three base 183 | // channels from the LoRaWAN specification are used, which certainly 184 | // works, so it is good for debugging, but can overload those 185 | // frequencies, so be sure to configure the full frequency range of 186 | // your network here (unless your network autoconfigures them). 187 | // Setting up channels should happen after LMIC_setSession, as that 188 | // configures the minimal channel set. 189 | // NA-US channels 0-71 are configured automatically 190 | LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 191 | LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI); // g-band 192 | LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 193 | LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 194 | LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 195 | LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 196 | LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 197 | LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 198 | LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK, DR_FSK), BAND_MILLI); // g2-band 199 | // TTN defines an additional channel at 869.525Mhz using SF9 for class B 200 | // devices' ping slots. LMIC does not have an easy way to define set this 201 | // frequency and support for class B is spotty and untested, so this 202 | // frequency is not configured here. 203 | #elif defined(CFG_us915) 204 | // NA-US channels 0-71 are configured automatically 205 | // but only one group of 8 should (a subband) should be active 206 | // TTN recommends the second sub band, 1 in a zero based count. 207 | // https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json 208 | LMIC_selectSubBand(1); 209 | #endif 210 | 211 | // Disable link check validation 212 | LMIC_setLinkCheckMode(0); 213 | 214 | // TTN uses SF9 for its RX2 window. 215 | LMIC.dn2Dr = DR_SF9; 216 | 217 | // Set data rate and transmit power for uplink (note: txpow seems to be ignored by the library) 218 | LMIC_setDrTxpow(DR_SF7,14); 219 | 220 | // Start job 221 | do_send(&sendjob); 222 | } 223 | 224 | void loop() { 225 | os_runloop_once(); 226 | } 227 | -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/hal/hal.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Matthijs Kooijman 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * This the HAL to run LMIC on top of the Arduino environment. 9 | *******************************************************************************/ 10 | 11 | #include 12 | #include 13 | #include "../lmic.h" 14 | #include "hal.h" 15 | #include 16 | 17 | // ----------------------------------------------------------------------------- 18 | // I/O 19 | 20 | static void hal_io_init () { 21 | // NSS and DIO0 are required, DIO1 is required for LoRa, DIO2 for FSK 22 | ASSERT(lmic_pins.nss != LMIC_UNUSED_PIN); 23 | ASSERT(lmic_pins.dio[0] != LMIC_UNUSED_PIN); 24 | ASSERT(lmic_pins.dio[1] != LMIC_UNUSED_PIN || lmic_pins.dio[2] != LMIC_UNUSED_PIN); 25 | 26 | #ifdef LMIC_SPI_PINS_IN_MAPPING 27 | ASSERT(lmic_pins.mosi != LMIC_UNUSED_PIN 28 | || lmic_pins.miso != LMIC_UNUSED_PIN 29 | || lmic_pins.sck != LMIC_UNUSED_PIN); 30 | #endif 31 | 32 | pinMode(lmic_pins.nss, OUTPUT); 33 | if (lmic_pins.rxtx != LMIC_UNUSED_PIN) 34 | pinMode(lmic_pins.rxtx, OUTPUT); 35 | if (lmic_pins.rst != LMIC_UNUSED_PIN) 36 | pinMode(lmic_pins.rst, OUTPUT); 37 | 38 | pinMode(lmic_pins.dio[0], INPUT); 39 | if (lmic_pins.dio[1] != LMIC_UNUSED_PIN) 40 | pinMode(lmic_pins.dio[1], INPUT); 41 | if (lmic_pins.dio[2] != LMIC_UNUSED_PIN) 42 | pinMode(lmic_pins.dio[2], INPUT); 43 | } 44 | 45 | // val == 1 => tx 1 46 | void hal_pin_rxtx (u1_t val) { 47 | if (lmic_pins.rxtx != LMIC_UNUSED_PIN) 48 | digitalWrite(lmic_pins.rxtx, val); 49 | } 50 | 51 | // set radio RST pin to given value (or keep floating!) 52 | void hal_pin_rst (u1_t val) { 53 | if (lmic_pins.rst == LMIC_UNUSED_PIN) 54 | return; 55 | 56 | if(val == 0 || val == 1) { // drive pin 57 | pinMode(lmic_pins.rst, OUTPUT); 58 | digitalWrite(lmic_pins.rst, val); 59 | } else { // keep pin floating 60 | pinMode(lmic_pins.rst, INPUT); 61 | } 62 | } 63 | 64 | static bool dio_states[NUM_DIO] = {0}; 65 | 66 | static void hal_io_check() { 67 | uint8_t i; 68 | for (i = 0; i < NUM_DIO; ++i) { 69 | if (lmic_pins.dio[i] == LMIC_UNUSED_PIN) 70 | continue; 71 | 72 | if (dio_states[i] != digitalRead(lmic_pins.dio[i])) { 73 | dio_states[i] = !dio_states[i]; 74 | if (dio_states[i]) 75 | radio_irq_handler(i); 76 | } 77 | } 78 | } 79 | 80 | // ----------------------------------------------------------------------------- 81 | // SPI 82 | 83 | static const SPISettings settings(10E6, MSBFIRST, SPI_MODE0); 84 | 85 | // Initialize SPI, allowing override of default SPI pins on certain boards. 86 | static void hal_spi_init () { 87 | #if defined(ESP32) 88 | // On the ESP32 the default is _use_hw_ss(false), 89 | // so we can set the last parameter to anything. 90 | SPI.begin(lmic_pins.sck, lmic_pins.miso, lmic_pins.mosi, 0x00); 91 | #elif defined(NRF51) 92 | SPI.begin(lmic_pins.sck, lmic_pins.mosi, lmic_pins.miso); 93 | #else 94 | //unknown board, or board without SPI pin select ability 95 | SPI.begin(); 96 | #endif 97 | } 98 | 99 | void hal_pin_nss (u1_t val) { 100 | if (!val) 101 | SPI.beginTransaction(settings); 102 | else 103 | SPI.endTransaction(); 104 | 105 | //Serial.println(val?">>":"<<"); 106 | digitalWrite(lmic_pins.nss, val); 107 | } 108 | 109 | // perform SPI transaction with radio 110 | u1_t hal_spi (u1_t out) { 111 | u1_t res = SPI.transfer(out); 112 | /* 113 | Serial.print(">"); 114 | Serial.print(out, HEX); 115 | Serial.print("<"); 116 | Serial.println(res, HEX); 117 | */ 118 | return res; 119 | } 120 | 121 | // ----------------------------------------------------------------------------- 122 | // TIME 123 | 124 | static void hal_time_init () { 125 | // Nothing to do 126 | } 127 | 128 | u4_t hal_ticks () { 129 | // Because micros() is scaled down in this function, micros() will 130 | // overflow before the tick timer should, causing the tick timer to 131 | // miss a significant part of its values if not corrected. To fix 132 | // this, the "overflow" serves as an overflow area for the micros() 133 | // counter. It consists of three parts: 134 | // - The US_PER_OSTICK upper bits are effectively an extension for 135 | // the micros() counter and are added to the result of this 136 | // function. 137 | // - The next bit overlaps with the most significant bit of 138 | // micros(). This is used to detect micros() overflows. 139 | // - The remaining bits are always zero. 140 | // 141 | // By comparing the overlapping bit with the corresponding bit in 142 | // the micros() return value, overflows can be detected and the 143 | // upper bits are incremented. This is done using some clever 144 | // bitwise operations, to remove the need for comparisons and a 145 | // jumps, which should result in efficient code. By avoiding shifts 146 | // other than by multiples of 8 as much as possible, this is also 147 | // efficient on AVR (which only has 1-bit shifts). 148 | static uint8_t overflow = 0; 149 | 150 | // Scaled down timestamp. The top US_PER_OSTICK_EXPONENT bits are 0, 151 | // the others will be the lower bits of our return value. 152 | uint32_t scaled = micros() >> US_PER_OSTICK_EXPONENT; 153 | // Most significant byte of scaled 154 | uint8_t msb = scaled >> 24; 155 | // Mask pointing to the overlapping bit in msb and overflow. 156 | const uint8_t mask = (1 << (7 - US_PER_OSTICK_EXPONENT)); 157 | // Update overflow. If the overlapping bit is different 158 | // between overflow and msb, it is added to the stored value, 159 | // so the overlapping bit becomes equal again and, if it changed 160 | // from 1 to 0, the upper bits are incremented. 161 | overflow += (msb ^ overflow) & mask; 162 | 163 | // Return the scaled value with the upper bits of stored added. The 164 | // overlapping bit will be equal and the lower bits will be 0, so 165 | // bitwise or is a no-op for them. 166 | return scaled | ((uint32_t)overflow << 24); 167 | 168 | // 0 leads to correct, but overly complex code (it could just return 169 | // micros() unmodified), 8 leaves no room for the overlapping bit. 170 | static_assert(US_PER_OSTICK_EXPONENT > 0 && US_PER_OSTICK_EXPONENT < 8, "Invalid US_PER_OSTICK_EXPONENT value"); 171 | } 172 | 173 | // Returns the number of ticks until time. Negative values indicate that 174 | // time has already passed. 175 | static s4_t delta_time(u4_t time) { 176 | return (s4_t)(time - hal_ticks()); 177 | } 178 | 179 | void hal_waitUntil (u4_t time) { 180 | s4_t delta = delta_time(time); 181 | // From delayMicroseconds docs: Currently, the largest value that 182 | // will produce an accurate delay is 16383. 183 | while (delta > (16000 / US_PER_OSTICK)) { 184 | delay(16); 185 | delta -= (16000 / US_PER_OSTICK); 186 | } 187 | if (delta > 0) 188 | delayMicroseconds(delta * US_PER_OSTICK); 189 | } 190 | 191 | // check and rewind for target time 192 | u1_t hal_checkTimer (u4_t time) { 193 | // No need to schedule wakeup, since we're not sleeping 194 | return delta_time(time) <= 0; 195 | } 196 | 197 | static uint8_t irqlevel = 0; 198 | 199 | void hal_disableIRQs () { 200 | noInterrupts(); 201 | irqlevel++; 202 | } 203 | 204 | void hal_enableIRQs () { 205 | if(--irqlevel == 0) { 206 | interrupts(); 207 | 208 | // Instead of using proper interrupts (which are a bit tricky 209 | // and/or not available on all pins on AVR), just poll the pin 210 | // values. Since os_runloop disables and re-enables interrupts, 211 | // putting this here makes sure we check at least once every 212 | // loop. 213 | // 214 | // As an additional bonus, this prevents the can of worms that 215 | // we would otherwise get for running SPI transfers inside ISRs 216 | hal_io_check(); 217 | } 218 | } 219 | 220 | void hal_sleep () { 221 | // Not implemented 222 | } 223 | 224 | // ----------------------------------------------------------------------------- 225 | 226 | #if defined(LMIC_PRINTF_TO) 227 | static int uart_putchar (char c, FILE *) 228 | { 229 | LMIC_PRINTF_TO.write(c) ; 230 | return 0 ; 231 | } 232 | 233 | void hal_printf_init() { 234 | // create a FILE structure to reference our UART output function 235 | static FILE uartout; 236 | memset(&uartout, 0, sizeof(uartout)); 237 | 238 | // fill in the UART file descriptor with pointer to writer. 239 | fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE); 240 | 241 | // The uart is the standard output device STDOUT. 242 | stdout = &uartout ; 243 | } 244 | #endif // defined(LMIC_PRINTF_TO) 245 | 246 | void hal_init () { 247 | // configure radio I/O and interrupt handler 248 | hal_io_init(); 249 | // configure radio SPI 250 | hal_spi_init(); 251 | // configure timer and interrupt handler 252 | hal_time_init(); 253 | #if defined(LMIC_PRINTF_TO) 254 | // printf support 255 | hal_printf_init(); 256 | #endif 257 | } 258 | 259 | void hal_failed (const char *file, u2_t line) { 260 | #if defined(LMIC_FAILURE_TO) 261 | LMIC_FAILURE_TO.println("FAILURE "); 262 | LMIC_FAILURE_TO.print(file); 263 | LMIC_FAILURE_TO.print(':'); 264 | LMIC_FAILURE_TO.println(line); 265 | LMIC_FAILURE_TO.flush(); 266 | #endif 267 | hal_disableIRQs(); 268 | while(1); 269 | } 270 | -------------------------------------------------------------------------------- /src/rcommand.cpp: -------------------------------------------------------------------------------- 1 | // remote command interpreter 2 | // parses multiple number of command / value pairs from LoRaWAN remote command port (RCMDPORT) 3 | // checks commands and executes each command with 1 argument per command 4 | 5 | // Basic Config 6 | #include "globals.h" 7 | 8 | // LMIC-Arduino LoRaWAN Stack 9 | #include 10 | #include 11 | 12 | // Local logging tag 13 | static const char *TAG = "rcommand"; 14 | 15 | // table of remote commands and assigned functions 16 | typedef struct { 17 | const int nam; 18 | void (*func)(int); 19 | const bool store; 20 | } cmd_t; 21 | 22 | // functions defined in configmanager.cpp 23 | void eraseConfig(void); 24 | void saveConfig(void); 25 | 26 | // function defined in antenna.cpp 27 | #ifdef HAS_ANTENNA_SWITCH 28 | void antenna_select(const int8_t _ant); 29 | #endif 30 | 31 | // help function to assign LoRa datarates to numeric spreadfactor values 32 | void switch_lora (int sf, int tx) { 33 | if ( tx > 20 ) return; 34 | cfg.txpower = tx; 35 | switch (sf) { 36 | case 7: LMIC_setDrTxpow(DR_SF7,tx); cfg.lorasf=sf; break; 37 | case 8: LMIC_setDrTxpow(DR_SF8,tx); cfg.lorasf=sf; break; 38 | case 9: LMIC_setDrTxpow(DR_SF9,tx); cfg.lorasf=sf; break; 39 | case 10: LMIC_setDrTxpow(DR_SF10,tx); cfg.lorasf=sf; break; 40 | case 11: 41 | #if defined(CFG_eu868) 42 | LMIC_setDrTxpow(DR_SF11,tx); cfg.lorasf=sf; break; 43 | #elif defined(CFG_us915) 44 | LMIC_setDrTxpow(DR_SF11CR,tx); cfg.lorasf=sf; break; 45 | #endif 46 | case 12: 47 | #if defined(CFG_eu868) 48 | LMIC_setDrTxpow(DR_SF12,tx); cfg.lorasf=sf; break; 49 | #elif defined(CFG_us915) 50 | LMIC_setDrTxpow(DR_SF12CR,tx); cfg.lorasf=sf; break; 51 | #endif 52 | default: break; 53 | } 54 | } 55 | 56 | // set of functions that can be triggered by remote commands 57 | void set_reset(int val) { 58 | switch (val) { 59 | case 0: // restart device 60 | ESP_LOGI(TAG, "Remote command: restart device"); 61 | sprintf(display_lora, "Reset pending"); 62 | vTaskDelay(10000/portTICK_PERIOD_MS); // wait for LMIC to confirm LoRa downlink to server 63 | esp_restart(); 64 | break; 65 | case 1: // reset MAC counter 66 | ESP_LOGI(TAG, "Remote command: reset MAC counter"); 67 | macs.clear(); // clear all macs container 68 | wifis.clear(); // clear Wifi macs container 69 | #ifdef BLECOUNTER 70 | bles.clear(); // clear BLE macs container 71 | #endif 72 | salt_reset(); // get new 16bit salt 73 | sprintf(display_lora, "Reset counter"); 74 | break; 75 | case 2: // reset device to factory settings 76 | ESP_LOGI(TAG, "Remote command: reset device to factory settings"); 77 | sprintf(display_lora, "Factory reset"); 78 | eraseConfig(); 79 | break; 80 | } 81 | }; 82 | 83 | void set_rssi(int val) { 84 | cfg.rssilimit = val * -1; 85 | ESP_LOGI(TAG, "Remote command: set RSSI limit to %i", cfg.rssilimit); 86 | }; 87 | 88 | void set_wifiscancycle(int val) { 89 | cfg.wifiscancycle = val; 90 | ESP_LOGI(TAG, "Remote command: set Wifi scan cycle duration to %i seconds", cfg.wifiscancycle*2); 91 | }; 92 | 93 | void set_wifichancycle(int val) { 94 | cfg.wifichancycle = val; 95 | ESP_LOGI(TAG, "Remote command: set Wifi channel switch interval to %i seconds", cfg.wifichancycle/100); 96 | }; 97 | 98 | void set_blescantime(int val) { 99 | cfg.blescantime = val; 100 | ESP_LOGI(TAG, "Remote command: set BLE scan time to %i seconds", cfg.blescantime); 101 | }; 102 | 103 | void set_countmode(int val) { 104 | switch (val) { 105 | case 0: // cyclic unconfirmed 106 | cfg.countermode = 0; 107 | ESP_LOGI(TAG, "Remote command: set counter mode to cyclic unconfirmed"); 108 | break; 109 | case 1: // cumulative 110 | cfg.countermode = 1; 111 | ESP_LOGI(TAG, "Remote command: set counter mode to cumulative"); 112 | break; 113 | default: // cyclic confirmed 114 | cfg.countermode = 2; 115 | ESP_LOGI(TAG, "Remote command: set counter mode to cyclic confirmed"); 116 | break; 117 | } 118 | }; 119 | 120 | void set_screensaver(int val) { 121 | ESP_LOGI(TAG, "Remote command: set screen saver to %s ", val ? "on" : "off"); 122 | switch (val) { 123 | case 1: cfg.screensaver = val; break; 124 | default: cfg.screensaver = 0; break; 125 | } 126 | }; 127 | 128 | void set_display(int val) { 129 | ESP_LOGI(TAG, "Remote command: set screen to %s", val ? "on" : "off"); 130 | switch (val) { 131 | case 1: cfg.screenon = val; break; 132 | default: cfg.screenon = 0; break; 133 | } 134 | }; 135 | 136 | void set_lorasf(int val) { 137 | ESP_LOGI(TAG, "Remote command: set LoRa SF to %i", val); 138 | switch_lora(val, cfg.txpower); 139 | }; 140 | 141 | void set_loraadr(int val) { 142 | ESP_LOGI(TAG, "Remote command: set LoRa ADR mode to %s", val ? "on" : "off"); 143 | switch (val) { 144 | case 1: cfg.adrmode = val; break; 145 | default: cfg.adrmode = 0; break; 146 | } 147 | LMIC_setAdrMode(cfg.adrmode); 148 | }; 149 | 150 | void set_blescan(int val) { 151 | ESP_LOGI(TAG, "Remote command: set BLE scan mode to %s", val ? "on" : "off"); 152 | switch (val) { 153 | case 0: 154 | cfg.blescan = 0; 155 | #ifdef BLECOUNTER 156 | bles.clear(); // clear BLE macs container 157 | #endif 158 | break; 159 | default: 160 | cfg.blescan = 1; 161 | break; 162 | } 163 | }; 164 | 165 | void set_wifiant(int val) { 166 | ESP_LOGI(TAG, "Remote command: set Wifi antenna to %s", val ? "external" : "internal"); 167 | switch (val) { 168 | case 1: cfg.wifiant = val; break; 169 | default: cfg.wifiant = 0; break; 170 | } 171 | #ifdef HAS_ANTENNA_SWITCH 172 | antenna_select(cfg.wifiant); 173 | #endif 174 | }; 175 | 176 | void set_vendorfilter(int val) { 177 | ESP_LOGI(TAG, "Remote command: set vendorfilter mode to %s", val ? "on" : "off"); 178 | switch (val) { 179 | case 1: cfg.vendorfilter = val; break; 180 | default: cfg.vendorfilter = 0; break; 181 | } 182 | }; 183 | 184 | void set_rgblum(int val) { 185 | // Avoid wrong parameters 186 | cfg.rgblum = (val>=0 && val<=100) ? (uint8_t) val : RGBLUMINOSITY; 187 | ESP_LOGI(TAG, "Remote command: set RGB Led luminosity %d", cfg.rgblum); 188 | }; 189 | 190 | void set_lorapower(int val) { 191 | ESP_LOGI(TAG, "Remote command: set LoRa TXPOWER to %i", val); 192 | switch_lora(cfg.lorasf, val); 193 | }; 194 | 195 | void set_noop (int val) { 196 | ESP_LOGI(TAG, "Remote command: noop - doing nothing"); 197 | }; 198 | 199 | void get_config (int val) { 200 | ESP_LOGI(TAG, "Remote command: get configuration"); 201 | int size = sizeof(configData_t); 202 | // declare send buffer (char byte array) 203 | unsigned char *sendData = new unsigned char[size]; 204 | // copy current configuration (struct) to send buffer 205 | memcpy(sendData, &cfg, size); 206 | LMIC_setTxData2(RCMDPORT, sendData, size-1, 0); // send data unconfirmed on RCMD Port 207 | delete sendData; // free memory 208 | ESP_LOGI(TAG, "%i bytes queued in send queue", size-1); 209 | }; 210 | 211 | void get_uptime (int val) { 212 | ESP_LOGI(TAG, "Remote command: get uptime"); 213 | int size = sizeof(uptimecounter); 214 | unsigned char *sendData = new unsigned char[size]; 215 | memcpy(sendData, (unsigned char*)&uptimecounter, size); 216 | LMIC_setTxData2(RCMDPORT, sendData, size-1, 0); // send data unconfirmed on RCMD Port 217 | delete sendData; // free memory 218 | ESP_LOGI(TAG, "%i bytes queued in send queue", size-1); 219 | }; 220 | 221 | void get_cputemp (int val) { 222 | ESP_LOGI(TAG, "Remote command: get cpu temperature"); 223 | float temp = temperatureRead(); 224 | int size = sizeof(temp); 225 | unsigned char *sendData = new unsigned char[size]; 226 | memcpy(sendData, (unsigned char*)&temp, size); 227 | LMIC_setTxData2(RCMDPORT, sendData, size-1, 0); // send data unconfirmed on RCMD Port 228 | delete sendData; // free memory 229 | ESP_LOGI(TAG, "%i bytes queued in send queue", size-1); 230 | }; 231 | 232 | // assign previously defined functions to set of numeric remote commands 233 | // format: opcode, function, flag (1 = do make settings persistent / 0 = don't) 234 | // 235 | cmd_t table[] = { 236 | {0x01, set_rssi, true}, 237 | {0x02, set_countmode, true}, 238 | {0x03, set_noop, false}, 239 | {0x04, set_display, true}, 240 | {0x05, set_lorasf, true}, 241 | {0x06, set_lorapower, true}, 242 | {0x07, set_loraadr, true}, 243 | {0x08, set_screensaver, true}, 244 | {0x09, set_reset, false}, 245 | {0x0a, set_wifiscancycle, true}, 246 | {0x0b, set_wifichancycle, true}, 247 | {0x0c, set_blescantime, true}, 248 | {0x0d, set_vendorfilter, false}, 249 | {0x0e, set_blescan, true}, 250 | {0x0f, set_wifiant, true}, 251 | {0x10, set_rgblum, true}, 252 | {0x80, get_config, false}, 253 | {0x81, get_uptime, false}, 254 | {0x82, get_cputemp, false} 255 | }; 256 | 257 | // check and execute remote command 258 | void rcommand(int cmd, int arg) { 259 | int i = sizeof(table) / sizeof(table[0]); // number of commands in command table 260 | bool store_flag = false; 261 | while(i--) { 262 | if(cmd == table[i].nam) { // check if valid command 263 | table[i].func(arg); // then execute assigned function 264 | if ( table[i].store ) store_flag = true; // set save flag if function needs to store configuration 265 | break; // exit check loop, since command was found 266 | } 267 | } 268 | if (store_flag) saveConfig(); // if save flag is set: store new configuration in NVS to make it persistent 269 | } -------------------------------------------------------------------------------- /src/blecsan.cpp: -------------------------------------------------------------------------------- 1 | #ifdef BLECOUNTER 2 | 3 | /* code snippets taken from 4 | https://github.com/nkolban/esp32-snippets/tree/master/BLE/scanner 5 | */ 6 | 7 | // Basic Config 8 | #include "globals.h" 9 | 10 | // Bluetooth specific includes 11 | #include 12 | #include 13 | #include 14 | #include // needed for BLE_ADDR types, do not remove 15 | #include 16 | 17 | #define BT_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] 18 | 19 | // local Tag for logging 20 | static const char *TAG = "bt_loop"; 21 | 22 | // defined in macsniff.cpp 23 | bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type); 24 | 25 | // Prototypes 26 | static const char *bt_addr_t_to_string(esp_ble_addr_type_t type); 27 | static const char *btsig_gap_type(uint32_t gap_type); 28 | static void gap_callback_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); 29 | 30 | static const char *bt_addr_t_to_string(esp_ble_addr_type_t type) { 31 | switch(type) { 32 | case BLE_ADDR_TYPE_PUBLIC: 33 | return "BLE_ADDR_TYPE_PUBLIC"; 34 | case BLE_ADDR_TYPE_RANDOM: 35 | return "BLE_ADDR_TYPE_RANDOM"; 36 | case BLE_ADDR_TYPE_RPA_PUBLIC: 37 | return "BLE_ADDR_TYPE_RPA_PUBLIC"; 38 | case BLE_ADDR_TYPE_RPA_RANDOM: 39 | return "BLE_ADDR_TYPE_RPA_RANDOM"; 40 | default: 41 | return "Unknown addr_t"; 42 | } 43 | } // bt_addr_t_to_string 44 | 45 | static const char *btsig_gap_type(uint32_t gap_type) { 46 | switch (gap_type) 47 | { 48 | case 0x01: return "Flags"; 49 | case 0x02: return "Incomplete List of 16-bit Service Class UUIDs"; 50 | case 0x03: return "Complete List of 16-bit Service Class UUIDs"; 51 | case 0x04: return "Incomplete List of 32-bit Service Class UUIDs"; 52 | case 0x05: return "Complete List of 32-bit Service Class UUIDs"; 53 | case 0x06: return "Incomplete List of 128-bit Service Class UUIDs"; 54 | case 0x07: return "Complete List of 128-bit Service Class UUIDs"; 55 | case 0x08: return "Shortened Local Name"; 56 | case 0x09: return "Complete Local Name"; 57 | case 0x0A: return "Tx Power Level"; 58 | case 0x0D: return "Class of Device"; 59 | case 0x0E: return "Simple Pairing Hash C/C-192"; 60 | case 0x0F: return "Simple Pairing Randomizer R/R-192"; 61 | case 0x10: return "Device ID/Security Manager TK Value"; 62 | case 0x11: return "Security Manager Out of Band Flags"; 63 | case 0x12: return "Slave Connection Interval Range"; 64 | case 0x14: return "List of 16-bit Service Solicitation UUIDs"; 65 | case 0x1F: return "List of 32-bit Service Solicitation UUIDs"; 66 | case 0x15: return "List of 128-bit Service Solicitation UUIDs"; 67 | case 0x16: return "Service Data - 16-bit UUID"; 68 | case 0x20: return "Service Data - 32-bit UUID"; 69 | case 0x21: return "Service Data - 128-bit UUID"; 70 | case 0x22: return "LE Secure Connections Confirmation Value"; 71 | case 0x23: return "LE Secure Connections Random Value"; 72 | case 0x24: return "URI"; 73 | case 0x25: return "Indoor Positioning"; 74 | case 0x26: return "Transport Discovery Data"; 75 | case 0x17: return "Public Target Address"; 76 | case 0x18: return "Random Target Address"; 77 | case 0x19: return "Appearance"; 78 | case 0x1A: return "Advertising Interval"; 79 | case 0x1B: return "LE Bluetooth Device Address"; 80 | case 0x1C: return "LE Role"; 81 | case 0x1D: return "Simple Pairing Hash C-256"; 82 | case 0x1E: return "Simple Pairing Randomizer R-256"; 83 | case 0x3D: return "3D Information Data"; 84 | case 0xFF: return "Manufacturer Specific Data"; 85 | 86 | default: 87 | return "Unknown type"; 88 | } 89 | } // btsig_gap_type 90 | 91 | 92 | static void gap_callback_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) 93 | { 94 | esp_ble_gap_cb_param_t *p = (esp_ble_gap_cb_param_t *)param; 95 | esp_err_t status; 96 | 97 | ESP_LOGD(tag, "BT payload rcvd -> type: 0x%.2x -> %s", *p->scan_rst.ble_adv, btsig_gap_type(*p->scan_rst.ble_adv)); 98 | 99 | switch (event) 100 | { 101 | case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: 102 | { // restart scan 103 | status = esp_ble_gap_start_scanning(BLESCANTIME); 104 | if (status != ESP_OK) 105 | { 106 | ESP_LOGE(TAG, "esp_ble_gap_start_scanning: rc=%d", status); 107 | } 108 | } 109 | break; 110 | 111 | case ESP_GAP_BLE_SCAN_RESULT_EVT: 112 | { 113 | if ( p->scan_rst.search_evt == ESP_GAP_SEARCH_INQ_CMPL_EVT) // Inquiry complete, scan is done 114 | { // restart scan 115 | status = esp_ble_gap_start_scanning (BLESCANTIME); 116 | if (status != ESP_OK) 117 | { 118 | ESP_LOGE(TAG, "esp_ble_gap_start_scanning: rc=%d", status); 119 | } 120 | return; 121 | } 122 | 123 | if (p->scan_rst.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) // Inquiry result for a peer device 124 | { // evaluate sniffed packet 125 | ESP_LOGD(TAG, "Device address (bda): %02x:%02x:%02x:%02x:%02x:%02x", BT_BD_ADDR_HEX(p->scan_rst.bda)); 126 | ESP_LOGD(TAG, "Addr_type : %s", bt_addr_t_to_string(p->scan_rst.ble_addr_type)); 127 | ESP_LOGD(TAG, "RSSI : %d", p->scan_rst.rssi); 128 | 129 | if (!( cfg.rssilimit == 0 ) || (p->scan_rst.rssi > cfg.rssilimit )) { // rssi is negative value 130 | ESP_LOGI(TAG, "BLTH RSSI %d -> ignoring (limit: %d)", p->scan_rst.rssi, cfg.rssilimit); 131 | break; 132 | } 133 | 134 | #ifdef VENDORFILTER 135 | 136 | if (p->scan_rst.ble_addr_type == BLE_ADDR_TYPE_RANDOM) goto skip; 137 | if (p->scan_rst.ble_addr_type == BLE_ADDR_TYPE_RPA_RANDOM) goto skip; 138 | 139 | #endif 140 | 141 | // add this device and show new count total if it was not previously added 142 | if (cfg.blescan) // count only if BLE scan is enabled 143 | mac_add((uint8_t *) p->scan_rst.bda, p->scan_rst.rssi, MAC_SNIFF_BLE); 144 | break; 145 | 146 | skip: 147 | ESP_LOGD(TAG, "BT device filtered"); 148 | break; 149 | 150 | 151 | /* to be improved in vendorfilter if: 152 | 153 | // you can search for elements in the payload using the 154 | // function esp_ble_resolve_adv_data() 155 | // 156 | // Like this, that scans for the "Complete name" (looking inside the payload buffer) 157 | // uint8_t len; 158 | // uint8_t *data = esp_ble_resolve_adv_data(p->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &len); 159 | 160 | filter BLE devices using their advertisements to get filter alternative to vendor OUI 161 | if vendorfiltering is on, we ... 162 | - want to count: mobile phones and tablets 163 | - don't want to count: beacons, peripherals (earphones, headsets, printers), cars and machines 164 | see 165 | https://github.com/nkolban/ESP32_BLE_Arduino/blob/master/src/BLEAdvertisedDevice.cpp 166 | 167 | http://www.libelium.com/products/meshlium/smartphone-detection/ 168 | 169 | https://www.question-defense.com/2013/01/12/bluetooth-cod-bluetooth-class-of-deviceclass-of-service-explained 170 | 171 | https://www.bluetooth.com/specifications/assigned-numbers/baseband 172 | 173 | "The Class of Device (CoD) in case of Bluetooth which allows us to differentiate the type of 174 | device (smartphone, handsfree, computer, LAN/network AP). With this parameter we can 175 | differentiate among pedestrians and vehicles." 176 | 177 | */ 178 | 179 | } 180 | 181 | } 182 | break; 183 | 184 | default: 185 | break; 186 | } 187 | } // gap_callback_handler 188 | 189 | 190 | esp_err_t register_ble_functionality(void) 191 | { 192 | esp_err_t status; 193 | 194 | ESP_LOGI(TAG, "Register GAP callback"); 195 | 196 | // This function is called to occur gap event, such as scan result. 197 | //register the scan callback function to the gap module 198 | status = esp_ble_gap_register_callback(gap_callback_handler); 199 | if (status != ESP_OK) 200 | { 201 | ESP_LOGE(TAG, "esp_ble_gap_register_callback: rc=%d", status); 202 | return ESP_FAIL; 203 | } 204 | 205 | static esp_ble_scan_params_t ble_scan_params = 206 | { 207 | .scan_type = BLE_SCAN_TYPE_PASSIVE, 208 | .own_addr_type = BLE_ADDR_TYPE_RANDOM, 209 | 210 | #ifdef VENDORFILTER 211 | .scan_filter_policy = BLE_SCAN_FILTER_ALLOW_WLIST_PRA_DIR, 212 | // ADV_IND, ADV_NONCONN_IND, ADV_SCAN_IND packets are used for broadcasting 213 | // data in broadcast applications (e.g., Beacons), so we don't want them in vendorfilter mode 214 | #else 215 | .scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL, 216 | #endif 217 | .scan_interval = (uint16_t) (BLESCANINTERVAL / 0.625), // Time = N * 0.625 msec 218 | .scan_window = (uint16_t) (BLESCANWINDOW / 0.625) // Time = N * 0.625 msec 219 | }; 220 | 221 | ESP_LOGI(TAG, "Set GAP scan parameters"); 222 | 223 | // This function is called to set scan parameters. 224 | status = esp_ble_gap_set_scan_params(&ble_scan_params); 225 | if (status != ESP_OK) 226 | { 227 | ESP_LOGE(TAG, "esp_ble_gap_set_scan_params: rc=%d", status); 228 | return ESP_FAIL; 229 | } 230 | 231 | return ESP_OK ; 232 | } 233 | 234 | 235 | // Main start code running in its own Xtask 236 | void bt_loop(void *ignore) 237 | { 238 | esp_err_t status; 239 | 240 | // Initialize BT controller to allocate task and other resource. 241 | ESP_LOGI(TAG, "Enabling Bluetooth Controller"); 242 | esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); 243 | if (esp_bt_controller_init(&bt_cfg) != ESP_OK) 244 | { 245 | ESP_LOGE(TAG, "Bluetooth controller initialize failed"); 246 | goto end; 247 | } 248 | 249 | // Enable BT controller 250 | if (esp_bt_controller_enable(ESP_BT_MODE_BTDM) != ESP_OK) 251 | { 252 | ESP_LOGE(TAG, "Bluetooth controller enable failed"); 253 | goto end; 254 | } 255 | 256 | // Init and alloc the resource for bluetooth, must be prior to every bluetooth stuff 257 | ESP_LOGI(TAG, "Init Bluetooth stack"); 258 | status = esp_bluedroid_init(); 259 | if (status != ESP_OK) 260 | { 261 | ESP_LOGE(TAG, "%s init bluetooth failed\n", __func__); 262 | goto end; 263 | } 264 | 265 | // Enable bluetooth, must after esp_bluedroid_init() 266 | status = esp_bluedroid_enable(); 267 | if (status != ESP_OK) 268 | { 269 | ESP_LOGE(TAG, "%s enable bluetooth failed\n", __func__); 270 | goto end; 271 | } 272 | 273 | ESP_LOGI(TAG, "Register BLE functionality"); 274 | status = register_ble_functionality(); 275 | if (status != ESP_OK) 276 | { 277 | ESP_LOGE(TAG, "Register BLE functionality failed"); 278 | goto end; 279 | } 280 | 281 | while(1) 282 | { 283 | vTaskDelay(500/portTICK_PERIOD_MS); 284 | yield(); 285 | } 286 | 287 | end: 288 | ESP_LOGI(TAG, "Terminating BT logging task"); 289 | vTaskDelete(NULL); 290 | 291 | } // bt_loop 292 | 293 | #endif // BLECOUNTER -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/oslmic.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | *******************************************************************************/ 11 | 12 | //! \file 13 | #ifndef _oslmic_h_ 14 | #define _oslmic_h_ 15 | 16 | // Dependencies required for the LoRa MAC in C to run. 17 | // These settings can be adapted to the underlying system. 18 | // You should not, however, change the lmic.[hc] 19 | 20 | #include "config.h" 21 | #include 22 | #include 23 | 24 | #ifdef __cplusplus 25 | extern "C"{ 26 | #endif 27 | 28 | //================================================================================ 29 | //================================================================================ 30 | // Target platform as C library 31 | typedef uint8_t bit_t; 32 | typedef uint8_t u1_t; 33 | typedef int8_t s1_t; 34 | typedef uint16_t u2_t; 35 | typedef int16_t s2_t; 36 | typedef uint32_t u4_t; 37 | typedef int32_t s4_t; 38 | typedef unsigned int uint; 39 | typedef const char* str_t; 40 | 41 | #include 42 | #include "hal.h" 43 | #define EV(a,b,c) /**/ 44 | #define DO_DEVDB(field1,field2) /**/ 45 | #if !defined(CFG_noassert) 46 | #define ASSERT(cond) if(!(cond)) hal_failed(__FILE__, __LINE__) 47 | #else 48 | #define ASSERT(cond) /**/ 49 | #endif 50 | 51 | #define os_clearMem(a,b) memset(a,0,b) 52 | #define os_copyMem(a,b,c) memcpy(a,b,c) 53 | 54 | typedef struct osjob_t osjob_t; 55 | typedef struct band_t band_t; 56 | typedef struct chnldef_t chnldef_t; 57 | typedef struct rxsched_t rxsched_t; 58 | typedef struct bcninfo_t bcninfo_t; 59 | typedef const u1_t* xref2cu1_t; 60 | typedef u1_t* xref2u1_t; 61 | #define TYPEDEF_xref2rps_t typedef rps_t* xref2rps_t 62 | #define TYPEDEF_xref2rxsched_t typedef rxsched_t* xref2rxsched_t 63 | #define TYPEDEF_xref2chnldef_t typedef chnldef_t* xref2chnldef_t 64 | #define TYPEDEF_xref2band_t typedef band_t* xref2band_t 65 | #define TYPEDEF_xref2osjob_t typedef osjob_t* xref2osjob_t 66 | 67 | #define SIZEOFEXPR(x) sizeof(x) 68 | 69 | #define ON_LMIC_EVENT(ev) onEvent(ev) 70 | #define DECL_ON_LMIC_EVENT void onEvent(ev_t e) 71 | 72 | extern u4_t AESAUX[]; 73 | extern u4_t AESKEY[]; 74 | #define AESkey ((u1_t*)AESKEY) 75 | #define AESaux ((u1_t*)AESAUX) 76 | #define FUNC_ADDR(func) (&(func)) 77 | 78 | u1_t radio_rand1 (void); 79 | #define os_getRndU1() radio_rand1() 80 | 81 | #define DEFINE_LMIC struct lmic_t LMIC 82 | #define DECLARE_LMIC extern struct lmic_t LMIC 83 | 84 | void radio_init (void); 85 | void radio_irq_handler (u1_t dio); 86 | void os_init (void); 87 | void os_runloop (void); 88 | void os_runloop_once (void); 89 | 90 | //================================================================================ 91 | 92 | 93 | #ifndef RX_RAMPUP 94 | #define RX_RAMPUP (us2osticks(2000)) 95 | #endif 96 | #ifndef TX_RAMPUP 97 | #define TX_RAMPUP (us2osticks(2000)) 98 | #endif 99 | 100 | #ifndef OSTICKS_PER_SEC 101 | #define OSTICKS_PER_SEC 32768 102 | #elif OSTICKS_PER_SEC < 10000 || OSTICKS_PER_SEC > 64516 103 | #error Illegal OSTICKS_PER_SEC - must be in range [10000:64516]. One tick must be 15.5us .. 100us long. 104 | #endif 105 | 106 | typedef s4_t ostime_t; 107 | 108 | #if !HAS_ostick_conv 109 | #define us2osticks(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC) / 1000000)) 110 | #define ms2osticks(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC) / 1000)) 111 | #define sec2osticks(sec) ((ostime_t)( (int64_t)(sec) * OSTICKS_PER_SEC)) 112 | #define osticks2ms(os) ((s4_t)(((os)*(int64_t)1000 ) / OSTICKS_PER_SEC)) 113 | #define osticks2us(os) ((s4_t)(((os)*(int64_t)1000000 ) / OSTICKS_PER_SEC)) 114 | // Special versions 115 | #define us2osticksCeil(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 999999) / 1000000)) 116 | #define us2osticksRound(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 500000) / 1000000)) 117 | #define ms2osticksCeil(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 999) / 1000)) 118 | #define ms2osticksRound(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 500) / 1000)) 119 | #endif 120 | 121 | 122 | struct osjob_t; // fwd decl. 123 | typedef void (*osjobcb_t) (struct osjob_t*); 124 | struct osjob_t { 125 | struct osjob_t* next; 126 | ostime_t deadline; 127 | osjobcb_t func; 128 | }; 129 | TYPEDEF_xref2osjob_t; 130 | 131 | 132 | #ifndef HAS_os_calls 133 | 134 | #ifndef os_getDevKey 135 | void os_getDevKey (xref2u1_t buf); 136 | #endif 137 | #ifndef os_getArtEui 138 | void os_getArtEui (xref2u1_t buf); 139 | #endif 140 | #ifndef os_getDevEui 141 | void os_getDevEui (xref2u1_t buf); 142 | #endif 143 | #ifndef os_setCallback 144 | void os_setCallback (xref2osjob_t job, osjobcb_t cb); 145 | #endif 146 | #ifndef os_setTimedCallback 147 | void os_setTimedCallback (xref2osjob_t job, ostime_t time, osjobcb_t cb); 148 | #endif 149 | #ifndef os_clearCallback 150 | void os_clearCallback (xref2osjob_t job); 151 | #endif 152 | #ifndef os_getTime 153 | ostime_t os_getTime (void); 154 | #endif 155 | #ifndef os_getTimeSecs 156 | uint os_getTimeSecs (void); 157 | #endif 158 | #ifndef os_radio 159 | void os_radio (u1_t mode); 160 | #endif 161 | #ifndef os_getBattLevel 162 | u1_t os_getBattLevel (void); 163 | #endif 164 | 165 | #ifndef os_rlsbf4 166 | //! Read 32-bit quantity from given pointer in little endian byte order. 167 | u4_t os_rlsbf4 (xref2cu1_t buf); 168 | #endif 169 | #ifndef os_wlsbf4 170 | //! Write 32-bit quntity into buffer in little endian byte order. 171 | void os_wlsbf4 (xref2u1_t buf, u4_t value); 172 | #endif 173 | #ifndef os_rmsbf4 174 | //! Read 32-bit quantity from given pointer in big endian byte order. 175 | u4_t os_rmsbf4 (xref2cu1_t buf); 176 | #endif 177 | #ifndef os_wmsbf4 178 | //! Write 32-bit quntity into buffer in big endian byte order. 179 | void os_wmsbf4 (xref2u1_t buf, u4_t value); 180 | #endif 181 | #ifndef os_rlsbf2 182 | //! Read 16-bit quantity from given pointer in little endian byte order. 183 | u2_t os_rlsbf2 (xref2cu1_t buf); 184 | #endif 185 | #ifndef os_wlsbf2 186 | //! Write 16-bit quntity into buffer in little endian byte order. 187 | void os_wlsbf2 (xref2u1_t buf, u2_t value); 188 | #endif 189 | 190 | //! Get random number (default impl for u2_t). 191 | #ifndef os_getRndU2 192 | #define os_getRndU2() ((u2_t)((os_getRndU1()<<8)|os_getRndU1())) 193 | #endif 194 | #ifndef os_crc16 195 | u2_t os_crc16 (xref2u1_t d, uint len); 196 | #endif 197 | 198 | #endif // !HAS_os_calls 199 | 200 | // ====================================================================== 201 | // Table support 202 | // These macros for defining a table of constants and retrieving values 203 | // from it makes it easier for other platforms (like AVR) to optimize 204 | // table accesses. 205 | // Use CONST_TABLE() whenever declaring or defining a table, and 206 | // TABLE_GET_xx whenever accessing its values. The actual name of the 207 | // declared variable will be modified to prevent accidental direct 208 | // access. The accessor macros forward to an inline function to allow 209 | // proper type checking of the array element type. 210 | 211 | // Helper to add a prefix to the table name 212 | #define RESOLVE_TABLE(table) constant_table_ ## table 213 | 214 | // Accessors for table elements 215 | #define TABLE_GET_U1(table, index) table_get_u1(RESOLVE_TABLE(table), index) 216 | #define TABLE_GET_S1(table, index) table_get_s1(RESOLVE_TABLE(table), index) 217 | #define TABLE_GET_U2(table, index) table_get_u2(RESOLVE_TABLE(table), index) 218 | #define TABLE_GET_S2(table, index) table_get_s2(RESOLVE_TABLE(table), index) 219 | #define TABLE_GET_U4(table, index) table_get_u4(RESOLVE_TABLE(table), index) 220 | #define TABLE_GET_S4(table, index) table_get_s4(RESOLVE_TABLE(table), index) 221 | #define TABLE_GET_OSTIME(table, index) table_get_ostime(RESOLVE_TABLE(table), index) 222 | #define TABLE_GET_U1_TWODIM(table, index1, index2) table_get_u1(RESOLVE_TABLE(table)[index1], index2) 223 | 224 | #if defined(__AVR__) 225 | #include 226 | // Macro to define the getter functions. This loads data from 227 | // progmem using pgm_read_xx, or accesses memory directly when the 228 | // index is a constant so gcc can optimize it away; 229 | #define TABLE_GETTER(postfix, type, pgm_type) \ 230 | inline type table_get ## postfix(const type *table, size_t index) { \ 231 | if (__builtin_constant_p(table[index])) \ 232 | return table[index]; \ 233 | return pgm_read_ ## pgm_type(&table[index]); \ 234 | } 235 | 236 | TABLE_GETTER(_u1, u1_t, byte); 237 | TABLE_GETTER(_s1, s1_t, byte); 238 | TABLE_GETTER(_u2, u2_t, word); 239 | TABLE_GETTER(_s2, s2_t, word); 240 | TABLE_GETTER(_u4, u4_t, dword); 241 | TABLE_GETTER(_s4, s4_t, dword); 242 | 243 | // This assumes ostime_t is 4 bytes, so error out if it is not 244 | typedef int check_sizeof_ostime_t[(sizeof(ostime_t) == 4) ? 0 : -1]; 245 | TABLE_GETTER(_ostime, ostime_t, dword); 246 | 247 | // For AVR, store constants in PROGMEM, saving on RAM usage 248 | #define CONST_TABLE(type, name) const type PROGMEM RESOLVE_TABLE(name) 249 | 250 | #define lmic_printf(fmt, ...) printf_P(PSTR(fmt), ## __VA_ARGS__) 251 | #else 252 | inline u1_t table_get_u1(const u1_t *table, size_t index) { return table[index]; } 253 | inline s1_t table_get_s1(const s1_t *table, size_t index) { return table[index]; } 254 | inline u2_t table_get_u2(const u2_t *table, size_t index) { return table[index]; } 255 | inline s2_t table_get_s2(const s2_t *table, size_t index) { return table[index]; } 256 | inline u4_t table_get_u4(const u4_t *table, size_t index) { return table[index]; } 257 | inline s4_t table_get_s4(const s4_t *table, size_t index) { return table[index]; } 258 | inline ostime_t table_get_ostime(const ostime_t *table, size_t index) { return table[index]; } 259 | 260 | // Declare a table 261 | #define CONST_TABLE(type, name) const type RESOLVE_TABLE(name) 262 | #define lmic_printf printf 263 | #endif 264 | 265 | // ====================================================================== 266 | // AES support 267 | // !!Keep in sync with lorabase.hpp!! 268 | 269 | #ifndef AES_ENC // if AES_ENC is defined as macro all other values must be too 270 | #define AES_ENC 0x00 271 | #define AES_DEC 0x01 272 | #define AES_MIC 0x02 273 | #define AES_CTR 0x04 274 | #define AES_MICNOAUX 0x08 275 | #endif 276 | #ifndef AESkey // if AESkey is defined as macro all other values must be too 277 | extern xref2u1_t AESkey; 278 | extern xref2u1_t AESaux; 279 | #endif 280 | #ifndef os_aes 281 | u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len); 282 | #endif 283 | 284 | #ifdef __cplusplus 285 | } // extern "C" 286 | #endif 287 | 288 | #endif // _oslmic_h_ 289 | -------------------------------------------------------------------------------- /src/configmanager.cpp: -------------------------------------------------------------------------------- 1 | /* configmanager persists runtime configuration using NVRAM of ESP32*/ 2 | 3 | #include "globals.h" 4 | #include 5 | #include 6 | 7 | // Local logging tag 8 | static const char *TAG = "configmanager"; 9 | 10 | nvs_handle my_handle; 11 | 12 | esp_err_t err; 13 | 14 | // defined in antenna.cpp 15 | #ifdef HAS_ANTENNA_SWITCH 16 | void antenna_select(const int8_t _ant); 17 | #endif 18 | 19 | // populate cfg vars with factory settings 20 | void defaultConfig() { 21 | cfg.lorasf = LORASFDEFAULT; // 7-12, initial lora spreadfactor defined in main.h 22 | cfg.txpower = 15; // 2-15, lora tx power 23 | cfg.adrmode = 1; // 0=disabled, 1=enabled 24 | cfg.screensaver = 0; // 0=disabled, 1=enabled 25 | cfg.screenon = 1; // 0=disbaled, 1=enabled 26 | cfg.countermode = 0; // 0=cyclic, 1=cumulative, 2=cyclic confirmed 27 | cfg.rssilimit = 0; // threshold for rssilimiter, negative value! 28 | cfg.wifiscancycle = SEND_SECS; // wifi scan cycle [seconds/2] 29 | cfg.wifichancycle = WIFI_CHANNEL_SWITCH_INTERVAL; // wifi channel switch cycle [seconds/100] 30 | cfg.blescantime = BLESCANTIME; // BLE scan cycle duration [seconds] 31 | cfg.blescan = 1; // 0=disabled, 1=enabled 32 | cfg.wifiant = 0; // 0=internal, 1=external (for LoPy/LoPy4) 33 | cfg.vendorfilter = 1; // 0=disabled, 1=enabled 34 | cfg.rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%) 35 | 36 | strncpy( cfg.version, PROGVERSION, sizeof(cfg.version)-1 ); 37 | } 38 | 39 | void open_storage() { 40 | err = nvs_flash_init(); 41 | if (err == ESP_ERR_NVS_NO_FREE_PAGES) { 42 | // NVS partition was truncated and needs to be erased 43 | // Retry nvs_flash_init 44 | ESP_ERROR_CHECK(nvs_flash_erase()); 45 | err = nvs_flash_init(); 46 | } 47 | ESP_ERROR_CHECK( err ); 48 | 49 | // Open 50 | ESP_LOGI(TAG, "Opening NVS"); 51 | err = nvs_open("config", NVS_READWRITE, &my_handle); 52 | if (err != ESP_OK) 53 | ESP_LOGI(TAG, "Error (%d) opening NVS handle", err); 54 | else 55 | ESP_LOGI(TAG, "Done"); 56 | } 57 | 58 | // erase all keys and values in NVRAM 59 | void eraseConfig() { 60 | ESP_LOGI(TAG, "Clearing settings in NVS"); 61 | open_storage(); 62 | if (err == ESP_OK) { 63 | nvs_erase_all(my_handle); 64 | nvs_commit(my_handle); 65 | nvs_close(my_handle); 66 | ESP_LOGI(TAG, "Done");} 67 | else { 68 | ESP_LOGW(TAG, "NVS erase failed"); } 69 | } 70 | 71 | // save current configuration from RAM to NVRAM 72 | void saveConfig() { 73 | ESP_LOGI(TAG, "Storing settings in NVS"); 74 | open_storage(); 75 | if (err == ESP_OK) { 76 | int8_t flash8 = 0; 77 | int16_t flash16 = 0; 78 | size_t required_size; 79 | char storedversion[10]; 80 | 81 | if( nvs_get_str(my_handle, "version", storedversion, &required_size) != ESP_OK || strcmp(storedversion, cfg.version) != 0 ) 82 | nvs_set_str(my_handle, "version", cfg.version); 83 | 84 | if( nvs_get_i8(my_handle, "lorasf", &flash8) != ESP_OK || flash8 != cfg.lorasf ) 85 | nvs_set_i8(my_handle, "lorasf", cfg.lorasf); 86 | 87 | if( nvs_get_i8(my_handle, "txpower", &flash8) != ESP_OK || flash8 != cfg.txpower ) 88 | nvs_set_i8(my_handle, "txpower", cfg.txpower); 89 | 90 | if( nvs_get_i8(my_handle, "adrmode", &flash8) != ESP_OK || flash8 != cfg.adrmode ) 91 | nvs_set_i8(my_handle, "adrmode", cfg.adrmode); 92 | 93 | if( nvs_get_i8(my_handle, "screensaver", &flash8) != ESP_OK || flash8 != cfg.screensaver ) 94 | nvs_set_i8(my_handle, "screensaver", cfg.screensaver); 95 | 96 | if( nvs_get_i8(my_handle, "screenon", &flash8) != ESP_OK || flash8 != cfg.screenon ) 97 | nvs_set_i8(my_handle, "screenon", cfg.screenon); 98 | 99 | if( nvs_get_i8(my_handle, "countermode", &flash8) != ESP_OK || flash8 != cfg.countermode ) 100 | nvs_set_i8(my_handle, "countermode", cfg.countermode); 101 | 102 | if( nvs_get_i8(my_handle, "wifiscancycle", &flash8) != ESP_OK || flash8 != cfg.wifiscancycle ) 103 | nvs_set_i8(my_handle, "wifiscancycle", cfg.wifiscancycle); 104 | 105 | if( nvs_get_i8(my_handle, "wifichancycle", &flash8) != ESP_OK || flash8 != cfg.wifichancycle ) 106 | nvs_set_i8(my_handle, "wifichancycle", cfg.wifichancycle); 107 | 108 | if( nvs_get_i8(my_handle, "blescantime", &flash8) != ESP_OK || flash8 != cfg.blescantime ) 109 | nvs_set_i8(my_handle, "blescantime", cfg.blescantime); 110 | 111 | if( nvs_get_i8(my_handle, "blescanmode", &flash8) != ESP_OK || flash8 != cfg.blescan ) 112 | nvs_set_i8(my_handle, "blescanmode", cfg.blescan); 113 | 114 | if( nvs_get_i8(my_handle, "wifiant", &flash8) != ESP_OK || flash8 != cfg.wifiant ) 115 | nvs_set_i8(my_handle, "wifiant", cfg.wifiant); 116 | 117 | if( nvs_get_i8(my_handle, "vendorfilter", &flash8) != ESP_OK || flash8 != cfg.vendorfilter ) 118 | nvs_set_i8(my_handle, "vendorfilter", cfg.vendorfilter); 119 | 120 | if( nvs_get_i8(my_handle, "rgblum", &flash8) != ESP_OK || flash8 != cfg.rgblum ) 121 | nvs_set_i8(my_handle, "rgblum", cfg.rgblum); 122 | 123 | if( nvs_get_i16(my_handle, "rssilimit", &flash16) != ESP_OK || flash16 != cfg.rssilimit ) 124 | nvs_set_i16(my_handle, "rssilimit", cfg.rssilimit); 125 | 126 | err = nvs_commit(my_handle); 127 | nvs_close(my_handle); 128 | if ( err == ESP_OK ) { 129 | ESP_LOGI(TAG, "Done"); 130 | } else { 131 | ESP_LOGW(TAG, "NVS config write failed"); 132 | } 133 | } else { 134 | ESP_LOGW(TAG, "Error (%d) opening NVS handle", err); 135 | } 136 | } 137 | 138 | // set and save cfg.version 139 | void migrateVersion() { 140 | sprintf(cfg.version, "%s", PROGVERSION); 141 | ESP_LOGI(TAG, "version set to %s", cfg.version); 142 | saveConfig(); 143 | } 144 | 145 | // load configuration from NVRAM into RAM and make it current 146 | void loadConfig() { 147 | defaultConfig(); // start with factory settings 148 | ESP_LOGI(TAG, "Reading settings from NVS"); 149 | open_storage(); 150 | if (err != ESP_OK) { 151 | ESP_LOGW(TAG,"Error (%d) opening NVS handle, storing defaults", err); 152 | saveConfig(); } // saves factory settings to NVRAM 153 | else { 154 | int8_t flash8 = 0; 155 | int16_t flash16 = 0; 156 | size_t required_size; 157 | 158 | // check if configuration stored in NVRAM matches PROGVERSION 159 | if( nvs_get_str(my_handle, "version", NULL, &required_size) == ESP_OK ) { 160 | nvs_get_str(my_handle, "version", cfg.version, &required_size); 161 | ESP_LOGI(TAG, "NVRAM settings version = %s", cfg.version); 162 | if (strcmp(cfg.version, PROGVERSION)) { 163 | ESP_LOGI(TAG, "migrating NVRAM settings to new version %s", PROGVERSION); 164 | nvs_close(my_handle); 165 | migrateVersion(); 166 | } 167 | } else { 168 | ESP_LOGI(TAG, "new version %s, deleting NVRAM settings", PROGVERSION); 169 | nvs_close(my_handle); 170 | eraseConfig(); 171 | migrateVersion(); 172 | } 173 | 174 | // overwrite defaults with valid values from NVRAM 175 | if( nvs_get_i8(my_handle, "lorasf", &flash8) == ESP_OK ) { 176 | cfg.lorasf = flash8; 177 | ESP_LOGI(TAG, "lorasf = %i", flash8); 178 | } else { 179 | ESP_LOGI(TAG, "lorasf set to default %i", cfg.lorasf); 180 | saveConfig(); 181 | } 182 | 183 | if( nvs_get_i8(my_handle, "txpower", &flash8) == ESP_OK ) { 184 | cfg.txpower = flash8; 185 | ESP_LOGI(TAG, "txpower = %i", flash8); 186 | } else { 187 | ESP_LOGI(TAG, "txpower set to default %i", cfg.txpower); 188 | saveConfig(); 189 | } 190 | 191 | if( nvs_get_i8(my_handle, "adrmode", &flash8) == ESP_OK ) { 192 | cfg.adrmode = flash8; 193 | ESP_LOGI(TAG, "adrmode = %i", flash8); 194 | } else { 195 | ESP_LOGI(TAG, "adrmode set to default %i", cfg.adrmode); 196 | saveConfig(); 197 | } 198 | 199 | if( nvs_get_i8(my_handle, "screensaver", &flash8) == ESP_OK ) { 200 | cfg.screensaver = flash8; 201 | ESP_LOGI(TAG, "screensaver = %i", flash8); 202 | } else { 203 | ESP_LOGI(TAG, "screensaver set to default %i", cfg.screensaver); 204 | saveConfig(); 205 | } 206 | 207 | if( nvs_get_i8(my_handle, "screenon", &flash8) == ESP_OK ) { 208 | cfg.screenon = flash8; 209 | ESP_LOGI(TAG, "screenon = %i", flash8); 210 | } else { 211 | ESP_LOGI(TAG, "screenon set to default %i", cfg.screenon); 212 | saveConfig(); 213 | } 214 | 215 | if( nvs_get_i8(my_handle, "countermode", &flash8) == ESP_OK ) { 216 | cfg.countermode = flash8; 217 | ESP_LOGI(TAG, "countermode = %i", flash8); 218 | } else { 219 | ESP_LOGI(TAG, "countermode set to default %i", cfg.countermode); 220 | saveConfig(); 221 | } 222 | 223 | if( nvs_get_i8(my_handle, "wifiscancycle", &flash8) == ESP_OK ) { 224 | cfg.wifiscancycle = flash8; 225 | ESP_LOGI(TAG, "wifiscancycle = %i", flash8); 226 | } else { 227 | ESP_LOGI(TAG, "WIFI scan cycle set to default %i", cfg.wifiscancycle); 228 | saveConfig(); 229 | } 230 | 231 | if( nvs_get_i8(my_handle, "wifichancycle", &flash8) == ESP_OK ) { 232 | cfg.wifichancycle = flash8; 233 | ESP_LOGI(TAG, "wifichancycle = %i", flash8); 234 | } else { 235 | ESP_LOGI(TAG, "WIFI channel cycle set to default %i", cfg.wifichancycle); 236 | saveConfig(); 237 | } 238 | 239 | if( nvs_get_i8(my_handle, "wifiant", &flash8) == ESP_OK ) { 240 | cfg.wifiant = flash8; 241 | ESP_LOGI(TAG, "wifiantenna = %i", flash8); 242 | } else { 243 | ESP_LOGI(TAG, "WIFI antenna switch set to default %i", cfg.wifiant); 244 | saveConfig(); 245 | } 246 | 247 | if( nvs_get_i8(my_handle, "vendorfilter", &flash8) == ESP_OK ) { 248 | cfg.vendorfilter = flash8; 249 | ESP_LOGI(TAG, "vendorfilter = %i", flash8); 250 | } else { 251 | ESP_LOGI(TAG, "Vendorfilter mode set to default %i", cfg.vendorfilter); 252 | saveConfig(); 253 | } 254 | 255 | if( nvs_get_i8(my_handle, "rgblum", &flash8) == ESP_OK ) { 256 | cfg.rgblum = flash8; 257 | ESP_LOGI(TAG, "rgbluminosity = %i", flash8); 258 | } else { 259 | ESP_LOGI(TAG, "RGB luminosity set to default %i", cfg.rgblum); 260 | saveConfig(); 261 | } 262 | 263 | if( nvs_get_i8(my_handle, "blescantime", &flash8) == ESP_OK ) { 264 | cfg.blescantime = flash8; 265 | ESP_LOGI(TAG, "blescantime = %i", flash8); 266 | } else { 267 | ESP_LOGI(TAG, "BLEscantime set to default %i", cfg.blescantime); 268 | saveConfig(); 269 | } 270 | 271 | if( nvs_get_i8(my_handle, "blescanmode", &flash8) == ESP_OK ) { 272 | cfg.blescan = flash8; 273 | ESP_LOGI(TAG, "BLEscanmode = %i", flash8); 274 | } else { 275 | ESP_LOGI(TAG, "BLEscanmode set to default %i", cfg.blescan); 276 | saveConfig(); 277 | } 278 | 279 | if( nvs_get_i16(my_handle, "rssilimit", &flash16) == ESP_OK ) { 280 | cfg.rssilimit = flash16; 281 | ESP_LOGI(TAG, "rssilimit = %i", flash16); 282 | } else { 283 | ESP_LOGI(TAG, "rssilimit set to default %i", cfg.rssilimit); 284 | saveConfig(); 285 | } 286 | 287 | nvs_close(my_handle); 288 | ESP_LOGI(TAG, "Done"); 289 | 290 | // put actions to be triggered after config loaded here 291 | 292 | #ifdef HAS_ANTENNA_SWITCH // set antenna type, if device has one 293 | antenna_select(cfg.wifiant); 294 | #endif 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/aes/ideetron/AES-128_V10.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************************** 2 | #if defined(USE_IDEETRON_AES) 3 | * Copyright 2015, 2016 Ideetron B.V. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public License 16 | * along with this program. If not, see . 17 | ******************************************************************************************/ 18 | /****************************************************************************************** 19 | * 20 | * File: AES-128_V10.cpp 21 | * Author: Gerben den Hartog 22 | * Compagny: Ideetron B.V. 23 | * Website: http://www.ideetron.nl/LoRa 24 | * E-mail: info@ideetron.nl 25 | ******************************************************************************************/ 26 | /**************************************************************************************** 27 | * 28 | * Created on: 20-10-2015 29 | * Supported Hardware: ID150119-02 Nexus board with RFM95 30 | * 31 | * Firmware Version 1.0 32 | * First version 33 | ****************************************************************************************/ 34 | 35 | // This file was taken from 36 | // https://github.com/Ideetron/RFM95W_Nexus/tree/master/LoRaWAN_V31 for 37 | // use with LMIC. It was only cosmetically modified: 38 | // - AES_Encrypt was renamed to lmic_aes_encrypt. 39 | // - All other functions and variables were made static 40 | // - Tabs were converted to 2 spaces 41 | // - An #include and #if guard was added 42 | // - S_Table is now stored in PROGMEM 43 | 44 | #include "../../lmic/oslmic.h" 45 | 46 | #if defined(USE_IDEETRON_AES) 47 | 48 | /* 49 | ******************************************************************************************** 50 | * Global Variables 51 | ******************************************************************************************** 52 | */ 53 | 54 | static unsigned char State[4][4]; 55 | 56 | static CONST_TABLE(unsigned char, S_Table)[16][16] = { 57 | {0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76}, 58 | {0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0}, 59 | {0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15}, 60 | {0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75}, 61 | {0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84}, 62 | {0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF}, 63 | {0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8}, 64 | {0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2}, 65 | {0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73}, 66 | {0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB}, 67 | {0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79}, 68 | {0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08}, 69 | {0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A}, 70 | {0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E}, 71 | {0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF}, 72 | {0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16} 73 | }; 74 | 75 | extern "C" void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key); 76 | static void AES_Add_Round_Key(unsigned char *Round_Key); 77 | static unsigned char AES_Sub_Byte(unsigned char Byte); 78 | static void AES_Shift_Rows(); 79 | static void AES_Mix_Collums(); 80 | static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key); 81 | static void Send_State(); 82 | 83 | /* 84 | ***************************************************************************************** 85 | * Description : Function for encrypting data using AES-128 86 | * 87 | * Arguments : *Data Data to encrypt is a 16 byte long arry 88 | * *Key Key to encrypt data with is a 16 byte long arry 89 | ***************************************************************************************** 90 | */ 91 | void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key) 92 | { 93 | unsigned char i; 94 | unsigned char Row,Collum; 95 | unsigned char Round = 0x00; 96 | unsigned char Round_Key[16]; 97 | 98 | //Copy input to State arry 99 | for(Collum = 0; Collum < 4; Collum++) 100 | { 101 | for(Row = 0; Row < 4; Row++) 102 | { 103 | State[Row][Collum] = Data[Row + (4*Collum)]; 104 | } 105 | } 106 | 107 | //Copy key to round key 108 | for(i = 0; i < 16; i++) 109 | { 110 | Round_Key[i] = Key[i]; 111 | } 112 | 113 | //Add round key 114 | AES_Add_Round_Key(Round_Key); 115 | 116 | //Preform 9 full rounds 117 | for(Round = 1; Round < 10; Round++) 118 | { 119 | //Preform Byte substitution with S table 120 | for(Collum = 0; Collum < 4; Collum++) 121 | { 122 | for(Row = 0; Row < 4; Row++) 123 | { 124 | State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]); 125 | } 126 | } 127 | 128 | //Preform Row Shift 129 | AES_Shift_Rows(); 130 | 131 | //Mix Collums 132 | AES_Mix_Collums(); 133 | 134 | //Calculate new round key 135 | AES_Calculate_Round_Key(Round,Round_Key); 136 | 137 | //Add round key 138 | AES_Add_Round_Key(Round_Key); 139 | } 140 | 141 | //Last round whitout mix collums 142 | //Preform Byte substitution with S table 143 | for(Collum = 0; Collum < 4; Collum++) 144 | { 145 | for(Row = 0; Row < 4; Row++) 146 | { 147 | State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]); 148 | } 149 | } 150 | 151 | //Shift rows 152 | AES_Shift_Rows(); 153 | 154 | //Calculate new round key 155 | AES_Calculate_Round_Key(Round,Round_Key); 156 | 157 | //Add round Key 158 | AES_Add_Round_Key(Round_Key); 159 | 160 | //Copy the State into the data array 161 | for(Collum = 0; Collum < 4; Collum++) 162 | { 163 | for(Row = 0; Row < 4; Row++) 164 | { 165 | Data[Row + (4*Collum)] = State[Row][Collum]; 166 | } 167 | } 168 | 169 | } 170 | 171 | /* 172 | ***************************************************************************************** 173 | * Description : Function that add's the round key for the current round 174 | * 175 | * Arguments : *Round_Key 16 byte long array holding the Round Key 176 | ***************************************************************************************** 177 | */ 178 | static void AES_Add_Round_Key(unsigned char *Round_Key) 179 | { 180 | unsigned char Row,Collum; 181 | 182 | for(Collum = 0; Collum < 4; Collum++) 183 | { 184 | for(Row = 0; Row < 4; Row++) 185 | { 186 | State[Row][Collum] = State[Row][Collum] ^ Round_Key[Row + (4*Collum)]; 187 | } 188 | } 189 | } 190 | 191 | /* 192 | ***************************************************************************************** 193 | * Description : Function that substitutes a byte with a byte from the S_Table 194 | * 195 | * Arguments : Byte The byte that will be substituted 196 | * 197 | * Return : The return is the found byte in the S_Table 198 | ***************************************************************************************** 199 | */ 200 | static unsigned char AES_Sub_Byte(unsigned char Byte) 201 | { 202 | unsigned char S_Row,S_Collum; 203 | unsigned char S_Byte; 204 | 205 | //Split byte up in Row and Collum 206 | S_Row = ((Byte >> 4) & 0x0F); 207 | S_Collum = (Byte & 0x0F); 208 | 209 | //Find the correct byte in the S_Table 210 | S_Byte = TABLE_GET_U1_TWODIM(S_Table, S_Row, S_Collum); 211 | 212 | return S_Byte; 213 | } 214 | 215 | /* 216 | ***************************************************************************************** 217 | * Description : Function that preforms the shift row operation described in the AES standard 218 | ***************************************************************************************** 219 | */ 220 | static void AES_Shift_Rows() 221 | { 222 | unsigned char Buffer; 223 | 224 | //Row 0 doesn't change 225 | 226 | //Shift Row 1 one left 227 | //Store firt byte in buffer 228 | Buffer = State[1][0]; 229 | //Shift all bytes 230 | State[1][0] = State[1][1]; 231 | State[1][1] = State[1][2]; 232 | State[1][2] = State[1][3]; 233 | State[1][3] = Buffer; 234 | 235 | //Shift row 2 two left 236 | Buffer = State[2][0]; 237 | State[2][0] = State[2][2]; 238 | State[2][2] = Buffer; 239 | Buffer = State[2][1]; 240 | State[2][1] = State[2][3]; 241 | State[2][3] = Buffer; 242 | 243 | //Shift row 3 three left 244 | Buffer = State[3][3]; 245 | State[3][3] = State[3][2]; 246 | State[3][2] = State[3][1]; 247 | State[3][1] = State[3][0]; 248 | State[3][0] = Buffer; 249 | } 250 | 251 | /* 252 | ***************************************************************************************** 253 | * Description : Function that preforms the Mix Collums operation described in the AES standard 254 | ***************************************************************************************** 255 | */ 256 | static void AES_Mix_Collums() 257 | { 258 | unsigned char Row,Collum; 259 | unsigned char a[4], b[4]; 260 | for(Collum = 0; Collum < 4; Collum++) 261 | { 262 | for(Row = 0; Row < 4; Row++) 263 | { 264 | a[Row] = State[Row][Collum]; 265 | b[Row] = (State[Row][Collum] << 1); 266 | 267 | if((State[Row][Collum] & 0x80) == 0x80) 268 | { 269 | b[Row] = b[Row] ^ 0x1B; 270 | } 271 | } 272 | State[0][Collum] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; 273 | State[1][Collum] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; 274 | State[2][Collum] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; 275 | State[3][Collum] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; 276 | } 277 | } 278 | 279 | /* 280 | ***************************************************************************************** 281 | * Description : Function that calculaties the round key for the current round 282 | * 283 | * Arguments : Round Number of current Round 284 | * *Round_Key 16 byte long array holding the Round Key 285 | ***************************************************************************************** 286 | */ 287 | static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key) 288 | { 289 | unsigned char i,j; 290 | unsigned char b; 291 | unsigned char Temp[4]; 292 | unsigned char Buffer; 293 | unsigned char Rcon; 294 | 295 | //Calculate first Temp 296 | //Copy laste byte from previous key 297 | for(i = 0; i < 4; i++) 298 | { 299 | Temp[i] = Round_Key[i+12]; 300 | } 301 | 302 | //Rotate Temp 303 | Buffer = Temp[0]; 304 | Temp[0] = Temp[1]; 305 | Temp[1] = Temp[2]; 306 | Temp[2] = Temp[3]; 307 | Temp[3] = Buffer; 308 | 309 | //Substitute Temp 310 | for(i = 0; i < 4; i++) 311 | { 312 | Temp[i] = AES_Sub_Byte(Temp[i]); 313 | } 314 | 315 | //Calculate Rcon 316 | Rcon = 0x01; 317 | while(Round != 1) 318 | { 319 | b = Rcon & 0x80; 320 | Rcon = Rcon << 1; 321 | if(b == 0x80) 322 | { 323 | Rcon = Rcon ^ 0x1b; 324 | } 325 | Round--; 326 | } 327 | 328 | //XOR Rcon 329 | Temp[0] = Temp[0] ^ Rcon; 330 | 331 | //Calculate new key 332 | for(i = 0; i < 4; i++) 333 | { 334 | for(j = 0; j < 4; j++) 335 | { 336 | Round_Key[j + (4*i)] = Round_Key[j + (4*i)] ^ Temp[j]; 337 | Temp[j] = Round_Key[j + (4*i)]; 338 | } 339 | } 340 | } 341 | 342 | #endif // defined(USE_IDEETRON_AES) 343 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32-Paxcounter 2 | **Wifi & Bluetooth driven, LoRaWAN enabled, battery powered mini Paxcounter built on cheap ESP32 boards** 3 | 4 | ---> check branch "development" for latest alpha version <--- 5 | 6 | 7 | 8 | 9 | 10 | 11 | # Use case 12 | 13 | Paxcounter is a proof-of-concept device for metering passenger flows in realtime. It counts how many mobile devices are around. This gives an estimation how many people are around. Paxcounter detects Wifi and Bluetooth signals in the air, focusing on mobile devices by filtering vendor OUIs in the MAC adress. 14 | 15 | Intention of this project is to do this without intrusion in privacy: You don't need to track people owned devices, if you just want to count them. Therefore, Paxcounter does not persistenly store MAC adresses and does no kind of fingerprinting the scanned devices. 16 | 17 | Bonus of this project is that metered data is transferred via a LoRaWAN network, not via usual GSM/LTE or Wifi uplink. 18 | 19 | You can build this project battery powered and reach a full day uptime with a single 18650 Li-Ion cell. 20 | 21 | This can all be done with a single small and cheap ESP32 board for less than $20. 22 | 23 | # Hardware 24 | 25 | Currently supported IoT boards: 26 | - Heltec LoRa-32 {1} 27 | - TTGOv1 {1} 28 | - TTGOv2 {1}{4} 29 | - Pycom LoPy {2} 30 | - Pycom LoPy4 {2} 31 | - LoLin32 with [LoraNode32 shield](https://github.com/hallard/LoLin32-Lora) {2}{3} 32 | - LoLin32 Lite with [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora) {2}{3} 33 | 34 | {1} on board OLED Display supported; {2} on board RGB LED supported; {3} on board Hardware unique DEVEUI supported; {4} special wiring needed, see instructions in /hal/ttgov2.h 35 | 36 | Target platform must be selected in [platformio.ini](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/platformio.ini).
37 | Hardware dependent settings (pinout etc.) are stored in board files in /hal directory.
38 | 39 | 3D printable cases can be found (and, if wanted so, ordered) on Thingiverse, see 40 | Heltec and TTGOv2, for example.
41 | 42 | Power consumption: 43 | 44 | - Heltec ~720mW 45 | - TTGOv1 TBD 46 | - TTGOv2 ~990mW 47 | - LoPy with expansion board: ~690mW 48 | - LoPy pure, without expansion board: TBD 49 | - LoLin32 with [LoraNode32 shield](https://github.com/hallard/LoLin32-Lora): TBD 50 | - LoLin32 Lite with [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora): TBD 51 | 52 | These results where metered with software version 1.2.97 while continuously scanning wifi and ble, no LoRa TX’ing, OLED display (if present) on, 5V USB powered. 53 | 54 | # Building 55 | 56 | Use PlatformIO with your preferred IDE for development and building this code. 57 | 58 | Before compiling the code, **create file loraconf.h in your local /src directory** using the template [loraconf.sample.h](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/loraconf.sample.h) and populate it with your personal APPEUI und APPKEY for the LoRaWAN network. If you're using popular TheThingsNetwork you can copy&paste the keys from TTN console or output of ttnctl. 59 | 60 | To join the network only method OTAA is supported, not ABP. The DEVEUI for OTAA will be derived from the device's MAC adress during device startup and is shown as well on the device's display (if it has one) as on the serial console for copying it to your LoRaWAN network server settings. 61 | 62 | If your device has a fixed DEVEUI enter this in your local loraconf.h file. During compile time this DEVEUI will be grabbed from loraconf.h and inserted in the code. 63 | 64 | If your device has silicon **Unique ID** which is stored in serial EEPROM Microchip 24AA02E64 you don't need to change anything. The Unique ID will be read during startup and DEVEUI will be generated from it, overriding settings in loraconf.h. 65 | 66 | # Uploading 67 | 68 | To upload the code to your ESP32 board this needs to be switched from run to bootloader mode. Boards with USB bridge like Heltec and TTGO usually have an onboard logic which allows soft switching by the upload tool. In PlatformIO this happenes automatically.

69 | The LoPy/LoPy4 board needs to be set manually. See these 70 | instructions how to do it. Don't forget to press on board reset button after switching between run and bootloader mode.

71 | The original Pycom firmware is not needed, so there is no need to update it before flashing Paxcounter. Just flash the compiled paxcounter binary (.elf file) on your LoPy/LoPy4. If you later want to go back to the Pycom firmware, download the firmware from Pycom and flash it over. 72 | 73 | # Legal note 74 | 75 | **Depending on your country's laws it may be illegal to sniff wireless networks for MAC addresses. Please check and respect your country's laws before using this code!** 76 | 77 | (e.g. US citizens may want to check [Section 18 U.S. Code § 2511](https://www.law.cornell.edu/uscode/text/18/2511) and [discussion](https://github.com/schollz/howmanypeoplearearound/issues/4) on this) 78 | 79 | (e.g. UK citizens may want to check [Data Protection Act 1998](https://ico.org.uk/media/1560691/wi-fi-location-analytics-guidance.pdf) and [GDPR 2018](https://ico.org.uk/for-organisations/guide-to-the-general-data-protection-regulation-gdpr/key-definitions/)) 80 | 81 | (e.g. Citizens in the the Netherlands may want to read [this article](https://www.ivir.nl/publicaties/download/PrivacyInformatie_2016_6.pdf)) 82 | 83 | Note: If you use this software you do this at your own risk. That means that you alone - not the authors of this software - are responsible for the legal compliance of an application using this or build from this software and/or usage of a device created using this software. You should take special care and get prior legal advice if you plan metering passengers in public areas and/or publish data drawn from doing so. 84 | 85 | # Privacy disclosure 86 | 87 | Paxcounter generates identifiers for sniffed MAC adresses and collects them temporary in the device's RAM for a configurable scan cycle time (default 240 seconds). After each scan cycle the collected identifiers are cleared. Identifiers are generated by salting and hashing MAC adresses. The random salt value changes after each scan cycle. Identifiers and MAC adresses are never transferred to the LoRaWAN network. No persistent storing of MAC adresses, identifiers or timestamps and no other kind of analytics than counting are implemented in this code. Wireless networks are not touched by this code, but MAC adresses from wireless devices as well within as not within wireless networks, regardless if encrypted or unencrypted, are sniffed and processed by this code. If the bluetooth option in the code is enabled, bluetooth MACs are scanned and processed by the included BLE stack, then hashed and counted by this code. 88 | 89 | # Payload format description 90 | 91 | FPort1: 92 | 93 | byte 1: 16-bit WiFi counter, MSB 94 | byte 2: 16-bit WiFi counter, LSB 95 | byte 3: 16-bit BLE counter, MSB 96 | byte 4: 16-bit BLE counter, LSB 97 | 98 | FPort2: 99 | 100 | see remote command set 101 | 102 | # Remote command set 103 | 104 | The device listenes for remote control commands on LoRaWAN Port 2. 105 | Each command is followed by exactly one parameter. 106 | Multiple command/parameter pairs can be concatenated and sent in one single payload downlink. 107 | 108 | Note: all settings are stored in NVRAM and will be reloaded when device starts. To reset device to factory settings press button (if device has one), or send remote command 09 02 09 00 unconfirmed(!) once. 109 | 110 | 0x01 set scan RSSI limit 111 | 112 | 1 ... 255 used for wifi and bluetooth scan radius (greater values increase scan radius, values 50...110 make sense) 113 | 0 = RSSI limiter disabled [default] 114 | 115 | 0x02 set counter mode 116 | 117 | 0 = cyclic unconfirmed, mac counter reset after each wifi scan cycle, data is sent only once [default] 118 | 1 = cumulative counter, mac counter is never reset 119 | 2 = cyclic confirmed, like 0 but data is resent until confirmation by network received 120 | 121 | 0x03 (NOT YET IMPLEMENTED) set screen saver mode 122 | 123 | 0 = screen saver off [default] 124 | 1 = screen saver on 125 | 126 | 0x04 set display on/off 127 | 128 | 0 = display off 129 | 1 = display on [default] 130 | 131 | 0x05 set LoRa spread factor 132 | 133 | 7 ... 12 [default: 9] 134 | 135 | 0x06 set LoRa TXpower 136 | 137 | 2 ... 15 [default: 15] 138 | 139 | 0x07 set LoRa Adaptive Data Rate mode 140 | 141 | 0 = ADR off 142 | 1 = ADR on [default] 143 | 144 | note: set ADR to off, if device is moving, set to on, if not. 145 | 146 | 0x08 do nothing 147 | 148 | useful to clear pending commands from LoRaWAN server quere, or to check RSSI on device 149 | 150 | 0x09 reset functions 151 | 152 | 0 = restart device 153 | 1 = reset MAC counter to zero 154 | 2 = reset device to factory settings 155 | 156 | 0x0A set Wifi scan cycle and payload transmit cycle 157 | 158 | 0 ... 255 duration of a wifi scan cycle in seconds/2, after this payload is sent 159 | e.g. 120 -> 1 cycle runs for 240 seconds [default] 160 | 161 | 0x0B set Wifi channel switch interval timer 162 | 163 | 0 ... 255 timeout for scanning 1 wifi channel in seconds/100 164 | e.g. 50 -> each channel is scanned for 0,5 seconds [default] 165 | 166 | 0x0C set BLE scan cycle timer 167 | 168 | 0 ... 255 duration of a BLE scan cycle in seconds 169 | e.g. 11 -> 1 cycle runs for 11 seconds [default] 170 | 171 | 0x0D (NOT YET IMPLEMENTED) set BLE and WIFI vendorfilter mode 172 | 173 | 0 = disabled (use to count devices, not people) 174 | 1 = enabled [default] 175 | 176 | 0x0E set BLE scan mode 177 | 178 | 0 = disabled 179 | 1 = enabled [default] 180 | 181 | 0x0F set WIFI antenna switch (works on LoPy/LoPy4 only) 182 | 183 | 0 = internal antenna [default] 184 | 1 = external antenna 185 | 186 | 0x10 set RGB led luminosity (works on LoPy/LoPy4 and LoRaNode32 shield only) 187 | 188 | 0 ... 100 percentage of luminosity (100% = full light) 189 | e.g. 50 -> 50% of luminosity [default] 190 | 191 | 0x80 get device configuration 192 | 193 | device answers with it's current configuration. The configuration is a C structure declared in file [globals.h](src/globals.h#L24-L41) with the following definition: 194 | 195 | byte 1: Lora SF (7..12) 196 | byte 2: Lora TXpower (2..15) 197 | byte 3: Lora ADR (1=on, 0=off) 198 | byte 4: Screensaver status (1=on, 0=off) 199 | byte 5: Display status (1=on, 0=off) 200 | byte 6: Counter mode (0=cyclic unconfirmed, 1=cumulative, 2=cyclic confirmed) 201 | bytes 7-8: RSSI limiter threshold value (negative) 202 | byte 9: Wifi scan cycle duration in seconds/2 (0..255) 203 | byte 10: Wifi channel switch interval in seconds/100 (0..255) 204 | byte 11: BLE scan cycle duration in seconds (0..255) 205 | byte 12: BLE scan mode (1=on, 0=0ff) 206 | byte 13: Wifi antenna switch (0=internal, 1=external) 207 | byte 14: Vendorfilter mode (0=disabled, 1=enabled) 208 | byte 15: RGB LED luminosity (0..100 %) 209 | bytes 16-25: Software version (ASCII format) 210 | 211 | 0x81 get device uptime 212 | 213 | bytes 1-7: Uptime in seconds (little endian format) 214 | 215 | 0x82 get device cpu temperature 216 | 217 | bytes 1-3: chip temperature in celsius (little endian format) 218 | 219 | # RGB Led color description 220 | 221 | Description of the RGB LED color (LoPy/LoPy4 and Lolin32 only): 222 | 223 | - Yellow quick blink: joining LoRaWAN network in progress or pending 224 | - Blue blink: LoRaWAN data transmit (including waiting for receive windows) in progress or pending 225 | - Green each blink: seen a new Wifi device 226 | - Magenta each blink: seen a new BLE device 227 | 228 | # License 229 | 230 | Copyright 2018 Oliver Brandmueller 231 | 232 | Copyright 2018 Klaus Wilting 233 | 234 | Licensed under the Apache License, Version 2.0 (the "License"); 235 | you may not use this file except in compliance with the License. 236 | You may obtain a copy of the License at 237 | 238 | http://www.apache.org/licenses/LICENSE-2.0 239 | 240 | Unless required by applicable law or agreed to in writing, software 241 | distributed under the License is distributed on an "AS IS" BASIS, 242 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 243 | See the License for the specific language governing permissions and 244 | limitations under the License. 245 | 246 | NOTICE: 247 | Parts of the source files in this repository are made available under different licenses, 248 | see file LICENSE.txt in this repository. Refer to each individual source file for more details. 249 | 250 | # Credits 251 | 252 | Thanks to Charles Hallard (https://github.com/hallard) for major contributions to this project. 253 | -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/lmic.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | *******************************************************************************/ 11 | 12 | //! @file 13 | //! @brief LMIC API 14 | 15 | #ifndef _lmic_h_ 16 | #define _lmic_h_ 17 | 18 | #include "oslmic.h" 19 | #include "lorabase.h" 20 | 21 | #ifdef __cplusplus 22 | extern "C"{ 23 | #endif 24 | 25 | // LMIC version 26 | #define LMIC_VERSION_MAJOR 1 27 | #define LMIC_VERSION_MINOR 5 28 | #define LMIC_VERSION_BUILD 1431528305 29 | 30 | enum { MAX_FRAME_LEN = 64 }; //!< Library cap on max frame length 31 | enum { TXCONF_ATTEMPTS = 8 }; //!< Transmit attempts for confirmed frames 32 | enum { MAX_MISSED_BCNS = 20 }; // threshold for triggering rejoin requests 33 | enum { MAX_RXSYMS = 100 }; // stop tracking beacon beyond this 34 | 35 | enum { LINK_CHECK_CONT = 12 , // continue with this after reported dead link 36 | LINK_CHECK_DEAD = 24 , // after this UP frames and no response from NWK assume link is dead 37 | LINK_CHECK_INIT = -12 , // UP frame count until we inc datarate 38 | LINK_CHECK_OFF =-128 }; // link check disabled 39 | 40 | enum { TIME_RESYNC = 6*128 }; // secs 41 | enum { TXRX_GUARD_ms = 6000 }; // msecs - don't start TX-RX transaction before beacon 42 | enum { JOIN_GUARD_ms = 9000 }; // msecs - don't start Join Req/Acc transaction before beacon 43 | enum { TXRX_BCNEXT_secs = 2 }; // secs - earliest start after beacon time 44 | enum { RETRY_PERIOD_secs = 3 }; // secs - random period for retrying a confirmed send 45 | 46 | #if defined(CFG_eu868) // EU868 spectrum ==================================================== 47 | 48 | enum { MAX_CHANNELS = 16 }; //!< Max supported channels 49 | enum { MAX_BANDS = 4 }; 50 | 51 | enum { LIMIT_CHANNELS = (1<<4) }; // EU868 will never have more channels 52 | //! \internal 53 | struct band_t { 54 | u2_t txcap; // duty cycle limitation: 1/txcap 55 | s1_t txpow; // maximum TX power 56 | u1_t lastchnl; // last used channel 57 | ostime_t avail; // channel is blocked until this time 58 | }; 59 | TYPEDEF_xref2band_t; //!< \internal 60 | 61 | #elif defined(CFG_us915) // US915 spectrum ================================================= 62 | 63 | enum { MAX_XCHANNELS = 2 }; // extra channels in RAM, channels 0-71 are immutable 64 | enum { MAX_TXPOW_125kHz = 30 }; 65 | 66 | #endif // ========================================================================== 67 | 68 | // Keep in sync with evdefs.hpp::drChange 69 | enum { DRCHG_SET, DRCHG_NOJACC, DRCHG_NOACK, DRCHG_NOADRACK, DRCHG_NWKCMD }; 70 | enum { KEEP_TXPOW = -128 }; 71 | 72 | 73 | #if !defined(DISABLE_PING) 74 | //! \internal 75 | struct rxsched_t { 76 | u1_t dr; 77 | u1_t intvExp; // 0..7 78 | u1_t slot; // runs from 0 to 128 79 | u1_t rxsyms; 80 | ostime_t rxbase; 81 | ostime_t rxtime; // start of next spot 82 | u4_t freq; 83 | }; 84 | TYPEDEF_xref2rxsched_t; //!< \internal 85 | #endif // !DISABLE_PING 86 | 87 | 88 | #if !defined(DISABLE_BEACONS) 89 | //! Parsing and tracking states of beacons. 90 | enum { BCN_NONE = 0x00, //!< No beacon received 91 | BCN_PARTIAL = 0x01, //!< Only first (common) part could be decoded (info,lat,lon invalid/previous) 92 | BCN_FULL = 0x02, //!< Full beacon decoded 93 | BCN_NODRIFT = 0x04, //!< No drift value measured yet 94 | BCN_NODDIFF = 0x08 }; //!< No differential drift measured yet 95 | //! Information about the last and previous beacons. 96 | struct bcninfo_t { 97 | ostime_t txtime; //!< Time when the beacon was sent 98 | s1_t rssi; //!< Adjusted RSSI value of last received beacon 99 | s1_t snr; //!< Scaled SNR value of last received beacon 100 | u1_t flags; //!< Last beacon reception and tracking states. See BCN_* values. 101 | u4_t time; //!< GPS time in seconds of last beacon (received or surrogate) 102 | // 103 | u1_t info; //!< Info field of last beacon (valid only if BCN_FULL set) 104 | s4_t lat; //!< Lat field of last beacon (valid only if BCN_FULL set) 105 | s4_t lon; //!< Lon field of last beacon (valid only if BCN_FULL set) 106 | }; 107 | #endif // !DISABLE_BEACONS 108 | 109 | // purpose of receive window - lmic_t.rxState 110 | enum { RADIO_RST=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3 }; 111 | // Netid values / lmic_t.netid 112 | enum { NETID_NONE=(int)~0U, NETID_MASK=(int)0xFFFFFF }; 113 | // MAC operation modes (lmic_t.opmode). 114 | enum { OP_NONE = 0x0000, 115 | OP_SCAN = 0x0001, // radio scan to find a beacon 116 | OP_TRACK = 0x0002, // track my networks beacon (netid) 117 | OP_JOINING = 0x0004, // device joining in progress (blocks other activities) 118 | OP_TXDATA = 0x0008, // TX user data (buffered in pendTxData) 119 | OP_POLL = 0x0010, // send empty UP frame to ACK confirmed DN/fetch more DN data 120 | OP_REJOIN = 0x0020, // occasionally send JOIN REQUEST 121 | OP_SHUTDOWN = 0x0040, // prevent MAC from doing anything 122 | OP_TXRXPEND = 0x0080, // TX/RX transaction pending 123 | OP_RNDTX = 0x0100, // prevent TX lining up after a beacon 124 | OP_PINGINI = 0x0200, // pingable is initialized and scheduling active 125 | OP_PINGABLE = 0x0400, // we're pingable 126 | OP_NEXTCHNL = 0x0800, // find a new channel 127 | OP_LINKDEAD = 0x1000, // link was reported as dead 128 | OP_TESTMODE = 0x2000, // developer test mode 129 | }; 130 | // TX-RX transaction flags - report back to user 131 | enum { TXRX_ACK = 0x80, // confirmed UP frame was acked 132 | TXRX_NACK = 0x40, // confirmed UP frame was not acked 133 | TXRX_NOPORT = 0x20, // set if a frame with a port was RXed, clr if no frame/no port 134 | TXRX_PORT = 0x10, // set if a frame with a port was RXed, LMIC.frame[LMIC.dataBeg-1] => port 135 | TXRX_DNW1 = 0x01, // received in 1st DN slot 136 | TXRX_DNW2 = 0x02, // received in 2dn DN slot 137 | TXRX_PING = 0x04 }; // received in a scheduled RX slot 138 | // Event types for event callback 139 | enum _ev_t { EV_SCAN_TIMEOUT=1, EV_BEACON_FOUND, 140 | EV_BEACON_MISSED, EV_BEACON_TRACKED, EV_JOINING, 141 | EV_JOINED, EV_RFU1, EV_JOIN_FAILED, EV_REJOIN_FAILED, 142 | EV_TXCOMPLETE, EV_LOST_TSYNC, EV_RESET, 143 | EV_RXCOMPLETE, EV_LINK_DEAD, EV_LINK_ALIVE }; 144 | typedef enum _ev_t ev_t; 145 | 146 | enum { 147 | // This value represents 100% error in LMIC.clockError 148 | MAX_CLOCK_ERROR = 65536, 149 | }; 150 | 151 | struct lmic_t { 152 | // Radio settings TX/RX (also accessed by HAL) 153 | ostime_t txend; 154 | ostime_t rxtime; 155 | u4_t freq; 156 | s1_t rssi; 157 | s1_t snr; 158 | rps_t rps; 159 | u1_t rxsyms; 160 | u1_t dndr; 161 | s1_t txpow; // dBm 162 | 163 | osjob_t osjob; 164 | 165 | // Channel scheduling 166 | #if defined(CFG_eu868) 167 | band_t bands[MAX_BANDS]; 168 | u4_t channelFreq[MAX_CHANNELS]; 169 | u2_t channelDrMap[MAX_CHANNELS]; 170 | u2_t channelMap; 171 | #elif defined(CFG_us915) 172 | u4_t xchFreq[MAX_XCHANNELS]; // extra channel frequencies (if device is behind a repeater) 173 | u2_t xchDrMap[MAX_XCHANNELS]; // extra channel datarate ranges ---XXX: ditto 174 | u2_t channelMap[(72+MAX_XCHANNELS+15)/16]; // enabled bits 175 | u2_t chRnd; // channel randomizer 176 | #endif 177 | u1_t txChnl; // channel for next TX 178 | u1_t globalDutyRate; // max rate: 1/2^k 179 | ostime_t globalDutyAvail; // time device can send again 180 | 181 | u4_t netid; // current network id (~0 - none) 182 | u2_t opmode; 183 | u1_t upRepeat; // configured up repeat 184 | s1_t adrTxPow; // ADR adjusted TX power 185 | u1_t datarate; // current data rate 186 | u1_t errcr; // error coding rate (used for TX only) 187 | u1_t rejoinCnt; // adjustment for rejoin datarate 188 | #if !defined(DISABLE_BEACONS) 189 | s2_t drift; // last measured drift 190 | s2_t lastDriftDiff; 191 | s2_t maxDriftDiff; 192 | #endif 193 | 194 | u2_t clockError; // Inaccuracy in the clock. CLOCK_ERROR_MAX 195 | // represents +/-100% error 196 | 197 | u1_t pendTxPort; 198 | u1_t pendTxConf; // confirmed data 199 | u1_t pendTxLen; // +0x80 = confirmed 200 | u1_t pendTxData[MAX_LEN_PAYLOAD]; 201 | 202 | u2_t devNonce; // last generated nonce 203 | u1_t nwkKey[16]; // network session key 204 | u1_t artKey[16]; // application router session key 205 | devaddr_t devaddr; 206 | u4_t seqnoDn; // device level down stream seqno 207 | u4_t seqnoUp; 208 | 209 | u1_t dnConf; // dn frame confirm pending: LORA::FCT_ACK or 0 210 | s1_t adrAckReq; // counter until we reset data rate (0=off) 211 | u1_t adrChanged; 212 | 213 | u1_t rxDelay; // Rx delay after TX 214 | 215 | u1_t margin; 216 | bit_t ladrAns; // link adr adapt answer pending 217 | bit_t devsAns; // device status answer pending 218 | u1_t adrEnabled; 219 | u1_t moreData; // NWK has more data pending 220 | #if !defined(DISABLE_MCMD_DCAP_REQ) 221 | bit_t dutyCapAns; // have to ACK duty cycle settings 222 | #endif 223 | #if !defined(DISABLE_MCMD_SNCH_REQ) 224 | u1_t snchAns; // answer set new channel 225 | #endif 226 | // 2nd RX window (after up stream) 227 | u1_t dn2Dr; 228 | u4_t dn2Freq; 229 | #if !defined(DISABLE_MCMD_DN2P_SET) 230 | u1_t dn2Ans; // 0=no answer pend, 0x80+ACKs 231 | #endif 232 | 233 | // Class B state 234 | #if !defined(DISABLE_BEACONS) 235 | u1_t missedBcns; // unable to track last N beacons 236 | u1_t bcninfoTries; // how often to try (scan mode only) 237 | #endif 238 | #if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING) 239 | u1_t pingSetAns; // answer set cmd and ACK bits 240 | #endif 241 | #if !defined(DISABLE_PING) 242 | rxsched_t ping; // pingable setup 243 | #endif 244 | 245 | // Public part of MAC state 246 | u1_t txCnt; 247 | u1_t txrxFlags; // transaction flags (TX-RX combo) 248 | u1_t dataBeg; // 0 or start of data (dataBeg-1 is port) 249 | u1_t dataLen; // 0 no data or zero length data, >0 byte count of data 250 | u1_t frame[MAX_LEN_FRAME]; 251 | 252 | #if !defined(DISABLE_BEACONS) 253 | u1_t bcnChnl; 254 | u1_t bcnRxsyms; // 255 | ostime_t bcnRxtime; 256 | bcninfo_t bcninfo; // Last received beacon info 257 | #endif 258 | }; 259 | //! \var struct lmic_t LMIC 260 | //! The state of LMIC MAC layer is encapsulated in this variable. 261 | DECLARE_LMIC; //!< \internal 262 | 263 | //! Construct a bit map of allowed datarates from drlo to drhi (both included). 264 | #define DR_RANGE_MAP(drlo,drhi) (((u2_t)0xFFFF<<(drlo)) & ((u2_t)0xFFFF>>(15-(drhi)))) 265 | #if defined(CFG_eu868) 266 | enum { BAND_MILLI=0, BAND_CENTI=1, BAND_DECI=2, BAND_AUX=3 }; 267 | bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap); 268 | #endif 269 | bit_t LMIC_setupChannel (u1_t channel, u4_t freq, u2_t drmap, s1_t band); 270 | void LMIC_disableChannel (u1_t channel); 271 | #if defined(CFG_us915) 272 | void LMIC_enableChannel (u1_t channel); 273 | void LMIC_enableSubBand (u1_t band); 274 | void LMIC_disableSubBand (u1_t band); 275 | void LMIC_selectSubBand (u1_t band); 276 | #endif 277 | 278 | void LMIC_setDrTxpow (dr_t dr, s1_t txpow); // set default/start DR/txpow 279 | void LMIC_setAdrMode (bit_t enabled); // set ADR mode (if mobile turn off) 280 | #if !defined(DISABLE_JOIN) 281 | bit_t LMIC_startJoining (void); 282 | #endif 283 | 284 | void LMIC_shutdown (void); 285 | void LMIC_init (void); 286 | void LMIC_reset (void); 287 | void LMIC_clrTxData (void); 288 | void LMIC_setTxData (void); 289 | int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed); 290 | void LMIC_sendAlive (void); 291 | 292 | #if !defined(DISABLE_BEACONS) 293 | bit_t LMIC_enableTracking (u1_t tryBcnInfo); 294 | void LMIC_disableTracking (void); 295 | #endif 296 | 297 | #if !defined(DISABLE_PING) 298 | void LMIC_stopPingable (void); 299 | void LMIC_setPingable (u1_t intvExp); 300 | #endif 301 | #if !defined(DISABLE_JOIN) 302 | void LMIC_tryRejoin (void); 303 | #endif 304 | 305 | void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey); 306 | void LMIC_setLinkCheckMode (bit_t enabled); 307 | void LMIC_setClockError(u2_t error); 308 | 309 | // Declare onEvent() function, to make sure any definition will have the 310 | // C conventions, even when in a C++ file. 311 | DECL_ON_LMIC_EVENT; 312 | 313 | // Special APIs - for development or testing 314 | // !!!See implementation for caveats!!! 315 | 316 | #ifdef __cplusplus 317 | } // extern "C" 318 | #endif 319 | 320 | #endif // _lmic_h_ 321 | -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/lorabase.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | *******************************************************************************/ 11 | 12 | #ifndef _lorabase_h_ 13 | #define _lorabase_h_ 14 | 15 | #ifdef __cplusplus 16 | extern "C"{ 17 | #endif 18 | 19 | // ================================================================================ 20 | // BEG: Keep in sync with lorabase.hpp 21 | // 22 | 23 | enum _cr_t { CR_4_5=0, CR_4_6, CR_4_7, CR_4_8 }; 24 | enum _sf_t { FSK=0, SF7, SF8, SF9, SF10, SF11, SF12, SFrfu }; 25 | enum _bw_t { BW125=0, BW250, BW500, BWrfu }; 26 | typedef u1_t cr_t; 27 | typedef u1_t sf_t; 28 | typedef u1_t bw_t; 29 | typedef u1_t dr_t; 30 | // Radio parameter set (encodes SF/BW/CR/IH/NOCRC) 31 | typedef u2_t rps_t; 32 | TYPEDEF_xref2rps_t; 33 | 34 | enum { ILLEGAL_RPS = 0xFF }; 35 | enum { DR_PAGE_EU868 = 0x00 }; 36 | enum { DR_PAGE_US915 = 0x10 }; 37 | 38 | // Global maximum frame length 39 | enum { STD_PREAMBLE_LEN = 8 }; 40 | enum { MAX_LEN_FRAME = 64 }; 41 | enum { LEN_DEVNONCE = 2 }; 42 | enum { LEN_ARTNONCE = 3 }; 43 | enum { LEN_NETID = 3 }; 44 | enum { DELAY_JACC1 = 5 }; // in secs 45 | enum { DELAY_DNW1 = 1 }; // in secs down window #1 46 | enum { DELAY_EXTDNW2 = 1 }; // in secs 47 | enum { DELAY_JACC2 = DELAY_JACC1+(int)DELAY_EXTDNW2 }; // in secs 48 | enum { DELAY_DNW2 = DELAY_DNW1 +(int)DELAY_EXTDNW2 }; // in secs down window #1 49 | enum { BCN_INTV_exp = 7 }; 50 | enum { BCN_INTV_sec = 1<> 3) & 0x3); } 350 | inline rps_t setBw (rps_t params, bw_t cr) { return (rps_t)((params & ~0x18) | (cr<<3)); } 351 | inline cr_t getCr (rps_t params) { return (cr_t)((params >> 5) & 0x3); } 352 | inline rps_t setCr (rps_t params, cr_t cr) { return (rps_t)((params & ~0x60) | (cr<<5)); } 353 | inline int getNocrc(rps_t params) { return ((params >> 7) & 0x1); } 354 | inline rps_t setNocrc(rps_t params, int nocrc) { return (rps_t)((params & ~0x80) | (nocrc<<7)); } 355 | inline int getIh (rps_t params) { return ((params >> 8) & 0xFF); } 356 | inline rps_t setIh (rps_t params, int ih) { return (rps_t)((params & ~0xFF00) | (ih<<8)); } 357 | inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) { 358 | return sf | (bw<<3) | (cr<<5) | (nocrc?(1<<7):0) | ((ih&0xFF)<<8); 359 | } 360 | #define MAKERPS(sf,bw,cr,ih,nocrc) ((rps_t)((sf) | ((bw)<<3) | ((cr)<<5) | ((nocrc)?(1<<7):0) | ((ih&0xFF)<<8))) 361 | // Two frames with params r1/r2 would interfere on air: same SFx + BWx 362 | inline int sameSfBw(rps_t r1, rps_t r2) { return ((r1^r2)&0x1F) == 0; } 363 | 364 | extern CONST_TABLE(u1_t, _DR2RPS_CRC)[]; 365 | inline rps_t updr2rps (dr_t dr) { return (rps_t)TABLE_GET_U1(_DR2RPS_CRC, dr+1); } 366 | inline rps_t dndr2rps (dr_t dr) { return setNocrc(updr2rps(dr),1); } 367 | inline int isFasterDR (dr_t dr1, dr_t dr2) { return dr1 > dr2; } 368 | inline int isSlowerDR (dr_t dr1, dr_t dr2) { return dr1 < dr2; } 369 | inline dr_t incDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+2)==ILLEGAL_RPS ? dr : (dr_t)(dr+1); } // increase data rate 370 | inline dr_t decDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr )==ILLEGAL_RPS ? dr : (dr_t)(dr-1); } // decrease data rate 371 | inline dr_t assertDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)==ILLEGAL_RPS ? DR_DFLTMIN : dr; } // force into a valid DR 372 | inline bit_t validDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; } // in range 373 | inline dr_t lowerDR (dr_t dr, u1_t n) { while(n--){dr=decDR(dr);} return dr; } // decrease data rate by n steps 374 | 375 | // 376 | // BEG: Keep in sync with lorabase.hpp 377 | // ================================================================================ 378 | 379 | 380 | // Convert between dBm values and power codes (MCMD_LADR_XdBm) 381 | s1_t pow2dBm (u1_t mcmd_ladr_p1); 382 | // Calculate airtime 383 | ostime_t calcAirTime (rps_t rps, u1_t plen); 384 | // Sensitivity at given SF/BW 385 | int getSensitivity (rps_t rps); 386 | 387 | #ifdef __cplusplus 388 | } // extern "C" 389 | #endif 390 | 391 | #endif // _lorabase_h_ 392 | -------------------------------------------------------------------------------- /lib/arduino-lmic-1.5.0-arduino-2-tweaked/README.md: -------------------------------------------------------------------------------- 1 | Arduino-LMIC library 2 | ==================== 3 | This repository contains the IBM LMIC (LoraMAC-in-C) library, slightly 4 | modified to run in the Arduino environment, allowing using the SX1272, 5 | SX1276 tranceivers and compatible modules (such as some HopeRF RFM9x 6 | modules). 7 | 8 | This library mostly exposes the functions defined by LMIC, it makes no 9 | attempt to wrap them in a higher level API that is more in the Arduino 10 | style. To find out how to use the library itself, see the examples, or 11 | see the PDF file in the doc subdirectory. 12 | 13 | This library requires Arduino IDE version 1.6.6 or above, since it 14 | requires C99 mode to be enabled by default. 15 | 16 | Installing 17 | ---------- 18 | To install this library: 19 | 20 | - install it using the Arduino Library manager ("Sketch" -> "Include 21 | Library" -> "Manage Libraries..."), or 22 | - download a zipfile from github using the "Download ZIP" button and 23 | install it using the IDE ("Sketch" -> "Include Library" -> "Add .ZIP 24 | Library..." 25 | - clone this git repository into your sketchbook/libraries folder. 26 | 27 | For more info, see https://www.arduino.cc/en/Guide/Libraries 28 | 29 | Features 30 | -------- 31 | The LMIC library provides a fairly complete LoRaWAN Class A and Class B 32 | implementation, supporting the EU-868 and US-915 bands. Only a limited 33 | number of features was tested using this port on Arduino hardware, so be 34 | careful when using any of the untested features. 35 | 36 | What certainly works: 37 | - Sending packets uplink, taking into account duty cycling. 38 | - Encryption and message integrity checking. 39 | - Receiving downlink packets in the RX2 window. 40 | - Custom frequencies and datarate settings. 41 | - Over-the-air activation (OTAA / joining). 42 | 43 | What has not been tested: 44 | - Receiving downlink packets in the RX1 window. 45 | - Receiving and processing MAC commands. 46 | - Class B operation. 47 | 48 | If you try one of these untested features and it works, be sure to let 49 | us know (creating a github issue is probably the best way for that). 50 | 51 | Configuration 52 | ------------- 53 | A number of features can be configured or disabled by editing the 54 | `config.h` file in the library folder. Unfortunately the Arduino 55 | environment does not offer any way to do this (compile-time) 56 | configuration from the sketch, so be careful to recheck your 57 | configuration when you switch between sketches or update the library. 58 | 59 | At the very least, you should set the right type of transceiver (SX1272 60 | vs SX1276) in config.h, most other values should be fine at their 61 | defaults. 62 | 63 | Supported hardware 64 | ------------------ 65 | This library is intended to be used with plain LoRa transceivers, 66 | connecting to them using SPI. In particular, the SX1272 and SX1276 67 | families are supported (which should include SX1273, SX1277, SX1278 and 68 | SX1279 which only differ in the available frequencies, bandwidths and 69 | spreading factors). It has been tested with both SX1272 and SX1276 70 | chips, using the Semtech SX1272 evaluation board and the HopeRF RFM92 71 | and RFM95 boards (which supposedly contain an SX1272 and SX1276 chip 72 | respectively). 73 | 74 | This library contains a full LoRaWAN stack and is intended to drive 75 | these Transceivers directly. It is *not* intended to be used with 76 | full-stack devices like the Microchip RN2483 and the Embit LR1272E. 77 | These contain a transceiver and microcontroller that implements the 78 | LoRaWAN stack and exposes a high-level serial interface instead of the 79 | low-level SPI transceiver interface. 80 | 81 | This library is intended to be used inside the Arduino environment. It 82 | should be architecture-independent, so it should run on "normal" AVR 83 | arduinos, but also on the ARM-based ones, and some success has been seen 84 | running on the ESP8266 board as well. It was tested on the Arduino Uno, 85 | Pinoccio Scout, Teensy LC and 3.x, ESP8266, Arduino 101. 86 | 87 | This library an be quite heavy, especially if the fairly small ATmega 88 | 328p (such as in the Arduino Uno) is used. In the default configuration, 89 | the available 32K flash space is nearly filled up (this includes some 90 | debug output overhead, though). By disabling some features in `config.h` 91 | (like beacon tracking and ping slots, which are not typically needed), 92 | some space can be freed up. Some work is underway to replace the AES 93 | encryption implementation, which should free up another 8K or so of 94 | flash in the future, making this library feasible to run on a 328p 95 | microcontroller. 96 | 97 | Connections 98 | ----------- 99 | To make this library work, your Arduino (or whatever Arduino-compatible 100 | board you are using) should be connected to the transceiver. The exact 101 | connections are a bit dependent on the transceiver board and Arduino 102 | used, so this section tries to explain what each connection is for and 103 | in what cases it is (not) required. 104 | 105 | Note that the SX1272 module runs at 3.3V and likely does not like 5V on 106 | its pins (though the datasheet is not say anything about this, and my 107 | transceiver did not obviously break after accidentally using 5V I/O for 108 | a few hours). To be safe, make sure to use a level shifter, or an 109 | Arduino running at 3.3V. The Semtech evaluation board has 100 ohm resistors in 110 | series with all data lines that might prevent damage, but I would not 111 | count on that. 112 | 113 | ### Power 114 | The SX127x transceivers need a supply voltage between 1.8V and 3.9V. 115 | Using a 3.3V supply is typical. Some modules have a single power pin 116 | (like the HopeRF modules, labeled 3.3V) but others expose multiple power 117 | pins for different parts (like the Semtech evaluation board that has 118 | `VDD_RF`, `VDD_ANA` and `VDD_FEM`), which can all be connected together. 119 | Any *GND* pins need to be connected to the Arduino *GND* pin(s). 120 | 121 | ### SPI 122 | The primary way of communicating with the transceiver is through SPI 123 | (Serial Peripheral Interface). This uses four pins: MOSI, MISO, SCK and 124 | SS. The former three need to be directly connected: so MOSI to MOSI, 125 | MISO to MISO, SCK to SCK. Where these pins are located on your Arduino 126 | varies, see for example the "Connections" section of the [Arduino SPI 127 | documentation](SPI). 128 | 129 | The SS (slave select) connection is a bit more flexible. On the SPI 130 | slave side (the transceiver), this must be connect to the pin 131 | (typically) labeled *NSS*. On the SPI master (Arduino) side, this pin 132 | can connect to any I/O pin. Most Arduinos also have a pin labeled "SS", 133 | but this is only relevant when the Arduino works as an SPI slave, which 134 | is not the case here. Whatever pin you pick, you need to tell the 135 | library what pin you used through the pin mapping (see below). 136 | 137 | [SPI]: https://www.arduino.cc/en/Reference/SPI 138 | 139 | ### DIO pins 140 | The DIO (digitial I/O) pins on the transceiver board can be configured 141 | for various functions. The LMIC library uses them to get instant status 142 | information from the transceiver. For example, when a LoRa transmission 143 | starts, the DIO0 pin is configured as a TxDone output. When the 144 | transmission is complete, the DIO0 pin is made high by the transceiver, 145 | which can be detected by the LMIC library. 146 | 147 | The LMIC library needs only access to DIO0, DIO1 and DIO2, the other 148 | DIOx pins can be left disconnected. On the Arduino side, they can 149 | connect to any I/O pin, since the current implementation does not use 150 | interrupts or other special hardware features (though this might be 151 | added in the feature, see also the "Timing" section). 152 | 153 | In LoRa mode the DIO pins are used as follows: 154 | * DIO0: TxDone and RxDone 155 | * DIO1: RxTimeout 156 | 157 | In FSK mode they are used as follows:: 158 | * DIO0: PayloadReady and PacketSent 159 | * DIO2: TimeOut 160 | 161 | Both modes need only 2 pins, but the tranceiver does not allow mapping 162 | them in such a way that all needed interrupts map to the same 2 pins. 163 | So, if both LoRa and FSK modes are used, all three pins must be 164 | connected. 165 | 166 | The pins used on the Arduino side should be configured in the pin 167 | mapping in your sketch (see below). 168 | 169 | ### Reset 170 | The transceiver has a reset pin that can be used to explicitely reset 171 | it. The LMIC library uses this to ensure the chip is in a consistent 172 | state at startup. In practice, this pin can be left disconnected, since 173 | the transceiver will already be in a sane state on power-on, but 174 | connecting it might prevent problems in some cases. 175 | 176 | On the Arduino side, any I/O pin can be used. The pin number used must 177 | be configured in the pin mapping (see below). 178 | 179 | ### RXTX 180 | The transceiver contains two separate antenna connections: One for RX 181 | and one for TX. A typical transceiver board contains an antenna switch 182 | chip, that allows switching a single antenna between these RX and TX 183 | connections. Such a antenna switcher can typically be told what 184 | position it should be through an input pin, often labeled *RXTX*. 185 | 186 | The easiest way to control the antenna switch is to use the *RXTX* pin 187 | on the SX127x transceiver. This pin is automatically set high during TX 188 | and low during RX. For example, the HopeRF boards seem to have this 189 | connection in place, so they do not expose any *RXTX* pins and the pin 190 | can be marked as unused in the pin mapping. 191 | 192 | Some boards do expose the antenna switcher pin, and sometimes also the 193 | SX127x *RXTX* pin. For example, the SX1272 evaluation board calls the 194 | former *FEM_CTX* and the latter *RXTX*. Again, simply connecting these 195 | together with a jumper wire is the easiest solution. 196 | 197 | Alternatively, or if the SX127x *RXTX* pin is not available, LMIC can be 198 | configured to control the antenna switch. Connect the antenna switch 199 | control pin (e.g. *FEM_CTX* on the Semtech evaluation board) to any I/O 200 | pin on the Arduino side, and configure the pin used in the pin map (see 201 | below). It is not entirely clear why would *not* want the transceiver to 202 | control the antenna directly, though. 203 | 204 | ### Pin mapping 205 | As described above, most connections can use arbitrary I/O pins on the 206 | Arduino side. To tell the LMIC library about these, a pin mapping struct 207 | is used in the sketch file. 208 | 209 | For example, this could look like this: 210 | 211 | lmic_pinmap lmic_pins = { 212 | .nss = 6, 213 | .rxtx = LMIC_UNUSED_PIN, 214 | .rst = 5, 215 | .dio = {2, 3, 4}, 216 | }; 217 | 218 | The names refer to the pins on the transceiver side, the numbers refer 219 | to the Arduino pin numbers (to use the analog pins, use constants like 220 | `A0`). For the DIO pins, the three numbers refer to DIO0, DIO1 and DIO2 221 | respectively. Any pins that are not needed should be specified as 222 | `LMIC_UNUSED_PIN`. The nss and dio0 pin is required, the others can 223 | potentially left out (depending on the environments and requirements, 224 | see the notes above for when a pin can or cannot be left out). 225 | 226 | The name of this struct must always be `lmic_pins`, which is a special name 227 | recognized by the library. 228 | 229 | #### LoRa Nexus by Ideetron 230 | This board uses the following pin mapping: 231 | 232 | const lmic_pinmap lmic_pins = { 233 | .nss = 10, 234 | .rxtx = LMIC_UNUSED_PIN, 235 | .rst = LMIC_UNUSED_PIN, // hardwired to AtMega RESET 236 | .dio = {4, 5, 7}, 237 | }; 238 | 239 | Examples 240 | -------- 241 | This library currently provides three examples: 242 | 243 | - `ttn-abp.ino` shows a basic transmission of a "Hello, world!" message 244 | using the LoRaWAN protocol. It contains some frequency settings and 245 | encryption keys intended for use with The Things Network, but these 246 | also correspond to the default settings of most gateways, so it 247 | should work with other networks and gateways as well. This example 248 | uses activation-by-personalization (ABP, preconfiguring a device 249 | address and encryption keys), and does not employ over-the-air 250 | activation. 251 | 252 | Reception of packets (in response to transmission, using the RX1 and 253 | RX2 receive windows is also supported). 254 | 255 | - `ttn-otaa.ino` also sends a "Hello, world!" message, but uses over 256 | the air activation (OTAA) to first join a network to establish a 257 | session and security keys. This was tested with The Things Network, 258 | but should also work (perhaps with some changes) for other networks. 259 | 260 | - `raw.ino` shows how to access the radio on a somewhat low level, 261 | and allows to send raw (non-LoRaWAN) packets between nodes directly. 262 | This is useful to verify basic connectivity, and when no gateway is 263 | available, but this example also bypasses duty cycle checks, so be 264 | careful when changing the settings. 265 | 266 | Timing 267 | ------ 268 | Unfortunately, the SX127x tranceivers do not support accurate 269 | timekeeping themselves (there is a sequencer that is *almost* sufficient 270 | for timing the RX1 and RX2 downlink windows, but that is only available 271 | in FSK mode, not in LoRa mode). This means that the microcontroller is 272 | responsible for keeping track of time. In particular, it should note 273 | when a packet finished transmitting, so it can open up the RX1 and RX2 274 | receive windows at a fixed time after the end of transmission. 275 | 276 | This timing uses the Arduino `micros()` timer, which has a granularity 277 | of 4μs and is based on the primary microcontroller clock. For timing 278 | events, the tranceiver uses its DIOx pins as interrupt outputs. In the 279 | current implementation, these pins are not handled by an actual 280 | interrupt handler, but they are just polled once every LMIC loop, 281 | resulting in a bit inaccuracy in the timestamping. Also, running 282 | scheduled jobs (such as opening up the receive windows) is done using a 283 | polling approach, which might also result in further delays. 284 | 285 | Fortunately, LoRa is a fairly slow protocol and the timing of the 286 | receive windows is not super critical. To synchronize transmitter and 287 | receiver, a preamble is first transmitted. Using LoRaWAN, this preamble 288 | consists of 8 symbols, of which the receiver needs to see 4 symbols to 289 | lock on. The current implementation tries to enable the receiver for 5 290 | symbol times at 1.5 symbol after the start of the receive window, 291 | meaning that a inacurracy of plus or minus 2.5 symbol times should be 292 | acceptable. 293 | 294 | At the fastest LoRa setting supported by the tranceiver (SF5BW500) a 295 | single preamble symbol takes 64μs, so the receive window timing should 296 | be accurate within 160μs (for LoRaWAN this is SF7BW250, needing accuracy 297 | within 1280μs). This is certainly within a crystal's accuracy, but using 298 | the internal oscillator is probably not feasible (which is 1% - 10% 299 | accurate, depending on calibration). This accuracy should also be 300 | feasible with the polling approach used, provided that the LMIC loop is 301 | run often enough. 302 | 303 | It would be good to properly review this code at some point, since it 304 | seems that in some places some offsets and corrections are applied that 305 | might not be appropriate for the Arduino environment. So if reception is 306 | not working, the timing is something to have a closer look at. 307 | 308 | The LMIC library was intended to connect the DIO pins to interrupt 309 | lines and run code inside the interrupt handler. However, doing this 310 | opens up an entire can of worms with regard to doing SPI transfers 311 | inside interrupt routines (some of which is solved by the Arduino 312 | `beginTransaction()` API, but possibly not everything). One simpler 313 | alternative could be to use an interrupt handler to just store a 314 | timestamp, and then do the actual handling in the main loop (this 315 | requires modifications of the library to pass a timestamp to the LMIC 316 | `radio_irq_handler()` function). 317 | 318 | An even more accurate solution could be to use a dedicated timer with an 319 | input capture unit, that can store the timestamp of a change on the DIO0 320 | pin (the only one that is timing-critical) entirely in hardware. 321 | Unfortunately, timer0, as used by Arduino's `millis()` and `micros()` 322 | functions does not seem to have an input capture unit, meaning a 323 | separate timer is needed for this. 324 | 325 | If the main microcontroller does not have a crystal, but uses the 326 | internal oscillator, the clock output of the transceiver (on DIO5) could 327 | be usable to drive this timer instead of the main microcontroller clock, 328 | to ensure the receive window timing is sufficiently accurate. Ideally, 329 | this would use timer2, which supports asynchronous mode (e.g. running 330 | while the microcontroller is sleeping), but that timer does not have an 331 | input capture unit. Timer1 has one, but it seems it will stop running 332 | once the microcontroller sleeps. Running the microcontroller in idle 333 | mode with a slower clock might be feasible, though. Instead of using the 334 | main crystal oscillator of the transceiver, it could be possible to use 335 | the transceiver's internal RC oscillator (which is calibrated against 336 | the transceiver crystal), or to calibrate the microcontroller internal 337 | RC oscillator using the transceiver's clkout. However, that datasheet is 338 | a bit vague on the RC oscillator's accuracy and how to use it exactly 339 | (some registers seem to be FSK-mode only), so this needs some 340 | experiments. 341 | 342 | Downlink datarate 343 | ----------------- 344 | Note that the datarate used for downlink packets in the RX2 window 345 | defaults to SF12BW125 according to the specification, but some networks 346 | use different values (iot.semtech.com and The Things Network both use 347 | SF9BW). When using personalized activate (ABP), it is your 348 | responsibility to set the right settings, e.g. by adding this to your 349 | sketch (after calling `LMIC_setSession`). `ttn-abp.ino` already does 350 | this. 351 | 352 | LMIC.dn2Dr = DR_SF9; 353 | 354 | When using OTAA, the network communicates the RX2 settings in the 355 | join accept message, but the LMIC library does not currently process 356 | these settings. Until that is solved (see issue #20), you should 357 | manually set the RX2 rate, *after* joining (see the handling of 358 | `EV_JOINED` in the `ttn-otaa.ino` for an example. 359 | 360 | License 361 | ------- 362 | Most source files in this repository are made available under the 363 | Eclipse Public License v1.0. The examples which use a more liberal 364 | license. Some of the AES code is available under the LGPL. Refer to each 365 | individual source file for more details. 366 | --------------------------------------------------------------------------------