Categories
ESP32 Home Assistant

ESP32 LED-Steuerung

Dezente Beleuchtung ist eine schöne Sache. Vor allem, wenn sie sich einfach anpassen und steuern lässt.

Nachdem ich einige kommerzielle Lösungen angeschaut habe, habe ich mich dazu entschieden eine eigene auf Basis eines ESP32 zu entwickeln, die ich in den Home Assistant integrieren kann. Auch weil ich die Steuerung von Farbe, An- sowie Ausschaltzeitpunkt automatisieren wollte.

Funktionsweise

Zunächst muss man sich mit der Steuerung von LED-Bändern beschäftigen, um eine Steuerung entwickeln zu können. LED Bänder bestehen in der Regel aus vielen RGB (Rot-Grün-Blau) LEDs. Je nach Helligkeit der einzelnen Farben, ändert sich die wahrgenommene Farbe. Sind beispielsweise nur Rot und Grün zu je 100% an, ergibt das die Farbe gelb. Kommt blau zu 100% dazu, ergibt sich weiß. Je nach dem zu wie viel Prozent jede Farbe an ist, entsteht eine Mischfarbe. Wichtig ist das Verhältnis der Werte zueinander. Um die LEDs zu dimmen, werden die Verhältnisse der Farben zu einander gleich gehalten, jedoch alle LEDs insgesamt in der Helligkeit reduziert.

Im Gegensatz zu klassischen Glühbirnen, werden LEDs nicht durch das reduzieren der Spannung verdunkelt, sondern durch das “Takten” und die damit verbundene Helligkeitsreduzierung. Nehmen wir eine Sekunde; In dieser Sekunde ist die eine Farbe der LED die gesamte Zeit an, eine andere nur zu 50%. Als Betrachter nimmt man die andere Farbe somit um 50% weniger Hell wahr. Man schaltet jedoch nicht die andere Farbe eine halbe Sekunde ab und diese dann wieder eine halbe Sekunde an, sondern man wählt eine weitaus schnellere, für das Auge nicht wahrnehmbare Frequenz, in der die zweite Farbe an- und ausgeschaltet wird. Die Folge wäre sonst ein unangenehmer und ständiger Farbwechsel. Im Ergebnis ist; Die andere LED zwar in Summe nur 50% der Zeit an, aber der Wechsel zwischen AN und AUS ist so schnell, dass dieser vom menschlichen Auge nicht wahrgenommen wird. Was das menschliche Auge wahrnimmt ist eine geringere Helligkeit der anderen Farbe und damit bildet sich eine entsprechende Mischfarbe.

Ich habe 15m LED Band im Einsatz. Ein Test hat gezeigt, dass wie zu erwarten das in Reihe schalten der drei Bänder dazu führt, dass das letzte LED Band fast nicht mehr leuchtet. Die Bänder müssen also parallel an die Platine angeschlossen werden. Im Parallelbetrieb gemessen, habe ich bei der Farbe weiß, die den höchsten Stromfluss aufweist, zirka 4.5-5 Ampere bei einer Betriebsspannung von 12V gemessen. Das entspricht einer Leistung von zirka 60W. Das heißt der Pluspol über den der Maximalstrom von 5 Ampere fließt und die zugehörigen Komponenten müssen entsprechend dimensioniert werden. Wichtig an dieser Stelle ist: Man kann keine pauschale Aussage über die Stromaufnahme eines 15m LED Bandes treffen, das hängt von vielen Faktoren ab, beispielsweise wie viele LEDs pro Meter installiert sind.

Zudem habe ich festgestellt, dass die LEDs immer unter Spannung stehen. Das heißt es wird nur der Stromabfluss gesteuert, nicht der Zufluss. Das empfinde ich als großes Manko dieses Bandes, da ich eigentlich nicht möchte, dass dauerhaft 12V am Band anliegen.

Hardware

Zum Einsatz kommen ein ESP32, sowie eine selbst entwickelte Platine.

Das LED-Band besteht aus RGB 5050 LEDs.

Ein ESP32 hat den Vorteil, dass er das sogenannte PWM auf vielen PINs unterstützt. Es können zwar nur zehn verschiedene PWM Kanäle genutzt werden, was aber für ein RGB Band völlig ausreichend ist.

