Skip to content

Commit

Permalink
Migrate to the new esp-iot-framework version
Browse files Browse the repository at this point in the history
  • Loading branch information
DrA1ex committed Sep 22, 2024
1 parent 3507d5d commit 9074d98
Show file tree
Hide file tree
Showing 36 changed files with 775 additions and 654 deletions.
1 change: 0 additions & 1 deletion .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,5 @@ CheckOptions:
readability-identifier-naming.PublicMemberCase: lower_case
readability-identifier-naming.ProtectedMemberCase: lower_case
readability-identifier-naming.PrivateMemberCase: lower_case
readability-identifier-naming.ClassMemberPrefix:
readability-identifier-naming.PrivateMemberPrefix: _
readability-identifier-naming.PrivateMethodPrefix: _
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pio run -t upload -e $PLATFORM-$ENV --upload-port "$ADDRESS"
| Endpoint | Method | Parameters | Response | Description |
|----------------------|-----------|--------------------------|-----------------------------------------------------------|---------------------------------------------------------|
| `/api/status` | `GET` | None | `{"status": "ok", "value": number, "brightness": number}` | Retrieves the current power and brightness values. |
| `/api/power` | `GET` | `value` (1 or 0) | {"status": "ok"} | Sets the power state (on/off). |
| `/api/power` | `GET` | `value` (1 or 0) | {"status": "ok"} | Sets the power _state (on/off). |
| `/api/brightness` | `GET` | `value` (0-100) | {"status": "ok"} | Updates the brightness level. |
| `/api/debug` | `GET` | None | Plain Text | Provides debugging information. |
| `/api/restart` | `GET` | None | Plain Text: "OK" | Restarts the server and saves configuration. |
Expand All @@ -86,10 +86,10 @@ pio run -t upload -e $PLATFORM-$ENV --upload-port "$ADDRESS"

| Topic In * | Topic Out * | Type | Values | Comments |
|---------------------------|-------------------------------|-------------|----------------------|---------------------------------------|
| `MQTT_TOPIC_POWER` | `MQTT_OUT_TOPIC_POWER` | `uint8_t` | 0..1 | Power state: ON (1) / OFF (0) |
| `MQTT_TOPIC_BRIGHTNESS` | `MQTT_OUT_TOPIC_BRIGHTNESS` | `uint16_t` | 0..`DAC_MAX_VALUE` | Brightness level, can switch to 0..100 (`MQTT_CONVERT_BRIGHTNESS`) |
| `MQTT_TOPIC_POWER` | `MQTT_OUT_TOPIC_POWER` | `uint8_t` | 0..1 | Power _state: ON (1) / OFF (0) |
| `MQTT_TOPIC_BRIGHTNESS` | `MQTT_OUT_TOPIC_BRIGHTNESS` | `uint16_t` | 0..`PWM_MAX_VALUE` | Brightness level, can switch to 0..100 (`MQTT_CONVERT_BRIGHTNESS`) |
| `MQTT_TOPIC_COLOR` | `MQTT_OUT_TOPIC_COLOR` | `uint32_t` | 0..0xFFFFFF | Color value (ARGB or RGB format) |
| `MQTT_TOPIC_NIGHT_MODE` | `MQTT_OUT_TOPIC_NIGHT_MODE` | `uint8_t` | 0..1 | Night mode state: ON (1) / OFF (0) |
| `MQTT_TOPIC_NIGHT_MODE` | `MQTT_OUT_TOPIC_NIGHT_MODE` | `uint8_t` | 0..1 | Night mode _state: ON (1) / OFF (0) |

\* Actual topic values decalred in `constants.h`

