Skip to content

Commit

Permalink
Merge pull request #753 from cyberman54/development
Browse files Browse the repository at this point in the history
v2.4.0
  • Loading branch information
cyberman54 authored Mar 6, 2021
2 parents 011c539 + e54c386 commit 74bc84b
Show file tree
Hide file tree
Showing 19 changed files with 277 additions and 30 deletions.
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Compile time configuration is spread across several files. Before compiling the
## platformio.ini
Edit `platformio_orig.ini` and select desired hardware target in section boards. To add a new board, create an appropriate hardware abstraction layer file in hal subdirectory, and add a pointer to this file in sections board. Copy or rename to `platformio.ini` in the root directory of the project. Now start Platformio. Note: Platformio is looking for `platformio.ini` in the root directory and won't start if it does not find this file.

## src/paxcounter.conf
## paxcounter.conf
Edit `src/paxcounter_orig.conf` and tailor settings in this file according to your needs and use case. Please take care of the duty cycle regulations of the LoRaWAN network you're going to use. Copy or rename to `src/paxcounter.conf`.

If your device has a **real time clock** it can be updated bei either LoRaWAN network or GPS time, according to settings *TIME_SYNC_INTERVAL* and *TIME_SYNC_LORAWAN* in `paxcounter.conf`.
Expand All @@ -110,7 +110,7 @@ To configure OTAA, leave `#define LORA_ABP` deactivated (commented). To use ABP,
The file `src/loraconf_sample.h` contains more information about the values to provide.

## src/ota.conf
Create file `src/ota.conf` using the template [src/ota.sample.conf](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/ota.sample.conf) and enter your WIFI network&key. These settings are used for downloading updates. 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.
Create file `src/ota.conf` using the template [src/ota.sample.conf](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/ota.sample.conf) and enter your WIFI network&key. These settings are used for downloading updates via WiFi, either from a remote https server, or locally via WebUI. If you want to use a remote server, you need a <A HREF="https://bintray.com/JFrog">Bintray account</A>. Enter your Bintray user account data in ota.conf. If you don't need wireless firmware updates just rename ota.sample.conf to ota.conf.

# Building

Expand All @@ -124,8 +124,11 @@ 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.
- **During runtime, automated using OTA via WIFI:**
After the ESP32 board is initially flashed and has joined a LoRaWAN network, the firmware can update itself by OTA. This process is kicked off by sending a remote control command (see below) via LoRaWAN to the board. The board then tries to connect via WiFi to a cloud service (JFrog Bintray), checks for update, and if available downloads the binary and reboots with it. If something goes wrong during this process, the board reboots back to the current version. Prerequisites for OTA are: 1. You own a Bintray repository, 2. you pushed the update binary to the Bintray repository, 3. internet access via encrypted (WPA2) WiFi is present at the board's site, 4. WiFi credentials were set in ota.conf and initially flashed to the board. Step 2 runs automated, just enter the credentials in ota.conf and set `upload_protocol = custom` in platformio.ini. Then press build and lean back watching platformio doing build and upload.

- **During runtime, manually using OTA via WIFI:**
If option *BOOTMENU* is defined in `paxcounter.conf`, the ESP32 board will try to connect to a known WiFi access point each time cold starting (after a power cycle or a reset), using the WiFi credentials given in `ota.conf`. Once connected to the WiFi it will fire up a simple webserver, providing a bootstrap menu waiting for a user interaction (pressing "START" button in menu). This process will be aborted by ESP32 hardware watchdog after *BOOTDELAY* seconds, ensuring booting the device to runmode. Once a user interaction in bootstrap menu was detected, the watchdog time will be extended to *BOOTTIMEOUT* seconds. During this time a firmware upload can be performed manually by user, e.g. using a smartphone in tethering mode providing the firmware upload file.

# Legal note

Expand Down Expand Up @@ -437,6 +440,7 @@ Send for example `8386` as Downlink on Port 2 to get battery status and time/dat
2 = reset device to factory settings
3 = flush send queues
4 = restart device (warmstart)
8 = reboot device to maintenance mode (local web server)
9 = reboot device to OTA update via Wifi mode

0x0A set LoRaWAN payload send cycle
Expand Down
16 changes: 16 additions & 0 deletions include/boot.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef BOOT_H
#define BOOT_H

#include "globals.h"
#include "hash.h"

#include <Update.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>

void start_boot_menu(void);

#endif // BOOT_H
4 changes: 2 additions & 2 deletions include/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ enum runmode_t {
RUNMODE_NORMAL,
RUNMODE_WAKEUP,
RUNMODE_UPDATE,
RUNMODE_SLEEP
RUNMODE_SLEEP,
RUNMODE_MAINTENANCE
};