PWM bzw. ausgeschrieben Pulse-width modulation ermöglicht es, PINs des ESP32 in einer bestimmten Frequenz unter Bestimmung der Pulsbreite, also wie breit ist der Teil, in dem der PIN an und wie breit ist der Teil, in dem der PIN aus ist, zu setzen.

Das LED Band wird mit einer Spannung von 12V betrieben und benötigt hohe Ströme. Beides kann der ESP32 nicht leisten. Aus diesem Grund habe ich mich dafür entschieden für das Schalten des LED Bandes MOSFETs zu nutzen. MOSFETs haben den großen Vorteil, dass sie im Gegensatz zu Transistoren einen sehr geringen Innenwiderstand bzw. Spannungsabfall aufweisen. Aus diesem Grund fällt sehr wenig Leistung ab und die Wärmeentwicklung hält sich in Grenzen. Wichtig bei der Verwendung von MOSFETs ist, dass sie immer sauber durchgeschaltet oder gesperrt werden, sonst erhöht sich hier Innenwiderstand signifikant, was zwangsläufig zur Zerstörung des MOSFET führt. Je nach Typ sollte an einem MOSFET am Gate entweder 0V oder die Betriebsspannung (der Source) anliegen. Ein Blick auf das Datenblatt des ESP32 enthüllt; Der Mikrocontroller kann maximal 3.3V und Ströme im Milliampere Bereich leisten. Für ein sauberes Sperren bzw. Durchschalten des MOSFET nicht ausreichend. Aus diesem Grund verwende ich ich einen BC547C Transistor als Schalter, um die MOSFETs an zu steuern.

Um den “Abfluss” der Ströme der einzelnen Farben zu steuern verwende ich N-MOSFETs. Um den “Zufluss” bei deaktiviertem Band respektive einer Helligkeit von null ab zu schalten, verwende ich einen P-MOSFET. Warum man nicht eine Art MOSFET für beide Arten der Steuerung verwenden kann, lässt sich in entsprechender Fachliteratur oder im Internet nachlesen.

Da mein verwendeter P-MOSFETs einen signifikant höheren Innenwiderstand (~0,06 Ohm) aufweist, als die verwendeten N-MOSFETS (~0,006 Ohm) und sich daher eine entsprechende Wärmeentwicklung einstellt, habe ich den P-MOSFET mit einem Kühlköper, sowie das Gehäuse mit einem Lüfter ausgestattet. Mit ein Grund für den Lüfter ist, dass es im Inneren des Gartenhauses gerade im Sommer etwas wärmer werden kann und ich vermeiden wollte, dass sich im Gehäuse hohe Temperaturen bei einer sowieso schon hohen Außentemperatur bilden.

Platine

Hier die Platine dazu (Afillate): https://aisler.net/p/ZSWOKSID

Bauteile:

  • Mikrocontroller: ESP32 mit 30 PINs
  • C1, C2, C3 : 10nF
  • LED1, LED2, LED3, LED4 : Standard LED
    (dienen zur optischen Kontrolle)
  • R1, R3, R5, R7 : 1kO
  • R2, R4, R6 : 2.2kO
  • R8 : 2.2kO
  • R9, R10, R11, R12 : 3.3kO
  • R13, R14, R15, R16 : 5.6kO
  • L1 : 10uH
  • T1, T2, T3, T4 : Transistor BC547C
  • 3x MOSFET IRL1004
  • 1x MOSFET IRF5210
  • 1x DC Wandler RECOM R-78E50-05
  • 1x TO-220 Kühlkörper V FI356
  • 2x 7.62mm PCB Screw Terminal Block (3-Pin)

Messpunkte:

  • M1 : Blau
  • M2 : Rot
  • M3 : Grün
  • M4 : Eingangsspannung
  • M5 : On/Off Spannung
  • M6 : 5V DC
  • M7 : Gate Blau
  • M8 : Gate Rot
  • M9 : Gate Grün
  • M10 : Gate On/Off
  • M11 : GND
  • M12 : On/Off Spannung (wie M4)
Verlötete Platine (oben)
Verlötete Platine (seite)

