Skip to content

Instantly share code, notes, and snippets.

@JoeKarlsson
Created March 25, 2019 23:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save JoeKarlsson/c3ef576871821339e8ba283482989117 to your computer and use it in GitHub Desktop.
Save JoeKarlsson/c3ef576871821339e8ba283482989117 to your computer and use it in GitHub Desktop.
Moodlite Arduino Backup: https://moodlite.co.uk/
/*
Name: Moodlite.ino
Created: 27.12.2018
Version: 2.0
AuthorS: Spigot (M.V.), Shiryou & Steve Wagg aka CdRsKuLL
License: This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License v3.0 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/gpl.html>
Support: Wether you use this project, have learned something from it, or just like it, please consider supporting us on our web site. :)
Web page: http://moodlite.co.uk
Notes: Go to http://192.168.100.250 in a web browser connected to this access point to see it
*/
// ------------------ Basic settings ------------------
#define FASTLED_INTERRUPT_RETRY_COUNT 20 // Use this to determine how many times FastLED will attempt to re-transmit a frame if interrupted
#define STANDBY_LED_BRIGHTNESS 2 // Define Standby mode LED brightness
#define DEBUG_ON true // Flag to turn On/Off debugging [True = On, False = Off]
#define STATIC_IP false // Flag to use static IP [True = Static IP, False = DHCP - AP mode]
#define RESET_BUTTON false // Flag to use hardware reset button [True = Use HW button, False = Don't use HW button]
#define NTP_ON false // Flag to use NTP Server [True = Use NTP server, False = Don't use NTP server]
#define MQTT_ON false // Flag to use MQTT [True = Use MQTT server, False = Don't use MQTT server]
#define OTA_ON true // Flag to use OTA updates [True = Use OTA updates, False = Don't use OTA updates]
#define PIR_ON false // Flag to use PIR sensor [True = Use PIR sensor, False = Don't use PIR sensor]
#define ESP8266 true // Flag to use ESP8266
//#define ESP32 true // Flag to use ESP32
// ----------------------------------------------------
// ------------------- Libraries ----------------------
// LED Strip
#include "FastLED.h"
#if ESP8266 // ESP8266
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#elif ESP32 // ESP32
#include <WiFi.h>
#include <ESPmDNS.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "SPIFFS.h"
#define FORMAT_SPIFFS_IF_FAILED true
#endif
// EEPROM
#include <EEPROM.h>
// SPIFFS
#include <SPIFFSEditor.h>
// Time
#include <TimeLib.h>
#include <Timezone.h>
// MQTT
#include <PubSubClient.h>
// OTA
#include <ArduinoOTA.h>
// Other
#include "Timer.h"
#include <base64.h>
#include <string>
// ----------------------------------------------------
// -------------------- Program -----------------------
// --- Debugging ---
#define Serial if (DEBUG_ON) Serial
#if DEBUG_ON
#define DEBUG(x) Serial.print(x)
#define DEBUGLN(x) Serial.println(x)
#define DEBUGF(x, y) Serial.printf(x, y)
#else
#define DEBUG(x)
#define DEBUGLN(x)
#endif
// --- WiFi IP Settings ---
#if STATIC_IP
// Static IP
IPAddress ipaWifiIP(192, 168, 100, 250);
IPAddress ipaWifiSubnet(255, 255, 255, 0);
IPAddress ipaWifiGateway(192, 168, 100, 1);
// If you don't wish to use DNS leave "ipaWifiDns" variable as it is, otherwise set yours DNS or Google DNS (8.8.8.8)
IPAddress ipaWifiDns(0, 0, 0, 0);
// Configure:
// cHostName, cWifiSsid, cWifiPassword are 20 chars long, the last char is ending of char array
char cHostName[21] = "Moodlite";
char cWifiSsid[21] = "....";
char cWifiPassword[21] = "....";
#else
// WiFi AP (DHCP)
IPAddress ipaWifiApIP(192, 168, 100, 250);
IPAddress ipaWifiApSubnet(255, 255, 255, 0);
IPAddress ipaWifiApGateway(192, 168, 100, 1);
// Leave unconfigured:
// cHostName, cWifiSsid, cWifiPassword are 20 chars long, the last char is ending of char array
char cHostName[21] = "Moodlite";
char cWifiSsid[21] = "";
char cWifiPassword[21] = "";
// AP Password
// ! Must be at least 8 chars long
const char* WIFI_AP_PASSWORD = "Moodlite";
#endif
// --- Reset button ---
#if RESET_BUTTON
#define RST_BTN_PIN D6
#endif
// --- RGB LED ---
#define LED_PIN 3 // NodeMCU PIN s
//#define LED_PIN D5 // Wemos PIN D1
// LEDs settings
#define MAX_NUM_LEDS 253
#define NUM_LEDS 253
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
// --- PIR sensor ---
#if PIR_ON
#define PIR_PIN D7
#endif
// --- EEPROM data ---
#define SSID_START 0x0
#define SSID_MAX 0x15
#define PASSWORD_START (SSID_START + SSID_MAX)
#define PASSWORD_MAX 0x15
#define HOSTNAME_START (PASSWORD_START + PASSWORD_MAX)
#define HOSTNAME_MAX 0x15
#define LED_BRIGHTNESS_START (HOSTNAME_START + HOSTNAME_MAX)
#define LED_BRIGHTNESS_MAX 0x1
#define BACKLIGHT_START (LED_BRIGHTNESS_START + LED_BRIGHTNESS_MAX)
#define BACKLIGHT_MAX 0x1
#define LED_COLOR_START (BACKLIGHT_START + BACKLIGHT_MAX)
#define LED_COLOR_MAX 0x6
#define AUTDISPLAYON_START (LED_COLOR_START + LED_COLOR_MAX)
#define AUTDISPLAYON_MAX 0x1
#define DISPLAYON_START (AUTDISPLAYON_START + AUTDISPLAYON_MAX)
#define DISPLAYON_MAX 0x1
#define DISPLAYOFF_START (DISPLAYON_START + DISPLAYON_MAX)
#define DISPLAYOFF_MAX 0x1
#define LED_SPEED_START (DISPLAYOFF_START + DISPLAYOFF_MAX)
#define LED_SPEED_MAX 0x1
#define LED_PATTERN_START (LED_SPEED_START + LED_SPEED_MAX)
#define LED_PATTERN_MAX 0x1
#define NUMBER_OF_LEDS_START (LED_PATTERN_START + LED_PATTERN_MAX)
#define NUMBER_OF_LEDS_MAX 0x3
#define NTP_SERVER_START (NUMBER_OF_LEDS_START + NUMBER_OF_LEDS_MAX)
#define NTP_SERVER_MAX 0x20
#define NUMBER_OF_LEDS_CORNER_START (NTP_SERVER_START + NTP_SERVER_MAX)
#define NUMBER_OF_LEDS_CORNER_MAX 0x1
#define MQTT_HOSTNAME_START (NUMBER_OF_LEDS_CORNER_START + NUMBER_OF_LEDS_CORNER_MAX)
#define MQTT_HOSTNAME_MAX 0x15
#define MQTT_PORT_START (MQTT_HOSTNAME_START + MQTT_HOSTNAME_MAX)
#define MQTT_PORT_MAX 0x0A
#define MQTT_USERNAME_START (MQTT_PORT_START + MQTT_PORT_MAX)
#define MQTT_USERNAME_MAX 0x15
#define MQTT_USERPASSWORD_START (MQTT_USERNAME_START + MQTT_USERNAME_MAX)
#define MQTT_USERPASSWORD_MAX 0x15
#define MQTT_CLIENT_ID_START (MQTT_USERPASSWORD_START + MQTT_USERPASSWORD_MAX)
#define MQTT_CLIENT_ID_MAX 0x15
#define PIR_SENSOR_ENABLED_START (MQTT_CLIENT_ID_START + MQTT_CLIENT_ID_MAX)
#define PIR_SENSOR_ENABLED_MAX 0x1
#define LED_COLOR_PATTERN_START (PIR_SENSOR_ENABLED_START + PIR_SENSOR_ENABLED_MAX)
#define LED_COLOR_PATTERN_MAX 0x1
// --- Constants ---
// Timers
// 1s
const int TR1S = 1000;
// 30min
const int TR30M = 1800000;
// 1h
const int TR1H = 3600000;
// 3h
const int TR3H = 10800000;
// EEPROM
const int EEPROM_MIN_ADDR = 0;
const int EEPROM_MAX_ADDR = 511;
//NTP server
// Ntp local port to listen for UDP packets
const unsigned int NTPLOCALUDPPORT = 2390;
// Ntp time stamp is in the first 48 bytes of the message
const int NTPPACKETSIZE = 48;
// --- Variables ---
// RGB Strip
// Brightness
// Range: 0 - 255
// 0 - Off (Dark:Black)
// 128 - Half lit
// 255 - Fully lit
byte beLedBrightness;
byte beLedOldBrightness;
boolean bBrighntessChange;
boolean bBacklight = true;
String sLedHexColor;
boolean bAutDisplayEnabled = true;
byte beDisplayOn;
byte beDisplayOff;
byte beRedLight = 255;
byte beGreenLight = 0;
byte beBlueLight = 0;
byte beLedSpeed = 10;
byte beLedOldSpeed;
byte beLedPattern = 0;
byte beLedColorPattern = 0;
int iNrOfLeds;
String sNrOfLeds;
byte beNrOfLedsCorner;
String sNrOfLedsCorner;
// NTP server
// Server name
String sNtpServer;
// Buffer to hold incoming and outgoing packets
byte beNtpPacketBuffer[NTPPACKETSIZE];
// No Ntp packet received
boolean bNtpPacketError = false;
// Counter which triggers NTP server check
byte beNtpCounterTrigger = 0;
// Button
boolean bResetButtonState = 0;
// MQTT
char cMqttHostName[21] = "";
char cMqttPort[11] = "";
int iMqttPort;
char cMqttUserName[21] = "";
char cMqttUserPassword[21] = "";
char cMqttClientId[21] = "";
// Topics for MQTT client to SET values to Moodlite
const char* cMqttSetTopic[11] = { "Moodlite/Brightness/Set", "Moodlite/Pattern/Set", "Moodlite/FixedPattern/Set", "Moodlite/PatternColor/Set", "Moodlite/Color/Set", "Moodlite/Speed/Set", "Moodlite/Backlight/Set", "Moodlite/DisplayOnEnabled/Set",
"Moodlite/DisplayOn/Set", "Moodlite/DisplayOff/Set", "Moodlite/PirSensorEndabled/Set"
};
// Topics for MQTT client to GET values from Moodlite
const char* cMqttGetTopic[11] = { "Moodlite/Brightness/Get", "Moodlite/Pattern/Get", "Moodlite/FixedPattern/Get", "Moodlite/PatternColor/Get", "Moodlite/Color/Get", "Moodlite/Speed/Get", "Moodlite/Backlight/Get", "Moodlite/DisplayOnEnabled/Get",
"Moodlite/DisplayOn/Get", "Moodlite/DisplayOff/Get", "Moodlite/PirSensorEndabled/Get"
};
// String sMqttValue = "";
boolean bMqttConnError = false;
// PIR sensor
// The time the sensor calibrates (10s - 60s)
int iPirCalibrationTime = 10;
// The time when the sensor outputs a low impulse
int iPirLowIn;
// The amount of milliseconds the sensor has to be low before we assume all motion has stopped
int iPirPause = 5000;
boolean bPirLockLow = true;
boolean bPirTakeLowTime;
boolean bPirBacklightOn = false;
boolean bPirSensorEnabled = false;
// --- Objects initialization ---
// Structure to store RGB colors converted from HEX color code
struct stRGBColors {
byte beRed;
byte beGreen;
byte beBlue;
};
struct stRGBColors stLedColors;
TBlendType currentBlending;
extern const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM;
// Initialize Wifi client
WiFiClient wifiClient;
// LEDs
CRGB crgbLeds[NUM_LEDS];
CRGB crgbLedColors = CRGB::White;
CRGB crgbOffColor = CRGB::Black;
CRGBPalette16 crgbCurrentPalette;
extern CRGBPalette16 myRedWhiteBluePalette;
// List of patterns to cycle through. Each is defined as a separate function below.
typedef void(*SimplePatternList[])();
SimplePatternList gPatterns = { &fullColor, &standard, &confetti, &sinelon, &juggle, &bpm, &kitt, &plasma, &blendwave, &inoise8_fire, &rainbow_beat, &rainbow, &rainbowWithGlitter };
uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current
uint8_t gHue = 0; // rotating "base color" used by many of the patterns
// Timer
Timer trNtpCheckServer;
Timer trMqttCheckMessages;
Timer trMqttSendStatus;
// UDP Packets
// A UDP instance to let us send and receive packets over UDP
WiFiUDP udp;
// Ntp Server
// Time zones
// Central European Time (Frankfurt, Paris)
// Central European Summer Time
TimeChangeRule tchrCest = { "CEST", Last, Sun, Mar, 2, 120 };
// Central European Standard Time
TimeChangeRule tchrCet = { "CET", Last, Sun, Oct, 3, 60 };
Timezone tzCe(tchrCest, tchrCet);
TimeChangeRule *tcr;
// Ntp IP address
IPAddress ipaNtpServerIp;
// Mqtt IP address
IPAddress ipaMqttServerIp;
// Time
time_t utc, localTime;
// Mqtt
PubSubClient mqttClient(wifiClient);
long lastMsg = 0;
char msg[50];
int value = 0;
// --- Asynchronous web server ---
AsyncWebServer asyncWebServer(80);
AsyncWebSocket ayncWebSocket("/ws"); // access at ws://[esp ip]/ws
class ByteString : public String {
public:
ByteString(void *data, size_t len) :
String() {
copy(data, len);
}
ByteString() :
String() {
}
String& copy(const void *data, unsigned int length) {
if (!reserve(length)) {
invalidate();
return (*this);
}
len = length;
memcpy(buffer, data, length);
buffer[length] = 0;
return (*this);
}
};
// Asynchronous TCP Client to retrieve data/time
struct AsyncHTTPClient {
AsyncClient *aClient = NULL;
bool initialized = false;
String protocol;
String base64Authorization;
String host;
int port;
String uri;
String request;
ByteString response;
int statusCode;
void(*onSuccess)();
void(*onFail)(String);
void initialize(String url) {
// check for : (http: or https:
int index = url.indexOf(':');
if (index < 0) {
initialized = false; // This is not a URLs
}
protocol = url.substring(0, index);
DEBUGLN(protocol);
url.remove(0, (index + 3)); // remove http:// or https://
index = url.indexOf('/');
String hostPart = url.substring(0, index);
DEBUGLN(hostPart);
url.remove(0, index); // remove hostPart part
// get Authorization
index = hostPart.indexOf('@');
if (index >= 0) {
// auth info
String auth = hostPart.substring(0, index);
hostPart.remove(0, index + 1); // remove auth part including @
base64Authorization = base64::encode(auth);
}
// get port
port = 80; //Default
index = hostPart.indexOf(':');
if (index >= 0) {
host = hostPart.substring(0, index); // hostname
host.remove(0, (index + 1)); // remove hostname + :
DEBUGLN(host);
port = host.toInt(); // get port
DEBUGLN(port);
}
else {
host = hostPart;
DEBUGLN(host);
}
uri = url;
if (protocol != "http") {
initialized = false;
}
DEBUGLN(initialized);
request = "GET " + uri + " HTTP/1.1\r\nHost: " + host + "\r\n\r\n";
DEBUGLN(request);
initialized = true;
}
int getStatusCode() {
return (statusCode);
}
String getBody() {
if (statusCode == 200) {
int bodyStart = response.indexOf("\r\n\r\n") + 4;
return (response.substring(bodyStart));
}
else {
return ("");
}
}
static void clientError(void *arg, AsyncClient *client, int error) {
DEBUGLN("Connect Error");
AsyncHTTPClient *self = (AsyncHTTPClient *)arg;
self->onFail("Connection error");
self->aClient = NULL;
delete client;
}
static void clientDisconnect(void *arg, AsyncClient *client) {
DEBUGLN("Disconnected");
AsyncHTTPClient *self = (AsyncHTTPClient *)arg;
self->aClient = NULL;
delete client;
}
static void clientData(void *arg, AsyncClient *client, void *data, size_t len) {
DEBUGLN("Got response");
AsyncHTTPClient *self = (AsyncHTTPClient *)arg;
self->response = ByteString(data, len);
String status = self->response.substring(9, 12);
self->statusCode = atoi(status.c_str());
DEBUGLN(status.c_str());
if (self->statusCode == 200) {
self->onSuccess();
}
else {
self->onFail("Failed with code " + status);
}
}
static void clientConnect(void *arg, AsyncClient *client) {
DEBUGLN("Connected");
AsyncHTTPClient *self = (AsyncHTTPClient *)arg;
self->response.copy("", 0);
self->statusCode = -1;
// Clear oneError handler
self->aClient->onError(NULL, NULL);
// Set disconnect handler
client->onDisconnect(clientDisconnect, self);
client->onData(clientData, self);
//send the request
client->write(self->request.c_str());
}
void makeRequest(void(*success)(), void(*fail)(String msg)) {
onFail = fail;
if (!initialized) {
fail("Not initialized");
return;
}
if (aClient) { //client already exists
fail("Call taking forever");
return;
}
aClient = new AsyncClient();
if (!aClient) { //could not allocate client
fail("Out of memory");
return;
}
onSuccess = success;
aClient->onError(clientError, this);
aClient->onConnect(clientConnect, this);
if (!aClient->connect(host.c_str(), port)) {
DEBUGLN("Connect Fail");
fail("Connection failed");
AsyncClient *client = aClient;
aClient = NULL;
delete client;
}
}
};
AsyncHTTPClient httpClient;
// --- Setup functions ---
void setup() {
// --- Initialize serial communication ---
Serial.begin(9600);
// --- Initialize EEPROM ---
EEPROM.begin(512);
// --- Initialize SPIFFS ---
#if ESP8266
SPIFFS.begin();
#elif ESP32
SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED);
#endif
// --- Initialize Push button ---
// Initialize reset buton pin
#if RESET_BUTTON
pinMode(RST_BTN_PIN, INPUT);
#endif
// --- Uncomment to reset ---
// Erase EEPROM
// eraseEEPROM();
// Erase SPIFFS
//eraseSPIFFS();
// --- Initialize EEPROM data ----
// Load EEPROM data
initEEPROMData();
// --- Initialize WiFi ---
initWiFi();
// If Wifi connection is defined start the program
// Else start only web server
if (strlen(cWifiSsid) > 0) {
// --- RGB LED Strip ---
// Change default NUM_LEDS value with value stored in memory
#ifdef NUM_LEDS
#undef NUM_LEDS
#endif
#define NUM_LEDS iNrOfLeds
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(crgbLeds, iNrOfLeds);
FastLED.setBrightness(beLedBrightness);
// Turn Off all LEDs
FastLED.clear();
// Start LED pattern
changeLedColorPatternParameters(beLedColorPattern);
switchLedPattern(beLedPattern);
// --- Initialize NTP Server ---
#if NTP_ON
// --- UDP ---
udp.begin(NTPLOCALUDPPORT);
// If NTP server is defined start checking time
if (sNtpServer.length() > 0) {
// --- Initialize NTP Server ---
ntpCheckServer((void *)0);
// --- Initialize Timer ---
// Check NTP Server - Update actual time
trNtpCheckServer.every(TR3H, ntpCheckServer, (void*)0);
}
#endif
// --- Initialize MQTT connection ---
#if MQTT_ON
// If MQTT server is defined start connection
if (strlen(cMqttHostName) > 0) {
// --- Initialize Timer ---
// Check for MQTT mesages
trMqttCheckMessages.every(TR1S, mqttCheckMessage, (void*)0);
// Send MQTT statuses
trMqttSendStatus.every(TR30M, mqttSendStatus, (void*)0);
// --- Connect to MQTT broker ---
// Translate Name to IP
WiFi.hostByName(cMqttHostName, ipaMqttServerIp);
mqttClient.setServer(ipaMqttServerIp, iMqttPort);
mqttClient.setCallback(mqttCallback);
}
#endif
// --- Initialize OTA ---
#if OTA_ON
ArduinoOTA.onStart([]() {
String sType;
if (ArduinoOTA.getCommand() == U_FLASH) {
sType = "sketch";
}
else { // U_SPIFFS
sType = "filesystem";
}
SPIFFS.end();
DEBUGLN("Start updating " + sType);
});
ArduinoOTA.onEnd([]() {
DEBUGLN("End");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
DEBUGF("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
DEBUGF("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
DEBUGLN("Authentication error");
}
else if (error == OTA_BEGIN_ERROR) {
DEBUGLN("Begin error");
}
else if (error == OTA_CONNECT_ERROR) {
DEBUGLN("Connection error");
}
else if (error == OTA_RECEIVE_ERROR) {
DEBUGLN("Receive error");
}
else if (error == OTA_END_ERROR) {
DEBUGLN("End error");
}
});
ArduinoOTA.begin();
#endif
// --- Initialize PIR sensor ---
#if PIR_ON
pinMode(PIR_PIN, INPUT);
// Sensor time to calibrate
DEBUG("Calibrating sensor ");
for (int i = 0; i < iPirCalibrationTime; i++) {
DEBUG(".");
delay(1000);
}
DEBUGLN(" done.");
DEBUGLN("PIR sensor active.");
#endif
}
// --- Web server ---
MDNS.addService("http", "tcp", 80);
asyncWebServer.on("/", HTTP_GET, mainHandler);
asyncWebServer.on("/system", HTTP_GET, systemHandler);
asyncWebServer.on("/set_wifi", HTTP_POST, wifiHandler);
asyncWebServer.serveStatic("/css", SPIFFS, "/css");
asyncWebServer.serveStatic("/js", SPIFFS, "/js");
asyncWebServer.serveStatic("/images", SPIFFS, "/images");
// Attach AsyncWebSocket
ayncWebSocket.onEvent(onEvent);
asyncWebServer.addHandler(&ayncWebSocket);
asyncWebServer.begin();
ayncWebSocket.enable(true);
}
// --- Main loop function ---
void loop()
{
// If OTA is not defined skip code in compiler
#if OTA_ON
ArduinoOTA.handle();
#endif
// If RESET_BUTTON is not defined skip code in compiler
#if RESET_BUTTON
bResetButtonState = digitalRead(RST_BTN_PIN);
// When reset button pressed, erase EEPROM and restart ESP
if (bResetButtonState) {
eraseEEPROM();
}
#endif
// If NTP is not defined skip code in compiler
#if NTP_ON
// If NTP server is defined update timer
if (sNtpServer.length() > 0)
trNtpCheckServer.update();
#endif
// If MQTT is not defined skip code in compiler
#if MQTT_ON
// If MQTTS server is defined and no connection error exists, connect to server and check for message
if (strlen(cMqttHostName) > 0 && !(bMqttConnError)) {
if (!mqttClient.connected()) {
mqttConnect();
}
// Check for new messages
trMqttCheckMessages.update();
// Send status data
trMqttSendStatus.update();
}
#endif
// If PIR sensor is not defined skip code in compiler
#if PIR_ON
if (bPirSensorEnabled) {
getPirSensorState(digitalRead(PIR_PIN));
}
#endif
// LED pattern
// Display LED pattern if: Turn light ON enabled AND Nr. of LEDs per corner > 0 AND LED pattern is set AND LED pattern speed > 0
if (checkDisplay() && beNrOfLedsCorner > 0 && beLedPattern != 0 && beLedPattern != 99 && beLedSpeed > 0) {
gPatterns[gCurrentPatternNumber]();
FastLED.show();
FastLED.delay(1000 / beLedSpeed);
EVERY_N_MILLISECONDS(20) { gHue++; } // slowly cycle the "base color" through the rainbow
}
}
// ----------------------------------------------------
// ------------------- Functions ----------------------
// Initialize WiFi
void initWiFi() {
DEBUGLN("--- initWiFi - Start ---");
// Disconnect() needs to be done before new connection
WiFi.disconnect();
MDNS.begin(cHostName);
#if STATIC_IP
if (strlen(cWifiSsid) > 0) {
// Set base config
// Check if DNS is configured
if ((int)ipaWifiDns[0] > 0) {
WiFi.config(ipaWifiIP, ipaWifiDns, ipaWifiGateway, ipaWifiSubnet);
}
else {
WiFi.config(ipaWifiIP, ipaWifiGateway, ipaWifiSubnet);
// If DNS is not configured don't use NTP
if (NTP_ON) {
DEBUGLN("NTP disabled due to DNS not configured.");
}
sNtpServer = "";
// Disable auto LED On/Off
bAutDisplayEnabled = false;
beDisplayOn = 0;
beDisplayOff = 0;
}
// Set Hostname
#if ESP8266
WiFi.hostname(cHostName);
#elif ESP32
WiFi.setHostname(cHostName);
#endif
bool bWifiInRange = false;
int iNumSsid = WiFi.scanNetworks(false);
if (iNumSsid == 0) {
DEBUGLN("No wifi networks found.");
}
else {
for (int i = 0; i < iNumSsid; ++i) {
if (WiFi.SSID(i) == cWifiSsid) {
bWifiInRange = true;
}
}
}
if (bWifiInRange) {
if (strlen(cWifiPassword) > 0) {
WiFi.begin(cWifiSsid, cWifiPassword);
}
else {
WiFi.begin(cWifiSsid);
}
DEBUGLN("Connecting to Wifi. Please wait.");
delay(10000);
}
else {
DEBUGLN("Wifi SSID not found.");
}
}
#else
// Wifi AP Config
WiFi.mode(WIFI_AP_STA);
WiFi.softAP(cHostName, WIFI_AP_PASSWORD);
WiFi.softAPConfig(ipaWifiApIP, ipaWifiApGateway, ipaWifiApSubnet);
if (strlen(cWifiSsid) > 0) {
// Set Hostname
#if ESP8266
WiFi.hostname(cHostName);
#elif ESP32
WiFi.setHostname(cHostName);
#endif
if (strlen(cWifiPassword) > 0) {
WiFi.begin(cWifiSsid, cWifiPassword);
}
else {
WiFi.begin(cWifiSsid);
}
DEBUGLN("Connecting to Wifi. Please wait.");
delay(10000);
}
#endif
// Status:
// WL_NO_SHIELD = 255,
// WL_IDLE_STATUS = 0,
// WL_NO_SSID_AVAIL = 1
// WL_SCAN_COMPLETED = 2
// WL_CONNECTED = 3
// WL_CONNECT_FAILED = 4
// WL_CONNECTION_LOST = 5
// WL_DISCONNECTED = 6
DEBUG("Wifi status: ");
DEBUGLN(WiFi.status());
if (WiFi.status() == WL_CONNECTED)
DEBUGLN("Wifi network started succesfully.");
else if (WiFi.status() == WL_DISCONNECTED)
DEBUGLN("Error conecting to wifi network.");
DEBUGLN("--- initWiFi - End ---");
}
// EEPROM functions
// Read byte from EEPROM
byte readByteFromEEPROM(int iStart) {
int iPosition = iStart;
byte beValue;
EEPROM.get(iPosition, beValue);
return (beValue);
}
// Write byte to EEPROM
void writeByteToEEPROM(byte beValue, int iStart) {
int iPosition = iStart;
//EEPROM.begin(512);
EEPROM.write(iPosition, beValue);
EEPROM.commit();
}
// Read string from EEPROM
String readStringFromEEPROM(int iStart, int iMax) {
int iEnd = iStart + iMax;
String sValue = "";
for (int i = iStart; i < iEnd; i++) {
byte readByte = EEPROM.read(i);
if (readByte > 0 && readByte < 128) {
sValue += char(readByte);
}
else {
break;
}
}
return (sValue);
}
// Write string to EEPROM
void writeStringToEEPROM(String sValue, int iStart, int iMax) {
int iEnd = iStart + iMax;
for (int i = iStart; i < iEnd; i++) {
if (i - iStart < sValue.length()) {
EEPROM.write(i, sValue[i - iStart]);
}
else {
EEPROM.write(i, 0);
break;
}
}
EEPROM.commit();
}
// Returns true if the address is between the
// minimum and maximum allowed values, false otherwise.
boolean checkEEPROMAdr(int iAddress) {
return ((iAddress >= EEPROM_MIN_ADDR) && (iAddress <= EEPROM_MAX_ADDR));
}
// Writes a sequence of bytes to eeprom starting at the specified address.
// Returns true if the whole array is successfully written.
// Returns false if the start or end addresses aren't between
// the minimum and maximum allowed values.
// When returning false, nothing gets written to eeprom.
boolean writeBytesToEEPROM(const byte* beArray, int iStartAddress, int iNumerOfBytes) {
// counter
int i;
// both first byte and last byte addresses must fall within
// the allowed range
if (!checkEEPROMAdr(iStartAddress) || !checkEEPROMAdr(iStartAddress + iNumerOfBytes)) {
return false;
}
for (i = 0; i < iNumerOfBytes; i++) {
EEPROM.write(iStartAddress + i, beArray[i]);
}
EEPROM.commit();
return true;
}
// Writes a string starting at the specified address.
// Returns true if the whole string is successfully written.
// Returns false if the address of one or more bytes fall outside the allowed range.
// If false is returned, nothing gets written to the eeprom.
boolean writeCharToEEPROM(const char* cString, int iAddress) {
int numBytes; // actual number of bytes to be written
//write the string contents plus the string terminator byte (0x00)
numBytes = strlen(cString) + 1;
return writeBytesToEEPROM((const byte*)cString, iAddress, numBytes);
}
// Reads a string starting from the specified address.
// Returns true if at least one byte (even only the string terminator one) is read.
// Returns false if the start address falls outside the allowed range or declare buffer size is zero.
//
// The reading might stop for several reasons:
// - no more space in the provided buffer
// - last eeprom address reached
// - string terminator byte (0x00) encountered.
boolean readCharFromEEPROM(char* cBuffer, int iAddress, int iBufferSize) {
byte ch; // byte read from eeprom
int bytesRead; // number of bytes read so far
if (!checkEEPROMAdr(iAddress)) { // check start address
return false;
}
if (cBuffer == 0) { // how can we store bytes in an empty buffer ?
return false;
}
// is there is room for the string terminator only, no reason to go further
if (iBufferSize == 1) {
cBuffer[0] = 0;
return true;
}
bytesRead = 0; // initialize byte counter
ch = EEPROM.read(iAddress + bytesRead); // read next byte from eeprom
cBuffer[bytesRead] = ch; // store it into the user buffer
bytesRead++; // increment byte counter
// stop conditions:
// - the character just read is the string terminator one (0x00)
// - we have filled the user buffer
// - we have reached the last eeprom address
while ((ch != 0x00) && (bytesRead < iBufferSize) && ((iAddress + bytesRead) <= EEPROM_MAX_ADDR)) {
// if no stop condition is met, read the next byte from eeprom
ch = EEPROM.read(iAddress + bytesRead);
cBuffer[bytesRead] = ch; // store it into the user buffer
bytesRead++; // increment byte counter
}
// make sure the user buffer has a string terminator, (0x00) as its last byte
if ((ch != 0x00) && (bytesRead >= 1)) {
cBuffer[bytesRead - 1] = 0;
}
return true;
}
// Erase EEPROM
void eraseEEPROM() {
DEBUGLN("--- eraseEEPROM - Start ---");
for (int i = 0; i < EEPROM.length(); i++) {
EEPROM.write(i, 0);
}
EEPROM.commit();
DEBUGLN("--- eraseEEPROM - End ---");
ESP.restart();
}
// Erase SPIFFS
void eraseSPIFFS() {
DEBUGLN("--- eraseSPIFFS - Start ---");
SPIFFS.format();
DEBUGLN("--- eraseSPIFFS - End ---");
}
// Initialize stored data
void initEEPROMData() {
DEBUGLN("--- initEEPROMData - Start ---");
// If static IP is deffined skip WiFi settings load from memory
#if STATIC_IP
#else
// Hostname
char cConfiguredHostname[21] = "";
readCharFromEEPROM(cConfiguredHostname, HOSTNAME_START, HOSTNAME_MAX);
if (strlen(cConfiguredHostname) > 0) {
sprintf(cHostName, "%s", cConfiguredHostname);
DEBUGLN("Hostname loaded from memory.");
}
else {
DEBUGLN("No Hostname stored in memory, loading default Hostname.");
}
DEBUG("Wifi Hostname: ");
DEBUGLN(cHostName);
// WiFi SSID
readCharFromEEPROM(cWifiSsid, SSID_START, SSID_MAX);
DEBUG("Wifi ssid: ");
DEBUGLN(cWifiSsid);
// WiFi Password
readCharFromEEPROM(cWifiPassword, PASSWORD_START, PASSWORD_MAX);
DEBUG("Wifi password: ");
DEBUGLN(cWifiPassword);
#endif
if (strlen(cWifiSsid) > 0) {
// Number of LEDs per corner
sNrOfLedsCorner = readByteFromEEPROM(NUMBER_OF_LEDS_CORNER_START);
beNrOfLedsCorner = sNrOfLedsCorner.toInt();
// Number of LEDs
sNrOfLeds = readStringFromEEPROM(NUMBER_OF_LEDS_START, NUMBER_OF_LEDS_MAX);
iNrOfLeds = sNrOfLeds.toInt();
// LED Color
sLedHexColor = readStringFromEEPROM(LED_COLOR_START, LED_COLOR_MAX);
stLedColors = hexToRGB(sLedHexColor);
crgbLedColors = CRGB(stLedColors.beRed, stLedColors.beGreen, stLedColors.beBlue);
// Manual Display On
bBacklight = readByteFromEEPROM(BACKLIGHT_START);
// Automatic Display On
bAutDisplayEnabled = readByteFromEEPROM(AUTDISPLAYON_START);
beDisplayOn = readByteFromEEPROM(DISPLAYON_START);
beDisplayOff = readByteFromEEPROM(DISPLAYOFF_START);
// PIR senor (motion sensor) enabled
bPirSensorEnabled = readByteFromEEPROM(PIR_SENSOR_ENABLED_START);
// LED Brightness
beLedBrightness = beLedOldBrightness = readByteFromEEPROM(LED_BRIGHTNESS_START);
// LED Speed
beLedSpeed = readByteFromEEPROM(LED_SPEED_START);
//LED Pattern
beLedPattern = readByteFromEEPROM(LED_PATTERN_START);
// NTP Server
sNtpServer = readStringFromEEPROM(NTP_SERVER_START, NTP_SERVER_MAX);
// MQTT Hostname
readCharFromEEPROM(cMqttHostName, MQTT_HOSTNAME_START, MQTT_HOSTNAME_MAX);
// MQTT port
readCharFromEEPROM(cMqttPort, MQTT_PORT_START, MQTT_PORT_MAX);
iMqttPort = atoi(cMqttPort);
// MQTT User name
readCharFromEEPROM(cMqttUserName, MQTT_USERNAME_START, MQTT_USERNAME_MAX);
// MQTT User password
readCharFromEEPROM(cMqttUserPassword, MQTT_USERPASSWORD_START, MQTT_USERPASSWORD_MAX);
// MQTT Client ID
readCharFromEEPROM(cMqttClientId, MQTT_CLIENT_ID_START, MQTT_CLIENT_ID_MAX);
//LED Color Pattern
beLedColorPattern = readByteFromEEPROM(LED_COLOR_PATTERN_START);
DEBUG("Manual Display On: ");
DEBUGLN(bBacklight);
DEBUG("Automatic Display On: ");
DEBUGLN(bAutDisplayEnabled);
DEBUG("Display On: ");
DEBUGLN(beDisplayOn);
DEBUG("Display Off: ");
DEBUGLN(beDisplayOff);
DEBUG("Pir sensor enabled: ");
DEBUGLN(bPirSensorEnabled);
DEBUG("Nr. of LEDs: ");
DEBUGLN(iNrOfLeds);
DEBUG("Nr. of LEDs per corner: ");
DEBUGLN(beNrOfLedsCorner);
DEBUG("Brightness: ");
DEBUGLN(beLedBrightness);
DEBUG("Speed: ");
DEBUGLN(beLedSpeed);
DEBUG("Pattern: ");
DEBUGLN(beLedPattern);
DEBUG("Pattern color: ");
DEBUGLN(beLedColorPattern);
DEBUG("LED color: ");
DEBUGLN(sLedHexColor);
DEBUG("NTP Server: ");
DEBUGLN(sNtpServer);
DEBUG("MQTT Hostname: ");
DEBUGLN(cMqttHostName);
DEBUG("MQTT port: ");
DEBUGLN(cMqttPort);
DEBUG("MQTT User name: ");
DEBUGLN(cMqttUserName);
DEBUG("MQTT User password: ");
DEBUGLN(cMqttUserPassword);
DEBUG("MQTT Client ID: ");
DEBUGLN(cMqttClientId);
}
DEBUGLN("--- initEEPROMData - End ---");
// If static IP is deffined and if wrong parameters for LEDs were stored, reload default values (e.g. first start)
// The minimum is 1 LED per corner and total 3 LEDs
#if STATIC_IP
if ((beNrOfLedsCorner < 1) || (iNrOfLeds < 3)) {
setDefaulValues();
ESP.restart();
}
#endif
}
// Set default values to EEPROM
void setDefaulValues() {
DEBUGLN("--- setDefaulValues - Start ---");
writeByteToEEPROM(1, BACKLIGHT_START);
writeByteToEEPROM(20, LED_BRIGHTNESS_START);
writeCharToEEPROM("FFFFFF", LED_COLOR_START);
writeByteToEEPROM(1, NUMBER_OF_LEDS_CORNER_START);
writeCharToEEPROM("3", NUMBER_OF_LEDS_START);
writeByteToEEPROM(50, LED_SPEED_START);
DEBUGLN("--- setDefaulValues - End ---");
}
// Web server functions
/*
msg is
<num>: for a message with no arguments or
<num>:<string>:<value> for a message that has a key/value pair
*/
void handleWSMsg(AsyncWebSocketClient *client, char *msg) {
DEBUGLN("--- handleWSMsg - Start ---");
DEBUGLN(msg);
String wholeMsg(msg);
int code = wholeMsg.substring(0, wholeMsg.indexOf(':')).toInt();
switch (code) {
case 1:
sendWifiValues(client);
break;
case 2:
sendSettingsValues(client);
break;
case 3:
sendLedValues(client);
break;
case 5:
updateValue(wholeMsg.substring(wholeMsg.indexOf(':') + 1));
break;
}
DEBUGLN("--- handleWSMsg - End ---");
}
void sendStatus(String msg) {
DEBUGLN("--- sendStatus - Start ---");
String json = "{\"type\":\"sv.status\",\"value\":\"" + msg + "\"}";
ayncWebSocket.textAll(json);
DEBUGLN("--- sendStatus - End ---");
}
void sendWifiValues(AsyncWebSocketClient *client) {
DEBUGLN("--- sendWifiValues - Start ---");
String json = String("{\"type\":\"sv.init.wifi\",\"value\":{\"ssid\":\"");
json += cWifiSsid;
json += "\",\"password\":\"";
json += cWifiPassword;
json += "\",\"hostname\":\"";
json += cHostName;
json += "\"}}";
client->text(json);
DEBUGLN("--- sendWifiValues - End ---");
}
void sendSettingsValues(AsyncWebSocketClient *client) {
DEBUGLN("--- sendSettingsValues - Start ---");
String sNtpDateTime = String(hour()) + " : " + String(minute()) + " : " + String(second());
String json = String("{\"type\":\"sv.init.settings\",\"value\":{") +
"\"ntp_server\":\"" + sNtpServer + "\"," +
"\"ntp_date_time\":\"" + sNtpDateTime + "\"," +
"\"led_strip_nr_of_leds_corner\":" + beNrOfLedsCorner + "," +
"\"led_strip_nr_of_leds\":" + iNrOfLeds + "," +
"\"mqtt_host_name\":\"" + cMqttHostName + "\"," +
"\"mqtt_port\":\"" + cMqttPort + "\"," +
"\"mqtt_user_name\":\"" + cMqttUserName + "\"," +
"\"mqtt_user_password\":\"" + cMqttUserPassword + "\"," +
"\"mqtt_client_id\":\"" + cMqttClientId + "\"" +
"}}";
client->text(json);
DEBUGLN("--- sendSettingsValues - End ---");
}
void sendLedValues(AsyncWebSocketClient *client) {
DEBUGLN("--- sendLedValues - Start ---");
String json = String("{\"type\":\"sv.init.leds\",\"value\":{") +
"\"pattern_color\":" + beLedColorPattern + "," +
"\"patterns\":" + beLedPattern + "," +
"\"fixed_patterns\":" + beLedPattern + "," +
"\"aut_display_enabled\":" + bAutDisplayEnabled + "," +
"\"display_on\":" + beDisplayOn + "," +
"\"display_off\":" + beDisplayOff + "," +
"\"backlight\":" + bBacklight + "," +
"\"pir_sensor_endabled\":" + bPirSensorEnabled + "," +
"\"colorpickerfield_led_strip\":\"" + sLedHexColor + "\"," +
"\"led_strip_brightness\":" + beLedBrightness + "," +
"\"led_strip_speed\":" + beLedSpeed +
"}}";
client->text(json);
DEBUGLN("--- sendLedValues - End ---");
}
void updateValue(String pair) {
DEBUGLN("--- updateValue - Start ---");
int index = pair.indexOf(':');
// _key has to hang around because key points to an internal data structure
String _key = pair.substring(0, index);
const char *key = _key.c_str();
String value = pair.substring(index + 1);
char cMqttValue[21];
// Automatic Display On
// True = On
// False = Off
if (strcmp("aut_display_enabled", key) == 0) {
DEBUG("aut_display_enabled: ");
DEBUGLN(value);
// Convert string value "true"/"false" to boolean for further calculation
const char *to_find = value.c_str(); // string we want to find
if (strcmp(to_find, "true") == 0) { // this is the key: a match returns 0
bAutDisplayEnabled = true;
}
else {
bAutDisplayEnabled = false;
}
// Publish new value
sprintf(cMqttValue, "%i", bAutDisplayEnabled);
mqttClient.publish(cMqttGetTopic[7], cMqttValue, true);
}
// Display On Hour
// Hour when backlight is on
else if (strcmp("display_on", key) == 0) {
DEBUG("display_on: ");
DEBUGLN(value);
beDisplayOn = value.toInt();
// Publish new value
sprintf(cMqttValue, "%i", beDisplayOn);
mqttClient.publish(cMqttGetTopic[8], cMqttValue, true);
}
// Hour when backlight is off
// Display Off Hour
else if (strcmp("display_off", key) == 0) {
DEBUG("display_off: ");
DEBUGLN(value);
beDisplayOff = value.toInt();
// Publish new value
sprintf(cMqttValue, "%i", beDisplayOff);
mqttClient.publish(cMqttGetTopic[9], cMqttValue, true);
}
// PIR sensor (motion sensor) On/Off
// True = On
// False = Off
if (strcmp("pir_sensor_endabled", key) == 0) {
DEBUG("pir_sensor_endabled: ");
DEBUGLN(value);
// Convert string value "true"/"false" to boolean for further calculation
const char *to_find = value.c_str(); // string we want to find
if (strcmp(to_find, "true") == 0) { // this is the key: a match returns 0
bPirSensorEnabled = true;
}
else {
bPirSensorEnabled = false;
}
// Publish new value
sprintf(cMqttValue, "%i", bPirSensorEnabled);
mqttClient.publish(cMqttGetTopic[10], cMqttValue, true);
}
// Manual Display On
// True = On
// False = Off
else if (strcmp("backlight", key) == 0) {
DEBUG("Manual Display On: ");
DEBUGLN(value);
// Convert string value "true"/"false" to boolean for further calculation
const char *to_find = value.c_str(); // string we want to find
if (strcmp(to_find, "true") == 0) { // this is the key: a match returns 0
bBacklight = true;
}
else {
bBacklight = false;
}
// If automatic Display On is turned Off
if (!bAutDisplayEnabled) {
// Brighntess change start
bBrighntessChange = true;
// if Manual Display On is On set brightness and colour to last value
if (bBacklight) {
beLedBrightness = beLedOldBrightness;
}
// if Manual Display On is Off save previous value and set brightness to 0
else {
beLedOldBrightness = beLedBrightness;
beLedBrightness = 0;
}
// Change Rgb Led brightness
FastLED.setBrightness(beLedBrightness);
FastLED.show();
// Restore pattern
switchLedPattern(beLedPattern);
// Brighntess change end
bBrighntessChange = false;
}
// Publish new value
sprintf(cMqttValue, "%i", bBacklight);
mqttClient.publish(cMqttGetTopic[6], cMqttValue, true);
}
// Clock's color
// Color HEX value
else if (strcmp("colorpickerfield_led_strip", key) == 0) {
DEBUG("colorpickerfield_led_strip: ");
DEBUGLN(value);
sLedHexColor = value;
stLedColors = hexToRGB(sLedHexColor);
DEBUG("beRed: ");
DEBUGLN(stLedColors.beRed);
DEBUG("beGreen: ");
DEBUGLN(stLedColors.beGreen);
DEBUG("beBlue: ");
DEBUGLN(stLedColors.beBlue);
crgbLedColors = CRGB(stLedColors.beRed, stLedColors.beGreen, stLedColors.beBlue);
// Change clock's color
// Turn Off all LEDs
FastLED.clear();
if (beLedPattern == 0) {
setLedsColor(0, iNrOfLeds, crgbLedColors);
FastLED.show();
}
// Publish new value
sprintf(cMqttValue, "%i,%i,%i", stLedColors.beRed, stLedColors.beGreen, stLedColors.beBlue);
mqttClient.publish(cMqttGetTopic[4], cMqttValue, true);
}
// Brightness
// Led strip brightness value
else if (strcmp("led_strip_brightness", key) == 0) {
// Brighntess change start
bBrighntessChange = true;
DEBUG("led_strip_brightness: ");
DEBUGLN(value);
// If brightness value changed remember previous value
if (abs(beLedBrightness - beLedOldBrightness) > 0)
beLedOldBrightness = beLedBrightness;
beLedBrightness = value.toInt();
// Change Rgb LED brightness
FastLED.setBrightness(beLedBrightness);
FastLED.show();
DEBUGLN(beLedBrightness);
// Publish new value
sprintf(cMqttValue, "%i", beLedBrightness);
mqttClient.publish(cMqttGetTopic[0], cMqttValue, true);
// Brighntess change end
bBrighntessChange = false;
}
// Speed
// LEd strip speed value
else if (strcmp("led_strip_speed", key) == 0) {
DEBUG("led_strip_speed: ");
DEBUGLN(value);
beLedOldSpeed = beLedSpeed;
beLedSpeed = value.toInt();
DEBUGLN(beLedSpeed);
// Publish new value
sprintf(cMqttValue, "%i", beLedSpeed);
mqttClient.publish(cMqttGetTopic[5], cMqttValue, true);
}
// LED pattern color
else if (strcmp("patterncolor", key) == 0) {
DEBUG("patterncolor: ");
DEBUGLN(value);
beLedColorPattern = value.toInt();
FastLED.clear();
changeLedColorPatternParameters(beLedColorPattern);
DEBUGLN(beLedColorPattern);
// Publish new value
sprintf(cMqttValue, "%i", beLedColorPattern);
mqttClient.publish(cMqttGetTopic[3], cMqttValue, true);
}
// LED pattern
else if (strcmp("pattern", key) == 0) {
DEBUG("pattern: ");
DEBUGLN(value);
beLedPattern = value.toInt();
FastLED.clear();
changeLedPatternParameters(beLedPattern);
DEBUGLN(beLedPattern);
// MQTT
// Clear Fixced pattern value
sprintf(cMqttValue, "%i", 0);
mqttClient.publish(cMqttGetTopic[2], cMqttValue, true);
// Publish new value
sprintf(cMqttValue, "%i", beLedPattern);
mqttClient.publish(cMqttGetTopic[1], cMqttValue, true);
}
// LED fixed pattern
else if (strcmp("fixedpattern", key) == 0) {
DEBUG("fixedpattern: ");
DEBUGLN(value);
beLedPattern = value.toInt();
FastLED.clear();
changeLedFixedPatternParameters(beLedPattern);
DEBUGLN(beLedPattern);
// MQTT
// Clear Pattern value
sprintf(cMqttValue, "%i", 0);
mqttClient.publish(cMqttGetTopic[1], cMqttValue, true);
// Publish new values
sprintf(cMqttValue, "%i", beLedPattern);
mqttClient.publish(cMqttGetTopic[2], cMqttValue, true);
}
// Store as default values
else if (strcmp("saveDefaultValues", key) == 0) {
DEBUG("saveDefaultValues: ");
DEBUGLN(value);
if (value.toInt() == 1) {
DEBUGLN("Store data");
DEBUG("Automatic Display On: ");
DEBUGLN(bAutDisplayEnabled);
writeByteToEEPROM((int)bAutDisplayEnabled, AUTDISPLAYON_START);
DEBUG("Display On hour: ");
DEBUGLN(beDisplayOn);
writeByteToEEPROM(beDisplayOn, DISPLAYON_START);
DEBUG("Display Off hour: ");
DEBUGLN(beDisplayOff);
writeByteToEEPROM(beDisplayOff, DISPLAYOFF_START);
DEBUG("Manual Display On: ");
DEBUGLN(bBacklight);
writeByteToEEPROM((int)bBacklight, BACKLIGHT_START);
DEBUG("PIR (motion) sensor enabled: ");
DEBUGLN(bPirSensorEnabled);
writeByteToEEPROM((int)bPirSensorEnabled, PIR_SENSOR_ENABLED_START);
DEBUG("Brightness: ");
DEBUGLN(beLedBrightness);
writeByteToEEPROM(beLedBrightness, LED_BRIGHTNESS_START);
DEBUG("LED pattern speed: ");
DEBUGLN(beLedSpeed);
writeByteToEEPROM(beLedSpeed, LED_SPEED_START);
DEBUG("LED pattern: ");
DEBUGLN(beLedPattern);
writeByteToEEPROM(beLedPattern, LED_PATTERN_START);
DEBUG("LED color pattern: ");
DEBUGLN(beLedColorPattern);
writeByteToEEPROM(beLedColorPattern, LED_COLOR_PATTERN_START);
DEBUG("Pattern 0 color: ");
DEBUGLN(sLedHexColor);
writeStringToEEPROM(sLedHexColor, LED_COLOR_START, LED_COLOR_MAX);
}
}
// Set NTP server
else if (strcmp("setNtpServer", key) == 0) {
DEBUG("setNtpServer: ");
DEBUGLN(value);
sNtpServer = value;
DEBUGLN(sNtpServer);
}
// Set number of LEDs per corner
else if (strcmp("setNrOfLedsCorner", key) == 0) {
DEBUG("setNrOfLedsCorner: ");
DEBUGLN(value);
sNrOfLedsCorner = value;
beNrOfLedsCorner = value.toInt();
DEBUGLN(beNrOfLedsCorner);
}
// Set total number of LEDs
else if (strcmp("setNrOfLeds", key) == 0) {
DEBUG("setNrOfLeds: ");
DEBUGLN(value);
sNrOfLeds = value;
iNrOfLeds = value.toInt();
DEBUGLN(iNrOfLeds);
}
// Save LEDs settings
else if (strcmp("saveLedsSettings", key) == 0) {
DEBUG("saveLedsSettings: ");
DEBUGLN(value);
if (value.toInt() == 1) {
writeStringToEEPROM(sNtpServer, NTP_SERVER_START, NTP_SERVER_MAX);
writeByteToEEPROM(beNrOfLedsCorner, NUMBER_OF_LEDS_CORNER_START);
writeStringToEEPROM(sNrOfLeds, NUMBER_OF_LEDS_START, NUMBER_OF_LEDS_MAX);
}
}
// MQTT
// MQTT Hostname
else if (strcmp("setMqttHostName", key) == 0) {
DEBUG("setMqttHostName: ");
DEBUGLN(value);
value.toCharArray(cMqttHostName, 21);
DEBUGLN(cMqttHostName);
}
// MQTT Port
else if (strcmp("setMqttPort", key) == 0) {
DEBUG("setMqttPort: ");
DEBUGLN(value);
value.toCharArray(cMqttPort, 11);
DEBUGLN(cMqttPort);
}
// MQTT User name
else if (strcmp("setMqttUserName", key) == 0) {
DEBUG("setMqttUserName: ");
DEBUGLN(value);
value.toCharArray(cMqttUserName, 21);
DEBUGLN(cMqttUserName);
}
// MQTT User password
else if (strcmp("setMqttUserPassword", key) == 0) {
DEBUG("setMqttUserPassword: ");
DEBUGLN(value);
value.toCharArray(cMqttUserPassword, 21);
DEBUGLN(cMqttUserPassword);
}
// MQTT Client ID
else if (strcmp("setMqttClient", key) == 0) {
DEBUG("setMqttClient: ");
DEBUGLN(value);
value.toCharArray(cMqttClientId, 21);
DEBUGLN(cMqttClientId);
}
// Save MQTT settings
else if (strcmp("saveMqttSettings", key) == 0) {
DEBUG("saveMqttSettings: ");
DEBUGLN(value);
if (value.toInt() == 1) {
writeCharToEEPROM(cMqttHostName, MQTT_HOSTNAME_START);
writeCharToEEPROM(cMqttPort, MQTT_PORT_START);
writeCharToEEPROM(cMqttUserName, MQTT_USERNAME_START);
writeCharToEEPROM(cMqttUserPassword, MQTT_USERPASSWORD_START);
writeCharToEEPROM(cMqttClientId, MQTT_CLIENT_ID_START);
}
}
// Restart
else if (strcmp("restart", key) == 0) {
DEBUG("restart: ");
DEBUGLN(value);
if (value.toInt() == 1) {
beLedPattern = 99;
DEBUGLN(99);
changeLedPatternParameters(beLedPattern);
}
}
DEBUGLN("--- updateValue - End ---");
}
void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
//Handle WebSocket event
switch (type) {
case WS_EVT_CONNECT:
DEBUGLN("WS connected");
break;
case WS_EVT_DISCONNECT:
DEBUGLN("WS disconnected");
break;
case WS_EVT_ERROR:
DEBUGLN("WS error");
DEBUGLN((char *)data);
break;
case WS_EVT_PONG:
DEBUGLN("WS pong");
break;
case WS_EVT_DATA: // Yay we got something!
DEBUGLN("WS data");
AwsFrameInfo *info = (AwsFrameInfo *)arg;
if (info->final && info->index == 0 && info->len == len) {
//the whole message is in a single frame and we got all of it's data
if (info->opcode == WS_TEXT) {
DEBUGLN("WS text data");
data[len] = 0;
handleWSMsg(client, (char *)data);
}
else {
DEBUGLN("WS binary data");
}
}
else {
DEBUGLN("WS data was split up!");
}
break;
}
}
String getInput(String type, String name, String label, String value) {
String input = "<input type=\"" + type + "\" name=\"" + name + "\" placeholder=\"" + label + "\" value=\"" + value + "\"/>";
return (input);
}
// Web server Handler
void mainHandler(AsyncWebServerRequest *request) {
DEBUGLN("--- mainHandler - Start ---");
// When no wifi connection is defined (main_ap.html from folder data is not used)
// When settings submitted function wifiHandler() is called to store new credentials
if (WiFi.status() != WL_CONNECTED) {
request->send(SPIFFS, "/main_ap.html");
}
// When normal operation (index.html from folder data is used)
else {
request->send(SPIFFS, "/index.html");
}
DEBUGLN("--- mainHandler - End ---");
}
// Get the header for a 2 column table
String getTableHead2Col(String tableHeader, String col1Header, String col2Header) {
String tableHead = "<h3>";
tableHead += tableHeader;
tableHead += "</h3>";
tableHead += "<table><thead><tr><th>";
tableHead += col1Header;
tableHead += "</th><th>";
tableHead += col2Header;
tableHead += "</th></tr></thead><tbody>";
return (tableHead);
}
String getTableRow2Col(String col1Val, String col2Val) {
String tableRow = "<tr><td>";
tableRow += col1Val;
tableRow += "</td><td>";
tableRow += col2Val;
tableRow += "</td></tr>";
return (tableRow);
}
String getTableRow2Col(String col1Val, int col2Val) {
String tableRow = "<tr><td>";
tableRow += col1Val;
tableRow += "</td><td>";
tableRow += col2Val;
tableRow += "</td></tr>";
return (tableRow);
}
String getTableFoot() {
return ("</tbody></table></div></div>");
}
void systemHandler(AsyncWebServerRequest *request) {
DEBUGLN("--- systemHandler - Start ---");
String response("<html><head><title>stLixieClock Stats</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><link rel=\"stylesheet\" href=\"/assets/table.css\"/></head><body>");
#if ESP8266
float voltage = (float)ESP.getVcc() / (float)1024;
voltage -= 0.01f; // by default reads high
char dtostrfbuffer[15];
dtostrf(voltage, 7, 2, dtostrfbuffer);
String vccString = String(dtostrfbuffer);
// ESP8266 Info table
response += getTableHead2Col("ESP8266 information", "Name", "Value");
response += getTableRow2Col("Sketch size", ESP.getSketchSize());
response += getTableRow2Col("Free sketch size", ESP.getFreeSketchSpace());
response += getTableRow2Col("Free heap", ESP.getFreeHeap());
response += getTableRow2Col("Boot version", ESP.getBootVersion());
response += getTableRow2Col("CPU Freqency (MHz)", ESP.getCpuFreqMHz());
response += getTableRow2Col("SDK version", ESP.getSdkVersion());
response += getTableRow2Col("Chip ID", ESP.getChipId());
response += getTableRow2Col("Flash Chip ID", ESP.getFlashChipId());
response += getTableRow2Col("Flash size", ESP.getFlashChipRealSize());
response += getTableRow2Col("Vcc", vccString);
response += getTableFoot();
#elif ESP32
// ESP32 Info table
response += getTableHead2Col("ESP32 information", "Name", "Value");
//TODO: ESP32 Info table Need to be coded. I cant find anithing about this for ESP32 but is working anyways so...
response += getTableFoot();
#endif
response += "</body></html>";
request->send(200, "text/html", response);
DEBUGLN("--- systemHandler - End ---");
}
// Called when changing wifi settings
// 1. when to Wifi connection defined
// 2. during normal operation
void wifiHandler(AsyncWebServerRequest *request) {
DEBUGLN("--- wifiHandler - Start ---");
int args = request->args();
AsyncWebParameter *p = request->getParam("ssid", true);
if (p) {
String _ssid = p->value();
writeStringToEEPROM(_ssid, SSID_START, SSID_MAX);
DEBUG("SSID: ");
DEBUGLN(_ssid.c_str());
}
else {
DEBUG("Erasing SSID.");
writeStringToEEPROM("", SSID_START, SSID_MAX);
}
p = request->getParam("password", true);
if (p) {
String _password = p->value();
writeStringToEEPROM(_password, PASSWORD_START, PASSWORD_MAX);
DEBUG("Password: ");
DEBUGLN(_password.c_str());
}
else {
DEBUG("Erasing password.");
writeStringToEEPROM("", PASSWORD_START, PASSWORD_MAX);
}
p = request->getParam("hostname", true);
if (p) {
String _hostname = p->value();
writeStringToEEPROM(_hostname, HOSTNAME_START, HOSTNAME_MAX);
DEBUG("Hostname: ");
DEBUGLN(_hostname.c_str());
}
else {
DEBUG("Erasing Hostname.");
writeStringToEEPROM("", HOSTNAME_START, HOSTNAME_MAX);
}
DEBUGLN("--- wifiHandler - End ---");
// Set default values
DEBUGLN("--- setDefaulValues - Start ---");
setDefaulValues();
DEBUGLN("--- setDefaulValues - End ---");
ESP.restart();
}
// Turn off LEDs
void turnOffLeds(byte beStartNr, byte beEndNr) {
for (int i = beStartNr; i <= beEndNr; i++) {
crgbLeds[i] = CRGB::Black;
}
}
// Set LEDs color
void setLedsColor(byte beStartNr, byte beEndNr, CRGB crgbColor) {
for (int i = beStartNr; i <= beEndNr; i++) {
crgbLeds[i] = crgbColor;
}
}
// Convert HEX color to RGB
struct stRGBColors hexToRGB(String sHexColor) {
struct stRGBColors rgbColors;
char charbuf[8];
long int rgb;
sHexColor.toCharArray(charbuf, 8);
rgb = strtol(charbuf, 0, 16); //=>rgb=0x001234FE;
rgbColors.beRed = (byte)(rgb >> 16);
rgbColors.beGreen = (byte)(rgb >> 8);
rgbColors.beBlue = (byte)(rgb);
return rgbColors;
}
// Convert RGB color to HEX
String rgbToHex(byte beRed, byte beGreen, byte beBlue) {
char hexColor[16] = "";
snprintf(hexColor, sizeof hexColor, "%02x%02x%02x", beRed, beGreen, beBlue);
//long hexColor = ((long)beRed << 16L) | ((long)beGreen << 8L) | (long)beBlue;
//sprintf(hexColor, "#%02x%02x%02x", beRed, beGreen, beBlue);
return String(hexColor);
}
// Change LED color pattern parameters
void changeLedColorPatternParameters(byte beColorParameter) {
CRGB crgbPurple = CHSV(HUE_PURPLE, 255, 255);
CRGB crgbGreen = CHSV(HUE_GREEN, 255, 255);
CRGB crgbBlack = CRGB::Black;
switch (beLedColorPattern) {
case 1: //Cloud
crgbCurrentPalette = CloudColors_p;
break;
case 2: //Lava
crgbCurrentPalette = LavaColors_p;
break;
case 3: //Ocean
crgbCurrentPalette = OceanColors_p;
break;
case 4: //Forest
crgbCurrentPalette = ForestColors_p;
break;
case 5: //Rainbow
crgbCurrentPalette = RainbowColors_p;
break;
case 6: //Rainbow Stripes
crgbCurrentPalette = RainbowStripeColors_p;
break;
case 7: //Party
crgbCurrentPalette = PartyColors_p;
break;
case 8: //Heat
crgbCurrentPalette = HeatColors_p;
break;
case 9: //Fire
crgbCurrentPalette = CRGBPalette16(
CRGB::Black, CRGB::Black, CRGB::Black, CHSV(0, 255,4),
CHSV(0, 255, 8), CRGB::Red, CRGB::Red, CRGB::Red,
CRGB::DarkOrange,CRGB::Orange, CRGB::Orange, CRGB::Orange,
CRGB::Yellow, CRGB::Yellow, CRGB::Gray, CRGB::Gray);
break;
case 10: //Random
for (int i = 0; i < 16; i++) {
crgbCurrentPalette[i] = CHSV(random8(), 255, random8());
}
break;
case 11: //Purple and Green
crgbCurrentPalette = CRGBPalette16(
crgbGreen, crgbGreen, crgbBlack, crgbBlack,
crgbPurple, crgbPurple, crgbBlack, crgbBlack,
crgbGreen, crgbGreen, crgbBlack, crgbBlack,
crgbPurple, crgbPurple, crgbBlack, crgbBlack
);
break;
case 12: //Red, White and Blue
crgbCurrentPalette = myRedWhiteBluePalette_p;
break;
}
// Change also pattern
if (beLedPattern == 5) {
setFirePattern();
}
}
// Switch selected pattern
void switchLedPattern(byte bePatternParameter) {
if (bePatternParameter >= 0 && bePatternParameter <= 99)
changeLedPatternParameters(bePatternParameter);
else if (bePatternParameter >= 99 && bePatternParameter < 199)
changeLedFixedPatternParameters(bePatternParameter);
else
;
}
// Change LED pattern parameters
void changeLedPatternParameters(byte bePatternParameter) {
switch (beLedPattern) {
case 1: //Fixed
setPattern(0);
fill_solid(crgbLeds, iNrOfLeds, crgbLedColors);
FastLED.show();
break;
case 2: //Standard
currentBlending = LINEARBLEND;
setPattern(1);
break;
case 3: //BPM
currentBlending = LINEARBLEND;
setPattern(5);
break;
case 4: //KITT
currentBlending = LINEARBLEND;
setPattern(6);
break;
case 5: //Plasma
currentBlending = LINEARBLEND;
setPattern(7);
break;
case 6: //Fire
currentBlending = LINEARBLEND;
setFirePattern();
setPattern(9);
break;
case 99:
FastLED.clear();
turnOffLeds(0, iNrOfLeds);
FastLED.show();
// Restart ESP to update values
ESP.restart();
break;
}
}
// Change LED fixed pattern parameters
void changeLedFixedPatternParameters(byte bePatternParameter) {
switch (beLedPattern) {
case 100: //Confetti
currentBlending = LINEARBLEND;
setPattern(2);
break;
case 101: //Sinelon
currentBlending = LINEARBLEND;
setPattern(3);
break;
case 102: //Juggle
currentBlending = LINEARBLEND;
setPattern(4);
break;
case 103: //Blendwave
currentBlending = LINEARBLEND;
setPattern(8);
break;
case 104: //Rainbow beat
currentBlending = LINEARBLEND;
setPattern(10);
break;
case 105: //Full rainbow
currentBlending = LINEARBLEND;
setPattern(11);
break;
case 106: //Rainbow with glitter
currentBlending = LINEARBLEND;
setPattern(12);
break;
case 99:
FastLED.clear();
turnOffLeds(0, iNrOfLeds);
FastLED.show();
// Restart ESP to update values
ESP.restart();
break;
}
}
// Check if actual hour is within display limits (Display On/Off hours)
boolean checkHour() {
byte beActualHour = hour();
boolean bDisplayDigits = beActualHour >= beDisplayOn && beActualHour < beDisplayOff;
boolean bRet = false;
if (beDisplayOn >= beDisplayOff) {
bDisplayDigits = beActualHour >= beDisplayOn || beActualHour < beDisplayOff;
}
if (bDisplayDigits) {
bRet = true;
}
return(bRet);
}
// Check if LEDs should be On/Off
boolean checkDisplay() {
boolean bRet = true;
// If Brighntness change is not executing
if (!bBrighntessChange) {
// Turn LED On/Off
// On = Automatic Display On is ON and Actual hour is within Display On hours limits
// OR
// Automatic Display On is ON and Manual Display On is on
// OR
// PIR sensor activated
// Off = everything else
if ((bAutDisplayEnabled && checkHour()) || (!bAutDisplayEnabled && bBacklight) || (bPirBacklightOn)) {
// On change
// beLedBrightness == STANDBY_LED_BRIGHTNESS -> Standby mode
if (beLedBrightness == STANDBY_LED_BRIGHTNESS) {
beLedBrightness = beLedOldBrightness;
}
}
else {
// On change
if (beLedBrightness > STANDBY_LED_BRIGHTNESS) {
beLedOldBrightness = beLedBrightness;
}
stadbyMode();
bRet = false;
}
// Execute only if device is configured
if (strlen(cWifiSsid) > 0) {
// Change Rgb Led brightness
FastLED.setBrightness(beLedBrightness);
FastLED.show();
}
}
return(bRet);
}
// NTP Server
void ntpCheckServer(void *context) {
DEBUGLN("--- ntpCheckServer - Start ---");
int cbParsePacket, i;
cbParsePacket = 0;
i = 1;
// Send an NTP packet to a time server
// Wait to see if a reply is available
while (!cbParsePacket) {
DEBUGLN(sNtpServer);
DEBUGLN(ipaNtpServerIp);
// Translate DNS name to IP
WiFi.hostByName(sNtpServer.c_str(), ipaNtpServerIp);
sendNTPpacket(ipaNtpServerIp);
delay(1000 * i);
cbParsePacket = udp.parsePacket();
DEBUGLN("No packet yet");
// i > 10 After 66s = (1000*i) = max 66s, stop sending packet and mark NTP packet error
if (i > 10) {
bNtpPacketError = true;
break;
}
i++;
}
// Switch to another NTP Server
if (!cbParsePacket) {
DEBUGLN("No packet yet");
DEBUGLN("Calling ntpCheckServer");
ntpCheckServer((void *)0);
}
else {
DEBUGLN(sNtpServer);
DEBUGLN(ipaNtpServerIp);
DEBUG("Packet received, length=");
DEBUGLN(cbParsePacket);
// Received a packet, read the data from it
// read the packet into the buffer
udp.read(beNtpPacketBuffer, NTPPACKETSIZE);
//the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, esxtract the two words:
unsigned long highWord = word(beNtpPacketBuffer[40], beNtpPacketBuffer[41]);
unsigned long lowWord = word(beNtpPacketBuffer[42], beNtpPacketBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
// now convert NTP time into everyday time:
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// subtract seventy years:
unsigned long epoch = secsSince1900 - seventyYears;
// print Unix time:
//DEBUGLN(epoch);
utc = epoch;
localTime = tzCe.toLocal(utc, &tcr);
// Set device time
setTime(localTime);
DEBUGLN(hour());
DEBUGLN(minute());
DEBUGLN(second());
}
DEBUGLN("--- ntpCheckServer - End ---");
}
// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress& address) {
DEBUGLN("--- sendNTPpacket - Start ---");
// set all bytes in the buffer to 0
memset(beNtpPacketBuffer, 0, NTPPACKETSIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
beNtpPacketBuffer[0] = 0b11100011; // LI, Version, Mode
beNtpPacketBuffer[1] = 0; // Stratum, or type of clock
beNtpPacketBuffer[2] = 6; // Polling Interval
beNtpPacketBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
beNtpPacketBuffer[12] = 49;
beNtpPacketBuffer[13] = 0x4E;
beNtpPacketBuffer[14] = 49;
beNtpPacketBuffer[15] = 52;
// All NTP fields have been given values, now
// You can send a packet requesting a timestamp:
udp.beginPacket(address, 123); //NTP requests are to port 123
udp.write(beNtpPacketBuffer, NTPPACKETSIZE);
udp.endPacket();
DEBUGLN("--- sendNTPpacket - End ---");
}
// LED patterns
void fillLedsFromPaletteColors(uint8_t ui8ColorIndex)
{
uint8_t ui8Brightness = 255;
for (int i = 0; i < ((iNrOfLeds / beNrOfLedsCorner) + beNrOfLedsCorner); i++) {
for (int j = i * beNrOfLedsCorner; j < ((i * beNrOfLedsCorner) + beNrOfLedsCorner); j++) {
crgbLeds[j] = ColorFromPalette(crgbCurrentPalette, ui8ColorIndex, ui8Brightness, currentBlending);
}
ui8ColorIndex += 3;
}
}
#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))
void setPattern(int numPattern){
// add one to the current pattern number, and wrap around at the end
gCurrentPatternNumber = (numPattern) % ARRAY_SIZE( gPatterns);
}
void standard(){
fillLedsFromPaletteColors(gHue);
}
void rainbow(){
// FastLED's built-in rainbow generator
fill_rainbow( crgbLeds, iNrOfLeds, gHue, 7);
}
void rainbowWithGlitter(){
// built-in FastLED rainbow, plus some random sparkly glitter
rainbow();
addGlitter(80);
}
void addGlitter( fract8 chanceOfGlitter){
if( random8() < chanceOfGlitter) {
crgbLeds[ random16(iNrOfLeds) ] += CRGB::White;
}
}
void fullColor(){
setLedsColor(0, iNrOfLeds, crgbLedColors);
FastLED.show();
}
void confetti(){
// random colored speckles that blink in and fade smoothly
fadeToBlackBy( crgbLeds, iNrOfLeds, 10);
int pos = random16(iNrOfLeds);
crgbLeds[pos] += CHSV( gHue + random8(64), 200, 255);
}
void sinelon(){
// a colored dot sweeping back and forth, with fading trails
fadeToBlackBy( crgbLeds, iNrOfLeds, 20);
int pos = beatsin16( 13, 0, iNrOfLeds-1 );
crgbLeds[pos] += CHSV( gHue, 255, 192);
}
void juggle(){
// eight colored dots, weaving in and out of sync with each other
fadeToBlackBy( crgbLeds, iNrOfLeds, 20);
byte dothue = 0;
for( int i = 0; i < 8; i++) {
crgbLeds[beatsin16( i+7, 0, iNrOfLeds-1 )] |= CHSV(dothue, 200, 255);
dothue += 32;
}
}
void bpm(){
// colored stripes pulsing at a defined Beats-Per-Minute (BPM)
uint8_t BeatsPerMinute = 62;
uint8_t beat = beatsin8( BeatsPerMinute, 64, 255);
for( int i = 0; i < iNrOfLeds; i++) { //9948
crgbLeds[i] = ColorFromPalette(crgbCurrentPalette, gHue+(i*2), beat-gHue+(i*10));
}
}
void kitt(){
// a colored dot sweeping back and forth, with fading trails
uint8_t BeatsPerMinute = 62;
uint8_t beat = beatsin8( BeatsPerMinute, 64, 255);
fadeToBlackBy( crgbLeds, iNrOfLeds, 20);
int pos = beatsin16( 13, 0, iNrOfLeds-1 );
crgbLeds[pos] += ColorFromPalette(crgbCurrentPalette, gHue+(pos*2), beat-gHue+(pos*10));
}
#define qsuba(x, b) ((x>b)?x-b:0) // Analog Unsigned subtraction macro. if result <0, then => 0
void plasma(){ // This is the heart of this program. Sure is short. . . and fast.
int thisPhase = beatsin8(6,-64,64); // Setting phase change for a couple of waves.
int thatPhase = beatsin8(7,-64,64);
for (int k=0; k<iNrOfLeds; k++) { // For each of the LED's in the strand, set a brightness based on a wave as follows:
int colorIndex = cubicwave8((k*23)+thisPhase)/2 + cos8((k*15)+thatPhase)/2; // Create a wave and add a phase change and add another wave with its own phase change.. Hey, you can even change the frequencies if you wish.
int thisBright = qsuba(colorIndex, beatsin8(7,0,96)); // qsub gives it a bit of 'black' dead space by setting sets a minimum value. If colorIndex < current value of beatsin8(), then bright = 0. Otherwise, bright = colorIndex..
crgbLeds[k] = ColorFromPalette(crgbCurrentPalette, colorIndex, thisBright, LINEARBLEND); // Let's now add the foreground colour.
}
}
CRGB clr1;
CRGB clr2;
uint8_t speed;
uint8_t loc1;
void blendwave(){
speed = beatsin8(6,0,255);
clr1 = blend(CHSV(beatsin8(3,0,255),255,255), CHSV(beatsin8(4,0,255),255,255), speed);
clr2 = blend(CHSV(beatsin8(4,0,255),255,255), CHSV(beatsin8(3,0,255),255,255), speed);
loc1 = beatsin8(10,0,iNrOfLeds-1);
fill_gradient_RGB(crgbLeds, 0, clr2, loc1, clr1);
fill_gradient_RGB(crgbLeds, loc1, clr2, iNrOfLeds-1, clr1);
}
uint32_t xscale = 20; // How far apart they are
uint32_t yscale = 3; // How fast they move
uint8_t indexFire = 0;
void inoise8_fire() {
for(int i = 0; i < NUM_LEDS; i++) {
indexFire = inoise8(i*xscale,millis()*yscale*iNrOfLeds/255); // X location is constant, but we move along the Y at the rate of millis()
crgbLeds[i] = ColorFromPalette(crgbCurrentPalette, min(i*(indexFire)>>6, 255), i*255/iNrOfLeds, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED.
} // The higher the value of i => the higher up the palette index (see palette definition).
// Also, the higher the value of i => the brighter the LED.
}
void setFirePattern() {
crgbCurrentPalette[0] = CRGB::Black;
crgbCurrentPalette[1] = CRGB::Black;
crgbCurrentPalette[2] = CRGB::Black;
crgbCurrentPalette[3] = CHSV(0, 255, 4);
crgbCurrentPalette[4] = CHSV(0, 255, 8);
crgbCurrentPalette[14] = CRGB::Gray;
crgbCurrentPalette[15] = CRGB::Gray;
}
void rainbow_beat() {
uint8_t beatA = beatsin8(17, 0, 255); // Starting hue
uint8_t beatB = beatsin8(13, 0, 255);
fill_rainbow(crgbLeds, iNrOfLeds, (beatA+beatB)/2, 8); // Use FastLED's fill_rainbow routine.
}
// This example shows how to set up a static color palette
// which is stored in PROGMEM (flash), which is almost always more
// plentiful than RAM. A static PROGMEM palette like this
// takes up 64 bytes of flash.
const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM =
{
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
};
// MQTT functions
// MQTT Callback
void mqttCallback(char* topic, byte* payload, unsigned int length) {
DEBUGLN("--- mqttCallback - Start ---");
char cMqttValue[21];
DEBUG("MQTT Message arrived [");
DEBUG(topic);
DEBUGLN("] ");
for (int i = 0; i < length; i++) {
cMqttValue[i] = (char)payload[i];
cMqttValue[i + 1] = '\0';
}
// Brightness
if (strcmp(topic, cMqttSetTopic[0]) == 0) {
DEBUG("MQTT Brightness: ");
DEBUGLN(cMqttValue);
// If brightness value changed remember previous value
if (abs(beLedBrightness - atoi(cMqttValue)) > 0) {
beLedOldBrightness = beLedBrightness;
}
beLedBrightness = atoi(cMqttValue);
// Publish new value
mqttClient.publish(cMqttGetTopic[0], cMqttValue, true);
FastLED.setBrightness(beLedBrightness);
FastLED.show();
}
// Pattern
else if (strcmp(topic, cMqttSetTopic[1]) == 0) {
DEBUG("MQTT Pattern: ");
DEBUGLN(cMqttValue);
// Clear Pattern value
if (atoi(cMqttValue) > 0) {
mqttClient.publish(cMqttGetTopic[2], "0", true);
}
if (atoi(cMqttValue) > 0) {
beLedPattern = atoi(cMqttValue);
FastLED.clear();
changeLedPatternParameters(beLedPattern);
}
// Publish new value
mqttClient.publish(cMqttGetTopic[1], cMqttValue, true);
}
// Fixed pattern
else if (strcmp(topic, cMqttSetTopic[2]) == 0) {
DEBUG("MQTT Fixed pattern: ");
DEBUGLN(cMqttValue);
// Clear Fixed pattern value
if (atoi(cMqttValue) > 0) {
mqttClient.publish(cMqttGetTopic[1], "0", true);
}
if (atoi(cMqttValue) > 0) {
beLedPattern = atoi(cMqttValue);
FastLED.clear();
changeLedFixedPatternParameters(beLedPattern);
}
// Publish new value
mqttClient.publish(cMqttGetTopic[2], cMqttValue, true);
}
// Pattern color
else if (strcmp(topic, cMqttSetTopic[3]) == 0) {
DEBUG("MQTT Pattern color: ");
DEBUGLN(cMqttValue);
beLedColorPattern = atoi(cMqttValue);
FastLED.clear();
changeLedColorPatternParameters(beLedColorPattern);
// Publish new value
mqttClient.publish(cMqttGetTopic[3], cMqttValue, true);
}
// Color
else if (strcmp(topic, cMqttSetTopic[4]) == 0) {
DEBUG("MQTT Color: ");
DEBUGLN(cMqttValue);
char cValue[16];
sprintf(cValue, "%s", cMqttValue);
char* cColors = strtok(cMqttValue, ",");
byte i = 0;
while (cColors != NULL)
{
switch (i) {
// Red
case 0:
stLedColors.beRed = atoi(cColors);
break;
// Green
case 1:
stLedColors.beGreen = atoi(cColors);
break;
// Blue
case 2:
stLedColors.beBlue = atoi(cColors);
break;
}
i++;
cColors = strtok(NULL, ",");
}
crgbLedColors = CRGB(stLedColors.beRed, stLedColors.beGreen, stLedColors.beBlue);
FastLED.clear();
if (beLedPattern == 0) {
setLedsColor(0, iNrOfLeds, crgbLedColors);
FastLED.show();
}
// Publish new value
mqttClient.publish(cMqttGetTopic[4], cValue, true);
sLedHexColor = rgbToHex(stLedColors.beRed, stLedColors.beGreen, stLedColors.beBlue);
}
// Speed
else if (strcmp(topic, cMqttSetTopic[5]) == 0) {
DEBUG("MQTT Speed: ");
DEBUGLN(cMqttValue);
beLedSpeed = atoi(cMqttValue);
// Publish new value
mqttClient.publish(cMqttGetTopic[5], cMqttValue, true);
}
// Manual Display On
else if (strcmp(topic, cMqttSetTopic[6]) == 0) {
DEBUG("MQTT Manual Display On: ");
DEBUGLN(cMqttValue);
bBacklight = atoi(cMqttValue);
DEBUGLN(cMqttValue);
// Publish new value
mqttClient.publish(cMqttGetTopic[6], cMqttValue, true);
}
// Automatic Display On enabled
else if (strcmp(topic, cMqttSetTopic[7]) == 0) {
DEBUG("MQTT Automatic Display On enabled: ");
DEBUGLN(cMqttValue);
bAutDisplayEnabled = atoi(cMqttValue);
DEBUGLN(cMqttValue);
// Publish new value
mqttClient.publish(cMqttGetTopic[7], cMqttValue, true);
}
// Display On hour
else if (strcmp(topic, cMqttSetTopic[8]) == 0) {
DEBUG("MQTT Display On hour: ");
DEBUGLN(cMqttValue);
beDisplayOn = atoi(cMqttValue);
DEBUGLN(cMqttValue);
// Publish new value
mqttClient.publish(cMqttGetTopic[8], cMqttValue, true);
}
// Display Off hour
else if (strcmp(topic, cMqttSetTopic[9]) == 0) {
DEBUG("MQTT Display Off hour: ");
DEBUGLN(cMqttValue);
beDisplayOff = atoi(cMqttValue);
DEBUGLN(cMqttValue);
// Publish new value
mqttClient.publish(cMqttGetTopic[9], cMqttValue, true);
}
// PIR sensor enabled
else if (strcmp(topic, cMqttSetTopic[10]) == 0) {
DEBUG("MQTT PIR sensor enabled: ");
DEBUGLN(cMqttValue);
bPirSensorEnabled = atoi(cMqttValue);
DEBUGLN(cMqttValue);
// Publish new value
mqttClient.publish(cMqttGetTopic[10], cMqttValue, true);
}
DEBUGLN("--- mqttCallback - End ---");
}
// Connect to server
void mqttConnect() {
DEBUGLN("--- mqttConnect - Start ---");
byte beMqttCounter = 0;
// Try to create connection
while (!mqttClient.connected()) {
DEBUGLN("Attempting MQTT connection...");
// Conect to MQTT broker with Hostname, User name, Password
if (mqttClient.connect(cMqttClientId, cMqttUserName, cMqttUserPassword)) {
DEBUGLN("connected");
// Subscribe MQTT topics
for (int i = 0; i < sizeof cMqttSetTopic / sizeof cMqttSetTopic[0]; i++) {
mqttClient.subscribe(cMqttSetTopic[i]);
DEBUG("Subscribbed to: ");
DEBUGLN(cMqttSetTopic[i]);
}
// Send status data to MQTT broker
//mqttSendStatus((void *)0);
}
else {
DEBUG("failed, rc=");
DEBUG(mqttClient.state());
DEBUGLN(" try again in 3 seconds");
// Wait 3 seconds before retrying
delay(3000);
beMqttCounter++;
}
// If counter exceed limit, stop MQTT connection
if (beMqttCounter > 5) {
bMqttConnError = true;
break;
}
}
DEBUGLN("--- mqttConnect - End ---");
}
// Check for MQTT message
void mqttCheckMessage(void *context) {
mqttClient.loop();
}
// Send MQTT status
void mqttSendStatus(void *context) {
DEBUGLN("--- mqttSendStatus - Start ---");
for (int i = 0; i < sizeof cMqttGetTopic / sizeof cMqttGetTopic[0]; i++) {
char cMqttValue[21];
switch (i) {
// Brightness
case 0:
sprintf(cMqttValue, "%i", beLedBrightness);
break;
// Pattern
case 1:
sprintf(cMqttValue, "%i", beLedPattern);
break;
// Fixed pattern
case 2:
sprintf(cMqttValue, "%i", beLedPattern);
break;
// Pattern color
case 3:
sprintf(cMqttValue, "%i", beLedColorPattern);
break;
// Color
case 4:
sprintf(cMqttValue, "%i,%i,%i", stLedColors.beRed, stLedColors.beGreen, stLedColors.beBlue);
break;
// Speed
case 5:
sprintf(cMqttValue, "%i", beLedSpeed);
break;
// Manual Display On
case 6:
sprintf(cMqttValue, "%i", bBacklight);
break;
// Automatic Display On enabled
case 7:
sprintf(cMqttValue, "%i", bAutDisplayEnabled);
break;
// Display On hour
case 8:
sprintf(cMqttValue, "%i", beDisplayOn);
break;
// Display Off hour
case 9:
sprintf(cMqttValue, "%i", beDisplayOff);
break;
// PIR sensor enabled
case 10:
sprintf(cMqttValue, "%i", bPirSensorEnabled);
break;
}
// Publish new value
mqttClient.publish(cMqttGetTopic[i], cMqttValue, true);
DEBUG("Published to: ");
DEBUGLN(cMqttGetTopic[i]);
DEBUG("Value: ");
DEBUGLN(cMqttValue);
}
DEBUGLN("--- mqttSendStatus - Start ---");
}
// Stadby mode
void stadbyMode() {
turnOffLeds(0, iNrOfLeds);
crgbLeds[1] = CRGB::Red;
beLedBrightness = STANDBY_LED_BRIGHTNESS;
}
// PIR sensor
void getPirSensorState(uint8_t uiPirPinState) {
if (uiPirPinState == HIGH) {
if (bPirLockLow) {
// Makes sure we wait for a transition to LOW before any further output is made
bPirLockLow = false;
bPirBacklightOn = true;
DEBUGLN("Motion detected.");
}
bPirTakeLowTime = true;
}
if (uiPirPinState == LOW) {
if (bPirTakeLowTime) {
iPirLowIn = millis(); // save the time of the transition from high to LOW
bPirTakeLowTime = false; // Make sure this is only done at the start of a LOW phase
}
// If the sensor is low for more than the given iPirPause, we assume that no more motion is going to happen
if (!bPirLockLow && millis() - iPirLowIn > iPirPause) {
// Makes sure this block of code is only executed again after a new motion sequence has been detected
bPirLockLow = true;
bPirBacklightOn = false;
DEBUGLN("Motion ended.");
}
}
}
// ----------------------------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment