basic led control implemented
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -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
|
||||
|
1
components/esp-nimble-cpp
Submodule
1
components/esp-nimble-cpp
Submodule
Submodule components/esp-nimble-cpp added at 7f853fa04b
@@ -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
69
main/ble.cpp
Normal 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
4
main/ble.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
void ble_setup();
|
||||
void ble_update();
|
159
main/led.cpp
Normal file
159
main/led.cpp
Normal 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
4
main/led.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
void led_setup();
|
||||
void led_update();
|
@@ -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
59
main/webserver.cpp
Normal 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
4
main/webserver.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
void webserver_setup();
|
||||
void webserver_update();
|
70
main/wifi.cpp
Normal file
70
main/wifi.cpp
Normal 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
6
main/wifi.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
extern char deviceName[32];
|
||||
|
||||
void wifi_setup();
|
||||
void wifi_update();
|
14
sdkconfig
14
sdkconfig
@@ -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
|
||||
|
||||
#
|
||||
|
Reference in New Issue
Block a user