Die Anschlüsse unten auf der Platine 3V3, GND, sowie SCL und SDA (I²C) dienen dazu, damit sich spätere Erweiterungen, wie beispielsweise ein Temperatursensor einfach nachrüsten lassen, falls man das möchte.

Gehäuse

Das Gehäuse ist 3D gedruckt und mit einem 40mm 12V Lüfter versehen. Der Lüfter wird an GND und Vout angeschlossen, damit dieser nur dann läuft, wenn das LED Band angeschaltet ist. Zudem habe ich vor Vin eine 10A Schmelzsicherung installiert, falls ein Kurzschluss z.B. in einem LED-Band auftritt und die Schutzschaltung im Netzteil nicht greifen sollte.

Die seitlichen Löcher im Gehäuse habe ich gebohrt. Der Lüfter bringt Luft ins Gehäuse, die über die Löcher austritt.

Gehäuse offen
Installierte LED-Steuerung, links zu sehen die Verteilung auf die LED-Bänder

Fotos

Home Assistant

Die Schwierigkeit mit Home Assistant liegt darin, dass die LED Farbe von RGB nach Hue und Saturation, sowie umgekehrt umgerechnet werden muss.

rest:
  - scan_interval: 60
    resource: http://<ip>/jsondoaction
    sensor:
     - name: "ESP32 Gartenhaus LED - Hue-Color"
       value_template: "{{ value_json.hue | int }}"
       
     - name: "ESP32 Gartenhaus LED - Saturation-Color"
       value_template: "{{ value_json.saturation | int }}"
       
     - name: "ESP32 Gartenhaus LED - Brightness"
       value_template: "{{ value_json.brightness | int }}"
rest_command:
  gartenhaus_led_set_color:
    url: "http://<ip>/jsondoaction"
    method: POST
    headers:
      accept: "application/json, text/html"
    payload: '{ "hue": {{ h }}, "saturation": {{ s }} }'
    content_type:  'application/json; charset=utf-8'
    
  gartenhaus_led_on:
    url: "http://<ip>/jsondoaction"
    method: POST
    headers:
      accept: "application/json, text/html"
    payload: '{ "ison": "1"}'
    content_type:  'application/json; charset=utf-8'
    
  gartenhaus_led_off:
    url: "http://<ip>/jsondoaction"
    method: POST
    headers:
      accept: "application/json, text/html"
    payload: '{ "ison": "0"}'
    content_type:  'application/json; charset=utf-8'
    
  gartenhaus_led_set_level:
    url: "http://<ip>/jsondoaction"
    method: POST
    headers:
      accept: "application/json, text/html"
    payload: '{ "brightness": {{ brightness }} }'
    content_type:  'application/json; charset=utf-8'
switch:
  - platform: rest
    name: "ESP32 Gartenhaus LED - Enabled"
    resource: http://<ip>/jsondoaction
    method: post
    body_on: "{'ison': '1' }"
    body_off: "{'ison': '0' }"
    is_on_template: "{{ value_json.ison }}"
    headers:
      Content-Type: application/json

Das Farbpicker Control wird hiermit erstellt und steht dann im Oberflächendesigner zur Verfügung:

light:
  - platform: template
    lights:
      gartenhaus_led_light:
        friendly_name: "Gartenhaus Colorpicker"
        color_template: "({{states('sensor.esp32_gartenhaus_led_hue_color') | int}}, {{states('sensor.esp32_gartenhaus_led_saturation_color') | int}})"
        level_template: "{{ states('sensor.esp32_gartenhaus_led_brightness') | int }}"
        set_color:
        - service: rest_command.gartenhaus_led_set_color
          data:
            h: "{{ h }}"
            s: "{{ s }}"
        set_level:
        - service: rest_command.gartenhaus_led_set_level
          data:
            brightness: "{{ brightness }}"
        turn_on:
        - service: rest_command.gartenhaus_led_on
        turn_off:
        - service: rest_command.gartenhaus_led_off
Home Assistant Steuerelement für Farbe und Helligkeit

Software

Der ESP32 meldet proaktiv Änderungen an den Home Assistant. Dafür ist ein sogenanntes Token oder Bearer genannt notwendig, das im Home Assistant generiert werden kann.

#include <ArduinoJson.h>
#include <HTTPClient.h>
#include <WebServer.h>

