commit
83ff32ba36
65
README.md
65
README.md
@ -110,7 +110,7 @@ To configure OTAA, leave `#define LORA_ABP` deactivated (commented). To use ABP,
|
||||
The file `src/loraconf_sample.h` contains more information about the values to provide.
|
||||
|
||||
## src/ota.conf
|
||||
Create file `src/ota.conf` using the template [src/ota.sample.conf](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/ota.sample.conf) and enter your WIFI network&key. These settings are used for downloading updates via WiFi, either from a remote https server, or locally via WebUI. If you want to use a remote server, you need a <A HREF="https://bintray.com/JFrog">Bintray account</A>. Enter your Bintray user account data in ota.conf. If you don't need wireless firmware updates just rename ota.sample.conf to ota.conf.
|
||||
Create file `src/ota.conf` using the template [src/ota.sample.conf](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/ota.sample.conf) and enter your WIFI network&key. These settings are used for downloading updates via WiFi, either from a remote https server, or locally via WebUI. If you want to use a remote server, you need a <A HREF="https://github.com/paxexpress/docs">PAX.express repository</A>. Enter your PAX.express credentials in ota.conf. If you don't need wireless firmware updates just rename ota.sample.conf to ota.conf.
|
||||
|
||||
# Building
|
||||
|
||||
@ -125,7 +125,7 @@ The LoPy/LoPy4/FiPy board needs to be set manually. See these
|
||||
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/FiPy. If you later want to go back to the Pycom firmware, download the firmware from Pycom and flash it over.
|
||||
|
||||
- **over the air (OTA), download via WiFi:**
|
||||
After the ESP32 board is initially flashed and has joined a LoRaWAN network, the firmware can update itself by OTA. This process is kicked off by sending a remote control command (see below) via LoRaWAN to the board. The board then tries to connect via WiFi to a cloud service (JFrog Bintray), checks for update, and if available downloads the binary and reboots with it. If something goes wrong during this process, the board reboots back to the current version. Prerequisites for OTA are: 1. You own a Bintray repository, 2. you pushed the update binary to the Bintray repository, 3. internet access via encrypted (WPA2) WiFi is present at the board's site, 4. WiFi credentials were set in ota.conf and initially flashed to the board. Step 2 runs automated, just enter the credentials in ota.conf and set `upload_protocol = custom` in platformio.ini. Then press build and lean back watching platformio doing build and upload.
|
||||
After the ESP32 board is initially flashed and has joined a LoRaWAN network, the firmware can update itself by OTA. This process is kicked off by sending a remote control command (see below) via LoRaWAN to the board. The board then tries to connect via WiFi to a cloud service (<A HREF="https://github.com/paxexpress">PAX.express</A>), checks for update, and if available downloads the binary and reboots with it. If something goes wrong during this process, the board reboots back to the current version. Prerequisites for OTA are: 1. You own a PAX.express repository, 2. you pushed the update binary to your PAX.express repository, 3. internet access via encrypted (WPA2) WiFi is present at the board's site, 4. WiFi credentials were set in ota.conf and initially flashed to the board. Step 2 runs automated, just enter the credentials in ota.conf and set `upload_protocol = custom` in platformio.ini. Then press build and lean back watching platformio doing build and upload.
|
||||
|
||||
- **over the air (OTA), upload via WiFi:**
|
||||
If option *BOOTMENU* is defined in `paxcounter.conf`, the ESP32 board will try to connect to a known WiFi access point each time cold starting (after a power cycle or a reset), using the WiFi credentials given in `ota.conf`. Once connected to the WiFi it will fire up a simple webserver, providing a bootstrap menu waiting for a user interaction (pressing "START" button in menu). This process will time out after *BOOTDELAY* seconds, ensuring booting the device to runmode. Once a user interaction in bootstrap menu was detected, the timeout will be extended to *BOOTTIMEOUT* seconds. During this time a firmware upload can be performed manually by user, e.g. using a smartphone in tethering mode providing the firmware upload file.
|
||||
@ -138,13 +138,13 @@ If option *BOOTMENU* is defined in `paxcounter.conf`, the ESP32 board will try t
|
||||
|
||||
(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/))
|
||||
|
||||
(e.g. Citizens in the the Netherlands may want to read [this article](https://www.ivir.nl/publicaties/download/PrivacyInformatie_2016_6.pdf) and [this article](https://autoriteitpersoonsgegevens.nl/nl/nieuws/europese-privacytoezichthouders-publiceren-opinie-eprivacyverordening))
|
||||
(e.g. Citizens in the the Netherlands and EU may want to read [this article](https://www.ivir.nl/publicaties/download/PrivacyInformatie_2016_6.pdf) and [this article](https://autoriteitpersoonsgegevens.nl/nl/nieuws/europese-privacytoezichthouders-publiceren-opinie-eprivacyverordening)) and [this decision](https://edpb.europa.eu/news/national-news/2021/dutch-dpa-fines-municipality-wi-fi-tracking_en)
|
||||
|
||||
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.
|
||||
|
||||
# Privacy disclosure
|
||||
|
||||
Paxcounter generates identifiers for sniffed MAC adresses and collects them temporary in the device's RAM for a configurable scan cycle time (default 60 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.
|
||||
Paxcounter generates identifiers for sniffed Wifi or Bluetooth MAC adresses and and collects them temporary in the device's RAM for a configurable scan cycle time (default 60 seconds). After each scan cycle the collected identifiers are cleared. Identifiers are generated by using the last 2 bytes of universal MAC adresses. Personal MAC adresses remain untouched and are not evaluated. 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.
|
||||
|
||||
# LED blink pattern
|
||||
|
||||
@ -154,7 +154,6 @@ Paxcounter generates identifiers for sniffed MAC adresses and collects them temp
|
||||
- Quick blink (20ms on each 1/5 second): joining LoRaWAN network in progress or pending
|
||||
- Small blink (10ms on each 1/2 second): LoRaWAN data transmit in progress or pending
|
||||
- Long blink (200ms on each 2 seconds): LoRaWAN stack error
|
||||
- Single long flash (2sec): Known beacon detected
|
||||
|
||||
**RGB LED:**
|
||||
|
||||
@ -164,7 +163,6 @@ Paxcounter generates identifiers for sniffed MAC adresses and collects them temp
|
||||
- Pink: LORAWAN MAC transmit in progress
|
||||
- Blue: LoRaWAN data transmit in progress or pending
|
||||
- Red: LoRaWAN stack error
|
||||
- White: Known Beacon detected
|
||||
|
||||
# Display
|
||||
|
||||
@ -181,7 +179,7 @@ by pressing the button of the device.
|
||||
|
||||
# Sensors and Peripherals
|
||||
|
||||
You can add up to 3 user defined sensors. Insert your sensor's payload scheme in [*sensor.cpp*](src/sensor.cpp). Bosch BMP180 / BME280 / BME680 environment sensors are supported, to activate configure BME in board's hal file before build. If you need Bosch's proprietary BSEC libraray (e.g. to get indoor air quality value from BME680) further enable *build_flags_sensors*, which comes on the price of reduced RAM and increased build size. Furthermore, SDS011, RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported, and to be configured in board's hal file. See [*generic.h*](src/hal/generic.h) for all options and for proper configuration of BME280/BME680.
|
||||
You can add up to 3 user defined sensors. Insert your sensor's payload scheme in [*sensor.cpp*](src/sensor.cpp). Bosch BMP180 / BME280 / BME680 environment sensors are supported, to activate configure BME in board's hal file before build. Furthermore, SDS011, RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported, and to be configured in board's hal file. See [*generic.h*](src/hal/generic.h) for all options and for proper configuration of BME280/BME680.
|
||||
|
||||
Output of user sensor data can be switched by user remote control command 0x14 sent to Port 2.
|
||||
|
||||
@ -190,7 +188,7 @@ Output of sensor and peripheral data is internally switched by a bitmask registe
|
||||
| Bit | Sensordata | Default
|
||||
| --- | ------------- | -------
|
||||
| 0 | Paxcounter | on
|
||||
| 1 | Beacon alarm | on
|
||||
| 1 | unused | off
|
||||
| 2 | BME280/680 | on
|
||||
| 3 | GPS* | on
|
||||
| 4 | User sensor 1 | on
|
||||
@ -206,7 +204,7 @@ Paxcounter supports a battery friendly power saving mode. In this mode the devic
|
||||
|
||||
# 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). Bonus: If your LORAWAN network does not support network time, you can run a Node-Red timeserver application using the enclosed [**Timeserver code**](/src/Node-RED/Timeserver.json). Configure MQTT nodes in Node-Red for the LORAWAN application used by paxocunter device.
|
||||
Paxcounter can keep a time-of-day synced with external or on board time sources. 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. Supported on board time sources are the RTC of ESP32 and a DS3231 RTC chip, both are kept sycned as fallback time sources. 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). Bonus: If your LORAWAN network does not support network time, you can run a Node-Red timeserver application using the enclosed [**Timeserver code**](/src/Node-RED/Timeserver.json). Configure the MQTT nodes in Node-Red for the LORAWAN application used by your paxocunter device. Time can also be set without precision liability, by simple remote command, see section remote control.
|
||||
|
||||
# Wall clock controller
|
||||
|
||||
@ -216,12 +214,11 @@ Paxcounter can be used to sync a wall clock which has a DCF77 or IF482 time tele
|
||||
|
||||
This describes how to set up a mobile PaxCounter:<br> Follow all steps so far for preparing the device, selecting the packed payload format. In `paxcounter.conf` set PAYLOAD_OPENSENSEBOX to 1. Register a new sensebox on https://opensensemap.org/. In the sensor configuration select "TheThingsNetwork" and set decoding profile to "LoRa serialization". Enter your TTN Application and Device ID. Setup decoding option using `[{"decoder":"latLng"},{"decoder":"uint16",sensor_id":"yoursensorid"}]`
|
||||
|
||||
# Covid-19 Exposure Notification System beacon detection
|
||||
# Covid-19 Exposure Notification System beacon detection (currently NOT working with v3.0.x, use v2.4.x for this feature)
|
||||
|
||||
Bluetooth low energy service UUID 0xFD6F, used by Google/Apple COVID-19 Exposure Notification System, can be monitored and counted. By comparing with the total number of observed devices this <A HREF="https://linux-fuer-wi.blogspot.com/2020/10/suche-die-zahl-64879.html">gives an indication</A> how many people staying in proximity are using Apps for tracing COVID-19 exposures, e.g. in Germany the "Corona Warn App". To achive best results with this funcion, use following settings in `paxcounter.conf`:
|
||||
|
||||
#define COUNT_ENS 1 // enable ENS monitoring function
|
||||
#define MACFILTER 0 // disable MAC filter
|
||||
#define BLECOUNTER 1 // enable bluetooth sniffing
|
||||
#define WIFICOUNTER 0 // disable wifi sniffing (improves BLE scan speed)
|
||||
#define HAS_SENSOR_1 1 // optional, in board's hal file: transmit ENS counter data to server
|
||||
@ -320,8 +317,8 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering.
|
||||
byte 13: Wifi antenna switch (0=internal, 1=external) [default 0]
|
||||
byte 14: count randomizated MACs only (0=disabled, 1=enabled) [default 1]
|
||||
byte 15: RGB LED luminosity (0..100 %) [default 30]
|
||||
byte 16: Payload filter mask
|
||||
byte 17: Beacon proximity alarm mode (1=on, 0=off) [default 0]
|
||||
byte 16: 0 (reserved)
|
||||
byte 17: 0 (reserved)
|
||||
bytes 18-28: Software version (ASCII format, terminating with zero)
|
||||
|
||||
|
||||
@ -337,10 +334,7 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering.
|
||||
|
||||
byte 1: static value 0x01
|
||||
|
||||
**Port #6:** Beacon proximity alarm
|
||||
|
||||
byte 1: Beacon RSSI reception level
|
||||
byte 2: Beacon identifier (0..255)
|
||||
**Port #6:** (unused)
|
||||
|
||||
**Port #7:** Environmental sensor data (only if device has feature BME)
|
||||
|
||||
@ -437,34 +431,29 @@ Send for example `83` `86` as Downlink on Port 2 to get battery status and time/
|
||||
0x09 reset functions (send this command UNconfirmed only to avoid boot loops!)
|
||||
|
||||
0 = restart device (coldstart)
|
||||
1 = reset MAC counter to zero
|
||||
2 = reset device to factory settings
|
||||
1 = (reserved, currently does nothing)
|
||||
2 = reset device to factory settings and restart device
|
||||
3 = flush send queues
|
||||
4 = restart device (warmstart)
|
||||
8 = reboot device to maintenance mode (local web server)
|
||||
9 = reboot device to OTA update via Wifi mode
|
||||
|
||||
0x0A set LoRaWAN payload send cycle
|
||||
0x0A set payload send cycle
|
||||
|
||||
0 ... 255 payload send cycle in seconds/2
|
||||
5 ... 255 payload send cycle in seconds/2
|
||||
e.g. 120 -> payload is transmitted each 240 seconds [default]
|
||||
|
||||
0x0B set Wifi channel hopping interval timer
|
||||
|
||||
0 ... 255 duration for scanning a wifi channel in seconds/100
|
||||
e.g. 50 -> each channel is scanned for 500 milliseconds [default]
|
||||
0 means no hopping, scanning on channel WIFI_CHANNEL_MIN only
|
||||
0 means no hopping, scanning on fixed single channel WIFI_CHANNEL_1
|
||||
|
||||
0x0C set Bluetooth channel switch interval timer
|
||||
|
||||
0 ... 255 duration for scanning a bluetooth advertising channel in seconds/100
|
||||
e.g. 8 -> each channel is scanned for 80 milliseconds [default]
|
||||
|
||||
0x0D (NOT YET IMPLEMENTED) set BLE and WIFI MAC filter mode
|
||||
|
||||
0 = disabled (use to count devices, not people)
|
||||
1 = enabled [default]
|
||||
|
||||
0x0E set Bluetooth scanner
|
||||
|
||||
0 = disabled
|
||||
@ -480,16 +469,6 @@ Send for example `83` `86` as Downlink on Port 2 to get battery status and time/
|
||||
0 ... 100 percentage of luminosity (100% = full light)
|
||||
e.g. 50 -> 50% of luminosity [default]
|
||||
|
||||
0x11 set beacon proximity alarm mode on/off
|
||||
|
||||
0 = Beacon monitor mode off [default]
|
||||
1 = Beacon monitor mode on, enables proximity alarm if test beacons are seen
|
||||
|
||||
0x12 set or reset a beacon MAC for proximity alarm
|
||||
|
||||
byte 1 = beacon ID (0..255)
|
||||
bytes 2..7 = beacon MAC with 6 digits (e.g. MAC 80:ab:00:01:02:03 -> 0x80ab00010203)
|
||||
|
||||
0x13 set user sensor mode
|
||||
|
||||
byte 1 = user sensor number (1..3)
|
||||
@ -499,7 +478,7 @@ Send for example `83` `86` as Downlink on Port 2 to get battery status and time/
|
||||
|
||||
byte 1 = sensor data payload mask (0..255, meaning of bits see below)
|
||||
0x01 = COUNT_DATA
|
||||
0x02 = ALARM_DATA
|
||||
0x02 = RESERVED_DATA
|
||||
0x04 = MEMS_DATA
|
||||
0x08 = GPS_DATA
|
||||
0x10 = SENSOR_1_DATA (also ENS counter)
|
||||
@ -530,8 +509,8 @@ Send for example `83` `86` as Downlink on Port 2 to get battery status and time/
|
||||
|
||||
0x19 set sleep cycle
|
||||
|
||||
0 ... 255 device sleep cycle in seconds/2
|
||||
e.g. 120 -> device sleeps 240 seconds after each send cycle [default = 0]
|
||||
bytes 1..2 = device sleep cycle in seconds/10 (MSB), 1 ... 255
|
||||
e.g. {0x04, 0xB0} -> device sleeps 20 minutes after each send cycle [default = 0]
|
||||
|
||||
0x20 load device configuration
|
||||
|
||||
@ -573,16 +552,20 @@ Send for example `83` `86` as Downlink on Port 2 to get battery status and time/
|
||||
0x01 = RTC
|
||||
0x02 = LORA
|
||||
0x03 = unsynched (never synched)
|
||||
0x04 = set (source unknown)
|
||||
|
||||
bits 4..7 time status
|
||||
0x00 = timeNotSet (never synched)
|
||||
0x01 = timeNeedsSync (last sync failed)
|
||||
0x02 = timeSet (synched)
|
||||
|
||||
0x87 set time/date
|
||||
0x87 sync time/date
|
||||
|
||||
Device synchronizes it's time/date by calling the preconfigured time source.
|
||||
|
||||
0x88 set time/date
|
||||
|
||||
bytes 1..4 = time/date to set in UTC epoch seconds (MSB, e.g. https://www.epochconverter.com/hex)
|
||||
|
||||
# License
|
||||
|
||||
|
38
build.py
38
build.py
@ -86,43 +86,43 @@ with open(otakeyfile) as myfile:
|
||||
key, value = line.partition("=")[::2]
|
||||
mykeys[key.strip()] = str(value).strip()
|
||||
|
||||
# usage of bintray: see https://github.com/r0oland/bintray-secure-ota
|
||||
# usage of paxexpress: see https://github.com/paxexpress/docs
|
||||
|
||||
# get bintray user credentials from ota key file
|
||||
user = mykeys["BINTRAY_USER"]
|
||||
repository = mykeys["BINTRAY_REPO"]
|
||||
apitoken = mykeys["BINTRAY_API_TOKEN"]
|
||||
# get paxexpress credentials from ota key file
|
||||
user = mykeys["PAXEXPRESS_USER"]
|
||||
repository = mykeys["PAXEXPRESS_REPO"]
|
||||
apitoken = mykeys["PAXEXPRESS_API_TOKEN"]
|
||||
|
||||
# get bintray upload parameters from platformio environment
|
||||
# get paxexpress upload parameters from platformio environment
|
||||
version = config.get("common", "release_version")
|
||||
package, dummy = halconfig.partition(".")[::2]
|
||||
|
||||
# put bintray user credentials to platformio environment
|
||||
env.Replace(BINTRAY_USER=user)
|
||||
env.Replace(BINTRAY_REPO=repository)
|
||||
env.Replace(BINTRAY_API_TOKEN=apitoken)
|
||||
# put paxexpress user credentials to platformio environment
|
||||
env.Replace(PAXEXPRESS_USER=user)
|
||||
env.Replace(PAXEXPRESS_REPO=repository)
|
||||
env.Replace(PAXEXPRESS_API_TOKEN=apitoken)
|
||||
|
||||
# get runtime credentials and put them to compiler directive
|
||||
env.Append(BUILD_FLAGS=[
|
||||
u'-DWIFI_SSID=\\"' + mykeys["OTA_WIFI_SSID"] + '\\"',
|
||||
u'-DWIFI_PASS=\\"' + mykeys["OTA_WIFI_PASS"] + '\\"',
|
||||
u'-DBINTRAY_USER=\\"' + mykeys["BINTRAY_USER"] + '\\"',
|
||||
u'-DBINTRAY_REPO=\\"' + mykeys["BINTRAY_REPO"] + '\\"',
|
||||
u'-DBINTRAY_PACKAGE=\\"' + package + '\\"',
|
||||
u'-DPAXEXPRESS_USER=\\"' + mykeys["PAXEXPRESS_USER"] + '\\"',
|
||||
u'-DPAXEXPRESS_REPO=\\"' + mykeys["PAXEXPRESS_REPO"] + '\\"',
|
||||
u'-DPAXEXPRESS_PACKAGE=\\"' + package + '\\"',
|
||||
u'-DARDUINO_LMIC_PROJECT_CONFIG_H=' + lmicconfig,
|
||||
u'-I \"' + srcdir + '\"'
|
||||
])
|
||||
|
||||
# function for pushing new firmware to bintray storage using API
|
||||
def publish_bintray(source, target, env):
|
||||
# function for pushing new firmware to paxexpress storage using API
|
||||
def publish_paxexpress(source, target, env):
|
||||
firmware_path = str(source[0])
|
||||
firmware_name = basename(firmware_path)
|
||||
url = "/".join([
|
||||
"https://api.bintray.com", "content",
|
||||
"https://pax.express", "content",
|
||||
user, repository, package, version, firmware_name
|
||||
])
|
||||
|
||||
print("Uploading {0} to Bintray. Version: {1}".format(
|
||||
print("Uploading {0} to PAX.express. Version: {1}".format(
|
||||
firmware_name, version))
|
||||
print(url)
|
||||
|
||||
@ -145,9 +145,9 @@ def publish_bintray(source, target, env):
|
||||
("%s\n%s" % (r.status_code, r.text) if r else str(e)))
|
||||
env.Exit(1)
|
||||
|
||||
print("The firmware has been successfuly published at Bintray.com!")
|
||||
print("Firmware has been successfully published at PAX.express!")
|
||||
|
||||
# put build file name and upload command to platformio environment
|
||||
env.Replace(
|
||||
PROGNAME="firmware_" + package + "_v%s" % version,
|
||||
UPLOADCMD=publish_bintray)
|
||||
UPLOADCMD=publish_paxexpress)
|
||||
|
@ -1,9 +0,0 @@
|
||||
#ifndef _BEACON_ARRAY_H
|
||||
#define _BEACON_ARRAY_H
|
||||
|
||||
std::array<uint64_t, 0xff>::iterator it;
|
||||
|
||||
std::array<uint64_t, 0xff> beacons = {0x0000010203040506, 0x0000aabbccddeeff,
|
||||
0x0000112233445566};
|
||||
|
||||
#endif
|
@ -1,17 +0,0 @@
|
||||
#ifndef _BLESCAN_H
|
||||
#define _BLESCAN_H
|
||||
|
||||
#include "globals.h"
|
||||
#include "macsniff.h"
|
||||
|
||||
// Bluetooth specific includes
|
||||
#include <esp_bt.h>
|
||||
#include <esp_bt_main.h>
|
||||
#include <esp_gap_ble_api.h>
|
||||
#include <esp_blufi_api.h> // needed for BLE_ADDR types, do not remove
|
||||
#include <esp_coexist.h>
|
||||
|
||||
void start_BLEscan(void);
|
||||
void stop_BLEscan(void);
|
||||
|
||||
#endif
|
@ -5,7 +5,9 @@
|
||||
#include "reset.h"
|
||||
#include <Preferences.h>
|
||||
|
||||
void saveConfig(bool erase = false);
|
||||
extern configData_t cfg;
|
||||
|
||||
void saveConfig(bool erase);
|
||||
bool loadConfig(void);
|
||||
void eraseConfig(void);
|
||||
int version_compare(const String v1, const String v2);
|
||||
|
@ -10,14 +10,14 @@
|
||||
#include "display.h"
|
||||
#include "sds011read.h"
|
||||
#include "sdcard.h"
|
||||
#include "macsniff.h"
|
||||
#include "reset.h"
|
||||
#include "led.h"
|
||||
#include "power.h"
|
||||
|
||||
extern Ticker cyclicTimer;
|
||||
|
||||
void setCyclicIRQ(void);
|
||||
void doHousekeeping(void);
|
||||
void reset_counters(void);
|
||||
uint32_t getFreeRAM();
|
||||
|
||||
#endif
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define _DCF77_H
|
||||
|
||||
#include "globals.h"
|
||||
#include "timekeeper.h"
|
||||
|
||||
#define DCF77_FRAME_SIZE (60)
|
||||
#define DCF77_PULSE_LENGTH (100)
|
||||
|
@ -1,8 +1,10 @@
|
||||
#ifndef _DISPLAY_H
|
||||
#define _DISPLAY_H
|
||||
|
||||
#include <libpax_api.h>
|
||||
#include "cyclic.h"
|
||||
#include "qrcode.h"
|
||||
#include "power.h"
|
||||
|
||||
#if (COUNT_ENS)
|
||||
#include "corona.h"
|
||||
@ -76,13 +78,17 @@
|
||||
#define QR_VERSION 3 // 29 x 29px
|
||||
|
||||
const uint8_t QR_SCALEFACTOR = (MY_DISPLAY_HEIGHT - 4) / 29; // 4px borderlines
|
||||
|
||||
extern uint8_t DisplayIsOn, displaybuf[];
|
||||
extern hw_timer_t *displayIRQ;
|
||||
extern uint8_t volatile channel; // wifi channel rotation counter
|
||||
|
||||
void dp_setup(int contrast = 0);
|
||||
void dp_refresh(bool nextPage = false);
|
||||
void dp_init(bool verbose = false);
|
||||
void dp_shutdown(void);
|
||||
void dp_drawPage(time_t t, bool nextpage);
|
||||
void dp_message(const char *msg, int line, bool invers);
|
||||
void dp_drawPage(bool nextpage);
|
||||
void dp_println(int lines = 1);
|
||||
void dp_printf(const char *format, ...);
|
||||
void dp_setFont(int font, int inv = 0);
|
||||
|
@ -1,13 +1,11 @@
|
||||
#ifndef _GLOBALS_H
|
||||
#define _GLOBALS_H
|
||||
#endif
|
||||
|
||||
// The mother of all embedded development...
|
||||
#include <Arduino.h>
|
||||
|
||||
// Time functions
|
||||
#include "microTime.h"
|
||||
#include <Timezone.h>
|
||||
#include <ezTime.h>
|
||||
#include <RtcDateTime.h>
|
||||
#include <Ticker.h>
|
||||
|
||||
@ -15,7 +13,6 @@
|
||||
#include <set>
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include "mallocator.h"
|
||||
#include <bsec.h>
|
||||
|
||||
#define _bit(b) (1U << (b))
|
||||
@ -23,7 +20,7 @@
|
||||
|
||||
// bits in payloadmask for filtering payload data
|
||||
#define COUNT_DATA _bit(0)
|
||||
#define ALARM_DATA _bit(1)
|
||||
#define RESERVED_DATA _bit(1)
|
||||
#define MEMS_DATA _bit(2)
|
||||
#define GPS_DATA _bit(3)
|
||||
#define SENSOR1_DATA _bit(4)
|
||||
@ -48,19 +45,11 @@
|
||||
for (;;) \
|
||||
; \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define _seconds() millis() / 1000.0
|
||||
|
||||
enum timesource_t { _gps, _rtc, _lora, _unsynced };
|
||||
enum snifftype_t { MAC_SNIFF_WIFI, MAC_SNIFF_BLE, MAC_SNIFF_BLE_ENS };
|
||||
enum runmode_t {
|
||||
RUNMODE_POWERCYCLE,
|
||||
RUNMODE_NORMAL,
|
||||
RUNMODE_WAKEUP,
|
||||
RUNMODE_UPDATE,
|
||||
RUNMODE_SLEEP,
|
||||
RUNMODE_MAINTENANCE
|
||||
};
|
||||
|
||||
// Struct holding devices's runtime configuration
|
||||
// using packed to avoid compiler padding, because struct will be memcpy'd to
|
||||
@ -75,15 +64,13 @@ typedef struct __attribute__((packed)) {
|
||||
uint8_t countermode; // 0=cyclic unconfirmed, 1=cumulative, 2=cyclic confirmed
|
||||
int16_t rssilimit; // threshold for rssilimiter, negative value!
|
||||
uint8_t sendcycle; // payload send cycle [seconds/2]
|
||||
uint8_t sleepcycle; // sleep cycle [seconds/2]
|
||||
uint16_t sleepcycle; // sleep cycle [seconds/10]
|
||||
uint8_t wifichancycle; // wifi channel switch cycle [seconds/100]
|
||||
uint8_t blescantime; // BLE scan cycle duration [seconds]
|
||||
uint8_t blescan; // 0=disabled, 1=enabled
|
||||
uint8_t wifiscan; // 0=disabled, 1=enabled
|
||||
uint8_t wifiant; // 0=internal, 1=external (for LoPy/LoPy4)
|
||||
uint8_t macfilter; // 0=disabled, 1=enabled
|
||||
uint8_t rgblum; // RGB Led luminosity (0..100%)
|
||||
uint8_t monitormode; // 0=disabled, 1=enabled
|
||||
uint8_t payloadmask; // bitswitches for payload data
|
||||
uint8_t enscount; // 0=disabled 1= enabled
|
||||
|
||||
@ -100,13 +87,6 @@ typedef struct {
|
||||
uint8_t Message[PAYLOAD_BUFFER_SIZE];
|
||||
} MessageBuffer_t;
|
||||
|
||||
// Struct for MAC processing queue
|
||||
typedef struct {
|
||||
uint8_t mac[6];
|
||||
int8_t rssi;
|
||||
snifftype_t sniff_type;
|
||||
} MacBuffer_t;
|
||||
|
||||
typedef struct {
|
||||
int32_t latitude;
|
||||
int32_t longitude;
|
||||
@ -131,22 +111,6 @@ typedef struct {
|
||||
float pm25;
|
||||
} sdsStatus_t;
|
||||
|
||||
extern std::set<uint16_t, std::less<uint16_t>, Mallocator<uint16_t>> macs;
|
||||
extern std::array<uint64_t, 0xff>::iterator it;
|
||||
extern std::array<uint64_t, 0xff> beacons;
|
||||
extern char clientId[20]; // unique clientID
|
||||
|
||||
extern configData_t cfg; // current device configuration
|
||||
extern char lmic_event_msg[LMIC_EVENTMSG_LEN]; // display buffer
|
||||
extern uint8_t volatile channel; // wifi channel rotation counter
|
||||
extern uint8_t volatile rf_load; // RF traffic indicator
|
||||
extern uint8_t batt_level; // display value
|
||||
extern uint16_t volatile macs_wifi, macs_ble; // display values
|
||||
extern bool volatile TimePulseTick; // 1sec pps flag set by GPS or RTC
|
||||
extern timesource_t timeSource;
|
||||
extern hw_timer_t *displayIRQ, *matrixDisplayIRQ, *ppsIRQ;
|
||||
extern SemaphoreHandle_t I2Caccess;
|
||||
extern TaskHandle_t irqHandlerTask, ClockTask, macProcessTask;
|
||||
extern TimerHandle_t WifiChanTimer;
|
||||
extern Timezone myTZ;
|
||||
|
||||
#endif
|
||||
#endif
|
@ -21,6 +21,8 @@
|
||||
#define MY_DISPLAY_SCL SCL
|
||||
#endif
|
||||
|
||||
extern SemaphoreHandle_t I2Caccess;
|
||||
|
||||
void i2c_init(void);
|
||||
void i2c_deinit(void);
|
||||
void i2c_scan(void);
|
||||
|
@ -2,10 +2,11 @@
|
||||
#define _IF482_H
|
||||
|
||||
#include "globals.h"
|
||||
#include "timekeeper.h"
|
||||
|
||||
#define IF482_FRAME_SIZE (17)
|
||||
#define IF482_SYNC_FIXUP (10) // calibration to fixup processing time [milliseconds]
|
||||
|
||||
String IRAM_ATTR IF482_Frame(time_t tt);
|
||||
String IRAM_ATTR IF482_Frame(time_t t);
|
||||
|
||||
#endif
|
@ -26,6 +26,8 @@ void mask_user_IRQ();
|
||||
void unmask_user_IRQ();
|
||||
void doIRQ(int irq);
|
||||
|
||||
extern TaskHandle_t irqHandlerTask;
|
||||
|
||||
#ifdef HAS_DISPLAY
|
||||
void IRAM_ATTR DisplayIRQ();
|
||||
#endif
|
||||
|
@ -6,8 +6,8 @@
|
||||
#include "ledmatrixdisplay.h"
|
||||
|
||||
extern uint8_t MatrixDisplayIsOn;
|
||||
|
||||
extern LEDMatrix matrix;
|
||||
extern hw_timer_t *matrixDisplayIRQ;
|
||||
|
||||
void init_matrix_display(bool reverse = false);
|
||||
void refreshTheMatrixDisplay(bool nextPage = false);
|
||||
|
13
include/libpax_helpers.h
Normal file
13
include/libpax_helpers.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef _LIBPAX_HELPERS_H
|
||||
#define _LIBPAX_HELPERS_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <libpax_api.h>
|
||||
#include "senddata.h"
|
||||
#include "configmanager.h"
|
||||
|
||||
void init_libpax(void);
|
||||
|
||||
extern uint16_t volatile libpax_macs_ble, libpax_macs_wifi; // libpax values
|
||||
|
||||
#endif
|
@ -19,6 +19,7 @@
|
||||
#endif
|
||||
|
||||
extern TaskHandle_t lmicTask, lorasendTask;
|
||||
extern char lmic_event_msg[LMIC_EVENTMSG_LEN]; // display buffer
|
||||
|
||||
esp_err_t lmic_init(void);
|
||||
void lora_setupForNetwork(bool preJoin);
|
||||
|
@ -1,25 +0,0 @@
|
||||
#ifndef _MACSNIFF_H
|
||||
#define _MACSNIFF_H
|
||||
|
||||
// ESP32 Functions
|
||||
#include <esp_wifi.h>
|
||||
|
||||
// Hash function for scrambling MAC addresses
|
||||
#include "hash.h"
|
||||
#include "senddata.h"
|
||||
#include "cyclic.h"
|
||||
#include "led.h"
|
||||
|
||||
#if (COUNT_ENS)
|
||||
#include "corona.h"
|
||||
#endif
|
||||
|
||||
uint32_t renew_salt(void);
|
||||
uint64_t macConvert(uint8_t *paddr);
|
||||
esp_err_t macQueueInit(void);
|
||||
void mac_process(void *pvParameters);
|
||||
void IRAM_ATTR mac_add(uint8_t *paddr, int8_t rssi, snifftype_t sniff_type);
|
||||
uint16_t mac_analyze(MacBuffer_t MacBuffer);
|
||||
void printKey(const char *name, const uint8_t *key, uint8_t len, bool lsb);
|
||||
|
||||
#endif
|
@ -5,15 +5,13 @@
|
||||
#include <esp_event_loop.h> // needed for Wifi event handler
|
||||
#include <esp32-hal-timer.h> // needed for timers
|
||||
#include <esp_coexist.h> // needed for coex version display
|
||||
#include <esp_wifi.h> // needed for wifi init / deinit
|
||||
|
||||
#include "globals.h"
|
||||
#include "reset.h"
|
||||
#include "i2c.h"
|
||||
#include "blescan.h"
|
||||
#include "wifiscan.h"
|
||||
#include "configmanager.h"
|
||||
#include "cyclic.h"
|
||||
#include "beacon_array.h"
|
||||
#include "ota.h"
|
||||
#include "irqhandler.h"
|
||||
#include "spislave.h"
|
||||
@ -22,5 +20,8 @@
|
||||
#include "timekeeper.h"
|
||||
#include "corona.h"
|
||||
#include "boot.h"
|
||||
#include "libpax_helpers.h"
|
||||
#include "power.h"
|
||||
#include "antenna.h"
|
||||
|
||||
#endif
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
|
||||
This Mallocator code was taken from:
|
||||
|
||||
CppCon2014 Presentations
|
||||
STL Features And Implementation Techniques
|
||||
Stephan T. Lavavej
|
||||
|
||||
https://github.com/CppCon/CppCon2014
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _MALLOCATOR_H
|
||||
#define _MALLOCATOR_H
|
||||
|
||||
#include <stdlib.h> // size_t, malloc, free
|
||||
#include <new> // bad_alloc, bad_array_new_length
|
||||
#include "esp32-hal-psram.h" // ps_malloc
|
||||
|
||||
template <class T> struct Mallocator {
|
||||
typedef T value_type;
|
||||
Mallocator() noexcept {} // default ctor not required
|
||||
template <class U> Mallocator(const Mallocator<U> &) noexcept {}
|
||||
template <class U> bool operator==(const Mallocator<U> &) const noexcept {
|
||||
return true;
|
||||
}
|
||||
template <class U> bool operator!=(const Mallocator<U> &) const noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
T *allocate(const size_t n) const {
|
||||
if (n == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (n > static_cast<size_t>(-1) / sizeof(T)) {
|
||||
throw std::bad_array_new_length();
|
||||
}
|
||||
|
||||
#ifndef BOARD_HAS_PSRAM
|
||||
void *const pv = malloc(n * sizeof(T));
|
||||
#else
|
||||
void *const pv = ps_malloc(n * sizeof(T));
|
||||
#endif
|
||||
|
||||
if (!pv) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
return static_cast<T *>(pv);
|
||||
}
|
||||
void deallocate(T *const p, size_t) const noexcept { free(p); }
|
||||
};
|
||||
|
||||
#endif
|
@ -2,6 +2,7 @@
|
||||
#define _MOBALINE_H
|
||||
|
||||
#include "globals.h"
|
||||
#include "timekeeper.h"
|
||||
#include "dcf77.h"
|
||||
|
||||
#define MOBALINE_FRAME_SIZE (33)
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "sensor.h"
|
||||
#include "sds011read.h"
|
||||
#include "gpsread.h"
|
||||
|
||||
// MyDevices CayenneLPP 1.0 channels for Synamic sensor payload format
|
||||
// all payload goes out on LoRa FPort 1
|
||||
@ -51,7 +52,6 @@ public:
|
||||
void addConfig(configData_t value);
|
||||
void addStatus(uint16_t voltage, uint64_t uptime, float cputemp, uint32_t mem,
|
||||
uint8_t reset0, uint32_t restarts);
|
||||
void addAlarm(int8_t rssi, uint8_t message);
|
||||
void addVoltage(uint16_t value);
|
||||
void addGPS(gpsStatus_t value);
|
||||
void addBME(bmeStatus_t value);
|
||||
@ -100,5 +100,6 @@ private:
|
||||
};
|
||||
|
||||
extern PayloadConvert payload;
|
||||
extern uint8_t batt_level;
|
||||
|
||||
#endif // _PAYLOAD_H_
|
||||
|
@ -8,12 +8,11 @@
|
||||
#include "configmanager.h"
|
||||
#include "lorawan.h"
|
||||
#include "sensor.h"
|
||||
#include "macsniff.h"
|
||||
#include "wifiscan.h"
|
||||
#include "cyclic.h"
|
||||
#include "timekeeper.h"
|
||||
#include "timesync.h"
|
||||
#include "blescan.h"
|
||||
#include "power.h"
|
||||
#include "antenna.h"
|
||||
|
||||
// maximum number of elements in rcommand interpreter queue
|
||||
#define RCMD_QUEUE_SIZE 5
|
||||
|
@ -16,6 +16,15 @@ void enter_deepsleep(const uint64_t wakeup_sec = 60,
|
||||
const gpio_num_t wakeup_gpio = GPIO_NUM_MAX);
|
||||
unsigned long long uptime(void);
|
||||
|
||||
enum runmode_t {
|
||||
RUNMODE_POWERCYCLE,
|
||||
RUNMODE_NORMAL,
|
||||
RUNMODE_WAKEUP,
|
||||
RUNMODE_UPDATE,
|
||||
RUNMODE_SLEEP,
|
||||
RUNMODE_MAINTENANCE
|
||||
};
|
||||
|
||||
extern RTC_NOINIT_ATTR runmode_t RTC_runmode;
|
||||
extern RTC_NOINIT_ATTR uint32_t RTC_restarts;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#ifndef _SENDDATA_H
|
||||
#define _SENDDATA_H
|
||||
|
||||
#include "libpax_helpers.h"
|
||||
#include "spislave.h"
|
||||
#include "mqttclient.h"
|
||||
#include "cyclic.h"
|
||||
@ -13,13 +14,14 @@
|
||||
#include "corona.h"
|
||||
#endif
|
||||
|
||||
extern Ticker sendTimer;
|
||||
extern struct count_payload_t count_from_libpax;
|
||||
|
||||
void SendPayload(uint8_t port);
|
||||
void sendData(void);
|
||||
void checkSendQueues(void);
|
||||
void flushQueues(void);
|
||||
bool allQueuesEmtpy(void);
|
||||
void setSendIRQ(void);
|
||||
void setSendIRQ(TimerHandle_t xTimer = NULL);
|
||||
void initSendDataTimer(uint8_t sendcycle);
|
||||
|
||||
#endif // _SENDDATA_H_
|
||||
|
@ -1,6 +1,8 @@
|
||||
#ifndef _SENSOR_H
|
||||
#define _SENSOR_H
|
||||
|
||||
#include "configmanager.h"
|
||||
|
||||
#define HAS_SENSORS (HAS_SENSOR_1 || HAS_SENSOR_2 || HAS_SENSOR_3)
|
||||
|
||||
uint8_t sensor_mask(uint8_t sensor_no);
|
||||
|
@ -3,15 +3,21 @@
|
||||
|
||||
#include "globals.h"
|
||||
#include "rtctime.h"
|
||||
#include "TimeLib.h"
|
||||
#include "irqhandler.h"
|
||||
#include "timesync.h"
|
||||
#include "gpsread.h"
|
||||
#include "if482.h"
|
||||
#include "dcf77.h"
|
||||
|
||||
enum timesource_t { _gps, _rtc, _lora, _unsynced, _set };
|
||||
|
||||
extern const char timeSetSymbols[];
|
||||
extern Ticker timesyncer;
|
||||
extern timesource_t timeSource;
|
||||
extern TaskHandle_t ClockTask;
|
||||
extern Timezone myTZ;
|
||||
extern bool volatile TimePulseTick; // 1sec pps flag set by GPS or RTC
|
||||
extern hw_timer_t *ppsIRQ;
|
||||
|
||||
void IRAM_ATTR CLOCKIRQ(void);
|
||||
void clock_init(void);
|
||||
@ -21,7 +27,8 @@ void setTimeSyncIRQ(void);
|
||||
uint8_t timepulse_init(void);
|
||||
time_t timeIsValid(time_t const t);
|
||||
void calibrateTime(void);
|
||||
void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, timesource_t mytimesource);
|
||||
void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
|
||||
timesource_t mytimesource);
|
||||
time_t compiledUTC(void);
|
||||
TickType_t tx_Ticks(uint32_t framesize, unsigned long baud, uint32_t config,
|
||||
int8_t rxPin, int8_t txPins);
|
||||
|
@ -1,19 +0,0 @@
|
||||
#ifndef _WIFISCAN_H
|
||||
#define _WIFISCAN_H
|
||||
|
||||
// ESP32 Functions
|
||||
#include <esp_wifi.h>
|
||||
|
||||
#include "hash.h" // Hash function for scrambling MAC addresses
|
||||
#include "antenna.h" // code for switching wifi antennas
|
||||
#include "macsniff.h"
|
||||
|
||||
extern TimerHandle_t WifiChanTimer;
|
||||
|
||||
void wifi_sniffer_init(void);
|
||||
void switch_wifi_sniffer(uint8_t state);
|
||||
void IRAM_ATTR wifi_sniffer_packet_handler(void *buff,
|
||||
wifi_promiscuous_pkt_type_t type);
|
||||
void switchWifiChannel(TimerHandle_t xTimer);
|
||||
|
||||
#endif
|
@ -16,7 +16,7 @@
|
||||
"include": "bintray-secure-ota/lib/BintrayClient"
|
||||
},
|
||||
"dependencies": {
|
||||
"ArduinoJson": "^5.13.1"
|
||||
"ArduinoJson": "^6"
|
||||
},
|
||||
"version": "1.0.0",
|
||||
"frameworks": "arduino",
|
||||
|
@ -1,105 +0,0 @@
|
||||
/*
|
||||
Parts of this file
|
||||
Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
**/
|
||||
|
||||
#ifndef BINTRAY_CERTIFICATES_H
|
||||
#define BINTRAY_CERTIFICATES_H
|
||||
|
||||
const char* BINTRAY_API_ROOT_CA = \
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n"
|
||||
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
|
||||
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n"
|
||||
"QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n"
|
||||
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n"
|
||||
"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n"
|
||||
"9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n"
|
||||
"CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n"
|
||||
"nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n"
|
||||
"43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n"
|
||||
"T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n"
|
||||
"gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n"
|
||||
"BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n"
|
||||
"TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n"
|
||||
"DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n"
|
||||
"hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n"
|
||||
"06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n"
|
||||
"PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n"
|
||||
"YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n"
|
||||
"CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
const char* BINTRAY_AKAMAI_ROOT_CA = \
|
||||
"-----BEGIN CERTIFICATE-----\n"\
|
||||
"MIIElDCCA3ygAwIBAgIQAf2j627KdciIQ4tyS8+8kTANBgkqhkiG9w0BAQsFADBh\n"\
|
||||
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"\
|
||||
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n"\
|
||||
"QTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaME0xCzAJBgNVBAYTAlVT\n"\
|
||||
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJzAlBgNVBAMTHkRpZ2lDZXJ0IFNIQTIg\n"\
|
||||
"U2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\n"\
|
||||
"ANyuWJBNwcQwFZA1W248ghX1LFy949v/cUP6ZCWA1O4Yok3wZtAKc24RmDYXZK83\n"\
|
||||
"nf36QYSvx6+M/hpzTc8zl5CilodTgyu5pnVILR1WN3vaMTIa16yrBvSqXUu3R0bd\n"\
|
||||
"KpPDkC55gIDvEwRqFDu1m5K+wgdlTvza/P96rtxcflUxDOg5B6TXvi/TC2rSsd9f\n"\
|
||||
"/ld0Uzs1gN2ujkSYs58O09rg1/RrKatEp0tYhG2SS4HD2nOLEpdIkARFdRrdNzGX\n"\
|
||||
"kujNVA075ME/OV4uuPNcfhCOhkEAjUVmR7ChZc6gqikJTvOX6+guqw9ypzAO+sf0\n"\
|
||||
"/RR3w6RbKFfCs/mC/bdFWJsCAwEAAaOCAVowggFWMBIGA1UdEwEB/wQIMAYBAf8C\n"\
|
||||
"AQAwDgYDVR0PAQH/BAQDAgGGMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYY\n"\
|
||||
"aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6\n"\
|
||||
"Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwN6A1\n"\
|
||||
"oDOGMWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RD\n"\
|
||||
"QS5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v\n"\
|
||||
"d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwHQYDVR0OBBYEFA+AYRyCMWHVLyjnjUY4tCzh\n"\
|
||||
"xtniMB8GA1UdIwQYMBaAFAPeUDVW0Uy7ZvCj4hsbw5eyPdFVMA0GCSqGSIb3DQEB\n"\
|
||||
"CwUAA4IBAQAjPt9L0jFCpbZ+QlwaRMxp0Wi0XUvgBCFsS+JtzLHgl4+mUwnNqipl\n"\
|
||||
"5TlPHoOlblyYoiQm5vuh7ZPHLgLGTUq/sELfeNqzqPlt/yGFUzZgTHbO7Djc1lGA\n"\
|
||||
"8MXW5dRNJ2Srm8c+cftIl7gzbckTB+6WohsYFfZcTEDts8Ls/3HB40f/1LkAtDdC\n"\
|
||||
"2iDJ6m6K7hQGrn2iWZiIqBtvLfTyyRRfJs8sjX7tN8Cp1Tm5gr8ZDOo0rwAhaPit\n"\
|
||||
"c+LJMto4JQtV05od8GiG7S5BNO98pVAdvzr508EIDObtHopYJeS4d60tbvVS3bR0\n"\
|
||||
"j6tJLp07kzQoH3jOlOrHvdPJbRzeXDLz\n"\
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
const char* CLOUDFRONT_API_ROOT_CA = \
|
||||
"-----BEGIN CERTIFICATE-----\n"\
|
||||
"MIIE3zCCA8egAwIBAgIQYxgNOPuAl3ip0DWjFhj4QDANBgkqhkiG9w0BAQsFADCB\n"\
|
||||
"yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL\n"\
|
||||
"ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp\n"\
|
||||
"U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW\n"\
|
||||
"ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0\n"\
|
||||
"aG9yaXR5IC0gRzUwHhcNMTcxMTA2MDAwMDAwWhcNMjIxMTA1MjM1OTU5WjBhMQsw\n"\
|
||||
"CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu\n"\
|
||||
"ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjCC\n"\
|
||||
"ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALs3zTTce2vJsmiQrUp1/0a6\n"\
|
||||
"IQoIjfUZVMn7iNvzrvI6iZE8euarBhprz6wt6F4JJES6Ypp+1qOofuBUdSAFrFC3\n"\
|
||||
"nGMabDDc2h8Zsdce3v3X4MuUgzeu7B9DTt17LNK9LqUv5Km4rTrUmaS2JembawBg\n"\
|
||||
"kmD/TyFJGPdnkKthBpyP8rrptOmSMmu181foXRvNjB2rlQSVSfM1LZbjSW3dd+P7\n"\
|
||||
"SUu0rFUHqY+Vs7Qju0xtRfD2qbKVMLT9TFWMJ0pXFHyCnc1zktMWSgYMjFDRjx4J\n"\
|
||||
"vheh5iHK/YPlELyDpQrEZyj2cxQUPUZ2w4cUiSE0Ta8PRQymSaG6u5zFsTODKYUC\n"\
|
||||
"AwEAAaOCAScwggEjMB0GA1UdDgQWBBROIlQgGJXm427mD/r6uRLtBhePOTAPBgNV\n"\
|
||||
"HRMBAf8EBTADAQH/MF8GA1UdIARYMFYwVAYEVR0gADBMMCMGCCsGAQUFBwIBFhdo\n"\
|
||||
"dHRwczovL2Quc3ltY2IuY29tL2NwczAlBggrBgEFBQcCAjAZDBdodHRwczovL2Qu\n"\
|
||||
"c3ltY2IuY29tL3JwYTAvBgNVHR8EKDAmMCSgIqAghh5odHRwOi8vcy5zeW1jYi5j\n"\
|
||||
"b20vcGNhMy1nNS5jcmwwDgYDVR0PAQH/BAQDAgGGMC4GCCsGAQUFBwEBBCIwIDAe\n"\
|
||||
"BggrBgEFBQcwAYYSaHR0cDovL3Muc3ltY2QuY29tMB8GA1UdIwQYMBaAFH/TZafC\n"\
|
||||
"3ey78DAJ80M5+gKvMzEzMA0GCSqGSIb3DQEBCwUAA4IBAQBQ3dNWKSUBip6n5X1N\n"\
|
||||
"ua8bjKLSJzXlnescavPECMpFBlIIKH2mc6mL2Xr/wkSIBDrsqAO3sBcmoJN+n8V3\n"\
|
||||
"0O5JelrtEAFYSyRDXfu78ZlHn6kvV5/jPUFECEM/hdN0x8WdLpGjJMqfs0EG5qHj\n"\
|
||||
"+UaxpucWD445wea4zlK7hUR+MA8fq0Yd1HEKj4c8TcgaQIHMa4KHr448cQ69e3CP\n"\
|
||||
"ECRhRNg+RAKT2I7SlaVzLvaB/8yym2oMCEsoqiRT8dbXg35aKEYmmzn3O/mnB7bG\n"\
|
||||
"Ud/EUrkIf7FVamgYZd1fSzQeg1cHqf0ja6eHpvq2bTl+cWFHaq/84KlHe5Rh0Csm\n"\
|
||||
"pZzn\n"\
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
#endif // BINTRAY_CERTIFICATES_H
|
@ -20,16 +20,12 @@
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include "BintrayClient.h"
|
||||
#include "BintrayCertificates.h"
|
||||
|
||||
BintrayClient::BintrayClient(const String &user, const String &repository, const String &package)
|
||||
: m_user(user), m_repo(repository), m_package(package),
|
||||
m_storage_host("dl.bintray.com"),
|
||||
m_api_host("api.bintray.com")
|
||||
m_storage_host("pax.express"),
|
||||
m_api_host("pax.express")
|
||||
{
|
||||
m_certificates.emplace_back("cloudfront.net", CLOUDFRONT_API_ROOT_CA);
|
||||
m_certificates.emplace_back("akamai.bintray.com", BINTRAY_AKAMAI_ROOT_CA);
|
||||
m_certificates.emplace_back("bintray.com", BINTRAY_API_ROOT_CA);
|
||||
}
|
||||
|
||||
String BintrayClient::getUser() const
|
||||
@ -83,7 +79,7 @@ String BintrayClient::requestHTTPContent(const String &url) const
|
||||
{
|
||||
String payload;
|
||||
HTTPClient http;
|
||||
http.begin(url, getCertificate(url));
|
||||
http.begin(url);
|
||||
int httpCode = http.GET();
|
||||
|
||||
if (httpCode > 0)
|
||||
|
@ -1 +0,0 @@
|
||||
|
@ -1,99 +0,0 @@
|
||||
/* DateStrings.cpp
|
||||
* Definitions for date strings for use with the Time library
|
||||
*
|
||||
* Updated for Arduino 1.5.7 18 July 2014
|
||||
*
|
||||
* No memory is consumed in the sketch if your code does not call any of the string methods
|
||||
* You can change the text of the strings, make sure the short strings are each exactly 3 characters
|
||||
* the long strings can be any length up to the constant dt_MAX_STRING_LEN defined in TimeLib.h
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(__AVR__)
|
||||
#include <avr/pgmspace.h>
|
||||
#else
|
||||
// for compatiblity with Arduino Due and Teensy 3.0 and maybe others?
|
||||
#define PROGMEM
|
||||
#define PGM_P const char *
|
||||
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
|
||||
#define pgm_read_word(addr) (*(const unsigned char **)(addr))
|
||||
#ifndef strcpy_P
|
||||
#define strcpy_P(dest, src) strcpy((dest), (src))
|
||||
#endif
|
||||
#endif
|
||||
#include <string.h> // for strcpy_P or strcpy
|
||||
#include "microTimeLib.h"
|
||||
|
||||
// the short strings for each day or month must be exactly dt_SHORT_STR_LEN
|
||||
#define dt_SHORT_STR_LEN 3 // the length of short strings
|
||||
|
||||
static char buffer[dt_MAX_STRING_LEN+1]; // must be big enough for longest string and the terminating null
|
||||
|
||||
const char monthStr0[] PROGMEM = "";
|
||||
const char monthStr1[] PROGMEM = "January";
|
||||
const char monthStr2[] PROGMEM = "February";
|
||||
const char monthStr3[] PROGMEM = "March";
|
||||
const char monthStr4[] PROGMEM = "April";
|
||||
const char monthStr5[] PROGMEM = "May";
|
||||
const char monthStr6[] PROGMEM = "June";
|
||||
const char monthStr7[] PROGMEM = "July";
|
||||
const char monthStr8[] PROGMEM = "August";
|
||||
const char monthStr9[] PROGMEM = "September";
|
||||
const char monthStr10[] PROGMEM = "October";
|
||||
const char monthStr11[] PROGMEM = "November";
|
||||
const char monthStr12[] PROGMEM = "December";
|
||||
|
||||
const PROGMEM char * const PROGMEM monthNames_P[] =
|
||||
{
|
||||
monthStr0,monthStr1,monthStr2,monthStr3,monthStr4,monthStr5,monthStr6,
|
||||
monthStr7,monthStr8,monthStr9,monthStr10,monthStr11,monthStr12
|
||||
};
|
||||
|
||||
const char monthShortNames_P[] PROGMEM = "ErrJanFebMarAprMayJunJulAugSepOctNovDec";
|
||||
|
||||
const char dayStr0[] PROGMEM = "Err";
|
||||
const char dayStr1[] PROGMEM = "Sunday";
|
||||
const char dayStr2[] PROGMEM = "Monday";
|
||||
const char dayStr3[] PROGMEM = "Tuesday";
|
||||
const char dayStr4[] PROGMEM = "Wednesday";
|
||||
const char dayStr5[] PROGMEM = "Thursday";
|
||||
const char dayStr6[] PROGMEM = "Friday";
|
||||
const char dayStr7[] PROGMEM = "Saturday";
|
||||
|
||||
const PROGMEM char * const PROGMEM dayNames_P[] =
|
||||
{
|
||||
dayStr0,dayStr1,dayStr2,dayStr3,dayStr4,dayStr5,dayStr6,dayStr7
|
||||
};
|
||||
|
||||
const char dayShortNames_P[] PROGMEM = "ErrSunMonTueWedThuFriSat";
|
||||
|
||||
/* functions to return date strings */
|
||||
|
||||
char* monthStr(uint8_t month)
|
||||
{
|
||||
strcpy_P(buffer, (PGM_P)pgm_read_word(&(monthNames_P[month])));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char* monthShortStr(uint8_t month)
|
||||
{
|
||||
for (int i=0; i < dt_SHORT_STR_LEN; i++)
|
||||
buffer[i] = pgm_read_byte(&(monthShortNames_P[i+ (month*dt_SHORT_STR_LEN)]));
|
||||
buffer[dt_SHORT_STR_LEN] = 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char* dayStr(uint8_t day)
|
||||
{
|
||||
strcpy_P(buffer, (PGM_P)pgm_read_word(&(dayNames_P[day])));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char* dayShortStr(uint8_t day)
|
||||
{
|
||||
uint8_t index = day*dt_SHORT_STR_LEN;
|
||||
for (int i=0; i < dt_SHORT_STR_LEN; i++)
|
||||
buffer[i] = pgm_read_byte(&(dayShortNames_P[index + i]));
|
||||
buffer[dt_SHORT_STR_LEN] = 0;
|
||||
return buffer;
|
||||
}
|
@ -1,164 +0,0 @@
|
||||
# Arduino Time Library
|
||||
|
||||
Time is a library that provides timekeeping functionality for Arduino.
|
||||
|
||||
The code is derived from the Playground DateTime library but is updated
|
||||
to provide an API that is more flexible and easier to use.
|
||||
|
||||
A primary goal was to enable date and time functionality that can be used with
|
||||
a variety of external time sources with minimum differences required in sketch logic.
|
||||
|
||||
Example sketches illustrate how similar sketch code can be used with: a Real Time Clock,
|
||||
internet NTP time service, GPS time data, and Serial time messages from a computer
|
||||
for time synchronization.
|
||||
|
||||
## Functionality
|
||||
|
||||
The functions available in the library include
|
||||
|
||||
```c
|
||||
hour(); // the hour now (0-23)
|
||||
minute(); // the minute now (0-59)
|
||||
second(); // the second now (0-59)
|
||||
day(); // the day now (1-31)
|
||||
weekday(); // day of the week (1-7), Sunday is day 1
|
||||
month(); // the month now (1-12)
|
||||
year(); // the full four digit year: (2009, 2010 etc)
|
||||
```
|
||||
|
||||
there are also functions to return the hour in 12-hour format
|
||||
|
||||
```c
|
||||
hourFormat12(); // the hour now in 12 hour format
|
||||
isAM(); // returns true if time now is AM
|
||||
isPM(); // returns true if time now is PM
|
||||
```
|
||||
|
||||
The time and date functions can take an optional parameter for the time. This prevents
|
||||
errors if the time rolls over between elements. For example, if a new minute begins
|
||||
between getting the minute and second, the values will be inconsistent. Using the
|
||||
following functions eliminates this problem
|
||||
|
||||
```c
|
||||
time_t t = now(); // store the current time in time variable t
|
||||
hour(t); // returns the hour for the given time t
|
||||
minute(t); // returns the minute for the given time t
|
||||
second(t); // returns the second for the given time t
|
||||
day(t); // the day for the given time t
|
||||
weekday(t); // day of the week for the given time t
|
||||
month(t); // the month for the given time t
|
||||
year(t); // the year for the given time t
|
||||
```
|
||||
|
||||
There are also two functions that return the number of milliseconds left-over. Care
|
||||
should be taken when using this value since there are no functions to set the time
|
||||
with sub-second accuracy and the value may jump when the time is synchronized.
|
||||
However, it is always consistent with the current time. To access these functions,
|
||||
you have to `#define TIMELIB_ENABLE_MILLIS` in your sketch.
|
||||
|
||||
```c
|
||||
time_t t = now(uint32_t& m) // store the current time in time variable t and milliseconds in m
|
||||
millisecond(); // the millisecond now (0-999)
|
||||
```
|
||||
|
||||
Functions for managing the timer services are:
|
||||
|
||||
```c
|
||||
setTime(t); // set the system time to the give time t
|
||||
setTime(hr,min,sec,day,mnth,yr); // alternative to above, yr is 2 or 4 digit yr
|
||||
// (2010 or 10 sets year to 2010)
|
||||
adjustTime(adjustment); // adjust system time by adding the adjustment value (in seconds)
|
||||
timeStatus(); // indicates if time has been set and recently synchronized
|
||||
// returns one of the following enumerations:
|
||||
timeNotSet // the time has never been set, the clock started on Jan 1, 1970
|
||||
timeNeedsSync // the time had been set but a sync attempt did not succeed
|
||||
timeSet // the time is set and is synced
|
||||
```
|
||||
|
||||
Time and Date values are not valid if the status is timeNotSet. Otherwise, values can be used but
|
||||
the returned time may have drifted if the status is timeNeedsSync.
|
||||
|
||||
```c
|
||||
setSyncProvider(getTimeFunction); // set the external time provider
|
||||
setSyncInterval(interval); // set the number of seconds between re-sync
|
||||
```
|
||||
|
||||
There are many convenience macros in the `time.h` file for time constants and conversion
|
||||
of time units.
|
||||
|
||||
To use the library, copy the download to the Library directory.
|
||||
|
||||
## Examples
|
||||
|
||||
The Time directory contains the Time library and some example sketches
|
||||
illustrating how the library can be used with various time sources:
|
||||
|
||||
- `TimeSerial.pde` shows Arduino as a clock without external hardware.
|
||||
It is synchronized by time messages sent over the serial port.
|
||||
A companion Processing sketch will automatically provide these messages
|
||||
if it is running and connected to the Arduino serial port.
|
||||
|
||||
- `TimeSerialDateStrings.pde` adds day and month name strings to the sketch above
|
||||
Short (3 characters) and long strings are available to print the days of
|
||||
the week and names of the months.
|
||||
|
||||
- `TimeRTC` uses a DS1307 real-time clock to provide time synchronization.
|
||||
A basic RTC library named DS1307RTC is included in the download.
|
||||
To run this sketch the DS1307RTC library must be installed.
|
||||
|
||||
- `TimeRTCSet` is similar to the above and adds the ability to set the Real Time Clock
|
||||
|
||||
- `TimeRTCLog` demonstrates how to calculate the difference between times.
|
||||
It is a very simple logger application that monitors events on digital pins
|
||||
and prints (to the serial port) the time of an event and the time period since
|
||||
the previous event.
|
||||
|
||||
- `TimeNTP` uses the Arduino Ethernet shield to access time using the internet NTP time service.
|
||||
The NTP protocol uses UDP and the UdpBytewise library is required, see:
|
||||
http://bitbucket.org/bjoern/arduino_osc/src/14667490521f/libraries/Ethernet/
|
||||
|
||||
- `TimeGPS` gets time from a GPS
|
||||
This requires the TinyGPS library from Mikal Hart:
|
||||
http://arduiniana.org/libraries/TinyGPS
|
||||
|
||||
## Differences
|
||||
|
||||
Differences between this code and the playground DateTime library
|
||||
although the Time library is based on the DateTime codebase, the API has changed.
|
||||
Changes in the Time library API:
|
||||
|
||||
- time elements are functions returning `int` (they are variables in DateTime)
|
||||
- Years start from 1970
|
||||
- days of the week and months start from 1 (they start from 0 in DateTime)
|
||||
- DateStrings do not require a separate library
|
||||
- time elements can be accessed non-atomically (in DateTime they are always atomic)
|
||||
- function added to automatically sync time with external source
|
||||
- `localTime` and `maketime` parameters changed, `localTime` renamed to `breakTime`
|
||||
|
||||
## Technical notes:
|
||||
|
||||
Internal system time is based on the standard Unix `time_t`.
|
||||
The value is the number of seconds since Jan 1, 1970.
|
||||
System time begins at zero when the sketch starts.
|
||||
|
||||
The internal time can be automatically synchronized at regular intervals to an external time source.
|
||||
This is enabled by calling the `setSyncProvider(provider)` function - the provider argument is
|
||||
the address of a function that returns the current time as a `time_t`.
|
||||
See the sketches in the examples directory for usage.
|
||||
|
||||
The default interval for re-syncing the time is 5 minutes but can be changed by calling the
|
||||
`setSyncInterval(interval)` method to set the number of seconds between re-sync attempts.
|
||||
|
||||
The Time library defines a structure for holding time elements that is a compact version of the C tm structure.
|
||||
All the members of the Arduino tm structure are bytes and the year is offset from 1970.
|
||||
Convenience macros provide conversion to and from the Arduino format.
|
||||
|
||||
Low level functions to convert between system time and individual time elements are provided:
|
||||
|
||||
```c
|
||||
breakTime(time, &tm); // break time_t into elements stored in tm struct
|
||||
makeTime(&tm); // return time_t from elements stored in tm struct
|
||||
```
|
||||
|
||||
The DS1307RTC library included in the download provides an example of how a time provider
|
||||
can use the low-level functions to interface with the Time library.
|
@ -1,64 +0,0 @@
|
||||
Please use this form only to report code defects or bugs.
|
||||
|
||||
For any question, even questions directly pertaining to this code, post your question on the forums related to the board you are using.
|
||||
|
||||
Arduino: forum.arduino.cc
|
||||
Teensy: forum.pjrc.com
|
||||
ESP8266: www.esp8266.com
|
||||
ESP32: www.esp32.com
|
||||
Adafruit Feather/Metro/Trinket: forums.adafruit.com
|
||||
Particle Photon: community.particle.io
|
||||
|
||||
If you are experiencing trouble but not certain of the cause, or need help using this code, ask on the appropriate forum. This is not the place to ask for support or help, even directly related to this code. Only use this form you are certain you have discovered a defect in this code!
|
||||
|
||||
Please verify the problem occurs when using the very latest version, using the newest version of Arduino and any other related software.
|
||||
|
||||
|
||||
----------------------------- Remove above -----------------------------
|
||||
|
||||
|
||||
|
||||
### Description
|
||||
|
||||
Describe your problem.
|
||||
|
||||
|
||||
|
||||
### Steps To Reproduce Problem
|
||||
|
||||
Please give detailed instructions needed for anyone to attempt to reproduce the problem.
|
||||
|
||||
|
||||
|
||||
### Hardware & Software
|
||||
|
||||
Board
|
||||
Shields / modules used
|
||||
Arduino IDE version
|
||||
Teensyduino version (if using Teensy)
|
||||
Version info & package name (from Tools > Boards > Board Manager)
|
||||
Operating system & version
|
||||
Any other software or hardware?
|
||||
|
||||
|
||||
### Arduino Sketch
|
||||
|
||||
```cpp
|
||||
// Change the code below by your sketch (please try to give the smallest code which demonstrates the problem)
|
||||
#include <Arduino.h>
|
||||
|
||||
// libraries: give links/details so anyone can compile your code for the same result
|
||||
|
||||
void setup() {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Errors or Incorrect Output
|
||||
|
||||
If you see any errors or incorrect output, please show it here. Please use copy & paste to give an exact copy of the message. Details matter, so please show (not merely describe) the actual message or error exactly as it appears.
|
||||
|
||||
|
@ -1,78 +0,0 @@
|
||||
/**
|
||||
* SyncArduinoClock.
|
||||
*
|
||||
* portIndex must be set to the port connected to the Arduino
|
||||
*
|
||||
* The current time is sent in response to request message from Arduino
|
||||
* or by clicking the display window
|
||||
*
|
||||
* The time message is 11 ASCII text characters; a header (the letter 'T')
|
||||
* followed by the ten digit system time (unix time)
|
||||
*/
|
||||
|
||||
|
||||
import processing.serial.*;
|
||||
import java.util.Date;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
public static final short portIndex = 0; // select the com port, 0 is the first port
|
||||
public static final String TIME_HEADER = "T"; //header for arduino serial time message
|
||||
public static final char TIME_REQUEST = 7; // ASCII bell character
|
||||
public static final char LF = 10; // ASCII linefeed
|
||||
public static final char CR = 13; // ASCII linefeed
|
||||
Serial myPort; // Create object from Serial class
|
||||
|
||||
void setup() {
|
||||
size(200, 200);
|
||||
println(Serial.list());
|
||||
println(" Connecting to -> " + Serial.list()[portIndex]);
|
||||
myPort = new Serial(this,Serial.list()[portIndex], 9600);
|
||||
println(getTimeNow());
|
||||
}
|
||||
|
||||
void draw()
|
||||
{
|
||||
textSize(20);
|
||||
textAlign(CENTER);
|
||||
fill(0);
|
||||
text("Click to send\nTime Sync", 0, 75, 200, 175);
|
||||
if ( myPort.available() > 0) { // If data is available,
|
||||
char val = char(myPort.read()); // read it and store it in val
|
||||
if(val == TIME_REQUEST){
|
||||
long t = getTimeNow();
|
||||
sendTimeMessage(TIME_HEADER, t);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(val == LF)
|
||||
; //igonore
|
||||
else if(val == CR)
|
||||
println();
|
||||
else
|
||||
print(val); // echo everying but time request
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mousePressed() {
|
||||
sendTimeMessage( TIME_HEADER, getTimeNow());
|
||||
}
|
||||
|
||||
|
||||
void sendTimeMessage(String header, long time) {
|
||||
String timeStr = String.valueOf(time);
|
||||
myPort.write(header); // send header and time to arduino
|
||||
myPort.write(timeStr);
|
||||
myPort.write('\n');
|
||||
}
|
||||
|
||||
long getTimeNow(){
|
||||
// java time is in ms, we want secs
|
||||
Date d = new Date();
|
||||
Calendar cal = new GregorianCalendar();
|
||||
long current = d.getTime()/1000;
|
||||
long timezone = cal.get(cal.ZONE_OFFSET)/1000;
|
||||
long daylight = cal.get(cal.DST_OFFSET)/1000;
|
||||
return current + timezone + daylight;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
SyncArduinoClock is a Processing sketch that responds to Arduino requests for
|
||||
time synchronization messages.
|
||||
|
||||
The portIndex must be set the Serial port connected to Arduino.
|
||||
|
||||
Download TimeSerial.pde onto Arduino and you should see the time
|
||||
message displayed when you run SyncArduinoClock in Processing.
|
||||
The Arduino time is set from the time on your computer through the
|
||||
Processing sketch.
|
@ -1,71 +0,0 @@
|
||||
/*
|
||||
* TimeRTC.pde
|
||||
* example code illustrating Time library with Real Time Clock.
|
||||
*
|
||||
* This example requires Markus Lange's Arduino Due RTC Library
|
||||
* https://github.com/MarkusLange/Arduino-Due-RTC-Library
|
||||
*/
|
||||
|
||||
#include <TimeLib.h>
|
||||
#include <rtc_clock.h>
|
||||
|
||||
// Select the Slowclock source
|
||||
//RTC_clock rtc_clock(RC);
|
||||
RTC_clock rtc_clock(XTAL);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
rtc_clock.init();
|
||||
if (rtc_clock.date_already_set() == 0) {
|
||||
// Unfortunately, the Arduino Due hardware does not seem to
|
||||
// be designed to maintain the RTC clock state when the
|
||||
// board resets. Markus described it thusly: "Uhh the Due
|
||||
// does reset with the NRSTB pin. This resets the full chip
|
||||
// with all backup regions including RTC, RTT and SC. Only
|
||||
// if the reset is done with the NRST pin will these regions
|
||||
// stay with their old values."
|
||||
rtc_clock.set_time(__TIME__);
|
||||
rtc_clock.set_date(__DATE__);
|
||||
// However, this might work on other unofficial SAM3X boards
|
||||
// with different reset circuitry than Arduino Due?
|
||||
}
|
||||
setSyncProvider(getArduinoDueTime);
|
||||
if(timeStatus()!= timeSet)
|
||||
Serial.println("Unable to sync with the RTC");
|
||||
else
|
||||
Serial.println("RTC has set the system time");
|
||||
}
|
||||
|
||||
time_t getArduinoDueTime()
|
||||
{
|
||||
return rtc_clock.unixtime();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
digitalClockDisplay();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void digitalClockDisplay(){
|
||||
// digital clock display of the time
|
||||
Serial.print(hour());
|
||||
printDigits(minute());
|
||||
printDigits(second());
|
||||
Serial.print(" ");
|
||||
Serial.print(day());
|
||||
Serial.print(" ");
|
||||
Serial.print(month());
|
||||
Serial.print(" ");
|
||||
Serial.print(year());
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void printDigits(int digits){
|
||||
// utility function for digital clock display: prints preceding colon and leading 0
|
||||
Serial.print(":");
|
||||
if(digits < 10)
|
||||
Serial.print('0');
|
||||
Serial.print(digits);
|
||||
}
|
||||
|
@ -1,87 +0,0 @@
|
||||
/*
|
||||
* TimeGPS.pde
|
||||
* example code illustrating time synced from a GPS
|
||||
*
|
||||
*/
|
||||
|
||||
#include <TimeLib.h>
|
||||
#include <TinyGPS.h> // http://arduiniana.org/libraries/TinyGPS/
|
||||
#include <SoftwareSerial.h>
|
||||
// TinyGPS and SoftwareSerial libraries are the work of Mikal Hart
|
||||
|
||||
SoftwareSerial SerialGPS = SoftwareSerial(10, 11); // receive on pin 10
|
||||
TinyGPS gps;
|
||||
|
||||
// To use a hardware serial port, which is far more efficient than
|
||||
// SoftwareSerial, uncomment this line and remove SoftwareSerial
|
||||
//#define SerialGPS Serial1
|
||||
|
||||
// Offset hours from gps time (UTC)
|
||||
const int offset = 1; // Central European Time
|
||||
//const int offset = -5; // Eastern Standard Time (USA)
|
||||
//const int offset = -4; // Eastern Daylight Time (USA)
|
||||
//const int offset = -8; // Pacific Standard Time (USA)
|
||||
//const int offset = -7; // Pacific Daylight Time (USA)
|
||||
|
||||
// Ideally, it should be possible to learn the time zone
|
||||
// based on the GPS position data. However, that would
|
||||
// require a complex library, probably incorporating some
|
||||
// sort of database using Eric Muller's time zone shape
|
||||
// maps, at http://efele.net/maps/tz/
|
||||
|
||||
time_t prevDisplay = 0; // when the digital clock was displayed
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(9600);
|
||||
while (!Serial) ; // Needed for Leonardo only
|
||||
SerialGPS.begin(4800);
|
||||
Serial.println("Waiting for GPS time ... ");
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
while (SerialGPS.available()) {
|
||||
if (gps.encode(SerialGPS.read())) { // process gps messages
|
||||
// when TinyGPS reports new data...
|
||||
unsigned long age;
|
||||
int Year;
|
||||
byte Month, Day, Hour, Minute, Second;
|
||||
gps.crack_datetime(&Year, &Month, &Day, &Hour, &Minute, &Second, NULL, &age);
|
||||
if (age < 500) {
|
||||
// set the Time to the latest GPS reading
|
||||
setTime(Hour, Minute, Second, Day, Month, Year);
|
||||
adjustTime(offset * SECS_PER_HOUR);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (timeStatus()!= timeNotSet) {
|
||||
if (now() != prevDisplay) { //update the display only if the time has changed
|
||||
prevDisplay = now();
|
||||
digitalClockDisplay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void digitalClockDisplay(){
|
||||
// digital clock display of the time
|
||||
Serial.print(hour());
|
||||
printDigits(minute());
|
||||
printDigits(second());
|
||||
Serial.print(" ");
|
||||
Serial.print(day());
|
||||
Serial.print(" ");
|
||||
Serial.print(month());
|
||||
Serial.print(" ");
|
||||
Serial.print(year());
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void printDigits(int digits) {
|
||||
// utility function for digital clock display: prints preceding colon and leading 0
|
||||
Serial.print(":");
|
||||
if(digits < 10)
|
||||
Serial.print('0');
|
||||
Serial.print(digits);
|
||||
}
|
||||
|
@ -1,135 +0,0 @@
|
||||
/*
|
||||
* Time_NTP.pde
|
||||
* Example showing time sync to NTP time source
|
||||
*
|
||||
* This sketch uses the Ethernet library
|
||||
*/
|
||||
|
||||
#include <TimeLib.h>
|
||||
#include <Ethernet.h>
|
||||
#include <EthernetUdp.h>
|
||||
#include <SPI.h>
|
||||
|
||||
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
|
||||
// NTP Servers:
|
||||
IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov
|
||||
// IPAddress timeServer(132, 163, 4, 102); // time-b.timefreq.bldrdoc.gov
|
||||
// IPAddress timeServer(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov
|
||||
|
||||
|
||||
const int timeZone = 1; // Central European Time
|
||||
//const int timeZone = -5; // Eastern Standard Time (USA)
|
||||
//const int timeZone = -4; // Eastern Daylight Time (USA)
|
||||
//const int timeZone = -8; // Pacific Standard Time (USA)
|
||||
//const int timeZone = -7; // Pacific Daylight Time (USA)
|
||||
|
||||
|
||||
EthernetUDP Udp;
|
||||
unsigned int localPort = 8888; // local port to listen for UDP packets
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(9600);
|
||||
while (!Serial) ; // Needed for Leonardo only
|
||||
delay(250);
|
||||
Serial.println("TimeNTP Example");
|
||||
if (Ethernet.begin(mac) == 0) {
|
||||
// no point in carrying on, so do nothing forevermore:
|
||||
while (1) {
|
||||
Serial.println("Failed to configure Ethernet using DHCP");
|
||||
delay(10000);
|
||||
}
|
||||
}
|
||||
Serial.print("IP number assigned by DHCP is ");
|
||||
Serial.println(Ethernet.localIP());
|
||||
Udp.begin(localPort);
|
||||
Serial.println("waiting for sync");
|
||||
setSyncProvider(getNtpTime);
|
||||
}
|
||||
|
||||
time_t prevDisplay = 0; // when the digital clock was displayed
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (timeStatus() != timeNotSet) {
|
||||
if (now() != prevDisplay) { //update the display only if time has changed
|
||||
prevDisplay = now();
|
||||
digitalClockDisplay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void digitalClockDisplay(){
|
||||
// digital clock display of the time
|
||||
Serial.print(hour());
|
||||
printDigits(minute());
|
||||
printDigits(second());
|
||||
Serial.print(" ");
|
||||
Serial.print(day());
|
||||
Serial.print(" ");
|
||||
Serial.print(month());
|
||||
Serial.print(" ");
|
||||
Serial.print(year());
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void printDigits(int digits){
|
||||
// utility for digital clock display: prints preceding colon and leading 0
|
||||
Serial.print(":");
|
||||
if(digits < 10)
|
||||
Serial.print('0');
|
||||
Serial.print(digits);
|
||||
}
|
||||
|
||||
/*-------- NTP code ----------*/
|
||||
|
||||
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
|
||||
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
|
||||
|
||||
time_t getNtpTime()
|
||||
{
|
||||
while (Udp.parsePacket() > 0) ; // discard any previously received packets
|
||||
Serial.println("Transmit NTP Request");
|
||||
sendNTPpacket(timeServer);
|
||||
uint32_t beginWait = millis();
|
||||
while (millis() - beginWait < 1500) {
|
||||
int size = Udp.parsePacket();
|
||||
if (size >= NTP_PACKET_SIZE) {
|
||||
Serial.println("Receive NTP Response");
|
||||
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
|
||||
unsigned long secsSince1900;
|
||||
// convert four bytes starting at location 40 to a long integer
|
||||
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
|
||||
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
|
||||
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
|
||||
secsSince1900 |= (unsigned long)packetBuffer[43];
|
||||
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
|
||||
}
|
||||
}
|
||||
Serial.println("No NTP Response :-(");
|
||||
return 0; // return 0 if unable to get the time
|
||||
}
|
||||
|
||||
// send an NTP request to the time server at the given address
|
||||
void sendNTPpacket(IPAddress &address)
|
||||
{
|
||||
// set all bytes in the buffer to 0
|
||||
memset(packetBuffer, 0, NTP_PACKET_SIZE);
|
||||
// Initialize values needed to form NTP request
|
||||
// (see URL above for details on the packets)
|
||||
packetBuffer[0] = 0b11100011; // LI, Version, Mode
|
||||
packetBuffer[1] = 0; // Stratum, or type of clock
|
||||
packetBuffer[2] = 6; // Polling Interval
|
||||
packetBuffer[3] = 0xEC; // Peer Clock Precision
|
||||
// 8 bytes of zero for Root Delay & Root Dispersion
|
||||
packetBuffer[12] = 49;
|
||||
packetBuffer[13] = 0x4E;
|
||||
packetBuffer[14] = 49;
|
||||
packetBuffer[15] = 52;
|
||||
// all NTP fields have been given values, now
|
||||
// you can send a packet requesting a timestamp:
|
||||
Udp.beginPacket(address, 123); //NTP requests are to port 123
|
||||
Udp.write(packetBuffer, NTP_PACKET_SIZE);
|
||||
Udp.endPacket();
|
||||
}
|
||||
|
@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Time_NTP.pde
|
||||
* Example showing time sync to NTP time source
|
||||
*
|
||||
* Also shows how to handle DST automatically.
|
||||
*
|
||||
* This sketch uses the EtherCard library:
|
||||
* http://jeelabs.org/pub/docs/ethercard/
|
||||
*/
|
||||
|
||||
#include <TimeLib.h>
|
||||
#include <EtherCard.h>
|
||||
|
||||
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
|
||||
|
||||
// NTP Server
|
||||
const char timeServer[] PROGMEM = "pool.ntp.org";
|
||||
|
||||
const int utcOffset = 1; // Central European Time
|
||||
//const int utcOffset = -5; // Eastern Standard Time (USA)
|
||||
//const int utcOffset = -4; // Eastern Daylight Time (USA)
|
||||
//const int utcOffset = -8; // Pacific Standard Time (USA)
|
||||
//const int utcOffset = -7; // Pacific Daylight Time (USA)
|
||||
|
||||
// Packet buffer, must be big enough to packet and payload
|
||||
#define BUFFER_SIZE 550
|
||||
byte Ethernet::buffer[BUFFER_SIZE];
|
||||
|
||||
const unsigned int remotePort = 123;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(9600);
|
||||
|
||||
while (!Serial) // Needed for Leonardo only
|
||||
;
|
||||
delay(250);
|
||||
|
||||
Serial.println("TimeNTP_ENC28J60 Example");
|
||||
|
||||
if (ether.begin(BUFFER_SIZE, mac) == 0) {
|
||||
// no point in carrying on, so do nothing forevermore:
|
||||
while (1) {
|
||||
Serial.println("Failed to access Ethernet controller");
|
||||
delay(10000);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ether.dhcpSetup()) {
|
||||
// no point in carrying on, so do nothing forevermore:
|
||||
while (1) {
|
||||
Serial.println("Failed to configure Ethernet using DHCP");
|
||||
delay(10000);
|
||||
}
|
||||
}
|
||||
|
||||
ether.printIp("IP number assigned by DHCP is ", ether.myip);
|
||||
|
||||
Serial.println("waiting for sync");
|
||||
//setSyncProvider(getNtpTime); // Use this for GMT time
|
||||
setSyncProvider(getDstCorrectedTime); // Use this for local, DST-corrected time
|
||||
}
|
||||
|
||||
time_t prevDisplay = 0; // when the digital clock was displayed
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (timeStatus() != timeNotSet) {
|
||||
if (now() != prevDisplay) { //update the display only if time has changed
|
||||
prevDisplay = now();
|
||||
digitalClockDisplay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void digitalClockDisplay(){
|
||||
// digital clock display of the time
|
||||
Serial.print(hour());
|
||||
printDigits(minute());
|
||||
printDigits(second());
|
||||
Serial.print(" ");
|
||||
Serial.print(day());
|
||||
Serial.print(" ");
|
||||
Serial.print(month());
|
||||
Serial.print(" ");
|
||||
Serial.print(year());
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void printDigits(int digits){
|
||||
// utility for digital clock display: prints preceding colon and leading 0
|
||||
Serial.print(":");
|
||||
if(digits < 10)
|
||||
Serial.print('0');
|
||||
Serial.print(digits);
|
||||
}
|
||||
|
||||
/*-------- NTP code ----------*/
|
||||
|
||||
// SyncProvider that returns UTC time
|
||||
time_t getNtpTime()
|
||||
{
|
||||
// Send request
|
||||
Serial.println("Transmit NTP Request");
|
||||
if (!ether.dnsLookup(timeServer)) {
|
||||
Serial.println("DNS failed");
|
||||
return 0; // return 0 if unable to get the time
|
||||
} else {
|
||||
//ether.printIp("SRV: ", ether.hisip);
|
||||
ether.ntpRequest(ether.hisip, remotePort);
|
||||
|
||||
// Wait for reply
|
||||
uint32_t beginWait = millis();
|
||||
while (millis() - beginWait < 1500) {
|
||||
word len = ether.packetReceive();
|
||||
ether.packetLoop(len);
|
||||
|
||||
unsigned long secsSince1900 = 0L;
|
||||
if (len > 0 && ether.ntpProcessAnswer(&secsSince1900, remotePort)) {
|
||||
Serial.println("Receive NTP Response");
|
||||
return secsSince1900 - 2208988800UL;
|
||||
}
|
||||
}
|
||||
|
||||
Serial.println("No NTP Response :-(");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Alternative SyncProvider that automatically handles Daylight Saving Time (DST) periods,
|
||||
* at least in Europe, see below.
|
||||
*/
|
||||
time_t getDstCorrectedTime (void) {
|
||||
time_t t = getNtpTime ();
|
||||
|
||||
if (t > 0) {
|
||||
TimeElements tm;
|
||||
breakTime (t, tm);
|
||||
t += (utcOffset + dstOffset (tm.Day, tm.Month, tm.Year + 1970, tm.Hour)) * SECS_PER_HOUR;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/* This function returns the DST offset for the current UTC time.
|
||||
* This is valid for the EU, for other places see
|
||||
* http://www.webexhibits.org/daylightsaving/i.html
|
||||
*
|
||||
* Results have been checked for 2012-2030 (but should work since
|
||||
* 1996 to 2099) against the following references:
|
||||
* - http://www.uniquevisitor.it/magazine/ora-legale-italia.php
|
||||
* - http://www.calendario-365.it/ora-legale-orario-invernale.html
|
||||
*/
|
||||
byte dstOffset (byte d, byte m, unsigned int y, byte h) {
|
||||
// Day in March that DST starts on, at 1 am
|
||||
byte dstOn = (31 - (5 * y / 4 + 4) % 7);
|
||||
|
||||
// Day in October that DST ends on, at 2 am
|
||||
byte dstOff = (31 - (5 * y / 4 + 1) % 7);
|
||||
|
||||
if ((m > 3 && m < 10) ||
|
||||
(m == 3 && (d > dstOn || (d == dstOn && h >= 1))) ||
|
||||
(m == 10 && (d < dstOff || (d == dstOff && h <= 1))))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,156 +0,0 @@
|
||||
/*
|
||||
* TimeNTP_ESP8266WiFi.ino
|
||||
* Example showing time sync to NTP time source
|
||||
*
|
||||
* This sketch uses the ESP8266WiFi library
|
||||
*/
|
||||
|
||||
#include <TimeLib.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
const char ssid[] = "*************"; // your network SSID (name)
|
||||
const char pass[] = "********"; // your network password
|
||||
|
||||
// NTP Servers:
|
||||
static const char ntpServerName[] = "us.pool.ntp.org";
|
||||
//static const char ntpServerName[] = "time.nist.gov";
|
||||
//static const char ntpServerName[] = "time-a.timefreq.bldrdoc.gov";
|
||||
//static const char ntpServerName[] = "time-b.timefreq.bldrdoc.gov";
|
||||
//static const char ntpServerName[] = "time-c.timefreq.bldrdoc.gov";
|
||||
|
||||
const int timeZone = 1; // Central European Time
|
||||
//const int timeZone = -5; // Eastern Standard Time (USA)
|
||||
//const int timeZone = -4; // Eastern Daylight Time (USA)
|
||||
//const int timeZone = -8; // Pacific Standard Time (USA)
|
||||
//const int timeZone = -7; // Pacific Daylight Time (USA)
|
||||
|
||||
|
||||
WiFiUDP Udp;
|
||||
unsigned int localPort = 8888; // local port to listen for UDP packets
|
||||
|
||||
time_t getNtpTime();
|
||||
void digitalClockDisplay();
|
||||
void printDigits(int digits);
|
||||
void sendNTPpacket(IPAddress &address);
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(9600);
|
||||
while (!Serial) ; // Needed for Leonardo only
|
||||
delay(250);
|
||||
Serial.println("TimeNTP Example");
|
||||
Serial.print("Connecting to ");
|
||||
Serial.println(ssid);
|
||||
WiFi.begin(ssid, pass);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
Serial.print("IP number assigned by DHCP is ");
|
||||
Serial.println(WiFi.localIP());
|
||||
Serial.println("Starting UDP");
|
||||
Udp.begin(localPort);
|
||||
Serial.print("Local port: ");
|
||||
Serial.println(Udp.localPort());
|
||||
Serial.println("waiting for sync");
|
||||
setSyncProvider(getNtpTime);
|
||||
setSyncInterval(300);
|
||||
}
|
||||
|
||||
time_t prevDisplay = 0; // when the digital clock was displayed
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (timeStatus() != timeNotSet) {
|
||||
if (now() != prevDisplay) { //update the display only if time has changed
|
||||
prevDisplay = now();
|
||||
digitalClockDisplay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void digitalClockDisplay()
|
||||
{
|
||||
// digital clock display of the time
|
||||
Serial.print(hour());
|
||||
printDigits(minute());
|
||||
printDigits(second());
|
||||
Serial.print(" ");
|
||||
Serial.print(day());
|
||||
Serial.print(".");
|
||||
Serial.print(month());
|
||||
Serial.print(".");
|
||||
Serial.print(year());
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void printDigits(int digits)
|
||||
{
|
||||
// utility for digital clock display: prints preceding colon and leading 0
|
||||
Serial.print(":");
|
||||
if (digits < 10)
|
||||
Serial.print('0');
|
||||
Serial.print(digits);
|
||||
}
|
||||
|
||||
/*-------- NTP code ----------*/
|
||||
|
||||
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
|
||||
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
|
||||
|
||||
time_t getNtpTime()
|
||||
{
|
||||
IPAddress ntpServerIP; // NTP server's ip address
|
||||
|
||||
while (Udp.parsePacket() > 0) ; // discard any previously received packets
|
||||
Serial.println("Transmit NTP Request");
|
||||
// get a random server from the pool
|
||||
WiFi.hostByName(ntpServerName, ntpServerIP);
|
||||
Serial.print(ntpServerName);
|
||||
Serial.print(": ");
|
||||
Serial.println(ntpServerIP);
|
||||
sendNTPpacket(ntpServerIP);
|
||||
uint32_t beginWait = millis();
|
||||
while (millis() - beginWait < 1500) {
|
||||
int size = Udp.parsePacket();
|
||||
if (size >= NTP_PACKET_SIZE) {
|
||||
Serial.println("Receive NTP Response");
|
||||
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
|
||||
unsigned long secsSince1900;
|
||||
// convert four bytes starting at location 40 to a long integer
|
||||
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
|
||||
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
|
||||
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
|
||||
secsSince1900 |= (unsigned long)packetBuffer[43];
|
||||
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
|
||||
}
|
||||
}
|
||||
Serial.println("No NTP Response :-(");
|
||||
return 0; // return 0 if unable to get the time
|
||||
}
|
||||
|
||||
// send an NTP request to the time server at the given address
|
||||
void sendNTPpacket(IPAddress &address)
|
||||
{
|
||||
// set all bytes in the buffer to 0
|
||||
memset(packetBuffer, 0, NTP_PACKET_SIZE);
|
||||
// Initialize values needed to form NTP request
|
||||
// (see URL above for details on the packets)
|
||||
packetBuffer[0] = 0b11100011; // LI, Version, Mode
|
||||
packetBuffer[1] = 0; // Stratum, or type of clock
|
||||
packetBuffer[2] = 6; // Polling Interval
|
||||
packetBuffer[3] = 0xEC; // Peer Clock Precision
|
||||
// 8 bytes of zero for Root Delay & Root Dispersion
|
||||
packetBuffer[12] = 49;
|
||||
packetBuffer[13] = 0x4E;
|
||||
packetBuffer[14] = 49;
|
||||
packetBuffer[15] = 52;
|
||||
// all NTP fields have been given values, now
|
||||
// you can send a packet requesting a timestamp:
|
||||
Udp.beginPacket(address, 123); //NTP requests are to port 123
|
||||
Udp.write(packetBuffer, NTP_PACKET_SIZE);
|
||||
Udp.endPacket();
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* TimeRTC.pde
|
||||
* example code illustrating Time library with Real Time Clock.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <TimeLib.h>
|
||||
#include <Wire.h>
|
||||
#include <DS1307RTC.h> // a basic DS1307 library that returns time as a time_t
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) ; // wait until Arduino Serial Monitor opens
|
||||
setSyncProvider(RTC.get); // the function to get the time from the RTC
|
||||
if(timeStatus()!= timeSet)
|
||||
Serial.println("Unable to sync with the RTC");
|
||||
else
|
||||
Serial.println("RTC has set the system time");
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (timeStatus() == timeSet) {
|
||||
digitalClockDisplay();
|
||||
} else {
|
||||
Serial.println("The time has not been set. Please run the Time");
|
||||
Serial.println("TimeRTCSet example, or DS1307RTC SetTime example.");
|
||||
Serial.println();
|
||||
delay(4000);
|
||||
}
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void digitalClockDisplay(){
|
||||
// digital clock display of the time
|
||||
Serial.print(hour());
|
||||
printDigits(minute());
|
||||
printDigits(second());
|
||||
Serial.print(" ");
|
||||
Serial.print(day());
|
||||
Serial.print(" ");
|
||||
Serial.print(month());
|
||||
Serial.print(" ");
|
||||
Serial.print(year());
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void printDigits(int digits){
|
||||
// utility function for digital clock display: prints preceding colon and leading 0
|
||||
Serial.print(":");
|
||||
if(digits < 10)
|
||||
Serial.print('0');
|
||||
Serial.print(digits);
|
||||
}
|
||||
|
@ -1,109 +0,0 @@
|
||||
/*
|
||||
* TimeRTCLogger.ino
|
||||
* example code illustrating adding and subtracting Time.
|
||||
*
|
||||
* this sketch logs pin state change events
|
||||
* the time of the event and time since the previous event is calculated and sent to the serial port.
|
||||
*/
|
||||
|
||||
#include <TimeLib.h>
|
||||
#include <Wire.h>
|
||||
#include <DS1307RTC.h> // a basic DS1307 library that returns time as a time_t
|
||||
|
||||
const int nbrInputPins = 6; // monitor 6 digital pins
|
||||
const int inputPins[nbrInputPins] = {2,3,4,5,6,7}; // pins to monitor
|
||||
boolean state[nbrInputPins] ; // the state of the monitored pins
|
||||
time_t prevEventTime[nbrInputPins] ; // the time of the previous event
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
setSyncProvider(RTC.get); // the function to sync the time from the RTC
|
||||
for (int i=0; i < nbrInputPins; i++) {
|
||||
pinMode( inputPins[i], INPUT);
|
||||
// uncomment these lines if pull-up resistors are wanted
|
||||
// pinMode( inputPins[i], INPUT_PULLUP);
|
||||
// state[i] = HIGH;
|
||||
}
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
for (int i=0; i < nbrInputPins; i++) {
|
||||
boolean val = digitalRead(inputPins[i]);
|
||||
if (val != state[i]) {
|
||||
time_t duration = 0; // the time since the previous event
|
||||
state[i] = val;
|
||||
time_t timeNow = now();
|
||||
if (prevEventTime[i] > 0) {
|
||||
// if this was not the first state change, calculate the time from the previous change
|
||||
duration = timeNow - prevEventTime[i];
|
||||
}
|
||||
logEvent(inputPins[i], val, timeNow, duration ); // log the event
|
||||
prevEventTime[i] = timeNow; // store the time for this event
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void logEvent( int pin, boolean state, time_t timeNow, time_t duration)
|
||||
{
|
||||
Serial.print("Pin ");
|
||||
Serial.print(pin);
|
||||
if (state == HIGH) {
|
||||
Serial.print(" went High at ");
|
||||
} else {
|
||||
Serial.print(" went Low at ");
|
||||
}
|
||||
showTime(timeNow);
|
||||
if (duration > 0) {
|
||||
// only display duration if greater than 0
|
||||
Serial.print(", Duration was ");
|
||||
showDuration(duration);
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
|
||||
void showTime(time_t t)
|
||||
{
|
||||
// display the given time
|
||||
Serial.print(hour(t));
|
||||
printDigits(minute(t));
|
||||
printDigits(second(t));
|
||||
Serial.print(" ");
|
||||
Serial.print(day(t));
|
||||
Serial.print(" ");
|
||||
Serial.print(month(t));
|
||||
Serial.print(" ");
|
||||
Serial.print(year(t));
|
||||
}
|
||||
|
||||
void printDigits(int digits){
|
||||
// utility function for digital clock display: prints preceding colon and leading 0
|
||||
Serial.print(":");
|
||||
if(digits < 10)
|
||||
Serial.print('0');
|
||||
Serial.print(digits);
|
||||
}
|
||||
|
||||
void showDuration(time_t duration)
|
||||
{
|
||||
// prints the duration in days, hours, minutes and seconds
|
||||
if (duration >= SECS_PER_DAY) {
|
||||
Serial.print(duration / SECS_PER_DAY);
|
||||
Serial.print(" day(s) ");
|
||||
duration = duration % SECS_PER_DAY;
|
||||
}
|
||||
if (duration >= SECS_PER_HOUR) {
|
||||
Serial.print(duration / SECS_PER_HOUR);
|
||||
Serial.print(" hour(s) ");
|
||||
duration = duration % SECS_PER_HOUR;
|
||||
}
|
||||
if (duration >= SECS_PER_MIN) {
|
||||
Serial.print(duration / SECS_PER_MIN);
|
||||
Serial.print(" minute(s) ");
|
||||
duration = duration % SECS_PER_MIN;
|
||||
}
|
||||
Serial.print(duration);
|
||||
Serial.print(" second(s) ");
|
||||
}
|
||||
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* TimeRTCSet.pde
|
||||
* example code illustrating Time library with Real Time Clock.
|
||||
*
|
||||
* RTC clock is set in response to serial port time message
|
||||
* A Processing example sketch to set the time is included in the download
|
||||
* On Linux, you can use "date +T%s > /dev/ttyACM0" (UTC time zone)
|
||||
*/
|
||||
|
||||
#include <TimeLib.h>
|
||||
#include <Wire.h>
|
||||
#include <DS1307RTC.h> // a basic DS1307 library that returns time as a time_t
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) ; // Needed for Leonardo only
|
||||
setSyncProvider(RTC.get); // the function to get the time from the RTC
|
||||
if (timeStatus() != timeSet)
|
||||
Serial.println("Unable to sync with the RTC");
|
||||
else
|
||||
Serial.println("RTC has set the system time");
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (Serial.available()) {
|
||||
time_t t = processSyncMessage();
|
||||
if (t != 0) {
|
||||
RTC.set(t); // set the RTC and the system time to the received value
|
||||
setTime(t);
|
||||
}
|
||||
}
|
||||
digitalClockDisplay();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void digitalClockDisplay(){
|
||||
// digital clock display of the time
|
||||
Serial.print(hour());
|
||||
printDigits(minute());
|
||||
printDigits(second());
|
||||
Serial.print(" ");
|
||||
Serial.print(day());
|
||||
Serial.print(" ");
|
||||
Serial.print(month());
|
||||
Serial.print(" ");
|
||||
Serial.print(year());
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void printDigits(int digits){
|
||||
// utility function for digital clock display: prints preceding colon and leading 0
|
||||
Serial.print(":");
|
||||
if(digits < 10)
|
||||
Serial.print('0');
|
||||
Serial.print(digits);
|
||||
}
|
||||
|
||||
/* code to process time sync messages from the serial port */
|
||||
#define TIME_HEADER "T" // Header tag for serial time sync message
|
||||
|
||||
unsigned long processSyncMessage() {
|
||||
unsigned long pctime = 0L;
|
||||
const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013
|
||||
|
||||
if(Serial.find(TIME_HEADER)) {
|
||||
pctime = Serial.parseInt();
|
||||
return pctime;
|
||||
if( pctime < DEFAULT_TIME) { // check the value is a valid time (greater than Jan 1 2013)
|
||||
pctime = 0L; // return 0 to indicate that the time is not valid
|
||||
}
|
||||
}
|
||||
return pctime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,81 +0,0 @@
|
||||
/*
|
||||
* TimeSerial.pde
|
||||
* example code illustrating Time library set through serial port messages.
|
||||
*
|
||||
* Messages consist of the letter T followed by ten digit time (as seconds since Jan 1 1970)
|
||||
* you can send the text on the next line using Serial Monitor to set the clock to noon Jan 1 2013
|
||||
T1357041600
|
||||
*
|
||||
* A Processing example sketch to automatically send the messages is included in the download
|
||||
* On Linux, you can use "date +T%s\n > /dev/ttyACM0" (UTC time zone)
|
||||
*/
|
||||
|
||||
#include <TimeLib.h>
|
||||
|
||||
#define TIME_HEADER "T" // Header tag for serial time sync message
|
||||
#define TIME_REQUEST 7 // ASCII bell character requests a time sync message
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) ; // Needed for Leonardo only
|
||||
pinMode(13, OUTPUT);
|
||||
setSyncProvider( requestSync); //set function to call when sync required
|
||||
Serial.println("Waiting for sync message");
|
||||
}
|
||||
|
||||
void loop(){
|
||||
if (Serial.available()) {
|
||||
processSyncMessage();
|
||||
}
|
||||
if (timeStatus()!= timeNotSet) {
|
||||
digitalClockDisplay();
|
||||
}
|
||||
if (timeStatus() == timeSet) {
|
||||
digitalWrite(13, HIGH); // LED on if synced
|
||||
} else {
|
||||
digitalWrite(13, LOW); // LED off if needs refresh
|
||||
}
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void digitalClockDisplay(){
|
||||
// digital clock display of the time
|
||||
Serial.print(hour());
|
||||
printDigits(minute());
|
||||
printDigits(second());
|
||||
Serial.print(" ");
|
||||
Serial.print(day());
|
||||
Serial.print(" ");
|
||||
Serial.print(month());
|
||||
Serial.print(" ");
|
||||
Serial.print(year());
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void printDigits(int digits){
|
||||
// utility function for digital clock display: prints preceding colon and leading 0
|
||||
Serial.print(":");
|
||||
if(digits < 10)
|
||||
Serial.print('0');
|
||||
Serial.print(digits);
|
||||
}
|
||||
|
||||
|
||||
void processSyncMessage() {
|
||||
unsigned long pctime;
|
||||
const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013
|
||||
|
||||
if(Serial.find(TIME_HEADER)) {
|
||||
pctime = Serial.parseInt();
|
||||
if( pctime >= DEFAULT_TIME) { // check the integer is a valid time (greater than Jan 1 2013)
|
||||
setTime(pctime); // Sync Arduino clock to the time received on the serial port
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time_t requestSync()
|
||||
{
|
||||
Serial.write(TIME_REQUEST);
|
||||
return 0; // the time will be sent later in response to serial mesg
|
||||
}
|
||||
|
@ -1,108 +0,0 @@
|
||||
/*
|
||||
* TimeSerialDateStrings.pde
|
||||
* example code illustrating Time library date strings
|
||||
*
|
||||
* This sketch adds date string functionality to TimeSerial sketch
|
||||
* Also shows how to handle different messages
|
||||
*
|
||||
* A message starting with a time header sets the time
|
||||
* A Processing example sketch to automatically send the messages is inclided in the download
|
||||
* On Linux, you can use "date +T%s\n > /dev/ttyACM0" (UTC time zone)
|
||||
*
|
||||
* A message starting with a format header sets the date format
|
||||
|
||||
* send: Fs\n for short date format
|
||||
* send: Fl\n for long date format
|
||||
*/
|
||||
|
||||
#include <TimeLib.h>
|
||||
|
||||
// single character message tags
|
||||
#define TIME_HEADER 'T' // Header tag for serial time sync message
|
||||
#define FORMAT_HEADER 'F' // Header tag indicating a date format message
|
||||
#define FORMAT_SHORT 's' // short month and day strings
|
||||
#define FORMAT_LONG 'l' // (lower case l) long month and day strings
|
||||
|
||||
#define TIME_REQUEST 7 // ASCII bell character requests a time sync message
|
||||
|
||||
static boolean isLongFormat = true;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) ; // Needed for Leonardo only
|
||||
setSyncProvider( requestSync); //set function to call when sync required
|
||||
Serial.println("Waiting for sync message");
|
||||
}
|
||||
|
||||
void loop(){
|
||||
if (Serial.available() > 1) { // wait for at least two characters
|
||||
char c = Serial.read();
|
||||
if( c == TIME_HEADER) {
|
||||
processSyncMessage();
|
||||
}
|
||||
else if( c== FORMAT_HEADER) {
|
||||
processFormatMessage();
|
||||
}
|
||||
}
|
||||
if (timeStatus()!= timeNotSet) {
|
||||
digitalClockDisplay();
|
||||
}
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void digitalClockDisplay() {
|
||||
// digital clock display of the time
|
||||
Serial.print(hour());
|
||||
printDigits(minute());
|
||||
printDigits(second());
|
||||
Serial.print(" ");
|
||||
if(isLongFormat)
|
||||
Serial.print(dayStr(weekday()));
|
||||
else
|
||||
Serial.print(dayShortStr(weekday()));
|
||||
Serial.print(" ");
|
||||
Serial.print(day());
|
||||
Serial.print(" ");
|
||||
if(isLongFormat)
|
||||
Serial.print(monthStr(month()));
|
||||
else
|
||||
Serial.print(monthShortStr(month()));
|
||||
Serial.print(" ");
|
||||
Serial.print(year());
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void printDigits(int digits) {
|
||||
// utility function for digital clock display: prints preceding colon and leading 0
|
||||
Serial.print(":");
|
||||
if(digits < 10)
|
||||
Serial.print('0');
|
||||
Serial.print(digits);
|
||||
}
|
||||
|
||||
void processFormatMessage() {
|
||||
char c = Serial.read();
|
||||
if( c == FORMAT_LONG){
|
||||
isLongFormat = true;
|
||||
Serial.println(F("Setting long format"));
|
||||
}
|
||||
else if( c == FORMAT_SHORT) {
|
||||
isLongFormat = false;
|
||||
Serial.println(F("Setting short format"));
|
||||
}
|
||||
}
|
||||
|
||||
void processSyncMessage() {
|
||||
unsigned long pctime;
|
||||
const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013 - paul, perhaps we define in time.h?
|
||||
|
||||
pctime = Serial.parseInt();
|
||||
if( pctime >= DEFAULT_TIME) { // check the integer is a valid time (greater than Jan 1 2013)
|
||||
setTime(pctime); // Sync Arduino clock to the time received on the serial port
|
||||
}
|
||||
}
|
||||
|
||||
time_t requestSync() {
|
||||
Serial.write(TIME_REQUEST);
|
||||
return 0; // the time will be sent later in response to serial mesg
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
/*
|
||||
* TimeRTC.pde
|
||||
* example code illustrating Time library with Real Time Clock.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <TimeLib.h>
|
||||
|
||||
void setup() {
|
||||
// set the Time library to use Teensy 3.0's RTC to keep time
|
||||
setSyncProvider(getTeensy3Time);
|
||||
|
||||
Serial.begin(115200);
|
||||
while (!Serial); // Wait for Arduino Serial Monitor to open
|
||||
delay(100);
|
||||
if (timeStatus()!= timeSet) {
|
||||
Serial.println("Unable to sync with the RTC");
|
||||
} else {
|
||||
Serial.println("RTC has set the system time");
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (Serial.available()) {
|
||||
time_t t = processSyncMessage();
|
||||
if (t != 0) {
|
||||
Teensy3Clock.set(t); // set the RTC
|
||||
setTime(t);
|
||||
}
|
||||
}
|
||||
digitalClockDisplay();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void digitalClockDisplay() {
|
||||
// digital clock display of the time
|
||||
Serial.print(hour());
|
||||
printDigits(minute());
|
||||
printDigits(second());
|
||||
Serial.print(" ");
|
||||
Serial.print(day());
|
||||
Serial.print(" ");
|
||||
Serial.print(month());
|
||||
Serial.print(" ");
|
||||
Serial.print(year());
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
time_t getTeensy3Time()
|
||||
{
|
||||
return Teensy3Clock.get();
|
||||
}
|
||||
|
||||
/* code to process time sync messages from the serial port */
|
||||
#define TIME_HEADER "T" // Header tag for serial time sync message
|
||||
|
||||
unsigned long processSyncMessage() {
|
||||
unsigned long pctime = 0L;
|
||||
const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013
|
||||
|
||||
if(Serial.find(TIME_HEADER)) {
|
||||
pctime = Serial.parseInt();
|
||||
return pctime;
|
||||
if( pctime < DEFAULT_TIME) { // check the value is a valid time (greater than Jan 1 2013)
|
||||
pctime = 0L; // return 0 to indicate that the time is not valid
|
||||
}
|
||||
}
|
||||
return pctime;
|
||||
}
|
||||
|
||||
void printDigits(int digits){
|
||||
// utility function for digital clock display: prints preceding colon and leading 0
|
||||
Serial.print(":");
|
||||
if(digits < 10)
|
||||
Serial.print('0');
|
||||
Serial.print(digits);
|
||||
}
|
||||
|
@ -1,35 +0,0 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For Time
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
time_t KEYWORD1
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
now KEYWORD2
|
||||
millisecond KEYWORD2
|
||||
second KEYWORD2
|
||||
minute KEYWORD2
|
||||
hour KEYWORD2
|
||||
day KEYWORD2
|
||||
month KEYWORD2
|
||||
year KEYWORD2
|
||||
isAM KEYWORD2
|
||||
isPM KEYWORD2
|
||||
weekday KEYWORD2
|
||||
setTime KEYWORD2
|
||||
adjustTime KEYWORD2
|
||||
setSyncProvider KEYWORD2
|
||||
setSyncInterval KEYWORD2
|
||||
timeStatus KEYWORD2
|
||||
TimeLib KEYWORD2
|
||||
#######################################
|
||||
# Instances (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
@ -1,26 +0,0 @@
|
||||
{
|
||||
"name": "Time",
|
||||
"description": "Time keeping library",
|
||||
"keywords": "Time, date, hour, minute, second, day, week, month, year, RTC",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Margolis"
|
||||
},
|
||||
{
|
||||
"name": "Paul Stoffregen",
|
||||
"email": "paul@pjrc.com",
|
||||
"url": "http://www.pjrc.com",
|
||||
"maintainer": true
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PaulStoffregen/Time"
|
||||
},
|
||||
"version": "1.5",
|
||||
"homepage": "http://playground.arduino.cc/Code/Time",
|
||||
"frameworks": "Arduino",
|
||||
"examples": [
|
||||
"examples/*/*.ino"
|
||||
]
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
name=Time
|
||||
version=1.5
|
||||
author=Michael Margolis
|
||||
maintainer=Paul Stoffregen
|
||||
sentence=Timekeeping functionality for Arduino
|
||||
paragraph=Date and Time functions, with provisions to synchronize to external time sources like GPS and NTP (Internet). This library is often used together with TimeAlarms and DS1307RTC.
|
||||
category=Timing
|
||||
url=http://playground.arduino.cc/code/time
|
||||
architectures=*
|
||||
|
@ -1,185 +0,0 @@
|
||||
/*
|
||||
time.h - low level time and date functions
|
||||
*/
|
||||
|
||||
/*
|
||||
July 3 2011 - fixed elapsedSecsThisWeek macro (thanks Vincent Valdy for this)
|
||||
- fixed daysToTime_t macro (thanks maniacbug)
|
||||
*/
|
||||
|
||||
#ifndef _Time_h
|
||||
#ifdef __cplusplus
|
||||
#define _Time_h
|
||||
|
||||
#include <inttypes.h>
|
||||
#ifndef __AVR__
|
||||
#include <sys/types.h> // for __time_t_defined, but avr libc lacks sys/types.h
|
||||
#endif
|
||||
|
||||
#if !defined(__time_t_defined) // avoid conflict with newlib or other posix libc
|
||||
typedef unsigned long time_t;
|
||||
#endif
|
||||
|
||||
#define usePPS
|
||||
#define TIMELIB_ENABLE_MILLIS
|
||||
|
||||
// This ugly hack allows us to define C++ overloaded functions, when included
|
||||
// from within an extern "C", as newlib's sys/stat.h does. Actually it is
|
||||
// intended to include "time.h" from the C library (on ARM, but AVR does not
|
||||
// have that file at all). On Mac and Windows, the compiler will find this
|
||||
// "Time.h" instead of the C library "time.h", so we may cause other weird
|
||||
// and unpredictable effects by conflicting with the C library header "time.h",
|
||||
// but at least this hack lets us define C++ functions as intended. Hopefully
|
||||
// nothing too terrible will result from overriding the C library header?!
|
||||
extern "C++" {
|
||||
typedef enum { timeNotSet, timeNeedsSync, timeSet } timeStatus_t;
|
||||
|
||||
typedef enum {
|
||||
dowInvalid,
|
||||
dowSunday,
|
||||
dowMonday,
|
||||
dowTuesday,
|
||||
dowWednesday,
|
||||
dowThursday,
|
||||
dowFriday,
|
||||
dowSaturday
|
||||
} timeDayOfWeek_t;
|
||||
|
||||
typedef enum {
|
||||
tmSecond,
|
||||
tmMinute,
|
||||
tmHour,
|
||||
tmWday,
|
||||
tmDay,
|
||||
tmMonth,
|
||||
tmYear,
|
||||
tmNbrFields
|
||||
} tmByteFields;
|
||||
|
||||
typedef struct {
|
||||
uint8_t Second;
|
||||
uint8_t Minute;
|
||||
uint8_t Hour;
|
||||
uint8_t Wday; // day of week, sunday is day 1
|
||||
uint8_t Day;
|
||||
uint8_t Month;
|
||||
uint8_t Year; // offset from 1970;
|
||||
} tmElements_t, TimeElements, *tmElementsPtr_t;
|
||||
|
||||
// convenience macros to convert to and from tm years
|
||||
#define tmYearToCalendar(Y) ((Y) + 1970) // full four digit year
|
||||
#define CalendarYrToTm(Y) ((Y)-1970)
|
||||
#define tmYearToY2k(Y) ((Y)-30) // offset is from 2000
|
||||
#define y2kYearToTm(Y) ((Y) + 30)
|
||||
|
||||
typedef time_t (*getExternalTime)();
|
||||
// typedef void (*setExternalTime)(const time_t); // not used in this version
|
||||
|
||||
/*==============================================================================*/
|
||||
/* Useful Constants */
|
||||
#define SECS_PER_MIN ((time_t)(60UL))
|
||||
#define SECS_PER_HOUR ((time_t)(3600UL))
|
||||
#define SECS_PER_DAY ((time_t)(SECS_PER_HOUR * 24UL))
|
||||
#define DAYS_PER_WEEK ((time_t)(7UL))
|
||||
#define SECS_PER_WEEK ((time_t)(SECS_PER_DAY * DAYS_PER_WEEK))
|
||||
#define SECS_PER_YEAR \
|
||||
((time_t)(SECS_PER_DAY * 365UL)) // TODO: ought to handle leap years
|
||||
#define SECS_YR_2000 ((time_t)(946684800UL)) // the time at the start of y2k
|
||||
|
||||
/* Useful Macros for getting elapsed time */
|
||||
#define numberOfSeconds(_time_) ((_time_) % SECS_PER_MIN)
|
||||
#define numberOfMinutes(_time_) (((_time_) / SECS_PER_MIN) % SECS_PER_MIN)
|
||||
#define numberOfHours(_time_) (((_time_) % SECS_PER_DAY) / SECS_PER_HOUR)
|
||||
#define dayOfWeek(_time_) \
|
||||
((((_time_) / SECS_PER_DAY + 4) % DAYS_PER_WEEK) + 1) // 1 = Sunday
|
||||
#define elapsedDays(_time_) \
|
||||
((_time_) / SECS_PER_DAY) // this is number of days since Jan 1 1970
|
||||
#define elapsedSecsToday(_time_) \
|
||||
((_time_) % SECS_PER_DAY) // the number of seconds since last midnight
|
||||
// The following macros are used in calculating alarms and assume the clock is
|
||||
// set to a date later than Jan 1 1971 Always set the correct time before
|
||||
// settting alarms
|
||||
#define previousMidnight(_time_) \
|
||||
(((_time_) / SECS_PER_DAY) * \
|
||||
SECS_PER_DAY) // time at the start of the given day
|
||||
#define nextMidnight(_time_) \
|
||||
(previousMidnight(_time_) + SECS_PER_DAY) // time at the end of the given day
|
||||
#define elapsedSecsThisWeek(_time_) \
|
||||
(elapsedSecsToday(_time_) + \
|
||||
((dayOfWeek(_time_) - 1) * SECS_PER_DAY)) // note that week starts on day 1
|
||||
#define previousSunday(_time_) \
|
||||
((_time_)-elapsedSecsThisWeek( \
|
||||
_time_)) // time at the start of the week for the given time
|
||||
#define nextSunday(_time_) \
|
||||
(previousSunday(_time_) + \
|
||||
SECS_PER_WEEK) // time at the end of the week for the given time
|
||||
|
||||
/* Useful Macros for converting elapsed time to a time_t */
|
||||
#define minutesToTime_t ((M))((M)*SECS_PER_MIN)
|
||||
#define hoursToTime_t ((H))((H)*SECS_PER_HOUR)
|
||||
#define daysToTime_t ((D))((D)*SECS_PER_DAY) // fixed on Jul 22 2011
|
||||
#define weeksToTime_t ((W))((W)*SECS_PER_WEEK)
|
||||
|
||||
/*============================================================================*/
|
||||
/* time and date functions */
|
||||
int hour(); // the hour now
|
||||
int hour(time_t t); // the hour for the given time
|
||||
int hourFormat12(); // the hour now in 12 hour format
|
||||
int hourFormat12(time_t t); // the hour for the given time in 12 hour format
|
||||
uint8_t isAM(); // returns true if time now is AM
|
||||
uint8_t isAM(time_t t); // returns true the given time is AM
|
||||
uint8_t isPM(); // returns true if time now is PM
|
||||
uint8_t isPM(time_t t); // returns true the given time is PM
|
||||
int minute(); // the minute now
|
||||
int minute(time_t t); // the minute for the given time
|
||||
int second(); // the second now
|
||||
int second(time_t t); // the second for the given time
|
||||
#ifdef TIMELIB_ENABLE_MILLIS
|
||||
int millisecond(); // the millisecond now
|
||||
int microsecond();
|
||||
#endif
|
||||
int day(); // the day now
|
||||
int day(time_t t); // the day for the given time
|
||||
int weekday(); // the weekday now (Sunday is day 1)
|
||||
int weekday(time_t t); // the weekday for the given time
|
||||
int month(); // the month now (Jan is month 1)
|
||||
int month(time_t t); // the month for the given time
|
||||
int year(); // the full four digit year: (2009, 2010 etc)
|
||||
int year(time_t t); // the year for the given time
|
||||
|
||||
time_t now(); // return the current time as seconds since Jan 1 1970
|
||||
#ifdef TIMELIB_ENABLE_MILLIS
|
||||
time_t now(uint32_t &sysTimeMicros); // return the current time as seconds and
|
||||
// microseconds since Jan 1 1970
|
||||
|
||||
#endif
|
||||
#ifdef usePPS
|
||||
void IRAM_ATTR SyncToPPS();
|
||||
#endif
|
||||
void setTime(time_t t);
|
||||
void setTime(int hr, int min, int sec, int day, int month, int yr);
|
||||
void adjustTime(long adjustment);
|
||||
|
||||
/* date strings */
|
||||
#define dt_MAX_STRING_LEN \
|
||||
9 // length of longest date string (excluding terminating null)
|
||||
char *monthStr(uint8_t month);
|
||||
char *dayStr(uint8_t day);
|
||||
char *monthShortStr(uint8_t month);
|
||||
char *dayShortStr(uint8_t day);
|
||||
|
||||
/* time sync functions */
|
||||
timeStatus_t
|
||||
timeStatus(); // indicates if time has been set and recently synchronized
|
||||
void setSyncProvider(
|
||||
getExternalTime getTimeFunction); // identify the external time provider
|
||||
void setSyncInterval(
|
||||
time_t interval); // set the number of seconds between re-sync
|
||||
|
||||
/* low level functions to convert to and from system time */
|
||||
void breakTime(time_t time, tmElements_t &tm); // break time_t into elements
|
||||
time_t makeTime(const tmElements_t &tm); // convert time elements into time_t
|
||||
|
||||
} // extern "C++"
|
||||
#endif // __cplusplus
|
||||
#endif /* _Time_h */
|
@ -1,353 +0,0 @@
|
||||
/*
|
||||
time.c - low level time and date functions
|
||||
Copyright (c) Michael Margolis 2009-2014
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
1.0 6 Jan 2010 - initial release
|
||||
1.1 12 Feb 2010 - fixed leap year calculation error
|
||||
1.2 1 Nov 2010 - fixed setTime bug (thanks to Korman for this)
|
||||
1.3 24 Mar 2012 - many edits by Paul Stoffregen: fixed timeStatus() to update
|
||||
status, updated examples for Arduino 1.0, fixed ARM
|
||||
compatibility issues, added TimeArduinoDue and TimeTeensy3
|
||||
examples, add error checking and messages to RTC examples,
|
||||
add examples to DS1307RTC library.
|
||||
1.4 5 Sep 2014 - compatibility with Arduino 1.5.7
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#define TIMELIB_ENABLE_MILLIS
|
||||
#define usePPS
|
||||
|
||||
#include "TimeLib.h"
|
||||
|
||||
// Convert days since epoch to week day. Sunday is day 1.
|
||||
#define DAYS_TO_WDAY(x) (((x) + 4) % 7) + 1
|
||||
|
||||
static tmElements_t cacheElements; // a cache of time elements
|
||||
static time_t cacheTime; // the time the cache was updated
|
||||
static uint32_t syncInterval =
|
||||
300; // time sync will be attempted after this many seconds
|
||||
|
||||
void refreshCache(time_t t) {
|
||||
if (t != cacheTime) {
|
||||
breakTime(t, cacheElements);
|
||||
cacheTime = t;
|
||||
}
|
||||
}
|
||||
|
||||
int hour() { // the hour now
|
||||
return hour(now());
|
||||
}
|
||||
|
||||
int hour(time_t t) { // the hour for the given time
|
||||
refreshCache(t);
|
||||
return cacheElements.Hour;
|
||||
}
|
||||
|
||||
int hourFormat12() { // the hour now in 12 hour format
|
||||
return hourFormat12(now());
|
||||
}
|
||||
|
||||
int hourFormat12(time_t t) { // the hour for the given time in 12 hour format
|
||||
refreshCache(t);
|
||||
if (cacheElements.Hour == 0)
|
||||
return 12; // 12 midnight
|
||||
else if (cacheElements.Hour > 12)
|
||||
return cacheElements.Hour - 12;
|
||||
else
|
||||
return cacheElements.Hour;
|
||||
}
|
||||
|
||||
uint8_t isAM() { // returns true if time now is AM
|
||||
return !isPM(now());
|
||||
}
|
||||
|
||||
uint8_t isAM(time_t t) { // returns true if given time is AM
|
||||
return !isPM(t);
|
||||
}
|
||||
|
||||
uint8_t isPM() { // returns true if PM
|
||||
return isPM(now());
|
||||
}
|
||||
|
||||
uint8_t isPM(time_t t) { // returns true if PM
|
||||
return (hour(t) >= 12);
|
||||
}
|
||||
|
||||
int minute() { return minute(now()); }
|
||||
|
||||
int minute(time_t t) { // the minute for the given time
|
||||
refreshCache(t);
|
||||
return cacheElements.Minute;
|
||||
}
|
||||
|
||||
int second() { return second(now()); }
|
||||
|
||||
int second(time_t t) { // the second for the given time
|
||||
refreshCache(t);
|
||||
return cacheElements.Second;
|
||||
}
|
||||
|
||||
int millisecond() {
|
||||
uint32_t ms;
|
||||
now(ms);
|
||||
ms = ms / 1000;
|
||||
return (int)ms;
|
||||
}
|
||||
|
||||
int microsecond() {
|
||||
uint32_t us;
|
||||
now(us);
|
||||
return (int)us;
|
||||
}
|
||||
|
||||
int day() { return (day(now())); }
|
||||
|
||||
int day(time_t t) { // the day for the given time (0-6)
|
||||
refreshCache(t);
|
||||
return cacheElements.Day;
|
||||
}
|
||||
|
||||
int weekday() { // Sunday is day 1
|
||||
return weekday(now());
|
||||
}
|
||||
|
||||
int weekday(time_t t) {
|
||||
refreshCache(t);
|
||||
return cacheElements.Wday;
|
||||
}
|
||||
|
||||
int month() { return month(now()); }
|
||||
|
||||
int month(time_t t) { // the month for the given time
|
||||
refreshCache(t);
|
||||
return cacheElements.Month;
|
||||
}
|
||||
|
||||
int year() { // as in Processing, the full four digit year: (2009, 2010 etc)
|
||||
return year(now());
|
||||
}
|
||||
|
||||
int year(time_t t) { // the year for the given time
|
||||
refreshCache(t);
|
||||
return tmYearToCalendar(cacheElements.Year);
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* functions to convert to and from system time */
|
||||
/* These are for interfacing with time serivces and are not normally needed in a
|
||||
* sketch */
|
||||
|
||||
// leap year calulator expects year argument as years offset from 1970
|
||||
#define LEAP_YEAR(Y) \
|
||||
(((1970 + (Y)) > 0) && !((1970 + (Y)) % 4) && \
|
||||
(((1970 + (Y)) % 100) || !((1970 + (Y)) % 400)))
|
||||
#define daysInYear(year) ((time_t)(LEAP_YEAR(year) ? 366 : 365))
|
||||
|
||||
static const uint8_t monthDays[] = {
|
||||
31, 28, 31, 30, 31, 30, 31,
|
||||
31, 30, 31, 30, 31}; // API starts months from 1, this array starts from 0
|
||||
|
||||
void breakTime(time_t time, tmElements_t &tm) {
|
||||
// break the given time_t into time components
|
||||
// this is a more compact version of the C library localtime function
|
||||
// note that year is offset from 1970 !!!
|
||||
|
||||
uint8_t period;
|
||||
time_t length;
|
||||
|
||||
tm.Second = time % 60;
|
||||
time /= 60; // now it is minutes
|
||||
tm.Minute = time % 60;
|
||||
time /= 60; // now it is hours
|
||||
tm.Hour = time % 24;
|
||||
time /= 24; // now it is days since 1 Jan 1970
|
||||
|
||||
// if the number of days since epoch matches cacheTime, then can take date
|
||||
// elements from cacheElements and avoid expensive calculation.
|
||||
if (time == (cacheTime / SECS_PER_DAY)) {
|
||||
if (&tm != &cacheElements) { // check whether tm is actually cacheElements
|
||||
tm.Wday = cacheElements.Wday;
|
||||
tm.Day = cacheElements.Day;
|
||||
tm.Month = cacheElements.Month;
|
||||
tm.Year = cacheElements.Year;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
tm.Wday = DAYS_TO_WDAY(time);
|
||||
|
||||
period = 0;
|
||||
while (time >= (length = daysInYear(period))) {
|
||||
time -= length;
|
||||
period++;
|
||||
}
|
||||
tm.Year = period; // year is offset from 1970
|
||||
// time is now days since 1 Jan of the year
|
||||
|
||||
bool leap_year = LEAP_YEAR(period);
|
||||
period = 0;
|
||||
while (period < 12 &&
|
||||
time >= (length = monthDays[period] + (leap_year && period == 1))) {
|
||||
time -= length;
|
||||
period++;
|
||||
}
|
||||
tm.Month = period + 1; // jan is month 1
|
||||
// time is now days since the 1st day of the month
|
||||
|
||||
tm.Day = time + 1; // day of month
|
||||
}
|
||||
|
||||
time_t makeTime(const tmElements_t &tm) {
|
||||
// assemble time elements into time_t
|
||||
// note year argument is offset from 1970 (see macros in time.h to convert to
|
||||
// other formats) previous version used full four digit year (or digits since
|
||||
// 2000),i.e. 2009 was 2009 or 9
|
||||
|
||||
int i;
|
||||
uint32_t seconds;
|
||||
|
||||
// seconds from 1970 till 1 jan 00:00:00 of the given year
|
||||
seconds = SECS_PER_DAY * (365 * tm.Year);
|
||||
for (i = 0; i < tm.Year; i++) {
|
||||
if (LEAP_YEAR(i)) {
|
||||
seconds += SECS_PER_DAY; // add extra days for leap years
|
||||
}
|
||||
}
|
||||
|
||||
// add days for this year, months start from 1
|
||||
for (i = 1; i < tm.Month; i++) {
|
||||
if ((i == 2) && LEAP_YEAR(tm.Year)) {
|
||||
seconds += SECS_PER_DAY * 29;
|
||||
} else {
|
||||
seconds += SECS_PER_DAY * monthDays[i - 1]; // monthDay array starts from
|
||||
// 0
|
||||
}
|
||||
}
|
||||
seconds += (tm.Day - 1) * SECS_PER_DAY;
|
||||
seconds += tm.Hour * SECS_PER_HOUR;
|
||||
seconds += tm.Minute * SECS_PER_MIN;
|
||||
seconds += tm.Second;
|
||||
return (time_t)seconds;
|
||||
}
|
||||
/*=====================================================*/
|
||||
/* Low level system time functions */
|
||||
|
||||
static time_t sysTime = 0;
|
||||
static uint32_t prevMicros = 0;
|
||||
static time_t nextSyncTime = 0;
|
||||
static timeStatus_t Status = timeNotSet;
|
||||
|
||||
getExternalTime getTimePtr; // pointer to external sync function
|
||||
// setExternalTime setTimePtr; // not used in this version
|
||||
|
||||
#ifdef TIME_DRIFT_INFO // define this to get drift data
|
||||
time_t sysUnsyncedTime = 0; // the time sysTime unadjusted by sync
|
||||
#endif
|
||||
|
||||
#ifdef usePPS
|
||||
void IRAM_ATTR SyncToPPS() {
|
||||
sysTime++;
|
||||
prevMicros = micros();
|
||||
}
|
||||
#endif
|
||||
|
||||
time_t now() {
|
||||
uint32_t sysTimeMicros;
|
||||
return now(sysTimeMicros);
|
||||
}
|
||||
|
||||
time_t now(uint32_t &sysTimeMicros) {
|
||||
// calculate number of seconds passed since last call to now()
|
||||
while ((sysTimeMicros = micros() - prevMicros) >= 1000000) {
|
||||
// micros() and prevMicros are both unsigned ints thus the subtraction will
|
||||
// always result in a positive difference. This is OK since it corrects for
|
||||
// wrap-around and micros() is monotonic.
|
||||
sysTime++;
|
||||
prevMicros += 1000000;
|
||||
#ifdef TIME_DRIFT_INFO
|
||||
sysUnsyncedTime++; // this can be compared to the synced time to measure
|
||||
// long term drift
|
||||
#endif
|
||||
}
|
||||
if (nextSyncTime <= sysTime) {
|
||||
if (getTimePtr != 0) {
|
||||
time_t t = getTimePtr();
|
||||
|
||||
if (t != 0) {
|
||||
setTime(t);
|
||||
} else {
|
||||
nextSyncTime = sysTime + syncInterval;
|
||||
Status = (Status == timeNotSet) ? timeNotSet : timeNeedsSync;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sysTime;
|
||||
}
|
||||
|
||||
void setTime(time_t t) {
|
||||
#ifdef TIME_DRIFT_INFO
|
||||
if (sysUnsyncedTime == 0)
|
||||
sysUnsyncedTime = t; // store the time of the first call to set a valid Time
|
||||
#endif
|
||||
|
||||
sysTime = t;
|
||||
nextSyncTime = t + (time_t)syncInterval;
|
||||
Status = timeSet;
|
||||
#ifndef usePPS
|
||||
prevMicros =
|
||||
micros(); // restart counting from now (thanks to Korman for this fix)
|
||||
#endif
|
||||
}
|
||||
|
||||
void setTime(int hr, int min, int sec, int dy, int mnth, int yr) {
|
||||
// year can be given as full four digit year or two digts (2010 or 10 for
|
||||
// 2010); it is converted to years since 1970
|
||||
if (yr > 99)
|
||||
yr = CalendarYrToTm(yr);
|
||||
else
|
||||
yr = tmYearToY2k(yr);
|
||||
cacheElements.Year = yr;
|
||||
cacheElements.Month = mnth;
|
||||
cacheElements.Day = dy;
|
||||
cacheElements.Hour = hr;
|
||||
cacheElements.Minute = min;
|
||||
cacheElements.Second = sec;
|
||||
cacheTime = makeTime(cacheElements);
|
||||
cacheElements.Wday = DAYS_TO_WDAY(cacheTime / SECS_PER_DAY);
|
||||
setTime(cacheTime);
|
||||
}
|
||||
|
||||
void adjustTime(long adjustment) { sysTime += adjustment; }
|
||||
|
||||
// indicates if time has been set and recently synchronized
|
||||
timeStatus_t timeStatus() {
|
||||
now(); // required to actually update the status
|
||||
return Status;
|
||||
}
|
||||
|
||||
void setSyncProvider(getExternalTime getTimeFunction) {
|
||||
getTimePtr = getTimeFunction;
|
||||
nextSyncTime = sysTime;
|
||||
now(); // this will sync the clock
|
||||
}
|
||||
|
||||
void setSyncInterval(
|
||||
time_t interval) { // set the number of seconds between re-sync
|
||||
syncInterval = (uint32_t)interval;
|
||||
nextSyncTime = sysTime + syncInterval;
|
||||
}
|
@ -1 +0,0 @@
|
||||
#include "TimeLib.h"
|
@ -1,5 +1,4 @@
|
||||
; PlatformIO Project Configuration File
|
||||
; NOTE: PlatformIO v5 is needed!
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; http://docs.platformio.org/page/projectconf.html
|
||||
@ -41,7 +40,7 @@ halfile = ttgobeam10.h
|
||||
[platformio]
|
||||
; upload firmware to board with usb cable
|
||||
default_envs = usb
|
||||
; upload firmware to a jfrog bintray repository
|
||||
; upload firmware to a paxexpress repository
|
||||
;default_envs = ota
|
||||
; use latest versions of libraries
|
||||
;default_envs = dev
|
||||
@ -49,7 +48,7 @@ description = Paxcounter is a device for metering passenger flows in realtime. I
|
||||
|
||||
[common]
|
||||
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
||||
release_version = 2.4.0
|
||||
release_version = 3.0.0
|
||||
; 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
|
||||
@ -62,12 +61,13 @@ monitor_speed = 115200
|
||||
upload_speed = 115200 ; set by build.py and taken from hal file
|
||||
display_library = ; set by build.py and taken from hal file
|
||||
lib_deps_lora =
|
||||
mcci-catena/MCCI LoRaWAN LMIC library @ ^3.3.0
|
||||
;mcci-catena/MCCI LoRaWAN LMIC library @ ^3.3.0
|
||||
https://github.com/mcci-catena/arduino-lmic.git
|
||||
lib_deps_display =
|
||||
bitbank2/OneBitDisplay @ 1.9.0
|
||||
bitbank2/OneBitDisplay @ ^1.10.0
|
||||
bitbank2/BitBang_I2C @ ^2.1.3
|
||||
ricmoo/QRCode @ ^0.0.1
|
||||
bodmer/TFT_eSPI @ ^2.3.51
|
||||
bodmer/TFT_eSPI @ ^2.3.58
|
||||
lib_deps_ledmatrix =
|
||||
seeed-studio/Ultrathin_LED_Matrix @ ^1.0.0
|
||||
lib_deps_rgbled =
|
||||
@ -77,18 +77,19 @@ lib_deps_gps =
|
||||
lib_deps_sensors =
|
||||
adafruit/Adafruit Unified Sensor @ ^1.1.4
|
||||
adafruit/Adafruit BME280 Library @ ^2.1.1
|
||||
adafruit/Adafruit BMP085 Library @ ^1.1.0
|
||||
adafruit/Adafruit BMP085 Library @ ^1.2.0
|
||||
boschsensortec/BSEC Software Library @ 1.6.1480
|
||||
https://github.com/ricki-z/SDS011.git
|
||||
lib_deps_basic =
|
||||
https://github.com/SukkoPera/Arduino-Rokkit-Hash.git
|
||||
bblanchon/ArduinoJson @ ^6
|
||||
jchristensen/Timezone @ ^1.2.4
|
||||
ropg/ezTime @ ^0.8.3
|
||||
makuna/RTC @ ^2.3.5
|
||||
spacehuhn/SimpleButton
|
||||
lewisxhe/AXP202X_Library @ ^1.1.3
|
||||
geeksville/esp32-micro-sdcard @ ^0.1.1
|
||||
256dpi/MQTT @ ^2.4.7
|
||||
256dpi/MQTT @ ^2.4.8
|
||||
https://github.com/cyberman54/libpax.git
|
||||
lib_deps_all =
|
||||
${common.lib_deps_basic}
|
||||
${common.lib_deps_lora}
|
||||
@ -105,12 +106,11 @@ build_flags_basic =
|
||||
'-DPROGVERSION="${common.release_version}"'
|
||||
'-Wno-unknown-pragmas'
|
||||
'-Wno-unused-variable'
|
||||
build_flags_sensors =
|
||||
-Llib/Bosch-BSEC/src/esp32/
|
||||
-lalgobsec
|
||||
'-D LIBPAX_WIFI'
|
||||
'-D LIBPAX_BLE'
|
||||
'-D LIBPAX_ARDUINO'
|
||||
build_flags_all =
|
||||
${common.build_flags_basic}
|
||||
${common.build_flags_sensors}
|
||||
-mfix-esp32-psram-cache-issue
|
||||
|
||||
[env]
|
||||
@ -119,7 +119,7 @@ framework = arduino
|
||||
board = esp32dev
|
||||
board_build.partitions = min_spiffs.csv
|
||||
upload_speed = ${common.upload_speed}
|
||||
;upload_port = COM3
|
||||
;upload_port = COM12
|
||||
platform = ${common.platform_espressif32}
|
||||
lib_deps = ${common.lib_deps_all}
|
||||
build_flags = ${common.build_flags_all}
|
||||
@ -138,3 +138,6 @@ upload_protocol = esptool
|
||||
upload_protocol = esptool
|
||||
build_type = debug
|
||||
platform = https://github.com/platformio/platform-espressif32.git#develop
|
||||
platform_packages =
|
||||
; use upstream Git version
|
||||
framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git
|
@ -25,7 +25,7 @@
|
||||
"t": "set",
|
||||
"p": "payload.schedule",
|
||||
"pt": "msg",
|
||||
"to": "replace",
|
||||
"to": "first",
|
||||
"tot": "str"
|
||||
},
|
||||
{
|
||||
|
@ -62,11 +62,6 @@ function Decoder(bytes, port) {
|
||||
return decode(bytes, [uint8], ['button']);
|
||||
}
|
||||
|
||||
if (port === 6) {
|
||||
// beacon proximity alarm
|
||||
return decode(bytes, [int8, uint8], ['rssi', 'beacon']);
|
||||
}
|
||||
|
||||
if (port === 7) {
|
||||
// BME680 sensor data
|
||||
return decode(bytes, [float, pressure, ufloat, ufloat], ['temperature', 'pressure', 'humidity', 'air']);
|
||||
@ -248,7 +243,7 @@ var bitmap1 = function (byte) {
|
||||
}
|
||||
var i = bytesToInt(byte);
|
||||
var bm = ('00000000' + Number(i).toString(2)).substr(-8).split('').map(Number).map(Boolean);
|
||||
return ['adr', 'screensaver', 'screen', 'countermode', 'blescan', 'antenna', 'filter', 'alarm']
|
||||
return ['adr', 'screensaver', 'screen', 'countermode', 'blescan', 'antenna', 'reserved', 'reserved']
|
||||
.reduce(function (obj, pos, index) {
|
||||
obj[pos] = +bm[index];
|
||||
return obj;
|
||||
@ -262,7 +257,7 @@ var bitmap2 = function (byte) {
|
||||
}
|
||||
var i = bytesToInt(byte);
|
||||
var bm = ('00000000' + Number(i).toString(2)).substr(-8).split('').map(Number).map(Boolean);
|
||||
return ['battery', 'sensor3', 'sensor2', 'sensor1', 'gps', 'bme', 'alarm', 'counter']
|
||||
return ['battery', 'sensor3', 'sensor2', 'sensor1', 'gps', 'bme', 'reserved', 'counter']
|
||||
.reduce(function (obj, pos, index) {
|
||||
obj[pos] = +bm[index];
|
||||
return obj;
|
||||
|
@ -49,12 +49,6 @@ function Decoder(bytes, port) {
|
||||
decoded.button = bytes[i++];
|
||||
}
|
||||
|
||||
if (port === 6) {
|
||||
var i = 0;
|
||||
decoded.rssi = bytes[i++];
|
||||
decoded.beacon = bytes[i++];
|
||||
}
|
||||
|
||||
if (port === 7) {
|
||||
var i = 0;
|
||||
decoded.temperature = ((bytes[i++] << 8) | bytes[i++]);
|
||||
|
@ -70,11 +70,6 @@ function decodeUplink(input) {
|
||||
data = decode(input.bytes, [uint8], ['button']);
|
||||
}
|
||||
|
||||
if (input.fPort === 6) {
|
||||
// beacon proximity alarm
|
||||
data = decode(input.bytes, [int8, uint8], ['rssi', 'beacon']);
|
||||
}
|
||||
|
||||
if (input.fPort === 7) {
|
||||
// BME680 sensor data
|
||||
data = decode(input.bytes, [float, pressure, ufloat, ufloat], ['temperature', 'pressure', 'humidity', 'air']);
|
||||
@ -263,7 +258,7 @@ var bitmap1 = function (byte) {
|
||||
}
|
||||
var i = bytesToInt(byte);
|
||||
var bm = ('00000000' + Number(i).toString(2)).substr(-8).split('').map(Number).map(Boolean);
|
||||
return ['adr', 'screensaver', 'screen', 'countermode', 'blescan', 'antenna', 'filter', 'alarm']
|
||||
return ['adr', 'screensaver', 'screen', 'countermode', 'blescan', 'antenna', 'reserved', 'reserved']
|
||||
.reduce(function (obj, pos, index) {
|
||||
obj[pos] = +bm[index];
|
||||
return obj;
|
||||
@ -277,7 +272,7 @@ var bitmap2 = function (byte) {
|
||||
}
|
||||
var i = bytesToInt(byte);
|
||||
var bm = ('00000000' + Number(i).toString(2)).substr(-8).split('').map(Number).map(Boolean);
|
||||
return ['battery', 'sensor3', 'sensor2', 'sensor1', 'gps', 'bme', 'alarm', 'counter']
|
||||
return ['battery', 'sensor3', 'sensor2', 'sensor1', 'gps', 'bme', 'reserved', 'counter']
|
||||
.reduce(function (obj, pos, index) {
|
||||
obj[pos] = +bm[index];
|
||||
return obj;
|
||||
|
@ -56,12 +56,6 @@ function decodeUplink(input) {
|
||||
data.button = input.bytes[i++];
|
||||
}
|
||||
|
||||
if (input.fPort === 6) {
|
||||
var i = 0;
|
||||
data.rssi = input.bytes[i++];
|
||||
data.beacon = input.bytes[i++];
|
||||
}
|
||||
|
||||
if (input.fPort === 7) {
|
||||
var i = 0;
|
||||
data.temperature = ((input.bytes[i++] << 8) | input.bytes[i++]);
|
||||
|
302
src/blecsan.cpp
302
src/blecsan.cpp
@ -1,302 +0,0 @@
|
||||
// some code snippets taken from
|
||||
// https://github.com/nkolban/esp32-snippets/tree/master/BLE/scanner
|
||||
|
||||
#include "blescan.h"
|
||||
|
||||
#define BT_BD_ADDR_HEX(addr) \
|
||||
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]
|
||||
|
||||
// local Tag for logging
|
||||
static const char TAG[] = "bluetooth";
|
||||
|
||||
#ifdef VERBOSE
|
||||
const char *bt_addr_t_to_string(esp_ble_addr_type_t type) {
|
||||
switch (type) {
|
||||
case BLE_ADDR_TYPE_PUBLIC:
|
||||
return "BLE_ADDR_TYPE_PUBLIC";
|
||||
case BLE_ADDR_TYPE_RANDOM:
|
||||
return "BLE_ADDR_TYPE_RANDOM";
|
||||
case BLE_ADDR_TYPE_RPA_PUBLIC:
|
||||
return "BLE_ADDR_TYPE_RPA_PUBLIC";
|
||||
case BLE_ADDR_TYPE_RPA_RANDOM:
|
||||
return "BLE_ADDR_TYPE_RPA_RANDOM";
|
||||
default:
|
||||
return "Unknown addr_t";
|
||||
}
|
||||
} // bt_addr_t_to_string
|
||||
|
||||
const char *btsig_gap_type(uint32_t gap_type) {
|
||||
switch (gap_type) {
|
||||
case 0x01:
|
||||
return "Flags";
|
||||
case 0x02:
|
||||
return "Incomplete List of 16-bit Service Class UUIDs";
|
||||
case 0x03:
|
||||
return "Complete List of 16-bit Service Class UUIDs";
|
||||
case 0x04:
|
||||
return "Incomplete List of 32-bit Service Class UUIDs";
|
||||
case 0x05:
|
||||
return "Complete List of 32-bit Service Class UUIDs";
|
||||
case 0x06:
|
||||
return "Incomplete List of 128-bit Service Class UUIDs";
|
||||
case 0x07:
|
||||
return "Complete List of 128-bit Service Class UUIDs";
|
||||
case 0x08:
|
||||
return "Shortened Local Name";
|
||||
case 0x09:
|
||||
return "Complete Local Name";
|
||||
case 0x0A:
|
||||
return "Tx Power Level";
|
||||
case 0x0D:
|
||||
return "Class of Device";
|
||||
case 0x0E:
|
||||
return "Simple Pairing Hash C/C-192";
|
||||
case 0x0F:
|
||||
return "Simple Pairing Randomizer R/R-192";
|
||||
case 0x10:
|
||||
return "Device ID/Security Manager TK Value";
|
||||
case 0x11:
|
||||
return "Security Manager Out of Band Flags";
|
||||
case 0x12:
|
||||
return "Slave Connection Interval Range";
|
||||
case 0x14:
|
||||
return "List of 16-bit Service Solicitation UUIDs";
|
||||
case 0x1F:
|
||||
return "List of 32-bit Service Solicitation UUIDs";
|
||||
case 0x15:
|
||||
return "List of 128-bit Service Solicitation UUIDs";
|
||||
case 0x16:
|
||||
return "Service Data - 16-bit UUID";
|
||||
case 0x20:
|
||||
return "Service Data - 32-bit UUID";
|
||||
case 0x21:
|
||||
return "Service Data - 128-bit UUID";
|
||||
case 0x22:
|
||||
return "LE Secure Connections Confirmation Value";
|
||||
case 0x23:
|
||||
return "LE Secure Connections Random Value";
|
||||
case 0x24:
|
||||
return "URI";
|
||||
case 0x25:
|
||||
return "Indoor Positioning";
|
||||
case 0x26:
|
||||
return "Transport Discovery Data";
|
||||
case 0x17:
|
||||
return "Public Target Address";
|
||||
case 0x18:
|
||||
return "Random Target Address";
|
||||
case 0x19:
|
||||
return "Appearance";
|
||||
case 0x1A:
|
||||
return "Advertising Interval";
|
||||
case 0x1B:
|
||||
return "LE Bluetooth Device Address";
|
||||
case 0x1C:
|
||||
return "LE Role";
|
||||
case 0x1D:
|
||||
return "Simple Pairing Hash C-256";
|
||||
case 0x1E:
|
||||
return "Simple Pairing Randomizer R-256";
|
||||
case 0x3D:
|
||||
return "3D Information Data";
|
||||
case 0xFF:
|
||||
return "Manufacturer Specific Data";
|
||||
|
||||
default:
|
||||
return "Unknown type";
|
||||
}
|
||||
} // btsig_gap_type
|
||||
#endif
|
||||
|
||||
// using IRAM_ATTR here to speed up callback function
|
||||
IRAM_ATTR void gap_callback_handler(esp_gap_ble_cb_event_t event,
|
||||
esp_ble_gap_cb_param_t *param) {
|
||||
|
||||
esp_ble_gap_cb_param_t *p = (esp_ble_gap_cb_param_t *)param;
|
||||
|
||||
#if (COUNT_ENS)
|
||||
// UUID of Exposure Notification Service (ENS)
|
||||
// https://blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf
|
||||
static const char ensMagicBytes[] = "\x16\x6f\xfd";
|
||||
#endif
|
||||
|
||||
#ifdef VERBOSE
|
||||
ESP_LOGV(TAG, "BT payload rcvd -> type: 0x%.2x -> %s", *p->scan_rst.ble_adv,
|
||||
btsig_gap_type(*p->scan_rst.ble_adv));
|
||||
#endif
|
||||
|
||||
switch (event) {
|
||||
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
|
||||
// restart scan
|
||||
esp_ble_gap_start_scanning(BLESCANTIME);
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_SCAN_RESULT_EVT:
|
||||
// evaluate scan results
|
||||
if (p->scan_rst.search_evt ==
|
||||
ESP_GAP_SEARCH_INQ_CMPL_EVT) // Inquiry complete, scan is done
|
||||
{ // restart scan
|
||||
esp_ble_gap_start_scanning(BLESCANTIME);
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->scan_rst.search_evt ==
|
||||
ESP_GAP_SEARCH_INQ_RES_EVT) // Inquiry result for a peer device
|
||||
{ // evaluate sniffed packet
|
||||
#ifdef VERBOSE
|
||||
ESP_LOGV(TAG, "Device address (bda): %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
BT_BD_ADDR_HEX(p->scan_rst.bda));
|
||||
ESP_LOGV(TAG, "Addr_type : %s",
|
||||
bt_addr_t_to_string(p->scan_rst.ble_addr_type));
|
||||
ESP_LOGV(TAG, "RSSI : %d", p->scan_rst.rssi);
|
||||
#endif
|
||||
|
||||
#if (MACFILTER)
|
||||
if ((p->scan_rst.ble_addr_type == BLE_ADDR_TYPE_RANDOM) ||
|
||||
(p->scan_rst.ble_addr_type == BLE_ADDR_TYPE_RPA_RANDOM)) {
|
||||
#ifdef VERBOSE
|
||||
ESP_LOGV(TAG, "BT device filtered");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
// add this device mac to processing queue
|
||||
|
||||
#if (COUNT_ENS)
|
||||
// check for ens signature
|
||||
if (cfg.enscount) {
|
||||
if (strstr((const char *)p->scan_rst.ble_adv, ensMagicBytes) != NULL)
|
||||
mac_add((uint8_t *)p->scan_rst.bda, p->scan_rst.rssi,
|
||||
MAC_SNIFF_BLE_ENS);
|
||||
else
|
||||
mac_add((uint8_t *)p->scan_rst.bda, p->scan_rst.rssi, MAC_SNIFF_BLE);
|
||||
}
|
||||
#else
|
||||
mac_add((uint8_t *)p->scan_rst.bda, p->scan_rst.rssi, MAC_SNIFF_BLE);
|
||||
#endif
|
||||
|
||||
/* to be improved in macfilter:
|
||||
// you can search for elements in the payload using the
|
||||
// function esp_ble_resolve_adv_data()
|
||||
//
|
||||
// Like this, that scans for the "Complete name" (looking inside the
|
||||
payload buffer)
|
||||
// uint8_t len;
|
||||
// uint8_t *data = esp_ble_resolve_adv_data(p->scan_rst.ble_adv,
|
||||
ESP_BLE_AD_TYPE_NAME_CMPL, &len);
|
||||
|
||||
filter BLE devices using their advertisements to get filter alternative
|
||||
to vendor OUI if macfiltering is on, we ...
|
||||
- want to count: mobile phones and tablets
|
||||
- don't want to count: beacons, peripherals (earphones, headsets,
|
||||
printers), cars and machines see
|
||||
https://github.com/nkolban/ESP32_BLE_Arduino/blob/master/src/BLEAdvertisedDevice.cpp
|
||||
|
||||
http://www.libelium.com/products/meshlium/smartphone-detection/
|
||||
|
||||
https://www.question-defense.com/2013/01/12/bluetooth-cod-bluetooth-class-of-deviceclass-of-service-explained
|
||||
|
||||
https://www.bluetooth.com/specifications/assigned-numbers/baseband
|
||||
|
||||
"The Class of Device (CoD) in case of Bluetooth which allows us to
|
||||
differentiate the type of device (smartphone, handsfree, computer,
|
||||
LAN/network AP). With this parameter we can differentiate among
|
||||
pedestrians and vehicles."
|
||||
|
||||
*/
|
||||
|
||||
} // evaluate sniffed packet
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
} // switch
|
||||
} // gap_callback_handler
|
||||
|
||||
esp_err_t register_ble_callback(bool unregister = false) {
|
||||
|
||||
if (unregister) {
|
||||
|
||||
ESP_LOGI(TAG, "Unregister GAP callback...");
|
||||
esp_ble_gap_stop_scanning();
|
||||
esp_ble_gap_register_callback(NULL);
|
||||
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
ESP_LOGI(TAG, "Register GAP callback...");
|
||||
|
||||
// This function is called when gap event occurs, such as scan result.
|
||||
// register the scan callback function to the gap module
|
||||
esp_ble_gap_register_callback(&gap_callback_handler);
|
||||
|
||||
static esp_ble_scan_params_t ble_scan_params = {
|
||||
.scan_type = BLE_SCAN_TYPE_PASSIVE,
|
||||
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
|
||||
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||||
|
||||
/*
|
||||
#if (MACFILTER)
|
||||
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_WLIST_PRA_DIR,
|
||||
// ADV_IND, ADV_NONCONN_IND, ADV_SCAN_IND packets are used
|
||||
for broadcasting
|
||||
// data in broadcast applications (e.g., Beacons), so we
|
||||
don't want them in
|
||||
// macfilter mode
|
||||
#else
|
||||
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||||
#endif
|
||||
*/
|
||||
|
||||
.scan_interval =
|
||||
(uint16_t)(cfg.blescantime * 10 / 0.625), // Time = N * 0.625 msec
|
||||
.scan_window =
|
||||
(uint16_t)(BLESCANWINDOW / 0.625), // Time = N * 0.625 msec
|
||||
.scan_duplicate = BLE_SCAN_DUPLICATE_ENABLE};
|
||||
|
||||
ESP_LOGI(TAG, "Set GAP scan parameters");
|
||||
|
||||
// This function is called to set scan parameters.
|
||||
esp_ble_gap_set_scan_params(&ble_scan_params);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
} // register_ble_callback
|
||||
|
||||
void start_BLEscan(void) {
|
||||
#if (BLECOUNTER)
|
||||
ESP_LOGI(TAG, "Initializing bluetooth scanner ...");
|
||||
// Initialize BT controller to allocate task and other resource.
|
||||
if (btStart()) { // enable bt_controller
|
||||
esp_coex_preference_set(ESP_COEX_PREFER_BT);
|
||||
esp_bluedroid_init();
|
||||
esp_bluedroid_enable();
|
||||
// Register callback function for capturing bluetooth packets
|
||||
register_ble_callback(false);
|
||||
ESP_LOGI(TAG, "Bluetooth scanner started");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Bluetooth controller start failed. Resetting device");
|
||||
do_reset(true);
|
||||
}
|
||||
#endif // BLECOUNTER
|
||||
} // start_BLEscan
|
||||
|
||||
void stop_BLEscan(void) {
|
||||
#if (BLECOUNTER)
|
||||
ESP_LOGI(TAG, "Shutting down bluetooth scanner ...");
|
||||
register_ble_callback(true); // unregister capture function
|
||||
ESP_LOGD(TAG, "bluedroid disable...");
|
||||
esp_bluedroid_disable();
|
||||
ESP_LOGD(TAG, "bluedroid deinit...");
|
||||
esp_bluedroid_deinit();
|
||||
if (!btStop()) { // disable bt_controller
|
||||
ESP_LOGE(TAG, "Bluetooth controller stop failed. Resetting device");
|
||||
do_reset(true);
|
||||
}
|
||||
esp_coex_preference_set(ESP_COEX_PREFER_WIFI);
|
||||
ESP_LOGI(TAG, "Bluetooth scanner stopped");
|
||||
#endif // BLECOUNTER
|
||||
} // stop_BLEscan
|
@ -197,7 +197,7 @@ void updateState(void) {
|
||||
memcpy(cfg.bsecstate, bsecstate_buffer, BSEC_MAX_STATE_BLOB_SIZE);
|
||||
cfg.bsecstate[BSEC_MAX_STATE_BLOB_SIZE] = BSEC_MAX_STATE_BLOB_SIZE;
|
||||
ESP_LOGI(TAG, "saving BSEC state to NVRAM");
|
||||
saveConfig();
|
||||
saveConfig(false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -76,14 +76,6 @@ void IRAM_ATTR watchdog() { xTaskResumeFromISR(RestartHandle); }
|
||||
|
||||
void start_boot_menu(void) {
|
||||
|
||||
uint8_t mac[6];
|
||||
char clientId[20];
|
||||
|
||||
// hash 6 byte MAC to 4 byte hash
|
||||
esp_eth_get_mac(mac);
|
||||
const uint32_t hashedmac = myhash((const char *)mac, 6);
|
||||
snprintf(clientId, 20, "paxcounter_%08x", hashedmac);
|
||||
|
||||
const char *host = clientId;
|
||||
const char *ssid = WIFI_SSID;
|
||||
const char *password = WIFI_PASS;
|
||||
|
@ -8,15 +8,17 @@ static const char TAG[] = __FILE__;
|
||||
|
||||
// default settings for device data to be sent
|
||||
#define PAYLOADMASK \
|
||||
((GPS_DATA | ALARM_DATA | MEMS_DATA | COUNT_DATA | SENSOR1_DATA | \
|
||||
SENSOR2_DATA | SENSOR3_DATA) & \
|
||||
(~BATT_DATA))
|
||||
((GPS_DATA | MEMS_DATA | COUNT_DATA | SENSOR1_DATA | SENSOR2_DATA | \
|
||||
SENSOR3_DATA) & \
|
||||
(~BATT_DATA) & (~RESERVED_DATA))
|
||||
|
||||
// namespace for device runtime preferences
|
||||
#define DEVCONFIG "paxcntcfg"
|
||||
|
||||
Preferences nvram;
|
||||
|
||||
configData_t cfg; // struct holds current device configuration
|
||||
|
||||
static const uint8_t cfgMagicBytes[] = {0x21, 0x76, 0x87, 0x32, 0xf4};
|
||||
static const size_t cfgLen = sizeof(cfg), cfgLen2 = sizeof(cfgMagicBytes);
|
||||
static uint8_t buffer[cfgLen + cfgLen2];
|
||||
@ -42,7 +44,7 @@ static void defaultConfig(configData_t *myconfig) {
|
||||
COUNTERMODE; // 0=cyclic, 1=cumulative, 2=cyclic confirmed
|
||||
myconfig->rssilimit = 0; // threshold for rssilimiter, negative value!
|
||||
myconfig->sendcycle = SENDCYCLE; // payload send cycle [seconds/2]
|
||||
myconfig->sleepcycle = SLEEPCYCLE; // sleep cycle [seconds/2]
|
||||
myconfig->sleepcycle = SLEEPCYCLE; // sleep cycle [seconds/10]
|
||||
myconfig->wifichancycle =
|
||||
WIFI_CHANNEL_SWITCH_INTERVAL; // wifi channel switch cycle [seconds/100]
|
||||
myconfig->blescantime =
|
||||
@ -51,9 +53,7 @@ static void defaultConfig(configData_t *myconfig) {
|
||||
myconfig->blescan = BLECOUNTER; // 0=disabled, 1=enabled
|
||||
myconfig->wifiscan = WIFICOUNTER; // 0=disabled, 1=enabled
|
||||
myconfig->wifiant = 0; // 0=internal, 1=external (for LoPy/LoPy4)
|
||||
myconfig->macfilter = MACFILTER; // 0=disabled, 1=enabled
|
||||
myconfig->rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%)
|
||||
myconfig->monitormode = 0; // 0=disabled, 1=enabled
|
||||
myconfig->payloadmask = PAYLOADMASK; // payloads as defined in default
|
||||
myconfig->enscount = COUNT_ENS; // 0=disabled, 1=enabled
|
||||
|
||||
|
@ -29,9 +29,6 @@ void doHousekeeping() {
|
||||
ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d",
|
||||
uxTaskGetStackHighWaterMark(irqHandlerTask),
|
||||
eTaskGetState(irqHandlerTask));
|
||||
ESP_LOGD(TAG, "MACprocessor %d bytes left | Taskstate = %d",
|
||||
uxTaskGetStackHighWaterMark(macProcessTask),
|
||||
eTaskGetState(macProcessTask));
|
||||
ESP_LOGD(TAG, "Rcommand interpreter %d bytes left | Taskstate = %d",
|
||||
uxTaskGetStackHighWaterMark(rcmdTask), eTaskGetState(rcmdTask));
|
||||
#if (HAS_LORA)
|
||||
@ -92,24 +89,18 @@ void doHousekeeping() {
|
||||
|
||||
// check free heap memory
|
||||
if (ESP.getMinFreeHeap() <= MEM_LOW) {
|
||||
ESP_LOGI(TAG,
|
||||
ESP_LOGW(TAG,
|
||||
"Memory full, counter cleared (heap low water mark = %d Bytes / "
|
||||
"free heap = %d bytes)",
|
||||
ESP.getMinFreeHeap(), ESP.getFreeHeap());
|
||||
reset_counters(); // clear macs container and reset all counters
|
||||
|
||||
if (ESP.getMinFreeHeap() <= MEM_LOW) // check again
|
||||
do_reset(true); // memory leak, reset device
|
||||
do_reset(true); // memory leak, reset device
|
||||
}
|
||||
|
||||
// check free PSRAM memory
|
||||
#ifdef BOARD_HAS_PSRAM
|
||||
if (ESP.getMinFreePsram() <= MEM_LOW) {
|
||||
ESP_LOGI(TAG, "PSRAM full, counter cleared");
|
||||
reset_counters(); // clear macs container and reset all counters
|
||||
|
||||
if (ESP.getMinFreePsram() <= MEM_LOW) // check again
|
||||
do_reset(true); // memory leak, reset device
|
||||
ESP_LOGW(TAG, "PSRAM full, counter cleared");
|
||||
do_reset(true); // memory leak, reset device
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -131,17 +122,4 @@ uint32_t getFreeRAM() {
|
||||
#else
|
||||
return ESP.getFreePsram();
|
||||
#endif
|
||||
}
|
||||
|
||||
void reset_counters() {
|
||||
#if ((WIFICOUNTER) || (BLECOUNTER))
|
||||
macs.clear(); // clear all macs container
|
||||
macs_wifi = 0;
|
||||
macs_ble = 0;
|
||||
renew_salt(); // get new salt
|
||||
#ifdef HAS_DISPLAY
|
||||
dp_plotCurve(0, true);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
@ -20,11 +20,9 @@ static const char TAG[] = __FILE__;
|
||||
void DCF77_Pulse(time_t t, uint8_t const *DCFpulse) {
|
||||
|
||||
TickType_t startTime = xTaskGetTickCount();
|
||||
uint8_t sec = second(t);
|
||||
uint8_t sec = myTZ.second(t);
|
||||
|
||||
t = myTZ.toLocal(now());
|
||||
ESP_LOGD(TAG, "[%02d:%02d:%02d.%03d] DCF second %d", hour(t), minute(t),
|
||||
second(t), millisecond(), sec);
|
||||
ESP_LOGD(TAG, "[%s] DCF second: %d", myTZ.dateTime("H:i:s.v").c_str(), sec);
|
||||
|
||||
// induce a DCF Pulse
|
||||
for (uint8_t pulse = 0; pulse <= 2; pulse++) {
|
||||
@ -53,7 +51,7 @@ void DCF77_Pulse(time_t t, uint8_t const *DCFpulse) {
|
||||
} // for
|
||||
} // DCF77_Pulse()
|
||||
|
||||
uint8_t *IRAM_ATTR DCF77_Frame(time_t const tt) {
|
||||
uint8_t *IRAM_ATTR DCF77_Frame(time_t const t) {
|
||||
|
||||
// array of dcf pulses for one minute, secs 0..16 and 20 are never touched, so
|
||||
// we keep them statically to avoid same recalculation every minute
|
||||
@ -64,35 +62,35 @@ uint8_t *IRAM_ATTR DCF77_Frame(time_t const tt) {
|
||||
dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_1};
|
||||
|
||||
uint8_t Parity;
|
||||
time_t t = myTZ.toLocal(tt); // convert to local time
|
||||
|
||||
// ENCODE DST CHANGE ANNOUNCEMENT (Sec 16)
|
||||
DCFpulse[16] = dcf_0; // not yet implemented
|
||||
|
||||
// ENCODE DAYLIGHTSAVING (secs 17..18)
|
||||
DCFpulse[17] = myTZ.locIsDST(t) ? dcf_1 : dcf_0;
|
||||
DCFpulse[18] = myTZ.locIsDST(t) ? dcf_0 : dcf_1;
|
||||
DCFpulse[17] = myTZ.isDST(t) ? dcf_1 : dcf_0;
|
||||
DCFpulse[18] = myTZ.isDST(t) ? dcf_0 : dcf_1;
|
||||
|
||||
// ENCODE MINUTE (secs 21..28)
|
||||
Parity = dec2bcd(minute(t), 21, 27, DCFpulse);
|
||||
Parity = dec2bcd(myTZ.minute(t), 21, 27, DCFpulse);
|
||||
DCFpulse[28] = setParityBit(Parity);
|
||||
|
||||
// ENCODE HOUR (secs 29..35)
|
||||
Parity = dec2bcd(hour(t), 29, 34, DCFpulse);
|
||||
Parity = dec2bcd(myTZ.hour(t), 29, 34, DCFpulse);
|
||||
DCFpulse[35] = setParityBit(Parity);
|
||||
|
||||
// ENCODE DATE (secs 36..58)
|
||||
Parity = dec2bcd(day(t), 36, 41, DCFpulse);
|
||||
Parity += dec2bcd((weekday(t) - 1) ? (weekday(t) - 1) : 7, 42, 44, DCFpulse);
|
||||
Parity += dec2bcd(month(t), 45, 49, DCFpulse);
|
||||
Parity += dec2bcd(year(t) - 2000, 50, 57, DCFpulse);
|
||||
Parity = dec2bcd(myTZ.day(t), 36, 41, DCFpulse);
|
||||
Parity += dec2bcd((myTZ.weekday(t) - 1) ? (myTZ.weekday(t) - 1) : 7, 42, 44,
|
||||
DCFpulse);
|
||||
Parity += dec2bcd(myTZ.month(t), 45, 49, DCFpulse);
|
||||
Parity += dec2bcd(myTZ.year(t) - 2000, 50, 57, DCFpulse);
|
||||
DCFpulse[58] = setParityBit(Parity);
|
||||
|
||||
// ENCODE MARK (sec 59)
|
||||
DCFpulse[59] = dcf_Z; // !! missing code here for leap second !!
|
||||
|
||||
// timestamp this frame with it's minute
|
||||
DCFpulse[60] = minute(t);
|
||||
DCFpulse[60] = myTZ.minute(t);
|
||||
|
||||
return DCFpulse;
|
||||
|
||||
|
@ -39,14 +39,13 @@ MY_FONT_STRETCHED: 16x32px = 8 chars / line
|
||||
// local Tag for logging
|
||||
static const char TAG[] = __FILE__;
|
||||
|
||||
// helper array for converting month values to text
|
||||
const char *printmonth[] = {"xxx", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||||
uint8_t DisplayIsOn = 0;
|
||||
uint8_t displaybuf[MY_DISPLAY_WIDTH * MY_DISPLAY_HEIGHT / 8] = {0};
|
||||
static uint8_t plotbuf[MY_DISPLAY_WIDTH * MY_DISPLAY_HEIGHT / 8] = {0};
|
||||
static int dp_row = 0, dp_col = 0, dp_font = 0;
|
||||
|
||||
hw_timer_t *displayIRQ = NULL;
|
||||
|
||||
QRCode qrcode;
|
||||
|
||||
#ifdef HAS_DISPLAY
|
||||
@ -159,7 +158,7 @@ void dp_init(bool verbose) {
|
||||
#if !(BOOTMENU)
|
||||
delay(8000);
|
||||
#endif
|
||||
|
||||
|
||||
#endif // HAS_LORA
|
||||
|
||||
} // verbose
|
||||
@ -175,20 +174,20 @@ void dp_init(bool verbose) {
|
||||
|
||||
void dp_refresh(bool nextPage) {
|
||||
|
||||
// update counter values from libpax
|
||||
libpax_counter_count(&count_from_libpax);
|
||||
|
||||
#ifndef HAS_BUTTON
|
||||
static uint32_t framecounter = 0;
|
||||
#endif
|
||||
|
||||
// update histogram
|
||||
dp_plotCurve(macs.size(), false);
|
||||
dp_plotCurve(count_from_libpax.pax, false);
|
||||
|
||||
// if display is switched off we don't refresh it to relax cpu
|
||||
if (!DisplayIsOn && (DisplayIsOn == cfg.screenon))
|
||||
return;
|
||||
|
||||
const time_t t =
|
||||
myTZ.toLocal(now()); // note: call now() here *before* locking mutex!
|
||||
|
||||
// block i2c bus access
|
||||
if (!I2C_MUTEX_LOCK())
|
||||
ESP_LOGV(TAG, "[%0.3f] i2c mutex lock failed", _seconds());
|
||||
@ -207,7 +206,7 @@ void dp_refresh(bool nextPage) {
|
||||
}
|
||||
#endif
|
||||
|
||||
dp_drawPage(t, nextPage);
|
||||
dp_drawPage(nextPage);
|
||||
dp_dump(displaybuf);
|
||||
|
||||
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||
@ -215,7 +214,7 @@ void dp_refresh(bool nextPage) {
|
||||
} // mutex
|
||||
} // refreshDisplay()
|
||||
|
||||
void dp_drawPage(time_t t, bool nextpage) {
|
||||
void dp_drawPage(bool nextpage) {
|
||||
|
||||
// write display content to display buffer
|
||||
// nextpage = true -> flip 1 page
|
||||
@ -238,7 +237,7 @@ void dp_drawPage(time_t t, bool nextpage) {
|
||||
// display number of unique macs total Wifi + BLE
|
||||
if (DisplayPage < 5) {
|
||||
dp_setFont(MY_FONT_STRETCHED);
|
||||
dp_printf("%-5d", macs.size());
|
||||
dp_printf("%-5d", count_from_libpax.pax);
|
||||
}
|
||||
|
||||
switch (DisplayPage) {
|
||||
@ -262,7 +261,7 @@ void dp_drawPage(time_t t, bool nextpage) {
|
||||
|
||||
#if ((WIFICOUNTER) && (BLECOUNTER))
|
||||
if (cfg.wifiscan)
|
||||
dp_printf("WIFI:%-5d", macs_wifi);
|
||||
dp_printf("WIFI:%-5d", count_from_libpax.wifi_count);
|
||||
else
|
||||
dp_printf("WIFI:off");
|
||||
if (cfg.blescan)
|
||||
@ -271,17 +270,17 @@ void dp_drawPage(time_t t, bool nextpage) {
|
||||
dp_printf(" CWA:%-5d", cwa_report());
|
||||
else
|
||||
#endif
|
||||
dp_printf("BLTH:%-5d", macs_ble);
|
||||
dp_printf("BLTH:%-5d", count_from_libpax.ble_count);
|
||||
else
|
||||
dp_printf(" BLTH:off");
|
||||
#elif ((WIFICOUNTER) && (!BLECOUNTER))
|
||||
if (cfg.wifiscan)
|
||||
dp_printf("WIFI:%-5d", macs_wifi);
|
||||
dp_printf("WIFI:%-5d", count_from_libpax.wifi_count);
|
||||
else
|
||||
dp_printf("WIFI:off");
|
||||
#elif ((!WIFICOUNTER) && (BLECOUNTER))
|
||||
if (cfg.blescan)
|
||||
dp_printf("BLTH:%-5d", macs_ble);
|
||||
dp_printf("BLTH:%-5d", count_from_libpax.ble_count);
|
||||
#if (COUNT_ENS)
|
||||
if (cfg.enscount)
|
||||
dp_printf("(CWA:%d)", cwa_report());
|
||||
@ -312,7 +311,6 @@ void dp_drawPage(time_t t, bool nextpage) {
|
||||
dp_printf(" ");
|
||||
#endif
|
||||
dp_printf(" ch:%02d", channel);
|
||||
// dp_printf(" due:%02d", rf_load);
|
||||
dp_println();
|
||||
|
||||
// line 5: RSSI limiter + free memory
|
||||
@ -326,9 +324,7 @@ void dp_drawPage(time_t t, bool nextpage) {
|
||||
#if (TIME_SYNC_INTERVAL)
|
||||
timeState = TimePulseTick ? ' ' : timeSetSymbols[timeSource];
|
||||
TimePulseTick = false;
|
||||
|
||||
dp_printf("%02d.%3s %4d", day(t), printmonth[month(t)], year(t));
|
||||
dp_printf(" %02d:%02d:%02d", hour(t), minute(t), second(t));
|
||||
dp_printf("%s", myTZ.dateTime("d.M Y H:i:s").c_str());
|
||||
|
||||
// display inverse timeState if clock controller is enabled
|
||||
#if (defined HAS_DCF77) || (defined HAS_IF482)
|
||||
@ -341,7 +337,7 @@ void dp_drawPage(time_t t, bool nextpage) {
|
||||
dp_println();
|
||||
#endif // TIME_SYNC_INTERVAL
|
||||
|
||||
// line 7: LORA network status
|
||||
// line 7: LMIC status
|
||||
// yyyyyyyyyyyyy xx SFab
|
||||
|
||||
#if (HAS_LORA)
|
||||
@ -351,7 +347,6 @@ void dp_drawPage(time_t t, bool nextpage) {
|
||||
dp_setFont(MY_FONT_SMALL, !cfg.adrmode);
|
||||
dp_printf("%-4s", getSfName(updr2rps(LMIC.datarate)));
|
||||
dp_setFont(MY_FONT_SMALL, 0);
|
||||
dp_println();
|
||||
#endif // HAS_LORA
|
||||
break;
|
||||
|
||||
@ -447,7 +442,7 @@ void dp_drawPage(time_t t, bool nextpage) {
|
||||
|
||||
dp_setFont(MY_FONT_LARGE);
|
||||
dp_setTextCursor(0, 4);
|
||||
dp_printf("%02d:%02d:%02d", hour(t), minute(t), second(t));
|
||||
dp_printf("%s", myTZ.dateTime("H:i:s").c_str());
|
||||
break;
|
||||
|
||||
// ---------- page 5: pax graph ----------
|
||||
@ -613,6 +608,14 @@ void dp_shutdown(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// print static message on display
|
||||
void dp_message(const char *msg, int line, bool invers) {
|
||||
dp_setTextCursor(0, line);
|
||||
dp_setFont(MY_FONT_NORMAL, invers ? 1 : 0);
|
||||
dp_printf("%-16s", msg);
|
||||
dp_dump(displaybuf);
|
||||
} // dp_message
|
||||
|
||||
// ------------- QR code plotter -----------------
|
||||
|
||||
void dp_printqr(uint16_t offset_x, uint16_t offset_y, const char *Message) {
|
||||
@ -720,6 +723,7 @@ void dp_plotCurve(uint16_t count, bool reset) {
|
||||
static uint16_t last_count = 0, col = 0, row = 0;
|
||||
uint16_t v_scroll = 0;
|
||||
|
||||
// nothing new to plot? -> then exit early
|
||||
if ((last_count == count) && !reset)
|
||||
return;
|
||||
|
||||
|
@ -111,16 +111,16 @@ time_t get_gpstime(uint16_t *msec) {
|
||||
uint32_t zdatime = atof(gpstime.value());
|
||||
|
||||
// convert time to maketime format and make time
|
||||
tm.Second = zdatime % 100; // second
|
||||
tm.Minute = (zdatime / 100) % 100; // minute
|
||||
tm.Hour = zdatime / 10000; // hour
|
||||
tm.Day = atoi(gpsday.value()); // day
|
||||
tm.Month = atoi(gpsmonth.value()); // month
|
||||
tm.Year = CalendarYrToTm(atoi(gpsyear.value())); // year offset from 1970
|
||||
tm.Second = zdatime % 100; // second
|
||||
tm.Minute = (zdatime / 100) % 100; // minute
|
||||
tm.Hour = zdatime / 10000; // hour
|
||||
tm.Day = atoi(gpsday.value()); // day
|
||||
tm.Month = atoi(gpsmonth.value()); // month
|
||||
tm.Year = atoi(gpsyear.value()) - 1970; // year offset from 1970
|
||||
t = makeTime(tm);
|
||||
|
||||
ESP_LOGD(TAG, "GPS time/date = %02d:%02d:%02d / %02d.%02d.%2d", tm.Hour,
|
||||
tm.Minute, tm.Second, tm.Day, tm.Month, tm.Year + 1970);
|
||||
ESP_LOGD(TAG, "GPS date/time: %s",
|
||||
UTC.dateTime(t, "d.M Y H:i:s T").c_str());
|
||||
|
||||
// add protocol delay with millisecond precision
|
||||
t += delay_ms / 1000 - 1; // whole seconds
|
||||
@ -163,7 +163,8 @@ void gps_loop(void *pvParameters) {
|
||||
|
||||
// (only) while device time is not set or unsynched, and we have a valid
|
||||
// GPS time, we trigger a device time update to poll time from GPS
|
||||
if (timeSource == _unsynced && gpstime.isUpdated()) {
|
||||
if ((timeSource == _unsynced || timeSource == _set) &&
|
||||
(gpstime.isUpdated() && gpstime.isValid() && gpstime.age() < 1000)) {
|
||||
now();
|
||||
calibrateTime();
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ User, long press -> send a button message
|
||||
Reset -> reset device
|
||||
*/
|
||||
|
||||
//#define HAS_DISPLAY 1
|
||||
#define HAS_DISPLAY 1
|
||||
#define MY_DISPLAY_SDA SDA
|
||||
#define MY_DISPLAY_SCL SCL
|
||||
#define MY_DISPLAY_RST NOT_A_PIN
|
||||
|
@ -5,6 +5,8 @@
|
||||
// Local logging tag
|
||||
static const char TAG[] = __FILE__;
|
||||
|
||||
SemaphoreHandle_t I2Caccess;
|
||||
|
||||
void i2c_init(void) { Wire.begin(MY_DISPLAY_SDA, MY_DISPLAY_SCL, 100000); }
|
||||
|
||||
void i2c_deinit(void) { Wire.~TwoWire(); }
|
||||
|
@ -14,7 +14,7 @@ IF482.cpp depends on code in RTCTIME.cpp.
|
||||
|
||||
/*
|
||||
IF482 Generator to control clocks with IF482 telegram input (e.g. BÜRK BU190)
|
||||
|
||||
|
||||
|
||||
Example IF482 telegram: "OAL160806F170400"
|
||||
|
||||
@ -84,9 +84,8 @@ not evaluated by model BU-190, use "F" instead for this model
|
||||
// Local logging tag
|
||||
static const char TAG[] = __FILE__;
|
||||
|
||||
String IRAM_ATTR IF482_Frame(time_t printTime) {
|
||||
String IRAM_ATTR IF482_Frame(time_t t) {
|
||||
|
||||
time_t t = myTZ.toLocal(printTime);
|
||||
char mon, out[IF482_FRAME_SIZE + 1];
|
||||
|
||||
switch (timeStatus()) { // indicates if time has been set and recently synced
|
||||
@ -102,13 +101,11 @@ String IRAM_ATTR IF482_Frame(time_t printTime) {
|
||||
} // switch
|
||||
|
||||
// generate IF482 telegram
|
||||
snprintf(out, sizeof(out), "O%cL%02u%02u%02u%1u%02u%02u%02u\r", mon,
|
||||
year(t) - 2000, month(t), day(t), weekday(t), hour(t), minute(t),
|
||||
second(t));
|
||||
snprintf(out, sizeof(out), "O%cL%s\r", mon, myTZ.dateTime(t, UTC_TIME, "ymdwHis").c_str());
|
||||
|
||||
ESP_LOGD(TAG, "[%s] IF482 date/time: %s", myTZ.dateTime("H:i:s.v").c_str(),
|
||||
out);
|
||||
|
||||
t = myTZ.toLocal(now());
|
||||
ESP_LOGD(TAG, "[%02d:%02d:%02d.%03d] IF482 = %s", hour(t), minute(t),
|
||||
second(t), millisecond(), out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
// Local logging tag
|
||||
static const char TAG[] = __FILE__;
|
||||
|
||||
TaskHandle_t irqHandlerTask = NULL;
|
||||
|
||||
// irq handler task, handles all our application level interrupts
|
||||
void irqHandler(void *pvParameters) {
|
||||
|
||||
@ -90,9 +92,9 @@ void irqHandler(void *pvParameters) {
|
||||
// goto sleep if we have a sleep cycle
|
||||
if (cfg.sleepcycle)
|
||||
#ifdef HAS_BUTTON
|
||||
enter_deepsleep(cfg.sleepcycle * 2, (gpio_num_t)HAS_BUTTON);
|
||||
enter_deepsleep(cfg.sleepcycle * 10, (gpio_num_t)HAS_BUTTON);
|
||||
#else
|
||||
enter_deepsleep(cfg.sleepcycle * 2);
|
||||
enter_deepsleep(cfg.sleepcycle * 10);
|
||||
#endif
|
||||
}
|
||||
} // for
|
||||
|
@ -12,7 +12,9 @@ static const char TAG[] = __FILE__;
|
||||
uint8_t MatrixDisplayIsOn = 0;
|
||||
static uint8_t displaybuf[LED_MATRIX_WIDTH * LED_MATRIX_HEIGHT / 8] = {0};
|
||||
static unsigned long ulLastNumMacs = 0;
|
||||
static time_t ulLastTime = myTZ.toLocal(now());
|
||||
static time_t ulLastTime = now();
|
||||
|
||||
hw_timer_t *matrixDisplayIRQ = NULL;
|
||||
|
||||
LEDMatrix matrix(LED_MATRIX_LA_74138, LED_MATRIX_LB_74138, LED_MATRIX_LC_74138,
|
||||
LED_MATRIX_LD_74138, LED_MATRIX_EN_74138, LED_MATRIX_DATA_R1,
|
||||
@ -45,7 +47,6 @@ void init_matrix_display(bool reverse) {
|
||||
void refreshTheMatrixDisplay(bool nextPage) {
|
||||
static uint8_t DisplayPage = 0, col = 0, row = 0;
|
||||
uint8_t level;
|
||||
char buff[16];
|
||||
|
||||
// if Matrixdisplay is switched off we don't refresh it to relax cpu
|
||||
if (!MatrixDisplayIsOn && (MatrixDisplayIsOn == cfg.screenon))
|
||||
@ -74,11 +75,13 @@ void refreshTheMatrixDisplay(bool nextPage) {
|
||||
|
||||
case 0:
|
||||
|
||||
if (cfg.countermode == 1)
|
||||
// update counter values from libpax
|
||||
libpax_counter_count(&count_from_libpax);
|
||||
|
||||
{ // cumulative counter mode -> display total number of pax
|
||||
if (ulLastNumMacs != macs.size()) {
|
||||
ulLastNumMacs = macs.size();
|
||||
if (cfg.countermode == 1) {
|
||||
// cumulative counter mode -> display total number of pax
|
||||
if (ulLastNumMacs != count_from_libpax.pax) {
|
||||
ulLastNumMacs = count_from_libpax.pax;
|
||||
matrix.clear();
|
||||
DrawNumber(String(ulLastNumMacs));
|
||||
}
|
||||
@ -86,10 +89,10 @@ void refreshTheMatrixDisplay(bool nextPage) {
|
||||
|
||||
else { // cyclic counter mode -> plot a line diagram
|
||||
|
||||
if (ulLastNumMacs != macs.size()) {
|
||||
if (ulLastNumMacs != count_from_libpax.pax) {
|
||||
|
||||
// next count cycle?
|
||||
if (macs.size() == 0) {
|
||||
if (count_from_libpax.pax == 0) {
|
||||
|
||||
// matrix full? then scroll left 1 dot, else increment column
|
||||
if (col < (LED_MATRIX_WIDTH - 1))
|
||||
@ -101,7 +104,7 @@ void refreshTheMatrixDisplay(bool nextPage) {
|
||||
matrix.drawPoint(col, row, 0); // clear current dot
|
||||
|
||||
// scale and set new dot
|
||||
ulLastNumMacs = macs.size();
|
||||
ulLastNumMacs = count_from_libpax.pax;
|
||||
level = ulLastNumMacs / LINE_DIAGRAM_DIVIDER;
|
||||
row = level <= LED_MATRIX_HEIGHT
|
||||
? LED_MATRIX_HEIGHT - 1 - level % LED_MATRIX_HEIGHT
|
||||
@ -113,13 +116,11 @@ void refreshTheMatrixDisplay(bool nextPage) {
|
||||
|
||||
case 1:
|
||||
|
||||
const time_t t = myTZ.toLocal(now());
|
||||
const time_t t = now();
|
||||
if (ulLastTime != t) {
|
||||
ulLastTime = t;
|
||||
matrix.clear();
|
||||
snprintf(buff, sizeof(buff), "%02d:%02d:%02d", hour(t), minute(t),
|
||||
second(t));
|
||||
DrawNumber(String(buff));
|
||||
DrawNumber(myTZ.dateTime("H:i:s").c_str());
|
||||
}
|
||||
break;
|
||||
|
||||
|
22
src/libpax_helpers.cpp
Normal file
22
src/libpax_helpers.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include "libpax_helpers.h"
|
||||
|
||||
// Local logging tag
|
||||
static const char TAG[] = __FILE__;
|
||||
|
||||
// libpax payload
|
||||
struct count_payload_t count_from_libpax;
|
||||
uint16_t volatile libpax_macs_ble, libpax_macs_wifi;
|
||||
|
||||
void process_count(void) {
|
||||
ESP_LOGD(TAG, "pax: %d / %d / %d", count_from_libpax.pax,
|
||||
count_from_libpax.wifi_count, count_from_libpax.ble_count);
|
||||
libpax_macs_ble = count_from_libpax.ble_count;
|
||||
libpax_macs_wifi = count_from_libpax.wifi_count;
|
||||
setSendIRQ();
|
||||
}
|
||||
|
||||
void init_libpax(void) {
|
||||
libpax_counter_init(process_count, &count_from_libpax,
|
||||
cfg.sendcycle * 2 * 1000, cfg.countermode);
|
||||
libpax_counter_start();
|
||||
}
|
@ -21,6 +21,7 @@ RTC_DATA_ATTR lmic_t RTC_LMIC;
|
||||
|
||||
static QueueHandle_t LoraSendQueue;
|
||||
TaskHandle_t lmicTask = NULL, lorasendTask = NULL;
|
||||
char lmic_event_msg[LMIC_EVENTMSG_LEN]; // display buffer for LMIC event message
|
||||
|
||||
class MyHalConfig_t : public Arduino_LMIC::HalConfiguration_t {
|
||||
|
||||
@ -163,6 +164,18 @@ void os_getDevEui(u1_t *buf) {
|
||||
|
||||
#if (VERBOSE)
|
||||
|
||||
// Display a key
|
||||
void printKey(const char *name, const uint8_t *key, uint8_t len, bool lsb) {
|
||||
const uint8_t *p;
|
||||
char keystring[len + 1] = "", keybyte[3];
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
p = lsb ? key + len - i - 1 : key + i;
|
||||
snprintf(keybyte, 3, "%02X", *p);
|
||||
strncat(keystring, keybyte, 2);
|
||||
}
|
||||
ESP_LOGI(TAG, "%s: %s", name, keystring);
|
||||
}
|
||||
|
||||
// Display OTAA keys
|
||||
void showLoraKeys(void) {
|
||||
// LMIC may not have used callback to fill
|
||||
|
198
src/macsniff.cpp
198
src/macsniff.cpp
@ -1,198 +0,0 @@
|
||||
|
||||
// Basic Config
|
||||
#include "globals.h"
|
||||
#include "macsniff.h"
|
||||
|
||||
// Local logging tag
|
||||
static const char TAG[] = __FILE__;
|
||||
|
||||
static QueueHandle_t MacQueue;
|
||||
TaskHandle_t macProcessTask;
|
||||
|
||||
static uint32_t salt = renew_salt();
|
||||
|
||||
uint32_t renew_salt(void) {
|
||||
salt = esp_random();
|
||||
ESP_LOGV(TAG, "new salt = %04X", salt);
|
||||
return salt;
|
||||
}
|
||||
|
||||
int8_t isBeacon(uint64_t mac) {
|
||||
it = std::find(beacons.begin(), beacons.end(), mac);
|
||||
if (it != beacons.end())
|
||||
return std::distance(beacons.begin(), it);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Display a key
|
||||
void printKey(const char *name, const uint8_t *key, uint8_t len, bool lsb) {
|
||||
const uint8_t *p;
|
||||
char keystring[len + 1] = "", keybyte[3];
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
p = lsb ? key + len - i - 1 : key + i;
|
||||
snprintf(keybyte, 3, "%02X", *p);
|
||||
strncat(keystring, keybyte, 2);
|
||||
}
|
||||
ESP_LOGI(TAG, "%s: %s", name, keystring);
|
||||
}
|
||||
|
||||
uint64_t macConvert(uint8_t *paddr) {
|
||||
uint64_t *mac;
|
||||
mac = (uint64_t *)paddr;
|
||||
return (__builtin_bswap64(*mac) >> 16);
|
||||
}
|
||||
|
||||
esp_err_t macQueueInit() {
|
||||
_ASSERT(MAC_QUEUE_SIZE > 0);
|
||||
MacQueue = xQueueCreate(MAC_QUEUE_SIZE, sizeof(MacBuffer_t));
|
||||
if (MacQueue == 0) {
|
||||
ESP_LOGE(TAG, "Could not create MAC processing queue. Aborting.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGI(TAG, "MAC processing queue created, size %d Bytes",
|
||||
MAC_QUEUE_SIZE * sizeof(MacBuffer_t));
|
||||
|
||||
xTaskCreatePinnedToCore(mac_process, // task function
|
||||
"mac_process", // name of task
|
||||
3072, // stack size of task
|
||||
(void *)1, // parameter of the task
|
||||
1, // priority of the task
|
||||
&macProcessTask, // task handle
|
||||
1); // CPU core
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// sniffed MAC processing task
|
||||
void mac_process(void *pvParameters) {
|
||||
_ASSERT((uint32_t)pvParameters == 1); // FreeRTOS check
|
||||
|
||||
MacBuffer_t MacBuffer;
|
||||
|
||||
while (1) {
|
||||
|
||||
// fetch next or wait for incoming MAC from sniffing queue
|
||||
if (xQueueReceive(MacQueue, &MacBuffer, portMAX_DELAY) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!");
|
||||
continue;
|
||||
}
|
||||
|
||||
// update traffic indicator
|
||||
rf_load = uxQueueMessagesWaiting(MacQueue);
|
||||
// process fetched mac
|
||||
mac_analyze(MacBuffer);
|
||||
}
|
||||
delay(2); // yield to CPU
|
||||
}
|
||||
|
||||
// enqueue message in MAC processing queue
|
||||
void IRAM_ATTR mac_add(uint8_t *paddr, int8_t rssi, snifftype_t sniff_type) {
|
||||
|
||||
MacBuffer_t MacBuffer;
|
||||
|
||||
MacBuffer.rssi = rssi;
|
||||
MacBuffer.sniff_type = sniff_type;
|
||||
memcpy(MacBuffer.mac, paddr, 6);
|
||||
|
||||
if (xQueueSendToBackFromISR(MacQueue, (void *)&MacBuffer, (TickType_t)0) !=
|
||||
pdPASS)
|
||||
ESP_LOGW(TAG, "Dense radio traffic, packet lost!");
|
||||
}
|
||||
|
||||
uint16_t mac_analyze(MacBuffer_t MacBuffer) {
|
||||
|
||||
uint32_t *mac; // pointer to shortened 4 byte MAC
|
||||
uint32_t saltedmac;
|
||||
uint16_t hashedmac;
|
||||
|
||||
if ((cfg.rssilimit) &&
|
||||
(MacBuffer.rssi < cfg.rssilimit)) { // rssi is negative value
|
||||
ESP_LOGI(TAG, "%s RSSI %d -> ignoring (limit: %d)",
|
||||
(MacBuffer.sniff_type == MAC_SNIFF_WIFI) ? "WIFI" : "BLTH",
|
||||
MacBuffer.rssi, cfg.rssilimit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// in beacon monitor mode check if seen MAC is a known beacon
|
||||
if (cfg.monitormode) {
|
||||
int8_t beaconID = isBeacon(macConvert(MacBuffer.mac));
|
||||
if (beaconID >= 0) {
|
||||
ESP_LOGI(TAG, "Beacon ID#%d detected", beaconID);
|
||||
blink_LED(COLOR_WHITE, 2000);
|
||||
payload.reset();
|
||||
payload.addAlarm(MacBuffer.rssi, beaconID);
|
||||
SendPayload(BEACONPORT);
|
||||
}
|
||||
};
|
||||
|
||||
// only last 3 MAC Address bytes are used for MAC address anonymization
|
||||
// but since it's uint32 we take 4 bytes to avoid 1st value to be 0.
|
||||
// this gets MAC in msb (= reverse) order, but doesn't matter for hashing it.
|
||||
mac = (uint32_t *)(MacBuffer.mac + 2);
|
||||
|
||||
// salt and hash MAC, and if new unique one, store identifier in container
|
||||
// and increment counter on display
|
||||
// https://en.wikipedia.org/wiki/MAC_Address_Anonymization
|
||||
|
||||
// reversed 4 byte MAC added to current salt
|
||||
saltedmac = *mac + salt;
|
||||
|
||||
// hashed 4 byte MAC
|
||||
// to save RAM, we use only lower 2 bytes of hash, since collisions don't
|
||||
// matter in our use case
|
||||
hashedmac = myhash((const char *)&saltedmac, 4);
|
||||
|
||||
auto newmac = macs.insert(hashedmac); // add hashed MAC, if new unique
|
||||
bool added =
|
||||
newmac.second ? true : false; // true if hashed MAC is unique in container
|
||||
|
||||
// Count only if MAC was not yet seen
|
||||
if (added) {
|
||||
|
||||
switch (MacBuffer.sniff_type) {
|
||||
|
||||
case MAC_SNIFF_WIFI:
|
||||
macs_wifi++; // increment Wifi MACs counter
|
||||
blink_LED(COLOR_GREEN, 50);
|
||||
break;
|
||||
|
||||
case MAC_SNIFF_BLE:
|
||||
macs_ble++; // increment BLE Macs counter
|
||||
blink_LED(COLOR_MAGENTA, 50);
|
||||
break;
|
||||
#if (COUNT_ENS)
|
||||
case MAC_SNIFF_BLE_ENS:
|
||||
macs_ble++; // increment BLE Macs counter
|
||||
cwa_mac_add(hashedmac); // process ENS beacon
|
||||
blink_LED(COLOR_WHITE, 50);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
|
||||
} // switch
|
||||
} // added
|
||||
|
||||
// Log scan result
|
||||
ESP_LOGV(TAG,
|
||||
"%s %s RSSI %ddBi -> MAC %0x:%0x:%0x:%0x:%0x:%0x -> salted %04X"
|
||||
" -> hashed %04X -> WiFi:%d "
|
||||
"BLTH:%d "
|
||||
#if (COUNT_ENS)
|
||||
"(CWA:%d)"
|
||||
#endif
|
||||
"-> %d Bytes left",
|
||||
added ? "new " : "known",
|
||||
MacBuffer.sniff_type == MAC_SNIFF_WIFI ? "WiFi" : "BLTH",
|
||||
MacBuffer.rssi, MacBuffer.mac[0], MacBuffer.mac[1], MacBuffer.mac[2],
|
||||
MacBuffer.mac[3], MacBuffer.mac[4], MacBuffer.mac[5], saltedmac,
|
||||
hashedmac, macs_wifi, macs_ble,
|
||||
#if (COUNT_ENS)
|
||||
cwa_report(),
|
||||
#endif
|
||||
getFreeRAM());
|
||||
|
||||
// if an unknown Wifi or BLE mac was counted, return hash of this mac, else 0
|
||||
return (added ? hashedmac : 0);
|
||||
}
|
122
src/main.cpp
122
src/main.cpp
@ -38,7 +38,6 @@ timesync_proc 1 3 processes realtime time sync requests
|
||||
irqhandler 1 2 cyclic tasks (i.e. displayrefresh) triggered by timers
|
||||
gpsloop 1 1 reads data from GPS via serial or i2c
|
||||
lorasendtask 1 1 feeds data from lora sendqueue to lmcic
|
||||
macprocess 1 1 MAC analyzer loop
|
||||
rmcd_process 1 1 Remote command interpreter loop
|
||||
IDLE 1 0 ESP32 arduino scheduler -> runs wifi channel rotator
|
||||
|
||||
@ -66,11 +65,11 @@ MatrixDisplayIRQ-> esp32 timer 3
|
||||
ButtonIRQ -> external GPIO
|
||||
PMUIRQ -> PMU chip GPIO
|
||||
|
||||
fired by software (Ticker.h)
|
||||
TIMESYNC_IRQ -> setTimeSyncIRQ()
|
||||
CYCLIC_IRQ -> setCyclicIRQ()
|
||||
SENDCYCLE_IRQ -> setSendIRQ()
|
||||
BME_IRQ -> setBMEIRQ()
|
||||
fired by software
|
||||
TIMESYNC_IRQ -> setTimeSyncIRQ() -> Ticker.h
|
||||
CYCLIC_IRQ -> setCyclicIRQ() -> Ticker.h
|
||||
SENDCYCLE_IRQ -> setSendIRQ() -> xTimer
|
||||
BME_IRQ -> setBMEIRQ() -> Ticker.h
|
||||
|
||||
ClockTask (Core 1), see timekeeper.cpp
|
||||
|
||||
@ -87,35 +86,11 @@ triggers pps 1 sec impulse
|
||||
// Basic Config
|
||||
#include "main.h"
|
||||
|
||||
configData_t cfg; // struct holds current device configuration
|
||||
char lmic_event_msg[LMIC_EVENTMSG_LEN]; // display buffer for LMIC event message
|
||||
uint8_t batt_level = 0; // display value
|
||||
uint8_t volatile channel = WIFI_CHANNEL_MIN; // channel rotation counter
|
||||
uint8_t volatile rf_load = 0; // RF traffic indicator
|
||||
uint16_t volatile macs_wifi = 0, macs_ble = 0; // globals for display
|
||||
|
||||
hw_timer_t *ppsIRQ = NULL, *displayIRQ = NULL, *matrixDisplayIRQ = NULL;
|
||||
|
||||
TaskHandle_t irqHandlerTask = NULL, ClockTask = NULL;
|
||||
SemaphoreHandle_t I2Caccess;
|
||||
bool volatile TimePulseTick = false;
|
||||
timesource_t timeSource = _unsynced;
|
||||
|
||||
// container holding unique MAC address hashes with Memory Alloctor using PSRAM,
|
||||
// if present
|
||||
DRAM_ATTR std::set<uint16_t, std::less<uint16_t>, Mallocator<uint16_t>> macs;
|
||||
|
||||
// initialize payload encoder
|
||||
PayloadConvert payload(PAYLOAD_BUFFER_SIZE);
|
||||
|
||||
// set Time Zone for user setting from paxcounter.conf
|
||||
TimeChangeRule myDST = DAYLIGHT_TIME;
|
||||
TimeChangeRule mySTD = STANDARD_TIME;
|
||||
Timezone myTZ(myDST, mySTD);
|
||||
|
||||
// local Tag for logging
|
||||
static const char TAG[] = __FILE__;
|
||||
|
||||
char clientId[20] = {0}; // unique ClientID
|
||||
|
||||
void setup() {
|
||||
|
||||
char features[100] = "";
|
||||
@ -143,6 +118,19 @@ void setup() {
|
||||
// load device configuration from NVRAM and set runmode
|
||||
do_after_reset();
|
||||
|
||||
// set time zone to user value from paxcounter.conf
|
||||
#ifdef TIME_SYNC_TIMEZONE
|
||||
myTZ.setPosix(TIME_SYNC_TIMEZONE);
|
||||
#endif
|
||||
|
||||
// hash 6 byte device MAC to 4 byte clientID
|
||||
uint8_t mac[6];
|
||||
esp_eth_get_mac(mac);
|
||||
const uint32_t hashedmac = myhash((const char *)mac, 6);
|
||||
snprintf(clientId, 20, "paxcounter_%08x", hashedmac);
|
||||
ESP_LOGI(TAG, "Starting %s v%s (runmode=%d / restarts=%d)", clientId,
|
||||
PROGVERSION, RTC_runmode, RTC_restarts);
|
||||
|
||||
// print chip information on startup if in verbose mode after coldstart
|
||||
#if (VERBOSE)
|
||||
|
||||
@ -298,31 +286,43 @@ void setup() {
|
||||
if (RTC_runmode == RUNMODE_MAINTENANCE)
|
||||
start_boot_menu();
|
||||
|
||||
// start mac processing task
|
||||
ESP_LOGI(TAG, "Starting MAC processor...");
|
||||
macQueueInit();
|
||||
#if ((WIFICOUNTER) || (BLECOUNTER))
|
||||
// use libpax timer to trigger cyclic senddata
|
||||
ESP_LOGI(TAG, "Starting libpax...");
|
||||
struct libpax_config_t configuration;
|
||||
libpax_default_config(&configuration);
|
||||
|
||||
// configure WIFI sniffing
|
||||
configuration.wificounter = cfg.wifiscan;
|
||||
configuration.wifi_channel_map = WIFI_CHANNEL_ALL;
|
||||
configuration.wifi_channel_switch_interval = cfg.wifichancycle;
|
||||
configuration.wifi_rssi_threshold = cfg.rssilimit;
|
||||
ESP_LOGI(TAG, "WIFISCAN: %s", cfg.wifiscan ? "on" : "off");
|
||||
|
||||
// configure BLE sniffing
|
||||
configuration.blecounter = cfg.blescan;
|
||||
configuration.blescantime = cfg.blescantime;
|
||||
ESP_LOGI(TAG, "BLESCAN: %s", cfg.blescan ? "on" : "off");
|
||||
|
||||
int config_update = libpax_update_config(&configuration);
|
||||
if (config_update != 0) {
|
||||
ESP_LOGE(TAG, "Error in libpax configuration.");
|
||||
} else {
|
||||
init_libpax();
|
||||
}
|
||||
#else
|
||||
// use stand alone timer to trigger cyclic senddata
|
||||
initSendDataTimer(cfg.sendcycle * 2);
|
||||
#endif
|
||||
|
||||
#if (BLECOUNTER)
|
||||
strcat_P(features, " BLE");
|
||||
#endif
|
||||
|
||||
// start rcommand processing task
|
||||
ESP_LOGI(TAG, "Starting rcommand interpreter...");
|
||||
rcmd_init();
|
||||
|
||||
// start BLE scan callback if BLE function is enabled in NVRAM configuration
|
||||
// or remove bluetooth stack from RAM, if option bluetooth is not compiled
|
||||
#if (BLECOUNTER)
|
||||
strcat_P(features, " BLE");
|
||||
if (cfg.blescan) {
|
||||
ESP_LOGI(TAG, "Starting Bluetooth...");
|
||||
start_BLEscan();
|
||||
} else
|
||||
btStop();
|
||||
#else
|
||||
// remove bluetooth stack to gain more free memory
|
||||
btStop();
|
||||
esp_bt_mem_release(ESP_BT_MODE_BTDM);
|
||||
esp_coex_preference_set(
|
||||
ESP_COEX_PREFER_WIFI); // configure Wifi/BT coexist lib
|
||||
#endif
|
||||
|
||||
// initialize gps
|
||||
#if (HAS_GPS)
|
||||
strcat_P(features, " GPS");
|
||||
@ -389,10 +389,6 @@ void setup() {
|
||||
strcat_P(features, " SDS");
|
||||
#endif
|
||||
|
||||
#if (MACFILTER)
|
||||
strcat_P(features, " FILTER");
|
||||
#endif
|
||||
|
||||
// initialize matrix display
|
||||
#ifdef HAS_MATRIX_DISPLAY
|
||||
strcat_P(features, " LED_MATRIX");
|
||||
@ -427,24 +423,11 @@ void setup() {
|
||||
|
||||
#if (WIFICOUNTER)
|
||||
strcat_P(features, " WIFI");
|
||||
// install wifi driver in RAM and start channel hopping
|
||||
wifi_sniffer_init();
|
||||
// start wifi sniffing, if enabled
|
||||
if (cfg.wifiscan) {
|
||||
ESP_LOGI(TAG, "Starting Wifi...");
|
||||
switch_wifi_sniffer(1);
|
||||
} else
|
||||
switch_wifi_sniffer(0);
|
||||
#else
|
||||
// remove wifi driver from RAM, if option wifi not compiled
|
||||
esp_wifi_deinit();
|
||||
#endif
|
||||
|
||||
// initialize salt value using esp_random() called by random() in
|
||||
// arduino-esp32 core. Note: do this *after* wifi has started, since
|
||||
// function gets it's seed from RF noise
|
||||
reset_counters();
|
||||
|
||||
// start state machine
|
||||
ESP_LOGI(TAG, "Starting Interrupt Handler...");
|
||||
xTaskCreatePinnedToCore(irqHandler, // task function
|
||||
@ -510,7 +493,6 @@ void setup() {
|
||||
#endif // HAS_BUTTON
|
||||
|
||||
// cyclic function interrupts
|
||||
sendTimer.attach(cfg.sendcycle * 2, setSendIRQ);
|
||||
cyclicTimer.attach(HOMECYCLE, setCyclicIRQ);
|
||||
|
||||
// only if we have a timesource we do timesync
|
||||
|
@ -18,11 +18,10 @@ static const char TAG[] = __FILE__;
|
||||
void MOBALINE_Pulse(time_t t, uint8_t const *DCFpulse) {
|
||||
|
||||
TickType_t startTime = xTaskGetTickCount();
|
||||
uint8_t sec = second(t);
|
||||
uint8_t sec = myTZ.second(t);
|
||||
|
||||
t = myTZ.toLocal(now());
|
||||
ESP_LOGD(TAG, "[%02d:%02d:%02d.%03d] MOBALINE bit %d", hour(t), minute(t),
|
||||
second(t), millisecond(), sec);
|
||||
ESP_LOGD(TAG, "[%s] MOBALINE sec: %d", myTZ.dateTime("H:i:s.v").c_str(),
|
||||
sec);
|
||||
|
||||
// induce 3 pulses
|
||||
for (uint8_t pulse = 0; pulse <= 3; pulse++) {
|
||||
@ -68,13 +67,13 @@ uint8_t *IRAM_ATTR MOBALINE_Frame(time_t const tt) {
|
||||
|
||||
static uint8_t DCFpulse[DCF77_FRAME_SIZE + 1];
|
||||
|
||||
time_t t = myTZ.toLocal(tt); // convert to local time
|
||||
time_t t = myTZ.tzTime(tt); // convert to local time
|
||||
|
||||
// ENCODE HEAD (bit 0))
|
||||
DCFpulse[0] = dcf_Z; // not yet implemented
|
||||
|
||||
// ENCODE DAYLIGHTSAVING (bit 1)
|
||||
DCFpulse[1] = myTZ.locIsDST(t) ? dcf_1 : dcf_0;
|
||||
DCFpulse[1] = myTZ.isDST(t) ? dcf_1 : dcf_0;
|
||||
|
||||
// ENCODE DATE (bits 2..20)
|
||||
dec2bcd(false, year(t) - 2000, 2, 9, DCFpulse);
|
||||
|
@ -22,6 +22,7 @@ esp_err_t mqtt_init(void) {
|
||||
|
||||
// setup network connection and MQTT client
|
||||
ETH.begin();
|
||||
ETH.setHostname(clientId);
|
||||
mqttClient.begin(MQTT_SERVER, MQTT_PORT, netClient);
|
||||
mqttClient.setKeepAlive(MQTT_KEEPALIVE);
|
||||
mqttClient.onMessageAdvanced(mqtt_callback);
|
||||
@ -42,14 +43,8 @@ esp_err_t mqtt_init(void) {
|
||||
}
|
||||
|
||||
int mqtt_connect(const char *my_host, const uint16_t my_port) {
|
||||
IPAddress mqtt_server_ip;
|
||||
uint8_t mac[6];
|
||||
char clientId[20];
|
||||
|
||||
// hash 6 byte MAC to 4 byte hash
|
||||
esp_eth_get_mac(mac);
|
||||
const uint32_t hashedmac = myhash((const char *)mac, 6);
|
||||
snprintf(clientId, 20, "paxcounter_%08x", hashedmac);
|
||||
IPAddress mqtt_server_ip;
|
||||
|
||||
ESP_LOGI(TAG, "MQTT name is %s", MQTT_CLIENTNAME);
|
||||
|
||||
|
39
src/ota.cpp
39
src/ota.cpp
@ -21,8 +21,9 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
const BintrayClient bintray(BINTRAY_USER, BINTRAY_REPO, BINTRAY_PACKAGE);
|
||||
// usage of bintray: see https://github.com/r0oland/bintray-secure-ota
|
||||
const BintrayClient paxexpress(PAXEXPRESS_USER, PAXEXPRESS_REPO,
|
||||
PAXEXPRESS_PACKAGE);
|
||||
// usage of paxexpress: see https://github.com/paxexpress/docs
|
||||
|
||||
// Connection port (HTTPS)
|
||||
const int port = 443;
|
||||
@ -41,6 +42,8 @@ inline String getHeaderValue(String header, String headerName) {
|
||||
|
||||
void start_ota_update() {
|
||||
|
||||
const char *host = clientId;
|
||||
|
||||
switch_LED(LED_ON);
|
||||
|
||||
// init display
|
||||
@ -65,8 +68,28 @@ void start_ota_update() {
|
||||
ESP_LOGI(TAG, "Starting Wifi OTA update");
|
||||
ota_display(1, "**", WIFI_SSID);
|
||||
|
||||
WiFi.disconnect(true);
|
||||
WiFi.config(INADDR_NONE, INADDR_NONE,
|
||||
INADDR_NONE); // call is only a workaround for bug in WiFi class
|
||||
// see https://github.com/espressif/arduino-esp32/issues/806
|
||||
WiFi.setHostname(host);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin();
|
||||
|
||||
// Connect to WiFi network
|
||||
// workaround applied here to bypass WIFI_AUTH failure
|
||||
// see https://github.com/espressif/arduino-esp32/issues/2501
|
||||
|
||||
// 1st try
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
||||
while (WiFi.status() == WL_DISCONNECTED) {
|
||||
delay(500);
|
||||
}
|
||||
// 2nd try
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
||||
delay(500);
|
||||
}
|
||||
|
||||
uint8_t i = WIFI_MAX_TRY;
|
||||
int ret = 1; // 0 = finished, 1 = retry, -1 = abort
|
||||
@ -74,7 +97,6 @@ void start_ota_update() {
|
||||
while (i--) {
|
||||
ESP_LOGI(TAG, "Trying to connect to %s, attempt %u of %u", WIFI_SSID,
|
||||
WIFI_MAX_TRY - i, WIFI_MAX_TRY);
|
||||
delay(10000); // wait for stable connect
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
// we now have wifi connection and try to do an OTA over wifi update
|
||||
ESP_LOGI(TAG, "Connected to %s", WIFI_SSID);
|
||||
@ -89,6 +111,7 @@ void start_ota_update() {
|
||||
if (WiFi.status() == WL_CONNECTED)
|
||||
goto end; // OTA update finished or OTA max attemps reached
|
||||
}
|
||||
delay(10000); // wait for stable connect
|
||||
WiFi.reconnect();
|
||||
}
|
||||
|
||||
@ -121,7 +144,7 @@ int do_ota_update() {
|
||||
if (WiFi.status() != WL_CONNECTED)
|
||||
return 1;
|
||||
|
||||
const String latest = bintray.getLatestVersion();
|
||||
const String latest = paxexpress.getLatestVersion();
|
||||
|
||||
if (latest.length() == 0) {
|
||||
ESP_LOGI(TAG, "Could not fetch info on latest firmware");
|
||||
@ -138,21 +161,20 @@ int do_ota_update() {
|
||||
ota_display(3, "**", "");
|
||||
if (WiFi.status() != WL_CONNECTED)
|
||||
return 1;
|
||||
String firmwarePath = bintray.getBinaryPath(latest);
|
||||
String firmwarePath = paxexpress.getBinaryPath(latest);
|
||||
if (!firmwarePath.endsWith(".bin")) {
|
||||
ESP_LOGI(TAG, "Unsupported binary format");
|
||||
ota_display(3, " E", "file type error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
String currentHost = bintray.getStorageHost();
|
||||
String currentHost = paxexpress.getStorageHost();
|
||||
String prevHost = currentHost;
|
||||
|
||||
WiFiClientSecure client;
|
||||
|
||||
client.setCACert(bintray.getCertificate(currentHost));
|
||||
client.setTimeout(RESPONSE_TIMEOUT_MS);
|
||||
|
||||
client.setInsecure();
|
||||
if (!client.connect(currentHost.c_str(), port)) {
|
||||
ESP_LOGI(TAG, "Cannot connect to %s", currentHost.c_str());
|
||||
ota_display(3, " E", "connection lost");
|
||||
@ -162,7 +184,6 @@ int do_ota_update() {
|
||||
while (redirect) {
|
||||
if (currentHost != prevHost) {
|
||||
client.stop();
|
||||
client.setCACert(bintray.getCertificate(currentHost));
|
||||
if (!client.connect(currentHost.c_str(), port)) {
|
||||
ESP_LOGI(TAG, "Redirect detected, but cannot connect to %s",
|
||||
currentHost.c_str());
|
||||
|
@ -2,7 +2,7 @@
|
||||
OTA_WIFI_SSID = MyHomeWifi
|
||||
OTA_WIFI_PASS = FooBar42!
|
||||
|
||||
[bintray]
|
||||
BINTRAY_USER = MyBintrayUser
|
||||
BINTRAY_REPO = MyBintrayRepo
|
||||
BINTRAY_API_TOKEN = 3894a7a51d70c6523c1b7479261c34845ebf7878
|
||||
[paxexpress]
|
||||
PAXEXPRESS_USER = MyPaxexpressUser
|
||||
PAXEXPRESS_REPO = MyPaxexpressRepo
|
||||
PAXEXPRESS_API_TOKEN = 3894a7a51d70c6523c1b7479261c34845ebf7878
|
@ -14,15 +14,13 @@
|
||||
|
||||
// Payload send cycle and encoding
|
||||
#define SENDCYCLE 30 // payload send cycle [seconds/2], 0 .. 255
|
||||
#define SLEEPCYCLE 0 // sleep time after a send cycle [seconds/2], 0 .. 255; 0 means no sleep [default = 0]
|
||||
#define SLEEPCYCLE 0 // sleep time after a send cycle [seconds/10], 0 .. 65535; 0 means no sleep [default = 0]
|
||||
#define PAYLOAD_ENCODER 2 // payload encoder: 1=Plain, 2=Packed, 3=Cayenne LPP dynamic, 4=Cayenne LPP packed
|
||||
#define COUNTERMODE 0 // 0=cyclic, 1=cumulative, 2=cyclic confirmed
|
||||
|
||||
// MAC sniffing parameters
|
||||
#define MACFILTER 1 // set to 0 if you want to scan all devices, 1 to scan only devices with random MACs (aka smartphones) [default = 1]
|
||||
#define BLECOUNTER 1 // set to 0 if you do not want to install the BLE sniffer
|
||||
#define WIFICOUNTER 1 // set to 0 if you do not want to install the WIFI sniffer
|
||||
#define MAC_QUEUE_SIZE 50 // size of MAC processing buffer (number of MACs) [default = 50]
|
||||
|
||||
// BLE scan parameters
|
||||
#define BLESCANTIME 0 // [seconds] scan duration, 0 means infinite [default], see note below
|
||||
@ -30,7 +28,7 @@
|
||||
#define BLESCANINTERVAL 80 // [illiseconds] scan interval, see below, 3 .. 10240, default 80ms = 100% duty cycle
|
||||
|
||||
// Corona Exposure Notification Service(ENS) counter
|
||||
#define COUNT_ENS 1 // count found number of devices which advertise Exposure Notification Service
|
||||
#define COUNT_ENS 0 // count found number of devices which advertise Exposure Notification Service
|
||||
// set to 1 if you want to enable this function [default=0]
|
||||
|
||||
/* Note: guide for setting bluetooth parameters
|
||||
@ -90,10 +88,7 @@
|
||||
#define TIME_SYNC_CYCLE 60 // delay between two time samples [seconds]
|
||||
#define TIME_SYNC_TIMEOUT 400 // timeout waiting for timeserver answer [seconds]
|
||||
#define TIME_SYNC_COMPILEDATE 0 // set to 1 to use compile date to initialize RTC after power outage [default = 0]
|
||||
|
||||
// time zone, see https://github.com/JChristensen/Timezone/blob/master/examples/WorldClock/WorldClock.ino
|
||||
#define DAYLIGHT_TIME {"CEST", Last, Sun, Mar, 2, 120} // Central European Summer Time
|
||||
#define STANDARD_TIME {"CET ", Last, Sun, Oct, 3, 60} // Central European Standard Time
|
||||
#define TIME_SYNC_TIMEZONE "CET-1CEST,M3.4.0/2,M10.4.0/3" // Timezone in POSIX format (example shows Germany/Berlin)
|
||||
|
||||
// Ports on which the device sends and listenes on LoRaWAN and SPI
|
||||
#define COUNTERPORT 1 // counts
|
||||
@ -103,7 +98,7 @@
|
||||
#define CONFIGPORT 3 // config query results
|
||||
#define GPSPORT 4 // gps - NOTE: set to 1 to send combined GPS+COUNTERPORT payload
|
||||
#define BUTTONPORT 5 // button pressed signal
|
||||
#define BEACONPORT 6 // beacon alarms
|
||||
#define RESERVEDPORT 6 // reserved (unused)
|
||||
#define BMEPORT 7 // BME680 sensor
|
||||
#define BATTPORT 8 // battery voltage
|
||||
#define TIMEPORT 9 // time query and response
|
||||
|
@ -1,6 +1,9 @@
|
||||
#include "globals.h"
|
||||
#include "payload.h"
|
||||
|
||||
// initialize payload encoder
|
||||
PayloadConvert payload(PAYLOAD_BUFFER_SIZE);
|
||||
|
||||
PayloadConvert::PayloadConvert(uint8_t size) {
|
||||
buffer = (uint8_t *)malloc(size);
|
||||
cursor = 0;
|
||||
@ -25,11 +28,6 @@ void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) {
|
||||
buffer[cursor++] = lowByte(value);
|
||||
}
|
||||
|
||||
void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) {
|
||||
buffer[cursor++] = rssi;
|
||||
buffer[cursor++] = msg;
|
||||
}
|
||||
|
||||
void PayloadConvert::addVoltage(uint16_t value) {
|
||||
buffer[cursor++] = highByte(value);
|
||||
buffer[cursor++] = lowByte(value);
|
||||
@ -49,16 +47,17 @@ void PayloadConvert::addConfig(configData_t value) {
|
||||
buffer[cursor++] = value.blescantime;
|
||||
buffer[cursor++] = value.blescan;
|
||||
buffer[cursor++] = value.wifiant;
|
||||
buffer[cursor++] = value.macfilter;
|
||||
buffer[cursor++] = 0; // reserved
|
||||
buffer[cursor++] = value.rgblum;
|
||||
buffer[cursor++] = value.payloadmask;
|
||||
buffer[cursor++] = value.monitormode;
|
||||
buffer[cursor++] = 0; // reserved
|
||||
memcpy(buffer + cursor, value.version, 10);
|
||||
cursor += 10;
|
||||
}
|
||||
|
||||
void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float cputemp,
|
||||
uint32_t mem, uint8_t reset0, uint32_t restarts) {
|
||||
uint32_t mem, uint8_t reset0,
|
||||
uint32_t restarts) {
|
||||
|
||||
buffer[cursor++] = highByte(voltage);
|
||||
buffer[cursor++] = lowByte(voltage);
|
||||
@ -164,11 +163,6 @@ void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) {
|
||||
writeUint16(value);
|
||||
}
|
||||
|
||||
void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) {
|
||||
writeUint8(rssi);
|
||||
writeUint8(msg);
|
||||
}
|
||||
|
||||
void PayloadConvert::addVoltage(uint16_t value) { writeUint16(value); }
|
||||
|
||||
void PayloadConvert::addConfig(configData_t value) {
|
||||
@ -181,14 +175,14 @@ void PayloadConvert::addConfig(configData_t value) {
|
||||
writeUint8(value.rgblum);
|
||||
writeBitmap(value.adrmode ? true : false, value.screensaver ? true : false,
|
||||
value.screenon ? true : false, value.countermode ? true : false,
|
||||
value.blescan ? true : false, value.wifiant ? true : false,
|
||||
value.macfilter ? true : false, value.monitormode ? true : false);
|
||||
value.blescan ? true : false, value.wifiant ? true : false, 0, 0);
|
||||
writeUint8(value.payloadmask);
|
||||
writeVersion(value.version);
|
||||
}
|
||||
|
||||
void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float cputemp,
|
||||
uint32_t mem, uint8_t reset0, uint32_t restarts) {
|
||||
uint32_t mem, uint8_t reset0,
|
||||
uint32_t restarts) {
|
||||
writeUint16(voltage);
|
||||
writeUptime(uptime);
|
||||
writeUint8((byte)cputemp);
|
||||
@ -368,19 +362,6 @@ void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) {
|
||||
}
|
||||
}
|
||||
|
||||
void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) {
|
||||
#if (PAYLOAD_ENCODER == 3)
|
||||
buffer[cursor++] = LPP_ALARM_CHANNEL;
|
||||
#endif
|
||||
buffer[cursor++] = LPP_PRESENCE;
|
||||
buffer[cursor++] = msg;
|
||||
#if (PAYLOAD_ENCODER == 3)
|
||||
buffer[cursor++] = LPP_MSG_CHANNEL;
|
||||
#endif
|
||||
buffer[cursor++] = LPP_ANALOG_INPUT;
|
||||
buffer[cursor++] = rssi;
|
||||
}
|
||||
|
||||
void PayloadConvert::addVoltage(uint16_t value) {
|
||||
uint16_t volt = value / 10;
|
||||
#if (PAYLOAD_ENCODER == 3)
|
||||
@ -400,7 +381,8 @@ void PayloadConvert::addConfig(configData_t value) {
|
||||
}
|
||||
|
||||
void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float celsius,
|
||||
uint32_t mem, uint8_t reset0, uint32_t restarts) {
|
||||
uint32_t mem, uint8_t reset0,
|
||||
uint32_t restarts) {
|
||||
uint16_t temp = celsius * 10;
|
||||
uint16_t volt = voltage / 10;
|
||||
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
|
||||
|
@ -5,6 +5,8 @@
|
||||
// Local logging tag
|
||||
static const char TAG[] = __FILE__;
|
||||
|
||||
uint8_t batt_level = 0; // display value
|
||||
|
||||
#ifdef BAT_MEASURE_ADC
|
||||
esp_adc_cal_characteristics_t *adc_characs =
|
||||
(esp_adc_cal_characteristics_t *)calloc(
|
||||
|
1045
src/rcommand.cpp
1045
src/rcommand.cpp
File diff suppressed because it is too large
Load Diff
@ -15,6 +15,7 @@ RTC_NOINIT_ATTR uint32_t RTC_restarts;
|
||||
// RTC_DATA_ATTR -> keep values after a wakeup from sleep
|
||||
RTC_DATA_ATTR struct timeval RTC_sleep_start_time;
|
||||
RTC_DATA_ATTR unsigned long long RTC_millis = 0;
|
||||
RTC_DATA_ATTR time_t RTC_time = 0;
|
||||
|
||||
timeval sleep_stop_time;
|
||||
|
||||
@ -32,7 +33,7 @@ void do_reset(bool warmstart) {
|
||||
LMIC_shutdown();
|
||||
}
|
||||
#endif
|
||||
RTC_runmode = RUNMODE_POWERCYCLE;
|
||||
reset_rtc_vars();
|
||||
ESP_LOGI(TAG, "restarting device (coldstart)");
|
||||
}
|
||||
esp_restart();
|
||||
@ -65,8 +66,12 @@ void do_after_reset(void) {
|
||||
sleep_time_ms =
|
||||
(sleep_stop_time.tv_sec - RTC_sleep_start_time.tv_sec) * 1000 +
|
||||
(sleep_stop_time.tv_usec - RTC_sleep_start_time.tv_usec) / 1000;
|
||||
ESP_LOGI(TAG, "Time spent in deep sleep: %d ms", sleep_time_ms);
|
||||
RTC_millis += sleep_time_ms; // increment system monotonic time
|
||||
ESP_LOGI(TAG, "Time spent in deep sleep: %d ms", sleep_time_ms);
|
||||
|
||||
// set time
|
||||
setMyTime(RTC_time + sleep_time_ms / 1000, sleep_time_ms % 1000, _set);
|
||||
|
||||
// set wakeup state, not if we have pending OTA update
|
||||
if (RTC_runmode == RUNMODE_SLEEP)
|
||||
RTC_runmode = RUNMODE_WAKEUP;
|
||||
@ -88,9 +93,6 @@ void do_after_reset(void) {
|
||||
RTC_restarts++;
|
||||
break;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Starting Software v%s (runmode=%d / restarts=%d)", PROGVERSION,
|
||||
RTC_runmode, RTC_restarts);
|
||||
}
|
||||
|
||||
void enter_deepsleep(const uint64_t wakeup_sec, gpio_num_t wakeup_gpio) {
|
||||
@ -100,31 +102,24 @@ void enter_deepsleep(const uint64_t wakeup_sec, gpio_num_t wakeup_gpio) {
|
||||
RTC_runmode = RUNMODE_SLEEP;
|
||||
int i;
|
||||
|
||||
// show message on display
|
||||
#ifdef HAS_DISPLAY
|
||||
dp_message("-GOING TO SLEEP-", 7, true);
|
||||
#endif
|
||||
|
||||
// validate wake up pin, if we have
|
||||
if (!GPIO_IS_VALID_GPIO(wakeup_gpio))
|
||||
wakeup_gpio = GPIO_NUM_MAX;
|
||||
|
||||
// stop further enqueuing of senddata and MAC processing
|
||||
sendTimer.detach();
|
||||
// stop further enqueuing of senddata and MAC processing
|
||||
// -> skipped, because shutting down bluedroid stack tends to crash
|
||||
// libpax_counter_stop();
|
||||
|
||||
// switch off radio and other power consuming hardware
|
||||
#if (WIFICOUNTER)
|
||||
switch_wifi_sniffer(0);
|
||||
#endif
|
||||
#if (BLECOUNTER)
|
||||
stop_BLEscan();
|
||||
btStop();
|
||||
#endif
|
||||
// switch off any power consuming hardware
|
||||
#if (HAS_SDS011)
|
||||
sds011_sleep(void);
|
||||
sds011_sleep();
|
||||
#endif
|
||||
|
||||
// stop MAC processing
|
||||
vTaskDelete(macProcessTask);
|
||||
|
||||
// halt interrupts accessing i2c bus
|
||||
mask_user_IRQ();
|
||||
|
||||
// wait a while (max 100 sec) to clear send queues
|
||||
ESP_LOGI(TAG, "Waiting until send queues are empty...");
|
||||
for (i = 100; i > 0; i--) {
|
||||
@ -133,7 +128,7 @@ void enter_deepsleep(const uint64_t wakeup_sec, gpio_num_t wakeup_gpio) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
// shutdown LMIC safely, waiting max 100 sec
|
||||
/// wait until LMIC is idle
|
||||
#if (HAS_LORA)
|
||||
ESP_LOGI(TAG, "Waiting until LMIC is idle...");
|
||||
for (i = 100; i > 0; i--) {
|
||||
@ -172,6 +167,9 @@ void enter_deepsleep(const uint64_t wakeup_sec, gpio_num_t wakeup_gpio) {
|
||||
digitalWrite(EXT_POWER_SW, EXT_POWER_OFF);
|
||||
#endif
|
||||
|
||||
// halt interrupts accessing i2c bus
|
||||
mask_user_IRQ();
|
||||
|
||||
// shutdown i2c bus
|
||||
i2c_deinit();
|
||||
|
||||
@ -192,6 +190,7 @@ void enter_deepsleep(const uint64_t wakeup_sec, gpio_num_t wakeup_gpio) {
|
||||
// time stamp sleep start time and save system monotonic time. Deep sleep.
|
||||
gettimeofday(&RTC_sleep_start_time, NULL);
|
||||
RTC_millis += millis();
|
||||
RTC_time = now();
|
||||
ESP_LOGI(TAG, "Going to sleep, good bye.");
|
||||
esp_deep_sleep_start();
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ uint8_t rtc_init(void) {
|
||||
if (!Rtc.IsDateTimeValid() || !timeIsValid(t)) {
|
||||
ESP_LOGW(TAG, "RTC has no recent time, setting to compiled time");
|
||||
Rtc.SetDateTime(
|
||||
RtcDateTime(compiledUTC() - SECS_YR_2000)); // epoch -> sec2000
|
||||
RtcDateTime(_COMPILETIME - SECS_YR_2000)); // epoch -> sec2000
|
||||
}
|
||||
#endif
|
||||
|
||||
|
166
src/senddata.cpp
166
src/senddata.cpp
@ -1,12 +1,23 @@
|
||||
// Basic Config
|
||||
#include "senddata.h"
|
||||
|
||||
Ticker sendTimer;
|
||||
|
||||
void setSendIRQ() {
|
||||
void setSendIRQ(TimerHandle_t xTimer) {
|
||||
xTaskNotify(irqHandlerTask, SENDCYCLE_IRQ, eSetBits);
|
||||
}
|
||||
|
||||
void initSendDataTimer(uint8_t sendcycle) {
|
||||
static TimerHandle_t SendDataTimer = NULL;
|
||||
|
||||
if (SendDataTimer == NULL) {
|
||||
SendDataTimer =
|
||||
xTimerCreate("SendDataTimer", pdMS_TO_TICKS(sendcycle * 1000), pdTRUE,
|
||||
(void *)0, setSendIRQ);
|
||||
xTimerStart(SendDataTimer, 0);
|
||||
} else {
|
||||
xTimerChangePeriod(SendDataTimer, pdMS_TO_TICKS(sendcycle * 1000), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// put data to send in RTos Queues used for transmit over channels Lora and SPI
|
||||
void SendPayload(uint8_t port) {
|
||||
|
||||
@ -57,7 +68,7 @@ void SendPayload(uint8_t port) {
|
||||
// write data to sdcard, if present
|
||||
#if (HAS_SDCARD)
|
||||
if (port == COUNTERPORT) {
|
||||
sdcardWriteData(macs_wifi, macs_ble
|
||||
sdcardWriteData(libpax_macs_wifi, libpax_macs_ble
|
||||
#if (COUNT_ENS)
|
||||
,
|
||||
cwa_report()
|
||||
@ -87,9 +98,12 @@ void sendData() {
|
||||
case COUNT_DATA:
|
||||
payload.reset();
|
||||
#if !(PAYLOAD_OPENSENSEBOX)
|
||||
payload.addCount(macs_wifi, MAC_SNIFF_WIFI);
|
||||
if (cfg.blescan)
|
||||
payload.addCount(macs_ble, MAC_SNIFF_BLE);
|
||||
ESP_LOGI(TAG, "Sending libpax wifi count: %d", libpax_macs_wifi);
|
||||
payload.addCount(libpax_macs_wifi, MAC_SNIFF_WIFI);
|
||||
if (cfg.blescan) {
|
||||
ESP_LOGI(TAG, "Sending libpax ble count: %d", libpax_macs_ble);
|
||||
payload.addCount(libpax_macs_ble, MAC_SNIFF_BLE);
|
||||
}
|
||||
#endif
|
||||
#if (HAS_GPS)
|
||||
if (GPSPORT == COUNTERPORT) {
|
||||
@ -102,115 +116,111 @@ void sendData() {
|
||||
}
|
||||
#endif
|
||||
#if (PAYLOAD_OPENSENSEBOX)
|
||||
payload.addCount(macs_wifi, MAC_SNIFF_WIFI);
|
||||
if (cfg.blescan)
|
||||
payload.addCount(macs_ble, MAC_SNIFF_BLE);
|
||||
ESP_LOGI(TAG, "Sending libpax wifi count: %d", libpax_macs_wifi);
|
||||
payload.addCount(libpax_macs_wifi, MAC_SNIFF_WIFI);
|
||||
if (cfg.blescan) {
|
||||
ESP_LOGI(TAG, "Sending libpax ble count: %d", libpax_macs_ble);
|
||||
payload.addCount(libpax_macs_ble, MAC_SNIFF_BLE);
|
||||
#endif
|
||||
#if (HAS_SDS011)
|
||||
sds011_store(&sds_status);
|
||||
payload.addSDS(sds_status);
|
||||
sds011_store(&sds_status);
|
||||
payload.addSDS(sds_status);
|
||||
#endif
|
||||
SendPayload(COUNTERPORT);
|
||||
// clear counter if not in cumulative counter mode
|
||||
if (cfg.countermode != 1) {
|
||||
reset_counters(); // clear macs container and reset all counters
|
||||
ESP_LOGI(TAG, "Counter cleared");
|
||||
}
|
||||
SendPayload(COUNTERPORT);
|
||||
#ifdef HAS_DISPLAY
|
||||
else
|
||||
dp_plotCurve(macs.size(), true);
|
||||
dp_plotCurve(libpax_macs_ble + libpax_macs_wifi, true);
|
||||
#endif
|
||||
break;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if (HAS_BME)
|
||||
case MEMS_DATA:
|
||||
payload.reset();
|
||||
payload.addBME(bme_status);
|
||||
SendPayload(BMEPORT);
|
||||
break;
|
||||
case MEMS_DATA:
|
||||
payload.reset();
|
||||
payload.addBME(bme_status);
|
||||
SendPayload(BMEPORT);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if (HAS_GPS)
|
||||
case GPS_DATA:
|
||||
if (GPSPORT != COUNTERPORT) {
|
||||
// send GPS position only if we have a fix
|
||||
if (gps_hasfix()) {
|
||||
gps_storelocation(&gps_status);
|
||||
payload.reset();
|
||||
payload.addGPS(gps_status);
|
||||
SendPayload(GPSPORT);
|
||||
} else
|
||||
ESP_LOGD(TAG, "No valid GPS position");
|
||||
}
|
||||
break;
|
||||
case GPS_DATA:
|
||||
if (GPSPORT != COUNTERPORT) {
|
||||
// send GPS position only if we have a fix
|
||||
if (gps_hasfix()) {
|
||||
gps_storelocation(&gps_status);
|
||||
payload.reset();
|
||||
payload.addGPS(gps_status);
|
||||
SendPayload(GPSPORT);
|
||||
} else
|
||||
ESP_LOGD(TAG, "No valid GPS position");
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if (HAS_SENSORS)
|
||||
#if (HAS_SENSOR_1)
|
||||
case SENSOR1_DATA:
|
||||
payload.reset();
|
||||
payload.addSensor(sensor_read(1));
|
||||
SendPayload(SENSOR1PORT);
|
||||
case SENSOR1_DATA:
|
||||
payload.reset();
|
||||
payload.addSensor(sensor_read(1));
|
||||
SendPayload(SENSOR1PORT);
|
||||
#if (COUNT_ENS)
|
||||
if (cfg.countermode != 1)
|
||||
cwa_clear();
|
||||
if (cfg.countermode != 1)
|
||||
cwa_clear();
|
||||
#endif
|
||||
break;
|
||||
break;
|
||||
#endif
|
||||
#if (HAS_SENSOR_2)
|
||||
case SENSOR2_DATA:
|
||||
payload.reset();
|
||||
payload.addSensor(sensor_read(2));
|
||||
SendPayload(SENSOR2PORT);
|
||||
break;
|
||||
case SENSOR2_DATA:
|
||||
payload.reset();
|
||||
payload.addSensor(sensor_read(2));
|
||||
SendPayload(SENSOR2PORT);
|
||||
break;
|
||||
#endif
|
||||
#if (HAS_SENSOR_3)
|
||||
case SENSOR3_DATA:
|
||||
payload.reset();
|
||||
payload.addSensor(sensor_read(3));
|
||||
SendPayload(SENSOR3PORT);
|
||||
break;
|
||||
case SENSOR3_DATA:
|
||||
payload.reset();
|
||||
payload.addSensor(sensor_read(3));
|
||||
SendPayload(SENSOR3PORT);
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
|
||||
case BATT_DATA:
|
||||
payload.reset();
|
||||
payload.addVoltage(read_voltage());
|
||||
SendPayload(BATTPORT);
|
||||
break;
|
||||
case BATT_DATA:
|
||||
payload.reset();
|
||||
payload.addVoltage(read_voltage());
|
||||
SendPayload(BATTPORT);
|
||||
break;
|
||||
#endif
|
||||
|
||||
} // switch
|
||||
bitmask &= ~mask;
|
||||
mask <<= 1;
|
||||
} // while (bitmask)
|
||||
} // sendData()
|
||||
} // switch
|
||||
bitmask &= ~mask;
|
||||
mask <<= 1;
|
||||
} // while (bitmask)
|
||||
} // sendData()
|
||||
|
||||
void flushQueues(void) {
|
||||
rcmd_queuereset();
|
||||
void flushQueues(void) {
|
||||
rcmd_queuereset();
|
||||
#if (HAS_LORA)
|
||||
lora_queuereset();
|
||||
lora_queuereset();
|
||||
#endif
|
||||
#ifdef HAS_SPI
|
||||
spi_queuereset();
|
||||
spi_queuereset();
|
||||
#endif
|
||||
#ifdef HAS_MQTT
|
||||
mqtt_queuereset();
|
||||
mqtt_queuereset();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool allQueuesEmtpy(void) {
|
||||
uint32_t rc = rcmd_queuewaiting();
|
||||
bool allQueuesEmtpy(void) {
|
||||
uint32_t rc = rcmd_queuewaiting();
|
||||
#if (HAS_LORA)
|
||||
rc += lora_queuewaiting();
|
||||
rc += lora_queuewaiting();
|
||||
#endif
|
||||
#ifdef HAS_SPI
|
||||
rc += spi_queuewaiting();
|
||||
rc += spi_queuewaiting();
|
||||
#endif
|
||||
#ifdef HAS_MQTT
|
||||
rc += mqtt_queuewaiting();
|
||||
rc += mqtt_queuewaiting();
|
||||
#endif
|
||||
return (rc == 0) ? true : false;
|
||||
}
|
||||
return (rc == 0) ? true : false;
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
#if (COUNT_ENS)
|
||||
#include "payload.h"
|
||||
#include "corona.h"
|
||||
#include "macsniff.h"
|
||||
extern PayloadConvert payload;
|
||||
#endif
|
||||
|
||||
@ -38,7 +37,7 @@ uint8_t sensor_mask(uint8_t sensor_no) {
|
||||
case 6:
|
||||
return (uint8_t)MEMS_DATA;
|
||||
case 7:
|
||||
return (uint8_t)ALARM_DATA;
|
||||
return (uint8_t)RESERVED_DATA;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
@ -8,11 +8,23 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define _COMPILETIME compileTime()
|
||||
|
||||
// Local logging tag
|
||||
static const char TAG[] = __FILE__;
|
||||
|
||||
// symbol to display current time source
|
||||
const char timeSetSymbols[] = {'G', 'R', 'L', '?'};
|
||||
// G = GPS / R = RTC / L = LORA / ? = unsynced / <blank> = sync unknown
|
||||
const char timeSetSymbols[] = {'G', 'R', 'L', '?', ' '};
|
||||
|
||||
// set Time Zone
|
||||
Timezone myTZ;
|
||||
|
||||
bool volatile TimePulseTick = false;
|
||||
timesource_t timeSource = _unsynced;
|
||||
|
||||
TaskHandle_t ClockTask = NULL;
|
||||
hw_timer_t *ppsIRQ = NULL;
|
||||
|
||||
#ifdef HAS_IF482
|
||||
#if (HAS_SDS011)
|
||||
@ -85,8 +97,8 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
|
||||
vTaskDelay(pdMS_TO_TICKS(1000 - t_msec % 1000));
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "[%0.3f] UTC time: %d.%03d sec", _seconds(),
|
||||
time_to_set, t_msec % 1000);
|
||||
ESP_LOGI(TAG, "[%0.3f] UTC time: %d.%03d sec", _seconds(), time_to_set,
|
||||
t_msec % 1000);
|
||||
|
||||
// if we have got an external timesource, set RTC time and shift RTC_INT pulse
|
||||
// to top of second
|
||||
@ -95,23 +107,25 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
|
||||
set_rtctime(time_to_set);
|
||||
#endif
|
||||
|
||||
// if we have a software pps timer, shift it to top of second
|
||||
#if (!defined GPS_INT && !defined RTC_INT)
|
||||
timerWrite(ppsIRQ, 0); // reset pps timer
|
||||
CLOCKIRQ(); // fire clock pps, this advances time 1 sec
|
||||
#endif
|
||||
// if we have a software pps timer, shift it to top of second
|
||||
if (ppsIRQ != NULL) {
|
||||
timerWrite(ppsIRQ, 0); // reset pps timer
|
||||
CLOCKIRQ(); // fire clock pps, this advances time 1 sec
|
||||
}
|
||||
|
||||
setTime(time_to_set); // set the time on top of second
|
||||
UTC.setTime(time_to_set); // set the time on top of second
|
||||
|
||||
timeSource = mytimesource; // set global variable
|
||||
timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ);
|
||||
ESP_LOGD(TAG, "[%0.3f] Timesync finished, time was set | source: %c",
|
||||
_seconds(), timeSetSymbols[mytimesource]);
|
||||
ESP_LOGD(TAG, "[%0.3f] Timesync finished, time was set | timesource=%d",
|
||||
_seconds(), mytimesource);
|
||||
} else {
|
||||
timesyncer.attach(TIME_SYNC_INTERVAL_RETRY * 60, setTimeSyncIRQ);
|
||||
time_t unix_sec_at_compilation = compiledUTC();
|
||||
ESP_LOGD(TAG, "[%0.3f] Failed to synchronise time from source %c | unix sec obtained from source: %d | unix sec at program compilation: %d",
|
||||
_seconds(), timeSetSymbols[mytimesource], time_to_set, unix_sec_at_compilation);
|
||||
ESP_LOGD(TAG,
|
||||
"[%0.3f] Failed to synchronise time from source %c | unix sec "
|
||||
"obtained from source: %d | unix sec at program compilation: %d",
|
||||
_seconds(), timeSetSymbols[mytimesource], time_to_set,
|
||||
_COMPILETIME);
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,7 +171,6 @@ uint8_t timepulse_init() {
|
||||
} // timepulse_init
|
||||
|
||||
void timepulse_start(void) {
|
||||
|
||||
#ifdef GPS_INT // start external clock gps pps line
|
||||
attachInterrupt(digitalPinToInterrupt(GPS_INT), CLOCKIRQ, RISING);
|
||||
#elif defined RTC_INT // start external clock rtc
|
||||
@ -177,7 +190,7 @@ void IRAM_ATTR CLOCKIRQ(void) {
|
||||
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
|
||||
SyncToPPS(); // advance systime, see microTime.h
|
||||
// syncToPPS(); // currently not used
|
||||
|
||||
// advance wall clock, if we have
|
||||
#if (defined HAS_IF482 || defined HAS_DCF77)
|
||||
@ -197,16 +210,10 @@ void IRAM_ATTR CLOCKIRQ(void) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
|
||||
// helper function to check plausibility of a time
|
||||
// helper function to check plausibility of a given epoch time
|
||||
time_t timeIsValid(time_t const t) {
|
||||
// is it a time in the past? we use compile date to guess
|
||||
return (t >= compiledUTC() ? t : 0);
|
||||
}
|
||||
|
||||
// helper function to convert compile time to UTC time
|
||||
time_t compiledUTC(void) {
|
||||
static time_t t = myTZ.toUTC(RtcDateTime(__DATE__, __TIME__).Epoch32Time());
|
||||
return t;
|
||||
return (t < myTZ.tzTime(_COMPILETIME) ? 0 : t);
|
||||
}
|
||||
|
||||
// helper function to calculate serial transmit time
|
||||
@ -291,7 +298,7 @@ void clock_loop(void *taskparameter) { // ClockTask
|
||||
t = time_t(printtime); // new adjusted UTC time seconds
|
||||
|
||||
// send IF482 telegram
|
||||
IF482.print(IF482_Frame(t + 1)); // note: telegram is for *next* second
|
||||
IF482.print(IF482_Frame(t + 2)); // note: telegram is for *next* second
|
||||
|
||||
#elif defined HAS_DCF77
|
||||
|
||||
@ -300,7 +307,7 @@ void clock_loop(void *taskparameter) { // ClockTask
|
||||
|
||||
if (minute(nextmin(t)) == // do we still have a recent frame?
|
||||
DCFpulse[DCF77_FRAME_SIZE]) // (timepulses could be missed!)
|
||||
DCF77_Pulse(t, DCFpulse); // then output current second's pulse
|
||||
DCF77_Pulse(t + 1, DCFpulse); // then output next second's pulse
|
||||
|
||||
// else we have no recent frame, thus suppressing clock output
|
||||
|
||||
|
@ -97,8 +97,7 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) {
|
||||
// wait until a timestamp was received
|
||||
if (xTaskNotifyWait(0x00, ULONG_MAX, &rcv_seqNo,
|
||||
pdMS_TO_TICKS(TIME_SYNC_TIMEOUT * 1000)) == pdFALSE) {
|
||||
ESP_LOGW(TAG, "[d%0.3f] Timesync aborted: timed out",
|
||||
_seconds());
|
||||
ESP_LOGW(TAG, "[d%0.3f] Timesync aborted: timed out", _seconds());
|
||||
goto Fail; // no timestamp received before timeout
|
||||
}
|
||||
|
||||
@ -143,6 +142,9 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) {
|
||||
time_offset_ms += TIME_SYNC_FIXUP;
|
||||
time_offset_ms %= 1000;
|
||||
|
||||
ESP_LOGD(TAG, "LORA date/time: %s",
|
||||
myTZ.dateTime(time_offset_sec, "d.M Y H:i:s T").c_str());
|
||||
|
||||
setMyTime(time_offset_sec, time_offset_ms, _lora);
|
||||
|
||||
// send timesync end char to show timesync was successful
|
||||
@ -166,8 +168,8 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) {
|
||||
|
||||
// store incoming timestamps
|
||||
void timesync_store(uint32_t timestamp, timesync_t timestamp_type) {
|
||||
ESP_LOGD(TAG, "[%0.3f] seq#%d[%d]: t%d=%d", _seconds(),
|
||||
time_sync_seqNo, sample_idx, timestamp_type, timestamp);
|
||||
ESP_LOGD(TAG, "[%0.3f] seq#%d[%d]: t%d=%d", _seconds(), time_sync_seqNo,
|
||||
sample_idx, timestamp_type, timestamp);
|
||||
timesync_timestamp[sample_idx][timestamp_type] = timestamp;
|
||||
}
|
||||
|
||||
@ -232,8 +234,7 @@ void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) {
|
||||
lmic_time_reference_t lmicTime;
|
||||
|
||||
if (flag != 1) {
|
||||
ESP_LOGW(TAG, "[%0.3f] Network did not answer time request",
|
||||
_seconds());
|
||||
ESP_LOGW(TAG, "[%0.3f] Network did not answer time request", _seconds());
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
|
105
src/wifiscan.cpp
105
src/wifiscan.cpp
@ -1,105 +0,0 @@
|
||||
// Basic Config
|
||||
#include "globals.h"
|
||||
#include "wifiscan.h"
|
||||
|
||||
// Local logging tag
|
||||
static const char TAG[] = "wifi";
|
||||
|
||||
TimerHandle_t WifiChanTimer;
|
||||
|
||||
typedef struct {
|
||||
unsigned frame_ctrl : 16;
|
||||
unsigned duration_id : 16;
|
||||
uint8_t addr1[6]; // receiver address
|
||||
uint8_t addr2[6]; // sender address
|
||||
uint8_t addr3[6]; // filtering address
|
||||
unsigned sequence_ctrl : 16;
|
||||
uint8_t addr4[6]; // optional
|
||||
} wifi_ieee80211_mac_hdr_t;
|
||||
|
||||
typedef struct {
|
||||
wifi_ieee80211_mac_hdr_t hdr;
|
||||
uint8_t payload[0]; // network data ended with 4 bytes csum (CRC32)
|
||||
} wifi_ieee80211_packet_t;
|
||||
|
||||
// using IRAM_ATTR here to speed up callback function
|
||||
IRAM_ATTR void wifi_sniffer_packet_handler(void *buff,
|
||||
wifi_promiscuous_pkt_type_t type) {
|
||||
|
||||
const wifi_promiscuous_pkt_t *ppkt = (wifi_promiscuous_pkt_t *)buff;
|
||||
const wifi_ieee80211_packet_t *ipkt =
|
||||
(wifi_ieee80211_packet_t *)ppkt->payload;
|
||||
const wifi_ieee80211_mac_hdr_t *hdr = &ipkt->hdr;
|
||||
|
||||
// process seen MAC
|
||||
#if MACFILTER
|
||||
// we guess it's a smartphone, if U/L bit #2 of MAC is set
|
||||
// bit #2 = 1 -> local mac (randomized) / bit #2 = 0 -> universal mac
|
||||
if ((hdr->addr2[0] & 0b10) == 0)
|
||||
return;
|
||||
else
|
||||
#endif
|
||||
mac_add((uint8_t *)hdr->addr2, ppkt->rx_ctrl.rssi, MAC_SNIFF_WIFI);
|
||||
}
|
||||
|
||||
// Software-timer driven Wifi channel rotation callback function
|
||||
void switchWifiChannel(TimerHandle_t xTimer) {
|
||||
channel =
|
||||
(channel % WIFI_CHANNEL_MAX) + 1; // rotate channel 1..WIFI_CHANNEL_MAX
|
||||
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
|
||||
}
|
||||
|
||||
void wifi_sniffer_init(void) {
|
||||
|
||||
wifi_country_t wifi_country = {WIFI_MY_COUNTRY, WIFI_CHANNEL_MIN,
|
||||
WIFI_CHANNEL_MAX, 100,
|
||||
WIFI_COUNTRY_POLICY_MANUAL};
|
||||
|
||||
wifi_init_config_t wifi_cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
wifi_cfg.event_handler = NULL; // we don't need a wifi event handler
|
||||
wifi_cfg.nvs_enable = 0; // we don't need any wifi settings from NVRAM
|
||||
wifi_cfg.wifi_task_core_id = 0; // we want wifi task running on core 0
|
||||
|
||||
wifi_promiscuous_filter_t wifi_filter = {.filter_mask =
|
||||
WIFI_PROMIS_FILTER_MASK_MGMT |
|
||||
WIFI_PROMIS_FILTER_MASK_DATA};
|
||||
|
||||
esp_wifi_init(&wifi_cfg); // start Wifi task
|
||||
esp_wifi_set_country(&wifi_country); // set locales for RF and channels
|
||||
esp_wifi_set_storage(WIFI_STORAGE_RAM);
|
||||
esp_wifi_set_mode(WIFI_MODE_NULL);
|
||||
esp_wifi_set_ps(WIFI_PS_NONE); // no modem power saving
|
||||
esp_wifi_set_promiscuous_filter(&wifi_filter); // set frame filter
|
||||
esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler);
|
||||
|
||||
// setup wifi channel hopping timer
|
||||
WifiChanTimer = xTimerCreate("WifiChannelTimer",
|
||||
(cfg.wifichancycle > 0)
|
||||
? pdMS_TO_TICKS(cfg.wifichancycle * 10)
|
||||
: pdMS_TO_TICKS(50),
|
||||
pdTRUE, (void *)0, switchWifiChannel);
|
||||
}
|
||||
|
||||
void switch_wifi_sniffer(uint8_t state) {
|
||||
if (state) {
|
||||
// start sniffer
|
||||
#if (BLECOUNTER)
|
||||
// workaround needed for ESP-IDF v3.3
|
||||
// see https://github.com/espressif/esp-idf/issues/5427
|
||||
esp_wifi_set_ps(WIFI_PS_MIN_MODEM);
|
||||
#endif
|
||||
esp_wifi_start();
|
||||
esp_wifi_set_promiscuous(true);
|
||||
esp_wifi_set_channel(WIFI_CHANNEL_MIN, WIFI_SECOND_CHAN_NONE);
|
||||
// start channel hopping timer
|
||||
if (cfg.wifichancycle > 0)
|
||||
xTimerStart(WifiChanTimer, (TickType_t)0);
|
||||
} else {
|
||||
// start channel hopping timer
|
||||
if (xTimerIsTimerActive(WifiChanTimer) != pdFALSE)
|
||||
xTimerStop(WifiChanTimer, (TickType_t)0);
|
||||
// stop sniffer
|
||||
esp_wifi_set_promiscuous(false);
|
||||
esp_wifi_stop();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user