basic led control implemented

This commit is contained in:
2021-08-28 19:31:44 +02:00
parent 84310e97ef
commit 50a7ae811b
13 changed files with 486 additions and 5 deletions

3
.gitmodules vendored
View File

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

View File

@@ -1,13 +1,21 @@
set(headers
ble.h
led.h
webserver.h
wifi.h
)
set(sources
ble.cpp
led.cpp
main.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 esphttpdutils espwifistack expected fmt
arduino-esp32 ArduinoJson FastLED-idf cpputils date espchrono espcpputils esp-nimble-cpp esphttpdutils espwifistack expected fmt
)
idf_component_register(

69
main/ble.cpp Normal file
View File

@@ -0,0 +1,69 @@
#include "ble.h"
// esp-idf includes
#include <esp_log.h>
// 3rdparty lib includes
#include <NimBLEDevice.h>
#include <ArduinoJson.h>
// local includes
#include "wifi.h"
namespace {
constexpr const char * const TAG = "BLE";
BLEServer *server{};
BLEService *service{};
BLECharacteristic *characteristic{};
class BleCallbacks : public NimBLECharacteristicCallbacks
{
public:
void onWrite(NimBLECharacteristic* pCharacteristic) override;
};
BleCallbacks bleCallbacks;
}
void ble_setup()
{
BLEDevice::init(deviceName);
const auto serviceUuid{"26e743b6-8cf2-43b0-8bd0-f762a03e120e"};
server = BLEDevice::createServer();
service = server->createService(serviceUuid);
characteristic = service->createCharacteristic("87d4bcc1-233f-4317-87e9-02b93f60f27b", NIMBLE_PROPERTY::WRITE);
characteristic->setCallbacks(&bleCallbacks);
service->start();
BLEAdvertising *advertising = BLEDevice::getAdvertising();
advertising->addServiceUUID(serviceUuid);
advertising->setScanResponse(true);
BLEDevice::startAdvertising();
}
void ble_update()
{
}
namespace {
void BleCallbacks::onWrite(NimBLECharacteristic* pCharacteristic)
{
const auto &val = pCharacteristic->getValue();
StaticJsonDocument<256> doc;
if (const auto error = deserializeJson(doc, val))
{
ESP_LOGW(TAG, "ignoring cmd with invalid json: %.*s %s", val.size(), val.data(), error.c_str());
return;
}
// TODO
}
}

4
main/ble.h Normal file
View File

@@ -0,0 +1,4 @@
#pragma once
void ble_setup();
void ble_update();

159
main/led.cpp Normal file
View File

@@ -0,0 +1,159 @@
#include "led.h"
// system includes
#include <array>
// esp-idf includes
#include <driver/ledc.h>
// Arduino includes
#include <Arduino.h>
// 3rdparty lib includes
#include <FastLED.h>
#include <espchrono.h>
#define LEDC_CHANNEL_0 0
#define LEDC_CHANNEL_1 0
#define LEDC_CHANNEL_2 0
#define LEDC_TIMER_13_BIT 13
#define LEDC_BASE_FREQ 5000
namespace {
std::array<CRGB, 5> leds;
CRGBPalette16 currentPalette;
TBlendType currentBlending;
extern CRGBPalette16 myRedWhiteBluePalette;
extern const TProgmemPalette16 myRedWhiteBluePalette_p;
void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 255);
}
namespace {
void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax)
{
// calculate duty, 8191 from 2 ^ 13 - 1
uint32_t duty = (8191 / valueMax) * min(value, valueMax);
// write duty to LEDC
ledcWrite(channel, duty);
}
void FillLEDsFromPaletteColors( uint8_t colorIndex)
{
uint8_t brightness = 255;
for( int i = 0; i < leds.size(); ++i) {
leds[i] = ColorFromPalette( currentPalette, colorIndex, brightness, currentBlending);
colorIndex += 3;
}
}
void SetupTotallyRandomPalette()
{
for( int i = 0; i < 16; ++i) {
currentPalette[i] = CHSV( random8(), 255, random8());
}
}
void SetupBlackAndWhiteStripedPalette()
{
// 'black out' all 16 palette entries...
fill_solid( currentPalette, 16, CRGB::Black);
// and set every fourth one to white.
currentPalette[0] = CRGB::White;
currentPalette[4] = CRGB::White;
currentPalette[8] = CRGB::White;
currentPalette[12] = CRGB::White;
}
void SetupPurpleAndGreenPalette()
{
CRGB purple = CHSV( HUE_PURPLE, 255, 255);
CRGB green = CHSV( HUE_GREEN, 255, 255);
CRGB black = CRGB::Black;
currentPalette = CRGBPalette16(
green, green, black, black,
purple, purple, black, black,
green, green, black, black,
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(); };
uint8_t secondHand = (millis() / 1000) % 60;
static uint8_t lastSecond = 99;
if( lastSecond != secondHand) {
lastSecond = secondHand;
if( secondHand == 0) { currentPalette = RainbowColors_p; currentBlending = LINEARBLEND; }
if( secondHand == 10) { currentPalette = RainbowStripeColors_p; currentBlending = NOBLEND; }
if( secondHand == 15) { currentPalette = RainbowStripeColors_p; currentBlending = LINEARBLEND; }
if( secondHand == 20) { SetupPurpleAndGreenPalette(); currentBlending = LINEARBLEND; }
if( secondHand == 25) { SetupTotallyRandomPalette(); currentBlending = LINEARBLEND; }
if( secondHand == 30) { SetupBlackAndWhiteStripedPalette(); currentBlending = NOBLEND; }
if( secondHand == 35) { SetupBlackAndWhiteStripedPalette(); currentBlending = LINEARBLEND; }
if( secondHand == 40) { currentPalette = CloudColors_p; currentBlending = LINEARBLEND; }
if( secondHand == 45) { currentPalette = PartyColors_p; currentBlending = LINEARBLEND; }
if( secondHand == 50) { currentPalette = myRedWhiteBluePalette_p; currentBlending = NOBLEND; }
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);
}

