Merge branch 'development' into gps-fix

This commit is contained in:
Verkehrsrot 2018-06-10 16:58:18 +02:00 committed by GitHub
commit e2e3a1cdde
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 122 additions and 91 deletions

106
README.md
View File

@ -20,18 +20,23 @@ This can all be done with a single small and cheap ESP32 board for less than $20
# Hardware # Hardware
Supported ESP32 based LoRa IoT boards: Supported ESP32 based LoRa IoT boards:
- Heltec LoRa-32 {1} - **Heltec LoRa-32** a)
- TTGOv1 {1} - **TTGOv1** a)
- TTGOv2 {1}{4} - **TTGOv2** a,d)
- TTGOv2.1 {1}{5} - **TTGOv2.1** a),e)
- TTGO T-Beam {4}{5} - **TTGO T-Beam** d),e),f)
- Pycom LoPy {2} - **Pycom LoPy** b),f)*
- Pycom LoPy4 {2} - **Pycom LoPy4** b),f)*
- Pycom FiPy {2} - **Pycom FiPy** b),f)*
- LoLin32 with [LoraNode32 shield](https://github.com/hallard/LoLin32-Lora) {2}{3} - **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) {2}{3} - **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/<board>.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/<board>.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).<br> Target platform must be selected in [platformio.ini](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/platformio.ini).<br>
Hardware dependent settings (pinout etc.) are stored in board files in /hal directory.<br> Hardware dependent settings (pinout etc.) are stored in board files in /hal directory.<br>
@ -102,46 +107,43 @@ Legend for RGB LED (LoPy/LoPy4/FiPy/Lolin32 only):
# Payload # Payload
LoRaWAN Port #1: Counter data **LoRaWAN Port #1:**
byte 1: WiFi counter, MSB Paxcounter data
byte 2: WiFi counter, LSB
byte 3: BLE counter, MSB
byte 4: BLE counter, LSB
LoRaWAN Port #2: Remote commands 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]
see remote control GPS data (only, if GPS is present and has a fix)
LoRaWAN Port #3: GPS data 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]
bytes 1-4: Latitude **LoRaWAN Port #2:**
bytes 4-8: Longitude
bytes 9-10: Satellites
bytes 11-12: HDOP
bytes 13-14: Altitude
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 -
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: Decoder:
```javascript ```javascript
function Decoder(bytes, port) { function Decoder(bytes, port) {
// decode counter messages
var decoded = {}; var decoded = {};
if (port === 1) { if (port === 1) {
decoded.wifi = (bytes[0] << 8) | bytes[1]; decoded.wifi = (bytes[0] << 8) | bytes[1];
decoded.ble = (bytes[2] << 8) | bytes[3]; decoded.ble = (bytes[2] << 8) | bytes[3];
} decoded.latitude = ((bytes[7] << 24) | (bytes[6] << 16) | (bytes[5] << 8) | bytes[4]);
decoded.longitude = ((bytes[11] << 24) | (bytes[10] << 16) | (bytes[9] << 8) | bytes[8]);
// decode GPS messages decoded.sats = (bytes[13] << 8) | bytes[12];
if (port === 3) { decoded.hdop = (bytes[15] << 8) | bytes[14];
decoded.latitude = (bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]; decoded.altitude = (bytes[17] << 8) | bytes[16];
decoded.longitude = (bytes[7] << 24) | (bytes[6] << 16) | (bytes[5] << 8) | bytes[4];
decoded.satellites = (bytes[9] << 8) | bytes[8];
decoded.hdop = (bytes[11] << 8) | bytes[10];
decoded.altitude = (bytes[13] << 8) | bytes[12];
} }
return decoded; return decoded;
@ -153,24 +155,23 @@ Converter:
```javascript ```javascript
function Converter(decoded, port) { function Converter(decoded, port) {
var converted = decoded; var converted = decoded;
// sum up ble + wifi counters
if (port === 1) { if (port === 1) {
converted.pax = converted.ble + converted.wifi; converted.pax = converted.ble + converted.wifi;
converted.hdop /= 100;
converted.latitude /= 1000000;
converted.longitude /= 1000000;
} }
// convert some GPS values
if (port === 3) {
converted.latitude = converted.latitude / 100000;
converted.longitude = converted.longitude / 100000;
converted.hdop = converted.hdop / 100;
return converted; return converted;
} }
``` ```
# Remote control # Remote command set
The device listenes for remote control commands on LoRaWAN Port 2. The device listenes for remote control commands on LoRaWAN Port 2.
Each command is followed by exactly one parameter. Each command is followed by exactly one parameter.
For "set" commands, multiple command/parameter pairs can be concatenated and sent in one downlink, all commands are executed. For "get" commands, only one command/parameter pair per downlink is processed. Multiple command/parameter pairs can be concatenated and sent in one single payload downlink.
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. 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.
@ -185,10 +186,10 @@ Note: all settings are stored in NVRAM and will be reloaded when device starts.
1 = cumulative counter, mac counter is never reset 1 = cumulative counter, mac counter is never reset
2 = cyclic confirmed, like 0 but data is resent until confirmation by network received 2 = cyclic confirmed, like 0 but data is resent until confirmation by network received
0x03 set GPS on/off (NOT YET IMPLEMENTED) 0x03 (NOT YET IMPLEMENTED) set screen saver mode
0 = GPS off [default] 0 = screen saver off [default]
1 = GPS on, GPS data set (if present) is added to payload 1 = screen saver on
0x04 set display on/off 0x04 set display on/off
@ -258,7 +259,7 @@ Note: all settings are stored in NVRAM and will be reloaded when device starts.
0x80 get device configuration 0x80 get device configuration
device answers with it's current configuration. The configuration is a C structure declared in file [globals.h](src/globals.h#L32-L50) with the following definition: device answers with it's current configuration. The configuration is a C structure declared in file [globals.h](src/globals.h#L27-L44) with the following definition:
byte 1: Lora SF (7..12) byte 1: Lora SF (7..12)
byte 2: Lora TXpower (2..15) byte 2: Lora TXpower (2..15)
@ -274,8 +275,7 @@ device answers with it's current configuration. The configuration is a C structu
byte 13: Wifi antenna switch (0=internal, 1=external) byte 13: Wifi antenna switch (0=internal, 1=external)
byte 14: Vendorfilter mode (0=disabled, 1=enabled) byte 14: Vendorfilter mode (0=disabled, 1=enabled)
byte 15: RGB LED luminosity (0..100 %) byte 15: RGB LED luminosity (0..100 %)
byte 16: GPS status (1=on, 0=off) bytes 16-26: Software version (ASCII format, terminating with zero)
bytes 17-27: Software version (ASCII format, terminating with zero)
0x81 get device uptime 0x81 get device uptime
@ -289,13 +289,13 @@ 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) bytes 1-2: battery voltage in millivolt, 0 if unreadable (little endian format)
0x84 get device GPS status (NOT YET IMPLEMENTED) 0x84 get device GPS status
bytes 1-4: latitude bytes 1-4: latitude
bytes 5-8: longitude bytes 5-8: longitude
byte 9: number of satellites byte 9-10: number of satellites
byte 10: HDOP byte 11-12: HDOP
bytes 11-12: altidute [meter] bytes 13-14: altidute [meter]
# License # License

View File

@ -11,11 +11,11 @@
; ---> SELECT TARGET PLATFORM HERE! <--- ; ---> SELECT TARGET PLATFORM HERE! <---
[platformio] [platformio]
;env_default = heltec env_default = heltec
;env_default = ttgov1 ;env_default = ttgov1
;env_default = ttgov2 ;env_default = ttgov2
;env_default = ttgov21 ;env_default = ttgov21
env_default = ttgobeam ;env_default = ttgobeam
;env_default = lopy ;env_default = lopy
;env_default = lopy4 ;env_default = lopy4
;env_default = fipy ;env_default = fipy
@ -32,8 +32,7 @@ lib_deps_display =
lib_deps_rgbled = lib_deps_rgbled =
SmartLeds@>=1.1.3 SmartLeds@>=1.1.3
lib_deps_gps = lib_deps_gps =
https://github.com/mikalhart/TinyGPSPlus.git TinyGPSPlus@>=1.0.2
;beware of TinyGPSplus in PlatformIO library manager, it loads old v.092 labeled as 1.0.0 !!
build_flags = build_flags =
; we need build_flag for logging, otherwise we can't use ESP_LOGx in arduino framework ; 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! <--- ; ---> NOTE: For production run set DEBUG_LEVEL level to NONE! <---

View File

@ -6,7 +6,7 @@
static const char TAG[] = "main"; static const char TAG[] = "main";
// read GPS data and cast to global struct // read GPS data and cast to global struct
void gps_read(){ void gps_read() {
gps_status.latitude = (uint32_t) (gps.location.lat() * 1000000); gps_status.latitude = (uint32_t) (gps.location.lat() * 1000000);
gps_status.longitude = (uint32_t) (gps.location.lng() * 1000000); gps_status.longitude = (uint32_t) (gps.location.lng() * 1000000);
gps_status.satellites = (uint8_t) gps.satellites.value(); gps_status.satellites = (uint8_t) gps.satellites.value();
@ -19,11 +19,10 @@ void gps_loop(void * pvParameters) {
configASSERT( ( ( uint32_t ) pvParameters ) == 1 ); // FreeRTOS check configASSERT( ( ( uint32_t ) pvParameters ) == 1 ); // FreeRTOS check
#ifdef GPS_SERIAL // initialize and, if needed, configure, GPS
#if defined GPS_SERIAL
HardwareSerial GPS_Serial(1); HardwareSerial GPS_Serial(1);
#endif #elif defined GPS_I2C
#ifdef GPS_I2C
// to be done // to be done
#endif #endif
@ -31,7 +30,8 @@ void gps_loop(void * pvParameters) {
if (cfg.gpsmode) if (cfg.gpsmode)
{ {
#ifdef GPS_SERIAL #if defined GPS_SERIAL
// serial connect to GPS device // serial connect to GPS device
GPS_Serial.begin(GPS_SERIAL); GPS_Serial.begin(GPS_SERIAL);
@ -39,19 +39,34 @@ void gps_loop(void * pvParameters) {
// feed GPS decoder with serial NMEA data from GPS device // feed GPS decoder with serial NMEA data from GPS device
while (GPS_Serial.available()) { while (GPS_Serial.available()) {
gps.encode(GPS_Serial.read()); gps.encode(GPS_Serial.read());
vTaskDelay(1/portTICK_PERIOD_MS); // reset watchdog
} }
vTaskDelay(1/portTICK_PERIOD_MS); // reset watchdog
} }
// after GPS function was disabled, close connect to GPS device // after GPS function was disabled, close connect to GPS device
GPS_Serial.end(); GPS_Serial.end();
#endif
#ifdef GPS_I2C #elif defined GPS_I2C
// I2C connect to GPS device
/* // I2C connect to GPS device with 100 kHz
to be done 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 #endif
} }

View File

@ -4,9 +4,13 @@
#define HAS_LED NOT_A_PIN // LoPy4 has no on board LED, so we use RGB LED on LoPy4 #define HAS_LED NOT_A_PIN // LoPy4 has no on board LED, so we use RGB LED on LoPy4
#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0 #define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0
// use only if your LoPy lives on a Pytrack expansion board // !!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 HAS_GPS 1
//#define GPS_I2C GPIO_NUM_9, GPIO_NUM_8 // SDA, SCL //#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 //#define HAS_BUTTON GPIO_NUM_4
// Hardware pin definitions for Pycom LoPy board // Hardware pin definitions for Pycom LoPy board

View File

@ -4,9 +4,13 @@
#define HAS_LED NOT_A_PIN // LoPy4 has no on board LED, so we use RGB LED on LoPy4 #define HAS_LED NOT_A_PIN // LoPy4 has no on board LED, so we use RGB LED on LoPy4
#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0 #define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0
// use only if your LoPy lives on a Pytrack expansion board // !!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 HAS_GPS 1
//#define GPS_I2C GPIO_NUM_9, GPIO_NUM_8 // SDA, SCL //#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 //#define HAS_BUTTON GPIO_NUM_4
// Hardware pin definitions for Pycom LoPy4 board // Hardware pin definitions for Pycom LoPy4 board

View File

@ -129,17 +129,29 @@ void do_send(osjob_t* j){
mydata[3] = 0; mydata[3] = 0;
} }
// Prepare upstream data transmission at the next possible time.
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 #ifdef HAS_GPS
static uint8_t gpsdata[18];
if (cfg.gpsmode && gps.location.isValid()) { if (cfg.gpsmode && gps.location.isValid()) {
gps_read(); gps_read();
LMIC_setTxData2(GPSPORT, (byte*)&gps_status, sizeof(gps_status), (cfg.countermode & 0x02)); memcpy (gpsdata, mydata, 4);
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); 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 #endif
// clear counter if not in cumulative counter mode // clear counter if not in cumulative counter mode
@ -178,10 +190,6 @@ void onEvent (ev_t ev) {
strcpy_P(buff, PSTR("JOINED")); strcpy_P(buff, PSTR("JOINED"));
sprintf(display_lora, " "); // clear previous lmic status message from display 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 // set data rate adaptation
LMIC_setAdrMode(cfg.adrmode); LMIC_setAdrMode(cfg.adrmode);
// Set data rate and transmit power (note: txpower seems to be ignored by the library) // Set data rate and transmit power (note: txpower seems to be ignored by the library)

View File

@ -586,8 +586,8 @@ xTaskCreatePinnedToCore(sniffer_loop, "wifisniffer", 2048, ( void * ) 1, 1, NULL
// if device has GPS and GPS function is enabled, start GPS reader task on core 0 // if device has GPS and GPS function is enabled, start GPS reader task on core 0
#ifdef HAS_GPS #ifdef HAS_GPS
if (cfg.gpsmode) { if (cfg.gpsmode) {
ESP_LOGI(TAG, "Starting GPS task on core 0"); ESP_LOGI(TAG, "Starting GPS task on core 0");
xTaskCreatePinnedToCore(gps_loop, "gpsfeed", 2048, ( void * ) 1, 1, NULL, 0); xTaskCreatePinnedToCore(gps_loop, "gpsfeed", 2048, ( void * ) 1, 1, NULL, 0);
} }
#endif #endif
@ -630,8 +630,9 @@ void loop() {
} }
#ifdef HAS_GPS #ifdef HAS_GPS
if ( (uptime() % 10000) == 0 ) // log NMEA status every 30 seconds, useful for debugging GPS connection
ESP_LOGI(TAG, "GPS NMEA data: passed %d / failed: %d / with fix: %d", gps.passedChecksum(), gps.failedChecksum(), gps.sentencesWithFix()); if ( (uptime() % 30000) == 0 )
ESP_LOGD(TAG, "GPS NMEA data: passed %d / failed: %d / with fix: %d", gps.passedChecksum(), gps.failedChecksum(), gps.sentencesWithFix());
#endif #endif
vTaskDelay(1/portTICK_PERIOD_MS); // reset watchdog vTaskDelay(1/portTICK_PERIOD_MS); // reset watchdog