#include <WiFiClientSecure.h>

#include <TaskScheduler.h>

#include <NTPClient.h>
#include <WiFiUdp.h>

#include <ESP_Color.h>


const String LedEnabledSwitchName = "switch.esp32_gartenhaus_led_enabled";
const String LedHueIntName = "sensor.esp32_gartenhaus_led_hue_color";
const String LedSaturationIntName = "sensor.esp32_gartenhaus_led_saturation_color";
const String LedBrightnessIntName = "sensor.esp32_gartenhaus_led_brightness";

// --------- WIFI -----------

[...]

unsigned long previousMillis = 0;
unsigned long interval = 30000;

// --------- END WIFI -------

// --------- INITS -------

const char* ssid = STASSID;
const char* password = STAPSK;
const char* deviceName = DEVICENAME;
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "de.pool.ntp.org", 0, 0);
WebServer server(80);
StaticJsonDocument<1024> jsonDocument;
char jsonBuffer[1024];

const String HomeAssistantBearerName = "Authorization";
const String HomeAssistantBearerContent = "Bearer xxx";

const String SendApiIotUrl = "http://<homeAssistantIp>:8123/api/states/";



// --------- END INITS -------

// --------- SCHEDULER BEGIN -------

void checkFreeRam();
Task scheduleCheckFreeRam(21*1000, TASK_FOREVER, &checkFreeRam);

void wifiReconnectCheck();
Task scheduleWifiReconnectCheck(8*60*1000, TASK_FOREVER, &wifiReconnectCheck);

void refreshTime();
Task scheduleRefreshTime(10*60*1000, TASK_FOREVER, &refreshTime);

Scheduler runner;

// --------- SCHEDULER END ---------

// ------- DEFINITIONS ----------
static int selfCheckPinDuration = 500;
static String linkColorNormal = "#2321B0";
static String linkColorVisited = "#2321B0";
static String activeMarkerBegin = "<b>&raquo;";
static String activeMarkerEnd = "&laquo;</b>";
// ------- END DEFINITIONS ----------

// ------- PINS ----------
static int morsePin = 2;
static int i2cSdaPin = 21;
static int i2cSclPin = 22;

static int pinOutBlue = 32;
static int pinOutRed = 33;
static int pinOutGreen = 27;
static int pinOutAll = 18;

const int freq = 5000;

const int blueChannel = 2;
const int redChannel = 0;
const int greenChannel = 1;

const int resolution = 8; // 256
// ------- END PINS ----------

// --------- Variables ---------

int freeHeap = 0;

bool isOn = false;
int blueVal = 255;
int redVal = 255;
int greenVal = 255;

int hueVal = 0;
int saturationVal = 0;
int brightnessVal = 100;

// --------- END Variables ---------

void setup() 
{
  // put your setup code here, to run once:
  initSerial();
  initWifi();
  initSchedules();
  initPinModes();
  initRgbPwm();
  buildFromRgb();
  updateLed();
  initTimeClient();
  initServer();
  checkFreeRam();  
}

void loop() 
{
  server.handleClient();
  runner.execute();
}

void initSerial()
{
  Serial.begin(115200); 
  while(!Serial){} // Waiting for serial connection
  Serial.println();
}


void initWifi()
{
  Serial.println("WiFi init");
  Serial.print("Wifi: ");
  Serial.println(ssid);
  //Serial.print("WifiPW: ");
  //Serial.println(password);
  
  Serial.println("turn wifi off...");
  WiFi.mode(WIFI_OFF);
  delay(10);
  //WiFi.forceSleepBegin();
  delay(200);
  //WiFi.forceSleepWake();
  WiFi.mode(WIFI_STA);
  delay(250);
  
  WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);    
  delay(200); 
  //WiFi.mode(WIFI_STA);
  Serial.println("setting hostname");
  WiFi.setHostname(deviceName);
  delay(200); 
  Serial.println("Connecting to WiFi..");
  WiFi.begin(ssid, password);
  delay(200); 

  int iCounter = 0;
  int iMax = 30;
  while (WiFi.status() != WL_CONNECTED && iCounter < iMax)
  {
    digitalWrite(morsePin, LOW);
    delay(500);
    digitalWrite(morsePin, HIGH);
    delay(500);
    Serial.print(".");
    iCounter++;
  }
  
  Serial.println(WiFi.localIP()); 
}

