diff --git a/README.md b/README.md index 00efc562..3010e460 100644 --- a/README.md +++ b/README.md @@ -20,18 +20,23 @@ This can all be done with a single small and cheap ESP32 board for less than $20 # Hardware Supported ESP32 based LoRa IoT boards: -- Heltec LoRa-32 {1} -- TTGOv1 {1} -- TTGOv2 {1}{4} -- TTGOv2.1 {1}{5} -- TTGO T-Beam {4}{5} -- Pycom LoPy {2} -- Pycom LoPy4 {2} -- Pycom FiPy {2} -- LoLin32 with [LoraNode32 shield](https://github.com/hallard/LoLin32-Lora) {2}{3} -- LoLin32 Lite with [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora) {2}{3} +- **Heltec LoRa-32** a) +- **TTGOv1** a) +- **TTGOv2** a,d) +- **TTGOv2.1** a),e) +- **TTGO T-Beam** d),e),f) +- **Pycom LoPy** b),f)* +- **Pycom LoPy4** b),f)* +- **Pycom FiPy** b),f)* +- **LoLin32** with [LoraNode32 shield](https://github.com/hallard/LoLin32-Lora) b),c) +- **LoLin32 Lite** with [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora) b),c) -{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 file /hal/.h; {5} battery voltage monitoring supported +a) on board OLED Display supported; +b) on board RGB LED supported; +c) on board Hardware unique DEVEUI supported; +d) external wiring needed, see instructions in file /hal/.h; +e) battery voltage monitoring supported; +f) on board GPS supported, *for Pycom devices with additional PyTrack board Target platform must be selected in [platformio.ini](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/platformio.ini).
Hardware dependent settings (pinout etc.) are stored in board files in /hal directory.
@@ -102,40 +107,68 @@ Legend for RGB LED (LoPy/LoPy4/FiPy/Lolin32 only): # Payload -FPort1: +**LoRaWAN Port #1:** - byte 1: 16-bit WiFi counter, MSB - byte 2: 16-bit WiFi counter, LSB - byte 3: 16-bit BLE counter, MSB - byte 4: 16-bit BLE counter, LSB + Paxcounter data -FPort2: + byte 1-2: Number of unique pax, first seen on Wifi + byte 3-4: Number of unique pax, first seen on Bluetooth [0 if BT disabled] + + GPS data (only, if GPS is present and has a fix) + + bytes 5-8: GPS latitude + bytes 9-12: GPS longitude + bytes 13-14: GPS number of satellites + bytes 15-16: GPS HDOP + bytes 17-18: GPS altitude [meter] - see remote command set +**LoRaWAN Port #2:** -If you're using [TheThingsNetwork](https://www.thethingsnetwork.org/) you may want to use a payload converter. Go to TTN Console - Application - Payload Formats and paste the code example below in tabs Decoder and Converter. Make sure that your application parses the fields `pax`, `ble` and `wifi`. + - see remote control - -Decoder: +If you're using [TheThingsNetwork](https://www.thethingsnetwork.org/) (TTN) you may want to use a payload converter. Go to TTN Console - Application - Payload Formats and paste the code example below in tabs Decoder and Converter. Make sure that your application parses the fields `pax`, `ble` and `wifi`. + +To map a GPS capable paxcounter device and at the same time contribute to TTN coverage mapping, you simply activate the [TTNmapper integration](https://www.thethingsnetwork.org/docs/applications/ttnmapper/) in TTN Console. Paxcounter generates ttnmapper compatible data fields. + +**Decoder:** ```javascript function Decoder(bytes, port) { - var decoded = {}; - if (port === 1) { - decoded.wifi = (bytes[0] << 8) | bytes[1]; - decoded.ble = (bytes[2] << 8) | bytes[3]; + var decoded = {}; + + if (port === 1) { + var i = 0; + decoded.wifi = (bytes[i++] << 8) | bytes[i++]; + decoded.ble = (bytes[i++] << 8) | bytes[i++]; + if (bytes.length > 4) { + decoded.latitude = ( (bytes[i++]) | (bytes[i++] << 8) | (bytes[i++] << 16) | bytes[i++] << 24 ); + decoded.longitude = ( (bytes[i++]) | (bytes[i++] << 8) | (bytes[i++] << 16) | bytes[i++] << 24 ); + decoded.sats = ( bytes[i++] | (bytes[i++] << 8) ); + decoded.hdop = ( bytes[i++] | (bytes[i++] << 8) ); + decoded.altitude = ( bytes[i++] | (bytes[i++] << 8) ); } - return decoded; + } + + return decoded; } ``` -Converter: +**Converter:** ```javascript function Converter(decoded, port) { + var converted = decoded; + if (port === 1) { converted.pax = converted.ble + converted.wifi; + if (converted.hdop) { + converted.hdop /= 100; + converted.latitude /= 1000000; + converted.longitude /= 1000000; + } } + return converted; } ``` @@ -262,6 +295,14 @@ device answers with it's current configuration. The configuration is a C structu bytes 1-2: battery voltage in millivolt, 0 if unreadable (little endian format) +0x84 get device GPS status + + bytes 1-4: latitude + bytes 5-8: longitude + byte 9-10: number of satellites + byte 11-12: HDOP + bytes 13-14: altidute [meter] + # License Copyright 2018 Oliver Brandmueller diff --git a/platformio.ini b/platformio.ini index 3fb30f06..be9dda45 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,11 +11,11 @@ ; ---> SELECT TARGET PLATFORM HERE! <--- [platformio] -;env_default = heltec +env_default = heltec ;env_default = ttgov1 ;env_default = ttgov2 ;env_default = ttgov21 -env_default = ttgobeam +;env_default = ttgobeam ;env_default = lopy ;env_default = lopy4 ;env_default = fipy @@ -27,11 +27,12 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng [common_env_data] platform_espressif32 = espressif32@>=1.0.2 board_build.partitions = no_ota.csv -board_upload.maximum_size = 2097152 lib_deps_display = U8g2@>=2.22.14 lib_deps_rgbled = - SmartLeds + SmartLeds@>=1.1.3 +lib_deps_gps = + TinyGPSPlus@>=1.0.2 build_flags = ; we need build_flag for logging, otherwise we can't use ESP_LOGx in arduino framework ; ---> NOTE: For production run set DEBUG_LEVEL level to NONE! <--- @@ -51,7 +52,6 @@ platform = ${common_env_data.platform_espressif32} framework = arduino board = heltec_wifi_lora_32 board_build.partitions = ${common_env_data.board_build.partitions} -board_upload.maximum_size = ${common_env_data.board_upload.maximum_size} monitor_speed = 115200 upload_speed = 115200 lib_deps = @@ -65,7 +65,6 @@ platform = ${common_env_data.platform_espressif32} framework = arduino board = esp32dev board_build.partitions = ${common_env_data.board_build.partitions} -board_upload.maximum_size = ${common_env_data.board_upload.maximum_size} monitor_speed = 115200 upload_speed = 115200 lib_deps = @@ -79,7 +78,6 @@ platform = ${common_env_data.platform_espressif32} framework = arduino board = esp32dev board_build.partitions = ${common_env_data.board_build.partitions} -board_upload.maximum_size = ${common_env_data.board_upload.maximum_size} monitor_speed = 115200 upload_speed = 921600 lib_deps = @@ -93,7 +91,6 @@ platform = ${common_env_data.platform_espressif32} framework = arduino board = esp32dev board_build.partitions = ${common_env_data.board_build.partitions} -board_upload.maximum_size = ${common_env_data.board_upload.maximum_size} monitor_speed = 115200 upload_speed = 921600 lib_deps = @@ -107,11 +104,11 @@ platform = ${common_env_data.platform_espressif32} framework = arduino board = esp32dev board_build.partitions = ${common_env_data.board_build.partitions} -board_upload.maximum_size = ${common_env_data.board_upload.maximum_size} monitor_speed = 115200 upload_speed = 921600 lib_deps = ${common_env_data.lib_deps_display} + ${common_env_data.lib_deps_gps} build_flags = ${common_env_data.build_flags} -include "src/hal/ttgobeam.h" @@ -121,7 +118,6 @@ platform = espressif32@1.0.1 framework = arduino board = esp32dev board_build.partitions = ${common_env_data.board_build.partitions} -board_upload.maximum_size = ${common_env_data.board_upload.maximum_size} monitor_speed = 115200 upload_speed = 921600 lib_deps = @@ -135,7 +131,6 @@ platform = ${common_env_data.platform_espressif32} framework = arduino board = esp32dev board_build.partitions = ${common_env_data.board_build.partitions} -board_upload.maximum_size = ${common_env_data.board_upload.maximum_size} monitor_speed = 115200 upload_speed = 921600 lib_deps = @@ -149,7 +144,6 @@ platform = ${common_env_data.platform_espressif32} framework = arduino board = esp32dev board_build.partitions = ${common_env_data.board_build.partitions} -board_upload.maximum_size = ${common_env_data.board_upload.maximum_size} monitor_speed = 115200 upload_speed = 921600 lib_deps = @@ -163,7 +157,6 @@ platform = ${common_env_data.platform_espressif32} framework = arduino board = lolin32 board_build.partitions = ${common_env_data.board_build.partitions} -board_upload.maximum_size = ${common_env_data.board_upload.maximum_size} monitor_speed = 115200 upload_speed = 256000 lib_deps = @@ -177,7 +170,6 @@ platform = ${common_env_data.platform_espressif32} framework = arduino board = lolin32 board_build.partitions = ${common_env_data.board_build.partitions} -board_upload.maximum_size = ${common_env_data.board_upload.maximum_size} monitor_speed = 115200 upload_speed = 921600 lib_deps = diff --git a/src/configmanager.cpp b/src/configmanager.cpp index 97ae1ee5..867a071f 100644 --- a/src/configmanager.cpp +++ b/src/configmanager.cpp @@ -22,7 +22,7 @@ void defaultConfig() { cfg.txpower = 15; // 2-15, lora tx power cfg.adrmode = 1; // 0=disabled, 1=enabled cfg.screensaver = 0; // 0=disabled, 1=enabled - cfg.screenon = 1; // 0=disbaled, 1=enabled + cfg.screenon = 1; // 0=disabled, 1=enabled cfg.countermode = 0; // 0=cyclic, 1=cumulative, 2=cyclic confirmed cfg.rssilimit = 0; // threshold for rssilimiter, negative value! cfg.sendcycle = SEND_SECS; // payload send cycle [seconds/2] @@ -32,6 +32,7 @@ void defaultConfig() { cfg.wifiant = 0; // 0=internal, 1=external (for LoPy/LoPy4) cfg.vendorfilter = 1; // 0=disabled, 1=enabled cfg.rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%) + cfg.gpsmode = 1; // 0=disabled, 1=enabled strncpy( cfg.version, PROGVERSION, sizeof(cfg.version)-1 ); } @@ -120,6 +121,9 @@ void saveConfig() { if( nvs_get_i8(my_handle, "rgblum", &flash8) != ESP_OK || flash8 != cfg.rgblum ) nvs_set_i8(my_handle, "rgblum", cfg.rgblum); + if( nvs_get_i8(my_handle, "gpsmode", &flash8) != ESP_OK || flash8 != cfg.gpsmode ) + nvs_set_i8(my_handle, "gpsmode", cfg.gpsmode); + if( nvs_get_i16(my_handle, "rssilimit", &flash16) != ESP_OK || flash16 != cfg.rssilimit ) nvs_set_i16(my_handle, "rssilimit", cfg.rssilimit); @@ -284,6 +288,14 @@ void loadConfig() { saveConfig(); } + if( nvs_get_i8(my_handle, "gpsmode", &flash8) == ESP_OK ) { + cfg.gpsmode = flash8; + ESP_LOGI(TAG, "GPSmode = %d", flash8); + } else { + ESP_LOGI(TAG, "GPSmode set to default %d", cfg.gpsmode); + saveConfig(); + } + nvs_close(my_handle); ESP_LOGI(TAG, "Done"); diff --git a/src/configmanager.h b/src/configmanager.h new file mode 100644 index 00000000..f9c5d3fa --- /dev/null +++ b/src/configmanager.h @@ -0,0 +1,8 @@ +#ifndef CONFIGMANAGER_H +#define CONFIGMANAGER_H + +void eraseConfig(void); +void saveConfig(void); +void loadConfig(void); + +#endif \ No newline at end of file diff --git a/src/globals.h b/src/globals.h index 275f1c3b..04ef15d7 100644 --- a/src/globals.h +++ b/src/globals.h @@ -11,6 +11,11 @@ #include #endif +//GPS +#ifdef HAS_GPS + #include +#endif + // LMIC-Arduino LoRaWAN Stack #include #include @@ -32,7 +37,7 @@ typedef struct { uint8_t screensaver; // 0=disabled, 1=enabled uint8_t screenon; // 0=disabled, 1=enabled uint8_t countermode; // 0=cyclic unconfirmed, 1=cumulative, 2=cyclic confirmed - int16_t rssilimit; // threshold for rssilimiter, negative value! + int16_t rssilimit; // threshold for rssilimiter, negative value! uint8_t sendcycle; // payload send cycle [seconds/2] uint8_t wifichancycle; // wifi channel switch cycle [seconds/100] uint8_t blescantime; // BLE scan cycle duration [seconds] @@ -40,14 +45,29 @@ typedef struct { uint8_t wifiant; // 0=internal, 1=external (for LoPy/LoPy4) uint8_t vendorfilter; // 0=disabled, 1=enabled uint8_t rgblum; // RGB Led luminosity (0..100%) - char version[10]; // Firmware version + uint8_t gpsmode; // 0=disabled, 1=enabled + char version[10]; // Firmware version } configData_t; +#ifdef HAS_GPS + typedef struct { + uint32_t latitude; + uint32_t longitude; + uint8_t satellites; + uint16_t hdop; + uint16_t altitude; + } gpsStatus_t; + extern gpsStatus_t gps_status; // struct for storing gps data + extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe +#endif + extern configData_t cfg; extern uint64_t uptimecounter; -extern osjob_t sendjob; +extern osjob_t sendjob, rcmdjob; extern char display_lora[], display_lmic[]; extern int countermode, screensaver, adrmode, lorasf, txpower, rlim; extern uint16_t macs_total, macs_wifi, macs_ble; // MAC counters extern std::set macs; -extern hw_timer_t * channelSwitch; // hardware timer used for wifi channel switching \ No newline at end of file +extern hw_timer_t * channelSwitch; // hardware timer used for wifi channel switching +extern xref2u1_t rcmd_data; // buffer for rcommand results size +extern u1_t rcmd_data_size; // buffer for rcommand results size diff --git a/src/gpsread.cpp b/src/gpsread.cpp new file mode 100644 index 00000000..eb38eb73 --- /dev/null +++ b/src/gpsread.cpp @@ -0,0 +1,80 @@ +#ifdef HAS_GPS + +#include "globals.h" + +// Local logging tag +static const char TAG[] = "main"; + +// read GPS data and cast to global struct +void gps_read() { + gps_status.latitude = (uint32_t) (gps.location.lat() * 1000000); + gps_status.longitude = (uint32_t) (gps.location.lng() * 1000000); + gps_status.satellites = (uint8_t) gps.satellites.value(); + gps_status.hdop = (uint16_t) gps.hdop.value(); + gps_status.altitude = (uint16_t) gps.altitude.meters(); +} + +// GPS serial feed FreeRTos Task +void gps_loop(void * pvParameters) { + + configASSERT( ( ( uint32_t ) pvParameters ) == 1 ); // FreeRTOS check + + // initialize and, if needed, configure, GPS + #if defined GPS_SERIAL + HardwareSerial GPS_Serial(1); + #elif defined GPS_I2C + // to be done + #endif + + while(1) { + + if (cfg.gpsmode) + { + #if defined GPS_SERIAL + + // serial connect to GPS device + GPS_Serial.begin(GPS_SERIAL); + + while(cfg.gpsmode) { + // feed GPS decoder with serial NMEA data from GPS device + while (GPS_Serial.available()) { + gps.encode(GPS_Serial.read()); + } + vTaskDelay(1/portTICK_PERIOD_MS); // reset watchdog + } + // after GPS function was disabled, close connect to GPS device + GPS_Serial.end(); + + #elif defined GPS_I2C + + // I2C connect to GPS device with 100 kHz + Wire.begin(GPS_I2C_PINS, 100000); + Wire.beginTransmission(GPS_I2C_ADDRESS_WRITE); + Wire.write(0x00); + + i2c_ret == Wire.beginTransmission(GPS_I2C_ADDRESS_READ); + if (i2c_ret == 0) { // check if device seen on i2c bus + while(cfg.gpsmode) { + // feed GPS decoder with serial NMEA data from GPS device + while (Wire.available()) { + Wire.requestFrom(GPS_I2C_ADDRESS_READ, 255); + gps.encode(Wire.read()); + vTaskDelay(1/portTICK_PERIOD_MS); // reset watchdog + } + } + // after GPS function was disabled, close connect to GPS device + + Wire.endTransmission(); + Wire.setClock(400000); // Set back to 400KHz to speed up OLED + } + + #endif + } + + vTaskDelay(1/portTICK_PERIOD_MS); // reset watchdog + + } // end of infinite loop + +} // gps_loop() + +#endif // HAS_GPS \ No newline at end of file diff --git a/src/hal/fipy.h b/src/hal/fipy.h index 7622d811..9f88928d 100644 --- a/src/hal/fipy.h +++ b/src/hal/fipy.h @@ -2,18 +2,18 @@ #define CFG_sx1272_radio 1 #define HAS_LED NOT_A_PIN // FiPy has no on board LED, so we use RGB LED -#define HAS_RGB_LED 0 // WS2812B RGB LED on GPIO0 +#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0 // Hardware pin definitions for Pycom FiPy board -#define PIN_SPI_SS 18 -#define PIN_SPI_MOSI 27 -#define PIN_SPI_MISO 19 -#define PIN_SPI_SCK 5 +#define PIN_SPI_SS GPIO_NUM_18 +#define PIN_SPI_MOSI GPIO_NUM_27 +#define PIN_SPI_MISO GPIO_NUM_19 +#define PIN_SPI_SCK GPIO_NUM_5 #define RST LMIC_UNUSED_PIN -#define DIO0 23 // LoRa IRQ -#define DIO1 23 // workaround +#define DIO0 GPIO_NUM_23 // LoRa IRQ +#define DIO1 GPIO_NUM_23 // workaround #define DIO2 LMIC_UNUSED_PIN // select WIFI antenna (internal = onboard / external = u.fl socket) -#define HAS_ANTENNA_SWITCH 21 // pin for switching wifi antenna -#define WIFI_ANTENNA 0 // 0 = internal, 1 = external +#define HAS_ANTENNA_SWITCH GPIO_NUM_21 // pin for switching wifi antenna +#define WIFI_ANTENNA 0 // 0 = internal, 1 = external diff --git a/src/hal/heltec.h b/src/hal/heltec.h index ed2e63db..74bdb89a 100644 --- a/src/hal/heltec.h +++ b/src/hal/heltec.h @@ -8,18 +8,18 @@ #define HAS_BUTTON GPIO_NUM_0 // button "PROG" on board // re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS 18 // ESP32 GPIO18 (Pin18) -- SX1276 NSS (Pin19) SPI Chip Select Input -#define PIN_SPI_MOSI 27 // ESP32 GPIO27 (Pin27) -- SX1276 MOSI (Pin18) SPI Data Input -#define PIN_SPI_MISO 19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output -#define PIN_SPI_SCK 5 // ESP32 GPIO5 (Pin5) -- SX1276 SCK (Pin16) SPI Clock Input +#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- SX1276 NSS (Pin19) SPI Chip Select Input +#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- SX1276 MOSI (Pin18) SPI Data Input +#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output +#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- SX1276 SCK (Pin16) SPI Clock Input // non arduino pin definitions -#define RST 14 // ESP32 GPIO14 (Pin14) -- SX1276 NRESET (Pin7) Reset Trigger Input -#define DIO0 26 // ESP32 GPIO26 (Pin15) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done -#define DIO1 33 // ESP32 GPIO33 (Pin13) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout +#define RST GPIO_NUM_14 // ESP32 GPIO14 (Pin14) -- SX1276 NRESET (Pin7) Reset Trigger Input +#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 (Pin15) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done +#define DIO1 GPIO_NUM_33 // ESP32 GPIO33 (Pin13) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout #define DIO2 LMIC_UNUSED_PIN // 32 ESP32 GPIO32 (Pin12) -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only) // Hardware pin definitions for Heltec LoRa-32 Board with OLED SSD1306 I2C Display -#define OLED_RST 16 // ESP32 GPIO16 (Pin16) -- SD1306 RST -#define OLED_SDA 4 // ESP32 GPIO4 (Pin4) -- SD1306 D1+D2 -#define OLED_SCL 15 // ESP32 GPIO15 (Pin15) -- SD1306 D0 +#define OLED_RST GPIO_NUM_16 // ESP32 GPIO16 (Pin16) -- SD1306 RST +#define OLED_SDA GPIO_NUM_4 // ESP32 GPIO4 (Pin4) -- SD1306 D1+D2 +#define OLED_SCL GPIO_NUM_15 // ESP32 GPIO15 (Pin15) -- SD1306 D0 diff --git a/src/hal/lopy.h b/src/hal/lopy.h index 219a3a96..d54004f6 100644 --- a/src/hal/lopy.h +++ b/src/hal/lopy.h @@ -2,16 +2,25 @@ #define CFG_sx1272_radio 1 #define HAS_LED NOT_A_PIN // LoPy4 has no on board LED, so we use RGB LED on LoPy4 -#define HAS_RGB_LED 0 // WS2812B RGB LED on GPIO0 +#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0 + +// !!EXPERIMENTAL - not tested yet!! +// uncomment this only if your LoPy lives on a Pytrack expansion board with GPS +// see http://www.quectel.com/UploadImage/Downlad/Quectel_L76-L_I2C_Application_Note_V1.0.pdf +//#define HAS_GPS 1 +//#define GPS_I2C_PINS GPIO_NUM_9, GPIO_NUM_8 // SDA, SCL +//#define GPS_I2C_ADDRESS_READ 0x21 +//#define GPS_I2C_ADDRESS_WRITE 0x20 +//#define HAS_BUTTON GPIO_NUM_4 // Hardware pin definitions for Pycom LoPy board -#define PIN_SPI_SS 17 -#define PIN_SPI_MOSI 27 -#define PIN_SPI_MISO 19 -#define PIN_SPI_SCK 5 -#define RST 18 -#define DIO0 23 // LoRa IRQ -#define DIO1 23 // workaround +#define PIN_SPI_SS GPIO_NUM_17 +#define PIN_SPI_MOSI GPIO_NUM_27 +#define PIN_SPI_MISO GPIO_NUM_19 +#define PIN_SPI_SCK GPIO_NUM_5 +#define RST GPIO_NUM_18 +#define DIO0 GPIO_NUM_23 // LoRa IRQ +#define DIO1 GPIO_NUM_23 // workaround #define DIO2 LMIC_UNUSED_PIN // select WIFI antenna (internal = onboard / external = u.fl socket) diff --git a/src/hal/lopy4.h b/src/hal/lopy4.h index aaed263a..708a861d 100644 --- a/src/hal/lopy4.h +++ b/src/hal/lopy4.h @@ -2,16 +2,25 @@ #define CFG_sx1276_radio 1 #define HAS_LED NOT_A_PIN // LoPy4 has no on board LED, so we use RGB LED on LoPy4 -#define HAS_RGB_LED 0 // WS2812B RGB LED on GPIO0 +#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0 + +// !!EXPERIMENTAL - not tested yet!!f +// uncomment this only if your LoPy lives on a Pytrack expansion board with GPS +// see http://www.quectel.com/UploadImage/Downlad/Quectel_L76-L_I2C_Application_Note_V1.0.pdf +//#define HAS_GPS 1 +//#define GPS_I2C_PINS GPIO_NUM_9, GPIO_NUM_8 // SDA, SCL +//#define GPS_I2C_ADDRESS_READ 0x21 +//#define GPS_I2C_ADDRESS_WRITE 0x20 +//#define HAS_BUTTON GPIO_NUM_4 // Hardware pin definitions for Pycom LoPy4 board -#define PIN_SPI_SS 18 -#define PIN_SPI_MOSI 27 -#define PIN_SPI_MISO 19 -#define PIN_SPI_SCK 5 +#define PIN_SPI_SS GPIO_NUM_18 +#define PIN_SPI_MOSI GPIO_NUM_27 +#define PIN_SPI_MISO GPIO_NUM_19 +#define PIN_SPI_SCK GPIO_NUM_5 #define RST LMIC_UNUSED_PIN -#define DIO0 23 // LoRa IRQ -#define DIO1 23 // workaround +#define DIO0 GPIO_NUM_23 // LoRa IRQ +#define DIO1 GPIO_NUM_23 // workaround #define DIO2 LMIC_UNUSED_PIN // select WIFI antenna (internal = onboard / external = u.fl socket) diff --git a/src/hal/ttgobeam.h b/src/hal/ttgobeam.h index aa3ac479..aafbb3a9 100644 --- a/src/hal/ttgobeam.h +++ b/src/hal/ttgobeam.h @@ -2,21 +2,21 @@ #define CFG_sx1276_radio 1 // HPD13A LoRa SoC -#define HAS_LED 21 // on board green LED_G1 -// #define HAS_BUTTON GPIO_NUM_39 // button on board next to battery indicator LED (other one is reset) -> not tested yet +#define HAS_LED GPIO_NUM_21 // on board green LED_G1 +//#define HAS_BUTTON GPIO_NUM_39 // on board button "BOOT" (next to reset button) !! seems not to work!! #define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7 #define BATT_FACTOR 2 // voltage divider 100k/100k on board -// #define HAS_GPS // to be done -// GSP serial (9600, SERIAL_8N1, 12, 15); //17-TX 18-RX +#define HAS_GPS 1 // use on board GPS +#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_12, GPIO_NUM_15 // UBlox NEO 6M or 7M with default configuration // re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS 18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select Input -#define PIN_SPI_MOSI 27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input -#define PIN_SPI_MISO 19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output -#define PIN_SPI_SCK 5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input +#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select Input +#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input +#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output +#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input // non arduino pin definitions #define RST LMIC_UNUSED_PIN // connected to ESP32 RST/EN -#define DIO0 26 // ESP32 GPIO26 <-> HPD13A IO0 -#define DIO1 33 // Lora1 <-> HPD13A IO1 // !! NEEDS EXTERNAL WIRING !! -#define DIO2 32 // Lora2 <-> HPD13A IO2 // needs external wiring, but not necessary for LoRa, only FSK +#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 <-> HPD13A IO0 +#define DIO1 GPIO_NUM_33 // Lora1 <-> HPD13A IO1 // !! NEEDS EXTERNAL WIRING !! +#define DIO2 LMIC_UNUSED_PIN // Lora2 <-> HPD13A IO2 // needs external wiring, but not necessary for LoRa, only FSK diff --git a/src/hal/ttgov1.h b/src/hal/ttgov1.h index c8e1a12d..df5735b5 100644 --- a/src/hal/ttgov1.h +++ b/src/hal/ttgov1.h @@ -9,18 +9,18 @@ #define HAS_BUTTON GPIO_NUM_0 // button "PRG" on board // re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS 18 // ESP32 GPIO18 (Pin18) -- SX1276 NSS (Pin19) SPI Chip Select Input -#define PIN_SPI_MOSI 27 // ESP32 GPIO27 (Pin27) -- SX1276 MOSI (Pin18) SPI Data Input -#define PIN_SPI_MISO 19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output -#define PIN_SPI_SCK 5 // ESP32 GPIO5 (Pin5) -- SX1276 SCK (Pin16) SPI Clock Input +#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- SX1276 NSS (Pin19) SPI Chip Select Input +#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- SX1276 MOSI (Pin18) SPI Data Input +#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output +#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- SX1276 SCK (Pin16) SPI Clock Input // non arduino pin definitions -#define RST 14 // ESP32 GPIO14 (Pin14) -- SX1276 NRESET (Pin7) Reset Trigger Input -#define DIO0 26 // ESP32 GPIO26 (Pin15) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done -#define DIO1 33 // ESP32 GPIO33 (Pin13) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout +#define RST GPIO_NUM_14 // ESP32 GPIO14 (Pin14) -- SX1276 NRESET (Pin7) Reset Trigger Input +#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 (Pin15) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done +#define DIO1 GPIO_NUM_33 // ESP32 GPIO33 (Pin13) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout #define DIO2 LMIC_UNUSED_PIN // 32 ESP32 GPIO32 (Pin12) -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only) // Hardware pin definitions for TTGOv1 Board with OLED SSD1306 I2C Display -#define OLED_RST 16 // ESP32 GPIO16 (Pin16) -- SD1306 Reset -#define OLED_SDA 4 // ESP32 GPIO4 (Pin4) -- SD1306 Data -#define OLED_SCL 15 // ESP32 GPIO15 (Pin15) -- SD1306 Clock +#define OLED_RST GPIO_NUM_16 // ESP32 GPIO16 (Pin16) -- SD1306 Reset +#define OLED_SDA GPIO_NUM_4 // ESP32 GPIO4 (Pin4) -- SD1306 Data +#define OLED_SCL GPIO_NUM_15 // ESP32 GPIO15 (Pin15) -- SD1306 Clock diff --git a/src/hal/ttgov2.h b/src/hal/ttgov2.h index ebdfe87d..e4bb49b4 100644 --- a/src/hal/ttgov2.h +++ b/src/hal/ttgov2.h @@ -10,21 +10,21 @@ #define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature // re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS 18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select Input -#define PIN_SPI_MOSI 27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input -#define PIN_SPI_MISO 19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output -#define PIN_SPI_SCK 5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input +#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select Input +#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input +#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output +#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input // non arduino pin definitions #define RST LMIC_UNUSED_PIN // connected to ESP32 RST/EN -#define DIO0 26 // ESP32 GPIO26 wired on PCB to HPD13A -#define DIO1 33 // HPDIO1 on pcb, needs to be wired external to GPIO33 +#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 wired on PCB to HPD13A +#define DIO1 GPIO_NUM_33 // HPDIO1 on pcb, needs to be wired external to GPIO33 #define DIO2 LMIC_UNUSED_PIN // 32 HPDIO2 on pcb, needs to be wired external to GPIO32 (not necessary for LoRa, only FSK) // Hardware pin definitions for TTGO V2 Board with OLED SSD1306 0,96" I2C Display #define OLED_RST U8X8_PIN_NONE // connected to CPU RST/EN -#define OLED_SDA 21 // ESP32 GPIO4 (Pin4) -- SD1306 D1+D2 -#define OLED_SCL 22 // ESP32 GPIO15 (Pin15) -- SD1306 D0 +#define OLED_SDA GPIO_NUM_21 // ESP32 GPIO4 (Pin4) -- SD1306 D1+D2 +#define OLED_SCL GPIO_NUM_22 // ESP32 GPIO15 (Pin15) -- SD1306 D0 /* diff --git a/src/hal/ttgov21.h b/src/hal/ttgov21.h index ebedf11b..1e3b38ec 100644 --- a/src/hal/ttgov21.h +++ b/src/hal/ttgov21.h @@ -4,23 +4,23 @@ #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C #define DISPLAY_FLIP 1 // rotated display -#define HAS_LED 23 // green on board LED_G3 (not in initial board version) +#define HAS_LED GPIO_NUM_23 // green on board LED_G3 (not in initial board version) #define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7 #define BATT_FACTOR 2 // voltage divider 100k/100k on board // re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS 18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select Input -#define PIN_SPI_MOSI 27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input -#define PIN_SPI_MISO 19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output -#define PIN_SPI_SCK 5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input +#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select Input +#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input +#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output +#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input // non arduino pin definitions #define RST LMIC_UNUSED_PIN // connected to ESP32 RST/EN -#define DIO0 26 // ESP32 GPIO26 <-> HPD13A IO0 -#define DIO1 33 // ESP32 GPIO33 <-> HPDIO1 <-> HPD13A IO1 -#define DIO2 32 // ESP32 GPIO32 <-> HPDIO2 <-> HPD13A IO2 +#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 <-> HPD13A IO0 +#define DIO1 GPIO_NUM_33 // ESP32 GPIO33 <-> HPDIO1 <-> HPD13A IO1 +#define DIO2 GPIO_NUM_32 // ESP32 GPIO32 <-> HPDIO2 <-> HPD13A IO2 // Hardware pin definitions for TTGO V2 Board with OLED SSD1306 0,96" I2C Display #define OLED_RST U8X8_PIN_NONE // connected to CPU RST/EN -#define OLED_SDA 21 // ESP32 GPIO4 (Pin4) -- SD1306 D1+D2 -#define OLED_SCL 22 // ESP32 GPIO15 (Pin15) -- SD1306 D0 \ No newline at end of file +#define OLED_SDA GPIO_NUM_21 // ESP32 GPIO4 (Pin4) -- SD1306 D1+D2 +#define OLED_SCL GPIO_NUM_22 // ESP32 GPIO15 (Pin15) -- SD1306 D0 \ No newline at end of file diff --git a/src/lorawan.cpp b/src/lorawan.cpp index ec80d252..b832b311 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -109,14 +109,14 @@ void do_send(osjob_t* j){ // Check if there is a pending TX/RX job running if (LMIC.opmode & OP_TXRXPEND) { - ESP_LOGI(TAG, "OP_TXRXPEND, not sending"); + ESP_LOGI(TAG, "LoRa busy, rescheduling"); sprintf(display_lmic, "LORA BUSY"); goto end; } // prepare payload with sum of unique WIFI MACs seen static uint8_t mydata[4]; - + mydata[0] = (macs_wifi & 0xff00) >> 8; mydata[1] = macs_wifi & 0xff; @@ -129,11 +129,31 @@ void do_send(osjob_t* j){ mydata[3] = 0; } - // Prepare upstream data transmission at the next possible time. - LMIC_setTxData2(1, mydata, sizeof(mydata), (cfg.countermode & 0x02)); - ESP_LOGI(TAG, "Packet queued"); - sprintf(display_lmic, "PACKET QUEUED"); + #ifdef HAS_GPS + static uint8_t gpsdata[18]; + if (cfg.gpsmode && gps.location.isValid()) { + gps_read(); + memcpy (gpsdata, mydata, 4); + memcpy (gpsdata+4, &gps_status, sizeof(gps_status)); + ESP_LOGI(TAG, "lat=%.6f / lon=%.6f | %u Sats | HDOP=%.1f | Altitude=%u m", \ + gps_status.latitude / (float) 1000000, \ + gps_status.longitude / (float) 1000000, \ + gps_status.satellites, \ + gps_status.hdop / (float) 100, \ + gps_status.altitude); + LMIC_setTxData2(COUNTERPORT, gpsdata, sizeof(gpsdata), (cfg.countermode & 0x02)); + ESP_LOGI(TAG, "%d bytes queued to send", sizeof(gpsdata)); + } + else { + #endif + LMIC_setTxData2(COUNTERPORT, mydata, sizeof(mydata), (cfg.countermode & 0x02)); + ESP_LOGI(TAG, "%d bytes queued to send", sizeof(mydata)); + sprintf(display_lmic, "PACKET QUEUED"); + #ifdef HAS_GPS + } + #endif + // clear counter if not in cumulative counter mode if (cfg.countermode != 1) { reset_counters(); // clear macs container and reset all counters @@ -170,10 +190,6 @@ void onEvent (ev_t ev) { strcpy_P(buff, PSTR("JOINED")); sprintf(display_lora, " "); // clear previous lmic status message from display - // Disable link check validation (automatically enabled - // during join, but not supported by TTN at this time). -> do we need this? - // LMIC_setLinkCheckMode(0); - // set data rate adaptation LMIC_setAdrMode(cfg.adrmode); // Set data rate and transmit power (note: txpower seems to be ignored by the library) @@ -200,8 +216,9 @@ void onEvent (ev_t ev) { unsigned char* buffer = new unsigned char[MAX_LEN_FRAME]; memcpy(buffer, LMIC.frame, MAX_LEN_FRAME); //Copy data from cfg to char* int i, k = LMIC.dataBeg, l = LMIC.dataBeg+LMIC.dataLen-2; - for (i=k; i<=l; i+=2) + for (i=k; i<=l; i+=2) { rcommand(buffer[i], buffer[i+1]); + } delete[] buffer; //free memory } } diff --git a/src/lorawan.h b/src/lorawan.h new file mode 100644 index 00000000..de54b6e8 --- /dev/null +++ b/src/lorawan.h @@ -0,0 +1,10 @@ +#ifndef LORAWAN_H +#define LORAWAN_H + +void onEvent(ev_t ev); +void do_send(osjob_t* j); +void gen_lora_deveui(uint8_t * pdeveui); +void RevBytes(unsigned char* b, size_t c); +void get_hard_deveui(uint8_t *pdeveui); + +#endif \ No newline at end of file diff --git a/src/macsniff.h b/src/macsniff.h index a92046df..5eb07f30 100644 --- a/src/macsniff.h +++ b/src/macsniff.h @@ -1,3 +1,6 @@ +#ifndef MACSNIFF_H +#define MACSNIFF_H + // ESP32 Functions #include @@ -26,3 +29,5 @@ void wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type); // function defined in rokkithash.cpp uint32_t rokkit(const char * , int ); + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 9d169e01..a6216a3c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -39,7 +39,7 @@ Refer to LICENSE.txt file in repository for more details. // Initialize global variables configData_t cfg; // struct holds current device configuration -osjob_t sendjob; // LMIC job handler +osjob_t sendjob, rcmdjob; // LMIC job handler uint64_t uptimecounter = 0; // timer global for uptime counter uint8_t DisplayState = 0; // globals for state machine uint16_t macs_total = 0, macs_wifi = 0, macs_ble = 0; // MAC counters globals for display @@ -52,13 +52,20 @@ uint16_t LEDBlinkDuration = 0; // How long the blink need to be uint16_t LEDColor = COLOR_NONE; // state machine variable to set RGB LED color hw_timer_t * displaytimer = NULL; // configure hardware timer used for cyclic display refresh hw_timer_t * channelSwitch = NULL; // configure hardware timer used for wifi channel switching +xref2u1_t rcmd_data; // buffer for rcommand results size +u1_t rcmd_data_size; // buffer for rcommand results size -portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; // sync main loop and ISR when modifying shared variable DisplayIRQ +#ifdef HAS_GPS + gpsStatus_t gps_status; // struct for storing gps data + TinyGPSPlus gps; // create TinyGPS++ instance +#endif + +portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; // sync main loop and ISR when modifying IRQ handler shared variables std::set macs; // associative container holds total of unique MAC adress hashes (Wifi + BLE) // this variables will be changed in the ISR, and read in main loop -static volatile int ButtonPressed = 0, DisplayTimerIRQ = 0, ChannelTimerIRQ = 0; +static volatile int ButtonPressedIRQ = 0, DisplayTimerIRQ = 0, ChannelTimerIRQ = 0; // local Tag for logging static const char TAG[] = "main"; @@ -130,37 +137,14 @@ void lorawan_loop(void * pvParameters) { configASSERT( ( ( uint32_t ) pvParameters ) == 1 ); // FreeRTOS check - //static uint16_t lorawait = 0; - while(1) { - - // execute LMIC jobs - os_runloop_once(); - - /* - // check if payload is sent - while(LMIC.opmode & OP_TXRXPEND) { - if(!lorawait) - sprintf(display_lora, "LoRa wait"); - lorawait++; - // in case sending really fails: reset LMIC and rejoin network - if( (lorawait % MAXLORARETRY ) == 0) { - ESP_LOGI(TAG, "Payload not sent, resetting LMIC and rejoin"); - lorawait = 0; - LMIC_reset(); // Reset the MAC state. Session and pending data transfers will be discarded. - }; - vTaskDelay(1000/portTICK_PERIOD_MS); - } - */ - - vTaskDelay(1/portTICK_PERIOD_MS); // reset watchdog + os_runloop_once(); // execute LMIC jobs + vTaskDelay(1/portTICK_PERIOD_MS); // reset watchdog } } - /* end LMIC specific parts --------------------------------------------------------------- */ - /* beginn hardware specific parts -------------------------------------------------------- */ #ifdef HAS_DISPLAY @@ -183,14 +167,14 @@ void lorawan_loop(void * pvParameters) { bool btstop = btStop(); #endif +// Button IRQ Handler Routine, IRAM_ATTR necessary here, see https://github.com/espressif/arduino-esp32/issues/855 #ifdef HAS_BUTTON - // Button IRQ - // IRAM_ATTR necessary here, see https://github.com/espressif/arduino-esp32/issues/855 void IRAM_ATTR ButtonIRQ() { - ButtonPressed++; + ButtonPressedIRQ++; } #endif +// Wifi Channel Rotation Timer IRQ Handler Routine void IRAM_ATTR ChannelSwitchIRQ() { portENTER_CRITICAL(&timerMux); ChannelTimerIRQ++; @@ -360,11 +344,14 @@ uint64_t uptime() { #ifdef HAS_BUTTON void readButton() { - if (ButtonPressed) { - ButtonPressed--; + if (ButtonPressedIRQ) { + portENTER_CRITICAL(&timerMux); + ButtonPressedIRQ--; + portEXIT_CRITICAL(&timerMux); + ESP_LOGI(TAG, "Button pressed"); ESP_LOGI(TAG, "Button pressed, resetting device to factory defaults"); eraseConfig(); - esp_restart(); + esp_restart(); } } #endif @@ -483,6 +470,10 @@ void setup() { ESP_LOGI(TAG, "ESP32 SDK: %s", ESP.getSdkVersion()); #endif +#ifdef HAS_GPS + ESP_LOGI(TAG, "TinyGPS+ version %s", TinyGPSPlus::libraryVersion()); +#endif + // read settings from NVRAM loadConfig(); // includes initialize if necessary @@ -495,10 +486,8 @@ void setup() { #ifdef HAS_RGB_LED rgb_set_color(COLOR_PINK); strcat(features, " RGB"); - delay(1000); #endif - // initialize button handling if needed #ifdef HAS_BUTTON strcat(features, " BTN_"); @@ -521,6 +510,11 @@ void setup() { antenna_init(); #endif +// initialize gps if present +#ifdef HAS_GPS + strcat(features, " GPS"); +#endif + #ifdef HAS_DISPLAY strcat(features, " OLED"); // initialize display @@ -588,7 +582,15 @@ xTaskCreatePinnedToCore(sniffer_loop, "wifisniffer", 2048, ( void * ) 1, 1, NULL start_BLEscan(); } #endif - + +// if device has GPS and GPS function is enabled, start GPS reader task on core 0 +#ifdef HAS_GPS + if (cfg.gpsmode) { + ESP_LOGI(TAG, "Starting GPS task on core 0"); + xTaskCreatePinnedToCore(gps_loop, "gpsfeed", 2048, ( void * ) 1, 1, NULL, 0); + } +#endif + // kickoff sendjob -> joins network and rescedules sendjob for cyclic transmitting payload do_send(&sendjob); @@ -627,6 +629,12 @@ void loop() { reset_salt(); // get new salt for salting hashes } + #ifdef HAS_GPS + // log NMEA status every 30 seconds, useful for debugging GPS connection + if ( (uptime() % 30000) == 0 ) + ESP_LOGD(TAG, "GPS NMEA data: passed %d / failed: %d / with fix: %d", gps.passedChecksum(), gps.failedChecksum(), gps.sentencesWithFix()); + #endif + vTaskDelay(1/portTICK_PERIOD_MS); // reset watchdog } // end of infinite main loop diff --git a/src/main.h b/src/main.h index d0ba04c2..a2e0eeb0 100644 --- a/src/main.h +++ b/src/main.h @@ -1,6 +1,10 @@ +#include "configmanager.h" +#include "lorawan.h" +#include "macsniff.h" + // program version - note: increment version after modifications to configData_t struct!! -#define PROGVERSION "1.3.71" // use max 10 chars here! +#define PROGVERSION "1.3.8" // use max 10 chars here! #define PROGNAME "PAXCNT" //--- Declarations --- @@ -23,25 +27,15 @@ void reset_counters(void); void blink_LED(uint16_t set_color, uint16_t set_blinkduration); void led_loop(void); -// defined in configmanager.cpp -void eraseConfig(void); -void saveConfig(void); -void loadConfig(void); - -// defined in lorawan.cpp -void onEvent(ev_t ev); -void do_send(osjob_t* j); -void gen_lora_deveui(uint8_t * pdeveui); -void RevBytes(unsigned char* b, size_t c); -void get_hard_deveui(uint8_t *pdeveui); - -// defined in wifisniffer.cpp -void wifi_sniffer_init(void); -void wifi_sniffer_set_channel(uint8_t channel); -void wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type); // defined in blescan.cpp #ifdef BLECOUNTER - void start_BLEscan(void); - void stop_BLEscan(void); + void start_BLEscan(void); + void stop_BLEscan(void); +#endif + +//defined in gpsread.cpp +#ifdef HAS_GPS + void gps_read(void); + void gps_loop(void * pvParameters); #endif \ No newline at end of file diff --git a/src/paxcounter.conf b/src/paxcounter.conf index bc709486..47432573 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -37,11 +37,14 @@ // LoRa payload send cycle --> take care of duty cycle of LoRaWAN network! <-- #define SEND_SECS 120 // [seconds/2] -> 240 sec. #define MEM_LOW 2048 // [Bytes] low memory threshold triggering a send cycle +#define RETRANSMIT_RCMD 5 // [seconds] wait time before retransmitting rcommand results // Default LoRa Spreadfactor #define LORASFDEFAULT 9 // 7 ... 12 SF, according to LoRaWAN specs #define MAXLORARETRY 500 // maximum count of TX retries if LoRa busy #define RCMDPORT 2 // LoRaWAN Port on which device listenes for remote commands +#define GPSPORT 3 // LoRaWAN Port on which device sends gps data +#define COUNTERPORT 1 // LoRaWAN Port on which device sends counts // Default RGB LED luminosity (in %) #define RGBLUMINOSITY 30 // 30% diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 816820c5..fa061f9f 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -29,6 +29,25 @@ typedef struct { uint32_t read_voltage(void); #endif +// function sends result of get commands to LoRaWAN network +void do_transmit(osjob_t* j){ + // check if there is a pending TX/RX job running, if yes then reschedule transmission + if (LMIC.opmode & OP_TXRXPEND) { + ESP_LOGI(TAG, "LoRa busy, rescheduling"); + sprintf(display_lmic, "LORA BUSY"); + os_setTimedCallback(&rcmdjob, os_getTime()+sec2osticks(RETRANSMIT_RCMD), do_transmit); + } + LMIC_setTxData2(RCMDPORT, rcmd_data, rcmd_data_size, 0); // send data unconfirmed on RCMD Port + ESP_LOGI(TAG, "%d bytes queued to send", rcmd_data_size); + sprintf(display_lmic, "PACKET QUEUED"); +} + +// help function to transmit result of get commands, since callback function do_transmit() cannot have params +void transmit(xref2u1_t mydata, u1_t mydata_size){ + rcmd_data = mydata; + rcmd_data_size = mydata_size; + do_transmit(&rcmdjob); +} // help function to assign LoRa datarates to numeric spreadfactor values void switch_lora (uint8_t sf, uint8_t tx) { @@ -141,6 +160,14 @@ void set_display(uint8_t val) { } }; +void set_gps(uint8_t val) { + ESP_LOGI(TAG, "Remote command: set GPS to %s", val ? "on" : "off"); + switch (val) { + case 1: cfg.gpsmode = val; break; + default: cfg.gpsmode = 0; break; + } +}; + void set_lorasf(uint8_t val) { ESP_LOGI(TAG, "Remote command: set LoRa SF to %d", val); switch_lora(val, cfg.txpower); @@ -204,30 +231,20 @@ void set_lorapower(uint8_t val) { switch_lora(cfg.lorasf, val); }; -void set_noop (uint8_t val) { - ESP_LOGI(TAG, "Remote command: noop - doing nothing"); -}; - void get_config (uint8_t val) { ESP_LOGI(TAG, "Remote command: get configuration"); - int size = sizeof(configData_t); - LMIC_setTxData2(RCMDPORT, (byte*)&cfg, size, 0); // send data unconfirmed on RCMD Port - ESP_LOGI(TAG, "%d bytes queued in send queue", size); + transmit((byte*)&cfg, sizeof(cfg)); }; void get_uptime (uint8_t val) { ESP_LOGI(TAG, "Remote command: get uptime"); - int size = sizeof(uptimecounter); - LMIC_setTxData2(RCMDPORT, (byte*)&uptimecounter, size, 0); // send data unconfirmed on RCMD Port - ESP_LOGI(TAG, "%d bytes queued in send queue", size); + transmit((byte*)&uptimecounter, sizeof(uptimecounter)); }; void get_cputemp (uint8_t val) { ESP_LOGI(TAG, "Remote command: get cpu temperature"); float temp = temperatureRead(); - int size = sizeof(temp); - LMIC_setTxData2(RCMDPORT, (byte*)&temp, size, 0); // send data unconfirmed on RCMD Port - ESP_LOGI(TAG, "%d bytes queued in send queue", size); + transmit((byte*)&temp, sizeof(temp)); }; void get_voltage (uint8_t val) { @@ -237,11 +254,19 @@ void get_voltage (uint8_t val) { #else uint16_t voltage = 0; #endif - int size = sizeof(voltage); - LMIC_setTxData2(RCMDPORT, (byte*)&voltage, size, 0); // send data unconfirmed on RCMD Port - ESP_LOGI(TAG, "%d bytes queued in send queue", size); + transmit((byte*)&voltage, sizeof(voltage)); }; +void get_gps (uint8_t val) { + ESP_LOGI(TAG, "Remote command: get gps status"); + #ifdef HAS_GPS + gps_read(); + transmit((byte*)&gps_status, sizeof(gps_status)); + ESP_LOGI(TAG, "lat=%f / lon=%f | Sats=%u | HDOP=%u | Alti=%u", gps_status.latitude / 1000000, gps_status.longitude / 1000000, gps_status.satellites, gps_status.hdop, gps_status.altitude); + #else + ESP_LOGE(TAG, "GPS not present"); + #endif +}; // assign previously defined functions to set of numeric remote commands // format: opcode, function, flag (1 = do make settings persistent / 0 = don't) @@ -249,7 +274,7 @@ void get_voltage (uint8_t val) { cmd_t table[] = { {0x01, set_rssi, true}, {0x02, set_countmode, true}, - {0x03, set_noop, false}, + {0x03, set_gps, true}, {0x04, set_display, true}, {0x05, set_lorasf, true}, {0x06, set_lorapower, true}, @@ -266,7 +291,8 @@ cmd_t table[] = { {0x80, get_config, false}, {0x81, get_uptime, false}, {0x82, get_cputemp, false}, - {0x83, get_voltage, false} + {0x83, get_voltage, false}, + {0x84, get_gps, false} }; // check and execute remote command