added ota and more led strips

This commit is contained in:
2021-08-28 22:25:26 +02:00
parent 50a7ae811b
commit 851a2084f3
9 changed files with 267 additions and 61 deletions

3
.gitmodules vendored
View File

@ -37,3 +37,6 @@
[submodule "components/esp-nimble-cpp"]
path = components/esp-nimble-cpp
url = git@github.com:0xFEEDC0DE64/esp-nimble-cpp.git
[submodule "components/espasyncota"]
path = components/espasyncota
url = git@github.com:0xFEEDC0DE64/espasyncota.git

View File

@ -1,6 +1,7 @@
set(headers
ble.h
led.h
ota.h
webserver.h
wifi.h
)
@ -9,13 +10,15 @@ set(sources
ble.cpp
led.cpp
main.cpp
ota.cpp
webserver.cpp
wifi.cpp
)
set(dependencies
freertos nvs_flash esp_http_server esp_https_ota mdns app_update esp_system esp_websocket_client driver
arduino-esp32 ArduinoJson FastLED-idf cpputils date espchrono espcpputils esp-nimble-cpp esphttpdutils espwifistack expected fmt
arduino-esp32 ArduinoJson FastLED-idf cpputils date espchrono espasyncota espcpputils esp-nimble-cpp
esphttpdutils espwifistack expected fmt
)
idf_component_register(

View File

@ -13,9 +13,8 @@
#include <FastLED.h>
#include <espchrono.h>
#define LEDC_CHANNEL_0 0
#define LEDC_CHANNEL_1 0
#define LEDC_CHANNEL_2 0
using namespace std::chrono_literals;
#define LEDC_TIMER_13_BIT 13
#define LEDC_BASE_FREQ 5000
@ -25,10 +24,113 @@ std::array<CRGB, 5> leds;
CRGBPalette16 currentPalette;
TBlendType currentBlending;
extern CRGBPalette16 myRedWhiteBluePalette;
extern const TProgmemPalette16 myRedWhiteBluePalette_p;
const TProgmemPalette16 myRedWhiteBluePalette_p =
{
CRGB::Red,
CRGB::Gray, // 'white' is too bright compared to red and blue
CRGB::Blue,
CRGB::Black,
CRGB::Red,
CRGB::Gray,
CRGB::Blue,
CRGB::Black,
CRGB::Red,
CRGB::Red,
CRGB::Gray,
CRGB::Gray,
CRGB::Blue,
CRGB::Blue,
CRGB::Black,
CRGB::Black
};
void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 255);
void ChangePalettePeriodically();
void FillLEDsFromPaletteColors( uint8_t colorIndex);
espchrono::millis_clock::time_point lastRedraw{};
}
void led_setup()
{
currentPalette = RainbowColors_p;
currentBlending = LINEARBLEND;
ledcSetup(0, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcSetup(1, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcSetup(2, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcSetup(3, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcSetup(4, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcSetup(5, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcSetup(6, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcSetup(7, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcSetup(8, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcSetup(9, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcSetup(10, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcSetup(11, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcSetup(12, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcSetup(13, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcSetup(14, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcAttachPin(27, 0);
ledcAttachPin(33, 1);
ledcAttachPin(32, 2);
ledcAttachPin(23, 3);
ledcAttachPin(25, 4);
ledcAttachPin(26, 5);
ledcAttachPin(19, 6);
ledcAttachPin(21, 7);
ledcAttachPin(22, 8);
ledcAttachPin(18, 9);
ledcAttachPin(17, 10);
ledcAttachPin(16, 11);
ledcAttachPin(13, 12);
ledcAttachPin(14, 13);
ledcAttachPin(15, 14);
}
void led_update()
{
if (espchrono::ago(lastRedraw) < 20ms)
return;
lastRedraw = espchrono::millis_clock::now();
ChangePalettePeriodically();
static uint8_t startIndex = 0;
startIndex = startIndex + 1; /* motion speed */
FillLEDsFromPaletteColors(startIndex);
ledcAnalogWrite(0, leds[0].red);
ledcAnalogWrite(1, leds[0].green);
ledcAnalogWrite(2, leds[0].blue);
ledcAnalogWrite(3, leds[1].red);
ledcAnalogWrite(4, leds[1].green);
ledcAnalogWrite(5, leds[1].blue);
ledcAnalogWrite(6, leds[2].red);
ledcAnalogWrite(7, leds[2].green);
ledcAnalogWrite(8, leds[2].blue);
ledcAnalogWrite(9, leds[3].red);
ledcAnalogWrite(10, leds[3].green);
ledcAnalogWrite(11, leds[3].blue);
ledcAnalogWrite(12, leds[4].red);
ledcAnalogWrite(13, leds[4].green);
ledcAnalogWrite(14, leds[4].blue);
}
namespace {
@ -83,29 +185,6 @@ void SetupPurpleAndGreenPalette()
purple, purple, black, black );
}
const TProgmemPalette16 myRedWhiteBluePalette_p =
{
CRGB::Red,
CRGB::Gray, // 'white' is too bright compared to red and blue
CRGB::Blue,
CRGB::Black,
CRGB::Red,
CRGB::Gray,
CRGB::Blue,
CRGB::Black,
CRGB::Red,
CRGB::Red,
CRGB::Gray,
CRGB::Gray,
CRGB::Blue,
CRGB::Blue,
CRGB::Black,
CRGB::Black
};
void ChangePalettePeriodically()
{
constexpr auto millis = []() -> unsigned int { return espchrono::millis_clock::now().time_since_epoch().count(); };
@ -128,32 +207,4 @@ void ChangePalettePeriodically()
if( secondHand == 55) { currentPalette = myRedWhiteBluePalette_p; currentBlending = LINEARBLEND; }
}
}
}
void led_setup()
{
currentPalette = RainbowColors_p;
currentBlending = LINEARBLEND;
ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcSetup(LEDC_CHANNEL_1, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcSetup(LEDC_CHANNEL_2, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcAttachPin(27, LEDC_CHANNEL_0);
ledcAttachPin(33, LEDC_CHANNEL_1);
ledcAttachPin(32, LEDC_CHANNEL_2);
}
void led_update()
{
ChangePalettePeriodically();
static uint8_t startIndex = 0;
startIndex = startIndex + 1; /* motion speed */
FillLEDsFromPaletteColors( startIndex);
ledcAnalogWrite(LEDC_CHANNEL_0, leds[0].red);
ledcAnalogWrite(LEDC_CHANNEL_1, leds[0].green);
ledcAnalogWrite(LEDC_CHANNEL_2, leds[0].blue);
}
} // namespace

View File

@ -19,6 +19,7 @@
#include "wifi.h"
#include "ble.h"
#include "webserver.h"
#include "ota.h"
#include "led.h"
using namespace std::chrono_literals;
@ -65,6 +66,7 @@ extern "C" void app_main()
wifi_setup();
ble_setup();
webserver_setup();
ota_setup();
led_setup();
while (true)
@ -77,6 +79,7 @@ extern "C" void app_main()
wifi_update();
ble_update();
webserver_update();
ota_update();
led_update();
}
}

50
main/ota.cpp Normal file
View File

@ -0,0 +1,50 @@
#include "ota.h"
// esp-idf includes
#include <esp_log.h>
// 3rdparty lib includes
#include <delayedconstruction.h>
#include <espasyncota.h>
#include <espwifistack.h>
// local includes
#include "wifi.h"
namespace {
constexpr const char * const TAG = "OTA";
cpputils::DelayedConstruction<EspAsyncOta> _asyncOta;
} // namespace
EspAsyncOta &asyncOta{_asyncOta.getUnsafe()};
void ota_setup()
{
ESP_LOGI(TAG, "called");
_asyncOta.construct();
if (const auto result = _asyncOta->startTask(); !result)
{
ESP_LOGE(TAG, "starting OTA task failed: %.*s", result.error().size(), result.error().data());
return;
}
}
void ota_update()
{
_asyncOta->update();
}
tl::expected<void, std::string> triggerOta(std::string_view url)
{
ESP_LOGI(TAG, "%.*s", url.size(), url.data());
if (const auto result = _asyncOta->trigger(url, {}, {}, {}); !result)
return tl::make_unexpected(std::move(result).error());
wifi_stack::delete_scan_result();
return {};
}

18
main/ota.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
// system includes
#include <string>
#include <string_view>
// 3rdparty lib includes
#include <tl/expected.hpp>
// forward declares
class EspAsyncOta;
extern EspAsyncOta &asyncOta;
void ota_setup();
void ota_update();
tl::expected<void, std::string> triggerOta(std::string_view url);

View File

@ -7,8 +7,12 @@
// 3rdparty lib includes
#include <esphttpdutils.h>
#include <espcppmacros.h>
#include <fmt/core.h>
#include <espasyncota.h>
// local includes
#include "wifi.h"
#include "ota.h"
using namespace esphttpdutils;
@ -18,6 +22,7 @@ constexpr const char * const TAG = "WEBSERVER";
httpd_handle_t httpdHandle;
esp_err_t webserver_root_handler(httpd_req_t *req);
esp_err_t webserver_ota_handler(httpd_req_t *req);
} // namespace
void webserver_setup()
@ -34,7 +39,8 @@ void webserver_setup()
}
for (const httpd_uri_t &uri : {
httpd_uri_t { .uri = "/", .method = HTTP_GET, .handler = webserver_root_handler, .user_ctx = NULL },
httpd_uri_t { .uri = "/", .method = HTTP_GET, .handler = webserver_root_handler, .user_ctx = NULL },
httpd_uri_t { .uri = "/ota", .method = HTTP_GET, .handler = webserver_ota_handler, .user_ctx = NULL },
})
{
const auto result = httpd_register_uri_handler(httpdHandle, &uri);
@ -52,8 +58,78 @@ void webserver_update()
namespace {
esp_err_t webserver_root_handler(httpd_req_t *req)
{
std::string body = "hello world";
std::string body = fmt::format("<h1>{}</h1>", htmlentities(deviceName));
if (const auto otaStatus = asyncOta.status(); otaStatus == OtaCloudUpdateStatus::Idle)
{
body +=
"<form action=\"/ota\">"
"<label>ota url: <input type=\"url\" name=\"url\" required /></label>"
"<button type=\"submit\">Start</button>"
"</form>";
}
else
{
const auto progress = asyncOta.progress();
const char *color;
switch (otaStatus)
{
case OtaCloudUpdateStatus::Updating: color = "#b8b800"; break;
case OtaCloudUpdateStatus::Succeeded: color = "#13c200"; break;
default: color = "#b80000";
}
body += fmt::format("<p style=\"color: {};\">OTA: status={} progress={}", color, esphttpdutils::htmlentities(toString(otaStatus)), progress);
if (const auto totalSize = asyncOta.totalSize())
{
body += fmt::format(" totalSize={}", *totalSize);
if (*totalSize)
body += fmt::format(" percentage={:.1f}%", 100.f * progress / *totalSize);
}
if (const auto &message = asyncOta.message(); !message.empty())
body += fmt::format(" message={}", esphttpdutils::htmlentities(message));
body += "</p>\n";
}
CALL_AND_EXIT(webserver_resp_send_succ, req, "text/html", body)
}
esp_err_t webserver_ota_handler(httpd_req_t *req)
{
std::string query;
if (const size_t queryLength = httpd_req_get_url_query_len(req))
{
query.resize(queryLength);
CALL_AND_EXIT_ON_ERROR(httpd_req_get_url_query_str, req, query.data(), query.size() + 1)
}
char urlBufEncoded[256];
if (const auto result = httpd_query_key_value(query.data(), "url", urlBufEncoded, sizeof(urlBufEncoded)); result == ESP_ERR_NOT_FOUND)
{
CALL_AND_EXIT(webserver_resp_send_err, req, HTTPD_400_BAD_REQUEST, "text/plain", "url parameter missing")
}
else if (result != ESP_OK)
{
const auto msg = fmt::format("httpd_query_key_value() {} failed with {}", "url", esp_err_to_name(result));
ESP_LOGE(TAG, "%.*s", msg.size(), msg.data());
CALL_AND_EXIT(webserver_resp_send_err, req, HTTPD_400_BAD_REQUEST, "text/plain", msg)
}
char urlBuf[257];
esphttpdutils::urldecode(urlBuf, urlBufEncoded);
std::string_view url{urlBuf};
if (const auto result = triggerOta(url); !result)
{
ESP_LOGE(TAG, "%.*s", result.error().size(), result.error().data());
CALL_AND_EXIT(webserver_resp_send_err, req, HTTPD_400_BAD_REQUEST, "text/plain", result.error())
}
CALL_AND_EXIT(webserver_resp_send_succ, req, "text/plain", "OTA called...")
}
}

View File

@ -1,4 +1,5 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x10000, 0x5000,
otadata, data, ota, 0x15000, 0x2000,
app0, app, ota_0, 0x20000, 0x1F0000,
app0, app, ota_0, 0x20000, 0x190000,
app1, app, ota_1, 0x220000, 0x190000,

1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x10000 0x5000
3 otadata data ota 0x15000 0x2000
4 app0 app ota_0 0x20000 0x1F0000 0x190000
5 app1 app ota_1 0x220000 0x190000