Expand Down
2 changes: 1 addition & 1 deletion README_RU.MD
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pio run -t upload -e $PLATFORM-$ENV --upload-port "$ADDRESS"
| Топик Команд * | Топик Уведомлений * | Тип | Значения | Комментарии |
|------------------------|---------------------------|-------------|-----------------------|--------------------------------------|
| `MQTT_TOPIC_POWER` | `MQTT_OUT_TOPIC_POWER` | `uint8_t` | 0..1 | Состояние питания: ВКЛ (1) / ВЫКЛ (0) |
| `MQTT_TOPIC_BRIGHTNESS`| `MQTT_OUT_TOPIC_BRIGHTNESS`| `uint16_t` | 0..`DAC_MAX_VALUE` | Уровень яркости, можно переключать на диапазон 0..100 (`MQTT_CONVERT_BRIGHTNESS`) |
| `MQTT_TOPIC_BRIGHTNESS`| `MQTT_OUT_TOPIC_BRIGHTNESS`| `uint16_t` | 0..`PWM_MAX_VALUE` | Уровень яркости, можно переключать на диапазон 0..100 (`MQTT_CONVERT_BRIGHTNESS`) |
| `MQTT_TOPIC_COLOR` | `MQTT_OUT_TOPIC_COLOR` | `uint32_t` | 0..0xFFFFFF | Значение цвета (формат RGB) |
| `MQTT_TOPIC_NIGHT_MODE`| `MQTT_OUT_TOPIC_NIGHT_MODE`| `uint8_t` | 0..1 | Состояние ночного режима: ВКЛ (1) / ВЫКЛ (0) |

Expand Down
2 changes: 1 addition & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
framework = arduino
board_build.f_cpu = 160000000L
board_build.filesystem = littlefs
monitor_speed = 74880
monitor_speed = 115200

lib_deps =
me-no-dev/ESPAsyncTCP@^1.2.2
Expand Down
249 changes: 174 additions & 75 deletions src/app/application.cpp
Original file line number Diff line number Diff line change
@@ -1,124 +1,223 @@
#include "application.h"
#include "lib/utils/meta.h"
#include "utils/math.h"