void initPinModes()
{
  Serial.println("PIN MODES init");

  pinMode(morsePin, OUTPUT);
  digitalWrite(morsePin, LOW);

  pinMode(pinOutAll, OUTPUT);
  digitalWrite(pinOutAll, LOW);
}

void initSchedules()
{
  Serial.println("SCHEDULES init");
  
  runner.init();

  runner.addTask(scheduleCheckFreeRam);
  scheduleCheckFreeRam.enable();

  runner.addTask(scheduleWifiReconnectCheck);
  scheduleWifiReconnectCheck.enable();

  runner.addTask(scheduleRefreshTime);
  scheduleRefreshTime.enable();
}

void initTimeClient()
{
  timeClient.begin();
  delay(1000);
  timeClient.update();
}

void initServer()
{
  server.on("/", handleConnect);
  server.on("/jsondoaction", jsonDoAct);
  server.on("/jsondoaction", HTTP_POST, jsonDoAct);  
  server.onNotFound(handleConnect);
  server.begin();
  Serial.println("HTTP server started");
}

void wifiReconnectCheck()
{
  unsigned long currentMillis = millis();
  // if WiFi is down, try reconnecting every CHECK_WIFI_TIME seconds
  if ((WiFi.status() != WL_CONNECTED) && (currentMillis - previousMillis >=interval))
  {
    Serial.print(millis());
    Serial.println("Reconnecting to WiFi...");
    WiFi.disconnect();
    WiFi.reconnect();
    previousMillis = currentMillis;
  }
}

void refreshTime()
{
  timeClient.update();
}

void checkFreeRam()
{
  freeHeap = ESP.getFreeHeap();

  if (ESP.getFreeHeap() < 60000)
  {
    ESP.restart();
  }
}

// RGB functions

void initRgbPwm()
{
  ledcSetup(blueChannel, freq, resolution);
  ledcSetup(redChannel, freq, resolution);
  ledcSetup(greenChannel, freq, resolution);
  
  // attach the channel to the GPIO to be controlled
  ledcAttachPin(pinOutBlue, blueChannel);
  ledcAttachPin(pinOutRed, redChannel);
  ledcAttachPin(pinOutGreen, greenChannel);
}

void enableFetAll(bool bIn)
{
  if (bIn == true)
  {
    digitalWrite(pinOutAll, HIGH);
  }
  else
  {
    digitalWrite(pinOutAll, LOW);
  }

  isOn = bIn;
  sendStatusChangeSwitch(LedEnabledSwitchName, bIn);

}

void setLedValue(int iChannel, int iVal)
{

  int valToSet = (int) ( ( (float)brightnessVal / (float)255 ) * (float)iVal );

  if (valToSet >= 256)
  {
    valToSet = 255;
  }
  else if (valToSet < 0)
  {
    valToSet = 0;
  }

  ledcWrite(iChannel, 255-valToSet);
}

String convertToHexColor()
{
  String retStr = "";

  byte R = redVal;
  byte G = greenVal;
  byte B = blueVal;

  char hex[7] = {0};
  sprintf(hex,"%02X%02X%02X",R,G,B);

  retStr += String(hex);

  return retStr;
}

void buildFromRgb()
{
  float tmpRed = (float)redVal/(float)255;
  float tmpGreen = (float)greenVal/(float)255;
  float tmpBlue = (float)blueVal/(float)255;

  ESP_Color::Color colTmp = ESP_Color::Color(tmpRed, tmpGreen, tmpBlue);
  auto hsv = colTmp.ToHsv(); 

  float tmpHue = (float)360 * (float)hsv.H;
  float tmpSaturation = (float)100 * (float)hsv.S;

  hueVal = (int)tmpHue;
  saturationVal = (int)tmpSaturation;
}

void buildFromHsv()
{
  ESP_Color::Color colTmp = ESP_Color::Color::FromHsv((float)hueVal/(float)360, (float)saturationVal/(float)100, 1.00f);

  float tmpRed = ((float)255) * (float)colTmp.R;
  float tmpGreen = ((float)255) * (float)colTmp.G;
  float tmpBlue = ((float)255) * (float)colTmp.B;

  redVal = (int) tmpRed;
  greenVal = (int) tmpGreen;
  blueVal = (int) tmpBlue;
}

