This commit is contained in:
Oliver Brandmueller 2018-10-12 10:00:36 +02:00
commit 3d931a1459
67 changed files with 2662 additions and 983 deletions

1
.gitignore vendored
View File

@ -10,3 +10,4 @@
.clang_complete .clang_complete
.gcc-flags.json .gcc-flags.json
src/loraconf.h src/loraconf.h
src/ota.conf

View File

@ -20,9 +20,9 @@ Parts of the source files in this repository are made available under different
listed below. Refer to each individual source file for more details. listed below. Refer to each individual source file for more details.
------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------
wifisniffer.cpp wifiscan.cpp
Parts of wifisniffer.cpp were derived or taken from Prior art was used for wifiscan.cpp and taken from
* Copyright (c) 2017, Łukasz Marcin Podkalicki <lpodkalicki@gmail.com> * Copyright (c) 2017, Łukasz Marcin Podkalicki <lpodkalicki@gmail.com>
* ESP32/016 WiFi Sniffer * ESP32/016 WiFi Sniffer

View File

@ -31,8 +31,9 @@ This can all be done with a single small and cheap ESP32 board for less than $20
- Pycom: LoPy, LoPy4, FiPy - Pycom: LoPy, LoPy4, FiPy
- WeMos: LoLin32 + [LoraNode32 shield](https://github.com/hallard/LoLin32-Lora), - WeMos: LoLin32 + [LoraNode32 shield](https://github.com/hallard/LoLin32-Lora),
LoLin32lite + [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora) LoLin32lite + [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora)
- Adafruit ESP32 Feather + LoRa Wing + OLED Wing, #IoT Octopus32 (Octopus + ESP32 Feather)
*SPI only*: (coming soon) *SPI only*: (code yet to come)
- Pyom: WiPy - Pyom: WiPy
- WeMos: LoLin32, LoLin32 Lite, WeMos D32 - WeMos: LoLin32, LoLin32 Lite, WeMos D32
@ -44,10 +45,10 @@ Depending on board hardware following features are supported:
- Button - Button
- Silicon unique ID - Silicon unique ID
- Battery voltage monitoring - Battery voltage monitoring
- GPS - GPS (Generic serial NMEA, or Quectel L76 I2C)
Target platform must be selected in [platformio.ini](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/platformio.ini).<br> Target platform must be selected in [platformio.ini](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/platformio.ini).<br>
Hardware dependent settings (pinout etc.) are stored in board files in /hal directory.<br> Hardware dependent settings (pinout etc.) are stored in board files in /hal directory. If you want to use a ESP32 board which is not yet supported, use hal file generic.h and tailor pin mappings to your needs. Pull requests for new boards welcome.<br>
<b>3D printable cases</b> can be found (and, if wanted so, ordered) on Thingiverse, see <b>3D printable cases</b> can be found (and, if wanted so, ordered) on Thingiverse, see
<A HREF="https://www.thingiverse.com/thing:2670713">Heltec</A>, <A HREF="https://www.thingiverse.com/thing:2811127">TTGOv2</A>, <A HREF="https://www.thingiverse.com/thing:3005574">TTGOv2.1</A>, <A HREF="https://www.thingiverse.com/thing:3041339">T-BEAM</A> for example.<br> <A HREF="https://www.thingiverse.com/thing:2670713">Heltec</A>, <A HREF="https://www.thingiverse.com/thing:2811127">TTGOv2</A>, <A HREF="https://www.thingiverse.com/thing:3005574">TTGOv2.1</A>, <A HREF="https://www.thingiverse.com/thing:3041339">T-BEAM</A> for example.<br>
@ -62,6 +63,8 @@ Before compiling the code,
- **create file loraconf.h in your local /src directory** using the template [loraconf.sample.h](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/loraconf.sample.h) and populate it with your personal APPEUI und APPKEY for the LoRaWAN network. If you're using popular <A HREF="https://thethingsnetwork.org">TheThingsNetwork</A> you can copy&paste the keys from TTN console or output of ttnctl. - **create file loraconf.h in your local /src directory** using the template [loraconf.sample.h](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/loraconf.sample.h) and populate it with your personal APPEUI und APPKEY for the LoRaWAN network. If you're using popular <A HREF="https://thethingsnetwork.org">TheThingsNetwork</A> you can copy&paste the keys from TTN console or output of ttnctl.
- **create file ota.conf in your local /src directory** using the template [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. If you want to push own OTA updates 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.
To join the network only method OTAA is supported, not ABP. The DEVEUI for OTAA will be derived from the device's MAC adress during device startup and is shown as well on the device's display (if it has one) as on the serial console for copying it to your LoRaWAN network server settings. To join the network only method OTAA is supported, not ABP. The DEVEUI for OTAA will be derived from the device's MAC adress during device startup and is shown as well on the device's display (if it has one) as on the serial console for copying it to your LoRaWAN network server settings.
If your device has a fixed DEVEUI enter this in your local loraconf.h file. During compile time this DEVEUI will be grabbed from loraconf.h and inserted in the code. If your device has a fixed DEVEUI enter this in your local loraconf.h file. During compile time this DEVEUI will be grabbed from loraconf.h and inserted in the code.
@ -74,10 +77,14 @@ Use <A HREF="https://platformio.org/">PlatformIO</A> with your preferred IDE for
# Uploading # Uploading
To upload the code to your ESP32 board this needs to be switched from run to bootloader mode. Boards with USB bridge like Heltec and TTGO usually have an onboard logic which allows soft switching by the upload tool. In PlatformIO this happenes automatically.<p> - **Initially, using USB/UART cable:**
To upload the code via cable to your ESP32 board this needs to be switched from run to bootloader mode. Boards with USB bridge like Heltec and TTGO usually have an onboard logic which allows soft switching by the upload tool. In PlatformIO this happenes automatically.<p>
The LoPy/LoPy4/FiPy board needs to be set manually. See these The LoPy/LoPy4/FiPy board needs to be set manually. See these
<A HREF="https://www.thethingsnetwork.org/labs/story/program-your-lopy-from-the-arduino-ide-using-lmic">instructions</A> how to do it. Don't forget to press on board reset button after switching between run and bootloader mode.<p> <A HREF="https://www.thethingsnetwork.org/labs/story/program-your-lopy-from-the-arduino-ide-using-lmic">instructions</A> how to do it. Don't forget to press on board reset button after switching between run and bootloader mode.<p>
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. 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.
- **During runtime, using FOTA via WIFI:**
After the ESP32 board is initially flashed and has joined a LoRaWAN network, the firmware can update itself by FOTA. 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 FOTA 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.
# Legal note # Legal note
@ -128,7 +135,13 @@ If you're using [TheThingsNetwork](https://www.thethingsnetwork.org/) (TTN) you
To track a paxcounter device with on board GPS and at the same time contribute to TTN coverage mapping, you simply activate the [TTNmapper integration](https://www.thethingsnetwork.org/docs/applications/ttnmapper/) in TTN Console. The formats *plain* and *packed* generate the fields `latitude`, `longitude` and `hdop` required by ttnmapper. To track a paxcounter device with on board GPS and at the same time contribute to TTN coverage mapping, you simply activate the [TTNmapper integration](https://www.thethingsnetwork.org/docs/applications/ttnmapper/) in TTN Console. The formats *plain* and *packed* generate the fields `latitude`, `longitude` and `hdop` required by ttnmapper.
Hereafter described is the default *plain* format, which uses MSB bit numbering. Hereafter described is the default *plain* format, which uses MSB bit numbering. Under /TTN in this repository you find some ready-to-go decoders which you may copy to your TTN console:
[**plain_decoder.js**](src/TTN/plain_decoder.js) |
[**plain_converter.js**](src/TTN/plain_converter.js) |
[**packed_decoder.js**](src/TTN/packed_decoder.js) |
[**packed_converter.js**](src/TTN/packed_converter.js)
**Port #1:** Paxcount data **Port #1:** Paxcount data
@ -142,6 +155,7 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering.
byte 3-10: Uptime [seconds] byte 3-10: Uptime [seconds]
byte 11: CPU temperature [°C] byte 11: CPU temperature [°C]
bytes 12-15: Free RAM [bytes] bytes 12-15: Free RAM [bytes]
bytes 16-17: Last CPU reset reason [core 0, core 1]
**Port #3:** Device configuration query result **Port #3:** Device configuration query result
@ -181,55 +195,11 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering.
byte 1: Beacon RSSI reception level byte 1: Beacon RSSI reception level
byte 2: Beacon identifier (0..255) byte 2: Beacon identifier (0..255)
[**plain_decoder.js**](src/TTN/plain_decoder.js)
```javascript
function Decoder(bytes, port) {
var decoded = {};
if (port === 1) {
var i = 0;
decoded.wifi = (bytes[i++] << 8) | bytes[i++];
decoded.ble = (bytes[i++] << 8) | bytes[i++];
if (bytes.length > 4) {
decoded.latitude = ( (bytes[i++] << 24) | (bytes[i++] << 16) | (bytes[i++] << 8) | bytes[i++] );
decoded.longitude = ( (bytes[i++] << 24) | (bytes[i++] << 16) | (bytes[i++] << 8) | bytes[i++] );
decoded.sats = ( bytes[i++] );
decoded.hdop = ( bytes[i++] << 8) | (bytes[i++] );
decoded.altitude = ( bytes[i++] << 8) | (bytes[i++] );
}
}
return decoded;
}
```
[**plain_converter.js**](src/TTN/plain_converter.js)
```javascript
function Converter(decoded, port) {
var converted = decoded;
if (port === 1) {
converted.pax = converted.ble + converted.wifi;
if (converted.hdop) {
converted.hdop /= 100;
converted.latitude /= 1000000;
converted.longitude /= 1000000;
}
}
return converted;
}
```
# Remote control # Remote control
The device listenes for remote control commands on LoRaWAN Port 2. Multiple commands per downlink are possible by concatenating them. The device listenes for remote control commands on LoRaWAN Port 2. Multiple commands per downlink are possible by concatenating them.
Note: all settings are stored in NVRAM and will be reloaded when device starts. To reset device to factory settings send remote command 09 02 09 00 unconfirmed(!) once. Note: all settings are stored in NVRAM and will be reloaded when device starts.
0x01 set scan RSSI limit 0x01 set scan RSSI limit
@ -272,12 +242,13 @@ Note: all settings are stored in NVRAM and will be reloaded when device starts.
useful to clear pending commands from LoRaWAN server quere, or to check RSSI on device useful to clear pending commands from LoRaWAN server quere, or to check RSSI on device
0x09 reset functions 0x09 reset functions (send this command with confirmed ack only to avoid boot loops!)
0 = restart device 0 = restart device
1 = reset MAC counter to zero 1 = reset MAC counter to zero
2 = reset device to factory settings 2 = reset device to factory settings
3 = flush send queues 3 = flush send queues
9 = reboot device to OTA update via Wifi mode
0x0A set LoRaWAN payload send cycle 0x0A set LoRaWAN payload send cycle

93
build.py Normal file
View File

@ -0,0 +1,93 @@
# build.py
# pre-build script, setting up build environment
import sys
import os
import os.path
import requests
from os.path import basename
from platformio import util
Import("env")
# get platformio environment variables
project_config = util.load_project_config()
# check if file loraconf.h is present in source directory
keyfile = str(env.get("PROJECTSRC_DIR")) + "/loraconf.h"
if os.path.isfile(keyfile) and os.access(keyfile, os.R_OK):
print "Parsing LORAWAN keys from " + keyfile
else:
sys.exit("Missing file loraconf.h, please create it using template loraconf.sample.h! Aborting.")
# check if file ota.conf is present in source directory
keyfile = str(env.get("PROJECTSRC_DIR")) + "/" + project_config.get("common", "keyfile")
if os.path.isfile(keyfile) and os.access(keyfile, os.R_OK):
print "Parsing OTA keys from " + keyfile
else:
sys.exit("Missing file ota.conf, please create it using template ota.sample.conf! Aborting.")
# parse file ota.conf
mykeys = {}
with open(keyfile) as myfile:
for line in myfile:
key, value = line.partition("=")[::2]
mykeys[key.strip()] = str(value).strip()
# get bintray user credentials
user = mykeys["BINTRAY_USER"]
repository = mykeys["BINTRAY_REPO"]
apitoken = mykeys["BINTRAY_API_TOKEN"]
# get bintray upload parameters
version = project_config.get("common", "release_version")
package = str(env.get("PIOENV"))
# put bintray user credentials to platformio environment
env.Replace(BINTRAY_USER=user)
env.Replace(BINTRAY_REPO=repository)
env.Replace(BINTRAY_API_TOKEN=apitoken)
# get runtime credentials and put them to compiler directive
env.Replace(CPPDEFINES=[
('WIFI_SSID', '\\"' + mykeys["OTA_WIFI_SSID"] + '\\"'),
('WIFI_PASS', '\\"' + mykeys["OTA_WIFI_PASS"] + '\\"'),
('BINTRAY_USER', '\\"' + mykeys["BINTRAY_USER"] + '\\"'),
('BINTRAY_REPO', '\\"' + mykeys["BINTRAY_REPO"] + '\\"'),
])
# function for pushing new firmware to bintray storage using API
def publish_bintray(source, target, env):
firmware_path = str(source[0])
firmware_name = basename(firmware_path)
url = "/".join([
"https://api.bintray.com", "content",
user, repository, package, version, firmware_name
])
print("Uploading {0} to Bintray. Version: {1}".format(
firmware_name, version))
print(url)
headers = {
"Content-type": "application/octet-stream",
"X-Bintray-Publish": "1",
"X-Bintray-Override": "1"
}
r = requests.put(
url,
data=open(firmware_path, "rb"),
headers=headers,
auth=(user, apitoken))
if r.status_code != 201:
print("Failed to submit package: {0}\n{1}".format(
r.status_code, r.text))
else:
print("The firmware has been successfuly published at Bintray.com!")
# put build file name and upload command to platformio environment
env.Replace(
PROGNAME="firmware_" + package + "_v%s" % version,
UPLOADCMD=publish_bintray)

View File

@ -0,0 +1,24 @@
{
"name": "BintrayClient",
"keywords": "bintray, ota, cdn, storage",
"description": "A BintrayClient to connect to a JFrog Bintray.",
"authors": [
{
"name": "PlatformIO",
"url": "https://platformio.org/"
}
],
"repository": {
"type": "git",
"url": "https://github.com/platformio/platformio-examples"
},
"export": {
"include": "bintray-secure-ota/lib/BintrayClient"
},
"dependencies": {
"ArduinoJson": "^5.13.1"
},
"version": "1.0.0",
"frameworks": "arduino",
"platforms": "espressif32"
}

View File

@ -0,0 +1,105 @@
/*
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

View File

@ -0,0 +1,150 @@
/*
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.
**/
#include <Arduino.h>
#include <HTTPClient.h>
#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_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
{
return m_user;
}
String BintrayClient::getRepository() const
{
return m_repo;
}
String BintrayClient::getPackage() const
{
return m_package;
}
String BintrayClient::getStorageHost() const
{
return m_storage_host;
}
String BintrayClient::getApiHost() const
{
return m_api_host;
}
String BintrayClient::getLatestVersionRequestUrl() const
{
return String("https://") + getApiHost() + "/packages/" + getUser() + "/" + getRepository() + "/" + getPackage() + "/versions/_latest";
}
String BintrayClient::getBinaryRequestUrl(const String &version) const
{
return String("https://") + getApiHost() + "/packages/" + getUser() + "/" + getRepository() + "/" + getPackage() + "/versions/" + version + "/files";
}
const char *BintrayClient::getCertificate(const String &url) const
{
for(auto& cert: m_certificates) {
if(url.indexOf(cert.first) >= 0) {
return cert.second;
}
}
// Return the certificate for *.bintray.com by default
return m_certificates.rbegin()->second;
}
String BintrayClient::requestHTTPContent(const String &url) const
{
String payload;
HTTPClient http;
http.begin(url, getCertificate(url));
int httpCode = http.GET();
if (httpCode > 0)
{
if (httpCode == HTTP_CODE_OK)
{
payload = http.getString();
}
}
else
{
ESP_LOGE(TAG, "GET request failed, error: %s", http.errorToString(httpCode).c_str());
}
http.end();
return payload;
}
String BintrayClient::getLatestVersion() const
{
String version;
const String url = getLatestVersionRequestUrl();
String jsonResult = requestHTTPContent(url);
const size_t bufferSize = 1024;
if (jsonResult.length() > bufferSize)
{
ESP_LOGE(TAG, "Error: Firmware version data invalid.");
return version;
}
StaticJsonBuffer<bufferSize> jsonBuffer;
JsonObject &root = jsonBuffer.parseObject(jsonResult.c_str());
// Check for errors in parsing
if (!root.success())
{
ESP_LOGE(TAG, "Error: Firmware version data not found.");
return version;
}
return root.get<String>("name");
}
String BintrayClient::getBinaryPath(const String &version) const
{
String path;
const String url = getBinaryRequestUrl(version);
String jsonResult = requestHTTPContent(url);
const size_t bufferSize = 1024;
if (jsonResult.length() > bufferSize)
{
ESP_LOGE(TAG, "Error: Firmware download path data invalid.");
return path;
}
StaticJsonBuffer<bufferSize> jsonBuffer;
JsonArray &root = jsonBuffer.parseArray(jsonResult.c_str());
JsonObject &firstItem = root[0];
if (!root.success())
{ //Check for errors in parsing
ESP_LOGE(TAG, "Error: Firmware download path not found.");
return path;
}
return "/" + getUser() + "/" + getRepository() + "/" + firstItem.get<String>("path");
}

View File

@ -0,0 +1,50 @@
/*
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_CLIENT_H
#define BINTRAY_CLIENT_H
#include <vector>
#include <utility>
#include <WString.h>
class BintrayClient {
public:
BintrayClient(const String& user, const String& repository, const String& package);
String getUser() const;
String getRepository() const;
String getPackage() const;
String getStorageHost() const;
String getApiHost() const;
const char* getCertificate(const String& url) const;
String getLatestVersion() const;
String getBinaryPath(const String& version) const;
private:
String requestHTTPContent(const String& url) const;
String getLatestVersionRequestUrl() const;
String getBinaryRequestUrl(const String& version) const;
String m_user;
String m_repo;
String m_package;
const String m_storage_host;
const String m_api_host;
std::vector<std::pair<String, const char*>> m_certificates;
};
#endif // BINTRAY_CLIENT_H

View File

@ -907,7 +907,7 @@ static ostime_t nextJoinState (void) {
} else { } else {
LMIC.txChnl = os_getRndU1() & 0x3F; LMIC.txChnl = os_getRndU1() & 0x3F;
s1_t dr = DR_SF7 - ++LMIC.txCnt; s1_t dr = DR_SF7 - ++LMIC.txCnt;
if( dr < DR_SF10 ) { if( LMIC.txCnt > DR_SF7 ) {
dr = DR_SF10; dr = DR_SF10;
failed = 1; // All DR exhausted - signal failed failed = 1; // All DR exhausted - signal failed
} }

View File

@ -790,8 +790,12 @@ void radio_irq_handler (u1_t dio) {
// now read the FIFO // now read the FIFO
readBuf(RegFifo, LMIC.frame, LMIC.dataLen); readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
// read rx quality parameters // read rx quality parameters
LMIC.snr = readReg(LORARegPktSnrValue); // SNR [dB] * 4 //LMIC.snr = readReg(LORARegPktSnrValue); // SNR [dB] * 4
LMIC.rssi = readReg(LORARegPktRssiValue) - 125 + 64; // RSSI [dBm] (-196...+63) LMIC.snr = ((s1_t)readReg(LORARegPktSnrValue)) / 4;
//LMIC.rssi = readReg(LORARegPktRssiValue) - 125 + 64; // RSSI [dBm] (-196...+63)
LMIC.rssi = readReg(LORARegPktRssiValue) - 157; // RFI_HF for 868 and 915MHZ band
if (LMIC.snr < 0)
LMIC.rssi += LMIC.snr;
} else if( flags & IRQ_LORA_RXTOUT_MASK ) { } else if( flags & IRQ_LORA_RXTOUT_MASK ) {
// indicate timeout // indicate timeout
LMIC.dataLen = 0; LMIC.dataLen = 0;

View File

@ -1,10 +1,5 @@
; PlatformIO Project Configuration File ; PlatformIO Project Configuration File
; ;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples ; Please visit documentation for the other options and examples
; http://docs.platformio.org/page/projectconf.html ; http://docs.platformio.org/page/projectconf.html
@ -16,7 +11,8 @@ env_default = generic
;env_default = heltec ;env_default = heltec
;env_default = ttgov1 ;env_default = ttgov1
;env_default = ttgov2 ;env_default = ttgov2
;env_default = ttgov21 ;env_default = ttgov21old
;env_default = ttgov21new
;env_default = ttgobeam ;env_default = ttgobeam
;env_default = lopy ;env_default = lopy
;env_default = lopy4 ;env_default = lopy4
@ -24,14 +20,27 @@ env_default = generic
;env_default = lolin32litelora ;env_default = lolin32litelora
;env_default = lolin32lora ;env_default = lolin32lora
;env_default = lolin32lite ;env_default = lolin32lite
;env_default = octopus32
;env_default = ebox, heltec, ttgobeam, lopy4, lopy, ttgov21old, ttgov21new
; ;
description = Paxcounter is a proof-of-concept ESP32 device for metering passenger flows in realtime. It counts how many mobile devices are around. description = Paxcounter is a proof-of-concept ESP32 device for metering passenger flows in realtime. It counts how many mobile devices are around.
[common_env_data] [common]
platform_espressif32 = espressif32@1.3.0 ; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
;platform_espressif32 = https://github.com/platformio/platform-espressif32.git#feature/stage release_version = 1.6.0
board_build.partitions = no_ota.csv ; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
lib_deps_all = ; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
debug_level = 0
; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA
upload_protocol = esptool
;upload_protocol = custom
extra_scripts = pre:build.py
keyfile = ota.conf
platform_espressif32 = espressif32@1.4.0
board_build.partitions = min_spiffs.csv
monitor_speed = 115200
lib_deps_all =
ArduinoJson@^5.13.1
lib_deps_display = lib_deps_display =
U8g2@>=2.23.16 U8g2@>=2.23.16
lib_deps_rgbled = lib_deps_rgbled =
@ -39,199 +48,242 @@ lib_deps_rgbled =
lib_deps_gps = lib_deps_gps =
TinyGPSPlus@>=1.0.2 TinyGPSPlus@>=1.0.2
Time@>=1.5 Time@>=1.5
build_flags = build_flags =
; ---> NOTE: For production run set DEBUG_LEVEL level to NONE! <---
; otherwise device may leak RAM
;
; None
-DCORE_DEBUG_LEVEL=0
; Error
; -DCORE_DEBUG_LEVEL=1
; Warn
; -DCORE_DEBUG_LEVEL=2
; Info
; -DCORE_DEBUG_LEVEL=3
; Debug
; -DCORE_DEBUG_LEVEL=4
; Verbose
; -DCORE_DEBUG_LEVEL=5
;
; override lora settings from LMiC library in lmic/config.h and use main.h instead ; override lora settings from LMiC library in lmic/config.h and use main.h instead
-D_lmic_config_h_ -D_lmic_config_h_
-include "src/paxcounter.conf" -include "src/paxcounter.conf"
-include "src/hal/${PIOENV}.h" -include "src/hal/${PIOENV}.h"
-w -w
'-DCORE_DEBUG_LEVEL=${common.debug_level}'
'-DBINTRAY_PACKAGE="${PIOENV}"'
'-DPROGVERSION="${common.release_version}"'
[env:ebox] [env:ebox]
platform = ${common_env_data.platform_espressif32} platform = ${common.platform_espressif32}
framework = arduino framework = arduino
board = esp32dev board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions} board_build.partitions = ${common.board_build.partitions}
upload_speed = 115200 upload_speed = 115200
monitor_speed = 115200
lib_deps = lib_deps =
${common_env_data.lib_deps_all} ${common.lib_deps_all}
build_flags = build_flags =
${common_env_data.build_flags} ${common.build_flags}
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
[env:heltec] [env:heltec]
platform = ${common_env_data.platform_espressif32} platform = ${common.platform_espressif32}
framework = arduino framework = arduino
board = heltec_wifi_lora_32 board = heltec_wifi_lora_32
board_build.partitions = ${common_env_data.board_build.partitions} board_build.partitions = ${common.board_build.partitions}
upload_speed = 115200 upload_speed = 921600
monitor_speed = 115200
lib_deps = lib_deps =
${common_env_data.lib_deps_all} ${common.lib_deps_all}
${common_env_data.lib_deps_display} ${common.lib_deps_display}
build_flags = build_flags =
${common_env_data.build_flags} ${common.build_flags}
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
[env:ttgov1] [env:ttgov1]
platform = ${common_env_data.platform_espressif32} platform = ${common.platform_espressif32}
framework = arduino framework = arduino
board = esp32dev board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions} board_build.partitions = ${common.board_build.partitions}
upload_speed = 115200 upload_speed = 115200
monitor_speed = 115200
lib_deps = lib_deps =
${common_env_data.lib_deps_all} ${common.lib_deps_all}
${common_env_data.lib_deps_display} ${common.lib_deps_display}
build_flags = build_flags =
${common_env_data.build_flags} ${common.build_flags}
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
[env:ttgov2] [env:ttgov2]
platform = ${common_env_data.platform_espressif32} platform = ${common.platform_espressif32}
framework = arduino framework = arduino
board = esp32dev board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions} board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600 upload_speed = 921600
monitor_speed = 115200
lib_deps = lib_deps =
${common_env_data.lib_deps_all} ${common.lib_deps_all}
${common_env_data.lib_deps_display} ${common.lib_deps_display}
build_flags = build_flags =
${common_env_data.build_flags} ${common.build_flags}
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
[env:ttgov21] [env:ttgov21old]
platform = ${common_env_data.platform_espressif32} platform = ${common.platform_espressif32}
framework = arduino framework = arduino
board = esp32dev board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions} board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600 upload_speed = 921600
monitor_speed = 115200
lib_deps = lib_deps =
${common_env_data.lib_deps_all} ${common.lib_deps_all}
${common_env_data.lib_deps_display} ${common.lib_deps_display}
build_flags = build_flags =
${common_env_data.build_flags} ${common.build_flags}
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
[env:ttgov21new]
platform = ${common.platform_espressif32}
framework = arduino
board = esp32dev
board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600
lib_deps =
${common.lib_deps_all}
${common.lib_deps_display}
build_flags =
${common.build_flags}
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
[env:ttgobeam] [env:ttgobeam]
platform = ${common_env_data.platform_espressif32} platform = ${common.platform_espressif32}
framework = arduino framework = arduino
board = esp32dev board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions} board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600 upload_speed = 921600
monitor_speed = 115200
lib_deps = lib_deps =
${common_env_data.lib_deps_all} ${common.lib_deps_all}
${common_env_data.lib_deps_gps} ${common.lib_deps_gps}
build_flags = build_flags =
${common_env_data.build_flags} ${common.build_flags}
-mfix-esp32-psram-cache-issue -mfix-esp32-psram-cache-issue
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
[env:fipy] [env:fipy]
platform = ${common_env_data.platform_espressif32} platform = ${common.platform_espressif32}
framework = arduino framework = arduino
board = esp32dev board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions} board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600 upload_speed = 921600
monitor_speed = 115200
lib_deps = lib_deps =
${common_env_data.lib_deps_all} ${common.lib_deps_all}
${common_env_data.lib_deps_rgbled} ${common.lib_deps_rgbled}
build_flags = build_flags =
${common_env_data.build_flags} ${common.build_flags}
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
[env:lopy] [env:lopy]
platform = ${common_env_data.platform_espressif32} platform = ${common.platform_espressif32}
framework = arduino framework = arduino
board = esp32dev board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions} board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600 upload_speed = 921600
monitor_speed = 115200
lib_deps = lib_deps =
${common_env_data.lib_deps_all} ${common.lib_deps_all}
${common_env_data.lib_deps_rgbled} ${common.lib_deps_rgbled}
${common_env_data.lib_deps_gps} ${common.lib_deps_gps}
build_flags = build_flags =
${common_env_data.build_flags} ${common.build_flags}
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
[env:lopy4] [env:lopy4]
platform = ${common_env_data.platform_espressif32} platform = ${common.platform_espressif32}
framework = arduino framework = arduino
board = esp32dev board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions} board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600 upload_speed = 921600
monitor_speed = 115200
lib_deps = lib_deps =
${common_env_data.lib_deps_all} ${common.lib_deps_all}
${common_env_data.lib_deps_rgbled} ${common.lib_deps_rgbled}
${common_env_data.lib_deps_gps} ${common.lib_deps_gps}
build_flags = build_flags =
${common_env_data.build_flags} ${common.build_flags}
-mfix-esp32-psram-cache-issue -mfix-esp32-psram-cache-issue
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
[env:lolin32litelora] [env:lolin32litelora]
platform = ${common_env_data.platform_espressif32} platform = ${common.platform_espressif32}
framework = arduino framework = arduino
board = lolin32 board = lolin32
board_build.partitions = ${common_env_data.board_build.partitions} board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600 upload_speed = 921600
monitor_speed = 115200
lib_deps = lib_deps =
${common_env_data.lib_deps_all} ${common.lib_deps_all}
${common_env_data.lib_deps_rgbled} ${common.lib_deps_rgbled}
build_flags = build_flags =
${common_env_data.build_flags} ${common.build_flags}
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
[env:lolin32lora] [env:lolin32lora]
platform = ${common_env_data.platform_espressif32} platform = ${common.platform_espressif32}
framework = arduino framework = arduino
board = lolin32 board = lolin32
board_build.partitions = ${common_env_data.board_build.partitions} board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600 upload_speed = 921600
monitor_speed = 115200
lib_deps = lib_deps =
${common_env_data.lib_deps_all} ${common.lib_deps_all}
${common_env_data.lib_deps_rgbled} ${common.lib_deps_rgbled}
build_flags = build_flags =
${common_env_data.build_flags} ${common.build_flags}
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
[env:lolin32lite] [env:lolin32lite]
platform = ${common_env_data.platform_espressif32} platform = ${common.platform_espressif32}
framework = arduino framework = arduino
board = lolin32 board = lolin32
board_build.partitions = ${common_env_data.board_build.partitions} board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600 upload_speed = 921600
monitor_speed = 115200
lib_deps = lib_deps =
${common_env_data.lib_deps_all} ${common.lib_deps_all}
${common_env_data.lib_deps_rgbled} ${common.lib_deps_rgbled}
build_flags = build_flags =
${common_env_data.build_flags} ${common.build_flags}
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
[env:octopus32]
platform = ${common.platform_espressif32}
framework = arduino
board = esp32dev
board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600
lib_deps =
${common.lib_deps_all}
${common.lib_deps_rgbled}
build_flags =
${common.build_flags}
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
[env:generic] [env:generic]
platform = ${common_env_data.platform_espressif32} platform = ${common.platform_espressif32}
framework = arduino framework = arduino
board = esp32dev board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions} board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600 upload_speed = 921600
monitor_speed = 115200
lib_deps = lib_deps =
${common_env_data.lib_deps_all} ${common.lib_deps_all}
${common_env_data.lib_deps_rgbled} ${common.lib_deps_rgbled}
${common_env_data.lib_deps_gps} ${common.lib_deps_gps}
${common_env_data.lib_deps_display} ${common.lib_deps_display}
build_flags = build_flags =
${common_env_data.build_flags} ${common.build_flags}
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}

View File

@ -18,13 +18,12 @@ function Decoder(bytes, port) {
if (port === 2) { if (port === 2) {
// device status data // device status data
return decode(bytes, [uint16, uptime, uint8, uint32], ['voltage', 'uptime', 'cputemp', 'memory']); return decode(bytes, [uint16, uptime, uint8, uint32, uint8, uint8], ['voltage', 'uptime', 'cputemp', 'memory', 'reset0', 'reset1']);
} }
if (port === 3) { if (port === 3) {
// device config data // device config data
return decode(bytes, [uint8, uint8, uint16, uint8, uint8, uint8, uint8, bitmap], ['lorasf', 'txpower', 'rssilimit', 'sendcycle', 'wifichancycle', 'blescantime', 'rgblum', 'flags']); return decode(bytes, [uint8, uint8, uint16, uint8, uint8, uint8, uint8, bitmap, version], ['lorasf', 'txpower', 'rssilimit', 'sendcycle', 'wifichancycle', 'blescantime', 'rgblum', 'flags', 'version']);
} }
if (port === 4) { if (port === 4) {
@ -56,6 +55,14 @@ var bytesToInt = function (bytes) {
return i; return i;
}; };
var version = function (bytes) {
if (bytes.length !== version.BYTES) {
throw new Error('version must have exactly 10 bytes');
}
return String.fromCharCode.apply(null, bytes).split('\u0000')[0];
};
version.BYTES = 10;
var uint8 = function (bytes) { var uint8 = function (bytes) {
if (bytes.length !== uint8.BYTES) { if (bytes.length !== uint8.BYTES) {
throw new Error('uint8 must have exactly 1 byte'); throw new Error('uint8 must have exactly 1 byte');
@ -186,6 +193,7 @@ if (typeof module === 'object' && typeof module.exports !== 'undefined') {
latLng: latLng, latLng: latLng,
hdop: hdop, hdop: hdop,
bitmap: bitmap, bitmap: bitmap,
version: version,
decode: decode decode: decode
}; };
} }

View File

@ -25,6 +25,9 @@ function Decoder(bytes, port) {
decoded.uptime = ((bytes[i++] << 56) | (bytes[i++] << 48) | (bytes[i++] << 40) | (bytes[i++] << 32) | decoded.uptime = ((bytes[i++] << 56) | (bytes[i++] << 48) | (bytes[i++] << 40) | (bytes[i++] << 32) |
(bytes[i++] << 24) | (bytes[i++] << 16) | (bytes[i++] << 8) | bytes[i++]); (bytes[i++] << 24) | (bytes[i++] << 16) | (bytes[i++] << 8) | bytes[i++]);
decoded.temp = bytes[i++]; decoded.temp = bytes[i++];
decoded.memory = ((bytes[i++] << 24) | (bytes[i++] << 16) | (bytes[i++] << 8) | bytes[i++]);
decoded.reset0 = bytes[i++];
decoded.reset1 = bytes[i++];
} }
if (port === 5) { if (port === 5) {

View File

@ -49,4 +49,9 @@ uint16_t read_voltage() {
return voltage; return voltage;
} }
bool batt_sufficient() {
uint16_t volts = read_voltage();
return (( volts < 1000 ) || (volts > OTA_MIN_BATT)); // no battery or battery sufficient
}
#endif // HAS_BATTERY_PROBE #endif // HAS_BATTERY_PROBE

View File

@ -9,5 +9,6 @@
uint16_t read_voltage(void); uint16_t read_voltage(void);
void calibrate_voltage(void); void calibrate_voltage(void);
bool batt_sufficient(void);
#endif #endif

View File

@ -1,22 +1,15 @@
#ifdef HAS_BUTTON #ifdef HAS_BUTTON
#include "globals.h" #include "globals.h"
#include "senddata.h" #include "button.h"
// Local logging tag // Local logging tag
static const char TAG[] = "main"; static const char TAG[] = "main";
void IRAM_ATTR ButtonIRQ() { ButtonPressedIRQ++; }
void readButton() { void readButton() {
if (ButtonPressedIRQ) {
portENTER_CRITICAL(&timerMux);
ButtonPressedIRQ = 0;
portEXIT_CRITICAL(&timerMux);
ESP_LOGI(TAG, "Button pressed"); ESP_LOGI(TAG, "Button pressed");
payload.reset(); payload.reset();
payload.addButton(0x01); payload.addButton(0x01);
SendData(BUTTONPORT); SendData(BUTTONPORT);
}
} }
#endif #endif

View File

@ -1,7 +1,8 @@
#ifndef _BUTTON_H #ifndef _BUTTON_H
#define _BUTTON_H #define _BUTTON_H
void IRAM_ATTR ButtonIRQ(void); #include "senddata.h"
void readButton(void);
void readButton();
#endif #endif

View File

@ -31,6 +31,7 @@ void defaultConfig() {
cfg.rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%) cfg.rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%)
cfg.gpsmode = 1; // 0=disabled, 1=enabled cfg.gpsmode = 1; // 0=disabled, 1=enabled
cfg.monitormode = 0; // 0=disabled, 1=enabled cfg.monitormode = 0; // 0=disabled, 1=enabled
cfg.runmode = 0; // 0=normal, 1=update
strncpy(cfg.version, PROGVERSION, sizeof(cfg.version) - 1); strncpy(cfg.version, PROGVERSION, sizeof(cfg.version) - 1);
} }
@ -143,6 +144,10 @@ void saveConfig() {
flash8 != cfg.monitormode) flash8 != cfg.monitormode)
nvs_set_i8(my_handle, "monitormode", cfg.monitormode); nvs_set_i8(my_handle, "monitormode", cfg.monitormode);
if (nvs_get_i8(my_handle, "runmode", &flash8) != ESP_OK ||
flash8 != cfg.runmode)
nvs_set_i8(my_handle, "runmode", cfg.runmode);
if (nvs_get_i16(my_handle, "rssilimit", &flash16) != ESP_OK || if (nvs_get_i16(my_handle, "rssilimit", &flash16) != ESP_OK ||
flash16 != cfg.rssilimit) flash16 != cfg.rssilimit)
nvs_set_i16(my_handle, "rssilimit", cfg.rssilimit); nvs_set_i16(my_handle, "rssilimit", cfg.rssilimit);
@ -326,6 +331,14 @@ void loadConfig() {
saveConfig(); saveConfig();
} }
if (nvs_get_i8(my_handle, "runmode", &flash8) == ESP_OK) {
cfg.runmode = flash8;
ESP_LOGI(TAG, "Run mode = %d", flash8);
} else {
ESP_LOGI(TAG, "Run mode set to default %d", cfg.runmode);
saveConfig();
}
nvs_close(my_handle); nvs_close(my_handle);
ESP_LOGI(TAG, "Done"); ESP_LOGI(TAG, "Done");
} }

View File

@ -2,18 +2,36 @@
/* Interval can be set in paxcounter.conf (HOMECYCLE) */ /* Interval can be set in paxcounter.conf (HOMECYCLE) */
// Basic config // Basic config
#include "globals.h" #include "cyclic.h"
#include "senddata.h"
// Local logging tag // Local logging tag
static const char TAG[] = "main"; static const char TAG[] = "main";
// do all housekeeping // do all housekeeping
void doHomework() { void doHousekeeping() {
// update uptime counter // update uptime counter
uptime(); uptime();
// check if update mode trigger switch was set
if (cfg.runmode == 1)
ESP.restart();
// task storage debugging //
ESP_LOGD(TAG, "Wifiloop %d bytes left",
uxTaskGetStackHighWaterMark(wifiSwitchTask));
ESP_LOGD(TAG, "IRQhandler %d bytes left",
uxTaskGetStackHighWaterMark(irqHandlerTask));
#ifdef HAS_GPS
ESP_LOGD(TAG, "Gpsloop %d bytes left", uxTaskGetStackHighWaterMark(GpsTask));
#endif
#ifdef HAS_SPI
ESP_LOGD(TAG, "Spiloop %d bytes left", uxTaskGetStackHighWaterMark(SpiTask));
#endif
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
ESP_LOGD(TAG, "LEDloop %d bytes left", uxTaskGetStackHighWaterMark(ledLoopTask));
#endif
// read battery voltage into global variable // read battery voltage into global variable
#ifdef HAS_BATTERY_PROBE #ifdef HAS_BATTERY_PROBE
batt_voltage = read_voltage(); batt_voltage = read_voltage();
@ -39,27 +57,12 @@ void doHomework() {
esp_get_minimum_free_heap_size(), ESP.getFreeHeap()); esp_get_minimum_free_heap_size(), ESP.getFreeHeap());
SendData(COUNTERPORT); // send data before clearing counters SendData(COUNTERPORT); // send data before clearing counters
reset_counters(); // clear macs container and reset all counters reset_counters(); // clear macs container and reset all counters
reset_salt(); // get new salt for salting hashes get_salt(); // get new salt for salting hashes
if (esp_get_minimum_free_heap_size() <= MEM_LOW) // check again if (esp_get_minimum_free_heap_size() <= MEM_LOW) // check again
esp_restart(); // memory leak, reset device esp_restart(); // memory leak, reset device
} }
} // doHomework() } // doHousekeeping()
void checkHousekeeping() {
if (HomeCycleIRQ) {
portENTER_CRITICAL(&timerMux);
HomeCycleIRQ = 0;
portEXIT_CRITICAL(&timerMux);
doHomework();
}
}
void IRAM_ATTR homeCycleIRQ() {
portENTER_CRITICAL(&timerMux);
HomeCycleIRQ++;
portEXIT_CRITICAL(&timerMux);
}
// uptime counter 64bit to prevent millis() rollover after 49 days // uptime counter 64bit to prevent millis() rollover after 49 days
uint64_t uptime() { uint64_t uptime() {

View File

@ -1,9 +1,10 @@
#ifndef _CYCLIC_H #ifndef _CYCLIC_H
#define _CYCLIC_H #define _CYCLIC_H
void doHomework(void); #include "globals.h"
void checkHousekeeping(void); #include "senddata.h"
void homeCycleIRQ(void);
void doHousekeeping(void);
uint64_t uptime(void); uint64_t uptime(void);
void reset_counters(void); void reset_counters(void);
int redirect_log(const char *fmt, va_list args); int redirect_log(const char *fmt, va_list args);

View File

@ -4,7 +4,7 @@
#include "globals.h" #include "globals.h"
#include <esp_spi_flash.h> // needed for reading ESP32 chip attributes #include <esp_spi_flash.h> // needed for reading ESP32 chip attributes
HAS_DISPLAY u8x8(OLED_RST, OLED_SCL, OLED_SDA); HAS_DISPLAY u8x8(OLED_RST, I2C_SCL, I2C_SDA);
// helper string for converting LoRa spread factor values // helper string for converting LoRa spread factor values
#if defined(CFG_eu868) #if defined(CFG_eu868)
@ -13,7 +13,7 @@ const char lora_datarate[] = {"1211100908077BFSNA"};
const char lora_datarate[] = {"100908078CNA121110090807"}; const char lora_datarate[] = {"100908078CNA121110090807"};
#endif #endif
uint8_t DisplayState = 0; uint8_t volatile DisplayState = 0;
// helper function, prints a hex key on display // helper function, prints a hex key on display
void DisplayKey(const uint8_t *key, uint8_t len, bool lsb) { void DisplayKey(const uint8_t *key, uint8_t len, bool lsb) {
@ -25,8 +25,9 @@ void DisplayKey(const uint8_t *key, uint8_t len, bool lsb) {
u8x8.printf("\n"); u8x8.printf("\n");
} }
// show startup screen
void init_display(const char *Productname, const char *Version) { void init_display(const char *Productname, const char *Version) {
// show startup screen
uint8_t buf[32]; uint8_t buf[32];
u8x8.begin(); u8x8.begin();
u8x8.setFont(u8x8_font_chroma48medium8_r); u8x8.setFont(u8x8_font_chroma48medium8_r);
@ -36,14 +37,14 @@ void init_display(const char *Productname, const char *Version) {
u8x8.draw2x2String(0, 0, Productname); u8x8.draw2x2String(0, 0, Productname);
u8x8.setInverseFont(0); u8x8.setInverseFont(0);
u8x8.draw2x2String(2, 2, Productname); u8x8.draw2x2String(2, 2, Productname);
delay(1500); vTaskDelay(1500 / portTICK_PERIOD_MS);
u8x8.clear(); u8x8.clear();
u8x8.setFlipMode(1); u8x8.setFlipMode(1);
u8x8.setInverseFont(1); u8x8.setInverseFont(1);
u8x8.draw2x2String(0, 0, Productname); u8x8.draw2x2String(0, 0, Productname);
u8x8.setInverseFont(0); u8x8.setInverseFont(0);
u8x8.draw2x2String(2, 2, Productname); u8x8.draw2x2String(2, 2, Productname);
delay(1500); vTaskDelay(1500 / portTICK_PERIOD_MS);
u8x8.setFlipMode(0); u8x8.setFlipMode(0);
u8x8.clear(); u8x8.clear();
@ -74,7 +75,7 @@ void init_display(const char *Productname, const char *Version) {
DisplayKey(buf, 8, true); DisplayKey(buf, 8, true);
#endif // HAS_LORA #endif // HAS_LORA
delay(5000); vTaskDelay(3000 / portTICK_PERIOD_MS);
u8x8.clear(); u8x8.clear();
u8x8.setPowerSave(!cfg.screenon); // set display off if disabled u8x8.setPowerSave(!cfg.screenon); // set display off if disabled
u8x8.draw2x2String(0, 0, "PAX:0"); u8x8.draw2x2String(0, 0, "PAX:0");
@ -101,7 +102,7 @@ void refreshtheDisplay() {
if (!DisplayState) if (!DisplayState)
return; return;
uint8_t msgWaiting = 0; uint8_t msgWaiting;
char buff[16]; // 16 chars line buffer char buff[16]; // 16 chars line buffer
// update counter (lines 0-1) // update counter (lines 0-1)
@ -114,7 +115,8 @@ void refreshtheDisplay() {
// update Battery status (line 2) // update Battery status (line 2)
#ifdef HAS_BATTERY_PROBE #ifdef HAS_BATTERY_PROBE
u8x8.setCursor(0, 2); u8x8.setCursor(0, 2);
u8x8.printf(batt_voltage > 4000 ? "B:USB " : "B:%.1fV", batt_voltage / 1000.0); u8x8.printf(batt_voltage > 4000 ? "B:USB " : "B:%.1fV",
batt_voltage / 1000.0);
#endif #endif
// update GPS status (line 2) // update GPS status (line 2)
@ -126,7 +128,7 @@ void refreshtheDisplay() {
u8x8.printf("Sats:%.2d", gps.satellites.value()); u8x8.printf("Sats:%.2d", gps.satellites.value());
u8x8.setInverseFont(0); u8x8.setInverseFont(0);
} else } else
u8x8.printf("Sats:%.d", gps.satellites.value()); u8x8.printf("Sats:%.2d", gps.satellites.value());
#endif #endif
// update bluetooth counter + LoRa SF (line 3) // update bluetooth counter + LoRa SF (line 3)
@ -185,19 +187,4 @@ void refreshtheDisplay() {
} // refreshDisplay() } // refreshDisplay()
void IRAM_ATTR DisplayIRQ() {
portENTER_CRITICAL_ISR(&timerMux);
DisplayTimerIRQ++;
portEXIT_CRITICAL_ISR(&timerMux);
}
void updateDisplay() {
if (DisplayTimerIRQ) {
portENTER_CRITICAL(&timerMux);
DisplayTimerIRQ = 0;
portEXIT_CRITICAL(&timerMux);
refreshtheDisplay();
}
}
#endif // HAS_DISPLAY #endif // HAS_DISPLAY

View File

@ -3,12 +3,11 @@
#include <U8x8lib.h> #include <U8x8lib.h>
extern uint8_t DisplayState; extern uint8_t volatile DisplayState;
extern HAS_DISPLAY u8x8;
void init_display(const char *Productname, const char *Version); void init_display(const char *Productname, const char *Version);
void refreshtheDisplay(void); void refreshtheDisplay(void);
void DisplayKey(const uint8_t *key, uint8_t len, bool lsb); void DisplayKey(const uint8_t *key, uint8_t len, bool lsb);
void updateDisplay(void);
void DisplayIRQ(void);
#endif #endif

View File

@ -4,10 +4,6 @@
// The mother of all embedded development... // The mother of all embedded development...
#include <Arduino.h> #include <Arduino.h>
// attn: increment version after modifications to configData_t truct!
#define PROGVERSION "1.4.23" // use max 10 chars here!
#define PROGNAME "PAXCNT"
// std::set for unified array functions // std::set for unified array functions
#include <set> #include <set>
#include <array> #include <array>
@ -31,6 +27,7 @@ typedef struct {
uint8_t rgblum; // RGB Led luminosity (0..100%) uint8_t rgblum; // RGB Led luminosity (0..100%)
uint8_t gpsmode; // 0=disabled, 1=enabled uint8_t gpsmode; // 0=disabled, 1=enabled
uint8_t monitormode; // 0=disabled, 1=enabled uint8_t monitormode; // 0=disabled, 1=enabled
uint8_t runmode; // 0=normal, 1=update
char version[10]; // Firmware version char version[10]; // Firmware version
} configData_t; } configData_t;
@ -44,23 +41,22 @@ typedef struct {
// global variables // global variables
extern configData_t cfg; // current device configuration extern configData_t cfg; // current device configuration
extern char display_line6[], display_line7[]; // screen buffers extern char display_line6[], display_line7[]; // screen buffers
extern uint8_t channel; // wifi channel rotation counter extern uint8_t volatile channel; // wifi channel rotation counter
extern uint16_t macs_total, macs_wifi, macs_ble, batt_voltage; // display values extern uint16_t volatile macs_total, macs_wifi, macs_ble,
batt_voltage; // display values
extern std::set<uint16_t> macs; // temp storage for MACs extern std::set<uint16_t> macs; // temp storage for MACs
extern hw_timer_t *channelSwitch, *sendCycle; extern hw_timer_t *channelSwitch, *sendCycle;
extern portMUX_TYPE timerMux;
extern volatile int SendCycleTimerIRQ, HomeCycleIRQ, DisplayTimerIRQ,
ChannelTimerIRQ, ButtonPressedIRQ;
extern QueueHandle_t LoraSendQueue, SPISendQueue;
extern std::array<uint64_t, 0xff>::iterator it; extern std::array<uint64_t, 0xff>::iterator it;
extern std::array<uint64_t, 0xff> beacons; extern std::array<uint64_t, 0xff> beacons;
extern TaskHandle_t irqHandlerTask, wifiSwitchTask;
#ifdef HAS_GPS #ifdef HAS_GPS
#include "gps.h" #include "gps.h"
#endif #endif
#ifdef HAS_LED #if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
#include "led.h" #include "led.h"
#endif #endif
@ -70,6 +66,10 @@ extern std::array<uint64_t, 0xff> beacons;
#include "lorawan.h" #include "lorawan.h"
#endif #endif
#ifdef HAS_SPI
#include "spi.h"
#endif
#ifdef HAS_DISPLAY #ifdef HAS_DISPLAY
#include "display.h" #include "display.h"
#endif #endif

View File

@ -5,8 +5,9 @@
// Local logging tag // Local logging tag
static const char TAG[] = "main"; static const char TAG[] = "main";
TinyGPSPlus gps; TinyGPSPlus gps;
gpsStatus_t gps_status; gpsStatus_t gps_status;
TaskHandle_t GpsTask;
// read GPS data and cast to global struct // read GPS data and cast to global struct
void gps_read() { void gps_read() {
@ -25,52 +26,50 @@ void gps_loop(void *pvParameters) {
// initialize and, if needed, configure, GPS // initialize and, if needed, configure, GPS
#if defined GPS_SERIAL #if defined GPS_SERIAL
HardwareSerial GPS_Serial(1); HardwareSerial GPS_Serial(1);
GPS_Serial.begin(GPS_SERIAL);
#elif defined GPS_QUECTEL_L76 #elif defined GPS_QUECTEL_L76
uint8_t ret;
Wire.begin(GPS_QUECTEL_L76, 400000); // I2C connect to GPS device with 400 KHz Wire.begin(GPS_QUECTEL_L76, 400000); // I2C connect to GPS device with 400 KHz
Wire.beginTransmission(GPS_ADDR);
Wire.write(0x00); // dummy write
ret = Wire.endTransmission(); // check if chip is seen on i2c bus
if (ret) {
ESP_LOGE(TAG,
"Quectel L76 GPS chip not found on i2c bus, bus error %d. "
"Stopping GPS-Task.",
ret);
vTaskDelete(GpsTask);
} else {
ESP_LOGI(TAG, "Quectel L76 GPS chip found.");
}
#endif #endif
while (1) { while (1) {
if (cfg.gpsmode) { if (cfg.gpsmode) {
#if defined GPS_SERIAL #if defined GPS_SERIAL
// feed GPS decoder with serial NMEA data from GPS device
// serial connect to GPS device while (GPS_Serial.available()) {
GPS_Serial.begin(GPS_SERIAL); gps.encode(GPS_Serial.read());
while (cfg.gpsmode) {
// feed GPS decoder with serial NMEA data from GPS device
while (GPS_Serial.available()) {
gps.encode(GPS_Serial.read());
}
vTaskDelay(1 / portTICK_PERIOD_MS); // reset watchdog
} }
// after GPS function was disabled, close connect to GPS device
GPS_Serial.end();
#elif defined GPS_QUECTEL_L76 #elif defined GPS_QUECTEL_L76
Wire.requestFrom(GPS_ADDR, 32); // caution: this is a blocking call
Wire.beginTransmission(GPS_ADDR); while (Wire.available()) {
Wire.write(0x00); // dummy write to start read gps.encode(Wire.read());
Wire.endTransmission(); vTaskDelay(2 / portTICK_PERIOD_MS); // 2ms delay according L76 datasheet
Wire.beginTransmission(GPS_ADDR);
while (cfg.gpsmode) {
Wire.requestFrom(GPS_ADDR | 0x01, 32);
while (Wire.available()) {
gps.encode(Wire.read());
vTaskDelay(1 / portTICK_PERIOD_MS); // polling mode: 500ms sleep
}
} }
// after GPS function was disabled, close connect to GPS device #endif
Wire.endTransmission(); } // if (cfg.gpsmode)
#endif // GPS Type vTaskDelay(2 / portTICK_PERIOD_MS); // yield to CPU
}
vTaskDelay(1 / portTICK_PERIOD_MS); // reset watchdog
} // end of infinite loop } // end of infinite loop
vTaskDelete(NULL); // shoud never be reached
} // gps_loop() } // gps_loop()
#endif // HAS_GPS #endif // HAS_GPS

View File

@ -1,9 +1,13 @@
#ifndef _GPS_H #ifndef _GPS_H
#define _GPS_H #define _GPS_H
#include <TinyGPS++.h> // library for parsing NMEA data #include <TinyGPS++.h> // library for parsing NMEA data
#include <TimeLib.h> #include <TimeLib.h>
#ifdef GPS_QUECTEL_L76 // Needed for reading from I2C Bus
#include <Wire.h>
#endif
typedef struct { typedef struct {
uint32_t latitude; uint32_t latitude;
uint32_t longitude; uint32_t longitude;
@ -12,8 +16,10 @@ typedef struct {
uint16_t altitude; uint16_t altitude;
} gpsStatus_t; } gpsStatus_t;
extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe
extern gpsStatus_t gps_status; // Make struct for storing gps data globally available extern gpsStatus_t
gps_status; // Make struct for storing gps data globally available
extern TaskHandle_t GpsTask;
void gps_read(void); void gps_read(void);
void gps_loop(void *pvParameters); void gps_loop(void *pvParameters);

View File

@ -34,5 +34,9 @@
// pin definitions for I2C interface of OLED Display // pin definitions for I2C interface of OLED Display
#define OLED_RST GPIO_NUM_16 // SSD1306 RST #define OLED_RST GPIO_NUM_16 // SSD1306 RST
#define OLED_SDA GPIO_NUM_4 // SD1306 D1+D2 #define I2C_SDA GPIO_NUM_4 // SD1306 D1+D2
#define OLED_SCL GPIO_NUM_15 // SD1306 D0 #define I2C_SCL GPIO_NUM_15 // SD1306 D0
// I2C config for Microchip 24AA02E64 DEVEUI unique address
#define MCP_24AA02E64_I2C_ADDRESS 0x50 // I2C address for the 24AA02E64
#define MCP_24AA02E64_MAC_ADDRESS 0xF8 // Memory adress of unique deveui 64 bits

View File

@ -22,5 +22,5 @@
// Hardware pin definitions for Heltec LoRa-32 Board with OLED SSD1306 I2C Display // Hardware pin definitions for Heltec LoRa-32 Board with OLED SSD1306 I2C Display
#define OLED_RST GPIO_NUM_16 // ESP32 GPIO16 (Pin16) -- SD1306 RST #define OLED_RST GPIO_NUM_16 // ESP32 GPIO16 (Pin16) -- SD1306 RST
#define OLED_SDA GPIO_NUM_4 // ESP32 GPIO4 (Pin4) -- SD1306 D1+D2 #define I2C_SDA GPIO_NUM_4 // ESP32 GPIO4 (Pin4) -- SD1306 D1+D2
#define OLED_SCL GPIO_NUM_15 // ESP32 GPIO15 (Pin15) -- SD1306 D0 #define I2C_SCL GPIO_NUM_15 // ESP32 GPIO15 (Pin15) -- SD1306 D0

26
src/hal/heltecv2.h Normal file
View File

@ -0,0 +1,26 @@
// Hardware related definitions for Heltec V2 LoRa-32 Board
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define HAS_SPI 1 // comment out if device shall not send data via SPI
#define CFG_sx1276_radio 1
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
#define HAS_LED GPIO_NUM_25 // white LED on board
#define HAS_BUTTON GPIO_NUM_0 // button "PROG" on board
// re-define pin definitions of pins_arduino.h
#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 -- SX1276 NSS (Pin19) SPI Chip Select Input
#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 -- SX1276 MOSI (Pin18) SPI Data Input
#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 -- SX1276 MISO (Pin17) SPI Data Output
#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 -- SX1276 SCK (Pin16) SPI Clock Input
// non arduino pin definitions
#define RST GPIO_NUM_14 // ESP32 GPIO18 -- SX1276 NRESET (Pin7) Reset Trigger Input
#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done
#define DIO1 GPIO_NUM_34 // ESP32 GPIO33 -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout
#define DIO2 GPIO_NUM_35 // 32 ESP32 GPIO32 -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only)
// Hardware pin definitions for Heltec LoRa-32 Board with OLED SSD1306 I2C Display
#define OLED_RST GPIO_NUM_16 // ESP32 GPIO16 -- SD1306 RST
#define I2C_SDA GPIO_NUM_4 // ESP32 GPIO4 -- SD1306 D1+D2
#define I2C_SCL GPIO_NUM_15 // ESP32 GPIO15 -- SD1306 D0

View File

@ -31,8 +31,8 @@
// Hardware pin definitions for LoRaNode32 Board with OLED I2C Display // Hardware pin definitions for LoRaNode32 Board with OLED I2C Display
#define OLED_RST U8X8_PIN_NONE // Not reset pin #define OLED_RST U8X8_PIN_NONE // Not reset pin
#define OLED_SDA 14 // ESP32 GPIO14 (Pin14) -- OLED SDA #define I2C_SDA 14 // ESP32 GPIO14 (Pin14) -- OLED SDA
#define OLED_SCL 12 // ESP32 GPIO12 (Pin12) -- OLED SCL #define I2C_SCL 12 // ESP32 GPIO12 (Pin12) -- OLED SCL
// I2C config for Microchip 24AA02E64 DEVEUI unique address // I2C config for Microchip 24AA02E64 DEVEUI unique address
#define MCP_24AA02E64_I2C_ADDRESS 0x50 // I2C address for the 24AA02E64 #define MCP_24AA02E64_I2C_ADDRESS 0x50 // I2C address for the 24AA02E64

View File

@ -32,8 +32,8 @@
// Hardware pin definitions for LoRaNode32 Board with OLED I2C Display // Hardware pin definitions for LoRaNode32 Board with OLED I2C Display
#define OLED_RST U8X8_PIN_NONE // Not reset pin #define OLED_RST U8X8_PIN_NONE // Not reset pin
#define OLED_SDA 21 // ESP32 GPIO21 (Pin21) -- OLED SDA #define I2C_SDA 21 // ESP32 GPIO21 (Pin21) -- OLED SDA
#define OLED_SCL 22 // ESP32 GPIO22 (Pin22) -- OLED SCL #define I2C_SCL 22 // ESP32 GPIO22 (Pin22) -- OLED SCL
// I2C config for Microchip 24AA02E64 DEVEUI unique address // I2C config for Microchip 24AA02E64 DEVEUI unique address
#define MCP_24AA02E64_I2C_ADDRESS 0x50 // I2C address for the 24AA02E64 #define MCP_24AA02E64_I2C_ADDRESS 0x50 // I2C address for the 24AA02E64

View File

@ -3,7 +3,7 @@
#define HAS_LORA 1 // comment out if device shall not send data via LoRa #define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define HAS_SPI 1 // comment out if device shall not send data via SPI #define HAS_SPI 1 // comment out if device shall not send data via SPI
#define CFG_sx1272_radio 1 #define CFG_sx1272_radio 1
#define HAS_LED NOT_A_PIN // LoPy has no on board LED, so we use RGB LED on LoPy #define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED
#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0 #define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0
// Hardware pin definitions for Pycom LoPy board // Hardware pin definitions for Pycom LoPy board
@ -17,26 +17,19 @@
#define DIO2 GPIO_NUM_23 // Pin tied via diode to DIO0 #define DIO2 GPIO_NUM_23 // Pin tied via diode to DIO0
// select WIFI antenna (internal = onboard / external = u.fl socket) // select WIFI antenna (internal = onboard / external = u.fl socket)
#define HAS_ANTENNA_SWITCH 16 // pin for switching wifi antenna #define HAS_ANTENNA_SWITCH GPIO_NUM_16 // pin for switching wifi antenna
#define WIFI_ANTENNA 0 // 0 = internal, 1 = external #define WIFI_ANTENNA 0 // 0 = internal, 1 = external
// !!EXPERIMENTAL - not tested yet!! // uncomment this only if your LoPy runs on a PYTRACK BOARD
// uncomment this only if your LoPy runs on a Pytrack expansion board with GPS
// see http://www.quectel.com/UploadImage/Downlad/Quectel_L76-L_I2C_Application_Note_V1.0.pdf
//#define HAS_GPS 1 //#define HAS_GPS 1
//#define GPS_QUECTEL_L76 GPIO_NUM_25, GPIO_NUM_26 // SDA (P22), SCL (P21) //#define GPS_QUECTEL_L76 GPIO_NUM_25, GPIO_NUM_26 // SDA (P22), SCL (P21)
//#define GPS_ADDR 0x10 //#define GPS_ADDR 0x10
//#define HAS_BUTTON GPIO_NUM_37 // (P14)
//#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
// uncomment this only if your LoPy runs on a expansion board 3.0 // uncomment this only if your LoPy runs on a EXPANSION BOARD
//#define HAS_BATTERY_PROBE ADC1_GPIO39_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7 //#define HAS_LED GPIO_NUM_12 // use if LoPy is on Expansion Board, this has a user LED
//#define BATT_FACTOR 2 // voltage divider 1MOhm/1MOhm on board //#define LED_ACTIVE_LOW 1 // use if LoPy is on Expansion Board, this has a user LED
//#define HAS_BUTTON GPIO_NUM_37 // (P14) //#define HAS_BUTTON GPIO_NUM_13 // user button on expansion board
//#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown //#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
// uncomment this only if your LoPy runs on a expansion board 2.0
//#define HAS_BATTERY_PROBE ADC1_GPIO39_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7 //#define HAS_BATTERY_PROBE ADC1_GPIO39_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
//#define BATT_FACTOR 4 // voltage divider 115kOhm/56kOhm on board //#define BATT_FACTOR 2 // voltage divider 1MOhm/1MOhm -> expansion board 3.0
//#define HAS_BUTTON GPIO_NUM_13 // (P10) //#define BATT_FACTOR 4 // voltage divider 115kOhm/56kOhm -> expansion board 2.0
//#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown

View File

@ -1,10 +1,10 @@
// Hardware related definitions for Pycom LoPy Board (not: LoPy4) // Hardware related definitions for Pycom LoPy4 Board
#define HAS_LORA 1 // comment out if device shall not send data via LoRa #define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define HAS_SPI 1 // comment out if device shall not send data via SPI #define HAS_SPI 1 // comment out if device shall not send data via SPI
#define CFG_sx1276_radio 1 #define CFG_sx1276_radio 1
#define HAS_LED NOT_A_PIN // LoPy4 has no on board LED, so we use RGB LED on LoPy4 //#define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED
#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0 #define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0 (P2)
#define BOARD_HAS_PSRAM // use extra 4MB extern RAM #define BOARD_HAS_PSRAM // use extra 4MB extern RAM
// Hardware pin definitions for Pycom LoPy4 board // Hardware pin definitions for Pycom LoPy4 board
@ -18,26 +18,19 @@
#define DIO2 GPIO_NUM_23 // Pin tied via diode to DIO0 #define DIO2 GPIO_NUM_23 // Pin tied via diode to DIO0
// select WIFI antenna (internal = onboard / external = u.fl socket) // select WIFI antenna (internal = onboard / external = u.fl socket)
#define HAS_ANTENNA_SWITCH 21 // pin for switching wifi antenna #define HAS_ANTENNA_SWITCH GPIO_NUM_21 // pin for switching wifi antenna (P12)
#define WIFI_ANTENNA 0 // 0 = internal, 1 = external #define WIFI_ANTENNA 0 // 0 = internal, 1 = external
// !!EXPERIMENTAL - not tested yet!! // uncomment this only if your LoPy runs on a PYTRACK BOARD
// uncomment this only if your LoPy runs on a Pytrack expansion board with GPS
// see http://www.quectel.com/UploadImage/Downlad/Quectel_L76-L_I2C_Application_Note_V1.0.pdf
//#define HAS_GPS 1 //#define HAS_GPS 1
//#define GPS_QUECTEL_L76 GPIO_NUM_25, GPIO_NUM_26 // SDA (P22), SCL (P21) //#define GPS_QUECTEL_L76 GPIO_NUM_25, GPIO_NUM_26 // SDA (P22), SCL (P21)
//#define GPS_ADDR 0x10 //#define GPS_ADDR 0x10
//#define HAS_BUTTON GPIO_NUM_37 // (P14)
//#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
// uncomment this only if your LoPy runs on a expansion board 3.0 // uncomment this only if your LoPy runs on a EXPANSION BOARD
#define HAS_BATTERY_PROBE ADC1_GPIO39_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7 #define HAS_LED GPIO_NUM_12 // use if LoPy is on Expansion Board, this has a user LED
#define BATT_FACTOR 2 // voltage divider 1MOhm/1MOhm on board #define LED_ACTIVE_LOW 1 // use if LoPy is on Expansion Board, this has a user LED
#define HAS_BUTTON GPIO_NUM_37 // (P14) #define HAS_BUTTON GPIO_NUM_13 // user button on expansion board
#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown #define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
#define HAS_BATTERY_PROBE ADC1_GPIO39_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
// uncomment this only if your LoPy runs on a expansion board 2.0 #define BATT_FACTOR 2 // voltage divider 1MOhm/1MOhm -> expansion board 3.0
//#define HAS_BATTERY_PROBE ADC1_GPIO39_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7 //#define BATT_FACTOR 4 // voltage divider 115kOhm/56kOhm -> expansion board 2.0
//#define BATT_FACTOR 4 // voltage divider 115kOhm/56kOhm on board
//#define HAS_BUTTON GPIO_NUM_13 // (P10)
//#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown

41
src/hal/octopus32.h Normal file
View File

@ -0,0 +1,41 @@
// Hardware related definitions for #IoT Octopus32 with the Adafruit LoRaWAN Wing
// You can use this configuration also with the Adafruit ESP32 Feather + the LoRaWAN Wing
// In this config we use the Adafruit OLED Wing which is only 128x32 pixel, need to find a smaller font
// disable brownout detection (avoid unexpected reset on some boards)
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
#define HAS_LED 13 // ESP32 GPIO12 (pin22) On Board LED
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
//#define HAS_RGB_LED 13 // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
//#define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield
//#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define HAS_SPI 1 // comment out if device shall not send data via SPI
#define CFG_sx1276_radio 1 // RFM95 module
// re-define pin definitions of pins_arduino.h
#define PIN_SPI_SS 14 //14 // ESP32 GPIO5 (Pin5) -- SX1276 NSS (Pin19) SPI Chip Select Input
#define PIN_SPI_MOSI 18 // ESP32 GPIO23 (Pin23) -- SX1276 MOSI (Pin18) SPI Data Input
#define PIN_SPI_MISO 19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output
#define PIN_SPI_SCK 5 // ESP32 GPIO18 (Pin18) -- SX1276 SCK (Pin16) SPI Clock Input
//GPIO_NUM_
// non arduino pin definitions
#define RST LMIC_UNUSED_PIN // ESP32 GPIO25 (Pin25) -- SX1276 NRESET (Pin7) Reset Trigger Input
#define DIO0 33 // ESP32 GPIO27 (Pin27) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done
#define DIO1 33 // ESP32 GPIO26 (Pin26) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout
#define DIO2 LMIC_UNUSED_PIN // 4 ESP32 GPIO4 (Pin4) -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only)
#define DIO5 LMIC_UNUSED_PIN // 35 ESP32 GPIO35 (Pin35) -- SX1276 DIO5 not used by LMIC for LoRa (Timeout for FSK only)
// Hardware pin definitions for LoRaNode32 Board with OLED I2C Display
#define OLED_RST U8X8_PIN_NONE // Not reset pin
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // U8X8_SSD1306_128X32_UNIVISION_SW_I2C //
//#define DISPLAY_FLIP 1 // uncomment this for rotated display
#define I2C_SDA 23 //21 // ESP32 GPIO14 (Pin14) -- OLED SDA
#define I2C_SCL 22 //22 // ESP32 GPIO12 (Pin12) -- OLED SCL
// I2C config for Microchip 24AA02E64 DEVEUI unique address
//#define MCP_24AA02E64_I2C_ADDRESS 0x50 // I2C address for the 24AA02E64
//#define MCP_24AA02E64_MAC_ADDRESS 0xF8 // Memory adress of unique deveui 64 bits

View File

@ -24,5 +24,5 @@
// Hardware pin definitions for TTGOv1 Board with OLED SSD1306 I2C Display // Hardware pin definitions for TTGOv1 Board with OLED SSD1306 I2C Display
#define OLED_RST GPIO_NUM_16 // ESP32 GPIO16 (Pin16) -- SD1306 Reset #define OLED_RST GPIO_NUM_16 // ESP32 GPIO16 (Pin16) -- SD1306 Reset
#define OLED_SDA GPIO_NUM_4 // ESP32 GPIO4 (Pin4) -- SD1306 Data #define I2C_SDA GPIO_NUM_4 // ESP32 GPIO4 (Pin4) -- SD1306 Data
#define OLED_SCL GPIO_NUM_15 // ESP32 GPIO15 (Pin15) -- SD1306 Clock #define I2C_SCL GPIO_NUM_15 // ESP32 GPIO15 (Pin15) -- SD1306 Clock

View File

@ -25,8 +25,8 @@
// Hardware pin definitions for TTGO V2 Board with OLED SSD1306 0,96" I2C Display // Hardware pin definitions for TTGO V2 Board with OLED SSD1306 0,96" I2C Display
#define OLED_RST U8X8_PIN_NONE // connected to CPU RST/EN #define OLED_RST U8X8_PIN_NONE // connected to CPU RST/EN
#define OLED_SDA GPIO_NUM_21 // ESP32 GPIO21 -- SD1306 D1+D2 #define I2C_SDA GPIO_NUM_21 // ESP32 GPIO21 -- SD1306 D1+D2
#define OLED_SCL GPIO_NUM_22 // ESP32 GPIO22 -- SD1306 D0 #define I2C_SCL GPIO_NUM_22 // ESP32 GPIO22 -- SD1306 D0
/* source: /* source:
https://www.thethingsnetwork.org/forum/t/big-esp32-sx127x-topic-part-2/11973 https://www.thethingsnetwork.org/forum/t/big-esp32-sx127x-topic-part-2/11973

View File

@ -1,69 +0,0 @@
/* Hardware related definitions for TTGO V2.1 Board
/ ATTENTION: check your board version!
/ Different versions are on the market which need different settings in this file:
/ - without label -> use settings (2)
/ - labeled V1.5 on pcb -> use settings (2)
/ - labeled V1.6 on pcb -> use settings (1)
/ Choose the right configuration below
*/
/*
// (1) settings for board labeled "T3_V1.6" on pcb
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define HAS_SPI 1 // comment out if device shall not send data via SPI
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
#define HAS_LED GPIO_NUM_25 // green on board LED
#define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // uses GPIO7
#define BATT_FACTOR 2 // voltage divider 100k/100k on board
// re-define pin definitions of pins_arduino.h
#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select Input
#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input
#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output
#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input
// non arduino pin definitions
#define RST GPIO_NUM_23 // ESP32 GPIO23 <-> HPD13A RESET
#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 <-> HPD13A IO0
#define DIO1 GPIO_NUM_33 // ESP32 GPIO33 <-> HPDIO1 <-> HPD13A IO1
#define DIO2 GPIO_NUM_32 // ESP32 GPIO32 <-> HPDIO2 <-> HPD13A IO2
// Hardware pin definitions for TTGO V2 Board with OLED SSD1306 0,96" I2C Display
#define OLED_RST U8X8_PIN_NONE // connected to CPU RST/EN
#define OLED_SDA GPIO_NUM_21 // ESP32 GPIO21 -- SD1306 D1+D2
#define OLED_SCL GPIO_NUM_22 // ESP32 GPIO22 -- SD1306 D0
*/
// (2) settings for boards without label on pcb, or labeled v1.5 on pcb
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define HAS_SPI 1 // comment out if device shall not send data via SPI
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
#define HAS_LED NOT_A_PIN // no usable LED on board
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
#define DISPLAY_FLIP 1 // rotated display
#define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // uses GPIO7
#define BATT_FACTOR 2 // voltage divider 100k/100k on board
// re-define pin definitions of pins_arduino.h
#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select Input
#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input
#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output
#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input
// non arduino pin definitions
#define RST LMIC_UNUSED_PIN // connected to ESP32 RST/EN (old board)
//#define RST GPIO_NUM_12 // (boards labeled v1.5)
#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 <-> HPD13A IO0
#define DIO1 GPIO_NUM_33 // ESP32 GPIO33 <-> HPDIO1 <-> HPD13A IO1
#define DIO2 GPIO_NUM_32 // ESP32 GPIO32 <-> HPDIO2 <-> HPD13A IO2
// Hardware pin definitions for TTGO V2 Board with OLED SSD1306 0,96" I2C Display
#define OLED_RST U8X8_PIN_NONE // connected to CPU RST/EN
#define OLED_SDA GPIO_NUM_21 // ESP32 GPIO21 -- SD1306 D1+D2
#define OLED_SCL GPIO_NUM_22 // ESP32 GPIO22 -- SD1306 D0

30
src/hal/ttgov21new.h Normal file
View File

@ -0,0 +1,30 @@
/* Hardware related definitions for TTGO V2.1 Board
// ATTENTION: check your board version!
// This settings are for boards labeled v1.6 on pcb, NOT for v1.5 or older
*/
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define HAS_SPI 1 // comment out if device shall not send data via SPI
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
#define HAS_LED GPIO_NUM_25 // green on board LED
#define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // uses GPIO7
#define BATT_FACTOR 2 // voltage divider 100k/100k on board
// re-define pin definitions of pins_arduino.h
#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select Input
#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input
#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output
#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input
// non arduino pin definitions
#define RST GPIO_NUM_23 // ESP32 GPIO23 <-> HPD13A RESET
#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 <-> HPD13A IO0
#define DIO1 GPIO_NUM_33 // ESP32 GPIO33 <-> HPDIO1 <-> HPD13A IO1
#define DIO2 GPIO_NUM_32 // ESP32 GPIO32 <-> HPDIO2 <-> HPD13A IO2
// Hardware pin definitions for TTGO V2 Board with OLED SSD1306 0,96" I2C Display
#define OLED_RST U8X8_PIN_NONE // connected to CPU RST/EN
#define I2C_SDA GPIO_NUM_21 // ESP32 GPIO21 -- SD1306 D1+D2
#define I2C_SCL GPIO_NUM_22 // ESP32 GPIO22 -- SD1306 D0

33
src/hal/ttgov21old.h Normal file
View File

@ -0,0 +1,33 @@
/* Hardware related definitions for TTGO V2.1 Board
// ATTENTION: check your board version!
// This settings are for boards without label on pcb, or labeled v1.5 on pcb
*/
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define HAS_SPI 1 // comment out if device shall not send data via SPI
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
#define HAS_LED NOT_A_PIN // no usable LED on board
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
//#define DISPLAY_FLIP 1 // rotated display
//#define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // uses GPIO7
//#define BATT_FACTOR 2 // voltage divider 100k/100k on board
// re-define pin definitions of pins_arduino.h
#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select Input
#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input
#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output
#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input
// non arduino pin definitions
#define RST LMIC_UNUSED_PIN // connected to ESP32 RST/EN (old board)
//#define RST GPIO_NUM_12 // (boards labeled v1.5)
#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 <-> HPD13A IO0
#define DIO1 GPIO_NUM_33 // ESP32 GPIO33 <-> HPDIO1 <-> HPD13A IO1
#define DIO2 GPIO_NUM_32 // ESP32 GPIO32 <-> HPDIO2 <-> HPD13A IO2
// Hardware pin definitions for TTGO V2 Board with OLED SSD1306 0,96" I2C Display
#define OLED_RST U8X8_PIN_NONE // connected to CPU RST/EN
#define I2C_SDA GPIO_NUM_21 // ESP32 GPIO21 -- SD1306 D1+D2
#define I2C_SCL GPIO_NUM_22 // ESP32 GPIO22 -- SD1306 D0

View File

@ -34,9 +34,9 @@
* SOFTWARE. * SOFTWARE.
*/ */
#include <inttypes.h> #include "hash.h"
uint32_t rokkit(const char *data, int len) { uint32_t IRAM_ATTR rokkit(const char *data, int len) {
uint32_t hash, tmp; uint32_t hash, tmp;
int rem; int rem;

View File

@ -1,6 +1,9 @@
#ifndef _HASH_H #ifndef _HASH_H
#define _HASH_H #define _HASH_H
uint32_t rokkit(const char *data, int len); #include <Arduino.h>
#include <inttypes.h>
#endif uint32_t IRAM_ATTR rokkit(const char *data, int len);
#endif

74
src/irqhandler.cpp Normal file
View File

@ -0,0 +1,74 @@
#include "irqhandler.h"
// Local logging tag
static const char TAG[] = "main";
// irq handler task, handles all our application level interrupts
void irqHandler(void *pvParameters) {
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
uint32_t InterruptStatus;
// task remains in blocked state until it is notified by an irq
for (;;) {
xTaskNotifyWait(
0x00, // Don't clear any bits on entry
ULONG_MAX, // Clear all bits on exit
&InterruptStatus, // Receives the notification value
portMAX_DELAY); // wait forever (missing error handling here...)
// button pressed?
#ifdef HAS_BUTTON
if (InterruptStatus & BUTTON_IRQ)
readButton();
#endif
// display needs refresh?
#ifdef HAS_DISPLAY
if (InterruptStatus & DISPLAY_IRQ)
refreshtheDisplay();
#endif
// are cyclic tasks due?
if (InterruptStatus & CYCLIC_IRQ)
doHousekeeping();
// is time to send the payload?
if (InterruptStatus & SENDPAYLOAD_IRQ)
sendPayload();
}
vTaskDelete(NULL); // shoud never be reached
}
// esp32 hardware timer triggered interrupt service routines
// they notify the irq handler task
void IRAM_ATTR ChannelSwitchIRQ() {
xTaskNotifyGive(wifiSwitchTask);
portYIELD_FROM_ISR();
}
void IRAM_ATTR homeCycleIRQ() {
xTaskNotifyFromISR(irqHandlerTask, CYCLIC_IRQ, eSetBits, NULL);
portYIELD_FROM_ISR();
}
void IRAM_ATTR SendCycleIRQ() {
xTaskNotifyFromISR(irqHandlerTask, SENDPAYLOAD_IRQ, eSetBits, NULL);
portYIELD_FROM_ISR();
}
#ifdef HAS_DISPLAY
void IRAM_ATTR DisplayIRQ() {
xTaskNotifyFromISR(irqHandlerTask, DISPLAY_IRQ, eSetBits, NULL);
portYIELD_FROM_ISR();
}
#endif
#ifdef HAS_BUTTON
void IRAM_ATTR ButtonIRQ() {
xTaskNotifyFromISR(irqHandlerTask, BUTTON_IRQ, eSetBits, NULL);
portYIELD_FROM_ISR();
}
#endif

28
src/irqhandler.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef _IRQHANDLER_H
#define _IRQHANDLER_H
#define DISPLAY_IRQ 0x01
#define BUTTON_IRQ 0x02
#define SENDPAYLOAD_IRQ 0x04
#define CYCLIC_IRQ 0x08
#include "globals.h"
#include "cyclic.h"
#include "senddata.h"
void irqHandler(void *pvParameters);
void IRAM_ATTR ChannelSwitchIRQ();
void IRAM_ATTR homeCycleIRQ();
void IRAM_ATTR SendCycleIRQ();
#ifdef HAS_DISPLAY
#include "display.h"
void IRAM_ATTR DisplayIRQ();
#endif
#ifdef HAS_BUTTON
#include "button.h"
void IRAM_ATTR ButtonIRQ();
#endif
#endif

View File

@ -6,6 +6,8 @@ led_states LEDState = LED_OFF; // LED state global for state machine
led_states previousLEDState = led_states previousLEDState =
LED_ON; // This will force LED to be off at boot since State is OFF LED_ON; // This will force LED to be off at boot since State is OFF
TaskHandle_t ledLoopTask;
uint16_t LEDColor = COLOR_NONE, LEDBlinkDuration = 0; // state machine variables uint16_t LEDColor = COLOR_NONE, LEDBlinkDuration = 0; // state machine variables
unsigned long LEDBlinkStarted = 0; // When (in millis() led blink started) unsigned long LEDBlinkStarted = 0; // When (in millis() led blink started)
@ -94,69 +96,75 @@ void blink_LED(uint16_t set_color, uint16_t set_blinkduration) {
LEDState = LED_ON; // Let main set LED on LEDState = LED_ON; // Let main set LED on
} }
void led_loop() { void ledLoop(void *parameter) {
// Custom blink running always have priority other LoRaWAN led management while (1) {
if (LEDBlinkStarted && LEDBlinkDuration) { // Custom blink running always have priority other LoRaWAN led management
// Custom blink is finished, let this order, avoid millis() overflow if (LEDBlinkStarted && LEDBlinkDuration) {
if ((millis() - LEDBlinkStarted) >= LEDBlinkDuration) { // Custom blink is finished, let this order, avoid millis() overflow
// Led becomes off, and stop blink if ((millis() - LEDBlinkStarted) >= LEDBlinkDuration) {
LEDState = LED_OFF; // Led becomes off, and stop blink
LEDBlinkStarted = 0; LEDState = LED_OFF;
LEDBlinkDuration = 0; LEDBlinkStarted = 0;
LEDColor = COLOR_NONE; LEDBlinkDuration = 0;
LEDColor = COLOR_NONE;
} else {
// In case of LoRaWAN led management blinked off
LEDState = LED_ON;
}
// No custom blink, check LoRaWAN state
} else { } else {
// In case of LoRaWAN led management blinked off
LEDState = LED_ON;
}
// No custom blink, check LoRaWAN state
} else {
#ifdef HAS_LORA #ifdef HAS_LORA
// LED indicators for viusalizing LoRaWAN state // LED indicators for viusalizing LoRaWAN state
if (LMIC.opmode & (OP_JOINING | OP_REJOIN)) { if (LMIC.opmode & (OP_JOINING | OP_REJOIN)) {
LEDColor = COLOR_YELLOW; LEDColor = COLOR_YELLOW;
// quick blink 20ms on each 1/5 second // quick blink 20ms on each 1/5 second
LEDState = ((millis() % 200) < 20) ? LED_ON : LED_OFF; // TX data pending LEDState =
} else if (LMIC.opmode & (OP_TXDATA | OP_TXRXPEND)) { ((millis() % 200) < 20) ? LED_ON : LED_OFF; // TX data pending
LEDColor = COLOR_BLUE; } else if (LMIC.opmode & (OP_TXDATA | OP_TXRXPEND)) {
// small blink 10ms on each 1/2sec (not when joining) LEDColor = COLOR_BLUE;
LEDState = ((millis() % 500) < 10) ? LED_ON : LED_OFF; // small blink 10ms on each 1/2sec (not when joining)
// This should not happen so indicate a problem LEDState = ((millis() % 500) < 10) ? LED_ON : LED_OFF;
} else if (LMIC.opmode & // This should not happen so indicate a problem
((OP_TXDATA | OP_TXRXPEND | OP_JOINING | OP_REJOIN) == 0)) { } else if (LMIC.opmode &
LEDColor = COLOR_RED; ((OP_TXDATA | OP_TXRXPEND | OP_JOINING | OP_REJOIN) == 0)) {
// heartbeat long blink 200ms on each 2 seconds LEDColor = COLOR_RED;
LEDState = ((millis() % 2000) < 200) ? LED_ON : LED_OFF; // heartbeat long blink 200ms on each 2 seconds
} else LEDState = ((millis() % 2000) < 200) ? LED_ON : LED_OFF;
} else
#endif // HAS_LORA #endif // HAS_LORA
{ {
// led off // led off
LEDColor = COLOR_NONE; LEDColor = COLOR_NONE;
LEDState = LED_OFF; LEDState = LED_OFF;
}
} }
} // led need to change state? avoid digitalWrite() for nothing
// led need to change state? avoid digitalWrite() for nothing if (LEDState != previousLEDState) {
if (LEDState != previousLEDState) { if (LEDState == LED_ON) {
if (LEDState == LED_ON) { rgb_set_color(LEDColor);
rgb_set_color(LEDColor);
#ifdef LED_ACTIVE_LOW #ifdef LED_ACTIVE_LOW
digitalWrite(HAS_LED, LOW); digitalWrite(HAS_LED, LOW);
#else #else
digitalWrite(HAS_LED, HIGH); digitalWrite(HAS_LED, HIGH);
#endif #endif
} else { } else {
rgb_set_color(COLOR_NONE); rgb_set_color(COLOR_NONE);
#ifdef LED_ACTIVE_LOW #ifdef LED_ACTIVE_LOW
digitalWrite(HAS_LED, HIGH); digitalWrite(HAS_LED, HIGH);
#else #else
digitalWrite(HAS_LED, LOW); digitalWrite(HAS_LED, LOW);
#endif #endif
}
previousLEDState = LEDState;
} }
previousLEDState = LEDState; // give yield to CPU
} vTaskDelay(2 / portTICK_PERIOD_MS);
}; // led_loop() } // while(1)
vTaskDelete(NULL); // shoud never be reached
}; // ledloop()
#endif // #if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) #endif // #if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)

View File

@ -31,9 +31,11 @@ struct RGBColor {
enum led_states { LED_OFF, LED_ON }; enum led_states { LED_OFF, LED_ON };
extern TaskHandle_t ledLoopTask;
// Exported Functions // Exported Functions
void rgb_set_color(uint16_t hue); void rgb_set_color(uint16_t hue);
void blink_LED(uint16_t set_color, uint16_t set_blinkduration); void blink_LED(uint16_t set_color, uint16_t set_blinkduration);
void led_loop(); void ledLoop(void *parameter);
#endif #endif

View File

@ -1,16 +1,14 @@
#ifdef HAS_LORA #ifdef HAS_LORA
// Basic Config // Basic Config
#include "globals.h" #include "lorawan.h"
#include "rcommand.h"
#ifdef MCP_24AA02E64_I2C_ADDRESS
#include <Wire.h> // Needed for 24AA02E64, does not hurt anything if included and not used
#endif
// Local logging Tag // Local logging Tag
static const char TAG[] = "lora"; static const char TAG[] = "lora";
osjob_t sendjob;
QueueHandle_t LoraSendQueue;
// LMIC enhanced Pin mapping // LMIC enhanced Pin mapping
const lmic_pinmap lmic_pins = {.mosi = PIN_SPI_MOSI, const lmic_pinmap lmic_pins = {.mosi = PIN_SPI_MOSI,
.miso = PIN_SPI_MISO, .miso = PIN_SPI_MISO,
@ -37,6 +35,27 @@ void gen_lora_deveui(uint8_t *pdeveui) {
} }
} }
/* new version, does it with well formed mac according IEEE spec, but is
breaking change
// DevEUI generator using devices's MAC address
void gen_lora_deveui(uint8_t *pdeveui) {
uint8_t *p = pdeveui, dmac[6];
ESP_ERROR_CHECK(esp_efuse_mac_get_default(dmac));
// deveui is LSB, we reverse it so TTN DEVEUI display
// will remain the same as MAC address
// MAC is 6 bytes, devEUI 8, set middle 2 ones
// to an arbitrary value
*p++ = dmac[5];
*p++ = dmac[4];
*p++ = dmac[3];
*p++ = 0xfe;
*p++ = 0xff;
*p++ = dmac[2];
*p++ = dmac[1];
*p++ = dmac[0];
}
*/
// Function to do a byte swap in a byte array // Function to do a byte swap in a byte array
void RevBytes(unsigned char *b, size_t c) { void RevBytes(unsigned char *b, size_t c) {
u1_t i; u1_t i;
@ -77,29 +96,34 @@ void os_getDevEui(u1_t *buf) {
void get_hard_deveui(uint8_t *pdeveui) { void get_hard_deveui(uint8_t *pdeveui) {
// read DEVEUI from Microchip 24AA02E64 2Kb serial eeprom if present // read DEVEUI from Microchip 24AA02E64 2Kb serial eeprom if present
#ifdef MCP_24AA02E64_I2C_ADDRESS #ifdef MCP_24AA02E64_I2C_ADDRESS
uint8_t i2c_ret; uint8_t i2c_ret;
// Init this just in case, no more to 100KHz // Init this just in case, no more to 100KHz
Wire.begin(OLED_SDA, OLED_SCL, 100000); Wire.begin(I2C_SDA, I2C_SCL, 100000);
Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS); Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS);
Wire.write(MCP_24AA02E64_MAC_ADDRESS); Wire.write(MCP_24AA02E64_MAC_ADDRESS);
i2c_ret = Wire.endTransmission(); i2c_ret = Wire.endTransmission();
// check if device seen on i2c bus
// check if device was seen on i2c bus
if (i2c_ret == 0) { if (i2c_ret == 0) {
char deveui[32] = ""; char deveui[32] = "";
uint8_t data; uint8_t data;
Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS); Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS);
Wire.write(MCP_24AA02E64_MAC_ADDRESS); Wire.write(MCP_24AA02E64_MAC_ADDRESS);
Wire.endTransmission();
Wire.requestFrom(MCP_24AA02E64_I2C_ADDRESS, 8); Wire.requestFrom(MCP_24AA02E64_I2C_ADDRESS, 8);
while (Wire.available()) { while (Wire.available()) {
data = Wire.read(); data = Wire.read();
sprintf(deveui + strlen(deveui), "%02X ", data); sprintf(deveui + strlen(deveui), "%02X ", data);
*pdeveui++ = data; *pdeveui++ = data;
} }
i2c_ret = Wire.endTransmission(); ESP_LOGI(TAG, "Serial EEPROM found, read DEVEUI %s", deveui);
ESP_LOGI(TAG, "Serial EEPROM 24AA02E64 found, read DEVEUI %s", deveui); } else
} else { ESP_LOGI(TAG, "Could not read DEVEUI from serial EEPROM");
ESP_LOGI(TAG, "Serial EEPROM 24AA02E64 not found ret=%d", i2c_ret);
}
// Set back to 400KHz to speed up OLED // Set back to 400KHz to speed up OLED
Wire.setClock(400000); Wire.setClock(400000);
#endif // MCP 24AA02E64 #endif // MCP 24AA02E64
@ -183,6 +207,9 @@ void onEvent(ev_t ev) {
// the library) // the library)
switch_lora(cfg.lorasf, cfg.txpower); switch_lora(cfg.lorasf, cfg.txpower);
// kickoff first send job
os_setCallback(&sendjob, lora_send);
// show effective LoRa parameters after join // show effective LoRa parameters after join
ESP_LOGI(TAG, "ADR=%d, SF=%d, TXPOWER=%d", cfg.adrmode, cfg.lorasf, ESP_LOGI(TAG, "ADR=%d, SF=%d, TXPOWER=%d", cfg.adrmode, cfg.lorasf,
cfg.txpower); cfg.txpower);
@ -196,11 +223,9 @@ void onEvent(ev_t ev) {
if (LMIC.dataLen) { if (LMIC.dataLen) {
ESP_LOGI(TAG, "Received %d bytes of payload, RSSI %d SNR %d", ESP_LOGI(TAG, "Received %d bytes of payload, RSSI %d SNR %d",
LMIC.dataLen, LMIC.rssi, (signed char)LMIC.snr / 4); LMIC.dataLen, LMIC.rssi, (signed char)LMIC.snr);
// LMIC.snr = SNR twos compliment [dB] * 4
// LMIC.rssi = RSSI [dBm] (-196...+63)
sprintf(display_line6, "RSSI %d SNR %d", LMIC.rssi, sprintf(display_line6, "RSSI %d SNR %d", LMIC.rssi,
(signed char)LMIC.snr / 4); (signed char)LMIC.snr);
// check if command is received on command port, then call interpreter // check if command is received on command port, then call interpreter
if ((LMIC.txrxFlags & TXRX_PORT) && if ((LMIC.txrxFlags & TXRX_PORT) &&
@ -222,17 +247,6 @@ void onEvent(ev_t ev) {
} // onEvent() } // onEvent()
// LMIC FreeRTos Task
void lorawan_loop(void *pvParameters) {
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
while (1) {
os_runloop_once(); // execute LMIC jobs
vTaskDelay(1 / portTICK_PERIOD_MS); // reset watchdog
}
}
// helper function to assign LoRa datarates to numeric spreadfactor values // helper function to assign LoRa datarates to numeric spreadfactor values
void switch_lora(uint8_t sf, uint8_t tx) { void switch_lora(uint8_t sf, uint8_t tx) {
if (tx > 20) if (tx > 20)
@ -280,4 +294,25 @@ void switch_lora(uint8_t sf, uint8_t tx) {
} }
} }
void lora_send(osjob_t *job) {
MessageBuffer_t SendBuffer;
// Check if there is a pending TX/RX job running, if yes don't eat data
// since it cannot be sent right now
if ((LMIC.opmode & (OP_JOINING | OP_REJOIN | OP_TXDATA | OP_POLL)) != 0) {
// waiting for LoRa getting ready
} else {
if (xQueueReceive(LoraSendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
// SendBuffer gets struct MessageBuffer with next payload from queue
LMIC_setTxData2(SendBuffer.MessagePort, SendBuffer.Message,
SendBuffer.MessageSize, (cfg.countermode & 0x02));
ESP_LOGI(TAG, "%d bytes sent to LoRa", SendBuffer.MessageSize);
sprintf(display_line7, "PACKET QUEUED");
}
}
// reschedule job every 0,5 - 1 sec. including a bit of random to prevent
// systematic collisions
os_setTimedCallback(job, os_getTime() + 500 + ms2osticks(random(500)),
lora_send);
}
#endif // HAS_LORA #endif // HAS_LORA

View File

@ -1,11 +1,21 @@
#ifndef _LORAWAN_H #ifndef _LORAWAN_H
#define _LORAWAN_H #define _LORAWAN_H
#include "globals.h"
#include "rcommand.h"
// LMIC-Arduino LoRaWAN Stack // LMIC-Arduino LoRaWAN Stack
#include <lmic.h> #include <lmic.h>
#include <hal/hal.h> #include <hal/hal.h>
#include "loraconf.h" #include "loraconf.h"
// Needed for 24AA02E64, does not hurt anything if included and not used
#ifdef MCP_24AA02E64_I2C_ADDRESS
#include <Wire.h>
#endif
extern QueueHandle_t LoraSendQueue;
void onEvent(ev_t ev); void onEvent(ev_t ev);
void gen_lora_deveui(uint8_t *pdeveui); void gen_lora_deveui(uint8_t *pdeveui);
void RevBytes(unsigned char *b, size_t c); void RevBytes(unsigned char *b, size_t c);
@ -14,7 +24,7 @@ void os_getDevKey(u1_t *buf);
void os_getArtEui(u1_t *buf); void os_getArtEui(u1_t *buf);
void os_getDevEui(u1_t *buf); void os_getDevEui(u1_t *buf);
void showLoraKeys(void); void showLoraKeys(void);
void lorawan_loop(void *pvParameters);
void switch_lora(uint8_t sf, uint8_t tx); void switch_lora(uint8_t sf, uint8_t tx);
void lora_send(osjob_t *job);
#endif #endif

View File

@ -11,8 +11,8 @@ static const char TAG[] = "main";
uint16_t salt; uint16_t salt;
uint16_t reset_salt(void) { uint16_t get_salt(void) {
salt = random(65536); // get new 16bit random for salting hashes salt = (uint16_t)random(65536); // get new 16bit random for salting hashes
return salt; return salt;
} }
@ -71,8 +71,8 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) {
// https://en.wikipedia.org/wiki/MAC_Address_Anonymization // https://en.wikipedia.org/wiki/MAC_Address_Anonymization
snprintf(buff, sizeof(buff), "%08X", snprintf(buff, sizeof(buff), "%08X",
addr2int + (uint32_t)salt); // convert usigned 32-bit salted MAC to addr2int + (uint32_t)salt); // convert usigned 32-bit salted MAC
// 8 digit hex string // to 8 digit hex string
hashedmac = rokkit(&buff[3], 5); // hash MAC last string value, use 5 chars hashedmac = rokkit(&buff[3], 5); // hash MAC last string value, use 5 chars
// to fit hash in uint16_t container // to fit hash in uint16_t container
auto newmac = macs.insert(hashedmac); // add hashed MAC, if new unique auto newmac = macs.insert(hashedmac); // add hashed MAC, if new unique
@ -81,7 +81,6 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) {
// Count only if MAC was not yet seen // Count only if MAC was not yet seen
if (added) { if (added) {
// increment counter and one blink led // increment counter and one blink led
if (sniff_type == MAC_SNIFF_WIFI) { if (sniff_type == MAC_SNIFF_WIFI) {
macs_wifi++; // increment Wifi MACs counter macs_wifi++; // increment Wifi MACs counter

View File

@ -12,7 +12,7 @@
#define MAC_SNIFF_WIFI 0 #define MAC_SNIFF_WIFI 0
#define MAC_SNIFF_BLE 1 #define MAC_SNIFF_BLE 1
uint16_t reset_salt(void); uint16_t get_salt(void);
uint64_t macConvert(uint8_t *paddr); uint64_t macConvert(uint8_t *paddr);
bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type); bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type);
void printKey(const char *name, const uint8_t *key, uint8_t len, bool lsb); void printKey(const char *name, const uint8_t *key, uint8_t len, bool lsb);

View File

@ -21,38 +21,42 @@ NOTICE:
Parts of the source files in this repository are made available under different Parts of the source files in this repository are made available under different
licenses. Refer to LICENSE.txt file in repository for more details. licenses. Refer to LICENSE.txt file in repository for more details.
//////////////////////// ESP32-Paxcounter \\\\\\\\\\\\\\\\\\\\\\\\\\
Uused tasks and timers:
Task Core Prio Purpose
====================================================================================
wifiloop 0 4 rotates wifi channels
ledloop 0 3 blinks LEDs
gpsloop 0 2 reads data from GPS over serial or i2c
spiloop 0 2 reads/writes data on spi interface
IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
looptask 1 1 arduino core -> runs the LMIC LoRa stack
irqhandler 1 1 executes tasks triggered by irq
IDLE 1 0 ESP32 arduino scheduler
ESP32 hardware timers
==========================
0 Trigger display refresh
1 Trigger Wifi channel switch
2 Trigger send payload cycle
3 Trigger housekeeping cycle
*/ */
// Basic Config // Basic Config
#include "globals.h"
#include "main.h" #include "main.h"
configData_t cfg; // struct holds current device configuration configData_t cfg; // struct holds current device configuration
char display_line6[16], display_line7[16]; // display buffers char display_line6[16], display_line7[16]; // display buffers
uint8_t channel = 0; // channel rotation counter uint8_t volatile channel = 0; // channel rotation counter
uint16_t macs_total = 0, macs_wifi = 0, macs_ble = 0, uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0,
batt_voltage = 0; // globals for display batt_voltage = 0; // globals for display
// hardware timer for cyclic tasks hw_timer_t *channelSwitch, *sendCycle, *homeCycle, *displaytimer; // irq tasks
hw_timer_t *channelSwitch = NULL, *displaytimer = NULL, *sendCycle = NULL, TaskHandle_t irqHandlerTask, wifiSwitchTask;
*homeCycle = NULL;
// this variables will be changed in the ISR, and read in main loop
volatile int ButtonPressedIRQ = 0, ChannelTimerIRQ = 0, SendCycleTimerIRQ = 0,
DisplayTimerIRQ = 0, HomeCycleIRQ = 0;
// RTos send queues for payload transmit
#ifdef HAS_LORA
QueueHandle_t LoraSendQueue;
#endif
#ifdef HAS_SPI
QueueHandle_t SPISendQueue;
#endif
portMUX_TYPE timerMux =
portMUX_INITIALIZER_UNLOCKED; // sync main loop and ISR when modifying IRQ
// handler shared variables
std::set<uint16_t> macs; // container holding unique MAC adress hashes std::set<uint16_t> macs; // container holding unique MAC adress hashes
@ -62,17 +66,17 @@ PayloadConvert payload(PAYLOAD_BUFFER_SIZE);
// local Tag for logging // local Tag for logging
static const char TAG[] = "main"; static const char TAG[] = "main";
/* begin Aruino SETUP
* ------------------------------------------------------------ */
void setup() { void setup() {
// disable the default wifi logging
esp_log_level_set("wifi", ESP_LOG_NONE);
char features[100] = ""; char features[100] = "";
// disable brownout detection // disable brownout detection
#ifdef DISABLE_BROWNOUT #ifdef DISABLE_BROWNOUT
// register with brownout is at address DR_REG_RTCCNTL_BASE + 0xd4 // register with brownout is at address DR_REG_RTCCNTL_BASE + 0xd4
(*((volatile uint32_t *)ETS_UNCACHED_ADDR((DR_REG_RTCCNTL_BASE + 0xd4)))) = 0; (*((uint32_t volatile *)ETS_UNCACHED_ADDR((DR_REG_RTCCNTL_BASE + 0xd4)))) = 0;
#endif #endif
// setup debug output or silence device // setup debug output or silence device
@ -85,11 +89,108 @@ void setup() {
esp_log_set_vprintf(redirect_log); esp_log_set_vprintf(redirect_log);
#endif #endif
ESP_LOGI(TAG, "Starting %s v%s", PROGNAME, PROGVERSION); // read (and initialize on first run) runtime settings from NVRAM
loadConfig(); // includes initialize if necessary
// initialize system event handler for wifi task, needed for // initialize leds
// wifi_sniffer_init() #if (HAS_LED != NOT_A_PIN)
esp_event_loop_init(NULL, NULL); pinMode(HAS_LED, OUTPUT);
strcat_P(features, " LED");
#endif
#ifdef HAS_RGB_LED
rgb_set_color(COLOR_PINK);
strcat_P(features, " RGB");
#endif
// initialize wifi antenna
#ifdef HAS_ANTENNA_SWITCH
strcat_P(features, " ANT");
antenna_init();
antenna_select(cfg.wifiant);
#endif
// switch off bluetooth, if not compiled
#ifdef BLECOUNTER
strcat_P(features, " BLE");
#else
bool btstop = btStop();
#endif
// initialize battery status
#ifdef HAS_BATTERY_PROBE
strcat_P(features, " BATT");
calibrate_voltage();
batt_voltage = read_voltage();
#endif
#ifdef USE_OTA
strcat_P(features, " OTA");
// reboot to firmware update mode if ota trigger switch is set
if (cfg.runmode == 1) {
cfg.runmode = 0;
saveConfig();
start_ota_update();
}
#endif
// initialize button
#ifdef HAS_BUTTON
strcat_P(features, " BTN_");
#ifdef BUTTON_PULLUP
strcat_P(features, "PU");
// install button interrupt (pullup mode)
pinMode(HAS_BUTTON, INPUT_PULLUP);
#else
strcat_P(features, "PD");
// install button interrupt (pulldown mode)
pinMode(HAS_BUTTON, INPUT_PULLDOWN);
#endif // BUTTON_PULLUP
#endif // HAS_BUTTON
// initialize gps
#ifdef HAS_GPS
strcat_P(features, " GPS");
#endif
// initialize LoRa
#ifdef HAS_LORA
strcat_P(features, " LORA");
LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
if (LoraSendQueue == 0) {
ESP_LOGE(TAG, "Could not create LORA send queue. Aborting.");
exit(0);
} else
ESP_LOGI(TAG, "LORA send queue created, size %d Bytes",
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
ESP_LOGI(TAG, "Starting LMIC...");
os_init(); // initialize lmic run-time environment on core 1
LMIC_reset(); // initialize lmic MAC
LMIC_setClockError(MAX_CLOCK_ERROR * 1 /
100); // This tells LMIC to make the receive windows
// bigger, in case your clock is 1% faster or slower.
LMIC_startJoining(); // start joining
#endif
// initialize SPI
#ifdef HAS_SPI
strcat_P(features, " SPI");
SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
if (SPISendQueue == 0) {
ESP_LOGE(TAG, "Could not create SPI send queue. Aborting.");
exit(0);
} else
ESP_LOGI(TAG, "SPI send queue created, size %d Bytes",
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
#endif
#ifdef VENDORFILTER
strcat_P(features, " OUIFLT");
#endif
ESP_LOGI(TAG, "Starting %s v%s", PRODUCTNAME, PROGVERSION);
// print chip information on startup if in verbose mode // print chip information on startup if in verbose mode
#ifdef VERBOSE #ifdef VERBOSE
@ -112,126 +213,36 @@ void setup() {
#endif // verbose #endif // verbose
// read settings from NVRAM
loadConfig(); // includes initialize if necessary
#ifdef VENDORFILTER
strcat_P(features, " OUIFLT");
#endif
// initialize LoRa
#ifdef HAS_LORA
strcat_P(features, " LORA");
LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
if (LoraSendQueue == 0) {
ESP_LOGE(TAG, "Could not create LORA send queue. Aborting.");
exit(0);
} else
ESP_LOGI(TAG, "LORA send queue created, size %d Bytes",
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
#endif
// initialize SPI
#ifdef HAS_SPI
strcat_P(features, " SPI");
SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
if (SPISendQueue == 0) {
ESP_LOGE(TAG, "Could not create SPI send queue. Aborting.");
exit(0);
} else
ESP_LOGI(TAG, "SPI send queue created, size %d Bytes",
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
#endif
// initialize led
#if (HAS_LED != NOT_A_PIN)
pinMode(HAS_LED, OUTPUT);
strcat_P(features, " LED");
#endif
#ifdef HAS_RGB_LED
rgb_set_color(COLOR_PINK);
strcat_P(features, " RGB");
#endif
// initialize button
#ifdef HAS_BUTTON
strcat_P(features, " BTN_");
#ifdef BUTTON_PULLUP
strcat_P(features, "PU");
// install button interrupt (pullup mode)
pinMode(HAS_BUTTON, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, RISING);
#else
strcat_P(features, "PD");
// install button interrupt (pulldown mode)
pinMode(HAS_BUTTON, INPUT_PULLDOWN);
attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, FALLING);
#endif // BUTTON_PULLUP
#endif // HAS_BUTTON
// initialize wifi antenna
#ifdef HAS_ANTENNA_SWITCH
strcat_P(features, " ANT");
antenna_init();
antenna_select(cfg.wifiant);
#endif
// switch off bluetooth on esp32 module, if not compiled
#ifdef BLECOUNTER
strcat_P(features, " BLE");
#else
bool btstop = btStop();
#endif
// initialize gps
#ifdef HAS_GPS
strcat_P(features, " GPS");
#endif
// initialize battery status
#ifdef HAS_BATTERY_PROBE
strcat_P(features, " BATT");
calibrate_voltage();
batt_voltage = read_voltage();
#endif
// initialize display // initialize display
#ifdef HAS_DISPLAY #ifdef HAS_DISPLAY
strcat_P(features, " OLED"); strcat_P(features, " OLED");
DisplayState = cfg.screenon; DisplayState = cfg.screenon;
init_display(PROGNAME, PROGVERSION); init_display(PRODUCTNAME, PROGVERSION);
// setup display refresh trigger IRQ using esp32 hardware timer // setup display refresh trigger IRQ using esp32 hardware timer
// https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/ // https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/
// prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 0, count up // prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 0, count up
displaytimer = timerBegin(0, 80, true); displaytimer = timerBegin(0, 80, true);
// interrupt handler DisplayIRQ, triggered by edge // interrupt handler DisplayIRQ, triggered by edge
timerAttachInterrupt(displaytimer, &DisplayIRQ, true); timerAttachInterrupt(displaytimer, &DisplayIRQ, true);
// reload interrupt after each trigger of display refresh cycle // reload interrupt after each trigger of display refresh cycle
timerAlarmWrite(displaytimer, DISPLAYREFRESH_MS * 1000, true); timerAlarmWrite(displaytimer, DISPLAYREFRESH_MS * 1000, true);
// enable display interrupt
timerAlarmEnable(displaytimer);
#endif #endif
// setup channel rotation trigger IRQ using esp32 hardware timer 1
channelSwitch = timerBegin(1, 800, true);
timerAttachInterrupt(channelSwitch, &ChannelSwitchIRQ, true);
timerAlarmWrite(channelSwitch, cfg.wifichancycle * 1000, true);
timerAlarmEnable(channelSwitch);
// setup send cycle trigger IRQ using esp32 hardware timer 2 // setup send cycle trigger IRQ using esp32 hardware timer 2
sendCycle = timerBegin(2, 8000, true); sendCycle = timerBegin(2, 8000, true);
timerAttachInterrupt(sendCycle, &SendCycleIRQ, true); timerAttachInterrupt(sendCycle, &SendCycleIRQ, true);
timerAlarmWrite(sendCycle, cfg.sendcycle * 2 * 10000, true); timerAlarmWrite(sendCycle, cfg.sendcycle * 2 * 10000, true);
timerAlarmEnable(sendCycle);
// setup house keeping cycle trigger IRQ using esp32 hardware timer 3 // setup house keeping cycle trigger IRQ using esp32 hardware timer 3
homeCycle = timerBegin(3, 8000, true); homeCycle = timerBegin(3, 8000, true);
timerAttachInterrupt(homeCycle, &homeCycleIRQ, true); timerAttachInterrupt(homeCycle, &homeCycleIRQ, true);
timerAlarmWrite(homeCycle, HOMECYCLE * 10000, true); timerAlarmWrite(homeCycle, HOMECYCLE * 10000, true);
timerAlarmEnable(homeCycle);
// setup channel rotation trigger IRQ using esp32 hardware timer 1
channelSwitch = timerBegin(1, 800, true);
timerAttachInterrupt(channelSwitch, &ChannelSwitchIRQ, true);
timerAlarmWrite(channelSwitch, cfg.wifichancycle * 1000, true);
// show payload encoder // show payload encoder
#if PAYLOAD_ENCODER == 1 #if PAYLOAD_ENCODER == 1
@ -252,89 +263,106 @@ void setup() {
#ifdef VERBOSE #ifdef VERBOSE
showLoraKeys(); showLoraKeys();
#endif #endif
// initialize LoRaWAN LMIC run-time environment
os_init();
// reset LMIC MAC state
LMIC_reset();
// This tells LMIC to make the receive windows bigger, in case your clock is
// 1% faster or slower.
LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100);
// join network
LMIC_startJoining();
// start lmic runloop in rtos task on core 1
// (note: arduino main loop runs on core 1, too)
// https://techtutorialsx.com/2017/05/09/esp32-get-task-execution-core/
ESP_LOGI(TAG, "Starting Lora task on core 1");
xTaskCreatePinnedToCore(lorawan_loop, "loraloop", 2048, (void *)1,
(5 | portPRIVILEGE_BIT), NULL, 1);
#endif
// if device has GPS and it is enabled, start GPS reader task on core 0 with
// higher priority than wifi channel rotation task since we process serial
// streaming NMEA data
#ifdef HAS_GPS
if (cfg.gpsmode) {
ESP_LOGI(TAG, "Starting GPS task on core 0");
xTaskCreatePinnedToCore(gps_loop, "gpsloop", 2048, (void *)1, 2, NULL, 0);
}
#endif #endif
// start BLE scan callback if BLE function is enabled in NVRAM configuration // start BLE scan callback if BLE function is enabled in NVRAM configuration
#ifdef BLECOUNTER #ifdef BLECOUNTER
if (cfg.blescan) { if (cfg.blescan) {
ESP_LOGI(TAG, "Starting BLE task on core 1"); ESP_LOGI(TAG, "Starting Bluetooth...");
start_BLEscan(); start_BLEscan();
} }
#endif #endif
// start wifi in monitor mode and start channel rotation task on core 0 // start wifi in monitor mode and start channel rotation task on core 0
ESP_LOGI(TAG, "Starting Wifi task on core 0"); ESP_LOGI(TAG, "Starting Wifi...");
wifi_sniffer_init(); wifi_sniffer_init();
// initialize salt value using esp_random() called by random() in // initialize salt value using esp_random() called by random() in
// arduino-esp32 core. Note: do this *after* wifi has started, since function // arduino-esp32 core. Note: do this *after* wifi has started, since
// gets it's seed from RF noise // function gets it's seed from RF noise
reset_salt(); // get new 16bit for salting hashes get_salt(); // get new 16bit for salting hashes
xTaskCreatePinnedToCore(wifi_channel_loop, "wifiloop", 2048, (void *)1, 1,
NULL, 0); #ifdef HAS_GPS
ESP_LOGI(TAG, "Starting GPSloop...");
xTaskCreatePinnedToCore(gps_loop, // task function
"gpsloop", // name of task
1024, // stack size of task
(void *)1, // parameter of the task
2, // priority of the task
&GpsTask, // task handle
0); // CPU core
#endif
#ifdef HAS_SPI
ESP_LOGI(TAG, "Starting SPIloop...");
xTaskCreatePinnedToCore(spi_loop, // task function
"spiloop", // name of task
2048, // stack size of task
(void *)1, // parameter of the task
2, // priority of the task
&SpiTask, // task handle
0); // CPU core
#endif
// start state machine
ESP_LOGI(TAG, "Starting IRQ Handler...");
xTaskCreatePinnedToCore(irqHandler, // task function
"irqhandler", // name of task
2048, // stack size of task
(void *)1, // parameter of the task
1, // priority of the task
&irqHandlerTask, // task handle
1); // CPU core
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
// start led loop
ESP_LOGI(TAG, "Starting LEDloop...");
xTaskCreatePinnedToCore(ledLoop, // task function
"ledloop", // name of task
1024, // stack size of task
(void *)1, // parameter of the task
3, // priority of the task
&ledLoopTask, // task handle
0); // CPU core
#endif
// start wifi channel rotation task
ESP_LOGI(TAG, "Starting Wifi Channel rotation...");
xTaskCreatePinnedToCore(switchWifiChannel, // task function
"wifiloop", // name of task
2048, // stack size of task
NULL, // parameter of the task
4, // priority of the task
&wifiSwitchTask, // task handle
0); // CPU core
// start timer triggered interrupts
ESP_LOGI(TAG, "Starting Interrupts...");
#ifdef HAS_DISPLAY
timerAlarmEnable(displaytimer);
#endif
timerAlarmEnable(sendCycle);
timerAlarmEnable(homeCycle);
timerAlarmEnable(channelSwitch);
// start button interrupt
#ifdef HAS_BUTTON
#ifdef BUTTON_PULLUP
attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, RISING);
#else
attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, FALLING);
#endif
#endif // HAS_BUTTON
} // setup() } // setup()
/* end Arduino SETUP
* ------------------------------------------------------------ */
/* begin Arduino main loop
* ------------------------------------------------------ */
void loop() { void loop() {
while (1) { while (1) {
// state machine for switching display, LED, button, housekeeping, senddata #ifdef HAS_LORA
os_runloop_once(); // execute lmic scheduled jobs and events
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
led_loop();
#endif #endif
vTaskDelay(2 / portTICK_PERIOD_MS); // yield to CPU
}
#ifdef HAS_BUTTON vTaskDelete(NULL); // shoud never be reached
readButton(); }
#endif
#ifdef HAS_DISPLAY
updateDisplay();
#endif
// check housekeeping cycle and if expired do homework
checkHousekeeping();
// check send queue and process it
processSendBuffer();
// check send cycle and enqueue payload if cycle is expired
sendPayload();
// reset watchdog
vTaskDelay(1 / portTICK_PERIOD_MS);
} // loop()
}
/* end Arduino main loop
* ------------------------------------------------------------ */

View File

@ -1,15 +1,16 @@
#ifndef _MAIN_H #ifndef _MAIN_H
#define _MAIN_H #define _MAIN_H
//#include "led.h"
#include "macsniff.h"
#include "wifiscan.h"
#include "configmanager.h"
#include "senddata.h"
#include "cyclic.h"
#include "beacon_array.h"
#include <esp_spi_flash.h> // needed for reading ESP32 chip attributes #include <esp_spi_flash.h> // needed for reading ESP32 chip attributes
#include <esp_event_loop.h> // needed for Wifi event handler #include <esp_event_loop.h> // needed for Wifi event handler
#include <esp32-hal-timer.h> // needed for timers
#include "globals.h"
#include "wifiscan.h"
#include "configmanager.h"
#include "cyclic.h"
#include "beacon_array.h"
#include "ota.h"
#include "irqhandler.h"
#endif #endif

373
src/ota.cpp Normal file
View File

@ -0,0 +1,373 @@
#ifdef USE_OTA
/*
Parts of this code:
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.
*/
#include "ota.h"
using namespace std;
const BintrayClient bintray(BINTRAY_USER, BINTRAY_REPO, BINTRAY_PACKAGE);
// Connection port (HTTPS)
const int port = 443;
// Connection timeout
const uint32_t RESPONSE_TIMEOUT_MS = 5000;
// Variables to validate firmware content
int volatile contentLength = 0;
bool volatile isValidContentType = false;
// Local logging tag
static const char TAG[] = "main";
// helper function to extract header value from header
inline String getHeaderValue(String header, String headerName) {
return header.substring(strlen(headerName.c_str()));
}
void start_ota_update() {
/*
// check battery status if we can before doing ota
#ifdef HAS_BATTERY_PROBE
if (!batt_sufficient()) {
ESP_LOGW(TAG, "Battery voltage %dmV too low for OTA", batt_voltage);
return;
}
#endif
*/
// turn on LED
#if (HAS_LED != NOT_A_PIN)
#ifdef LED_ACTIVE_LOW
digitalWrite(HAS_LED, LOW);
#else
digitalWrite(HAS_LED, HIGH);
#endif
#endif
#ifdef HAS_DISPLAY
u8x8.begin();
u8x8.setFont(u8x8_font_chroma48medium8_r);
u8x8.clear();
#ifdef DISPLAY_FLIP
u8x8.setFlipMode(1);
#endif
u8x8.setInverseFont(1);
u8x8.print("SOFTWARE UPDATE \n");
u8x8.setInverseFont(0);
u8x8.print("WiFi connect ..\n");
u8x8.print("Has Update? ..\n");
u8x8.print("Fetching ..\n");
u8x8.print("Downloading ..\n");
u8x8.print("Rebooting ..");
#endif
ESP_LOGI(TAG, "Starting Wifi OTA update");
display(1, "**", WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PASS);
int i = WIFI_MAX_TRY;
while (i--) {
ESP_LOGI(TAG, "Trying to connect to %s", WIFI_SSID);
if (WiFi.status() == WL_CONNECTED)
break;
vTaskDelay(5000 / portTICK_PERIOD_MS);
}
if (i >= 0) {
ESP_LOGI(TAG, "Connected to %s", WIFI_SSID);
display(1, "OK", "WiFi connected");
do_ota_update(); // gets and flashes new firmware
} else {
ESP_LOGI(TAG, "Could not connect to %s, rebooting.", WIFI_SSID);
display(1, " E", "no WiFi connect");
}
display(5, "**", ""); // mark line rebooting
// turn off LED
#if (HAS_LED != NOT_A_PIN)
#ifdef LED_ACTIVE_LOW
digitalWrite(HAS_LED, HIGH);
#else
digitalWrite(HAS_LED, LOW);
#endif
#endif
vTaskDelay(5000 / portTICK_PERIOD_MS);
ESP.restart();
} // start_ota_update
void do_ota_update() {
char buf[17];
// Fetch the latest firmware version
ESP_LOGI(TAG, "Checking latest firmware version on server...");
display(2, "**", "checking version");
const String latest = bintray.getLatestVersion();
if (latest.length() == 0) {
ESP_LOGI(
TAG,
"Could not load info about the latest firmware. Rebooting to runmode.");
display(2, " E", "file not found");
return;
} else if (version_compare(latest, cfg.version) <= 0) {
ESP_LOGI(TAG, "Current firmware is up to date. Rebooting to runmode.");
display(2, "NO", "no update found");
return;
}
ESP_LOGI(TAG, "New firmware version v%s available. Downloading...",
latest.c_str());
display(2, "OK", latest.c_str());
display(3, "**", "");
String firmwarePath = bintray.getBinaryPath(latest);
if (!firmwarePath.endsWith(".bin")) {
ESP_LOGI(TAG, "Unsupported binary format, OTA update cancelled.");
display(3, " E", "file type error");
return;
}
String currentHost = bintray.getStorageHost();
String prevHost = currentHost;
WiFiClientSecure client;
client.setCACert(bintray.getCertificate(currentHost));
if (!client.connect(currentHost.c_str(), port)) {
ESP_LOGI(TAG, "Cannot connect to %s", currentHost.c_str());
display(3, " E", "connection lost");
return;
}
bool redirect = true;
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());
display(3, " E", "server error");
return;
}
}
ESP_LOGI(TAG, "Requesting %s", firmwarePath.c_str());
client.print(String("GET ") + firmwarePath + " HTTP/1.1\r\n");
client.print(String("Host: ") + currentHost + "\r\n");
client.print("Cache-Control: no-cache\r\n");
client.print("Connection: close\r\n\r\n");
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > RESPONSE_TIMEOUT_MS) {
ESP_LOGI(TAG, "Client Timeout.");
display(3, " E", "client timeout");
client.stop();
return;
}
}
while (client.available()) {
String line = client.readStringUntil('\n');
// Check if the line is end of headers by removing space symbol
line.trim();
// if the the line is empty, this is the end of the headers
if (!line.length()) {
break; // proceed to OTA update
}
// Check allowed HTTP responses
if (line.startsWith("HTTP/1.1")) {
if (line.indexOf("200") > 0) {
ESP_LOGI(TAG, "Got 200 status code from server. Proceeding to "
"firmware flashing");
redirect = false;
} else if (line.indexOf("302") > 0) {
ESP_LOGI(TAG, "Got 302 status code from server. Redirecting to the "
"new address");
redirect = true;
} else {
ESP_LOGI(TAG, "Could not get a valid firmware url.");
// Unexptected HTTP response. Retry or skip update?
redirect = false;
}
}
// Extracting new redirect location
if (line.startsWith("Location: ")) {
String newUrl = getHeaderValue(line, "Location: ");
ESP_LOGI(TAG, "Got new url: %s", newUrl.c_str());
newUrl.remove(0, newUrl.indexOf("//") + 2);
currentHost = newUrl.substring(0, newUrl.indexOf('/'));
newUrl.remove(newUrl.indexOf(currentHost), currentHost.length());
firmwarePath = newUrl;
continue;
}
// Checking headers
if (line.startsWith("Content-Length: ")) {
contentLength =
atoi((getHeaderValue(line, "Content-Length: ")).c_str());
ESP_LOGI(TAG, "Got %d bytes from server", contentLength);
}
if (line.startsWith("Content-Type: ")) {
String contentType = getHeaderValue(line, "Content-Type: ");
ESP_LOGI(TAG, "Got %s payload", contentType.c_str());
if (contentType == "application/octet-stream") {
isValidContentType = true;
}
}
}
}
display(3, "OK", ""); // line download
// check whether we have everything for OTA update
if (contentLength && isValidContentType) {
size_t written, current, size;
if (Update.begin(contentLength)) {
#ifdef HAS_DISPLAY
// register callback function for showing progress while streaming data
Update.onProgress(&show_progress);
#endif
int i = FLASH_MAX_TRY;
while ((i--) && (written != contentLength)) {
ESP_LOGI(TAG,
"Starting OTA update, attempt %u of %u. This will take some "
"time to complete...",
FLASH_MAX_TRY - i, FLASH_MAX_TRY);
display(4, "**", "writing...");
written = Update.writeStream(client);
if (written == contentLength) {
ESP_LOGI(TAG, "Written %u bytes successfully", written);
snprintf(buf, 17, "%u kB Done!", (uint16_t)(written / 1024));
display(4, "OK", buf);
break;
} else {
ESP_LOGI(TAG,
"Written only %u of %u bytes, OTA update attempt cancelled.",
written, contentLength);
}
}
if (Update.end()) {
if (Update.isFinished()) {
ESP_LOGI(
TAG,
"OTA update completed. Rebooting to runmode with new version.");
client.stop();
return;
} else {
ESP_LOGI(TAG, "Something went wrong! OTA update hasn't been finished "
"properly.");
}
} else {
ESP_LOGI(TAG, "An error occurred. Error #: %d", Update.getError());
snprintf(buf, 17, "Error #: %d", Update.getError());
display(4, " E", buf);
}
} else {
ESP_LOGI(TAG, "There isn't enough space to start OTA update");
display(4, " E", "disk full");
client.flush();
}
} else {
ESP_LOGI(TAG,
"There was no valid content in the response from the OTA server!");
display(4, " E", "response error");
client.flush();
}
ESP_LOGI(TAG,
"OTA update failed. Rebooting to runmode with current version.");
client.stop();
} // do_ota_update
void display(const uint8_t row, const std::string status,
const std::string msg) {
#ifdef HAS_DISPLAY
u8x8.setCursor(14, row);
u8x8.print((status.substr(0, 2)).c_str());
if (!msg.empty()) {
u8x8.clearLine(7);
u8x8.setCursor(0, 7);
u8x8.print(msg.substr(0, 16).c_str());
}
#endif
}
#ifdef HAS_DISPLAY
// callback function to show download progress while streaming data
void show_progress(size_t current, size_t size) {
char buf[17];
snprintf(buf, 17, "%-9lu (%3lu%%)", current, current * 100 / size);
display(4, "**", buf);
}
#endif
// helper function to compare two versions. Returns 1 if v2 is
// smaller, -1 if v1 is smaller, 0 if equal
int version_compare(const String v1, const String v2) {
// vnum stores each numeric part of version
int vnum1 = 0, vnum2 = 0;
// loop until both string are processed
for (int i = 0, j = 0; (i < v1.length() || j < v2.length());) {
// storing numeric part of version 1 in vnum1
while (i < v1.length() && v1[i] != '.') {
vnum1 = vnum1 * 10 + (v1[i] - '0');
i++;
}
// storing numeric part of version 2 in vnum2
while (j < v2.length() && v2[j] != '.') {
vnum2 = vnum2 * 10 + (v2[j] - '0');
j++;
}
if (vnum1 > vnum2)
return 1;
if (vnum2 > vnum1)
return -1;
// if equal, reset variables and go for next numeric
// part
vnum1 = vnum2 = 0;
i++;
j++;
}
return 0;
}
#endif // USE_OTA

25
src/ota.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef OTA_H
#define OTA_H
#ifdef USE_OTA
#include "globals.h"
#include "update.h"
#include "battery.h"
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <BintrayClient.h>
#include <string>
void do_ota_update();
void start_ota_update();
int version_compare(const String v1, const String v2);
void display(const uint8_t row, const std::string status,
const std::string msg);
#ifdef HAS_DISPLAY
void show_progress(size_t current, size_t size);
#endif
#endif // USE_OTA
#endif // OTA_H

8
src/ota.sample.conf Normal file
View File

@ -0,0 +1,8 @@
[ota]
OTA_WIFI_SSID = MyHomeWifi
OTA_WIFI_PASS = FooBar42!
[bintray]
BINTRAY_USER = MyBintrayUser
BINTRAY_REPO = MyBintrayRepo
BINTRAY_API_TOKEN = 3894a7a51d70c6523c1b7479261c34845ebf7878

View File

@ -4,12 +4,14 @@
// //
// Note: After editing, before "build", use "clean" button in PlatformIO! // Note: After editing, before "build", use "clean" button in PlatformIO!
#define PRODUCTNAME "PAXCNT"
// Verbose enables serial output // Verbose enables serial output
#define VERBOSE 1 // comment out to silence the device, for mute use build option #define VERBOSE 1 // comment out to silence the device, for mute use build option
// Payload send cycle and encoding // Payload send cycle and encoding
#define SEND_SECS 30 // payload send cycle [seconds/2] -> 60 sec. #define SEND_SECS 30 // payload send cycle [seconds/2] -> 60 sec.
#define PAYLOAD_ENCODER 1 // payload encoder: 1=Plain, 2=Packed, 3=CayenneLPP dynamic, 4=CayenneLPP packed #define PAYLOAD_ENCODER 2 // payload encoder: 1=Plain, 2=Packed, 3=CayenneLPP dynamic, 4=CayenneLPP packed
// Set this to include BLE counting and vendor filter functions // Set this to include BLE counting and vendor filter functions
#define VENDORFILTER 1 // comment out if you want to count things, not people #define VENDORFILTER 1 // comment out if you want to count things, not people
@ -63,6 +65,12 @@
#define DISPLAYREFRESH_MS 40 // OLED refresh cycle in ms [default = 40] -> 1000/40 = 25 frames per second #define DISPLAYREFRESH_MS 40 // OLED refresh cycle in ms [default = 40] -> 1000/40 = 25 frames per second
#define HOMECYCLE 30 // house keeping cycle in seconds [default = 30 secs] #define HOMECYCLE 30 // house keeping cycle in seconds [default = 30 secs]
// OTA settings
#define USE_OTA 1 // Comment out to disable OTA update
#define WIFI_MAX_TRY 20 // maximum number of wifi connect attempts for OTA update [default = 20]
#define FLASH_MAX_TRY 3 // maximum number of attempts for writing update binary to flash [default = 3]
#define OTA_MIN_BATT 3700 // minimum battery level vor OTA [millivolt]
// LMIC settings // LMIC settings
// define hardware independent LMIC settings here, settings of standard library in /lmic/config.h will be ignored // define hardware independent LMIC settings here, settings of standard library in /lmic/config.h will be ignored
// define hardware specifics settings in platformio.ini as build_flag for hardware environment // define hardware specifics settings in platformio.ini as build_flag for hardware environment

View File

@ -52,8 +52,9 @@ void PayloadConvert::addConfig(configData_t value) {
cursor += 10; cursor += 10;
} }
void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float cputemp,
float cputemp, uint32_t mem) { uint32_t mem, uint8_t reset1, uint8_t reset2) {
buffer[cursor++] = highByte(voltage); buffer[cursor++] = highByte(voltage);
buffer[cursor++] = lowByte(voltage); buffer[cursor++] = lowByte(voltage);
buffer[cursor++] = (byte)((uptime & 0xFF00000000000000) >> 56); buffer[cursor++] = (byte)((uptime & 0xFF00000000000000) >> 56);
@ -69,6 +70,8 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime,
buffer[cursor++] = (byte)((mem & 0x00FF0000) >> 16); buffer[cursor++] = (byte)((mem & 0x00FF0000) >> 16);
buffer[cursor++] = (byte)((mem & 0x0000FF00) >> 8); buffer[cursor++] = (byte)((mem & 0x0000FF00) >> 8);
buffer[cursor++] = (byte)((mem & 0x000000FF)); buffer[cursor++] = (byte)((mem & 0x000000FF));
buffer[cursor++] = (byte)(reset1);
buffer[cursor++] = (byte)(reset2);
} }
#ifdef HAS_GPS #ifdef HAS_GPS
@ -121,14 +124,17 @@ void PayloadConvert::addConfig(configData_t value) {
value.screenon ? true : false, value.countermode ? true : false, value.screenon ? true : false, value.countermode ? true : false,
value.blescan ? true : false, value.wifiant ? true : false, value.blescan ? true : false, value.wifiant ? true : false,
value.vendorfilter ? true : false, value.gpsmode ? true : false); value.vendorfilter ? true : false, value.gpsmode ? true : false);
writeVersion(value.version);
} }
void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float cputemp,
float cputemp, uint32_t mem) { uint32_t mem, uint8_t reset1, uint8_t reset2) {
writeUint16(voltage); writeUint16(voltage);
writeUptime(uptime); writeUptime(uptime);
writeUint8((byte)cputemp); writeUint8((byte)cputemp);
writeUint32(mem); writeUint32(mem);
writeUint8(reset1);
writeUint8(reset2);
} }
#ifdef HAS_GPS #ifdef HAS_GPS
@ -155,6 +161,11 @@ void PayloadConvert::writeUptime(uint64_t uptime) {
intToBytes(cursor, uptime, 8); intToBytes(cursor, uptime, 8);
} }
void PayloadConvert::writeVersion(char * version) {
memcpy(buffer + cursor, version, 10);
cursor += 10;
}
void PayloadConvert::writeLatLng(double latitude, double longitude) { void PayloadConvert::writeLatLng(double latitude, double longitude) {
intToBytes(cursor, latitude, 4); intToBytes(cursor, latitude, 4);
intToBytes(cursor, longitude, 4); intToBytes(cursor, longitude, 4);
@ -241,8 +252,8 @@ void PayloadConvert::addConfig(configData_t value) {
buffer[cursor++] = value.adrmode; buffer[cursor++] = value.adrmode;
} }
void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float celsius,
float celsius, uint32_t mem) { uint32_t mem, uint8_t reset1, uint8_t reset2) {
uint16_t temp = celsius * 10; uint16_t temp = celsius * 10;
uint16_t volt = voltage / 10; uint16_t volt = voltage / 10;
#ifdef HAS_BATTERY_PROBE #ifdef HAS_BATTERY_PROBE

View File

@ -35,7 +35,8 @@ public:
uint8_t *getBuffer(void); uint8_t *getBuffer(void);
void addCount(uint16_t value1, uint16_t value2); void addCount(uint16_t value1, uint16_t value2);
void addConfig(configData_t value); void addConfig(configData_t value);
void addStatus(uint16_t voltage, uint64_t uptime, float cputemp, uint32_t mem); void addStatus(uint16_t voltage, uint64_t uptime, float cputemp, uint32_t mem,
uint8_t reset1, uint8_t reset2);
void addAlarm(int8_t rssi, uint8_t message); void addAlarm(int8_t rssi, uint8_t message);
#ifdef HAS_GPS #ifdef HAS_GPS
void addGPS(gpsStatus_t value); void addGPS(gpsStatus_t value);
@ -44,7 +45,6 @@ public:
void addButton(uint8_t value); void addButton(uint8_t value);
#endif #endif
#if PAYLOAD_ENCODER == 1 // format plain #if PAYLOAD_ENCODER == 1 // format plain
private: private:
@ -64,6 +64,7 @@ private:
void writeUint8(uint8_t i); void writeUint8(uint8_t i);
void writeHumidity(float humidity); void writeHumidity(float humidity);
void writeTemperature(float temperature); void writeTemperature(float temperature);
void writeVersion(char * version);
void writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, bool g, void writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, bool g,
bool h); bool h);
@ -77,7 +78,6 @@ private:
#else #else
#error "No valid payload converter defined" #error "No valid payload converter defined"
#endif #endif
}; };
extern PayloadConvert payload; extern PayloadConvert payload;

View File

@ -18,7 +18,7 @@ void set_reset(uint8_t val[]) {
case 1: // reset MAC counter case 1: // reset MAC counter
ESP_LOGI(TAG, "Remote command: reset MAC counter"); ESP_LOGI(TAG, "Remote command: reset MAC counter");
reset_counters(); // clear macs reset_counters(); // clear macs
reset_salt(); // get new salt get_salt(); // get new salt
sprintf(display_line6, "Reset counter"); sprintf(display_line6, "Reset counter");
break; break;
case 2: // reset device to factory settings case 2: // reset device to factory settings
@ -31,6 +31,16 @@ void set_reset(uint8_t val[]) {
sprintf(display_line6, "Queue reset"); sprintf(display_line6, "Queue reset");
flushQueues(); flushQueues();
break; break;
case 9: // reset and ask for software update via Wifi OTA
ESP_LOGI(TAG, "Remote command: software update via Wifi");
#ifdef USE_OTA
sprintf(display_line6, "Software update");
cfg.runmode = 1;
#else
sprintf(display_line6, "Software update not implemented");
#endif // USE_OTA
break;
default: default:
ESP_LOGW(TAG, "Remote command: reset called with invalid parameter(s)"); ESP_LOGW(TAG, "Remote command: reset called with invalid parameter(s)");
} }
@ -203,7 +213,8 @@ void get_status(uint8_t val[]) {
#endif #endif
payload.reset(); payload.reset();
payload.addStatus(voltage, uptime() / 1000, temperatureRead(), payload.addStatus(voltage, uptime() / 1000, temperatureRead(),
ESP.getFreeHeap()); ESP.getFreeHeap(), rtc_get_reset_reason(0),
rtc_get_reset_reason(1));
SendData(STATUSPORT); SendData(STATUSPORT);
}; };
@ -221,14 +232,14 @@ void get_gps(uint8_t val[]) {
// assign previously defined functions to set of numeric remote commands // assign previously defined functions to set of numeric remote commands
// format: opcode, function, #bytes params, // format: opcode, function, #bytes params,
// flag (1 = do make settings persistent / 0 = don't) // flag (true = do make settings persistent / false = don't)
// //
cmd_t table[] = { cmd_t table[] = {
{0x01, set_rssi, 1, true}, {0x02, set_countmode, 1, true}, {0x01, set_rssi, 1, true}, {0x02, set_countmode, 1, true},
{0x03, set_gps, 1, true}, {0x04, set_display, 1, true}, {0x03, set_gps, 1, true}, {0x04, set_display, 1, true},
{0x05, set_lorasf, 1, true}, {0x06, set_lorapower, 1, true}, {0x05, set_lorasf, 1, true}, {0x06, set_lorapower, 1, true},
{0x07, set_loraadr, 1, true}, {0x08, set_screensaver, 1, true}, {0x07, set_loraadr, 1, true}, {0x08, set_screensaver, 1, true},
{0x09, set_reset, 1, false}, {0x0a, set_sendcycle, 1, true}, {0x09, set_reset, 1, true}, {0x0a, set_sendcycle, 1, true},
{0x0b, set_wifichancycle, 1, true}, {0x0c, set_blescantime, 1, true}, {0x0b, set_wifichancycle, 1, true}, {0x0c, set_blescantime, 1, true},
{0x0d, set_vendorfilter, 1, false}, {0x0e, set_blescan, 1, true}, {0x0d, set_vendorfilter, 1, false}, {0x0e, set_blescan, 1, true},
{0x0f, set_wifiant, 1, true}, {0x10, set_rgblum, 1, true}, {0x0f, set_wifiant, 1, true}, {0x10, set_rgblum, 1, true},

View File

@ -5,6 +5,7 @@
#include "configmanager.h" #include "configmanager.h"
#include "lorawan.h" #include "lorawan.h"
#include "macsniff.h" #include "macsniff.h"
#include <rom/rtc.h>
// table of remote commands and assigned functions // table of remote commands and assigned functions
typedef struct { typedef struct {

View File

@ -29,80 +29,38 @@ void SendData(uint8_t port) {
// clear counter if not in cumulative counter mode // clear counter if not in cumulative counter mode
if ((port == COUNTERPORT) && (cfg.countermode != 1)) { if ((port == COUNTERPORT) && (cfg.countermode != 1)) {
reset_counters(); // clear macs container and reset all counters reset_counters(); // clear macs container and reset all counters
reset_salt(); // get new salt for salting hashes get_salt(); // get new salt for salting hashes
ESP_LOGI(TAG, "Counter cleared"); ESP_LOGI(TAG, "Counter cleared");
} }
} // SendData } // SendData
// cyclic called function to prepare payload to send // interrupt triggered function to prepare payload to send
void sendPayload() { void sendPayload() {
if (SendCycleTimerIRQ) { // append counter data to payload
portENTER_CRITICAL(&timerMux); payload.reset();
SendCycleTimerIRQ = 0; payload.addCount(macs_wifi, cfg.blescan ? macs_ble : 0);
portEXIT_CRITICAL(&timerMux); // append GPS data, if present
// append counter data to payload
payload.reset();
payload.addCount(macs_wifi, cfg.blescan ? macs_ble : 0);
// append GPS data, if present
#ifdef HAS_GPS #ifdef HAS_GPS
// show NMEA data in debug mode, useful for debugging GPS on board // show NMEA data in debug mode, useful for debugging GPS on board
// connection // connection
ESP_LOGD(TAG, "GPS NMEA data: passed %d / failed: %d / with fix: %d", ESP_LOGD(TAG, "GPS NMEA data: passed %d / failed: %d / with fix: %d",
gps.passedChecksum(), gps.failedChecksum(), gps.passedChecksum(), gps.failedChecksum(), gps.sentencesWithFix());
gps.sentencesWithFix()); // log GPS position if we have a fix and gps data mode is enabled
// log GPS position if we have a fix and gps data mode is enabled if ((cfg.gpsmode) && (gps.location.isValid())) {
if ((cfg.gpsmode) && (gps.location.isValid())) { gps_read();
gps_read(); payload.addGPS(gps_status);
payload.addGPS(gps_status); ESP_LOGD(TAG, "lat=%.6f | lon=%.6f | %u Sats | HDOP=%.1f | Altitude=%um",
ESP_LOGD(TAG, "lat=%.6f | lon=%.6f | %u Sats | HDOP=%.1f | Altitude=%um", gps_status.latitude / (float)1e6,
gps_status.latitude / (float)1e6, gps_status.longitude / (float)1e6, gps_status.satellites,
gps_status.longitude / (float)1e6, gps_status.satellites, gps_status.hdop / (float)100, gps_status.altitude);
gps_status.hdop / (float)100, gps_status.altitude);
} else {
ESP_LOGD(TAG, "No valid GPS position or GPS data mode disabled");
}
#endif
SendData(COUNTERPORT);
}
} // sendpayload()
// interrupt handler used for payload send cycle timer
void IRAM_ATTR SendCycleIRQ() {
portENTER_CRITICAL(&timerMux);
SendCycleTimerIRQ++;
portEXIT_CRITICAL(&timerMux);
}
// cyclic called function to eat data from RTos send queues and transmit it
void processSendBuffer() {
MessageBuffer_t SendBuffer;
#ifdef HAS_LORA
// Check if there is a pending TX/RX job running
if ((LMIC.opmode & (OP_JOINING | OP_REJOIN | OP_TXDATA | OP_POLL)) != 0) {
// LoRa Busy -> don't eat data from queue, since it cannot be sent
} else { } else {
if (xQueueReceive(LoraSendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) { ESP_LOGD(TAG, "No valid GPS position or GPS data mode disabled");
// SendBuffer gets struct MessageBuffer with next payload from queue
LMIC_setTxData2(SendBuffer.MessagePort, SendBuffer.Message,
SendBuffer.MessageSize, (cfg.countermode & 0x02));
ESP_LOGI(TAG, "%d bytes sent to LoRa", SendBuffer.MessageSize);
sprintf(display_line7, "PACKET QUEUED");
}
} }
#endif #endif
SendData(COUNTERPORT);
#ifdef HAS_SPI } // sendpayload()
if (xQueueReceive(SPISendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
ESP_LOGI(TAG, "%d bytes sent to SPI", SendBuffer.MessageSize);
}
#endif
} // processSendBuffer
void flushQueues() { void flushQueues() {
#ifdef HAS_LORA #ifdef HAS_LORA

View File

@ -3,8 +3,7 @@
void SendData(uint8_t port); void SendData(uint8_t port);
void sendPayload(void); void sendPayload(void);
void SendCycleIRQ(void); void checkSendQueues(void);
void processSendBuffer(void);
void flushQueues(); void flushQueues();
#endif // _SENDDATA_H_ #endif // _SENDDATA_H_

30
src/spi.cpp Normal file
View File

@ -0,0 +1,30 @@
#ifdef HAS_SPI
#include "globals.h"
// Local logging tag
static const char TAG[] = "main";
MessageBuffer_t SendBuffer;
QueueHandle_t SPISendQueue;
TaskHandle_t SpiTask;
// SPI feed Task
void spi_loop(void *pvParameters) {
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
while (1) {
if (xQueueReceive(SPISendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
ESP_LOGI(TAG, "%d bytes sent to SPI", SendBuffer.MessageSize);
}
vTaskDelay(2 / portTICK_PERIOD_MS); // yield to CPU
} // end of infinite loop
vTaskDelete(NULL); // shoud never be reached
} // spi_loop()
#endif // HAS_SPI

9
src/spi.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef _SPI_H
#define _SPI_H
extern TaskHandle_t SpiTask;
extern QueueHandle_t SPISendQueue;
void spi_loop(void *pvParameters);
#endif

354
src/update.cpp Normal file
View File

@ -0,0 +1,354 @@
/*
this file copied from esp32-arduino library and patched, see PR
https://github.com/espressif/arduino-esp32/pull/1886
*/
#include "update.h"
#include "Arduino.h"
#include "esp_spi_flash.h"
#include "esp_ota_ops.h"
#include "esp_image_format.h"
static const char * _err2str(uint8_t _error){
if(_error == UPDATE_ERROR_OK){
return ("No Error");
} else if(_error == UPDATE_ERROR_WRITE){
return ("Flash Write Failed");
} else if(_error == UPDATE_ERROR_ERASE){
return ("Flash Erase Failed");
} else if(_error == UPDATE_ERROR_READ){
return ("Flash Read Failed");
} else if(_error == UPDATE_ERROR_SPACE){
return ("Not Enough Space");
} else if(_error == UPDATE_ERROR_SIZE){
return ("Bad Size Given");
} else if(_error == UPDATE_ERROR_STREAM){
return ("Stream Read Timeout");
} else if(_error == UPDATE_ERROR_MD5){
return ("MD5 Check Failed");
} else if(_error == UPDATE_ERROR_MAGIC_BYTE){
return ("Wrong Magic Byte");
} else if(_error == UPDATE_ERROR_ACTIVATE){
return ("Could Not Activate The Firmware");
} else if(_error == UPDATE_ERROR_NO_PARTITION){
return ("Partition Could Not be Found");
} else if(_error == UPDATE_ERROR_BAD_ARGUMENT){
return ("Bad Argument");
} else if(_error == UPDATE_ERROR_ABORT){
return ("Aborted");
}
return ("UNKNOWN");
}
static bool _partitionIsBootable(const esp_partition_t* partition){
uint8_t buf[4];
if(!partition){
return false;
}
if(!ESP.flashRead(partition->address, (uint32_t*)buf, 4)) {
return false;
}
if(buf[0] != ESP_IMAGE_HEADER_MAGIC) {
return false;
}
return true;
}
static bool _enablePartition(const esp_partition_t* partition){
uint8_t buf[4];
if(!partition){
return false;
}
if(!ESP.flashRead(partition->address, (uint32_t*)buf, 4)) {
return false;
}
buf[0] = ESP_IMAGE_HEADER_MAGIC;
return ESP.flashWrite(partition->address, (uint32_t*)buf, 4);
}
UpdateClass::UpdateClass()
: _error(0)
, _buffer(0)
, _bufferLen(0)
, _size(0)
, _progress_callback(NULL)
, _progress(0)
, _command(U_FLASH)
, _partition(NULL)
{
}
UpdateClass& UpdateClass::onProgress(THandlerFunction_Progress fn) {
_progress_callback = fn;
return *this;
}
void UpdateClass::_reset() {
if (_buffer)
delete[] _buffer;
_buffer = 0;
_bufferLen = 0;
_progress = 0;
_size = 0;
_command = U_FLASH;
}
bool UpdateClass::canRollBack(){
if(_buffer){ //Update is running
return false;
}
const esp_partition_t* partition = esp_ota_get_next_update_partition(NULL);
return _partitionIsBootable(partition);
}
bool UpdateClass::rollBack(){
if(_buffer){ //Update is running
return false;
}
const esp_partition_t* partition = esp_ota_get_next_update_partition(NULL);
return _partitionIsBootable(partition) && !esp_ota_set_boot_partition(partition);
}
bool UpdateClass::begin(size_t size, int command) {
if(_size > 0){
log_w("already running");
return false;
}
_reset();
_error = 0;
if(size == 0) {
_error = UPDATE_ERROR_SIZE;
return false;
}
if (command == U_FLASH) {
_partition = esp_ota_get_next_update_partition(NULL);
if(!_partition){
_error = UPDATE_ERROR_NO_PARTITION;
return false;
}
log_d("OTA Partition: %s", _partition->label);
}
else if (command == U_SPIFFS) {
_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL);
if(!_partition){
_error = UPDATE_ERROR_NO_PARTITION;
return false;
}
}
else {
_error = UPDATE_ERROR_BAD_ARGUMENT;
log_e("bad command %u", command);
return false;
}
if(size == UPDATE_SIZE_UNKNOWN){
size = _partition->size;
} else if(size > _partition->size){
_error = UPDATE_ERROR_SIZE;
log_e("too large %u > %u", size, _partition->size);
return false;
}
//initialize
_buffer = (uint8_t*)malloc(SPI_FLASH_SEC_SIZE);
if(!_buffer){
log_e("malloc failed");
return false;
}
_size = size;
_command = command;
_md5.begin();
return true;
}
void UpdateClass::_abort(uint8_t err){
_reset();
_error = err;
}
void UpdateClass::abort(){
_abort(UPDATE_ERROR_ABORT);
}
bool UpdateClass::_writeBuffer(){
//first bytes of new firmware
if(!_progress && _command == U_FLASH){
//check magic
if(_buffer[0] != ESP_IMAGE_HEADER_MAGIC){
_abort(UPDATE_ERROR_MAGIC_BYTE);
return false;
}
//remove magic byte from the firmware now and write it upon success
//this ensures that partially written firmware will not be bootable
_buffer[0] = 0xFF;
}
if(!ESP.flashEraseSector((_partition->address + _progress)/SPI_FLASH_SEC_SIZE)){
_abort(UPDATE_ERROR_ERASE);
return false;
}
if (!ESP.flashWrite(_partition->address + _progress, (uint32_t*)_buffer, _bufferLen)) {
_abort(UPDATE_ERROR_WRITE);
return false;
}
//restore magic or md5 will fail
if(!_progress && _command == U_FLASH){
_buffer[0] = ESP_IMAGE_HEADER_MAGIC;
}
_md5.add(_buffer, _bufferLen);
_progress += _bufferLen;
_bufferLen = 0;
return true;
}
bool UpdateClass::_verifyHeader(uint8_t data) {
if(_command == U_FLASH) {
if(data != ESP_IMAGE_HEADER_MAGIC) {
_abort(UPDATE_ERROR_MAGIC_BYTE);
return false;
}
return true;
} else if(_command == U_SPIFFS) {
return true;
}
return false;
}
bool UpdateClass::_verifyEnd() {
if(_command == U_FLASH) {
if(!_enablePartition(_partition) || !_partitionIsBootable(_partition)) {
_abort(UPDATE_ERROR_READ);
return false;
}
if(esp_ota_set_boot_partition(_partition)){
_abort(UPDATE_ERROR_ACTIVATE);
return false;
}
_reset();
return true;
} else if(_command == U_SPIFFS) {
_reset();
return true;
}
return false;
}
bool UpdateClass::setMD5(const char * expected_md5){
if(strlen(expected_md5) != 32)
{
return false;
}
_target_md5 = expected_md5;
return true;
}
bool UpdateClass::end(bool evenIfRemaining){
if(hasError() || _size == 0){
return false;
}
if(!isFinished() && !evenIfRemaining){
log_e("premature end: res:%u, pos:%u/%u\n", getError(), progress(), _size);
_abort(UPDATE_ERROR_ABORT);
return false;
}
if(evenIfRemaining) {
if(_bufferLen > 0) {
_writeBuffer();
}
_size = progress();
}
_md5.calculate();
if(_target_md5.length()) {
if(_target_md5 != _md5.toString()){
_abort(UPDATE_ERROR_MD5);
return false;
}
}
return _verifyEnd();
}
size_t UpdateClass::write(uint8_t *data, size_t len) {
if(hasError() || !isRunning()){
return 0;
}
if(len > remaining()){
_abort(UPDATE_ERROR_SPACE);
return 0;
}
size_t left = len;
while((_bufferLen + left) > SPI_FLASH_SEC_SIZE) {
size_t toBuff = SPI_FLASH_SEC_SIZE - _bufferLen;
memcpy(_buffer + _bufferLen, data + (len - left), toBuff);
_bufferLen += toBuff;
if(!_writeBuffer()){
return len - left;
}
left -= toBuff;
}
memcpy(_buffer + _bufferLen, data + (len - left), left);
_bufferLen += left;
if(_bufferLen == remaining()){
if(!_writeBuffer()){
return len - left;
}
}
return len;
}
size_t UpdateClass::writeStream(Stream &data) {
data.setTimeout(20000);
size_t written = 0;
size_t toRead = 0;
if(hasError() || !isRunning())
return 0;
if(!_verifyHeader(data.peek())) {
_reset();
return 0;
}
if (_progress_callback) {
_progress_callback(0, _size);
}
while(remaining()) {
toRead = data.readBytes(_buffer + _bufferLen, (SPI_FLASH_SEC_SIZE - _bufferLen));
if(toRead == 0) { //Timeout
delay(100);
toRead = data.readBytes(_buffer + _bufferLen, (SPI_FLASH_SEC_SIZE - _bufferLen));
if(toRead == 0) { //Timeout
_abort(UPDATE_ERROR_STREAM);
return written;
}
}
_bufferLen += toRead;
if((_bufferLen == remaining() || _bufferLen == SPI_FLASH_SEC_SIZE) && !_writeBuffer())
return written;
written += toRead;
if(_progress_callback) {
_progress_callback(_progress, _size);
}
}
if(_progress_callback) {
_progress_callback(_size, _size);
}
return written;
}
void UpdateClass::printError(Stream &out){
out.println(_err2str(_error));
}
UpdateClass Update;

181
src/update.h Normal file
View File

@ -0,0 +1,181 @@
#ifndef ESP8266UPDATER_H
#define ESP8266UPDATER_H
#include <Arduino.h>
#include <MD5Builder.h>
#include <functional>
#include "esp_partition.h"
#define UPDATE_ERROR_OK (0)
#define UPDATE_ERROR_WRITE (1)
#define UPDATE_ERROR_ERASE (2)
#define UPDATE_ERROR_READ (3)
#define UPDATE_ERROR_SPACE (4)
#define UPDATE_ERROR_SIZE (5)
#define UPDATE_ERROR_STREAM (6)
#define UPDATE_ERROR_MD5 (7)
#define UPDATE_ERROR_MAGIC_BYTE (8)
#define UPDATE_ERROR_ACTIVATE (9)
#define UPDATE_ERROR_NO_PARTITION (10)
#define UPDATE_ERROR_BAD_ARGUMENT (11)
#define UPDATE_ERROR_ABORT (12)
#define UPDATE_SIZE_UNKNOWN 0xFFFFFFFF
#define U_FLASH 0
#define U_SPIFFS 100
#define U_AUTH 200
class UpdateClass {
public:
typedef std::function<void(size_t, size_t)> THandlerFunction_Progress;
UpdateClass();
/*
This callback will be called when Update is receiving data
*/
UpdateClass& onProgress(THandlerFunction_Progress fn);
/*
Call this to check the space needed for the update
Will return false if there is not enough space
*/
bool begin(size_t size=UPDATE_SIZE_UNKNOWN, int command = U_FLASH);
/*
Writes a buffer to the flash and increments the address
Returns the amount written
*/
size_t write(uint8_t *data, size_t len);
/*
Writes the remaining bytes from the Stream to the flash
Uses readBytes() and sets UPDATE_ERROR_STREAM on timeout
Returns the bytes written
Should be equal to the remaining bytes when called
Usable for slow streams like Serial
*/
size_t writeStream(Stream &data);
/*
If all bytes are written
this call will write the config to eboot
and return true
If there is already an update running but is not finished and !evenIfRemainanig
or there is an error
this will clear everything and return false
the last error is available through getError()
evenIfRemaining is helpfull when you update without knowing the final size first
*/
bool end(bool evenIfRemaining = false);
/*
Aborts the running update
*/
void abort();
/*
Prints the last error to an output stream
*/
void printError(Stream &out);
/*
sets the expected MD5 for the firmware (hexString)
*/
bool setMD5(const char * expected_md5);
/*
returns the MD5 String of the sucessfully ended firmware
*/
String md5String(void){ return _md5.toString(); }
/*
populated the result with the md5 bytes of the sucessfully ended firmware
*/
void md5(uint8_t * result){ return _md5.getBytes(result); }
//Helpers
uint8_t getError(){ return _error; }
void clearError(){ _error = UPDATE_ERROR_OK; }
bool hasError(){ return _error != UPDATE_ERROR_OK; }
bool isRunning(){ return _size > 0; }
bool isFinished(){ return _progress == _size; }
size_t size(){ return _size; }
size_t progress(){ return _progress; }
size_t remaining(){ return _size - _progress; }
/*
Template to write from objects that expose
available() and read(uint8_t*, size_t) methods
faster than the writeStream method
writes only what is available
*/
template<typename T>
size_t write(T &data){
size_t written = 0;
if (hasError() || !isRunning())
return 0;
size_t available = data.available();
while(available) {
if(_bufferLen + available > remaining()){
available = remaining() - _bufferLen;
}
if(_bufferLen + available > 4096) {
size_t toBuff = 4096 - _bufferLen;
data.read(_buffer + _bufferLen, toBuff);
_bufferLen += toBuff;
if(!_writeBuffer())
return written;
written += toBuff;
} else {
data.read(_buffer + _bufferLen, available);
_bufferLen += available;
written += available;
if(_bufferLen == remaining()) {
if(!_writeBuffer()) {
return written;
}
}
}
if(remaining() == 0)
return written;
available = data.available();
}
return written;
}
/*
check if there is a firmware on the other OTA partition that you can bootinto
*/
bool canRollBack();
/*
set the other OTA partition as bootable (reboot to enable)
*/
bool rollBack();
private:
void _reset();
void _abort(uint8_t err);
bool _writeBuffer();
bool _verifyHeader(uint8_t data);
bool _verifyEnd();
uint8_t _error;
uint8_t *_buffer;
size_t _bufferLen;
size_t _size;
THandlerFunction_Progress _progress_callback;
uint32_t _progress;
uint32_t _command;
const esp_partition_t* _partition;
String _target_md5;
MD5Builder _md5;
};
extern UpdateClass Update;
#endif

View File

@ -1,4 +1,4 @@
std::array<uint32_t, 1433> vendors = { std::array<uint32_t, 1552> vendors = {
0x38f23e, 0x807abf, 0x90e7c4, 0x7c6193, 0x485073, 0x74e28c, 0x8463d6, 0x38f23e, 0x807abf, 0x90e7c4, 0x7c6193, 0x485073, 0x74e28c, 0x8463d6,
0xd48f33, 0x2c8a72, 0x980d2e, 0xa826d9, 0xd4206d, 0x00155d, 0x806c1b, 0xd48f33, 0x2c8a72, 0x980d2e, 0xa826d9, 0xd4206d, 0x00155d, 0x806c1b,
0xa470d6, 0x985fd3, 0x1c69a5, 0x382de8, 0xd087e2, 0x205531, 0x5440ad, 0xa470d6, 0x985fd3, 0x1c69a5, 0x382de8, 0xd087e2, 0x205531, 0x5440ad,
@ -14,193 +14,210 @@ std::array<uint32_t, 1433> vendors = {
0xd0dfc7, 0x1c62b8, 0x18e2c2, 0x001a8a, 0x002567, 0xa8f274, 0x001599, 0xd0dfc7, 0x1c62b8, 0x18e2c2, 0x001a8a, 0x002567, 0xa8f274, 0x001599,
0x0012fb, 0x7cf854, 0x8cc8cd, 0xe81132, 0xa02195, 0x8c71f8, 0x04180f, 0x0012fb, 0x7cf854, 0x8cc8cd, 0xe81132, 0xa02195, 0x8c71f8, 0x04180f,
0x9463d1, 0x0cdfa4, 0xcc051b, 0x68ebae, 0x60d0a9, 0x60a10a, 0xa07591, 0x9463d1, 0x0cdfa4, 0xcc051b, 0x68ebae, 0x60d0a9, 0x60a10a, 0xa07591,
0x001fcc, 0xec107b, 0xa01081, 0xf4f524, 0x9c99a0, 0x185936, 0x98fae3, 0x001fcc, 0xec107b, 0xa01081, 0xf4f524, 0xbc8385, 0x900628, 0xd4ae05,
0x640980, 0x8cbebe, 0xf8a45f, 0xbc8385, 0x900628, 0xd4ae05, 0x3c0518, 0x3c0518, 0xe8bba8, 0xbc3aea, 0x8c0ee3, 0x6c5c14, 0x78abbb, 0x1816c9,
0xc40bcb, 0xe8bba8, 0xbc3aea, 0x8c0ee3, 0x6c5c14, 0x78abbb, 0x1816c9,
0xfc8f90, 0x244b03, 0x988389, 0x14bb6e, 0x1c3ade, 0xf83f51, 0xd8e0e1, 0xfc8f90, 0x244b03, 0x988389, 0x14bb6e, 0x1c3ade, 0xf83f51, 0xd8e0e1,
0xecf342, 0x5092b9, 0xb4bff6, 0xc8d7b0, 0x982d68, 0xecd09f, 0xe446da, 0xecf342, 0x5092b9, 0xb4bff6, 0xc8d7b0, 0x982d68, 0xd80831, 0xdc5583,
0xf4f5db, 0xd80831, 0xdc5583, 0x2c54cf, 0x001fe3, 0x0026e2, 0x001e75, 0x2c54cf, 0x001fe3, 0x0026e2, 0x001e75, 0x6cd68a, 0x2021a5, 0x0c4885,
0x6cd68a, 0x2021a5, 0x0c4885, 0xdc0b34, 0xac0d1b, 0x60e3ac, 0xf895c7, 0xdc0b34, 0xac0d1b, 0x60e3ac, 0xf895c7, 0xc4438f, 0xa816b2, 0xe892a4,
0xc4438f, 0xa816b2, 0xe892a4, 0x700514, 0x88c9d0, 0x2c598a, 0x18f0e4, 0x700514, 0x88c9d0, 0x2c598a, 0xec8350, 0x4cdd31, 0x705aac, 0xfc643a,
0xec8350, 0x4cdd31, 0x703eac, 0x6c94f8, 0x84788b, 0x2cf0ee, 0x68d93c, 0xd4e6b7, 0x2802d8, 0x48605f, 0xf0766f, 0x40cbc0, 0x4098ad, 0x6c4d73,
0x24e314, 0x28f076, 0x285aeb, 0x087402, 0xccc760, 0x705aac, 0xfc643a, 0xc48466, 0xb8634d, 0x503237, 0xd4619d, 0xb0481a, 0x989e63, 0xdca904,
0xd4e6b7, 0x2802d8, 0x9c2ea1, 0x68967b, 0xbc6778, 0xa82066, 0xb065bd, 0x48a195, 0x6cab31, 0x7c5049, 0xe42b34, 0x1c36bb, 0x3c2eff, 0x6c96cf,
0xf0dce2, 0x7cd1c3, 0x705681, 0x2cbe08, 0x783a84, 0x84b153, 0x6476ba, 0x3035ad, 0xa8be27, 0x70a2b3, 0x4c57ca, 0x68fb7e, 0x90c1c6, 0xa4f1e8,
0x54ae27, 0xf437b7, 0xf0d1a9, 0x34c059, 0x04f7e4, 0x10ddb1, 0xb4f0ab, 0xac61ea, 0x38b54d, 0x00cdfe, 0x18af61, 0xcc4463, 0x34159e, 0x58b035,
0x848506, 0x7831c1, 0x8c7c92, 0xd0e140, 0xacfdec, 0xf82793, 0x40a6d9, 0xf0b479, 0x109add, 0x40a6d9, 0x7cf05f, 0xa4b197, 0x0c74c2, 0x403004,
0x109add, 0xf0b479, 0x58b035, 0x34159e, 0x286aba, 0xec852f, 0x44d884, 0x4860bc, 0xd02b20, 0x9ce33f, 0xf0989d, 0xace4b5, 0x6c72e7, 0x60fec5,
0x003ee1, 0x7c11be, 0x04e536, 0x881fa1, 0x04db56, 0x9cfc01, 0xc81ee7, 0x00a040, 0x000d93, 0xacbc32, 0x30d9d9, 0x6030d4, 0x94bf2d, 0xc49880,
0x34363b, 0xc01ada, 0xacbc32, 0x70700d, 0x7c5049, 0x503237, 0xd4619d, 0xe0338e, 0x68fef7, 0xbce143, 0x645aed, 0xc0b658, 0x881908, 0xfc2a9c,
0xb0481a, 0x989e63, 0xdca904, 0x48a195, 0x6cab31, 0x6c96cf, 0x3035ad, 0x44d884, 0xec852f, 0x286aba, 0x705681, 0x7cd1c3, 0xf0dce2, 0xb065bd,
0xa8be27, 0xb8634d, 0x9ce33f, 0xf0989d, 0xace4b5, 0xe42b34, 0x1c36bb, 0xa82066, 0xbc6778, 0x68967b, 0x848506, 0x54ae27, 0x6476ba, 0x84b153,
0x3c2eff, 0xf0766f, 0x40cbc0, 0x4098ad, 0x6c4d73, 0xc48466, 0xd02b20, 0x783a84, 0x2cbe08, 0x24e314, 0x68d93c, 0x2cf0ee, 0x84788b, 0x6c94f8,
0x3010e4, 0x380f4a, 0x685b35, 0xc86f1d, 0x701124, 0x38484c, 0x041552, 0x703eac, 0xb4f0ab, 0x10ddb1, 0x04f7e4, 0x34c059, 0xf0d1a9, 0xbc3baf,
0x786c1c, 0xbc3baf, 0x00a040, 0x60fec5, 0xcc4463, 0x6c72e7, 0x18af61, 0x786c1c, 0x041552, 0x38484c, 0x701124, 0xc86f1d, 0x685b35, 0x380f4a,
0x00cdfe, 0xac61ea, 0x38b54d, 0x60c547, 0x28e02c, 0x50ead6, 0x4860bc, 0x3010e4, 0x04db56, 0x881fa1, 0x04e536, 0xf82793, 0xacfdec, 0xd0e140,
0x403004, 0x0c74c2, 0xa4b197, 0x7cf05f, 0xa4f1e8, 0x70a2b3, 0x4c57ca, 0x8c7c92, 0x7831c1, 0xf437b7, 0x50ead6, 0x28e02c, 0x60c547, 0x7c11be,
0x68fb7e, 0x90c1c6, 0x9cf48e, 0xfcd848, 0x64b9e8, 0x001cb3, 0x000d93, 0x003ee1, 0xc01ada, 0x34363b, 0xc81ee7, 0x9cfc01, 0xccc760, 0x087402,
0x30d9d9, 0x6030d4, 0x94bf2d, 0xc49880, 0xe0338e, 0x68fef7, 0xbce143, 0x285aeb, 0x28f076, 0x70700d, 0x9cf48e, 0xfcd848, 0x001cb3, 0x64b9e8,
0x645aed, 0xc0b658, 0x881908, 0xfc2a9c, 0x48605f, 0x188796, 0x002376, 0x108ee0, 0x68e7c2, 0x3c576c, 0x0ce0dc, 0x702ad5, 0x889f6f, 0x1c427d,
0x84100d, 0x04c23e, 0x5c5188, 0xe89120, 0x9c6c15, 0x4886e8, 0x2c2997, 0x5029f5, 0x4c569d, 0x14c213, 0x38539c, 0x58e6ba, 0xb831b5, 0x90633b,
0x102f6b, 0x00eebd, 0x281878, 0x6045bd, 0x7ced8d, 0xe85b5b, 0x000d3a, 0x782327, 0xf8a45f, 0x8cbebe, 0x640980, 0x98fae3, 0x185936, 0x9c99a0,
0xe09861, 0xf4f1e1, 0x60beb5, 0xb4e1c4, 0x70aab2, 0x0026ff, 0x406f2a, 0xc40bcb, 0xecd09f, 0xf4f5db, 0xe446da, 0x18f0e4, 0x9c2ea1, 0x50a009,
0x002557, 0xf05a09, 0x503275, 0x28cc01, 0xb46293, 0x04fe31, 0x845181, 0x20a60c, 0xf8e94e, 0xf40616, 0xbcb863, 0x188796, 0x002376, 0x84100d,
0xd831cf, 0xf8d0bd, 0xfcc734, 0xe4b021, 0xb0ec71, 0x3cbbfd, 0x2cae2b, 0x04c23e, 0x5c5188, 0xe89120, 0x9c6c15, 0x4886e8, 0x2c2997, 0x102f6b,
0xc488e5, 0x7c9122, 0xe8b4c8, 0x18895b, 0xe0db10, 0xe09971, 0x6077e2, 0x00eebd, 0x281878, 0x6045bd, 0x7ced8d, 0xe85b5b, 0x000d3a, 0xe09861,
0x680571, 0x6c2f2c, 0x300d43, 0x6c2779, 0x607edd, 0x9c2a83, 0xe45d75, 0xf4f1e1, 0x60beb5, 0xb4e1c4, 0x70aab2, 0x0026ff, 0x406f2a, 0x002557,
0xe4faed, 0xc83f26, 0x54f201, 0xa06090, 0xac3743, 0x141f78, 0x006f64, 0xf05a09, 0x503275, 0x28cc01, 0xb46293, 0x04fe31, 0x845181, 0xd831cf,
0xdc6672, 0x001e7d, 0x3c6200, 0x0024e9, 0x002399, 0xe4e0c5, 0xe8039a, 0xf8d0bd, 0xfcc734, 0xe4b021, 0xb0ec71, 0x3cbbfd, 0x2cae2b, 0xc488e5,
0xc4731e, 0x8c7712, 0x2013e0, 0x0007ab, 0x0021d2, 0xbc4760, 0xd0176a, 0x7c9122, 0xe8b4c8, 0x18895b, 0xe0db10, 0xe09971, 0x6077e2, 0x680571,
0x2cbaba, 0x24920e, 0x40d3ae, 0xf01dbc, 0x24dbed, 0xac3613, 0x1449e0, 0x6c2f2c, 0x300d43, 0x6c2779, 0x607edd, 0x9c2a83, 0xe45d75, 0xe4faed,
0xc0bdd1, 0xe8508b, 0xf025b7, 0xc8ba94, 0xec1f72, 0x9852b1, 0x1489fd, 0xc83f26, 0x54f201, 0xa06090, 0xac3743, 0x141f78, 0x006f64, 0xdc6672,
0xccfe3c, 0x789ed0, 0xe440e2, 0x1caf05, 0xe492fb, 0x0073e0, 0xbc4486, 0x001e7d, 0x3c6200, 0x0024e9, 0x002399, 0xe4e0c5, 0xe8039a, 0xc4731e,
0x380b40, 0x002490, 0x0023d7, 0xfca13e, 0xa00798, 0x945103, 0xc819f7, 0x8c7712, 0x2013e0, 0x0007ab, 0x0021d2, 0xbc4760, 0xd0176a, 0x2cbaba,
0x2c4401, 0x28e31f, 0x0c1daf, 0x14f65a, 0x742344, 0xf0b429, 0xec51bc, 0x24920e, 0x40d3ae, 0xf01dbc, 0x24dbed, 0xac3613, 0x1449e0, 0xc0bdd1,
0xf079e8, 0x887598, 0xd0b128, 0xd00401, 0xf06d78, 0x10683f, 0x74a722, 0xe8508b, 0xf025b7, 0xc8ba94, 0xec1f72, 0x9852b1, 0x1489fd, 0xccfe3c,
0x58a2b5, 0x64899a, 0x88074b, 0x64bc0c, 0xa039f7, 0x041b6d, 0x001f6b, 0x789ed0, 0xe440e2, 0x1caf05, 0xe492fb, 0x0073e0, 0xbc4486, 0x380b40,
0x30b4b8, 0x503cea, 0x0c9838, 0x54fcf0, 0x08aed6, 0xa816d0, 0x88bd45, 0x002490, 0x0023d7, 0xfca13e, 0xa00798, 0x945103, 0xc819f7, 0x2c4401,
0x544e90, 0xd03311, 0x70e72c, 0xc0cecd, 0x98e0d9, 0xe0accb, 0x78d75f, 0xec51bc, 0xf079e8, 0x887598, 0xd0b128, 0xd00401, 0xf06d78, 0x10683f,
0x2078f0, 0x985aeb, 0xa45e60, 0x006d52, 0x5cadcf, 0xb8e856, 0x90b21f, 0x74a722, 0x58a2b5, 0x64899a, 0x88074b, 0x64bc0c, 0xa039f7, 0x041b6d,
0xa8bbcf, 0xc8b5b7, 0x18af8f, 0xf4f951, 0xf0c1f1, 0xec3586, 0x88cb87, 0x001f6b, 0x30b4b8, 0x503cea, 0x54fcf0, 0x08aed6, 0xa816d0, 0x88bd45,
0xac3c0b, 0xcc785f, 0xe88d28, 0x3ce072, 0xf41ba1, 0xa85b78, 0x9cf387,
0x34a395, 0x48437c, 0xac87a3, 0x00f76f, 0xc0f2fb, 0x1caba7, 0x60facd,
0x680927, 0x78a3e4, 0x68a86d, 0xb817c2, 0xb88d12, 0x5c8d4e, 0xe06678,
0x1c1ac0, 0xc8f650, 0x60d9c7, 0x44fb42, 0x64a3cb, 0xd8d1cb, 0x542696,
0x14109f, 0xbc52b7, 0xe0c97a, 0x5c95ae, 0x8cfaba, 0x0cbc9f, 0xbc4cc4,
0x0c1539, 0x908d6c, 0x80006e, 0xb418d1, 0x1499e2, 0xe0c767, 0xa860b6,
0x24f094, 0x90b0ed, 0xc4b301, 0xe05f45, 0x483b38, 0x88e87f, 0xb853ac,
0x2c3361, 0x784f43, 0x404d7f, 0xbc926b, 0x0452f3, 0x241eeb, 0xf431c3,
0x64a5c3, 0x606944, 0xe498d6, 0x0cd746, 0x440010, 0x1c9e46, 0x7c04d0,
0xbc9fef, 0x8866a5, 0x70f087, 0x886b6e, 0x4c74bf, 0x844167, 0xb4f61c,
0xe49adc, 0xb8c111, 0x3408bc, 0xd0817a, 0xc4618b, 0x68ab1e, 0x2c61f6,
0x0026bb, 0x00254b, 0x002436, 0x002332, 0x002312, 0x0019e3, 0x001451,
0x000a27, 0x003065, 0x0050e4, 0xd023db, 0xe0b9ba, 0x3451c9, 0x8c5877,
0x9803d8, 0xc82a14, 0x88c663, 0x8c7b9d, 0x5855ca, 0xcc08e0, 0xe80688,
0x641cb0, 0x3cdcbc, 0xf47190, 0x587a6a, 0xe4c483, 0x8cf5a3, 0x14568e, 0x641cb0, 0x3cdcbc, 0xf47190, 0x587a6a, 0xe4c483, 0x8cf5a3, 0x14568e,
0x8058f8, 0xf0d7aa, 0xc49ded, 0xb0aa36, 0x2c5bb8, 0x1c48ce, 0x24f5aa, 0x8058f8, 0xf0d7aa, 0xc49ded, 0xb0aa36, 0x2c5bb8, 0x1c48ce, 0x24f5aa,
0xf877b8, 0x682737, 0x5056bf, 0x9097f3, 0x58c5cb, 0xacafb9, 0x30074d, 0xf877b8, 0x682737, 0x5056bf, 0x9097f3, 0x58c5cb, 0xacafb9, 0x30074d,
0x5c5181, 0x389af6, 0xe0aa96, 0x507705, 0x2c4053, 0x084acf, 0x1cddea, 0x5c5181, 0x389af6, 0xe0aa96, 0x507705, 0x2c4053, 0x084acf, 0x1cddea,
0x08152f, 0xd461da, 0xc8d083, 0x88e9fe, 0x88ae07, 0x5c0947, 0x38892c, 0x08152f, 0xb8c111, 0x3408bc, 0x844167, 0xb4f61c, 0x68ab1e, 0x2c61f6,
0x40831d, 0x50bc96, 0x9ce65e, 0x90dd5d, 0x08f69c, 0x00092d, 0xf8db7f, 0xe49adc, 0xd0817a, 0xc4618b, 0x3451c9, 0xe0b9ba, 0xd023db, 0xb88d12,
0xe899c4, 0x24da9b, 0x1c56fe, 0xe4907e, 0x80c5e6, 0x800184, 0xf8cfc5, 0xb817c2, 0x68a86d, 0x78a3e4, 0x680927, 0x60facd, 0x1caba7, 0x784f43,
0xc808e9, 0x206274, 0x30d587, 0xc0eefb, 0x502e5c, 0x847a88, 0x0025ae, 0x404d7f, 0x7c04d0, 0xbc9fef, 0x8866a5, 0x88e87f, 0xb853ac, 0x2c3361,
0x002538, 0x0022a1, 0x00125a, 0x9cd917, 0x9068c3, 0x408805, 0xf8f1b6, 0xa860b6, 0x24f094, 0x90b0ed, 0xc4b301, 0xe05f45, 0x483b38, 0xe0c767,
0x001ccc, 0x94ebcd, 0xa4e4b8, 0x389496, 0x0cb319, 0x08ee8b, 0xa89fba, 0x1c9e46, 0x0cd746, 0x440010, 0xe498d6, 0x606944, 0x0452f3, 0x241eeb,
0xfc1910, 0x083d88, 0x5c2e59, 0x646cb2, 0xf884f2, 0x14b484, 0x608f5c, 0xf431c3, 0x64a5c3, 0xbc926b, 0x0050e4, 0x003065, 0x000a27, 0x001451,
0x4cbca5, 0x78595e, 0xb0d09c, 0x4ca56d, 0xa48431, 0xe4f8ef, 0x1432d1, 0x8c7b9d, 0x88c663, 0xc82a14, 0x9803d8, 0x8c5877, 0x0019e3, 0x002312,
0xe458e7, 0x8cbfa6, 0x7840e4, 0x9000db, 0x183a2d, 0x08373d, 0x50f520, 0x002332, 0x002436, 0x00254b, 0x0026bb, 0x70f087, 0x886b6e, 0x4c74bf,
0xa4ebd3, 0x28987b, 0xf40e22, 0x9c3aaf, 0x0821ef, 0xa0cbfd, 0x34145f, 0xe80688, 0xcc08e0, 0x5855ca, 0x5c0947, 0x38892c, 0x40831d, 0x50bc96,
0x6c8fb5, 0xac5f3e, 0x509ea7, 0xdccf96, 0x6c2483, 0xc09727, 0xd85b2a, 0x985aeb, 0x2078f0, 0x78d75f, 0xe0accb, 0x98e0d9, 0xc0cecd, 0x70e72c,
0xacc33a, 0x88797e, 0x00e091, 0x6cd032, 0xc041f6, 0x0017d5, 0x001247, 0xd03311, 0x5cadcf, 0x006d52, 0x48437c, 0x34a395, 0x9cf387, 0xa85b78,
0xe4121d, 0x684898, 0xf409d8, 0xb479a7, 0x002339, 0xd487d8, 0x184617, 0x908d6c, 0x0c1539, 0xbc4cc4, 0x0cbc9f, 0xa45e60, 0x544e90, 0x9ce65e,
0x5001bb, 0x380a94, 0xd857ef, 0x1c66aa, 0x58c38b, 0x001ee2, 0x001c43, 0x90dd5d, 0x08f69c, 0xd461da, 0xc8d083, 0x88e9fe, 0x88ae07, 0x18af8f,
0x001d25, 0x3c5a37, 0x549b12, 0x3c8bfe, 0x00265d, 0xd4e8b2, 0x0808c2, 0xc8b5b7, 0xa8bbcf, 0x90b21f, 0xb8e856, 0x1499e2, 0xb418d1, 0x80006e,
0xb0c4e7, 0xd890e8, 0x34aa8b, 0x24c696, 0x181eb0, 0x20d390, 0x343111, 0x60d9c7, 0xc8f650, 0x1c1ac0, 0xe06678, 0x5c8d4e, 0xc0f2fb, 0x00f76f,
0x34be00, 0x78521a, 0x7825ad, 0xf4d9fb, 0x0017c9, 0x00166b, 0x00166c, 0xac87a3, 0x542696, 0xd8d1cb, 0x64a3cb, 0x44fb42, 0xf41ba1, 0x3ce072,
0xe47cf9, 0x002454, 0x20d5bf, 0x30cda7, 0xc87e75, 0x00233a, 0x60a4d0, 0xe88d28, 0xcc785f, 0xac3c0b, 0x88cb87, 0xec3586, 0xf0c1f1, 0xf4f951,
0x2c0e3d, 0xd4970b, 0x64cc2e, 0xb0e235, 0x38a4ed, 0xf48b32, 0x7c787e, 0x8cfaba, 0x5c95ae, 0xe0c97a, 0xbc52b7, 0x14109f, 0x00c3f4, 0x74eb80,
0xc0d3c0, 0x440444, 0xc09f05, 0xcc2d83, 0x38295a, 0x4c1a3d, 0xa81b5a, 0xa82bb9, 0x7c6b9c, 0x1cc3eb, 0xbca58b, 0x70fd46, 0xd07fa0, 0x9caa1b,
0xdc6dcd, 0x54fa3e, 0x0c8910, 0xfcf136, 0x981dfa, 0x84a466, 0x1867b0, 0x18d717, 0xb4cb57, 0x74b587, 0xd81c79, 0x8cfe57, 0xc0a600, 0xa823fe,
0xccb11a, 0xb8bbaf, 0x60c5ad, 0x28395e, 0xc4ae12, 0xdc74a8, 0xc087eb, 0xfcaab6, 0xc0bdc8, 0xa887b3, 0x742344, 0xd832e3, 0xe06267, 0x482ca0,
0x74f61c, 0x986f60, 0x4c189a, 0x3cf591, 0x602101, 0xa89675, 0x608e08, 0x1801f1, 0x70bbe9, 0xf0b429, 0x0c9838, 0x0c1daf, 0x28e31f, 0x14f65a,
0x7c2edd, 0x3cf7a4, 0x342d0d, 0x94d029, 0x308454, 0x4c49e3, 0x087808, 0xd4c94b, 0x703a51, 0xdc080f, 0xf82d7c, 0x9c648b, 0x14d00d, 0x00092d,
0xd03169, 0xbc5451, 0x00bf61, 0xf80cf3, 0x30766f, 0x8c3ae3, 0x78f882, 0xf8db7f, 0xe899c4, 0x24da9b, 0x1c56fe, 0xe4907e, 0x80c5e6, 0x800184,
0xb4f1da, 0x0021fb, 0xd013fd, 0xa8b86e, 0x04b167, 0xd86375, 0xdcbfe9, 0xf8cfc5, 0xc808e9, 0x206274, 0x30d587, 0xc0eefb, 0x502e5c, 0x847a88,
0x306a85, 0x2047da, 0x8035c1, 0xd02598, 0xa8667f, 0x7014a6, 0x10417f, 0x0025ae, 0x002538, 0x0022a1, 0x00125a, 0x9cd917, 0x9068c3, 0x408805,
0xac293a, 0x94e96a, 0x0c4de9, 0x907240, 0xa88808, 0xc8e0eb, 0x54e43a, 0xf8f1b6, 0x001ccc, 0x94ebcd, 0xa4e4b8, 0x389496, 0x0cb319, 0x08ee8b,
0x28e14c, 0x848e0c, 0xb03495, 0xf0f61c, 0x0c3021, 0xd89695, 0x649abe, 0xa89fba, 0xfc1910, 0x083d88, 0x5c2e59, 0x646cb2, 0xf884f2, 0x14b484,
0x5cf5da, 0x20a2e4, 0xf02475, 0x24a074, 0x8863df, 0x609217, 0x34e2fd, 0x608f5c, 0x4cbca5, 0x78595e, 0xb0d09c, 0x4ca56d, 0xa48431, 0xe4f8ef,
0x0c3e9f, 0x6c709f, 0x6c4008, 0x5c97f3, 0x90fd61, 0x006171, 0x80e650, 0x1432d1, 0xe458e7, 0x8cbfa6, 0x7840e4, 0x9000db, 0x183a2d, 0x08373d,
0xdc2b2a, 0xb844d9, 0xe0f5c6, 0x949426, 0xcc29f5, 0x58404e, 0xdc0c5c, 0x50f520, 0xa4ebd3, 0x28987b, 0xf40e22, 0x9c3aaf, 0x0821ef, 0xa0cbfd,
0x2c200b, 0xdca4ca, 0x8c8fe9, 0x9810e8, 0xb49cdf, 0xa4e975, 0xc0a53e, 0x34145f, 0x6c8fb5, 0xac5f3e, 0x509ea7, 0xdccf96, 0x6c2483, 0xc09727,
0x9800c6, 0x787b8a, 0x3866f0, 0x20ee28, 0x08f4ab, 0x8c8590, 0xb48b19, 0xd85b2a, 0xacc33a, 0x88797e, 0x00e091, 0x6cd032, 0xc041f6, 0x0017d5,
0xe49a79, 0x28a02b, 0xb44bd2, 0x2cf0a2, 0xecadb8, 0x9801a7, 0x609ac1, 0x001247, 0xe4121d, 0x684898, 0xf409d8, 0xb479a7, 0x002339, 0xd487d8,
0xf07960, 0x9c8ba0, 0x4c3275, 0xe4e4ab, 0xc8334b, 0x00f4b9, 0x0c771a, 0x184617, 0x5001bb, 0x380a94, 0xd857ef, 0x1c66aa, 0x58c38b, 0x001ee2,
0x74e1b6, 0x64200c, 0xc0847a, 0x183451, 0xfc253f, 0x1040f3, 0x6cc26b, 0x001c43, 0x001d25, 0x3c5a37, 0x549b12, 0x3c8bfe, 0x00265d, 0xd4e8b2,
0x182032, 0x70dee2, 0x00c610, 0x101c0c, 0x7cfadf, 0x5cf938, 0x3871de, 0x0808c2, 0xb0c4e7, 0xd890e8, 0x34aa8b, 0x24c696, 0x181eb0, 0x20d390,
0xbc5436, 0x9c4fda, 0x1c5cf2, 0x60fb42, 0x002500, 0x00236c, 0x0021e9, 0x343111, 0x34be00, 0x78521a, 0x7825ad, 0xf4d9fb, 0x0017c9, 0x00166b,
0x001ff3, 0x001f5b, 0x001e52, 0x001d4f, 0x001124, 0xa8fad8, 0x5c969d, 0x00166c, 0xe47cf9, 0x002454, 0x20d5bf, 0x30cda7, 0xc87e75, 0x00233a,
0xe48b7f, 0x84fcfe, 0x444c0c, 0x8c2daa, 0x6c3e6d, 0x189efc, 0xc09f42, 0x60a4d0, 0x2c0e3d, 0x7c787e, 0xc0d3c0, 0x440444, 0xc09f05, 0xcc2d83,
0xb8f6b1, 0x406c8f, 0xa4d1d2, 0x040cce, 0xd89e3f, 0x28e7cf, 0xc8bcc8, 0x38295a, 0x4c1a3d, 0xa81b5a, 0xdc6dcd, 0x54fa3e, 0x0c8910, 0xfcf136,
0xd8a25e, 0x90840d, 0xf81edf, 0xb0ca68, 0x98ca33, 0x68ef43, 0xcc2db7, 0x981dfa, 0x84a466, 0x1867b0, 0xccb11a, 0xb8bbaf, 0x60c5ad, 0x28395e,
0xd4a33d, 0xe4e0a6, 0x70ef00, 0x80ad16, 0x641cae, 0x14205e, 0x5c1dd9, 0xc4ae12, 0xdc74a8, 0xc087eb, 0x74f61c, 0x986f60, 0x4c189a, 0x3cf591,
0x18f1d8, 0xf86fc1, 0xf099b6, 0xdcd3a2, 0x38e7d8, 0xd8b377, 0xb4cef6, 0x602101, 0xa89675, 0x608e08, 0x7c2edd, 0x3cf7a4, 0x342d0d, 0x94d029,
0xd40b1a, 0x5882a8, 0xb4ae2b, 0x0c413e, 0xd0929e, 0x4480eb, 0xb84fd5, 0x308454, 0x087808, 0xd03169, 0xbc5451, 0x641cae, 0xa4e975, 0xc0a53e,
0xec59e7, 0x3059b7, 0x501ac5, 0x1cb094, 0xa0f450, 0x002248, 0xec8892, 0x9800c6, 0x787b8a, 0x3866f0, 0x20ee28, 0x08f4ab, 0x8c8590, 0x68ef43,
0xb07994, 0x141aa3, 0xccc3ea, 0x34bb26, 0x40786a, 0xf40b93, 0x68ed43, 0xcc2db7, 0xd4a33d, 0xe4e0a6, 0x70ef00, 0xb0ca68, 0x9810e8, 0xb49cdf,
0x34bb1f, 0x489d24, 0x000f86, 0xacee9e, 0xc08997, 0x2827bf, 0xf05b7b, 0xdca4ca, 0x8c8fe9, 0x98ca33, 0xfc253f, 0x183451, 0xc0847a, 0x64200c,
0x7cf90e, 0xac5a14, 0xb0c559, 0xbcd11f, 0xa0b4a5, 0x80656d, 0x48137e, 0x74e1b6, 0x0c771a, 0x00f4b9, 0xc8334b, 0xb8f6b1, 0xc09f42, 0x189efc,
0xe83a12, 0x9c0298, 0x6c8336, 0xb8c68e, 0x74458a, 0xa49a58, 0xb4ef39, 0x6c3e6d, 0x8c2daa, 0xe4e4ab, 0x58404e, 0xdc0c5c, 0x2c200b, 0x609ac1,
0x14a364, 0x3ca10d, 0x206e9c, 0x183f47, 0x0c715d, 0x0c1420, 0xa80600, 0xf07960, 0x9c8ba0, 0x28a02b, 0xb44bd2, 0x9c4fda, 0x1c5cf2, 0x3871de,
0x6cf373, 0x78c3e9, 0xc83870, 0x288335, 0x44783e, 0x202d07, 0x98398e, 0xbc5436, 0x5cf938, 0x4c3275, 0x2cf0a2, 0xecadb8, 0x9801a7, 0xb48b19,
0x348a7b, 0xbc765e, 0x78009e, 0x68c44d, 0xf8e61a, 0x888322, 0x84b541, 0xe49a79, 0x406c8f, 0x00c610, 0x70dee2, 0x182032, 0x6cc26b, 0x1040f3,
0x0015b9, 0x001df6, 0xece09b, 0x606bbd, 0x0000f0, 0x4844f7, 0x1c5a3e, 0x001d4f, 0x001e52, 0x001f5b, 0x001ff3, 0x0021e9, 0x00236c, 0x002500,
0xf47b5e, 0x008701, 0xfc4203, 0x1c232c, 0xcc61e5, 0x404e36, 0x009ec8, 0x60fb42, 0xf81edf, 0x90840d, 0xd8a25e, 0xc8bcc8, 0x28e7cf, 0xd89e3f,
0xacf7f3, 0x102ab3, 0x584498, 0xa086c6, 0x7c1dd9, 0x9893cc, 0x3ccd93, 0x040cce, 0xa4d1d2, 0x7cfadf, 0x101c0c, 0x001124, 0x6c709f, 0x0c3e9f,
0xf06bca, 0x3423ba, 0xd022be, 0xd02544, 0xbc20a4, 0x14f42a, 0xbc851f, 0x34e2fd, 0x609217, 0x8863df, 0x80e650, 0x006171, 0x90fd61, 0x5c97f3,
0xb85e7b, 0xc462ea, 0x0023d6, 0x002491, 0x001b98, 0x44f459, 0x34c3ac, 0x6c4008, 0x24a074, 0xf02475, 0x20a2e4, 0x5cf5da, 0x649abe, 0x94e96a,
0x94d771, 0x4c3c16, 0x9401c2, 0xb43a28, 0xd0c1b1, 0xf008f1, 0x78471d, 0xac293a, 0x10417f, 0xb844d9, 0xdc2b2a, 0x14205e, 0x5c1dd9, 0x18f1d8,
0x3816d1, 0xd48890, 0x002566, 0x00265f, 0xacc1ee, 0x5cba37, 0x7802f8, 0xf86fc1, 0xf099b6, 0x907240, 0x0c4de9, 0xd89695, 0x0c3021, 0xf0f61c,
0x3096fb, 0xf0ee10, 0xa43d78, 0xec01ee, 0xb83765, 0xc4576e, 0x90f1aa, 0xb03495, 0x848e0c, 0x949426, 0xe0f5c6, 0x28e14c, 0x54e43a, 0xc8e0eb,
0x78bdbc, 0xd47ae2, 0x84c0ef, 0x7c1c68, 0xd463c6, 0x508f4c, 0x7c6456, 0xa88808, 0x444c0c, 0x84fcfe, 0xe48b7f, 0x5c969d, 0xa8fad8, 0x7014a6,
0x448f17, 0x04d6aa, 0x9ce063, 0xf06e0b, 0x5c865c, 0xf0b0e7, 0x209bcd, 0xa8667f, 0xd02598, 0xcc29f5, 0xdcd3a2, 0x08c5e1, 0x00bf61, 0xf80cf3,
0xcc20e8, 0x04d13a, 0x0cf346, 0x003de8, 0x485929, 0x34fcef, 0x002483, 0x30766f, 0x8c3ae3, 0x78f882, 0xb4f1da, 0x0021fb, 0xd013fd, 0xa8b86e,
0x001c62, 0x583f54, 0x40b0fa, 0xa8922c, 0x98d6f7, 0x505527, 0x0034da, 0xdcbfe9, 0x306a85, 0x4466fc, 0xfca621, 0x0ccb85, 0xa4d990, 0xd003df,
0xa09169, 0x88365f, 0x9c8c6e, 0xbcffeb, 0x685acf, 0x48746e, 0x54724f, 0x24fce5, 0xe4b2fb, 0xf83880, 0x241b7a, 0x402619, 0xbcfed9, 0x808223,
0x04f13e, 0x600308, 0x80ea96, 0x24a2e1, 0x90b931, 0x280b5c, 0xa8968a, 0x3830f9, 0x6c006b, 0x38a4ed, 0xb0e235, 0x64cc2e, 0xd86375, 0x80ad16,
0x9c04eb, 0x885395, 0x80929f, 0x98b8e3, 0xd8004d, 0x98fe94, 0x68644b, 0x2047da, 0x8035c1, 0x9487e0, 0x7c03ab, 0xd4970b, 0xf48b32, 0x4c49e3,
0xf099bf, 0xfce998, 0x48e9f1, 0x4c7c5f, 0x60f81d, 0x689c70, 0x2cb43a, 0x04b167, 0xd8ce3a, 0xb8c74a, 0xfc183c, 0xc0e862, 0xec2ce2, 0x64c753,
0x042665, 0xf4f15a, 0x207d74, 0x4c8d79, 0xfcfc48, 0x38c986, 0x70ece4, 0x38e7d8, 0xd8b377, 0xb4cef6, 0xd40b1a, 0x5882a8, 0xb4ae2b, 0x0c413e,
0xd81d72, 0x94f6a3, 0x78fd94, 0x48d705, 0x7c6df8, 0x3cab8e, 0x787e61, 0xd0929e, 0x4480eb, 0xb84fd5, 0xec59e7, 0x3059b7, 0x501ac5, 0x1cb094,
0xd4f46f, 0xc88550, 0xac7f3e, 0xa4c361, 0x087045, 0x40331a, 0xdc3714, 0xa0f450, 0x002248, 0xec8892, 0xb07994, 0x141aa3, 0xccc3ea, 0x34bb26,
0x789f70, 0x64b0a6, 0x84fcac, 0x6c19c0, 0x20ab37, 0xc0d012, 0xd4dccd, 0x40786a, 0xf40b93, 0x68ed43, 0x34bb1f, 0x489d24, 0x000f86, 0xacee9e,
0x484baa, 0xf80377, 0x14bd61, 0x78886d, 0xa85c2c, 0x00db70, 0xbcec5d, 0xc08997, 0x2827bf, 0xf05b7b, 0x7cf90e, 0xac5a14, 0xb0c559, 0xbcd11f,
0xdc415f, 0x30636b, 0x0c5101, 0x086d41, 0x04d3cf, 0x203cae, 0x748d08, 0xa0b4a5, 0x80656d, 0x48137e, 0xe83a12, 0x9c0298, 0x6c8336, 0xb8c68e,
0xa03be3, 0x186590, 0x0010fa, 0x000502, 0xb8782e, 0xa4d18c, 0xcc25ef, 0x74458a, 0xa49a58, 0xb4ef39, 0x14a364, 0x3ca10d, 0x206e9c, 0x183f47,
0x68dbca, 0x044bed, 0x6c8dc1, 0x38cada, 0xf45c89, 0x581faa, 0x24ab81, 0x0c715d, 0x0c1420, 0xa80600, 0x6cf373, 0x78c3e9, 0xc83870, 0x288335,
0x70cd60, 0x7cc537, 0xc42c03, 0xd83062, 0x40d32d, 0x7c6d62, 0x286ab8, 0x44783e, 0x202d07, 0x98398e, 0x348a7b, 0xbc765e, 0x78009e, 0x68c44d,
0x403cfc, 0xb8c75d, 0xe8040b, 0xe4ce8f, 0x3c0754, 0xa46706, 0x80b03d, 0xf8e61a, 0x888322, 0x84b541, 0x0015b9, 0x001df6, 0xece09b, 0x606bbd,
0xc83c85, 0xa04ea7, 0x409c28, 0x08e689, 0x4cb199, 0x98d6bb, 0x3cd0f8, 0x0000f0, 0x4844f7, 0x1c5a3e, 0xf47b5e, 0x008701, 0xfc4203, 0x1c232c,
0x7cc3a1, 0x002608, 0x001ec2, 0x001b63, 0x0017f2, 0x0016cb, 0x000393, 0xcc61e5, 0x404e36, 0x9893cc, 0x3ccd93, 0xf06bca, 0x3423ba, 0xd022be,
0x804971, 0x64e682, 0xb4f7a1, 0x785dc8, 0x48c796, 0x804e70, 0x3880df, 0xd02544, 0xbc20a4, 0x14f42a, 0xbc851f, 0xb85e7b, 0xc462ea, 0x0023d6,
0x1094bb, 0xf01898, 0x48a91c, 0xa056f3, 0x549963, 0x28ff3c, 0x902155, 0x002491, 0x001b98, 0x44f459, 0x34c3ac, 0x94d771, 0x4c3c16, 0x9401c2,
0x64a769, 0xbccfcc, 0xa4516f, 0x3c8375, 0x149a10, 0x0ce725, 0xc0335e, 0xb43a28, 0xd0c1b1, 0xf008f1, 0x78471d, 0x3816d1, 0xd48890, 0x002566,
0x20a99b, 0x4c0bbe, 0x7c1e52, 0xdcb4c4, 0x001dd8, 0x0017fa, 0x0003ff, 0x00265f, 0x5cba37, 0x3096fb, 0xf0ee10, 0xa43d78, 0xec01ee, 0xb83765,
0xf8e079, 0x1430c6, 0xe0757d, 0x9cd35b, 0x60af6d, 0xb85a73, 0x103047, 0xc4576e, 0x90f1aa, 0x78bdbc, 0xd47ae2, 0x84c0ef, 0x7c1c68, 0xd463c6,
0x109266, 0xb047bf, 0x7c0bc6, 0x804e81, 0x244b81, 0x50a4c8, 0x8425db, 0x7c6456, 0x448f17, 0x04d6aa, 0x9ce063, 0xf06e0b, 0x5c865c, 0x003de8,
0xd8c4e9, 0x50c8e5, 0x446d6c, 0x38d40b, 0x647791, 0x781fdb, 0x08fc88, 0x08e689, 0x7836cc, 0x08d46a, 0x485929, 0x34fcef, 0x002483, 0x001c62,
0x30c7ae, 0x18227e, 0x00f46f, 0x9ce6e7, 0xe498d1, 0x5cca1a, 0x70288b, 0x583f54, 0x40b0fa, 0xa8922c, 0x98d6f7, 0x505527, 0x0034da, 0xa09169,
0x4849c7, 0x205ef7, 0x182666, 0xc06599, 0xcc07ab, 0xe84e84, 0x50fc9f, 0x88365f, 0x9c8c6e, 0xbcffeb, 0x685acf, 0xb4f7a1, 0x785dc8, 0x48c796,
0xe432cb, 0x889b39, 0xbcb1f3, 0x38ece4, 0xccf9e8, 0xf0e77e, 0x5ce8eb, 0x804e70, 0x3880df, 0xdc415f, 0x30636b, 0xf45c89, 0x68dbca, 0x044bed,
0xb8d9ce, 0x70f927, 0x301966, 0x28bab5, 0x103b59, 0x6cb7f4, 0x001ee1, 0x6c8dc1, 0x38cada, 0xa4d18c, 0x186590, 0x64b0a6, 0x84fcac, 0x6c19c0,
0x0018af, 0xbc72b1, 0x78f7be, 0xf49f54, 0x00214c, 0x001632, 0xd0667b, 0x20ab37, 0x203cae, 0x748d08, 0xa03be3, 0x7c6d62, 0x40d32d, 0xd83062,
0x001377, 0x50b7c3, 0x8018a7, 0x444e1a, 0xe8e5d6, 0x5492be, 0x101dc0, 0xc42c03, 0x7cc537, 0x70cd60, 0xc0d012, 0xd4dccd, 0x484baa, 0xf80377,
0x0021d1, 0x68dfdd, 0xc46ab7, 0xfc64ba, 0x2082c0, 0x3480b3, 0x7451ba, 0x14bd61, 0xcc25ef, 0xb8782e, 0x000502, 0x0010fa, 0x000393, 0x0016cb,
0x64b473, 0xcc2d8c, 0x949aa9, 0x20dbab, 0x5c9960, 0x948bc1, 0x4827ea, 0x409c28, 0x78886d, 0xa85c2c, 0x00db70, 0x0c5101, 0x086d41, 0x04d3cf,
0x388c50, 0xa09347, 0xc8f230, 0x1c77f6, 0xe44790, 0xd4503f, 0x40163b, 0xbcec5d, 0x80b03d, 0xc83c85, 0xa04ea7, 0x0017f2, 0x001b63, 0x001ec2,
0x5c497d, 0xe47dbd, 0x503da1, 0x508569, 0x1077b1, 0x5cf6dc, 0x380195, 0x002608, 0xa4c361, 0xac7f3e, 0x280b5c, 0x90b931, 0x24a2e1, 0x80ea96,
0xbc1485, 0x88d50c, 0x947be7, 0x00ec0a, 0x54bd79, 0xdc44b6, 0x1007b6, 0x600308, 0x04f13e, 0x54724f, 0x48746e, 0xd4f46f, 0x787e61, 0x60f81d,
0xc0174d, 0xa407b6, 0x149f3c, 0x88b4a6, 0x2c5491, 0x5c70a3, 0x10f96f, 0x4c7c5f, 0x48e9f1, 0xfce998, 0xf099bf, 0x68644b, 0x789f70, 0x24ab81,
0xf01c13, 0x00aa70, 0xbcf5ac, 0xccfa00, 0xf8a9d0, 0x805a04, 0x5caf06, 0x581faa, 0xa46706, 0x3c0754, 0xe4ce8f, 0xe8040b, 0xb8c75d, 0x403cfc,
0xb81daa, 0x10f1f2, 0x0025e5, 0x0022a9, 0xc49a02, 0x344df7, 0xd41a3f, 0x98fe94, 0xd8004d, 0x98b8e3, 0x80929f, 0x885395, 0x9c04eb, 0xa8968a,
0xcc6ea4, 0xa46cf1, 0x0ca8a7, 0x54b802, 0x0469f8, 0xbc6c21, 0xc869cd, 0xdc3714, 0x40331a, 0x94f6a3, 0xd81d72, 0x70ece4, 0x38c986, 0xfcfc48,
0x80d605, 0x587f57, 0xa4b805, 0x70480f, 0x18f643, 0x748114, 0x18ee69, 0x4c8d79, 0x207d74, 0xf4f15a, 0x042665, 0x2cb43a, 0x689c70, 0x087045,
0xf0dbe2, 0xb8098a, 0x549f13, 0x2c1f23, 0x507a55, 0x9c35eb, 0xa43135, 0x3cab8e, 0x7c6df8, 0x48d705, 0x78fd94, 0xc88550, 0x286ab8, 0x7cc3a1,
0xd0034b, 0xa01828, 0xd0a637, 0xd04f7e, 0xd8bb2c, 0x80be05, 0xe0b52d, 0x3cd0f8, 0x98d6bb, 0x4cb199, 0x64e682, 0x804971, 0xcc20e8, 0x209bcd,
0x68ae20, 0xe8802e, 0x7c0191, 0x9c293f, 0x341298, 0x903c92, 0x24240e, 0xf0b0e7, 0xa056f3, 0x549963, 0x28ff3c, 0x1094bb, 0xf01898, 0x48a91c,
0xa0999b, 0xe0f847, 0x442a60, 0x1093e9, 0xdc2b61, 0xb8ff61, 0x18e7f4, 0x58b10f, 0x304b07, 0x1496e5, 0x80ceb9, 0xcc2119, 0x0057c1, 0x14c697,
0x78ca39, 0x5c5948, 0x60334b, 0x9027e4, 0xd49a20, 0xb09fba, 0x8c006d, 0xfc039f, 0x9c0cdf, 0x007204, 0x90e17b, 0x18810e, 0x608c4a, 0xa4d931,
0xc06394, 0x843835, 0xe4c63d, 0x54eaa8, 0xa886dd, 0xaccf5c, 0xf0dbf8, 0x6cc7ec, 0x647bce, 0x584498, 0xacc1ee, 0x7802f8, 0x508f4c, 0x04d13a,
0x98f0ab, 0xdc9b9c, 0x8c2937, 0xdc86d8, 0xa88e24, 0xd8cf9c, 0x04489a, 0x0cf346, 0x082525, 0xf460e2, 0xa45046, 0x009ec8, 0x7c1dd9, 0xa086c6,
0x3c15c2, 0x20c9d0, 0x74e2f5, 0x842999, 0x9c207b, 0x283737, 0x148fc6, 0x102ab3, 0xacf7f3, 0x601d91, 0x38f9d3, 0x44e66e, 0xe83617, 0x344262,
0x28cfda, 0x145a05, 0xa0edcd, 0x1ce62b, 0x3090ab, 0x7073cb, 0xf0cba1, 0xc09ad0, 0x902155, 0x64a769, 0xbccfcc, 0xa4516f, 0x3c8375, 0x149a10,
0x045453, 0x40b395, 0x008865, 0x30f7c5, 0x20768f, 0xc0ccf8, 0x80ed2c, 0x0ce725, 0xc0335e, 0x20a99b, 0x4c0bbe, 0x7c1e52, 0xdcb4c4, 0x7c6f06,
0xe8b2ac, 0x8489ad, 0x8c8ef2, 0xf40f24, 0x84a134, 0x1c9148, 0x5cf7e6, 0x001dd8, 0x0017fa, 0x000a75, 0x0003ff, 0xf8e079, 0x1430c6, 0xe0757d,
0xa0d795, 0xcc088d, 0x00b362, 0xf86214, 0xb0702d, 0xd0c5f3, 0x60f445, 0x9cd35b, 0x60af6d, 0xb85a73, 0x103047, 0x109266, 0xb047bf, 0x7c0bc6,
0x5082d5, 0x9c84bf, 0x48bf6b, 0x245ba7, 0xbca920, 0xb019c6, 0x58e28f, 0x804e81, 0x244b81, 0x50a4c8, 0x8425db, 0xd8c4e9, 0x50c8e5, 0x446d6c,
0xac1f74, 0x080007, 0xe425e7, 0x28cfe9, 0x9060f1, 0x741bb2, 0x28ed6a, 0x38d40b, 0x647791, 0x781fdb, 0x08fc88, 0x30c7ae, 0x18227e, 0x00f46f,
0x34ab37, 0x60a37d, 0x0056cd, 0x7081eb, 0x086698, 0x24f677, 0x7867d7, 0x9ce6e7, 0xe498d1, 0x5cca1a, 0x70288b, 0x4849c7, 0x205ef7, 0x182666,
0x5433cb, 0xd0d2b0, 0xd88f76, 0x3c2ef9, 0xdc56e7, 0x347c25, 0xd4909c, 0xc06599, 0xcc07ab, 0xe84e84, 0x50fc9f, 0xe432cb, 0x889b39, 0xbcb1f3,
0x041e64, 0x0026b0, 0x00264a, 0x0025bc, 0x0023df, 0x002241, 0x000a95, 0x38ece4, 0xccf9e8, 0xf0e77e, 0x5ce8eb, 0xb8d9ce, 0x70f927, 0x301966,
0x38e60a, 0x24181d, 0xf4c248, 0xa8515b, 0xc048e6, 0xd07714, 0x749eaf, 0x28bab5, 0x103b59, 0x6cb7f4, 0x001ee1, 0x0018af, 0xbc72b1, 0x78f7be,
0xb841a4, 0xf895ea, 0x50a67f, 0x647033, 0x846878}; 0xf49f54, 0x00214c, 0x001632, 0xd0667b, 0x001377, 0x50b7c3, 0x8018a7,
0x444e1a, 0xe8e5d6, 0x5492be, 0x101dc0, 0x0021d1, 0xcc2d8c, 0x949aa9,
0x20dbab, 0x5c9960, 0x88b4a6, 0x2c5491, 0x5c70a3, 0x10f96f, 0xf01c13,
0x00aa70, 0xbcf5ac, 0xccfa00, 0xf8a9d0, 0x805a04, 0x5caf06, 0xb81daa,
0x10f1f2, 0x0025e5, 0x0022a9, 0xc49a02, 0x344df7, 0xd41a3f, 0xcc6ea4,
0xa46cf1, 0x0ca8a7, 0x54b802, 0x24181d, 0xf4c248, 0xa8515b, 0xc048e6,
0xd07714, 0x2816a8, 0x84a134, 0x1c9148, 0xc0ccf8, 0x80ed2c, 0xe8b2ac,
0x8489ad, 0x20768f, 0x28ed6a, 0x34ab37, 0x60a37d, 0x0056cd, 0xbca920,
0x5082d5, 0x9c84bf, 0x00b362, 0xf86214, 0xb0702d, 0xd0c5f3, 0x0023df,
0x0025bc, 0x00264a, 0x0026b0, 0x041e64, 0xd49a20, 0x9027e4, 0x60334b,
0x5c5948, 0x60f445, 0x5cf7e6, 0xa0d795, 0xcc088d, 0x8c8ef2, 0xf40f24,
0x24f677, 0x7867d7, 0x5433cb, 0xd0d2b0, 0xd88f76, 0x3c2ef9, 0x7081eb,
0x086698, 0x9060f1, 0x741bb2, 0x28cfe9, 0xe425e7, 0xb019c6, 0x58e28f,
0xac1f74, 0x48bf6b, 0x245ba7, 0xdc56e7, 0x347c25, 0xd4909c, 0x080007,
0x000a95, 0x002241, 0x18ee69, 0x748114, 0x18f643, 0xd0a637, 0xa01828,
0xd0034b, 0xa43135, 0x9c35eb, 0x507a55, 0xa0999b, 0x24240e, 0x903c92,
0xa88e24, 0xe8802e, 0x68ae20, 0xe0b52d, 0x80be05, 0xd8bb2c, 0xd04f7e,
0x2c1f23, 0x549f13, 0xb8098a, 0xf0dbe2, 0x8c2937, 0xdc9b9c, 0x98f0ab,
0xf0dbf8, 0xaccf5c, 0x3c15c2, 0x04489a, 0xd8cf9c, 0xa886dd, 0x54eaa8,
0xe4c63d, 0x843835, 0xc06394, 0x8c006d, 0xb09fba, 0xdc86d8, 0x78ca39,
0x18e7f4, 0xb8ff61, 0xdc2b61, 0x1093e9, 0x442a60, 0xe0f847, 0x145a05,
0x28cfda, 0x148fc6, 0x283737, 0x045453, 0xf0cba1, 0x30f7c5, 0x008865,
0x40b395, 0x3090ab, 0x1ce62b, 0xa0edcd, 0x842999, 0x74e2f5, 0x20c9d0,
0x7073cb, 0x9c207b, 0x341298, 0x9c293f, 0x7c0191, 0x70480f, 0xa4b805,
0x587f57, 0x80d605, 0xc869cd, 0xbc6c21, 0x0469f8, 0x749eaf, 0xb841a4,
0xf895ea, 0x50a67f, 0x647033, 0x846878, 0x948bc1, 0x4827ea, 0x388c50,
0xa09347, 0xc8f230, 0x1c77f6, 0xe44790, 0xd4503f, 0x40163b, 0x5c497d,
0xe47dbd, 0x503da1, 0x508569, 0x1077b1, 0x5cf6dc, 0x380195, 0xbc1485,
0x88d50c, 0x947be7, 0x54bd79, 0xdc44b6, 0x1007b6, 0xc0174d, 0xa407b6,
0x149f3c, 0xd868c3, 0xc493d9, 0x00b5d0, 0x8c83e1, 0xfcb6d8, 0x6ce85c,
0x007c2d, 0xf47def, 0x7c8bb5, 0xdcf756, 0x68dfdd, 0x64b473, 0x7451ba,
0x3480b3, 0x2082c0, 0xfc64ba, 0xc46ab7, 0x00ec0a, 0x38e60a, 0x04e598,
0x2ca9f0, 0x586b14, 0x94b01f, 0x94f6d6, 0x40bc60};

View File

@ -6,7 +6,7 @@
static const char TAG[] = "wifi"; static const char TAG[] = "wifi";
static wifi_country_t wifi_country = {WIFI_MY_COUNTRY, WIFI_CHANNEL_MIN, static wifi_country_t wifi_country = {WIFI_MY_COUNTRY, WIFI_CHANNEL_MIN,
WIFI_CHANNEL_MAX, 0, WIFI_CHANNEL_MAX, 100,
WIFI_COUNTRY_POLICY_MANUAL}; WIFI_COUNTRY_POLICY_MANUAL};
// using IRAM_:ATTR here to speed up callback function // using IRAM_:ATTR here to speed up callback function
@ -27,15 +27,19 @@ IRAM_ATTR void wifi_sniffer_packet_handler(void *buff,
void wifi_sniffer_init(void) { void wifi_sniffer_init(void) {
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
cfg.nvs_enable = 0; // we don't need any wifi settings from NVRAM cfg.nvs_enable = 0; // we don't need any wifi settings from NVRAM
cfg.wifi_task_core_id = 0; // we want wifi task running on core 0
wifi_promiscuous_filter_t filter = { wifi_promiscuous_filter_t filter = {
.filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT}; // we need only MGMT frames // .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT}; // only MGMT frames
ESP_ERROR_CHECK(esp_wifi_init(&cfg)); // configure Wifi with cfg .filter_mask = WIFI_PROMIS_FILTER_MASK_ALL}; // we use all frames
ESP_ERROR_CHECK(esp_wifi_init(&cfg)); // configure Wifi with cfg
ESP_ERROR_CHECK( ESP_ERROR_CHECK(
esp_wifi_set_country(&wifi_country)); // set locales for RF and channels esp_wifi_set_country(&wifi_country)); // set locales for RF and channels
ESP_ERROR_CHECK( ESP_ERROR_CHECK(
esp_wifi_set_storage(WIFI_STORAGE_RAM)); // we don't need NVRAM esp_wifi_set_storage(WIFI_STORAGE_RAM)); // we don't need NVRAM
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL)); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL));
ESP_ERROR_CHECK(esp_wifi_stop());
ESP_ERROR_CHECK( ESP_ERROR_CHECK(
esp_wifi_set_promiscuous_filter(&filter)); // set MAC frame filter esp_wifi_set_promiscuous_filter(&filter)); // set MAC frame filter
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler)); ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler));
@ -43,30 +47,13 @@ void wifi_sniffer_init(void) {
} }
// Wifi channel rotation task // Wifi channel rotation task
void wifi_channel_loop(void *pvParameters) { void switchWifiChannel(void *parameter) {
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
while (1) { while (1) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // waiting for channel switch timer
if (ChannelTimerIRQ) { channel =
portENTER_CRITICAL(&timerMux); (channel % WIFI_CHANNEL_MAX) + 1; // rotate channel 1..WIFI_CHANNEL_MAX
ChannelTimerIRQ = 0; esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
portEXIT_CRITICAL(&timerMux); ESP_LOGD(TAG, "Wifi set channel %d", channel);
// rotates variable channel 1..WIFI_CHANNEL_MAX }
channel = (channel % WIFI_CHANNEL_MAX) + 1; vTaskDelete(NULL); // shoud never be reached
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
ESP_LOGD(TAG, "Wifi set channel %d", channel);
vTaskDelay(1 / portTICK_PERIOD_MS); // reset watchdog
}
} // end of infinite wifi channel rotation loop
} }
// IRQ handler
void IRAM_ATTR ChannelSwitchIRQ() {
portENTER_CRITICAL(&timerMux);
ChannelTimerIRQ++;
portEXIT_CRITICAL(&timerMux);
}

View File

@ -26,8 +26,7 @@ typedef struct {
} wifi_ieee80211_packet_t; } wifi_ieee80211_packet_t;
void wifi_sniffer_init(void); void wifi_sniffer_init(void);
void wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type); void IRAM_ATTR wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type);
void ChannelSwitchIRQ(void); void switchWifiChannel(void * parameter);
void wifi_channel_loop(void *pvParameters);
#endif #endif