4
main/led.h Normal file
View File

@@ -0,0 +1,4 @@
#pragma once
void led_setup();
void led_update();

View File

@@ -1 +1,87 @@
extern "C" void app_main() {}
#include "sdkconfig.h"
// esp-idf includes
#include <esp_log.h>
#include <freertos/FreeRTOS.h>
#if defined(CONFIG_ESP_TASK_WDT_PANIC) || defined(CONFIG_ESP_TASK_WDT)
#include <freertos/task.h>
#include <esp_task_wdt.h>
#endif
#ifdef CONFIG_APP_ROLLBACK_ENABLE
#include <esp_ota_ops.h>
#endif
#include <esp_system.h>
// 3rdparty lib includes
#include <espchrono.h>
// local includes
#include "wifi.h"
#include "ble.h"
#include "webserver.h"
#include "led.h"
using namespace std::chrono_literals;
namespace {
constexpr const char * const TAG = "MAIN";
} // namespace
extern "C" void app_main()
{
#if defined(CONFIG_ESP_TASK_WDT_PANIC) || defined(CONFIG_ESP_TASK_WDT)
{
const auto taskHandle = xTaskGetCurrentTaskHandle();
if (!taskHandle)
{
ESP_LOGE(TAG, "could not get handle to current main task!");
}
else if (const auto result = esp_task_wdt_add(taskHandle); result != ESP_OK)
{
ESP_LOGE(TAG, "could not add main task to watchdog: %s", esp_err_to_name(result));
}
}
#endif
#ifdef CONFIG_APP_ROLLBACK_ENABLE
esp_ota_img_states_t ota_state;
if (const esp_partition_t * const running = esp_ota_get_running_partition())
{
if (const auto result = esp_ota_get_state_partition(running, &ota_state); result == ESP_ERR_NOT_FOUND)
ota_state = ESP_OTA_IMG_VALID;
else if (result != ESP_OK)
{
ESP_LOGE(TAG, "esp_ota_get_state_partition() failed with %s", esp_err_to_name(result));
ota_state = ESP_OTA_IMG_UNDEFINED;
}
}
else
{
ESP_LOGE(TAG, "esp_ota_get_running_partition() returned nullptr");
ota_state = ESP_OTA_IMG_UNDEFINED;
}
#endif
wifi_setup();
ble_setup();
webserver_setup();
led_setup();
while (true)
{
#if defined(CONFIG_ESP_TASK_WDT_PANIC) || defined(CONFIG_ESP_TASK_WDT)
if (const auto result = esp_task_wdt_reset(); result != ESP_OK)
ESP_LOGE(TAG, "esp_task_wdt_reset() failed with %s", esp_err_to_name(result));
#endif
wifi_update();
ble_update();
webserver_update();
led_update();
}
}
auto espchrono::local_clock::timezone() noexcept -> time_zone
{
return time_zone{1h, DayLightSavingMode::EuropeanSummerTime};
}

59
main/webserver.cpp Normal file
View File