void Application::begin() {
_rgb_mode = sys_config().rgb_mode;
if (_rgb_mode) {
pinMode(sys_config().led_r_pin, OUTPUT);
pinMode(sys_config().led_g_pin, OUTPUT);
pinMode(sys_config().led_b_pin, OUTPUT);
D_PRINT("Starting application...");

if (!LittleFS.begin()) {
D_PRINT("Unable to initialize FS");
}

_bootstrap = std::make_unique<Bootstrap<Config, PacketType>>(&LittleFS);

auto &sys_config = _bootstrap->config().sys_config;
_bootstrap->begin({
.mdns_name = sys_config.mdns_name,
.wifi_mode = sys_config.wifi_mode,
.wifi_ssid = sys_config.wifi_ssid,
.wifi_password = sys_config.wifi_password,
.wifi_connection_timeout = sys_config.wifi_max_connection_attempt_interval,
.mqtt_enabled = sys_config.mqtt,
.mqtt_host = sys_config.mqtt_host,
.mqtt_port = sys_config.mqtt_port,
.mqtt_user = sys_config.mqtt_user,
.mqtt_password = sys_config.mqtt_password,
});

if (sys_config.rgb_mode) {
_led = std::make_unique<LedController>(
sys_config.led_r_pin, sys_config.led_g_pin, sys_config.led_b_pin);
} else {
pinMode(sys_config().led_pin, OUTPUT);
_led = std::make_unique<LedController>(sys_config.led_r_pin);
}

event_property_changed().subscribe(this,
[this](auto sender, auto type, auto) {
if (sender != this) _handle_property_change(type);
});
_led->begin();
_night_mode_manager = std::make_unique<NightModeManager>(_bootstrap->config());
_ntp_time = std::make_unique<NtpTime>();

load();
_api = std::make_unique<ApiWebServer>(*this);
_api->begin(_bootstrap->web_server());

_bootstrap->event_state_changed().subscribe(this, [this](auto sender, auto state, auto arg) {
_bootstrap_state_changed(sender, state, arg);
});
_bootstrap->timer().add_interval([this](auto) { _app_loop(); }, APP_LOOP_INTERVAL);

_setup();
change_state(AppState::INITIALIZATION);
}

void Application::_setup() {
NotificationBus::get().subscribe([this](auto sender, auto param) {
if (sender != this) _handle_property_change(param);
});

auto &ws_server = _bootstrap->ws_server();
auto &mqtt_server = _bootstrap->mqtt_server();

_metadata = std::make_unique<ConfigMetadata>(build_metadata(config()));
_metadata->visit([this, &ws_server, &mqtt_server](AbstractPropertyMeta *meta) {
auto binary_protocol = (BinaryProtocolMeta<PacketType> *) meta->get_binary_protocol();
if (binary_protocol->packet_type.has_value()) {
ws_server->register_parameter(*binary_protocol->packet_type, meta->get_parameter());
VERBOSE(D_PRINTF("WebSocket: Register property %s\r\n", __debug_enum_str(*binary_protocol->packet_type)));
}

auto mqtt_protocol = meta->get_mqtt_protocol();
if (mqtt_protocol->topic_in && mqtt_protocol->topic_out) {
mqtt_server->register_parameter(mqtt_protocol->topic_in, mqtt_protocol->topic_out, meta->get_parameter());
VERBOSE(D_PRINTF("MQTT: Register property %s <-> %s\r\n", mqtt_protocol->topic_in, mqtt_protocol->topic_out));
} else if (mqtt_protocol->topic_out) {
mqtt_server->register_notification(mqtt_protocol->topic_out, meta->get_parameter());
VERBOSE(D_PRINTF("MQTT: Register notification -> %s\r\n", mqtt_protocol->topic_out));
}

if (binary_protocol->packet_type.has_value()) {
_parameter_to_packet[meta->get_parameter()] = binary_protocol->packet_type.value();
}
});

ws_server->register_data_request(PacketType::GET_CONFIG, _metadata->data.config);
ws_server->register_command(PacketType::RESTART, [this] { _bootstrap->restart(); });
}

void Application::_handle_property_change(NotificationProperty type) {
if (type == NotificationProperty::POWER) {
void Application::event_loop() {
_bootstrap->event_loop();
}

void Application::_handle_property_change(const AbstractParameter *parameter) {
auto it = _parameter_to_packet.find(parameter);
if (it == _parameter_to_packet.end()) return;

auto type = it->second;
if (type == PacketType::POWER) {
set_power(config().power);
} else if (type >= NotificationProperty::NIGHT_MODE_ENABLED && type <= NotificationProperty::NIGHT_MODE_BRIGHTNESS) {
_night_mode_manager.reset();
} else if (type >= PacketType::NIGHT_MODE_ENABLED && type <= PacketType::NIGHT_MODE_BRIGHTNESS) {
_night_mode_manager->reset();
update();
} else {
update();
}
}

void Application::load() {
if (_rgb_mode) _load_calibration();
set_brightness(config().power ? brightness() : PIN_DISABLED);
}
_led->set_brightness(config().power ? config().brightness : PIN_DISABLED);

void Application::_load_calibration() {
_color_r = _convert_color(config().color, config().calibration, 16);
_color_g = _convert_color(config().color, config().calibration, 8);
_color_b = _convert_color(config().color, config().calibration, 0);
if (_led->rgb_mode()) {
_led->set_calibration(config().calibration);
_led->set_color(config().color);
}
}

void Application::update() {
_config_storage.save();
_bootstrap->save_changes();
load();
}

void Application::change_state(AppState s) {
state_change_time = millis();
state = s;
D_PRINTF("Change app state: %u\n", (uint8_t) s);
_state_change_time = millis();
_state = s;
D_PRINTF("Change app state: %s\r\n", __debug_enum_str(s));
}

void Application::set_power(bool on) {
config().power = on;

D_PRINTF("Turning Power: %s\n", on ? "ON" : "OFF");
if (state != AppState::INITIALIZATION) {
D_PRINTF("Turning Power: %s\r\n", on ? "ON" : "OFF");
if (_state != AppState::INITIALIZATION) {
change_state(on ? AppState::TURNING_ON : AppState::TURNING_OFF);
}

_config_storage.save();
_bootstrap->save_changes();
}

void Application::restart() {
D_PRINTF("Received restart signal. Restarting after %u ms.\r\n", RESTART_DELAY);

if (_config_storage.is_pending_commit()) _config_storage.force_save();

_timer.add_timeout([](auto) { ESP.restart(); }, RESTART_DELAY);
}

void Application::set_brightness(uint16_t value) {
auto brightness = DAC_MAX_VALUE - (uint16_t) floor(
log10f(10 - (float) value * 9 / DAC_MAX_VALUE) * DAC_MAX_VALUE);

if (_rgb_mode) {
_apply_rgb_brightness(brightness);
} else {
analogWrite(sys_config().led_pin, brightness);
}
}

void Application::_apply_rgb_brightness(uint16_t brightness) {
analogWrite(sys_config().led_r_pin, static_cast<uint32_t>(_color_r * brightness / DAC_MAX_VALUE));
analogWrite(sys_config().led_g_pin, static_cast<uint32_t>(_color_g * brightness / DAC_MAX_VALUE));
analogWrite(sys_config().led_b_pin, static_cast<uint32_t>(_color_b * brightness / DAC_MAX_VALUE));
}

uint16_t Application::brightness() {
uint16_t Application::_brightness() {
uint16_t result;
if (_night_mode_manager.is_night_time()) {
result = _night_mode_manager.get_brightness();
if (_night_mode_manager->is_night_time()) {
result = _night_mode_manager->get_brightness();
} else {
result = std::max(sys_config().led_min_brightness, config().brightness);
}


return std::min(DAC_MAX_VALUE, result);
return std::min(PWM_MAX_VALUE, result);
}

uint16_t Application::_convert_color(uint32_t color_data, uint32_t calibration_data, uint8_t bit) {
uint8_t color = (color_data >> bit) & 0xff;
uint8_t calibration = (calibration_data >> bit) & 0xff;

uint8_t calibrated_color = (uint16_t) color * calibration / 255;
return map16(_apply_gamma(calibrated_color), 255, DAC_MAX_VALUE);
void Application::_app_loop() {
#if defined(DEBUG) && DEBUG_LEVEL <= __DEBUG_LEVEL_VERBOSE
static unsigned long t = 0;
static unsigned long ii = 0;
if (ii % 10 == 0) D_PRINTF("Animation latency: %lu\r\n", millis() - t);

t = millis();
++ii;
#endif

switch (_state) {
case AppState::UNINITIALIZED:
break;

case AppState::INITIALIZATION: {
if (config().power) {
const auto factor = map16(
(millis() - _state_change_time) % sys_config().wifi_connect_flash_timeout,
sys_config().wifi_connect_flash_timeout,
PWM_MAX_VALUE
);

uint16_t brightness = _brightness() * cubic_wave16(factor, PWM_MAX_VALUE) / PWM_MAX_VALUE;
_led->set_brightness(brightness);
}
}
break;

case AppState::TURNING_ON: {
uint16_t factor = std::min<unsigned long>(PWM_MAX_VALUE,
(millis() - _state_change_time) * PWM_MAX_VALUE / sys_config().power_change_timeout);
uint16_t brightness = (uint16_t) _brightness() * ease_cubic16(factor, PWM_MAX_VALUE) / PWM_MAX_VALUE;
_led->set_brightness(brightness);

if (factor == PWM_MAX_VALUE) change_state(AppState::STAND_BY);
break;
}

case AppState::TURNING_OFF: {
uint16_t factor = PWM_MAX_VALUE - std::min<unsigned long>(PWM_MAX_VALUE,
(millis() - _state_change_time) * PWM_MAX_VALUE / sys_config().power_change_timeout);
uint16_t brightness = (uint16_t) _brightness() * ease_cubic16(factor, PWM_MAX_VALUE) / PWM_MAX_VALUE;
_led->set_brightness(brightness);

if (factor == 0) change_state(AppState::STAND_BY);
break;
}

case AppState::STAND_BY:
if (config().power && _night_mode_manager->is_night_time()) {
auto brightness = _night_mode_manager->get_brightness();
_led->set_brightness(brightness);
}
break;
}
}

uint8_t Application::_apply_gamma(uint8_t color, float gamma) {
if (gamma == 1.0f) return color;
void Application::_service_loop() {
_ntp_time->update();
_night_mode_manager->handle_night(*_ntp_time);
}

auto orig = (float) color / 255.0f;
auto adj = pow(orig, gamma) * 255.0f;
void Application::_bootstrap_state_changed(void *sender, BootstrapState state, void *arg) {
if (state == BootstrapState::INITIALIZING) {
_ntp_time->begin(TIME_ZONE);

auto result = (uint8_t) adj;
change_state(AppState::INITIALIZATION);
load();
} else if (state == BootstrapState::READY && !_initialized) {
_initialized = true;

// Avoid gamma-adjusting a positive number to zero
if (color > 0 && result == 0) return 1;
change_state(AppState::STAND_BY);
load();

return result;
_bootstrap->timer().add_interval([this](auto) {
_ntp_time->update();
_night_mode_manager->handle_night(*_ntp_time);
}, BOOTSTRAP_SERVICE_LOOP_INTERVAL);
}
}
Loading

0 comments on commit 9074d98

Please sign in to comment.