// Struct holding devices's runtime configuration
Expand All @@ -83,7 +84,6 @@ typedef struct __attribute__((packed)) {
uint8_t macfilter; // 0=disabled, 1=enabled
uint8_t rgblum; // RGB Led luminosity (0..100%)
uint8_t monitormode; // 0=disabled, 1=enabled
uint8_t runmode; // 0=normal, 1=update
uint8_t payloadmask; // bitswitches for payload data
uint8_t enscount; // 0=disabled 1= enabled

Expand Down
2 changes: 1 addition & 1 deletion include/hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
#include <Arduino.h>
#include <RokkitHash.h>

uint32_t IRAM_ATTR hash(const char *data, int len);
uint32_t IRAM_ATTR myhash(const char *data, int len);

#endif
1 change: 1 addition & 0 deletions include/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@
#include "lorawan.h"
#include "timekeeper.h"
#include "corona.h"
#include "boot.h"

#endif
1 change: 1 addition & 0 deletions include/mqttclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "globals.h"
#include "rcommand.h"
#include "hash.h"
#include <MQTT.h>
#include <ETH.h>
#include <mbedtls/base64.h>
Expand Down
2 changes: 1 addition & 1 deletion include/ota.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ void show_progress(unsigned long current, unsigned long size);

#endif // USE_OTA

#endif // OTA_H
#endif // OTA_H
7 changes: 3 additions & 4 deletions platformio_orig.ini
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ description = Paxcounter is a device for metering passenger flows in realtime. I

[common]
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
release_version = 2.3.0
release_version = 2.4.0
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
debug_level = 3
extra_scripts = pre:build.py
otakeyfile = ota.conf
lorakeyfile = loraconf.h
lmicconfigfile = lmic_config.h
platform_espressif32 = espressif32@3.0.0
platform_espressif32 = espressif32@3.1.0
monitor_speed = 115200
upload_speed = 115200 ; set by build.py and taken from hal file
display_library = ; set by build.py and taken from hal file
Expand Down Expand Up @@ -86,8 +86,7 @@ lib_deps_basic =
jchristensen/Timezone @ ^1.2.4
makuna/RTC @ ^2.3.5
spacehuhn/SimpleButton
;lewisxhe/AXP202X_Library @ ^1.1.2
https://github.com/lewisxhe/AXP202X_Library.git
lewisxhe/AXP202X_Library @ ^1.1.3
geeksville/esp32-micro-sdcard @ ^0.1.1
256dpi/MQTT @ ^2.4.7
lib_deps_all =
Expand Down
199 changes: 199 additions & 0 deletions src/boot.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
#include "boot.h"
#include "reset.h"

// Local logging tag
static const char TAG[] = __FILE__;

// start local web server with user interface for maintenance mode
// used for manually uploading a firmware file via wifi

void start_boot_menu(void) {

uint8_t mac[6];
char clientId[20];

// hash 6 byte MAC to 4 byte hash
esp_eth_get_mac(mac);
const uint32_t hashedmac = myhash((const char *)mac, 6);
snprintf(clientId, 20, "paxcounter_%08x", hashedmac);

const char *host = clientId;
const char *ssid = WIFI_SSID;
const char *password = WIFI_PASS;

RTC_runmode = RUNMODE_NORMAL;

hw_timer_t *timer = NULL;
timer = timerBegin(2, 80, true); // timer 2, div 80, countup
timerAttachInterrupt(timer, &esp_restart, true); // callback device reset
timerAlarmWrite(timer, BOOTDELAY * 1000000, false); // set time in us
timerAlarmEnable(timer); // enable interrupt

WebServer server(80);

const char *loginMenu =
"<form name='loginForm'>"
"<table width='20%' bgcolor='A09F9F' align='center'>"
"<tr>"
"<td colspan=2>"
"<center><font size=4><b>Maintenance Menu</b></font></center>"
"<br>"
"</td>"
"<br>"
"<br>"
"</tr>"
"<tr>"
"<td><input type='submit' onclick='start(this.form)' "
"value='Start'></td>"
"</tr>"
"</table>"
"</form>"
"<script>"
"function start(form) {window.open('/serverIndex')}"
"</script>";

const char *serverIndex =
"<script "
"src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/"
"jquery.min.js'></script>"
"<form method='POST' action='#' enctype='multipart/form-data' "
"id='upload_form'>"
"<input type='file' name='update'>"
"<input type='submit' value='Update'>"
"</form>"
"<div id='prg'>progress: 0%</div>"
"<script>"
"$('form').submit(function(e){"
"e.preventDefault();"
"var form = $('#upload_form')[0];"
"var data = new FormData(form);"
" $.ajax({"
"url: '/update',"
"type: 'POST',"
"data: data,"
"contentType: false,"
"processData:false,"
"xhr: function() {"
"var xhr = new window.XMLHttpRequest();"
"xhr.upload.addEventListener('progress', function(evt) {"
"if (evt.lengthComputable) {"
"var per = evt.loaded / evt.total;"
"$('#prg').html('progress: ' + Math.round(per*100) + '%');"
"}"
"}, false);"
"return xhr;"
"},"
"success:function(d, s) {"
"console.log('success!')"
"},"
"error: function (a, b, c) {"
"}"
"});"
"});"
"</script>";

WiFi.disconnect(true);
WiFi.config(INADDR_NONE, INADDR_NONE,
INADDR_NONE); // call is only a workaround for bug in WiFi class
// see https://github.com/espressif/arduino-esp32/issues/806
WiFi.setHostname(host);
WiFi.mode(WIFI_STA);

// Connect to WiFi network
// workaround applied here to avoid WIFI_AUTH failure
// see https://github.com/espressif/arduino-esp32/issues/2501
// 1st try
WiFi.begin(ssid, password);
while (WiFi.status() == WL_DISCONNECTED) {
delay(500);
}
// 2nd try
if (WiFi.status() != WL_CONNECTED) {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
}

MDNS.begin(host);

server.on("/", HTTP_GET, [&server, &loginMenu]() {
server.sendHeader("Connection", "close");
server.send(200, "text/html", loginMenu);
});

server.on("/serverIndex", HTTP_GET, [&server, &serverIndex, &timer]() {
timerAlarmWrite(timer, BOOTTIMEOUT * 1000000, false);
server.sendHeader("Connection", "close");
server.send(200, "text/html", serverIndex);
});

server.onNotFound([&server, &loginMenu]() {
server.sendHeader("Connection", "close");
server.send(200, "text/html", loginMenu);
});

// handling uploading firmware file
server.on(
"/update", HTTP_POST,
[&server]() {
server.sendHeader("Connection", "close");
server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
WiFi.disconnect(true);
if (!Update.hasError())
RTC_runmode = RUNMODE_POWERCYCLE;
esp_restart();
},

[&server, &timer]() {
bool success = false;
HTTPUpload &upload = server.upload();

switch (upload.status) {

case UPLOAD_FILE_START:
// start file transfer
ESP_LOGI(TAG, "Uploading %s", upload.filename.c_str());
success = Update.begin();
break;

case UPLOAD_FILE_WRITE:
// flashing firmware to ESP
success = (Update.write(upload.buf, upload.currentSize) ==
upload.currentSize);
break;

case UPLOAD_FILE_END:
success = Update.end(true); // true to set the size to the current
if (success)
ESP_LOGI(TAG, "Upload finished, %u bytes written",
upload.totalSize);
else
ESP_LOGE(TAG, "Upload failed, status=%d", upload.status);
break;

case UPLOAD_FILE_ABORTED:
default:
break;

} // switch

if (!success) {
ESP_LOGE(TAG, "Error: %s", Update.errorString());
WiFi.disconnect(true);
esp_restart();
}
});

server.begin();
MDNS.addService("http", "tcp", 80);
ESP_LOGI(TAG,
"WiFi connected to '%s', open http://%s.local or http://%s in your "
"browser",
WiFi.SSID().c_str(), clientId, WiFi.localIP().toString().c_str());

while (1) {
server.handleClient();
delay(1);
}
}
10 changes: 4 additions & 6 deletions src/cyclic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,14 @@ Ticker cyclicTimer;
extern boolean isSDS011Active;
#endif

void setCyclicIRQ() {
xTaskNotify(irqHandlerTask, CYCLIC_IRQ, eSetBits);
}
void setCyclicIRQ() { xTaskNotify(irqHandlerTask, CYCLIC_IRQ, eSetBits); }

// do all housekeeping
void doHousekeeping() {

// check if update mode trigger switch was set by rcommand
if (RTC_runmode == RUNMODE_UPDATE)
do_reset(true);
// check if update or maintenance mode trigger switch was set by rcommand
if ((RTC_runmode == RUNMODE_UPDATE) || (RTC_runmode == RUNMODE_MAINTENANCE))
do_reset(true); // warmstart

// heap and task storage debugging
ESP_LOGD(TAG, "Heap: Free:%d, Min:%d, Size:%d, Alloc:%d, StackHWM:%d",
Expand Down
5 changes: 3 additions & 2 deletions src/display.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,10 @@ void dp_init(bool verbose) {

// give user some time to read or take picture
dp_dump(displaybuf);
#if !(BOOTMENU)
delay(8000);
dp_contrast(DISPLAYCONTRAST);
dp_clear();
#endif

#endif // HAS_LORA

} // verbose
Expand Down
2 changes: 1 addition & 1 deletion src/hash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@
#undef ROKKIT_ENABLE_8BIT_OPTIMIZATIONS
#endif

uint32_t IRAM_ATTR hash(const char *data, int len) {
uint32_t IRAM_ATTR myhash(const char *data, int len) {
return rokkit(data, len);
}
2 changes: 1 addition & 1 deletion src/macsniff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ uint16_t mac_analyze(MacBuffer_t MacBuffer) {
// hashed 4 byte MAC
// to save RAM, we use only lower 2 bytes of hash, since collisions don't
// matter in our use case
hashedmac = hash((const char *)&saltedmac, 4);
hashedmac = myhash((const char *)&saltedmac, 4);

auto newmac = macs.insert(hashedmac); // add hashed MAC, if new unique
bool added =
Expand Down
Loading

0 comments on commit 74bc84b

Please sign in to comment.