void updateLed()
{
  setLedValue(blueChannel, blueVal);
  setLedValue(greenChannel, greenVal);
  setLedValue(redChannel, redVal);
}

// HTTP Server

void handleConnect()
{
  Serial.println("Connect");
  server.send(200, "text/html", SendHTML("")); 
}

String SendHTML(String context)
{
  String ptr = "<!DOCTYPE html> <html>\n";
  ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
  ptr +="<title>ESP32 Gartenhaus LED</title>\n";
  ptr +="<style>html { font-family: Arial; display: inline-block; margin: 0px auto; text-align: center;}\n";
  ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
  ptr +=".button {display: block;width: 80px;background-color: #3498db;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";
  ptr +="a, a:active, { color: " + linkColorNormal + "; text-decoration: underline; }\n";
  ptr +="a:visited { color: " + linkColorVisited + "; text-decoration: underline; }\n";
  ptr +="p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";
  ptr +="</style>\n";
  ptr +="</head>\n";
  ptr +="<body>\n";
  ptr +="<h1>ESP32 Gartenhaus LED</h1>\n";

  // TODO Webinterface

  String lineBreak = "<br><br>";

  ptr += "";

  ptr +="</body>\n";
  ptr +="</html>\n";
  return ptr;
}

void jsonDoAct()
{
  Serial.println("JSON Act");

  String status = "OK";
  int tmpInput = 0;
  bool tmpOnOff = false;
  bool tmpBuildFromRgb = false;
  bool tmpBuildFromHsv = false;
  bool tmpRefreshBrightness = false;

  if (server.hasArg("plain") == false) 
  {
    //handle error here
  }
  
  String body = server.arg("plain");
  Serial.println(body);
  deserializeJson(jsonDocument, body);

  // continue
  status = "OK";

  // ON OFF
  if (jsonDocument.containsKey("ison") == true)
  {
    tmpInput = jsonDocument["ison"];
    if (tmpInput == 1)
    {
      brightnessVal = 255;
      enableFetAll(true);
    }
    else
    {
      brightnessVal = 0;
      enableFetAll(false);
    }

    tmpOnOff = true;
  }

  // RGB
  if (jsonDocument.containsKey("blue") == true)
  {
    tmpInput = jsonDocument["blue"];
    if (tmpInput >= 0 && tmpInput <= 255)
    {
      blueVal = tmpInput;
      tmpBuildFromRgb = true;
    }
  }
  if (jsonDocument.containsKey("green") == true)
  {
    tmpInput = jsonDocument["green"];
    if (tmpInput >= 0 && tmpInput <= 255)
    {
      greenVal = tmpInput;
      tmpBuildFromRgb = true;
    }
  }
  if (jsonDocument.containsKey("red") == true)
  {
    tmpInput = jsonDocument["red"];
    if (tmpInput >= 0 && tmpInput <= 255)
    {
      redVal = tmpInput;
      tmpBuildFromRgb = true;
    }
  }

  // HSV
  if (jsonDocument.containsKey("hue") == true)
  {
    tmpInput = jsonDocument["hue"];
    if (tmpInput >= 0 && tmpInput <= 360)
    {
      hueVal = tmpInput;
      tmpBuildFromHsv = true;
    }
  }
  if (jsonDocument.containsKey("saturation") == true)
  {
    tmpInput = jsonDocument["saturation"];
    if (tmpInput >= 0 && tmpInput <= 100)
    {
      saturationVal = tmpInput;
      tmpBuildFromHsv = true;
    }
  }

  if (jsonDocument.containsKey("brightness") == true)
  {
    tmpInput = jsonDocument["brightness"];
    if (tmpInput >= 0 && tmpInput <= 255)
    {
      brightnessVal = tmpInput;
      if (brightnessVal == 0)
      {
        enableFetAll(false);
      }
      else
      {
        enableFetAll(true);
      }
      
      tmpRefreshBrightness = true;
      tmpOnOff = true;
    }
  }

  // build
  if (tmpBuildFromRgb == true || tmpBuildFromHsv == true || tmpRefreshBrightness == true || tmpOnOff == true)
  {
    if (tmpBuildFromRgb == true)
    {
      buildFromRgb();
    }
    
    if (tmpBuildFromHsv == true)
    {
      buildFromHsv();
    }

    updateLed();

    updateColorData();
  }

  createStatusJson(status);

  Serial.println(status);

  server.send(200, "application/json", jsonBuffer);
}

