Skip to content

Commit

Permalink
Merge pull request #323 from cyberman54/development
Browse files Browse the repository at this point in the history
v1.7.4
  • Loading branch information
cyberman54 authored Mar 24, 2019
2 parents c671bc6 + 185953b commit 77d1efe
Show file tree
Hide file tree
Showing 34 changed files with 477 additions and 261 deletions.
21 changes: 14 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ This can all be done with a single small and cheap ESP32 board for less than $20
- Heltec: LoRa-32 v1 and v2
- TTGO: T1, T2, T3, T-Beam, T-Fox
- Pycom: LoPy, LoPy4, FiPy
- Radioshuttle.de: [ECO Power Board](https://www.radioshuttle.de/esp32-eco-power/esp32-eco-power-board/)
- WeMos: LoLin32 + [LoraNode32 shield](https://github.com/hallard/LoLin32-Lora),
LoLin32lite + [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora)
- Adafruit ESP32 Feather + LoRa Wing + OLED Wing, #IoT Octopus32 (Octopus + ESP32 Feather)
Expand All @@ -42,16 +43,17 @@ LoLin32lite + [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-L
- Generic ESP32

Depending on board hardware following features are supported:
- LED (power/status)
- OLED Display (detailed status)
- RGB LED (colorized status)
- LED (shows power & status)
- OLED Display (shows detailed status)
- RGB LED (shows colorized status)
- Button
- Silicon unique ID
- Battery voltage monitoring
- GPS (Generic serial NMEA, or Quectel L76 I2C)
- Environmental sensor (Bosch BME280/BME680 I2C)
- Real Time Clock (Maxim DS3231 I2C)
- IF482 (serial) and DCF77 (gpio) time telegram generator
- Switch external power / battery

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. If you want to use a ESP32 board which is not yet supported, use hal file generic.h and tailor pin mappings to your needs. Pull requests for new boards welcome.<br>
Expand Down Expand Up @@ -157,9 +159,14 @@ Output of sensor and peripheral data is internally switched by a bitmask registe
| 6 | User sensor 3 |
| 7 | reserved |

# Clock controller

Paxcounter can be used to sync a clock which has DCF77 or IF482 time telegram input with. Use case of this function is to have paxcounter hardware integrated in clocks, and use it for both counting of pax and controlling the clock. Supported external time sources are GPS time, LORAWAN network time (v1.1) and on board RTC time. Precision of the synthetic DCF77 signal depends on precision of on board available time base. Supported are both external time base (e.g. timepulse pin of GPS chip or oscillator output of RTC chip) and internal ESP32 hardware timer. Selection of time base and clock frequency is done by #defines in the board's hal file, see example in [**generic.h**](src/hal/generic.h).
# Time sync

Paxcounter can keep it's time-of-day synced with an external time source. Set *#define TIME_SYNC_INTERVAL* in paxcounter.conf to enable time sync. Supported external time sources are GPS, LORAWAN network time and LORAWAN application timeserver time. An on board DS3231 RTC is kept sycned as fallback time source. Time accuracy depends on board's time base which generates the pulse per second. Supported are GPS PPS, SQW output of RTC, and internal ESP32 hardware timer. Time base is selected by #defines in the board's hal file, see example in [**generic.h**](src/hal/generic.h). If your LORAWAN network does not support network time, you can run a Node-Red timeserver application using the [**Timeserver code**](/src/TTN/Nodered-Timeserver.json) in TTN subdirectory. Configure MQTT nodes in Node-Red to the same LORAWAN application as paxocunter device is using.

# Wall clock controller

Paxcounter can be used to sync a wall clock which has a DCF77 or IF482 time telegram input. Set *#define HAS_IF482* or *#define HAS_DCF77* in board's hal file to setup clock controller. Use case of this function is to integrate paxcounter and clock. Accurary of the synthetic DCF77 signal depends on accuracy of on board's time base, see above.

# Payload format

Expand Down Expand Up @@ -187,8 +194,8 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering.

**Port #1:** Paxcount data

byte 1-2: Number of unique pax, first seen on Wifi
byte 3-4: Number of unique pax, first seen on Bluetooth [omited if BT disabled]
byte 1-2: Number of unique devices, seen on Wifi
byte 3-4: Number of unique devices, seen on Bluetooth [ommited if BT disabled]

**Port #2:** Device status query result

Expand Down
12 changes: 6 additions & 6 deletions include/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@

// I2C bus access control
#define I2C_MUTEX_LOCK() \
xSemaphoreTake(I2Caccess, (3 * DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) == \
pdTRUE
xSemaphoreTake(I2Caccess, pdMS_TO_TICKS(3 * DISPLAYREFRESH_MS)) == pdTRUE
#define I2C_MUTEX_UNLOCK() xSemaphoreGive(I2Caccess)

// Struct holding devices's runtime configuration
Expand Down Expand Up @@ -99,6 +98,7 @@ typedef struct {

enum sendprio_t { prio_low, prio_normal, prio_high };
enum timesource_t { _gps, _rtc, _lora, _unsynced };
enum mutexselect_t { no_mutex, do_mutex };

extern std::set<uint16_t, std::less<uint16_t>, Mallocator<uint16_t>> macs;
extern std::array<uint64_t, 0xff>::iterator it;
Expand All @@ -112,7 +112,7 @@ extern uint16_t volatile macs_total, macs_wifi, macs_ble,
extern bool volatile TimePulseTick; // 1sec pps flag set by GPS or RTC
extern timesource_t timeSource;
extern hw_timer_t *displayIRQ, *ppsIRQ;
extern SemaphoreHandle_t I2Caccess, TimePulse;
extern SemaphoreHandle_t I2Caccess;
extern TaskHandle_t irqHandlerTask, ClockTask;
extern TimerHandle_t WifiChanTimer;
extern Timezone myTZ;
Expand All @@ -123,11 +123,11 @@ extern time_t userUTCTime;
#include "payload.h"
#include "blescan.h"

#if(HAS_GPS)
#if (HAS_GPS)
#include "gpsread.h"
#endif

#if(HAS_LORA)
#if (HAS_LORA)
#include "lorawan.h"
#endif

Expand All @@ -147,7 +147,7 @@ extern time_t userUTCTime;
#include "antenna.h"
#endif

#if(HAS_SENSORS)
#if (HAS_SENSORS)
#include "sensor.h"
#endif

Expand Down
5 changes: 1 addition & 4 deletions include/if482.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@
#define _IF482_H

#include "globals.h"
#include "timekeeper.h"

#define IF482_FRAME_SIZE (17)
#define IF482_SYNC_FIXUP (3) // calibration to fixup processing time [milliseconds]

extern HardwareSerial IF482;

void IF482_Pulse(time_t t);
String IRAM_ATTR IF482_Frame(time_t tt);

#endif
3 changes: 3 additions & 0 deletions include/irqhandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
#define SENDCYCLE_IRQ 0x04
#define CYCLIC_IRQ 0x08
#define TIMESYNC_IRQ 0x10
#define MASK_IRQ 0x20
#define UNMASK_IRQ 0x40
#define RESERVED_IRQ 0x80

#include "globals.h"
#include "cyclic.h"
Expand Down
1 change: 1 addition & 0 deletions include/led.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@ void rgb_set_color(uint16_t hue);
void blink_LED(uint16_t set_color, uint16_t set_blinkduration);
void ledLoop(void *parameter);
void switch_LED(uint8_t state);
void switch_LED1(uint8_t state);

#endif
2 changes: 1 addition & 1 deletion include/rtctime.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
extern RtcDS3231<TwoWire> Rtc; // make RTC instance globally available

uint8_t rtc_init(void);
uint8_t set_rtctime(time_t t);
uint8_t set_rtctime(time_t t, mutexselect_t mutex);
void sync_rtctime(void);
time_t get_rtctime(void);
float get_rtctemp(void);
Expand Down
3 changes: 2 additions & 1 deletion include/timekeeper.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
#include "TimeLib.h"
#include "irqhandler.h"

#if(HAS_GPS)
#ifdef HAS_GPS
#include "gpsread.h"
#endif

#ifdef HAS_IF482
#include "if482.h"
#elif defined HAS_DCF77
Expand Down
9 changes: 3 additions & 6 deletions include/timesync.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,9 @@
#include "timesync.h"
#include "timekeeper.h"

#define TIME_SYNC_SAMPLES 2 // number of time requests for averaging
#define TIME_SYNC_CYCLE 20 // seconds between two time requests
#define TIME_SYNC_TIMEOUT 120 // timeout seconds waiting for timeserver answer
#define TIME_SYNC_TRIGGER 100 // time deviation in millisec triggering a sync
#define TIME_SYNC_FRAME_LENGTH 0x06 // timeserver answer frame length
#define TIME_SYNC_FIXUP 0 // calibration millisec to fixup processing time
//#define TIME_SYNC_TRIGGER 100 // threshold for time sync [milliseconds]
#define TIME_SYNC_FRAME_LENGTH 0x06 // timeserver answer frame length [bytes]
#define TIME_SYNC_FIXUP 6 // calibration to fixup processing time [milliseconds]

void send_timesync_req(void);
int recv_timesync_ans(uint8_t buf[], uint8_t buf_len);
Expand Down
24 changes: 21 additions & 3 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
env_default = generic
;env_default = ebox
;env_default = eboxtube
;env_default = ecopower
;env_default = heltec
;env_default = heltecv2
;env_default = ttgov1
Expand All @@ -24,13 +25,13 @@ env_default = generic
;env_default = lolin32lora
;env_default = lolin32lite
;env_default = octopus32
;env_default = ebox, eboxtube, heltec, ttgobeam, lopy4, lopy, ttgov21old, ttgov21new, ttgofox
;env_default = ecopower, eboxtube, heltec, ttgobeam, lopy4, lopy, ttgov21old, ttgov21new, ttgofox
;
description = Paxcounter is a proof-of-concept ESP32 device for metering passenger flows in realtime. It counts how many mobile devices are around.

[common]
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
release_version = 1.7.39
release_version = 1.7.4
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
debug_level = 3
Expand All @@ -44,7 +45,8 @@ platform_espressif32 = [email protected]
board_build.partitions = min_spiffs.csv
monitor_speed = 115200
lib_deps_lora =
MCCI LoRaWAN LMIC library@>=2.3.2
;MCCI LoRaWAN LMIC library@>=2.3.2
https://github.com/mcci-catena/arduino-lmic.git#6e5ebbe
lib_deps_display =
U8g2@>=2.25.7
lib_deps_rgbled =
Expand Down Expand Up @@ -113,6 +115,22 @@ upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}

[env:ecopower]
platform = ${common.platform_espressif32}
framework = arduino
board = esp32dev
board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600
lib_deps =
${common.lib_deps_basic}
${common.lib_deps_lora}
${common.lib_deps_display}
build_flags =
${common.build_flags_basic}
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}

[env:heltec]
platform = ${common.platform_espressif32}
framework = arduino
Expand Down
46 changes: 33 additions & 13 deletions src/TTN/Nodered-Timeserver.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"to": "",
"reg": false,
"x": 240,
"y": 464,
"y": 500,
"wires": [
[
"84f1cda2.069e7"
Expand Down Expand Up @@ -82,7 +82,7 @@
"retain": "",
"broker": "2a15ab6f.ab2244",
"x": 730,
"y": 464,
"y": 500,
"wires": []
},
{
Expand Down Expand Up @@ -135,7 +135,7 @@
"action": "",
"pretty": false,
"x": 580,
"y": 464,
"y": 500,
"wires": [
[
"72d5e7ee.d1eba8"
Expand Down Expand Up @@ -165,7 +165,7 @@
"action": "",
"property": "payload.payload_raw",
"x": 420,
"y": 464,
"y": 500,
"wires": [
[
"dac8aafa.389298"
Expand Down Expand Up @@ -212,11 +212,11 @@
"type": "function",
"z": "449c1517.e25f4c",
"name": "Generate Time Answer",
"func": "/* LoRaWAN Timeserver\n\nconstruct 6 byte timesync_answer from gateway timestamp and node's time_sync_req\n\nbyte meaning\n0 sequence number (taken from node's time_sync_req)\n1..4 current second (from epoch time 1970)\n5 1/250ths fractions of current second\n\n*/\n\nfunction timecompare(a, b) {\n \n const timeA = a.time;\n const timeB = b.time;\n\n let comparison = 0;\n if (timeA > timeB) {\n comparison = 1;\n } else if (timeA < timeB) {\n comparison = -1;\n }\n return comparison;\n}\n\nlet confidence = 2000; // max millisecond diff gateway time to server time\n\nvar gateways = msg.payload.metadata.gateways;\nvar gateway_time = gateways.map(gw => {\n return {\n time: new Date(gw.time),\n eui: gw.gtw_id,\n }\n });\nvar server_time = new Date(msg.payload.metadata.time);\n\n// validate all gateway timestamps against lorawan server_time (which is assumed to be recent)\nvar gw_timestamps = gateway_time.filter(function (element) {\n return ((element.time > (server_time - confidence) && element.time <= server_time));\n});\n\n// if no timestamp left, we have no valid one and exit\nif (gw_timestamps.length === 0) return null;\n\n// sort time array in ascending order to find most recent timestamp for time answer\ngw_timestamps.sort(timecompare);\n\nvar timestamp = gw_timestamps[0].time;\nvar eui = gw_timestamps[0].eui;\nvar offset = server_time - timestamp;\n\nvar seconds = Math.floor(timestamp/1000);\nvar fractions = (timestamp % 1000) / 4;\nvar seqno = msg.payload.payload_raw[0];\n\nlet buf = new ArrayBuffer(6);\nnew DataView(buf).setUint8(0, seqno);\nnew DataView(buf).setUint32(1, seconds);\nnew DataView(buf).setUint8(5, fractions);\n\nmsg.payload = new Buffer(new Uint8Array(buf));\nvar infoMsg = { payload: eui };\nvar offsetMsg = { payload: offset };\n\nreturn [infoMsg, offsetMsg, msg];",
"outputs": 3,
"func": "/* LoRaWAN Timeserver\n\nconstruct 6 byte timesync_answer from gateway timestamp and node's time_sync_req\n\nbyte meaning\n0 sequence number (taken from node's time_sync_req)\n1..4 current second (from epoch time 1970)\n5 1/250ths fractions of current second\n\n*/\n\nfunction timecompare(a, b) {\n \n const timeA = a.time;\n const timeB = b.time;\n\n let comparison = 0;\n if (timeA > timeB) {\n comparison = 1;\n } else if (timeA < timeB) {\n comparison = -1;\n }\n return comparison;\n}\n\nlet confidence = 2000; // max millisecond diff gateway time to server time\n\nvar deviceMsg = { payload: msg.payload.dev_id };\nvar gateways = msg.payload.metadata.gateways;\nvar gateway_time = gateways.map(gw => {\n return {\n time: new Date(gw.time),\n eui: gw.gtw_id,\n }\n });\nvar server_time = new Date(msg.payload.metadata.time);\n\n// validate all gateway timestamps against lorawan server_time (which is assumed to be recent)\nvar gw_timestamps = gateway_time.filter(function (element) {\n return ((element.time > (server_time - confidence) && element.time <= server_time));\n});\n\n// if no timestamp left, we have no valid one and exit\nif (gw_timestamps.length === 0) return [\"n/a\", \"n/a\", deviceMsg, 0xff];\n\n// sort time array in ascending order to find most recent timestamp for time answer\ngw_timestamps.sort(timecompare);\n\nvar timestamp = gw_timestamps[0].time;\nvar eui = gw_timestamps[0].eui;\nvar offset = server_time - timestamp;\n\nvar seconds = Math.floor(timestamp/1000);\nvar fractions = (timestamp % 1000) / 4;\nvar seqno = msg.payload.payload_raw[0];\n\nlet buf = new ArrayBuffer(6);\nnew DataView(buf).setUint8(0, seqno);\nnew DataView(buf).setUint32(1, seconds);\nnew DataView(buf).setUint8(5, fractions);\n\nmsg.payload = new Buffer(new Uint8Array(buf));\nvar euiMsg = { payload: eui };\nvar offsetMsg = { payload: offset };\n\nreturn [euiMsg, offsetMsg, deviceMsg, msg];",
"outputs": 4,
"noerr": 0,
"x": 360,
"y": 320,
"y": 340,
"wires": [
[
"37722d4b.08e3c2",
Expand All @@ -226,13 +226,17 @@
[
"46ce842a.614d5c"
],
[
"a5dbb4ef.019168"
],
[
"49e3c067.e782e"
]
],
"outputLabels": [
"gw_eui",
"offset_ms",
"device",
"time_sync_ans"
]
},
Expand All @@ -241,7 +245,7 @@
"type": "debug",
"z": "449c1517.e25f4c",
"name": "Timeserver Gw",
"active": true,
"active": false,
"tosidebar": false,
"console": false,
"tostatus": true,
Expand All @@ -264,7 +268,7 @@
"format": "{{msg.payload}}",
"layout": "col-center",
"x": 810,
"y": 340,
"y": 336,
"wires": []
},
{
Expand All @@ -290,7 +294,7 @@
"seg1": "",
"seg2": "",
"x": 710,
"y": 420,
"y": 416,
"wires": []
},
{
Expand All @@ -302,11 +306,11 @@
"width": 0,
"height": 0,
"name": "Recent server",
"label": "",
"label": "Gateway",
"format": "{{msg.payload}}",
"layout": "col-center",
"x": 700,
"y": 380,
"y": 376,
"wires": []
},
{
Expand All @@ -318,13 +322,29 @@
"outputs": 1,
"noerr": 0,
"x": 670,
"y": 340,
"y": 336,
"wires": [
[
"8712a5ac.ed18e8"
]
]
},
{
"id": "a5dbb4ef.019168",
"type": "ui_text",
"z": "449c1517.e25f4c",
"group": "edb7cc8d.a3817",
"order": 1,
"width": 0,
"height": 0,
"name": "Recent Device",
"label": "Device",
"format": "{{msg.payload}}",
"layout": "col-center",
"x": 700,
"y": 456,
"wires": []
},
{
"id": "2a15ab6f.ab2244",
"type": "mqtt-broker",
Expand Down
Loading

0 comments on commit 77d1efe

Please sign in to comment.