From 5099d25965e426f77051781e835d08fec3918c03 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 12 Aug 2018 00:17:57 +0200 Subject: [PATCH 1/4] testing --- lib/BintrayClient/library.json | 24 +++ lib/BintrayClient/src/BintrayCertificates.h | 102 +++++++++ lib/BintrayClient/src/BintrayClient.cpp | 149 +++++++++++++ lib/BintrayClient/src/BintrayClient.h | 49 +++++ platformio.ini | 46 +++- src/SecureOTA.cpp | 225 ++++++++++++++++++++ src/SecureOTA.h | 25 +++ src/globals.h | 1 + src/main.cpp | 4 +- src/rcommand.cpp | 29 ++- src/rcommand.h | 3 + 11 files changed, 648 insertions(+), 9 deletions(-) create mode 100644 lib/BintrayClient/library.json create mode 100644 lib/BintrayClient/src/BintrayCertificates.h create mode 100644 lib/BintrayClient/src/BintrayClient.cpp create mode 100644 lib/BintrayClient/src/BintrayClient.h create mode 100644 src/SecureOTA.cpp create mode 100644 src/SecureOTA.h diff --git a/lib/BintrayClient/library.json b/lib/BintrayClient/library.json new file mode 100644 index 00000000..677cec5c --- /dev/null +++ b/lib/BintrayClient/library.json @@ -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" +} diff --git a/lib/BintrayClient/src/BintrayCertificates.h b/lib/BintrayClient/src/BintrayCertificates.h new file mode 100644 index 00000000..ec5fdaa3 --- /dev/null +++ b/lib/BintrayClient/src/BintrayCertificates.h @@ -0,0 +1,102 @@ +/* + Copyright (c) 2014-present PlatformIO + + 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 diff --git a/lib/BintrayClient/src/BintrayClient.cpp b/lib/BintrayClient/src/BintrayClient.cpp new file mode 100644 index 00000000..b0e7543f --- /dev/null +++ b/lib/BintrayClient/src/BintrayClient.cpp @@ -0,0 +1,149 @@ +/* + Copyright (c) 2014-present PlatformIO + + 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 +#include +#include + +#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) + { + Serial.println("Error: Could parse JSON. Input data is too big!"); + return version; + } + StaticJsonBuffer jsonBuffer; + + JsonObject &root = jsonBuffer.parseObject(jsonResult.c_str()); + // Check for errors in parsing + if (!root.success()) + { + Serial.println("Error: Could not parse JSON!"); + return version; + } + return root.get("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) + { + Serial.println("Error: Could parse JSON. Input data is too big!"); + return path; + } + StaticJsonBuffer jsonBuffer; + + JsonArray &root = jsonBuffer.parseArray(jsonResult.c_str()); + JsonObject &firstItem = root[0]; + if (!root.success()) + { //Check for errors in parsing + Serial.println("Error: Could not parse JSON!"); + return path; + } + return "/" + getUser() + "/" + getRepository() + "/" + firstItem.get("path"); +} diff --git a/lib/BintrayClient/src/BintrayClient.h b/lib/BintrayClient/src/BintrayClient.h new file mode 100644 index 00000000..9761c7c6 --- /dev/null +++ b/lib/BintrayClient/src/BintrayClient.h @@ -0,0 +1,49 @@ +/* + Copyright (c) 2014-present PlatformIO + + 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 +#include +#include + +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> m_certificates; +}; + +#endif // BINTRAY_CLIENT_H diff --git a/platformio.ini b/platformio.ini index 98296872..91ee4e27 100644 --- a/platformio.ini +++ b/platformio.ini @@ -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,10 +27,45 @@ 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 = 9f02e2a2374c278fd79d5bcf4b4442fca9752012 +;api_token = ${env.BINTRAY_API_TOKEN} + +; Wi-Fi network settings +[wifi] +ssid = PRENZLNET-G +password = 435Huse8!? +;ssid = ${env.PIO_WIFI_SSID} +;password = ${env.PIO_WIFI_PASSWORD} + +[common] +platform = https://github.com/platformio/platform-espressif32.git + +; firmware version, please modify it between releases +; positive integer value +release_version = 1 + +; 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=0' + +; extra dependencies +lib_deps = ArduinoJson + + [common_env_data] platform_espressif32 = espressif32@1.2.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 = lib_deps_display = U8g2@>=2.23.12 @@ -48,9 +83,9 @@ build_flags = ; Error ; -DCORE_DEBUG_LEVEL=1 ; Warn - -DCORE_DEBUG_LEVEL=2 +; -DCORE_DEBUG_LEVEL=2 ; Info -; -DCORE_DEBUG_LEVEL=3 + -DCORE_DEBUG_LEVEL=3 ; Debug ; -DCORE_DEBUG_LEVEL=4 ; Verbose @@ -124,6 +159,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] diff --git a/src/SecureOTA.cpp b/src/SecureOTA.cpp new file mode 100644 index 00000000..7496c19c --- /dev/null +++ b/src/SecureOTA.cpp @@ -0,0 +1,225 @@ +/* + Copyright (c) 2014-present PlatformIO + + 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 +#include +#include +#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) + { + Serial.println("Could not load info about the latest firmware, so nothing to update. Continue ..."); + return; + } + else if (atoi(latest.c_str()) <= VERSION) + { + //Serial.println("The current firmware is up to date. Continue ..."); + return; + } + + Serial.println("There is a new version of firmware available: v." + 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")) + { + Serial.println("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)) + { + Serial.println("Cannot connect to " + currentHost); + return; + } + + bool redirect = true; + while (redirect) + { + if (currentHost != prevHost) + { + client.stop(); + client.setCACert(bintray.getCertificate(currentHost)); + if (!client.connect(currentHost.c_str(), port)) + { + Serial.println("Redirect detected! Cannot connect to " + currentHost + " for some reason!"); + return; + } + } + + //Serial.println("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) + { + Serial.println("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) + { + //Serial.println("Got 200 status code from server. Proceeding to firmware flashing"); + redirect = false; + } + else if (line.indexOf("302") > 0) + { + //Serial.println("Got 302 status code from server. Redirecting to the new address"); + redirect = true; + } + else + { + //Serial.println("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: "); + //Serial.println("Got new url: " + newUrl); + newUrl.remove(0, newUrl.indexOf("//") + 2); + currentHost = newUrl.substring(0, newUrl.indexOf('/')); + newUrl.remove(newUrl.indexOf(currentHost), currentHost.length()); + firmwarePath = newUrl; + //Serial.println("firmwarePath: " + firmwarePath); + continue; + } + + // Checking headers + if (line.startsWith("Content-Length: ")) + { + contentLength = atoi((getHeaderValue(line, "Content-Length: ")).c_str()); + Serial.println("Got " + String(contentLength) + " bytes from server"); + } + + if (line.startsWith("Content-Type: ")) + { + String contentType = getHeaderValue(line, "Content-Type: "); + //Serial.println("Got " + contentType + " payload."); + if (contentType == "application/octet-stream") + { + isValidContentType = true; + } + } + } + } + + // check whether we have everything for OTA update + if (contentLength && isValidContentType) + { + if (Update.begin(contentLength)) + { + Serial.println("Starting Over-The-Air update. This may take some time to complete ..."); + size_t written = Update.writeStream(client); + + if (written == contentLength) + { + Serial.println("Written : " + String(written) + " successfully"); + } + else + { + Serial.println("Written only : " + String(written) + "/" + String(contentLength) + ". Retry?"); + // Retry?? + } + + if (Update.end()) + { + if (Update.isFinished()) + { + Serial.println("OTA update has successfully completed. Rebooting ..."); + ESP.restart(); + } + else + { + Serial.println("Something went wrong! OTA update hasn't been finished properly."); + } + } + else + { + Serial.println("An error Occurred. Error #: " + String(Update.getError())); + } + } + else + { + Serial.println("There isn't enough space to start OTA update"); + client.flush(); + } + } + else + { + Serial.println("There was no valid content in the response from the OTA server!"); + client.flush(); + } +} \ No newline at end of file diff --git a/src/SecureOTA.h b/src/SecureOTA.h new file mode 100644 index 00000000..d8b17c3f --- /dev/null +++ b/src/SecureOTA.h @@ -0,0 +1,25 @@ +/* + Copyright (c) 2014-present PlatformIO + + 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 + +void checkFirmwareUpdates(); +void processOTAUpdate(const String &version); + +#endif // SECURE_OTA_H \ No newline at end of file diff --git a/src/globals.h b/src/globals.h index a34851a7..1043b13a 100644 --- a/src/globals.h +++ b/src/globals.h @@ -52,6 +52,7 @@ extern portMUX_TYPE timerMux; extern volatile int SendCycleTimerIRQ, HomeCycleIRQ, DisplayTimerIRQ, ChannelTimerIRQ, ButtonPressedIRQ; extern QueueHandle_t LoraSendQueue, SPISendQueue; +extern TaskHandle_t WifiLoopTask; extern std::array::iterator it; extern std::array beacons; diff --git a/src/main.cpp b/src/main.cpp index 1b258f14..b3825cb6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,6 +41,8 @@ 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; @@ -298,7 +300,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 diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 8899a805..70ac4c8e 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -138,7 +138,7 @@ void set_loraadr(uint8_t val[]) { ESP_LOGI(TAG, "Remote command: set LoRa ADR mode to %s", val[0] ? "on" : "off"); cfg.adrmode = val[0] ? 1 : 0; -LMIC_setAdrMode(cfg.adrmode); + LMIC_setAdrMode(cfg.adrmode); #else ESP_LOGW(TAG, "Remote command: LoRa not implemented"); #endif // HAS_LORA @@ -219,6 +219,29 @@ void get_gps(uint8_t val[]) { #endif }; +void set_update(uint8_t val[]) { + ESP_LOGI(TAG, "Remote command: get firmware update"); + + ESP_LOGI(TAG, "Stopping Wifi task on core 0"); + vTaskDelete(WifiLoopTask); + + ESP_LOGI(TAG, "Connecting to %s", WIFI_SSID); + ESP_ERROR_CHECK(esp_wifi_set_promiscuous(false)); // switch off monitor mode + //tcpipInit(); + tcpip_adapter_init(); + WiFi.mode(WIFI_STA); + + WiFi.begin(WIFI_SSID, WIFI_PASS); + + while (WiFi.status() != WL_CONNECTED) { + ESP_LOGI(TAG, "."); + delay(500); + } + + ESP_LOGI(TAG, "connected!"); + checkFirmwareUpdates(); +}; + // 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 +256,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 diff --git a/src/rcommand.h b/src/rcommand.h index 6afb905f..2d1902a1 100644 --- a/src/rcommand.h +++ b/src/rcommand.h @@ -6,6 +6,9 @@ #include "lorawan.h" #include "macsniff.h" +#include +#include "SecureOTA.h" + // table of remote commands and assigned functions typedef struct { const uint8_t opcode; From cc603d4ab8f5b8662633e4512a31a02aa6c53486 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 12 Aug 2018 15:42:58 +0200 Subject: [PATCH 2/4] testing --- README.md | 1 + lib/BintrayClient/src/BintrayClient.cpp | 8 ++--- platformio.ini | 39 ++++++++++----------- src/OTA.cpp | 30 ++++++++++++++++ src/OTA.h | 11 ++++++ src/SecureOTA.cpp | 46 ++++++++++++------------- src/TTN/packed_decoder.js | 3 +- src/globals.h | 8 ++++- src/main.cpp | 5 ++- src/paxcounter.conf | 2 +- src/payload.cpp | 16 +++++---- src/payload.h | 5 ++- src/rcommand.cpp | 21 ++++------- src/rcommand.h | 2 ++ src/wifiscan.cpp | 1 + 15 files changed, 123 insertions(+), 75 deletions(-) create mode 100644 src/OTA.cpp create mode 100644 src/OTA.h diff --git a/README.md b/README.md index 6d5b64d5..4d42fa2e 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,7 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering. byte 3-10: Uptime [seconds] bytes 11-14: CPU temperature [°C] bytes 15-18: Free RAM [bytes] + bytes 19-20: Last reset reasons core 0 / core 1 **Port #3:** Device configuration query result diff --git a/lib/BintrayClient/src/BintrayClient.cpp b/lib/BintrayClient/src/BintrayClient.cpp index b0e7543f..4479f539 100644 --- a/lib/BintrayClient/src/BintrayClient.cpp +++ b/lib/BintrayClient/src/BintrayClient.cpp @@ -109,7 +109,7 @@ String BintrayClient::getLatestVersion() const const size_t bufferSize = 1024; if (jsonResult.length() > bufferSize) { - Serial.println("Error: Could parse JSON. Input data is too big!"); + ESP_LOGI(TAG, "Error: Could not parse JSON. Input data is too big!"); return version; } StaticJsonBuffer jsonBuffer; @@ -118,7 +118,7 @@ String BintrayClient::getLatestVersion() const // Check for errors in parsing if (!root.success()) { - Serial.println("Error: Could not parse JSON!"); + ESP_LOGI(TAG, "Error: Could not parse JSON!"); return version; } return root.get("name"); @@ -133,7 +133,7 @@ String BintrayClient::getBinaryPath(const String &version) const const size_t bufferSize = 1024; if (jsonResult.length() > bufferSize) { - Serial.println("Error: Could parse JSON. Input data is too big!"); + ESP_LOGI(TAG, "Error: Could parse JSON. Input data is too big!"); return path; } StaticJsonBuffer jsonBuffer; @@ -142,7 +142,7 @@ String BintrayClient::getBinaryPath(const String &version) const JsonObject &firstItem = root[0]; if (!root.success()) { //Check for errors in parsing - Serial.println("Error: Could not parse JSON!"); + ESP_LOGI(TAG, "Error: Could not parse JSON!"); return path; } return "/" + getUser() + "/" + getRepository() + "/" + firstItem.get("path"); diff --git a/platformio.ini b/platformio.ini index 91ee4e27..b07f4d49 100644 --- a/platformio.ini +++ b/platformio.ini @@ -56,25 +56,7 @@ build_flags = '-DBINTRAY_REPO="${bintray.repository}"' '-DBINTRAY_PACKAGE="${bintray.package}"' '-DVERSION=0' - -; extra dependencies -lib_deps = ArduinoJson - - -[common_env_data] -platform_espressif32 = espressif32@1.2.0 -;platform_espressif32 = https://github.com/platformio/platform-espressif32.git#feature/stage -;board_build.partitions = no_ota.csv -board_build.partitions = min_spiffs.csv -lib_deps_all = -lib_deps_display = - U8g2@>=2.23.12 -lib_deps_rgbled = - SmartLeds@>=1.1.3 -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 ; @@ -90,13 +72,28 @@ build_flags = ; -DCORE_DEBUG_LEVEL=4 ; Verbose ; -DCORE_DEBUG_LEVEL=5 -; + +[common_env_data] +platform_espressif32 = espressif32@1.2.0 +;platform_espressif32 = https://github.com/platformio/platform-espressif32.git#feature/stage +;board_build.partitions = no_ota.csv +board_build.partitions = min_spiffs.csv +lib_deps_all = + ArduinoJson +lib_deps_display = + U8g2@>=2.23.12 +lib_deps_rgbled = + SmartLeds@>=1.1.3 +lib_deps_gps = + TinyGPSPlus@>=1.0.2 + Time@>=1.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 - + [env:ebox] platform = ${common_env_data.platform_espressif32} framework = arduino diff --git a/src/OTA.cpp b/src/OTA.cpp new file mode 100644 index 00000000..7b9849cd --- /dev/null +++ b/src/OTA.cpp @@ -0,0 +1,30 @@ +#include "OTA.h" + +const BintrayClient bintray(BINTRAY_USER, BINTRAY_REPO, BINTRAY_PACKAGE); + +void ota_wifi_init(void) { + const int RESPONSE_TIMEOUT_MS = 5000; + unsigned long timeout = millis(); + + ESP_ERROR_CHECK(esp_wifi_set_promiscuous(false)); // switch off monitor mode + tcpip_adapter_init(); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_start()); + + WiFi.begin(WIFI_SSID, WIFI_PASS); + WiFi.setHostname(PROGNAME); + +/* + while (WiFi.status() != WL_CONNECTED) { + ESP_LOGI(TAG, "WiFi Status %d", WiFi.status()); + if (millis() - timeout > RESPONSE_TIMEOUT_MS) { + ESP_LOGE(TAG, "WiFi connection timeout. Please check your settings!"); + } + + delay(500); + } + + configASSERT(WiFi.isConnected() == true); +*/ + +} \ No newline at end of file diff --git a/src/OTA.h b/src/OTA.h new file mode 100644 index 00000000..5133a685 --- /dev/null +++ b/src/OTA.h @@ -0,0 +1,11 @@ +#ifndef OTA_H +#define OTA_H + +#include +#include "globals.h" +#include +#include + +void ota_wifi_init(void); + +#endif // OTA_H \ No newline at end of file diff --git a/src/SecureOTA.cpp b/src/SecureOTA.cpp index 7496c19c..2ce69eca 100644 --- a/src/SecureOTA.cpp +++ b/src/SecureOTA.cpp @@ -37,16 +37,16 @@ void checkFirmwareUpdates() const String latest = bintray.getLatestVersion(); if (latest.length() == 0) { - Serial.println("Could not load info about the latest firmware, so nothing to update. Continue ..."); + ESP_LOGI(TAG, "Could not load info about the latest firmware, so nothing to update. Continue ..."); return; } else if (atoi(latest.c_str()) <= VERSION) { - //Serial.println("The current firmware is up to date. Continue ..."); + //ESP_LOGI(TAG, "The current firmware is up to date. Continue ..."); return; } - Serial.println("There is a new version of firmware available: v." + latest); + ESP_LOGI(TAG, "There is a new version of firmware available: v.%s", latest); processOTAUpdate(latest); } @@ -64,7 +64,7 @@ void processOTAUpdate(const String &version) String firmwarePath = bintray.getBinaryPath(version); if (!firmwarePath.endsWith(".bin")) { - Serial.println("Unsupported binary format. OTA update cannot be performed!"); + ESP_LOGI(TAG, "Unsupported binary format. OTA update cannot be performed!"); return; } @@ -76,7 +76,7 @@ void processOTAUpdate(const String &version) if (!client.connect(currentHost.c_str(), port)) { - Serial.println("Cannot connect to " + currentHost); + ESP_LOGI(TAG, "Cannot connect to %s", currentHost); return; } @@ -89,12 +89,12 @@ void processOTAUpdate(const String &version) client.setCACert(bintray.getCertificate(currentHost)); if (!client.connect(currentHost.c_str(), port)) { - Serial.println("Redirect detected! Cannot connect to " + currentHost + " for some reason!"); + ESP_LOGI(TAG, "Redirect detected! Cannot connect to %s for some reason!", currentHost); return; } } - //Serial.println("Requesting: " + firmwarePath); + //ESP_LOGI(TAG, "Requesting: " + firmwarePath); client.print(String("GET ") + firmwarePath + " HTTP/1.1\r\n"); client.print(String("Host: ") + currentHost + "\r\n"); @@ -106,7 +106,7 @@ void processOTAUpdate(const String &version) { if (millis() - timeout > RESPONSE_TIMEOUT_MS) { - Serial.println("Client Timeout !"); + ESP_LOGI(TAG, "Client Timeout !"); client.stop(); return; } @@ -128,17 +128,17 @@ void processOTAUpdate(const String &version) { if (line.indexOf("200") > 0) { - //Serial.println("Got 200 status code from server. Proceeding to firmware flashing"); + ESP_LOGI(TAG, "Got 200 status code from server. Proceeding to firmware flashing"); redirect = false; } else if (line.indexOf("302") > 0) { - //Serial.println("Got 302 status code from server. Redirecting to the new address"); + ESP_LOGI(TAG, "Got 302 status code from server. Redirecting to the new address"); redirect = true; } else { - //Serial.println("Could not get a valid firmware url"); + ESP_LOGI(TAG, "Could not get a valid firmware url"); //Unexptected HTTP response. Retry or skip update? redirect = false; } @@ -148,12 +148,12 @@ void processOTAUpdate(const String &version) if (line.startsWith("Location: ")) { String newUrl = getHeaderValue(line, "Location: "); - //Serial.println("Got new url: " + newUrl); + 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; - //Serial.println("firmwarePath: " + firmwarePath); + ESP_LOGI(TAG, "firmwarePath: %s", firmwarePath); continue; } @@ -161,13 +161,13 @@ void processOTAUpdate(const String &version) if (line.startsWith("Content-Length: ")) { contentLength = atoi((getHeaderValue(line, "Content-Length: ")).c_str()); - Serial.println("Got " + String(contentLength) + " bytes from server"); + ESP_LOGI(TAG, "Got %s bytes from server", String(contentLength)); } if (line.startsWith("Content-Type: ")) { String contentType = getHeaderValue(line, "Content-Type: "); - //Serial.println("Got " + contentType + " payload."); + ESP_LOGI(TAG, "Got %s payload", contentType); if (contentType == "application/octet-stream") { isValidContentType = true; @@ -181,16 +181,16 @@ void processOTAUpdate(const String &version) { if (Update.begin(contentLength)) { - Serial.println("Starting Over-The-Air update. This may take some time to complete ..."); + ESP_LOGI(TAG, "Starting Over-The-Air update. This may take some time to complete ..."); size_t written = Update.writeStream(client); if (written == contentLength) { - Serial.println("Written : " + String(written) + " successfully"); + ESP_LOGI(TAG, "Written %s successfully", String(written)); } else { - Serial.println("Written only : " + String(written) + "/" + String(contentLength) + ". Retry?"); + ESP_LOGI(TAG, "Written only %s / %s Retry?", String(written), String(contentLength)); // Retry?? } @@ -198,28 +198,28 @@ void processOTAUpdate(const String &version) { if (Update.isFinished()) { - Serial.println("OTA update has successfully completed. Rebooting ..."); + ESP_LOGI(TAG, "OTA update has successfully completed. Rebooting ..."); ESP.restart(); } else { - Serial.println("Something went wrong! OTA update hasn't been finished properly."); + ESP_LOGI(TAG, "Something went wrong! OTA update hasn't been finished properly."); } } else { - Serial.println("An error Occurred. Error #: " + String(Update.getError())); + ESP_LOGI(TAG, "An error occurred. Error #: %s", String(Update.getError())); } } else { - Serial.println("There isn't enough space to start OTA update"); + ESP_LOGI(TAG, "There isn't enough space to start OTA update"); client.flush(); } } else { - Serial.println("There was no valid content in the response from the OTA server!"); + ESP_LOGI(TAG, "There was no valid content in the response from the OTA server!"); client.flush(); } } \ No newline at end of file diff --git a/src/TTN/packed_decoder.js b/src/TTN/packed_decoder.js index 6bf29f09..32d6dc9f 100644 --- a/src/TTN/packed_decoder.js +++ b/src/TTN/packed_decoder.js @@ -18,7 +18,7 @@ function Decoder(bytes, port) { if (port === 2) { // device status data - return decode(bytes, [uint16, uptime, temperature, uint32], ['voltage', 'uptime', 'cputemp', 'memory']); + return decode(bytes, [uint16, uptime, temperature, uint32, uint8, uint8], ['voltage', 'uptime', 'cputemp', 'memory', 'reset', 'reset']); } @@ -181,6 +181,7 @@ if (typeof module === 'object' && typeof module.exports !== 'undefined') { uint16: uint16, uint32: uint32, uptime: uptime, + reset: reset, temperature: temperature, humidity: humidity, latLng: latLng, diff --git a/src/globals.h b/src/globals.h index 1043b13a..879a30e1 100644 --- a/src/globals.h +++ b/src/globals.h @@ -51,7 +51,7 @@ 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::iterator it; @@ -68,9 +68,15 @@ extern std::array 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 diff --git a/src/main.cpp b/src/main.cpp index b3825cb6..fe5d5742 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -46,6 +46,7 @@ TaskHandle_t WifiLoopTask = NULL; // RTos send queues for payload transmit #ifdef HAS_LORA QueueHandle_t LoraSendQueue; +TaskHandle_t LoraTask = NULL; #endif #ifdef HAS_SPI @@ -271,7 +272,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 @@ -332,6 +333,8 @@ void loop() { processSendBuffer(); // check send cycle and enqueue payload if cycle is expired sendPayload(); + // reset watchdog + vTaskDelay(1 / portTICK_PERIOD_MS); } // loop() } diff --git a/src/paxcounter.conf b/src/paxcounter.conf index 7cf76677..bcecc56d 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -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 diff --git a/src/payload.cpp b/src/payload.cpp index ab3d2c91..958d3fb3 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -52,8 +52,8 @@ 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) { uint32_t temp = (uint32_t)cputemp; buffer[cursor++] = highByte(voltage); buffer[cursor++] = lowByte(voltage); @@ -73,6 +73,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 @@ -127,12 +129,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); writeTemperature(cputemp); writeUint32(mem); + writeUint8(reset1); + writeUint8(reset2); } #ifdef HAS_GPS @@ -245,8 +249,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 diff --git a/src/payload.h b/src/payload.h index ba564ec7..92f2d4ee 100644 --- a/src/payload.h +++ b/src/payload.h @@ -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; diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 70ac4c8e..63fcd690 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -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); }; @@ -225,21 +226,13 @@ void set_update(uint8_t val[]) { 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); - ESP_ERROR_CHECK(esp_wifi_set_promiscuous(false)); // switch off monitor mode - //tcpipInit(); - tcpip_adapter_init(); - WiFi.mode(WIFI_STA); - - WiFi.begin(WIFI_SSID, WIFI_PASS); - - while (WiFi.status() != WL_CONNECTED) { - ESP_LOGI(TAG, "."); - delay(500); - } - - ESP_LOGI(TAG, "connected!"); + ota_wifi_init(); checkFirmwareUpdates(); + }; // assign previously defined functions to set of numeric remote commands diff --git a/src/rcommand.h b/src/rcommand.h index 2d1902a1..880382ed 100644 --- a/src/rcommand.h +++ b/src/rcommand.h @@ -5,6 +5,8 @@ #include "configmanager.h" #include "lorawan.h" #include "macsniff.h" +#include +#include "ota.h" #include #include "SecureOTA.h" diff --git a/src/wifiscan.cpp b/src/wifiscan.cpp index 4190e182..9cb2ae98 100644 --- a/src/wifiscan.cpp +++ b/src/wifiscan.cpp @@ -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)); From 0ee138b02229357a8653d3ac637a926ec35d2e8e Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 12 Aug 2018 23:42:39 +0200 Subject: [PATCH 3/4] JFrog Bintray OTA (experimental, not working yet) --- platformio.ini | 8 ++---- src/OTA.cpp | 64 ++++++++++++++++++++++++++++++++++++------------ src/OTA.h | 4 ++- src/cyclic.cpp | 4 +++ src/globals.h | 5 ++-- src/main.cpp | 11 ++++++--- src/rcommand.cpp | 12 +-------- src/rcommand.h | 4 --- 8 files changed, 69 insertions(+), 43 deletions(-) diff --git a/platformio.ini b/platformio.ini index b07f4d49..ddf0adc7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -32,14 +32,10 @@ user = cyberman54 repository = paxcounter package = esp32-paxcounter api_token = 9f02e2a2374c278fd79d5bcf4b4442fca9752012 -;api_token = ${env.BINTRAY_API_TOKEN} -; Wi-Fi network settings [wifi] -ssid = PRENZLNET-G -password = 435Huse8!? -;ssid = ${env.PIO_WIFI_SSID} -;password = ${env.PIO_WIFI_PASSWORD} +ssid = *** +password = *** [common] platform = https://github.com/platformio/platform-espressif32.git diff --git a/src/OTA.cpp b/src/OTA.cpp index 7b9849cd..23d64f5c 100644 --- a/src/OTA.cpp +++ b/src/OTA.cpp @@ -2,29 +2,61 @@ const BintrayClient bintray(BINTRAY_USER, BINTRAY_REPO, BINTRAY_PACKAGE); -void ota_wifi_init(void) { - const int RESPONSE_TIMEOUT_MS = 5000; - unsigned long timeout = millis(); +bool Wifi_Connected = false; - ESP_ERROR_CHECK(esp_wifi_set_promiscuous(false)); // switch off monitor mode +static esp_err_t event_handler(void *ctx, system_event_t *event) { + switch (event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + Wifi_Connected = true; + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + Wifi_Connected = false; + break; + default: + break; + } +} + +void ota_wifi_init(void) { + + // initialize the tcp stack tcpip_adapter_init(); + + // initialize the wifi event handler + ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + wifi_config_t sta_config = {}; + + strcpy((char *)sta_config.sta.ssid, WIFI_SSID); + strcpy((char *)sta_config.sta.password, WIFI_PASS); + sta_config.sta.bssid_set = false; + + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &sta_config)); ESP_ERROR_CHECK(esp_wifi_start()); - WiFi.begin(WIFI_SSID, WIFI_PASS); - WiFi.setHostname(PROGNAME); + // 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)); -/* - while (WiFi.status() != WL_CONNECTED) { - ESP_LOGI(TAG, "WiFi Status %d", WiFi.status()); - if (millis() - timeout > RESPONSE_TIMEOUT_MS) { - ESP_LOGE(TAG, "WiFi connection timeout. Please check your settings!"); - } +} - delay(500); - } +void start_ota_update() { + ESP_LOGI(TAG, "Stopping Wifi task on core 0"); + vTaskDelete(WifiLoopTask); - configASSERT(WiFi.isConnected() == true); -*/ + ESP_LOGI(TAG, "Stopping LORA task on core 1"); + vTaskDelete(LoraTask); + ESP_LOGI(TAG, "Connecting to %s", WIFI_SSID); + ota_wifi_init(); + checkFirmwareUpdates(); + ESP.restart(); // reached if update was not successful } \ No newline at end of file diff --git a/src/OTA.h b/src/OTA.h index 5133a685..8b6a27df 100644 --- a/src/OTA.h +++ b/src/OTA.h @@ -5,7 +5,9 @@ #include "globals.h" #include #include +#include "ota.h" +#include "SecureOTA.h" -void ota_wifi_init(void); +void start_ota_update(); #endif // OTA_H \ No newline at end of file diff --git a/src/cyclic.cpp b/src/cyclic.cpp index adbadda4..45ce16cd 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -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(); diff --git a/src/globals.h b/src/globals.h index 879a30e1..53aad820 100644 --- a/src/globals.h +++ b/src/globals.h @@ -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,7 @@ 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::iterator it; diff --git a/src/main.cpp b/src/main.cpp index fe5d5742..d52b4a7a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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, @@ -70,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 @@ -92,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 @@ -333,7 +338,7 @@ void loop() { processSendBuffer(); // check send cycle and enqueue payload if cycle is expired sendPayload(); - // reset watchdog + // reset watchdog vTaskDelay(1 / portTICK_PERIOD_MS); } // loop() diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 63fcd690..a052c48e 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -222,17 +222,7 @@ void get_gps(uint8_t val[]) { void set_update(uint8_t val[]) { ESP_LOGI(TAG, "Remote command: get firmware 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(); - checkFirmwareUpdates(); - + ota_update = true; }; // assign previously defined functions to set of numeric remote commands diff --git a/src/rcommand.h b/src/rcommand.h index 880382ed..c656c9fd 100644 --- a/src/rcommand.h +++ b/src/rcommand.h @@ -6,10 +6,6 @@ #include "lorawan.h" #include "macsniff.h" #include -#include "ota.h" - -#include -#include "SecureOTA.h" // table of remote commands and assigned functions typedef struct { From 7a58c8dece3a8a3e0384b8c748633e82cc362314 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Thu, 16 Aug 2018 21:10:13 +0200 Subject: [PATCH 4/4] testing --- src/OTA.cpp | 47 ++++++++++++++++++++++++++++++++--------------- src/wifiscan.cpp | 2 +- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/OTA.cpp b/src/OTA.cpp index 23d64f5c..1ec3be8e 100644 --- a/src/OTA.cpp +++ b/src/OTA.cpp @@ -4,16 +4,23 @@ const BintrayClient bintray(BINTRAY_USER, BINTRAY_REPO, BINTRAY_PACKAGE); bool Wifi_Connected = false; -static esp_err_t event_handler(void *ctx, system_event_t *event) { +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; @@ -22,30 +29,38 @@ static esp_err_t event_handler(void *ctx, system_event_t *event) { 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)); - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - wifi_config_t sta_config = {}; + // 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)); - strcpy((char *)sta_config.sta.ssid, WIFI_SSID); - strcpy((char *)sta_config.sta.password, WIFI_PASS); - sta_config.sta.bssid_set = false; + wifi_sta_config_t cfg; + strcpy((char *)cfg.ssid, WIFI_SSID); + strcpy((char *)cfg.password, WIFI_PASS); + cfg.bssid_set = false; - ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + 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_config)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &sta_cfg)); ESP_ERROR_CHECK(esp_wifi_start()); - - // 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)); - } void start_ota_update() { @@ -57,6 +72,8 @@ void start_ota_update() { ESP_LOGI(TAG, "Connecting to %s", WIFI_SSID); ota_wifi_init(); + delay(2000); + delay(2000); checkFirmwareUpdates(); ESP.restart(); // reached if update was not successful } \ No newline at end of file diff --git a/src/wifiscan.cpp b/src/wifiscan.cpp index 9cb2ae98..24d6504f 100644 --- a/src/wifiscan.cpp +++ b/src/wifiscan.cpp @@ -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