#include <WiFi.h>
#include <PubSubClient.h>
#include <FastLED.h>
#include "wifi_secrets.h"
#include "mqtt_secrets.h"
#include "data.h"
#define DEBUG_SERIAL_PORT
#define PIR_PIN 12 // GPIO 0
#define LED_PIN 2 // GPIO 2
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
#define NUM_LEDS 60
#define MQTT_VERSION MQTT_VERSION_3_1_1
// MQTT: topics
// state
const PROGMEM char* MQTT_LIGHT_STATE_TOPIC = "home/bedroom/night-light/light/state";
const PROGMEM char* MQTT_LIGHT_COMMAND_TOPIC = "home/bedroom/night-light/light/switch";
// brightness
const PROGMEM char* MQTT_BRIGHTNESS_STATE_TOPIC = "home/bedroom/night-light/brightness/status";
const PROGMEM char* MQTT_BRIGHTNESS_COMMAND_TOPIC = "home/bedroom/night-light/brightness/set";
// colors (rgb)
const PROGMEM char* MQTT_RGB_STATE_TOPIC = "home/bedroom/night-light/rgb/status";
const PROGMEM char* MQTT_RGB_COMMAND_TOPIC = "home/bedroom/night-light/rgb/set";
// motior sensor
const PROGMEM char* MQTT_MOTION_STATE_TOPIC = "home/bedroom/night-light/motion/status";
const PROGMEM char* MQTT_MOTION_AVAILABILITY_TOPIC = "home/bedroom/night-light/motion/availability";
// always on
const PROGMEM char* MQTT_ALWAYS_ON_STATE_TOPIC = "home/bedroom/night-light/always-on/status";
const PROGMEM char* MQTT_ALWAYS_ON_COMMAND_TOPIC = "home/bedroom/night-light/always-on/set";
// payloads
const PROGMEM char* LIGHT_ON = "ON";
const PROGMEM char* LIGHT_OFF = "OFF";
const PROGMEM char* MOTION_ON = "ON";
const PROGMEM char* MOTION_OFF = "OFF";
const PROGMEM char* AVAILABLE = "online";
const PROGMEM char* NOT_AVAILABLE = "offline";
boolean
animateLeds = false,
pirState = false,
alwaysOnState = false,
motionState = false,
lightState = true;
uint8_t
brightness = 150,
Rcolor = 255,
Gcolor = 0,
Bcolor = 255;
// buffer used to send/receive data with MQTT
const uint8_t MSG_BUFFER_SIZE = 20;
char msg_buffer[MSG_BUFFER_SIZE];
static uint32_t
currentTime,
tmr1 = millis(),
tmr2 = millis(),
tmr3 = millis(),
tmr4 = millis(),
tmr5 = millis(),
tmr6 = millis(),
tmr7 = millis();
WiFiClient espClient;
PubSubClient mqttClient(espClient);
CRGB leds[NUM_LEDS];
void setup() {
delay(2000);
Serial.begin(115200);
setupPins();
setupStrip();
// wifiConnect();
// setupMQTT();
}
void setupPins() {
pinMode(PIR_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT);
}
void setupStrip() {
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
FastLED.setMaxPowerInVoltsAndMilliamps(5, 1000);
FastLED.setBrightness(0);
FastLED.clear();
FastLED.show();
}
void setupMQTT() {
mqttClient.setServer(MQTT_BROKER, MQTT_PORT);
mqttClient.setCallback(callback);
}
void wifiConnect() {
//delay(10);
DEBUGLN();
DEBUG("Connecting to ");
DEBUGLN(WIFI_SSID);
WiFi.begin("Wokwi-GUEST", "", 6);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
DEBUG(".");
}
DEBUGLN("");
DEBUGLN("WiFi connected");
DEBUGLN("IP address: ");
DEBUGLN(WiFi.localIP());
}
void callback(char* topic, byte* payload, unsigned int length) {
// payload[length] = '\0';
// String message = (char*)payload;
String message;
for (uint8_t i = 0; i < length; i++) {
message.concat((char)payload[i]);
}
// handle message topic
if (String(MQTT_LIGHT_COMMAND_TOPIC).equals(topic)) {
if (message.equals(String(LIGHT_ON))) {
if (lightState != true) {
lightState = true;
publishLightState();
DEBUGLN("Light turned on");
animateLeds = true;
}
} else if (message.equals(String(LIGHT_OFF))) {
if (lightState != false) {
lightState = false;
publishLightState();
DEBUGLN("Light turned off");
animateLeds = true;
}
}
} else if (String(MQTT_BRIGHTNESS_COMMAND_TOPIC).equals(topic)) {
uint8_t new_brightness = message.toInt();
if (new_brightness < 0 || new_brightness > 255) {
return;
}
brightness = new_brightness;
publishBrightnessState();
DEBUGLN("Set new brightness: " + String(new_brightness));
animateLeds = true;
} else if (String(MQTT_RGB_COMMAND_TOPIC).equals(topic)) {
uint8_t firstIndex = message.indexOf(',');
uint8_t lastIndex = message.lastIndexOf(',');
uint8_t rgb_red = message.substring(0, firstIndex).toInt();
uint8_t rgb_green = message.substring(firstIndex + 1, lastIndex).toInt();
uint8_t rgb_blue = message.substring(lastIndex + 1).toInt();
if ((rgb_red < 0 || rgb_red > 255) || (rgb_green < 0 || rgb_green > 255) || (rgb_blue < 0 || rgb_blue > 255)) {
return;
}
Rcolor = rgb_red;
Gcolor = rgb_green;
Bcolor = rgb_blue;
setColor();
FastLED.show();
publishRGBState();
DEBUGLN("Set new color: " + String(Rcolor) + "," + String(Gcolor) + "," + String(Bcolor));
animateLeds = true;
} else if (String(MQTT_ALWAYS_ON_COMMAND_TOPIC).equals(topic)) {
if (message.equals(String(LIGHT_ON))) {
if (alwaysOnState != true) {
alwaysOnState = true;
publishAlwaysOnState();
DEBUGLN("AlwaysOn turned on");
animateLeds = true;
}
} else if (message.equals(String(LIGHT_OFF))) {
if (alwaysOnState != false) {
alwaysOnState = false;
publishAlwaysOnState();
DEBUGLN("AlwaysOn turned off");
animateLeds = true;
}
}
}
}
void loop() {
currentTime = millis();
// if (!mqttClient.connected())
// reconnect();
// mqttClient.loop();
//blinkBuiltInLed();
//updatePirState();
//controllLeds();
syncPirState();
test();
// if (!animateLeds) return;
// if ((millis() - tmr5) > 500) {
// }
autoRestart();
}
void autoRestart() {
if ((currentTime - tmr7) > 86400000UL) {
ESP.restart();
tmr5 = currentTime;
}
}
void syncPirState() {
if ((currentTime - tmr5) > 100UL) {
tmr5 = currentTime;
pirState = digitalRead(PIR_PIN);
digitalWrite(LED_BUILTIN, pirState);
if (pirState) {
prolongLight();
}
}
}
void prolongLight() {
tmr6 = currentTime;
}
void test() {
if ((currentTime - tmr6) > 5000UL) {
fill_solid(leds, NUM_LEDS, CRGB(Rcolor, Gcolor, Bcolor));
FastLED.setBrightness(100);
FastLED.show();
} else {
FastLED.clear();
FastLED.setBrightness(0);
FastLED.show();
}
}
void blinkBuiltInLed() {
if ((millis() - tmr1) > 1000UL) {
tmr1 = millis();
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}
}
void updatePirState() {
if ((currentTime - tmr2) > 100UL) {
tmr2 = currentTime;
pirState = digitalRead(PIR_PIN);
publishMotionStateOnChange();
}
}
void controllLeds() {
if (!animateLeds) return;
static bool direction = false;
uint8_t currentBrightness = FastLED.getBrightness();
//DEBUGLN("direction: " + String(direction));
if ((millis() - tmr3) > (direction ? 10UL : 20UL)) {
tmr3 = millis();
direction = (lightState && pirState) || alwaysOnState;
if (direction) {
DEBUGLN("FADE IN");
if (currentBrightness != brightness) {
applyBrightness((brightness > currentBrightness) ? currentBrightness + 1 : currentBrightness - 1 );
} else {
animateLeds = false;
}
} else {
DEBUGLN("FADE OUT");
if (currentBrightness > 0) {
applyBrightness(currentBrightness - 1);
} else {
animateLeds = false;
}
}
}
}
void applyBrightness(uint8_t value) {
FastLED.clear();
setColor();
//FastLED.setBrightness(((long)value * value + 255) >> 8);
FastLED.setBrightness(value);
FastLED.show();
DEBUGLN("apply brightness: " + String(value));
}
void publishMotionStateOnChange() {
if (pirState != motionState) {
motionState = pirState;
animateLeds = true;
publishMotionState();
DEBUGLN("motion state changed!");
}
// if (pirState) {
// if (!motionState) {
// motionState = true;
// animateLeds = true;
// publishMotionState();
// }
// } else {
// if (motionState) {
// motionState = false;
// animateLeds = true;
// publishMotionState();
// }
// }
}
void setColor() {
fill_solid(leds, NUM_LEDS, CRGB(Rcolor, Gcolor, Bcolor));
DEBUGLN("Set leds color");
}
void publishAlwaysOnState() {
if (!mqttClient.connected()) return;
mqttClient.publish(MQTT_ALWAYS_ON_STATE_TOPIC, alwaysOnState ? LIGHT_ON : LIGHT_OFF, true);
}
void publishLightState() {
if (!mqttClient.connected()) return;
mqttClient.publish(MQTT_LIGHT_STATE_TOPIC, lightState ? LIGHT_ON : LIGHT_OFF, true);
}
void publishBrightnessState() {
if (!mqttClient.connected()) return;
snprintf(msg_buffer, MSG_BUFFER_SIZE, "%d", brightness);
mqttClient.publish(MQTT_BRIGHTNESS_STATE_TOPIC, msg_buffer, true);
}
void publishRGBState() {
if (!mqttClient.connected()) return;
snprintf(msg_buffer, MSG_BUFFER_SIZE, "%d,%d,%d", Rcolor, Gcolor, Bcolor);
mqttClient.publish(MQTT_RGB_STATE_TOPIC, msg_buffer, true);
}
void publishMotionState() {
if (!mqttClient.connected()) return;
DEBUGLN(motionState ? "Motion detected!" : "Motion stopped!");
mqttClient.publish(MQTT_MOTION_STATE_TOPIC, motionState ? MOTION_ON : MOTION_OFF, true);
}
void publishMotionAvailability() {
if (!mqttClient.connected()) return;
DEBUGLN("Motion available!");
mqttClient.publish(MQTT_MOTION_AVAILABILITY_TOPIC, AVAILABLE, true);
}
void reconnect() {
// Loop until we're reconnected
while (!mqttClient.connected()) {
DEBUG("Attempting MQTT connection...");
// Attempt to connect
String client_id = String(MQTT_CLIENT_ID) + "-" + String(WiFi.macAddress());
if (mqttClient.connect(MQTT_CLIENT_ID, MQTT_USERNAME, MQTT_PASSWORD)) {
DEBUGLN("-> MQTT client connected");
publishMotionAvailability();
publishAlwaysOnState();
publishLightState();
publishBrightnessState();
publishRGBState();
mqttClient.subscribe(MQTT_ALWAYS_ON_COMMAND_TOPIC);
mqttClient.subscribe(MQTT_LIGHT_COMMAND_TOPIC);
mqttClient.subscribe(MQTT_BRIGHTNESS_COMMAND_TOPIC);
mqttClient.subscribe(MQTT_RGB_COMMAND_TOPIC);
} else {
DEBUG("failed, rc=");
DEBUG(mqttClient.state());
DEBUGLN("-> try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
// The End