Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
Beide Seiten der vorigen RevisionVorhergehende ÜberarbeitungNächste Überarbeitung | Vorhergehende ÜberarbeitungNächste ÜberarbeitungBeide Seiten der Revision | ||
de:tech:gardenled [2022/11/13 18:00] – [Programmierung] bullar | de:tech:gardenled [2022/11/14 13:23] – bullar | ||
---|---|---|---|
Zeile 3: | Zeile 3: | ||
===== Motivation ===== | ===== Motivation ===== | ||
- | Unser Grundstück ist - wie häufig hier in Thailand - von einer zwei Meter hohen Mauer eingerahmt. | + | Unser Grundstück ist - wie häufig hier in Thailand - von einer zwei Meter hohen Mauer eingerahmt. |
{{ : | {{ : | ||
Zeile 11: | Zeile 11: | ||
===== Lösungsansatz ===== | ===== Lösungsansatz ===== | ||
- | Im Garten liegt eine 230V Versorgung die aber nicht schaltbar oder dimmbar ist, da andere Verbraucher wie Aussensteckdosen und das Bodenlicht daran angeschlossen sind. | + | Im Garten liegt bei uns eine 230V Versorgung, die aber nicht schaltbar oder dimmbar ist, da andere Verbraucher wie Aussensteckdosen und das Bodenlicht daran angeschlossen sind. |
- | Eine Zusatzverdrahtung zum Haus habe ich ausgeschlossen nachdem ich versucht habe den Verlauf der unterirdischen Rohre zu verfolgen. Also muss etwas drahtloses her, das (wie immer) auch in [[https:// | + | Eine Zusatzverdrahtung zum Haus habe ich ausgeschlossen, nachdem ich erfolglos |
Vorab habe ich schon die LED Spots nach Aussehen, Leistung, Preis und Größe ausgewählt. Dabei fiel die Wahl auf einen 5W/600lm Strahler mit 12V AC/DC Versorgung im IP65 Gehäuse vom thailändischen Amazon: LAZADA. | Vorab habe ich schon die LED Spots nach Aussehen, Leistung, Preis und Größe ausgewählt. Dabei fiel die Wahl auf einen 5W/600lm Strahler mit 12V AC/DC Versorgung im IP65 Gehäuse vom thailändischen Amazon: LAZADA. | ||
Zeile 18: | Zeile 18: | ||
{{ : | {{ : | ||
- | Bei einem Preis von unter 4€ pro Stück kommt das ganze wirklich in einem wasserfesten Alugehäuse mit einer internen Steuerplatine und funktioniert! Lichtgeber ist eine 3mm LED die mit 21.5V bei einem Konstantstrom von 266mA betrieben wird. Die höhere Spannung wird durch einen Boost Regler (BP1808) erreicht, der auch einen ungenutzten DIM Eingang hat. Dieser kann analog oder über ein PWM Signal angesteuert werden. Ein erster Test mit einer 500Hz PWM zeigt das gewünschte Dimmverhalten. | + | Bei einem Preis von unter 4€ pro Stück kommt das ganze wirklich in einem wasserfesten Alugehäuse mit einer internen Steuerplatine und funktioniert! Lichtgeber ist eine 3mm LED die mit 21.5V bei einem Konstantstrom von 266mA betrieben wird. Die höhere Spannung wird durch einen Boost Regler |
{{: | {{: | ||
- | Jetzt brauchen wir nur ein Idee wie wir die im Home Assistant | + | Jetzt brauchen wir nur ein Idee wie wir die im Home Assistant |
- | Erste Versuche mit einer Kommunikation über Niedrigvoltleitungen schlugen aus Zuverlässigkeits- und Skalierungsgründen fehl. Dazu gibt es aber einen pfiffigen Ansatz: [[https:// | + | Erste Versuche mit einer Kommunikation über Niedrigvoltleitungen schlugen aus Zuverlässigkeits- und Skalierungsgründen fehl. Dazu gibt es durchaus |
- | Für Datenübertragungen über mehrere | + | Für Datenübertragungen über mehrere |
===== Hardware ===== | ===== Hardware ===== | ||
- | Der LED Boost Driver mit dem BP1808 muss um eine Schaltung für den. Empfang von seriellen Daten über RS485 erweitert werden und das ganze möglichst klein. Der Innenraum des Lampengehäuses fasst nur ca. 40mm x 20mm. Als Steuer-CPU fiel die Wahl auf ein [[https:// | + | Der LED Boost Driver mit dem BP1808 muss um eine Schaltung für den Empfang von seriellen Daten über RS485 erweitert werden und das ganze möglichst klein. Der Innenraum des Lampengehäuses fasst nur ca. 40mm x 20mm. Als Steuer-CPU fiel die Wahl auf einen [[https:// |
==== Schaltplan ==== | ==== Schaltplan ==== | ||
- | Alles zusammengefasst sieht der Schaltplan so aus: | + | Alles zusammengefasst sieht der Schaltplan |
{{ : | {{ : | ||
- | Der LED Boot Driver ist identisch zur Originalversion und nur durch die PWM-Ansteuerung ergänzt. Da ich bei den ersten Versuchen ein paar Hardwareausfälle (ATTINY85 + BP1808) wegen Überspannung hatte, | + | Der LED Boot Driver ist identisch zur Originalversion und nur durch die PWM-Ansteuerung ergänzt. Da ich bei den ersten Versuchen ein paar Hardwareausfälle (ATTINY85 + BP1808) wegen Überspannung hatte, |
==== PCB ==== | ==== PCB ==== | ||
- | Bei den geometrischen Vorgaben kommen wir um eine beidseitige Bestückung nicht herum. Aber auf der zweiten Seite befinden sich nur die vier SS14 Dioden (D3-D6) des Brückengleichrichters und der große Thru-hole Kondensator | + | Bei den geometrischen Vorgaben kommen wir um eine beidseitige Bestückung nicht herum. Aber auf der zweiten Seite befinden sich nur die vier SS14 Dioden (D3-D6) des Brückengleichrichters und der große Thru-hole Kondensator |
{{ : | {{ : | ||
Zeile 51: | Zeile 51: | ||
{{ : | {{ : | ||
+ | |||
+ | Um die Platine elektrisch isoliert in das Gehäuse einzubauen drucke ich aus flexiblem TPU ein zweiteiliges Cover. An beiden Enden über die Platine geschoben bleibt dann alles vor ungewollten Kurzschlüssen geschützt. | ||
+ | |||
+ | |||
+ | |||
Zeile 98: | Zeile 103: | ||
In den Spotlights lassen sich auch individuelle Helligkeits- und Fadewerte abspeichern, | In den Spotlights lassen sich auch individuelle Helligkeits- und Fadewerte abspeichern, | ||
+ | |||
+ | ==== Programmierung ==== | ||
+ | |||
+ | In der Werkseinstellung des ATTINY85 ist das // | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | Das Debug- und Programmingtool der ersten Wahl ist und bleibt //SNAP (PG164100)// | ||
+ | |||
+ | {{ : | ||
===== Integration ===== | ===== Integration ===== | ||
Zeile 105: | Zeile 120: | ||
Mein Wunsch es aber die Spotlights in Home Assistant (HA) zu integrieren. Dazu brauchen wir ein Gateway dass die Spotlights als //Lights// in HA abbildet und zusätzlich einen UART Server damit wir auch beliebige Befehle an die LEDs senden können. Klingt kompliziert ist es aber mit ESPHome nicht. | Mein Wunsch es aber die Spotlights in Home Assistant (HA) zu integrieren. Dazu brauchen wir ein Gateway dass die Spotlights als //Lights// in HA abbildet und zusätzlich einen UART Server damit wir auch beliebige Befehle an die LEDs senden können. Klingt kompliziert ist es aber mit ESPHome nicht. | ||
- | Für den UART server gibt es schon [[github:// | + | Für den UART server gibt es schon [[https://github.com/ |
<code C> | <code C> | ||
Zeile 123: | Zeile 138: | ||
digitalWrite(DIR_PORT, | digitalWrite(DIR_PORT, | ||
} | } | ||
- | |||
</ | </ | ||
+ | Für die Einbindung der LEDs als Lights schreiben wir wieder ein //custom component// | ||
+ | ++++ garden-leds.h | | ||
+ | <file C garden-leds.h> | ||
+ | #include " | ||
+ | using namespace esphome; | ||
+ | #define LED_GLOBAL | ||
+ | #define LED_ON | ||
+ | #define LED_FADE | ||
+ | #define LED_BRIGHTNESS | ||
+ | #define DIR_PORT | ||
+ | #define BAUDRATE | ||
+ | #define MAX_BYTES_PER_FRAME 16 | ||
+ | #define MAX_PAYLOAD 3 | ||
+ | uint8_t min_payload[3] = { LED_ON, LED_BRIGHTNESS, | ||
+ | // MIN Protocol v2.0 Copyright (c) 2014-2017 JK Energy Ltd. | ||
+ | // Use authorized under the MIT license. | ||
+ | // Special protocol bytes | ||
+ | enum { | ||
+ | HEADER_BYTE = 0xaaU, | ||
+ | STUFF_BYTE = 0x55U, | ||
+ | EOF_BYTE = 0x55U, | ||
+ | }; | ||
+ | // Number of bytes needed for a frame with a given payload length, excluding stuff bytes | ||
+ | // 3 header bytes, ID/control byte, length byte, seq byte, 4 byte CRC, EOF byte | ||
+ | #define ON_WIRE_SIZE(p) | ||
- | Jetzt legen wir ein | + | struct crc32_context { |
+ | uint32_t crc; | ||
+ | }; | ||
+ | struct min_context { | ||
+ | uint8_t rx_frame_payload_buf[MAX_PAYLOAD]; | ||
+ | uint32_t rx_frame_checksum; | ||
+ | struct crc32_context rx_checksum; | ||
+ | struct crc32_context tx_checksum; | ||
+ | uint8_t rx_header_bytes_seen; | ||
+ | uint8_t rx_frame_state; | ||
+ | uint8_t rx_frame_payload_bytes; | ||
+ | uint8_t rx_frame_id_control; | ||
+ | uint8_t rx_frame_seq; | ||
+ | uint8_t rx_frame_length; | ||
+ | uint8_t rx_control; | ||
+ | uint8_t tx_header_byte_countdown; | ||
+ | uint8_t port; // Number of the port associated with the context | ||
+ | }; | ||
- | < | + | // MIN calback functions |
+ | // --------------------------------------------------------------------------- | ||
+ | void min_tx_start(uint8_t port) { | ||
+ | digitalWrite(DIR_PORT, | ||
+ | } | ||
+ | |||
+ | // --------------------------------------------------------------------------- | ||
+ | void min_tx_finished(uint8_t port) { | ||
+ | Serial.flush(); | ||
+ | digitalWrite(DIR_PORT, | ||
+ | } | ||
+ | |||
+ | // --------------------------------------------------------------------------- | ||
+ | // Tell MIN how much space there is to write to the serial port. This is used | ||
+ | // inside MIN to decide whether to bother sending a frame or not. | ||
+ | uint16_t min_tx_space(uint8_t port) | ||
+ | { | ||
+ | return MAX_BYTES_PER_FRAME; | ||
+ | } | ||
+ | |||
+ | // --------------------------------------------------------------------------- | ||
+ | // Send a character on the designated port. | ||
+ | void min_tx_byte(uint8_t port, uint8_t byte) | ||
+ | { | ||
+ | // Ignore ' | ||
+ | Serial.write(byte); | ||
+ | } | ||
+ | |||
+ | // --------------------------------------------------------------------------- | ||
+ | static void crc32_init_context(struct crc32_context *context) | ||
+ | { | ||
+ | context-> | ||
+ | } | ||
+ | |||
+ | // --------------------------------------------------------------------------- | ||
+ | static void crc32_step(struct crc32_context *context, uint8_t byte) | ||
+ | { | ||
+ | uint32_t j; | ||
+ | context-> | ||
+ | for(j = 0; j < 8; j++) { | ||
+ | uint32_t mask = (uint32_t) -(context-> | ||
+ | context-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // --------------------------------------------------------------------------- | ||
+ | static uint32_t crc32_finalize(struct crc32_context *context) | ||
+ | { | ||
+ | return ~context-> | ||
+ | } | ||
+ | |||
+ | // --------------------------------------------------------------------------- | ||
+ | static void stuffed_tx_byte(struct min_context *self, uint8_t byte, bool crc) | ||
+ | { | ||
+ | // Transmit the byte | ||
+ | min_tx_byte(self-> | ||
+ | if(crc) { | ||
+ | crc32_step(& | ||
+ | } | ||
+ | |||
+ | // See if an additional stuff byte is needed | ||
+ | if(byte == HEADER_BYTE) { | ||
+ | if(--self-> | ||
+ | min_tx_byte(self-> | ||
+ | self-> | ||
+ | } | ||
+ | } | ||
+ | else { | ||
+ | self-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // --------------------------------------------------------------------------- | ||
+ | // Send frame on wire | ||
+ | static void on_wire_bytes(struct min_context *self, uint8_t id_control, uint8_t seq, uint8_t const *payload_base, | ||
+ | { | ||
+ | uint8_t n, i; | ||
+ | uint32_t checksum; | ||
+ | |||
+ | self-> | ||
+ | crc32_init_context(& | ||
+ | |||
+ | min_tx_start(self-> | ||
+ | |||
+ | // Header is 3 bytes; because unstuffed will reset receiver immediately | ||
+ | min_tx_byte(self-> | ||
+ | min_tx_byte(self-> | ||
+ | min_tx_byte(self-> | ||
+ | |||
+ | stuffed_tx_byte(self, | ||
+ | if(id_control & 0x80U) { | ||
+ | // Send the sequence number if it is a transport frame | ||
+ | stuffed_tx_byte(self, | ||
+ | } | ||
+ | |||
+ | stuffed_tx_byte(self, | ||
+ | |||
+ | for(i = 0, n = payload_len; | ||
+ | stuffed_tx_byte(self, | ||
+ | payload_offset++; | ||
+ | payload_offset &= payload_mask; | ||
+ | } | ||
+ | |||
+ | checksum = crc32_finalize(& | ||
+ | |||
+ | // Network order is big-endian. A decent C compiler will spot that this | ||
+ | // is extracting bytes and will use efficient instructions. | ||
+ | stuffed_tx_byte(self, | ||
+ | stuffed_tx_byte(self, | ||
+ | stuffed_tx_byte(self, | ||
+ | stuffed_tx_byte(self, | ||
+ | |||
+ | // Ensure end-of-frame doesn' | ||
+ | min_tx_byte(self-> | ||
+ | |||
+ | min_tx_finished(self-> | ||
+ | } | ||
+ | |||
+ | // --------------------------------------------------------------------------- | ||
+ | // Sends an application MIN frame on the wire (do not put into the transport queue) | ||
+ | void min_send_frame(struct min_context *self, uint8_t min_id, uint8_t const *payload, uint8_t payload_len) | ||
+ | { | ||
+ | if((ON_WIRE_SIZE(payload_len) <= min_tx_space(self-> | ||
+ | on_wire_bytes(self, | ||
+ | } | ||
+ | } | ||
+ | |||
+ | struct min_context min_ctx; | ||
+ | |||
+ | // --------------------------------------------------------------------------- | ||
+ | // float output class | ||
+ | class Min_Led : public Component, public FloatOutput { | ||
+ | | ||
+ | void setup() override { | ||
+ | // switch RS485 driver to receive mode | ||
+ | digitalWrite(DIR_PORT, | ||
+ | Serial.begin(BAUDRATE); | ||
+ | } | ||
+ | |||
+ | void write_state(float state) override { | ||
+ | // state is the amount this output should be on, from 0.0 to 1.0 | ||
+ | // we need to convert it to an integer first | ||
+ | int value = state * 255; | ||
+ | min_payload[1] = value; | ||
+ | min_send_frame(& | ||
+ | } | ||
+ | }; | ||
+ | </ | ||
+ | ++++ | ||
+ | |||
+ | ---- | ||
+ | |||
+ | |||
+ | Nun legen wir wieder ein neues Device in ESPHome an und verweisen auf die beiden //Custom Codes// '' | ||
+ | Den ersten Part haben wir vorher in '' | ||
+ | |||
+ | |||
+ | < | ||
esphome: | esphome: | ||
name: garden-leds | name: garden-leds | ||
Zeile 153: | Zeile 366: | ||
+ | |||
+ | Zu guter Letzt das Setup des //Light// und des // | ||
+ | |||
+ | <code yaml> | ||
+ | uart: | ||
+ | id: uart_min | ||
+ | tx_pin: GPIO1 | ||
+ | rx_pin: GPIO3 | ||
+ | baud_rate: 9600 | ||
+ | |||
+ | stream_server: | ||
+ | uart_id: uart_min | ||
+ | port: 6638 | ||
+ | |||
+ | output: | ||
+ | - platform: custom | ||
+ | type: float | ||
+ | lambda: |- | ||
+ | auto min_led_pwm = new Min_Led(); | ||
+ | App.register_component(min_led_pwm); | ||
+ | return{min_led_pwm}; | ||
+ | outputs: | ||
+ | id: led_pwm | ||
+ | |||
+ | light: | ||
+ | - platform: monochromatic | ||
+ | name: " | ||
+ | output: led_pwm | ||
+ | </ | ||
+ | |||
+ | Gegebenenfalls müssen die Pins '' | ||
+ | ==== Hardware Gateway ==== | ||
+ | |||
+ | Für die H/W brauchen wir neben dem ESP Modul der Wahl, einen AC/DC Step-Down Regler von ~12VAC auf +5VDC, level shifter | ||
+ | |||
+ | ^ ESP8266 ^ RS485 Driver ^ | ||
+ | | GPIO1 | DI | | ||
+ | | GPIO3 | RO | | ||
+ | | GPIO13 | RE+DE | | ||
+ | | +5V | VCC | | ||
+ | | GND | GND | | ||
+ | |||
+ | Das Ganze dann auf einer Lochrasterplatine aufgelötet und verpackt in einem wasserfesten Gehäuse sieht dann so aus: | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | ==== Gesamtverdrahtung ==== | ||
+ | |||
+ | Zwischen den Spotlights und dem Gateway wird ein 4-adriges Kabel benötigt. Da ich Aussen in einem wasserdichten Rohr verlege, reicht mir eine PVC-Schlauchleitung H03VV-F4G0, | ||
+ | |||
+ | ^ Gateway | ||
+ | | ~12V | ~12V | ~12V | ... | ~12V | | ||
+ | | ~12V | ~12V | ~12V | ... | ~12V | | ||
+ | | A | A | A | ... | A | | ||
+ | | B | B | B | ... | B | | ||
+ | |||
+ | Je nach Anzahl der Spots brauchen wir noch ein leistungsfähigen Trafo für ~230V auf ~12V. Hier kann man z.B. einen gebrauchten Halogentrafo wiederverwenden. Die finden sich zuhauf in Gebrauchtwarenmärkten. Meinen 200W Trafo inkl. Sicherungen gab es dort für 8, | ||
+ | |||
+ | |||
+ | ===== Inbetriebnahme ===== | ||
+ | |||
+ | Schliessen wir nun das fertig verdrahtete Konqlumerat an, steht uns das Licht mit DImmfunktion in Home Assistant zur Verfügung. Die Fadezeit kann dort festgelegt werden und steht standardmässig auf 1s. Durch den Stream-Server können wir aber auch mit z.B. einem PC auf die LEDs zugreifen. Dazu habe ich ein kleines Pythonprogramm geschrieben (Source Code im Downloadbereich). | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | Damit lassen sich alle Funktionen auf alle oder einzelne LEDs ausführen. Mit **CHK** wird nach angeschlossen LEDs gesucht und im unteren Bereich steht ein einfacher Skript Editor zur Verfügung der die Programmierung von Abläufen erlaubt, bei einer Stepzeit von 100ms also 10Hz. Ein Gimmick für Silvester oder andere Parties. | ||
+ | ===== Erfahrungen ===== | ||
+ | |||
+ | Im Moment gibt es nur einen Testaufbau mit zwei Spotlights der sehr gut funktioniert. Die vollständige Installation im Aussenbereich erfolgt erst 2023. Ich werde berichten... | ||
===== Downloads ===== | ===== Downloads ===== | ||
- | * {{: | + | * {{: |
- | * {{: | + | * {{: |
* {{: | * {{: | ||
* {{: | * {{: | ||
+ | * {{: | ||
+ | * {{| LED Test Tool Wifi (.py)}} | ||