Merge pull request #155 from cyberman54/development

v1.4.35
This commit is contained in:
Verkehrsrot 2018-09-19 16:48:23 +02:00 committed by GitHub
commit 569da23b6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1075 additions and 278 deletions

1
.gitignore vendored
View File

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

View File

@ -32,7 +32,7 @@ This can all be done with a single small and cheap ESP32 board for less than $20
- WeMos: LoLin32 + [LoraNode32 shield](https://github.com/hallard/LoLin32-Lora),
LoLin32lite + [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora)
*SPI only*: (coming soon)
*SPI only*: (code yet to come)
- Pyom: WiPy
- WeMos: LoLin32, LoLin32 Lite, WeMos D32
@ -47,7 +47,7 @@ Depending on board hardware following features are supported:
- GPS
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
<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 +62,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 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.
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 +76,14 @@ Use <A HREF="https://platformio.org/">PlatformIO</A> with your preferred IDE for
# 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
<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.
- **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
@ -128,7 +134,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.
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
@ -142,6 +154,7 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering.
byte 3-10: Uptime [seconds]
byte 11: CPU temperature [°C]
bytes 12-15: Free RAM [bytes]
bytes 16-17: Last CPU reset reason [core 0, core 1]
**Port #3:** Device configuration query result
@ -181,55 +194,11 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering.
byte 1: Beacon RSSI reception level
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
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
@ -272,12 +241,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
0x09 reset functions
0x09 reset functions (send this command with confirmed ack only to avoid boot loops!)
0 = restart device
1 = reset MAC counter to zero
2 = reset device to factory settings
3 = flush send queues
9 = reboot device to OTA update via Wifi mode
0x0A set LoRaWAN payload send cycle

94
build.py Normal file
View File

@ -0,0 +1,94 @@
# 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

@ -790,8 +790,12 @@ void radio_irq_handler (u1_t dio) {
// now read the FIFO
readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
// read rx quality parameters
LMIC.snr = readReg(LORARegPktSnrValue); // SNR [dB] * 4
LMIC.rssi = readReg(LORARegPktRssiValue) - 125 + 64; // RSSI [dBm] (-196...+63)
//LMIC.snr = readReg(LORARegPktSnrValue); // SNR [dB] * 4
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 ) {
// indicate timeout
LMIC.dataLen = 0;

View File

@ -1,10 +1,5 @@
; 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
; http://docs.platformio.org/page/projectconf.html
@ -16,7 +11,8 @@ env_default = generic
;env_default = heltec
;env_default = ttgov1
;env_default = ttgov2
;env_default = ttgov21
;env_default = ttgov21old
;env_default = ttgov21new
;env_default = ttgobeam
;env_default = lopy
;env_default = lopy4
@ -24,14 +20,26 @@ env_default = generic
;env_default = lolin32litelora
;env_default = lolin32lora
;env_default = lolin32lite
;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.
[common_env_data]
[common]
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
release_version = 1.4.35
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
debug_level = 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.3.0
;platform_espressif32 = https://github.com/platformio/platform-espressif32.git#feature/stage
board_build.partitions = no_ota.csv
lib_deps_all =
board_build.partitions = min_spiffs.csv
monitor_speed = 115200
lib_deps_all =
ArduinoJson@^5.13.1
lib_deps_display =
U8g2@>=2.23.16
lib_deps_rgbled =
@ -39,199 +47,227 @@ lib_deps_rgbled =
lib_deps_gps =
TinyGPSPlus@>=1.0.2
Time@>=1.5
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
;
build_flags =
; override lora settings from LMiC library in lmic/config.h and use main.h instead
-D_lmic_config_h_
-include "src/paxcounter.conf"
-include "src/hal/${PIOENV}.h"
-w
'-DCORE_DEBUG_LEVEL=${common.debug_level}'
'-DBINTRAY_PACKAGE="${PIOENV}"'
'-DPROGVERSION="${common.release_version}"'
[env:ebox]
platform = ${common_env_data.platform_espressif32}
platform = ${common.platform_espressif32}
framework = arduino
board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions}
board_build.partitions = ${common.board_build.partitions}
upload_speed = 115200
monitor_speed = 115200
lib_deps =
${common_env_data.lib_deps_all}
${common.lib_deps_all}
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]
platform = ${common_env_data.platform_espressif32}
platform = ${common.platform_espressif32}
framework = arduino
board = heltec_wifi_lora_32
board_build.partitions = ${common_env_data.board_build.partitions}
board_build.partitions = ${common.board_build.partitions}
upload_speed = 115200
monitor_speed = 115200
lib_deps =
${common_env_data.lib_deps_all}
${common_env_data.lib_deps_display}
${common.lib_deps_all}
${common.lib_deps_display}
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]
platform = ${common_env_data.platform_espressif32}
platform = ${common.platform_espressif32}
framework = arduino
board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions}
board_build.partitions = ${common.board_build.partitions}
upload_speed = 115200
monitor_speed = 115200
lib_deps =
${common_env_data.lib_deps_all}
${common_env_data.lib_deps_display}
${common.lib_deps_all}
${common.lib_deps_display}
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]
platform = ${common_env_data.platform_espressif32}
platform = ${common.platform_espressif32}
framework = arduino
board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions}
board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600
monitor_speed = 115200
lib_deps =
${common_env_data.lib_deps_all}
${common_env_data.lib_deps_display}
${common.lib_deps_all}
${common.lib_deps_display}
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]
platform = ${common_env_data.platform_espressif32}
[env:ttgov21old]
platform = ${common.platform_espressif32}
framework = arduino
board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions}
board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600
monitor_speed = 115200
lib_deps =
${common_env_data.lib_deps_all}
${common_env_data.lib_deps_display}
${common.lib_deps_all}
${common.lib_deps_display}
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]
platform = ${common_env_data.platform_espressif32}
platform = ${common.platform_espressif32}
framework = arduino
board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions}
board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600
monitor_speed = 115200
lib_deps =
${common_env_data.lib_deps_all}
${common_env_data.lib_deps_gps}
${common.lib_deps_all}
${common.lib_deps_gps}
build_flags =
${common_env_data.build_flags}
${common.build_flags}
-mfix-esp32-psram-cache-issue
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
[env:fipy]
platform = ${common_env_data.platform_espressif32}
platform = ${common.platform_espressif32}
framework = arduino
board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions}
board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600
monitor_speed = 115200
lib_deps =
${common_env_data.lib_deps_all}
${common_env_data.lib_deps_rgbled}
${common.lib_deps_all}
${common.lib_deps_rgbled}
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]
platform = ${common_env_data.platform_espressif32}
platform = ${common.platform_espressif32}
framework = arduino
board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions}
board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600
monitor_speed = 115200
lib_deps =
${common_env_data.lib_deps_all}
${common_env_data.lib_deps_rgbled}
${common_env_data.lib_deps_gps}
${common.lib_deps_all}
${common.lib_deps_rgbled}
${common.lib_deps_gps}
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]
platform = ${common_env_data.platform_espressif32}
platform = ${common.platform_espressif32}
framework = arduino
board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions}
board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600
monitor_speed = 115200
lib_deps =
${common_env_data.lib_deps_all}
${common_env_data.lib_deps_rgbled}
${common_env_data.lib_deps_gps}
${common.lib_deps_all}
${common.lib_deps_rgbled}
${common.lib_deps_gps}
build_flags =
${common_env_data.build_flags}
${common.build_flags}
-mfix-esp32-psram-cache-issue
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
[env:lolin32litelora]
platform = ${common_env_data.platform_espressif32}
platform = ${common.platform_espressif32}
framework = arduino
board = lolin32
board_build.partitions = ${common_env_data.board_build.partitions}
board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600
monitor_speed = 115200
lib_deps =
${common_env_data.lib_deps_all}
${common_env_data.lib_deps_rgbled}
${common.lib_deps_all}
${common.lib_deps_rgbled}
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]
platform = ${common_env_data.platform_espressif32}
platform = ${common.platform_espressif32}
framework = arduino
board = lolin32
board_build.partitions = ${common_env_data.board_build.partitions}
board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600
monitor_speed = 115200
lib_deps =
${common_env_data.lib_deps_all}
${common_env_data.lib_deps_rgbled}
${common.lib_deps_all}
${common.lib_deps_rgbled}
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]
platform = ${common_env_data.platform_espressif32}
platform = ${common.platform_espressif32}
framework = arduino
board = lolin32
board_build.partitions = ${common_env_data.board_build.partitions}
board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600
monitor_speed = 115200
lib_deps =
${common_env_data.lib_deps_all}
${common_env_data.lib_deps_rgbled}
${common.lib_deps_all}
${common.lib_deps_rgbled}
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:generic]
platform = ${common_env_data.platform_espressif32}
platform = ${common.platform_espressif32}
framework = arduino
board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions}
board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600
monitor_speed = 115200
lib_deps =
${common_env_data.lib_deps_all}
${common_env_data.lib_deps_rgbled}
${common_env_data.lib_deps_gps}
${common_env_data.lib_deps_display}
${common.lib_deps_all}
${common.lib_deps_rgbled}
${common.lib_deps_gps}
${common.lib_deps_display}
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) {
// 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) {
// 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) {
@ -56,6 +55,14 @@ var bytesToInt = function (bytes) {
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) {
if (bytes.length !== uint8.BYTES) {
throw new Error('uint8 must have exactly 1 byte');
@ -186,6 +193,7 @@ if (typeof module === 'object' && typeof module.exports !== 'undefined') {
latLng: latLng,
hdop: hdop,
bitmap: bitmap,
version: version,
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) |
(bytes[i++] << 24) | (bytes[i++] << 16) | (bytes[i++] << 8) | 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) {

View File

@ -31,6 +31,7 @@ void defaultConfig() {
cfg.rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%)
cfg.gpsmode = 1; // 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);
}
@ -143,6 +144,10 @@ void saveConfig() {
flash8 != 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 ||
flash16 != cfg.rssilimit)
nvs_set_i16(my_handle, "rssilimit", cfg.rssilimit);
@ -326,6 +331,14 @@ void loadConfig() {
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);
ESP_LOGI(TAG, "Done");
}

View File

@ -4,6 +4,7 @@
// Basic config
#include "globals.h"
#include "senddata.h"
#include "OTA.h"
// Local logging tag
static const char TAG[] = "main";
@ -14,6 +15,10 @@ void doHomework() {
// update uptime counter
uptime();
// check if update mode trigger switch was set
if (cfg.runmode == 1)
ESP.restart();
// read battery voltage into global variable
#ifdef HAS_BATTERY_PROBE
batt_voltage = read_voltage();

View File

@ -36,14 +36,14 @@ void init_display(const char *Productname, const char *Version) {
u8x8.draw2x2String(0, 0, Productname);
u8x8.setInverseFont(0);
u8x8.draw2x2String(2, 2, Productname);
delay(1500);
vTaskDelay(1500 / portTICK_PERIOD_MS);
u8x8.clear();
u8x8.setFlipMode(1);
u8x8.setInverseFont(1);
u8x8.draw2x2String(0, 0, Productname);
u8x8.setInverseFont(0);
u8x8.draw2x2String(2, 2, Productname);
delay(1500);
vTaskDelay(1500 / portTICK_PERIOD_MS);
u8x8.setFlipMode(0);
u8x8.clear();
@ -74,7 +74,7 @@ void init_display(const char *Productname, const char *Version) {
DisplayKey(buf, 8, true);
#endif // HAS_LORA
delay(5000);
vTaskDelay(3000 / portTICK_PERIOD_MS);
u8x8.clear();
u8x8.setPowerSave(!cfg.screenon); // set display off if disabled
u8x8.draw2x2String(0, 0, "PAX:0");

View File

@ -4,10 +4,6 @@
// The mother of all embedded development...
#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
#include <set>
#include <array>
@ -31,6 +27,7 @@ typedef struct {
uint8_t rgblum; // RGB Led luminosity (0..100%)
uint8_t gpsmode; // 0=disabled, 1=enabled
uint8_t monitormode; // 0=disabled, 1=enabled
uint8_t runmode; // 0=normal, 1=update
char version[10]; // Firmware version
} configData_t;
@ -42,7 +39,7 @@ typedef struct {
} MessageBuffer_t;
// 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 uint8_t channel; // wifi channel rotation counter
extern uint16_t macs_total, macs_wifi, macs_ble, batt_voltage; // display values
@ -51,7 +48,8 @@ extern hw_timer_t *channelSwitch, *sendCycle;
extern portMUX_TYPE timerMux;
extern volatile int SendCycleTimerIRQ, HomeCycleIRQ, DisplayTimerIRQ,
ChannelTimerIRQ, ButtonPressedIRQ;
extern QueueHandle_t LoraSendQueue, SPISendQueue;
// extern QueueHandle_t LoraSendQueue, SPISendQueue;
extern TaskHandle_t WifiLoopTask;
extern std::array<uint64_t, 0xff>::iterator it;
extern std::array<uint64_t, 0xff> beacons;
@ -67,9 +65,15 @@ extern std::array<uint64_t, 0xff> beacons;
#include "payload.h"
#ifdef HAS_LORA
extern QueueHandle_t LoraSendQueue;
extern TaskHandle_t LoraTask;
#include "lorawan.h"
#endif
#ifdef HAS_SPI
extern QueueHandle_t SPISendQueue;
#endif
#ifdef HAS_DISPLAY
#include "display.h"
#endif

View File

@ -42,7 +42,7 @@ void gps_loop(void *pvParameters) {
while (GPS_Serial.available()) {
gps.encode(GPS_Serial.read());
}
vTaskDelay(1 / portTICK_PERIOD_MS); // reset watchdog
vTaskDelay(2 / portTICK_PERIOD_MS); // reset watchdog
}
// after GPS function was disabled, close connect to GPS device
GPS_Serial.end();
@ -58,7 +58,7 @@ void gps_loop(void *pvParameters) {
Wire.requestFrom(GPS_ADDR | 0x01, 32);
while (Wire.available()) {
gps.encode(Wire.read());
vTaskDelay(1 / portTICK_PERIOD_MS); // polling mode: 500ms sleep
vTaskDelay(2 / portTICK_PERIOD_MS); // polling mode: 500ms sleep
}
}
// after GPS function was disabled, close connect to GPS device
@ -67,7 +67,7 @@ void gps_loop(void *pvParameters) {
#endif // GPS Type
}
vTaskDelay(1 / portTICK_PERIOD_MS); // reset watchdog
vTaskDelay(2 / portTICK_PERIOD_MS); // reset watchdog
} // end of infinite loop

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 OLED_SDA GPIO_NUM_21 // ESP32 GPIO21 -- SD1306 D1+D2
#define OLED_SCL GPIO_NUM_22 // ESP32 GPIO22 -- SD1306 D0

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

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

View File

@ -196,11 +196,9 @@ void onEvent(ev_t ev) {
if (LMIC.dataLen) {
ESP_LOGI(TAG, "Received %d bytes of payload, RSSI %d SNR %d",
LMIC.dataLen, LMIC.rssi, (signed char)LMIC.snr / 4);
// LMIC.snr = SNR twos compliment [dB] * 4
// LMIC.rssi = RSSI [dBm] (-196...+63)
LMIC.dataLen, LMIC.rssi, (signed char)LMIC.snr);
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
if ((LMIC.txrxFlags & TXRX_PORT) &&
@ -229,7 +227,7 @@ void lorawan_loop(void *pvParameters) {
while (1) {
os_runloop_once(); // execute LMIC jobs
vTaskDelay(1 / portTICK_PERIOD_MS); // reset watchdog
vTaskDelay(2 / portTICK_PERIOD_MS); // reset watchdog
}
}

View File

@ -24,7 +24,6 @@ licenses. Refer to LICENSE.txt file in repository for more details.
*/
// Basic Config
#include "globals.h"
#include "main.h"
configData_t cfg; // struct holds current device configuration
@ -41,9 +40,12 @@ hw_timer_t *channelSwitch = NULL, *displaytimer = NULL, *sendCycle = NULL,
volatile int ButtonPressedIRQ = 0, ChannelTimerIRQ = 0, SendCycleTimerIRQ = 0,
DisplayTimerIRQ = 0, HomeCycleIRQ = 0;
TaskHandle_t WifiLoopTask = NULL;
// RTos send queues for payload transmit
#ifdef HAS_LORA
QueueHandle_t LoraSendQueue;
TaskHandle_t LoraTask = NULL;
#endif
#ifdef HAS_SPI
@ -67,6 +69,9 @@ static const char TAG[] = "main";
void setup() {
// disable the default wifi logging
esp_log_level_set("wifi", ESP_LOG_NONE);
char features[100] = "";
// disable brownout detection
@ -85,11 +90,12 @@ void setup() {
esp_log_set_vprintf(redirect_log);
#endif
ESP_LOGI(TAG, "Starting %s v%s", PROGNAME, PROGVERSION);
ESP_LOGI(TAG, "Starting %s v%s", PRODUCTNAME, PROGVERSION);
// initialize system event handler for wifi task, needed for
// wifi_sniffer_init()
esp_event_loop_init(NULL, NULL);
// esp_event_loop_init(NULL, NULL);
// ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
// print chip information on startup if in verbose mode
#ifdef VERBOSE
@ -115,6 +121,13 @@ void setup() {
// read settings from NVRAM
loadConfig(); // includes initialize if necessary
// reboot to firmware update mode if ota trigger switch is set
if (cfg.runmode == 1) {
cfg.runmode = 0;
saveConfig();
start_ota_update();
}
#ifdef VENDORFILTER
strcat_P(features, " OUIFLT");
#endif
@ -200,7 +213,7 @@ void setup() {
#ifdef HAS_DISPLAY
strcat_P(features, " OLED");
DisplayState = cfg.screenon;
init_display(PROGNAME, PROGVERSION);
init_display(PRODUCTNAME, PROGVERSION);
// setup display refresh trigger IRQ using esp32 hardware timer
// https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/
@ -269,7 +282,7 @@ void setup() {
ESP_LOGI(TAG, "Starting Lora task on core 1");
xTaskCreatePinnedToCore(lorawan_loop, "loraloop", 2048, (void *)1,
(5 | portPRIVILEGE_BIT), NULL, 1);
(5 | portPRIVILEGE_BIT), &LoraTask, 1);
#endif
// if device has GPS and it is enabled, start GPS reader task on core 0 with
@ -294,11 +307,11 @@ void setup() {
ESP_LOGI(TAG, "Starting Wifi task on core 0");
wifi_sniffer_init();
// initialize salt value using esp_random() called by random() in
// arduino-esp32 core. Note: do this *after* wifi has started, since function
// gets it's seed from RF noise
// arduino-esp32 core. Note: do this *after* wifi has started, since
// function gets it's seed from RF noise
reset_salt(); // get new 16bit for salting hashes
xTaskCreatePinnedToCore(wifi_channel_loop, "wifiloop", 2048, (void *)1, 1,
NULL, 0);
&WifiLoopTask, 0);
} // setup()
/* end Arduino SETUP
@ -310,7 +323,8 @@ void setup() {
void loop() {
while (1) {
// state machine for switching display, LED, button, housekeeping, senddata
// state machine for switching display, LED, button, housekeeping,
// senddata
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
led_loop();
@ -330,8 +344,8 @@ void loop() {
processSendBuffer();
// check send cycle and enqueue payload if cycle is expired
sendPayload();
// reset watchdog
vTaskDelay(1 / portTICK_PERIOD_MS);
// reset watchdog
vTaskDelay(2 / portTICK_PERIOD_MS);
} // loop()
}

View File

@ -1,13 +1,15 @@
#ifndef _MAIN_H
#define _MAIN_H
//#include "led.h"
#include "globals.h"
#include "led.h"
#include "macsniff.h"
#include "wifiscan.h"
#include "configmanager.h"
#include "senddata.h"
#include "cyclic.h"
#include "beacon_array.h"
#include "OTA.h"
#include <esp_spi_flash.h> // needed for reading ESP32 chip attributes
#include <esp_event_loop.h> // needed for Wifi event handler

275
src/ota.cpp Normal file
View File

@ -0,0 +1,275 @@
/*
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"
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
volatile int contentLength = 0;
volatile bool isValidContentType = false;
// Local logging tag
static const char TAG[] = "main";
void start_ota_update() {
ESP_LOGI(TAG, "Starting Wifi OTA update");
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);
checkFirmwareUpdates(); // gets and flashes new firmware and restarts
} else
ESP_LOGI(TAG, "could not connect to %s, rebooting.", WIFI_SSID);
ESP.restart(); // reached only if update was not successful or no wifi connect
} // start_ota_update
void checkFirmwareUpdates() {
// Fetch the latest firmware version
ESP_LOGI(TAG, "OTA mode, checking latest firmware version on server...");
const String latest = bintray.getLatestVersion();
if (latest.length() == 0) {
ESP_LOGI(
TAG,
"Could not load info about the latest firmware. Rebooting to runmode.");
return;
} else if (version_compare(latest, cfg.version) <= 0) {
ESP_LOGI(TAG, "Current firmware is up to date. Rebooting to runmode.");
return;
}
ESP_LOGI(TAG, "New firmware version v%s available. Downloading...",
latest.c_str());
processOTAUpdate(latest);
}
// A helper function to extract header value from header
inline String getHeaderValue(String header, String headerName) {
return header.substring(strlen(headerName.c_str()));
}
/**
* OTA update processing
*/
void processOTAUpdate(const String &version) {
String firmwarePath = bintray.getBinaryPath(version);
if (!firmwarePath.endsWith(".bin")) {
ESP_LOGI(TAG, "Unsupported binary format, OTA update cancelled.");
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());
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());
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.");
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;
}
}
}
}
// check whether we have everything for OTA update
if (contentLength && isValidContentType) {
size_t written;
if (Update.begin(contentLength)) {
int i = FLASH_MAX_TRY;
while ((i--) && (written != contentLength)) {
ESP_LOGI(TAG,
"Starting OTA update, attempt %d of %d. This will take some "
"time to complete...",
FLASH_MAX_TRY - i, FLASH_MAX_TRY);
written = Update.writeStream(client);
if (written == contentLength) {
ESP_LOGI(TAG, "Written %d bytes successfully", written);
break;
} else {
ESP_LOGI(TAG,
"Written only %d of %d 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());
}
} else {
ESP_LOGI(TAG, "There isn't enough space to start OTA update");
client.flush();
}
} else {
ESP_LOGI(TAG,
"There was no valid content in the response from the OTA server!");
client.flush();
}
ESP_LOGI(TAG,
"OTA update failed. Rebooting to runmode with current version.");
client.stop();
}
// 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 untill 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;
}

15
src/ota.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef OTA_H
#define OTA_H
#include "globals.h"
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <Update.h>
#include <BintrayClient.h>
void checkFirmwareUpdates();
void processOTAUpdate(const String &version);
void start_ota_update();
int version_compare(const String v1, const String v2);
#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!
#define PRODUCTNAME "PAXCNT"
// Verbose enables serial output
#define VERBOSE 1 // comment out to silence the device, for mute use build option
// Payload send cycle and encoding
#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
#define VENDORFILTER 1 // comment out if you want to count things, not people
@ -63,6 +65,10 @@
#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]
// OTA settings
#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]
// LMIC settings
// 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

View File

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

View File

@ -35,7 +35,8 @@ public:
uint8_t *getBuffer(void);
void addCount(uint16_t value1, uint16_t value2);
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);
#ifdef HAS_GPS
void addGPS(gpsStatus_t value);
@ -44,7 +45,6 @@ public:
void addButton(uint8_t value);
#endif
#if PAYLOAD_ENCODER == 1 // format plain
private:
@ -64,6 +64,7 @@ private:
void writeUint8(uint8_t i);
void writeHumidity(float humidity);
void writeTemperature(float temperature);
void writeVersion(char * version);
void writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, bool g,
bool h);
@ -77,7 +78,6 @@ private:
#else
#error "No valid payload converter defined"
#endif
};
extern PayloadConvert payload;

View File

@ -31,6 +31,11 @@ void set_reset(uint8_t val[]) {
sprintf(display_line6, "Queue reset");
flushQueues();
break;
case 9: // reset and ask for software update via Wifi OTA
ESP_LOGI(TAG, "Remote command: software update via Wifi");
sprintf(display_line6, "Software update");
cfg.runmode = 1;
break;
default:
ESP_LOGW(TAG, "Remote command: reset called with invalid parameter(s)");
}
@ -203,7 +208,8 @@ void get_status(uint8_t val[]) {
#endif
payload.reset();
payload.addStatus(voltage, uptime() / 1000, temperatureRead(),
ESP.getFreeHeap());
ESP.getFreeHeap(), rtc_get_reset_reason(0),
rtc_get_reset_reason(1));
SendData(STATUSPORT);
};
@ -221,14 +227,14 @@ void get_gps(uint8_t val[]) {
// assign previously defined functions to set of numeric remote commands
// 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[] = {
{0x01, set_rssi, 1, true}, {0x02, set_countmode, 1, true},
{0x03, set_gps, 1, true}, {0x04, set_display, 1, true},
{0x05, set_lorasf, 1, true}, {0x06, set_lorapower, 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},
{0x0d, set_vendorfilter, 1, false}, {0x0e, set_blescan, 1, true},
{0x0f, set_wifiant, 1, true}, {0x10, set_rgblum, 1, true},

View File

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

View File

@ -6,7 +6,7 @@
static const char TAG[] = "wifi";
static wifi_country_t wifi_country = {WIFI_MY_COUNTRY, WIFI_CHANNEL_MIN,
WIFI_CHANNEL_MAX, 0,
WIFI_CHANNEL_MAX, 100,
WIFI_COUNTRY_POLICY_MANUAL};
// using IRAM_:ATTR here to speed up callback function
@ -36,6 +36,7 @@ void wifi_sniffer_init(void) {
ESP_ERROR_CHECK(
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_stop());
ESP_ERROR_CHECK(
esp_wifi_set_promiscuous_filter(&filter)); // set MAC frame filter
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler));
@ -58,7 +59,7 @@ void wifi_channel_loop(void *pvParameters) {
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
ESP_LOGD(TAG, "Wifi set channel %d", channel);
vTaskDelay(1 / portTICK_PERIOD_MS); // reset watchdog
vTaskDelay(2 / portTICK_PERIOD_MS); // reset watchdog
}
} // end of infinite wifi channel rotation loop