add WordClock Features
This commit is contained in:
parent
ebd4b76a85
commit
7dbef0b498
@ -36,6 +36,7 @@ lib_deps =
|
|||||||
ArduinoJson@>=6.0.0,<7.0.0
|
ArduinoJson@>=6.0.0,<7.0.0
|
||||||
ESP Async WebServer@>=1.2.0,<2.0.0
|
ESP Async WebServer@>=1.2.0,<2.0.0
|
||||||
AsyncMqttClient@>=0.8.2,<1.0.0
|
AsyncMqttClient@>=0.8.2,<1.0.0
|
||||||
|
adafruit/Adafruit NeoPixel@^1.7.0
|
||||||
|
|
||||||
[env:esp12e]
|
[env:esp12e]
|
||||||
platform = espressif8266
|
platform = espressif8266
|
||||||
|
159
src/Clock.cpp
Normal file
159
src/Clock.cpp
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
//
|
||||||
|
// Created by lukas on 06.03.21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Clock.h"
|
||||||
|
|
||||||
|
Clock::Clock() : strip(NUMPIXELS, D5, NEO_GRB + NEO_KHZ800), refreshTicker() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clock::init() {
|
||||||
|
strip.begin();
|
||||||
|
strip.clear();
|
||||||
|
strip.show();
|
||||||
|
|
||||||
|
refreshTicker.attach(10, [this]() { this->refreshTime(); });
|
||||||
|
this->refreshTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clock::paintAllwaysOnLeds() {
|
||||||
|
printWord(types::es, Adafruit_NeoPixel::Color(150, 150, 0));
|
||||||
|
printWord(types::ist, Adafruit_NeoPixel::Color(150, 0, 150));
|
||||||
|
printWord(types::uhr, Adafruit_NeoPixel::Color(0, 0, 150));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clock::printWord(std::vector<int> word, uint32_t color) {
|
||||||
|
for (const int i : word) {
|
||||||
|
strip.setPixelColor(i, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clock::refreshTime() {
|
||||||
|
strip.clear();
|
||||||
|
|
||||||
|
// grab the current instant in unix seconds
|
||||||
|
time_t now = time(nullptr);
|
||||||
|
if (now <= 604800) {
|
||||||
|
strip.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tm* loctime = localtime(&now);
|
||||||
|
|
||||||
|
const uint8_t hour = loctime->tm_hour;
|
||||||
|
const uint8_t minute = loctime->tm_min;
|
||||||
|
|
||||||
|
paintAllwaysOnLeds();
|
||||||
|
setTime(hour, minute);
|
||||||
|
strip.show();
|
||||||
|
|
||||||
|
Serial.print("time now: ");
|
||||||
|
Serial.print(hour);
|
||||||
|
Serial.print("h ");
|
||||||
|
Serial.print(minute);
|
||||||
|
Serial.println("M");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clock::setTime(uint8_t hour, uint8_t minute) {
|
||||||
|
const uint8_t minuteselector = minute / 5;
|
||||||
|
|
||||||
|
// if minuteselector >= 4 +1 to hour
|
||||||
|
if (minuteselector >= 4)
|
||||||
|
hour++;
|
||||||
|
|
||||||
|
// convert to 12h format
|
||||||
|
if (hour >= 12)
|
||||||
|
hour = hour - 12;
|
||||||
|
|
||||||
|
std::vector<int> hourWord;
|
||||||
|
|
||||||
|
hourWord = hour == 1 ? types::ein
|
||||||
|
: hour == 2 ? types::zwei
|
||||||
|
: hour == 3 ? types::drei
|
||||||
|
: hour == 4 ? types::vier
|
||||||
|
: hour == 5 ? types::fuenf2
|
||||||
|
: hour == 6 ? types::sechs
|
||||||
|
: hour == 7 ? types::sieben
|
||||||
|
: hour == 8 ? types::acht
|
||||||
|
: hour == 9 ? types::neun
|
||||||
|
: hour == 10 ? types::zehn2
|
||||||
|
: hour == 11 ? types::elf
|
||||||
|
: hour == 0 ? types::zwoelf
|
||||||
|
: hourWord;
|
||||||
|
|
||||||
|
printWord(hourWord, Adafruit_NeoPixel::Color(0, 150, 0));
|
||||||
|
|
||||||
|
// fuenf / zehn / viertl word
|
||||||
|
std::vector<int> minuteWord;
|
||||||
|
if (minuteselector == 1 || minuteselector == 5 || minuteselector == 7 || minuteselector == 11)
|
||||||
|
minuteWord = types::fuenf;
|
||||||
|
else if (minuteselector == 2 || minuteselector == 4 || minuteselector == 8 || minuteselector == 10)
|
||||||
|
minuteWord = types::zehn;
|
||||||
|
else if (minuteselector == 3)
|
||||||
|
minuteWord = types::viertel;
|
||||||
|
else if (minuteselector == 9)
|
||||||
|
minuteWord = types::dreiviertel;
|
||||||
|
else if (minuteselector == 6)
|
||||||
|
minuteWord = types::halb;
|
||||||
|
|
||||||
|
printWord(minuteWord, Adafruit_NeoPixel::Color(0, 150, 0));
|
||||||
|
|
||||||
|
// vor / nach
|
||||||
|
std::vector<int> vornachWord;
|
||||||
|
if (minuteselector == 1 || minuteselector == 2 || minuteselector == 3 || minuteselector == 7 || minuteselector == 8)
|
||||||
|
vornachWord = types::nach;
|
||||||
|
else if (minuteselector == 4 || minuteselector == 5 || minuteselector == 10 || minuteselector == 11)
|
||||||
|
vornachWord = types::vor;
|
||||||
|
printWord(vornachWord, Adafruit_NeoPixel::Color(150, 150, 150));
|
||||||
|
|
||||||
|
// halb
|
||||||
|
if (minuteselector >= 4 && minuteselector <= 8)
|
||||||
|
printWord(types::halb, Adafruit_NeoPixel::Color(150, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<int> types::es = calcPixels(0, 0, 2);
|
||||||
|
const std::vector<int> types::ist = calcPixels(3, 0, 3);
|
||||||
|
const std::vector<int> types::fuenf = calcPixels(8, 0, 4);
|
||||||
|
const std::vector<int> types::zehn = calcPixels(0, 1, 4);
|
||||||
|
const std::vector<int> types::zwanzig = calcPixels(5, 1, 7);
|
||||||
|
const std::vector<int> types::drei = calcPixels(1, 2, 3);
|
||||||
|
const std::vector<int> types::viertel = calcPixels(5, 2, 7);
|
||||||
|
const std::vector<int> types::dreiviertel = calcPixels(1, 2, 11);
|
||||||
|
const std::vector<int> types::vor = calcPixels(0, 3, 3);
|
||||||
|
const std::vector<int> types::nach = calcPixels(3, 3, 4);
|
||||||
|
const std::vector<int> types::halb = calcPixels(8, 3, 4);
|
||||||
|
const std::vector<int> types::zwoelf = calcPixels(0, 4, 5);
|
||||||
|
const std::vector<int> types::sieben = calcPixels(6, 4, 6);
|
||||||
|
const std::vector<int> types::ein = calcPixels(0, 5, 3);
|
||||||
|
const std::vector<int> types::eins = calcPixels(0, 5, 4);
|
||||||
|
const std::vector<int> types::vier = calcPixels(4, 5, 4);
|
||||||
|
const std::vector<int> types::acht = calcPixels(8, 5, 4);
|
||||||
|
const std::vector<int> types::sechs = calcPixels(0, 6, 5);
|
||||||
|
const std::vector<int> types::zwei = calcPixels(6, 6, 5);
|
||||||
|
const std::vector<int> types::fuenf2 = calcPixels(1, 7, 5);
|
||||||
|
const std::vector<int> types::elf = calcPixels(5, 7, 3);
|
||||||
|
const std::vector<int> types::zehn2 = calcPixels(8, 7, 4);
|
||||||
|
const std::vector<int> types::neun = calcPixels(0, 8, 4);
|
||||||
|
const std::vector<int> types::drei2 = calcPixels(4, 8, 4);
|
||||||
|
const std::vector<int> types::ein2 = calcPixels(6, 8, 3);
|
||||||
|
const std::vector<int> types::uhr = calcPixels(9, 8, 3);
|
||||||
|
|
||||||
|
uint8_t types::convert(uint8_t x, uint8_t y) {
|
||||||
|
const bool upRow = (x % 2) == 0;
|
||||||
|
|
||||||
|
int val = x * 9 + y;
|
||||||
|
// if its a row upwards we need to calculate the additional down and up pixels
|
||||||
|
if (upRow)
|
||||||
|
// we need to go the rest down (9 -y) and up (*2) and subtract the too many added 10
|
||||||
|
val += ((9 - y) * 2) - 10;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> types::calcPixels(uint8_t x, uint8_t y, uint8_t nrPixels) {
|
||||||
|
std::vector<int> vec;
|
||||||
|
for (uint8_t i = 0; i < nrPixels; i++) {
|
||||||
|
vec.push_back(convert(x + i, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
return vec;
|
||||||
|
}
|
48
src/Clock.h
Normal file
48
src/Clock.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
//
|
||||||
|
// Created by lukas on 06.03.21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LEDSTRIPINTERFACE_CLOCK_H
|
||||||
|
#define LEDSTRIPINTERFACE_CLOCK_H
|
||||||
|
|
||||||
|
#include <Ticker.h>
|
||||||
|
#include "Adafruit_NeoPixel.h"
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "../.pio/libdeps/node32s/Adafruit NeoPixel/Adafruit_NeoPixel.h"
|
||||||
|
|
||||||
|
#define NUMPIXELS 108 // Popular NeoPixel ring size
|
||||||
|
|
||||||
|
class Clock {
|
||||||
|
private:
|
||||||
|
Adafruit_NeoPixel strip{};
|
||||||
|
|
||||||
|
void paintAllwaysOnLeds();
|
||||||
|
void printWord(std::vector<int> word, uint32_t color);
|
||||||
|
void setTime(uint8_t hour, uint8_t minute);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Clock();
|
||||||
|
void init();
|
||||||
|
void refreshTime();
|
||||||
|
|
||||||
|
Ticker refreshTicker;
|
||||||
|
};
|
||||||
|
struct types {
|
||||||
|
static const std::vector<int>
|
||||||
|
es, ist, fuenf,
|
||||||
|
zehn, zwanzig, drei,
|
||||||
|
viertel, dreiviertel, vor,
|
||||||
|
nach, halb, zwoelf,
|
||||||
|
sieben, ein, eins,
|
||||||
|
vier, acht, sechs,
|
||||||
|
zwei, fuenf2, elf,
|
||||||
|
zehn2, neun, drei2,
|
||||||
|
ein2, uhr;
|
||||||
|
|
||||||
|
static uint8_t convert(uint8_t x, uint8_t y);
|
||||||
|
|
||||||
|
static std::vector<int> calcPixels(uint8_t x, uint8_t y, uint8_t nrPixels);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // LEDSTRIPINTERFACE_CLOCK_H
|
38
src/ClockService.cpp
Normal file
38
src/ClockService.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
//
|
||||||
|
// Created by lukas on 06.03.21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "ClockService.h"
|
||||||
|
|
||||||
|
ClockService::ClockService(AsyncWebServer* server, SecurityManager* securityManager) :
|
||||||
|
_httpEndpoint(LightState::read,
|
||||||
|
LightState::update,
|
||||||
|
this,
|
||||||
|
server,
|
||||||
|
LIGHT_SETTINGS_ENDPOINT_PATH,
|
||||||
|
securityManager,
|
||||||
|
AuthenticationPredicates::IS_AUTHENTICATED),
|
||||||
|
_webSocket(LightState::read,
|
||||||
|
LightState::update,
|
||||||
|
this,
|
||||||
|
server,
|
||||||
|
LIGHT_SETTINGS_SOCKET_PATH,
|
||||||
|
securityManager,
|
||||||
|
AuthenticationPredicates::IS_AUTHENTICATED),
|
||||||
|
clock() {
|
||||||
|
// configure led to be output
|
||||||
|
pinMode(LED_PIN, OUTPUT);
|
||||||
|
|
||||||
|
// configure settings service update handler to update LED state
|
||||||
|
addUpdateHandler([&](const String& originId) { onConfigUpdated(); }, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClockService::begin() {
|
||||||
|
_state.ledOn = DEFAULT_LED_STATE;
|
||||||
|
onConfigUpdated();
|
||||||
|
clock.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClockService::onConfigUpdated() {
|
||||||
|
digitalWrite(LED_PIN, _state.ledOn ? LED_ON : LED_OFF);
|
||||||
|
}
|
@ -1,11 +1,13 @@
|
|||||||
#ifndef LightStateService_h
|
//
|
||||||
#define LightStateService_h
|
// Created by lukas on 06.03.21.
|
||||||
|
//
|
||||||
|
|
||||||
#include <LightMqttSettingsService.h>
|
#pragma once
|
||||||
|
|
||||||
#include <HttpEndpoint.h>
|
#include <HttpEndpoint.h>
|
||||||
#include <MqttPubSub.h>
|
#include <MqttPubSub.h>
|
||||||
#include <WebSocketTxRx.h>
|
#include <WebSocketTxRx.h>
|
||||||
|
#include "Clock.h"
|
||||||
|
|
||||||
#define LED_PIN 2
|
#define LED_PIN 2
|
||||||
#define PRINT_DELAY 5000
|
#define PRINT_DELAY 5000
|
||||||
@ -66,23 +68,17 @@ class LightState {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class LightStateService : public StatefulService<LightState> {
|
class ClockService : public StatefulService<LightState> {
|
||||||
public:
|
public:
|
||||||
LightStateService(AsyncWebServer* server,
|
ClockService(AsyncWebServer* server, SecurityManager* securityManager);
|
||||||
SecurityManager* securityManager,
|
|
||||||
AsyncMqttClient* mqttClient,
|
|
||||||
LightMqttSettingsService* lightMqttSettingsService);
|
|
||||||
void begin();
|
void begin();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HttpEndpoint<LightState> _httpEndpoint;
|
HttpEndpoint<LightState> _httpEndpoint;
|
||||||
MqttPubSub<LightState> _mqttPubSub;
|
|
||||||
WebSocketTxRx<LightState> _webSocket;
|
WebSocketTxRx<LightState> _webSocket;
|
||||||
AsyncMqttClient* _mqttClient;
|
|
||||||
LightMqttSettingsService* _lightMqttSettingsService;
|
|
||||||
|
|
||||||
void registerConfig();
|
Clock clock;
|
||||||
|
|
||||||
void onConfigUpdated();
|
void onConfigUpdated();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
|
@ -1,16 +0,0 @@
|
|||||||
#include <LightMqttSettingsService.h>
|
|
||||||
|
|
||||||
LightMqttSettingsService::LightMqttSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) :
|
|
||||||
_httpEndpoint(LightMqttSettings::read,
|
|
||||||
LightMqttSettings::update,
|
|
||||||
this,
|
|
||||||
server,
|
|
||||||
LIGHT_BROKER_SETTINGS_PATH,
|
|
||||||
securityManager,
|
|
||||||
AuthenticationPredicates::IS_AUTHENTICATED),
|
|
||||||
_fsPersistence(LightMqttSettings::read, LightMqttSettings::update, this, fs, LIGHT_BROKER_SETTINGS_FILE) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightMqttSettingsService::begin() {
|
|
||||||
_fsPersistence.readFromFS();
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
#ifndef LightMqttSettingsService_h
|
|
||||||
#define LightMqttSettingsService_h
|
|
||||||
|
|
||||||
#include <HttpEndpoint.h>
|
|
||||||
#include <FSPersistence.h>
|
|
||||||
#include <SettingValue.h>
|
|
||||||
|
|
||||||
#define LIGHT_BROKER_SETTINGS_FILE "/config/brokerSettings.json"
|
|
||||||
#define LIGHT_BROKER_SETTINGS_PATH "/rest/brokerSettings"
|
|
||||||
|
|
||||||
class LightMqttSettings {
|
|
||||||
public:
|
|
||||||
String mqttPath;
|
|
||||||
String name;
|
|
||||||
String uniqueId;
|
|
||||||
|
|
||||||
static void read(LightMqttSettings& settings, JsonObject& root) {
|
|
||||||
root["mqtt_path"] = settings.mqttPath;
|
|
||||||
root["name"] = settings.name;
|
|
||||||
root["unique_id"] = settings.uniqueId;
|
|
||||||
}
|
|
||||||
|
|
||||||
static StateUpdateResult update(JsonObject& root, LightMqttSettings& settings) {
|
|
||||||
settings.mqttPath = root["mqtt_path"] | SettingValue::format("homeassistant/light/#{unique_id}");
|
|
||||||
settings.name = root["name"] | SettingValue::format("light-#{unique_id}");
|
|
||||||
settings.uniqueId = root["unique_id"] | SettingValue::format("light-#{unique_id}");
|
|
||||||
return StateUpdateResult::CHANGED;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class LightMqttSettingsService : public StatefulService<LightMqttSettings> {
|
|
||||||
public:
|
|
||||||
LightMqttSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager);
|
|
||||||
void begin();
|
|
||||||
|
|
||||||
private:
|
|
||||||
HttpEndpoint<LightMqttSettings> _httpEndpoint;
|
|
||||||
FSPersistence<LightMqttSettings> _fsPersistence;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // end LightMqttSettingsService_h
|
|
@ -1,73 +0,0 @@
|
|||||||
#include <LightStateService.h>
|
|
||||||
|
|
||||||
LightStateService::LightStateService(AsyncWebServer* server,
|
|
||||||
SecurityManager* securityManager,
|
|
||||||
AsyncMqttClient* mqttClient,
|
|
||||||
LightMqttSettingsService* lightMqttSettingsService) :
|
|
||||||
_httpEndpoint(LightState::read,
|
|
||||||
LightState::update,
|
|
||||||
this,
|
|
||||||
server,
|
|
||||||
LIGHT_SETTINGS_ENDPOINT_PATH,
|
|
||||||
securityManager,
|
|
||||||
AuthenticationPredicates::IS_AUTHENTICATED),
|
|
||||||
_mqttPubSub(LightState::haRead, LightState::haUpdate, this, mqttClient),
|
|
||||||
_webSocket(LightState::read,
|
|
||||||
LightState::update,
|
|
||||||
this,
|
|
||||||
server,
|
|
||||||
LIGHT_SETTINGS_SOCKET_PATH,
|
|
||||||
securityManager,
|
|
||||||
AuthenticationPredicates::IS_AUTHENTICATED),
|
|
||||||
_mqttClient(mqttClient),
|
|
||||||
_lightMqttSettingsService(lightMqttSettingsService) {
|
|
||||||
// configure led to be output
|
|
||||||
pinMode(LED_PIN, OUTPUT);
|
|
||||||
|
|
||||||
// configure MQTT callback
|
|
||||||
_mqttClient->onConnect(std::bind(&LightStateService::registerConfig, this));
|
|
||||||
|
|
||||||
// configure update handler for when the light settings change
|
|
||||||
_lightMqttSettingsService->addUpdateHandler([&](const String& originId) { registerConfig(); }, false);
|
|
||||||
|
|
||||||
// configure settings service update handler to update LED state
|
|
||||||
addUpdateHandler([&](const String& originId) { onConfigUpdated(); }, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightStateService::begin() {
|
|
||||||
_state.ledOn = DEFAULT_LED_STATE;
|
|
||||||
onConfigUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightStateService::onConfigUpdated() {
|
|
||||||
digitalWrite(LED_PIN, _state.ledOn ? LED_ON : LED_OFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightStateService::registerConfig() {
|
|
||||||
if (!_mqttClient->connected()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String configTopic;
|
|
||||||
String subTopic;
|
|
||||||
String pubTopic;
|
|
||||||
|
|
||||||
DynamicJsonDocument doc(256);
|
|
||||||
_lightMqttSettingsService->read([&](LightMqttSettings& settings) {
|
|
||||||
configTopic = settings.mqttPath + "/config";
|
|
||||||
subTopic = settings.mqttPath + "/set";
|
|
||||||
pubTopic = settings.mqttPath + "/state";
|
|
||||||
doc["~"] = settings.mqttPath;
|
|
||||||
doc["name"] = settings.name;
|
|
||||||
doc["unique_id"] = settings.uniqueId;
|
|
||||||
});
|
|
||||||
doc["cmd_t"] = "~/set";
|
|
||||||
doc["stat_t"] = "~/state";
|
|
||||||
doc["schema"] = "json";
|
|
||||||
doc["brightness"] = false;
|
|
||||||
|
|
||||||
String payload;
|
|
||||||
serializeJson(doc, payload);
|
|
||||||
_mqttClient->publish(configTopic.c_str(), 0, false, payload.c_str());
|
|
||||||
|
|
||||||
_mqttPubSub.configureTopics(pubTopic, subTopic);
|
|
||||||
}
|
|
16
src/main.cpp
16
src/main.cpp
@ -1,17 +1,12 @@
|
|||||||
#include <ESP8266React.h>
|
#include <ESP8266React.h>
|
||||||
#include <LightMqttSettingsService.h>
|
#include "ClockService.h"
|
||||||
#include <LightStateService.h>
|
|
||||||
|
|
||||||
#define SERIAL_BAUD_RATE 115200
|
#define SERIAL_BAUD_RATE 115200
|
||||||
|
|
||||||
AsyncWebServer server(80);
|
AsyncWebServer server(80);
|
||||||
ESP8266React esp8266React(&server);
|
ESP8266React esp8266React(&server);
|
||||||
LightMqttSettingsService lightMqttSettingsService =
|
|
||||||
LightMqttSettingsService(&server, esp8266React.getFS(), esp8266React.getSecurityManager());
|
ClockService cservice = ClockService(&server, esp8266React.getSecurityManager());
|
||||||
LightStateService lightStateService = LightStateService(&server,
|
|
||||||
esp8266React.getSecurityManager(),
|
|
||||||
esp8266React.getMqttClient(),
|
|
||||||
&lightMqttSettingsService);
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
// start serial and filesystem
|
// start serial and filesystem
|
||||||
@ -21,10 +16,7 @@ void setup() {
|
|||||||
esp8266React.begin();
|
esp8266React.begin();
|
||||||
|
|
||||||
// load the initial light settings
|
// load the initial light settings
|
||||||
lightStateService.begin();
|
cservice.begin();
|
||||||
|
|
||||||
// start the light service
|
|
||||||
lightMqttSettingsService.begin();
|
|
||||||
|
|
||||||
// start the server
|
// start the server
|
||||||
server.begin();
|
server.begin();
|
||||||
|
Loading…
Reference in New Issue
Block a user