Merge branch 'ota-test' into development

This commit is contained in:
Verkehrsrot 2018-09-15 14:45:23 +02:00 committed by GitHub
commit 8999c77579
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 768 additions and 41 deletions

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,102 @@
/*
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" \
"MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT\n" \
"MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i\n" \
"YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG\n" \
"EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg\n" \
"R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9\n" \
"9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq\n" \
"fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv\n" \
"iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU\n" \
"1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+\n" \
"bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW\n" \
"MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA\n" \
"ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l\n" \
"uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn\n" \
"Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS\n" \
"tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF\n" \
"PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un\n" \
"hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV\n" \
"5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==\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,149 @@
/*
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
{
Serial.printf("GET request failed, error: %s\n", 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_LOGI(TAG, "Error: Could not parse JSON. Input data is too big!");
return version;
}
StaticJsonBuffer<bufferSize> jsonBuffer;
JsonObject &root = jsonBuffer.parseObject(jsonResult.c_str());
// Check for errors in parsing
if (!root.success())
{
ESP_LOGI(TAG, "Error: Could not parse JSON!");
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_LOGI(TAG, "Error: Could parse JSON. Input data is too big!");
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_LOGI(TAG, "Error: Could not parse JSON!");
return path;
}
return "/" + getUser() + "/" + getRepository() + "/" + firstItem.get<String>("path");
}

View File

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

@ -11,12 +11,12 @@
; ---> SELECT TARGET PLATFORM HERE! <---
[platformio]
env_default = generic
;env_default = generic
;env_default = ebox
;env_default = heltec
;env_default = ttgov1
;env_default = ttgov2
;env_default = ttgov21
env_default = ttgov21
;env_default = ttgobeam
;env_default = lopy
;env_default = lopy4
@ -27,11 +27,55 @@ env_default = generic
;
description = Paxcounter is a proof-of-concept ESP32 device for metering passenger flows in realtime. It counts how many mobile devices are around.
[bintray]
user = cyberman54
repository = paxcounter
package = esp32-paxcounter
api_token = ***
[wifi]
ssid = ***
password = ***
[common]
platform = https://github.com/platformio/platform-espressif32.git
; firmware version, please modify it between releases
; positive integer value
release_version = 4
; build configuration based on Bintray and Wi-Fi settings
build_flags =
'-DWIFI_SSID="${wifi.ssid}"'
'-DWIFI_PASS="${wifi.password}"'
'-DBINTRAY_USER="${bintray.user}"'
'-DBINTRAY_REPO="${bintray.repository}"'
'-DBINTRAY_PACKAGE="${bintray.package}"'
-DVERSION=${common.release_version}
;
; ---> 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
[common_env_data]
platform_espressif32 = espressif32@1.3.0
;platform_espressif32 = https://github.com/platformio/platform-espressif32.git#feature/stage
board_build.partitions = no_ota.csv
;board_build.partitions = no_ota.csv
board_build.partitions = min_spiffs.csv
lib_deps_all =
ArduinoJson
lib_deps_display =
U8g2@>=2.23.16
lib_deps_rgbled =
@ -40,22 +84,6 @@ 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
;
; override lora settings from LMiC library in lmic/config.h and use main.h instead
-D_lmic_config_h_
-include "src/paxcounter.conf"
@ -124,6 +152,7 @@ lib_deps =
${common_env_data.lib_deps_all}
${common_env_data.lib_deps_display}
build_flags =
${common.build_flags}
${common_env_data.build_flags}
[env:ttgobeam]

79
src/OTA.cpp Normal file
View File

@ -0,0 +1,79 @@
#include "OTA.h"
const BintrayClient bintray(BINTRAY_USER, BINTRAY_REPO, BINTRAY_PACKAGE);
bool Wifi_Connected = false;
esp_err_t event_handler(void *ctx, system_event_t *event) {
switch (event->event_id) {
case SYSTEM_EVENT_STA_START:
esp_wifi_connect();
ESP_LOGI(TAG, "Event STA_START");
break;
case SYSTEM_EVENT_STA_GOT_IP:
Wifi_Connected = true;
ESP_LOGI(TAG, "Event STA_GOT_IP");
// print the local IP address
tcpip_adapter_ip_info_t ip_info;
ESP_ERROR_CHECK(tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info));
ESP_LOGI(TAG, "IP %s", ip4addr_ntoa(&ip_info.ip));
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
Wifi_Connected = false;
ESP_LOGI(TAG, "Event STA_DISCONNECTED");
break;
default:
break;
}
}
void ota_wifi_init(void) {
tcpip_adapter_if_t tcpip_if = TCPIP_ADAPTER_IF_STA;
// initialize the tcp stack
// nvs_flash_init();
tcpip_adapter_init();
tcpip_adapter_set_hostname(tcpip_if, PROGNAME);
tcpip_adapter_dhcpc_start(tcpip_if);
// initialize the wifi event handler
ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
// switch off monitor more
ESP_ERROR_CHECK(
esp_wifi_set_promiscuous(false)); // now switch on monitor mode
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(NULL));
wifi_sta_config_t cfg;
strcpy((char *)cfg.ssid, WIFI_SSID);
strcpy((char *)cfg.password, WIFI_PASS);
cfg.bssid_set = false;
wifi_config_t sta_cfg;
sta_cfg.sta = cfg;
wifi_init_config_t wifi_cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&wifi_cfg));
ESP_ERROR_CHECK(
esp_wifi_set_storage(WIFI_STORAGE_RAM)); // we don't need NVRAM
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &sta_cfg));
ESP_ERROR_CHECK(esp_wifi_start());
}
void start_ota_update() {
ESP_LOGI(TAG, "Stopping Wifi task on core 0");
vTaskDelete(WifiLoopTask);
ESP_LOGI(TAG, "Stopping LORA task on core 1");
vTaskDelete(LoraTask);
ESP_LOGI(TAG, "Connecting to %s", WIFI_SSID);
ota_wifi_init();
delay(2000);
delay(2000);
checkFirmwareUpdates();
ESP.restart(); // reached if update was not successful
}

13
src/OTA.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef OTA_H
#define OTA_H
#include <Arduino.h>
#include "globals.h"
#include <BintrayClient.h>
#include <WiFi.h>
#include "ota.h"
#include "SecureOTA.h"
void start_ota_update();
#endif // OTA_H

225
src/SecureOTA.cpp Normal file
View File

@ -0,0 +1,225 @@
/*
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 <WiFiClientSecure.h>
#include <Update.h>
#include <BintrayClient.h>
#include "SecureOTA.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;
void checkFirmwareUpdates()
{
// Fetch the latest firmware version
const String latest = bintray.getLatestVersion();
if (latest.length() == 0)
{
ESP_LOGI(TAG, "Could not load info about the latest firmware, so nothing to update. Continue ...");
return;
}
else if (atoi(latest.c_str()) <= VERSION)
{
//ESP_LOGI(TAG, "The current firmware is up to date. Continue ...");
return;
}
ESP_LOGI(TAG, "There is a new version of firmware available: v.%s", latest);
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 cannot be performed!");
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);
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! Cannot connect to %s for some reason!", currentHost);
return;
}
}
//ESP_LOGI(TAG, "Requesting: " + firmwarePath);
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);
newUrl.remove(0, newUrl.indexOf("//") + 2);
currentHost = newUrl.substring(0, newUrl.indexOf('/'));
newUrl.remove(newUrl.indexOf(currentHost), currentHost.length());
firmwarePath = newUrl;
ESP_LOGI(TAG, "firmwarePath: %s", firmwarePath);
continue;
}
// Checking headers
if (line.startsWith("Content-Length: "))
{
contentLength = atoi((getHeaderValue(line, "Content-Length: ")).c_str());
ESP_LOGI(TAG, "Got %s bytes from server", String(contentLength));
}
if (line.startsWith("Content-Type: "))
{
String contentType = getHeaderValue(line, "Content-Type: ");
ESP_LOGI(TAG, "Got %s payload", contentType);
if (contentType == "application/octet-stream")
{
isValidContentType = true;
}
}
}
}
// check whether we have everything for OTA update
if (contentLength && isValidContentType)
{
if (Update.begin(contentLength))
{
ESP_LOGI(TAG, "Starting Over-The-Air update. This may take some time to complete ...");
size_t written = Update.writeStream(client);
if (written == contentLength)
{
ESP_LOGI(TAG, "Written %s successfully", String(written));
}
else
{
ESP_LOGI(TAG, "Written only %s / %s Retry?", String(written), String(contentLength));
// Retry??
}
if (Update.end())
{
if (Update.isFinished())
{
ESP_LOGI(TAG, "OTA update has successfully completed. Rebooting ...");
ESP.restart();
}
else
{
ESP_LOGI(TAG, "Something went wrong! OTA update hasn't been finished properly.");
}
}
else
{
ESP_LOGI(TAG, "An error occurred. Error #: %s", String(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();
}
}

25
src/SecureOTA.h Normal file
View File

@ -0,0 +1,25 @@
/*
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 SECURE_OTA_H
#define SECURE_OTA_H
#include <Arduino.h>
void checkFirmwareUpdates();
void processOTAUpdate(const String &version);
#endif // SECURE_OTA_H

View File

@ -21,7 +21,6 @@ function Decoder(bytes, port) {
return decode(bytes, [uint16, uptime, uint8, uint32], ['voltage', 'uptime', 'cputemp', 'memory']);
}
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']);
@ -181,6 +180,7 @@ if (typeof module === 'object' && typeof module.exports !== 'undefined') {
uint16: uint16,
uint32: uint32,
uptime: uptime,
reset: reset,
temperature: temperature,
humidity: humidity,
latLng: latLng,

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,9 @@ void doHomework() {
// update uptime counter
uptime();
if (ota_update)
start_ota_update();
// read battery voltage into global variable
#ifdef HAS_BATTERY_PROBE
batt_voltage = read_voltage();

View File

@ -42,7 +42,8 @@ typedef struct {
} MessageBuffer_t;
// global variables
extern configData_t cfg; // current device configuration
extern configData_t cfg; // current device configuration
extern bool ota_update;
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 +52,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 +69,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

@ -27,7 +27,8 @@ licenses. Refer to LICENSE.txt file in repository for more details.
#include "globals.h"
#include "main.h"
configData_t cfg; // struct holds current device configuration
configData_t cfg; // struct holds current device configuration
bool ota_update = false; // triggers OTA update
char display_line6[16], display_line7[16]; // display buffers
uint8_t channel = 0; // channel rotation counter
uint16_t macs_total = 0, macs_wifi = 0, macs_ble = 0,
@ -41,9 +42,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 +71,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
@ -89,7 +96,8 @@ void setup() {
// 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
@ -269,7 +277,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
@ -298,7 +306,7 @@ void setup() {
// 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

View File

@ -9,7 +9,7 @@
// 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

View File

@ -54,6 +54,7 @@ void PayloadConvert::addConfig(configData_t value) {
void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime,
float cputemp, uint32_t mem) {
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
@ -123,12 +126,14 @@ void PayloadConvert::addConfig(configData_t value) {
value.vendorfilter ? true : false, value.gpsmode ? true : false);
}
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
@ -241,8 +246,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:
@ -77,7 +77,6 @@ private:
#else
#error "No valid payload converter defined"
#endif
};
extern PayloadConvert payload;

View File

@ -203,7 +203,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);
};
@ -219,6 +220,11 @@ void get_gps(uint8_t val[]) {
#endif
};
void set_update(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: get firmware update");
ota_update = true;
};
// assign previously defined functions to set of numeric remote commands
// format: opcode, function, #bytes params,
// flag (1 = do make settings persistent / 0 = don't)
@ -233,8 +239,8 @@ cmd_t table[] = {
{0x0d, set_vendorfilter, 1, false}, {0x0e, set_blescan, 1, true},
{0x0f, set_wifiant, 1, true}, {0x10, set_rgblum, 1, true},
{0x11, set_monitor, 1, true}, {0x12, set_beacon, 7, false},
{0x80, get_config, 0, false}, {0x81, get_status, 0, false},
{0x84, get_gps, 0, false}};
{0x20, set_update, 0, false}, {0x80, get_config, 0, false},
{0x81, get_status, 0, false}, {0x84, get_gps, 0, false}};
const uint8_t cmdtablesize =
sizeof(table) / sizeof(table[0]); // number of commands in command table

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));