@@ -0,0 +1,59 @@
#include "webserver.h"
// esp-idf includes
#include <esp_log.h>
#include <esp_http_server.h>
// 3rdparty lib includes
#include <esphttpdutils.h>
#include <espcppmacros.h>
// local includes
using namespace esphttpdutils;
namespace {
constexpr const char * const TAG = "WEBSERVER";
httpd_handle_t httpdHandle;
esp_err_t webserver_root_handler(httpd_req_t *req);
} // namespace
void webserver_setup()
{
{
httpd_config_t httpConfig HTTPD_DEFAULT_CONFIG();
httpConfig.core_id = 1;
httpConfig.max_uri_handlers = 14;
const auto result = httpd_start(&httpdHandle, &httpConfig);
ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "httpd_start(): %s", esp_err_to_name(result));
if (result != ESP_OK)
return;
}
for (const httpd_uri_t &uri : {
httpd_uri_t { .uri = "/", .method = HTTP_GET, .handler = webserver_root_handler, .user_ctx = NULL },
})
{
const auto result = httpd_register_uri_handler(httpdHandle, &uri);
ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "httpd_register_uri_handler() for %s: %s", uri.uri, esp_err_to_name(result));
//if (result != ESP_OK)
// return result;
}
}
void webserver_update()
{
}
namespace {
esp_err_t webserver_root_handler(httpd_req_t *req)
{
std::string body = "hello world";
CALL_AND_EXIT(webserver_resp_send_succ, req, "text/html", body)
}
}

4
main/webserver.h Normal file
View File

@@ -0,0 +1,4 @@
#pragma once
void webserver_setup();
void webserver_update();

70
main/wifi.cpp Normal file
View File

@@ -0,0 +1,70 @@
#include "wifi.h"
// esp-idf includes
#include <esp_log.h>
// 3rdparty lib includes
#include <espwifistack.h>
namespace {
constexpr const char * const TAG = "WIFI";
wifi_stack::config makeWifiConfig();
} // namespace
char deviceName[32] = "ledcontrol";
void wifi_setup()
{
if (const auto result = wifi_stack::get_default_mac_addr())
std::sprintf(deviceName, "ledcontrol_%02hhx%02hhx%02hhx", result->at(3), result->at(4), result->at(5));
else
ESP_LOGE(TAG, "get_default_mac_addr() failed: %.*s", result.error().size(), result.error().data());
ESP_LOGI(TAG, "deviceName = %s", deviceName);
wifi_stack::init(makeWifiConfig());
}
void wifi_update()
{
wifi_stack::update(makeWifiConfig());
}
namespace {
wifi_stack::config makeWifiConfig()
{
return wifi_stack::config {
.wifiEnabled = true,
.hostname = deviceName,
.sta = {
.wifis = std::array<wifi_stack::wifi_entry, 10> {
wifi_stack::wifi_entry { .ssid = "camp++", .key = {} },
wifi_stack::wifi_entry { .ssid = {}, .key = {} },
wifi_stack::wifi_entry { .ssid = {}, .key = {} },
wifi_stack::wifi_entry { .ssid = {}, .key = {} },
wifi_stack::wifi_entry { .ssid = {}, .key = {} },
wifi_stack::wifi_entry { .ssid = {}, .key = {} },
wifi_stack::wifi_entry { .ssid = {}, .key = {} },
wifi_stack::wifi_entry { .ssid = {}, .key = {} },
wifi_stack::wifi_entry { .ssid = {}, .key = {} },
wifi_stack::wifi_entry { .ssid = {}, .key = {} }
},
.min_rssi = -90
},
.ap = {
.ssid = deviceName,
.key = "CampPlusPlus",
.static_ip = {
.ip = {192, 168, 0, 1},
.subnet = {255, 255, 255, 0},
.gateway = {192, 168, 0, 1}
},
.channel = 1,
.authmode = WIFI_AUTH_WPA2_PSK,
.ssid_hidden = false,
.max_connection = 4,
.beacon_interval = 100
}
};
}
}

6
main/wifi.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
extern char deviceName[32];
void wifi_setup();
void wifi_update();

View File

@@ -130,8 +130,8 @@ CONFIG_PARTITION_TABLE_MD5=y
#
# FastLED
#
CONFIG_FASTLED_METHOD_I2S=y
# CONFIG_FASTLED_METHOD_RMT is not set
# CONFIG_FASTLED_METHOD_I2S is not set
CONFIG_FASTLED_METHOD_RMT=y
# end of FastLED
#
@@ -813,7 +813,7 @@ CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y
#
# LWIP
#
CONFIG_LWIP_LOCAL_HOSTNAME="espressif"
CONFIG_LWIP_LOCAL_HOSTNAME="ledcontroller"
CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y
# CONFIG_LWIP_L2_TO_L3_COPY is not set
# CONFIG_LWIP_IRAM_OPTIMIZATION is not set
@@ -1257,6 +1257,14 @@ CONFIG_WPA_MBEDTLS_CRYPTO=y
# CONFIG_WPA_WPS_WARS is not set
# CONFIG_WPA_11KV_SUPPORT is not set
# end of Supplicant
#
# ESP-NimBLE-CPP configuration
#
# CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT is not set
# CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT is not set
# CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT is not set
# end of ESP-NimBLE-CPP configuration
# end of Component config
#