// -------------------- JSON HELPER --------------------------

void createStatusJson(String statusIn) 
{
  if (statusIn == "")
  {
    statusIn = "OK";
  }
  
  jsonDocument.clear();  
  jsonDocument["state"] = statusIn;

  jsonDocument["ison"] = isOn;

  jsonDocument["blue"] = blueVal;
  jsonDocument["green"] = greenVal;
  jsonDocument["red"] = redVal;

  jsonDocument["hexcolor"] = convertToHexColor();

  jsonDocument["hue"] = hueVal;
  jsonDocument["saturation"] = saturationVal;
  jsonDocument["brightness"] = brightnessVal;

  jsonDocument["freeram"] = freeHeap;

  serializeJson(jsonDocument, jsonBuffer);
}

// -------------------- HTTP HELPER --------------------------

String boolToSwitch(bool inVal)
{
  if (inVal == true)
  {
    return "on";
  }
  return "off";
}

void sendStatusChangeInt(String attributeStringIn, int valueIn)
{
  Serial.println(sendInputStatusHttp(attributeStringIn, String(valueIn), false));
}

void sendStatusChangeSwitch(String attributeStringIn, bool valueIn)
{
  Serial.println(sendInputStatusHttp(attributeStringIn, boolToSwitch(valueIn), true));
}

void updateColorData()
{
  sendStatusChangeInt(LedHueIntName, hueVal);
  sendStatusChangeInt(LedSaturationIntName, saturationVal);
  sendStatusChangeInt(LedBrightnessIntName, brightnessVal);
}

String sendInputStatusHttp(String attributeStringIn, String valueIn, bool isString)
{
  String retStr;

  HTTPClient *http = new HTTPClient();
  http->setReuse(false);

  Serial.print("[HTTP] begin...\n");
  Serial.println("[HTTP] URL: " + SendApiIotUrl + attributeStringIn);
  
  if (http->begin(SendApiIotUrl + attributeStringIn))
  {
    Serial.print("[HTTP] POST...\n");
    http->setTimeout(5000);
    // start connection and send HTTP header
    http->addHeader("Content-Type", "application/json; charset=UTF-8");
    http->addHeader(HomeAssistantBearerName, HomeAssistantBearerContent); // auth

    // status Json
    String sendContent = "";
    if (isString == true)
    {
      sendContent = "{\"state\": \""+valueIn+"\" }";
    }
    else
    {
      sendContent = "{\"state\": "+valueIn+" }";
    }

    Serial.println("Content: " + sendContent);
    
    int httpCode = http->POST(sendContent);

    // httpCode will be negative on error
    if (httpCode > 0)
    {
      // HTTP header has been send and Server response header has been handled
      Serial.printf("[HTTP] POST... code: %d\n", httpCode);

      // file found at server
      if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY)
      {
        //String payload = http->getString();
        //Serial.println(payload);
        retStr = http->getString();
      }

      //String payload = http->getString();
      //Serial.println(payload);
    }
    else
    {
      Serial.printf("[HTTP] GET... failed, error: %s\n", http->errorToString(httpCode).c_str());
      retStr = "error: ";
      retStr += http->errorToString(httpCode).c_str();
    }

    http->end();
  }
  else
  {
    Serial.printf("[HTTP] Unable to connect\n");
    retStr = "ConErr";
  }

  delete http;
  http = NULL;

  //delay(200);

  return retStr;
}

Hinweis

Dieser Artikel dokumentiert lediglich meinen Aufbau. Für den Nachbau, die Nutzung einzelner Komponenten, die Platinen und den gesamten Inhalt wird die Haftung in jeglicher Form ausgeschlossen.

Leave a Reply

Your email address will not be published. Required fields are marked *




Enter Captcha Here :