16 578
правок
(не показано 27 промежуточных версий 2 участников) | |||
Строка 1: | Строка 1: | ||
{{DISPLAYTITLE:Подключение адресной ленты WS2812B к контроллеру Wiren Board}} | |||
==Описание== | |||
{{Wbincludes:Community}} | |||
Автор статьи: [https://t.me/Paulstrong Paul Strong] | |||
В этой статье рассмотрим один из способов управления адресной лентой WS2812B из Wiren Board с использованием ESP32. | |||
Здесь не будут рассмотрены характеристики железной части, этому уделено очень много внимания на просторах Интернет, тут будет сугубо техническая информация по процессу достижения цели, с небольшими лирическими отступлениями. | |||
* любая отладочная плата, основанная на esp32 | |||
* две bread board платы | Что нам понадобится: | ||
* соединительные проводки | * любая отладочная плата, основанная на esp32; | ||
* лента ws2812b нужной длины (в моём случае бухта 5м с мощностью 14.4Вт/м) | * две bread board платы; | ||
* блок питания 5V соответствующей мощности (в моём случае 100W) | * соединительные проводки; | ||
* резистор 220 ом | * лента ws2812b нужной длины (в моём случае бухта 5м с мощностью 14.4Вт/м); | ||
* microUSB дата-кабель (обычный, от зарядки, может не подойти!) | * блок питания 5V соответствующей мощности (в моём случае 100W); | ||
* комп/ноут для сборки и заливки прошивки | * резистор 220 ом; | ||
* VSCode + PlatformIO | * microUSB дата-кабель (обычный, от зарядки, может не подойти!); | ||
* контроллер Wirenboard с wb-rules v2.x | * комп/ноут для сборки и заливки прошивки; | ||
* wi-fi сеть в диапазоне 2.4G | * VSCode + PlatformIO; | ||
* контроллер Wirenboard с wb-rules v2.x; | |||
* wi-fi сеть в диапазоне 2.4G. | |||
==Сборка физической схемы== | ==Сборка физической схемы== | ||
[[Файл:Ws2812b_BB_Schema.png|thumb|right|600px| Схема сборки]] | |||
Для сборки нам нужно две bread board платы, собираем по схеме: | Для сборки нам нужно две bread board платы, собираем по схеме: | ||
* пин 5V подключаем к +5V ленты | * пин 5V подключаем к +5V ленты | ||
* пин GND | * пин GND | ||
* пинг GPIO27 подключаем через резистор 220ом к DIN ленты | * пинг GPIO27 подключаем через резистор 220ом к DIN ленты | ||
Обращаю внимание на то, что не рекомендуется тестировать конфигурацию более чем на 5 светодиодах, такого количества будет достаточно, чтобы понять как выглядит световая картинка в том или ином алгоритме. Подключение большего количества светодиодов может привести к выгоранию USB-порта на | Обращаю внимание на то, что не рекомендуется тестировать конфигурацию более чем на 5 светодиодах, такого количества будет достаточно, чтобы понять как выглядит световая картинка в том или ином алгоритме. | ||
Подключение большего количества светодиодов может привести к выгоранию USB-порта на компьютере, т.к. там обычно предельно допустимый ток 350мА, а один светодиод на такой ленте потребляет около 50мА. | |||
==Заливаем первую прошивку | ==Первый скетч== | ||
Подразумевается что на | Заливаем первую прошивку. | ||
Подразумевается что на компьютере уже установлена среда разработки VSCode и расширение PlatformIO. Создадим новый проект. | |||
[[Файл:Pio-create-prj.jpg]] | [[Файл:Pio-create-prj.jpg]] | ||
Далее нам нужно подключить к проекту библиотеку для работы с лентой, они есть разные, я использовал FastLED | Чтобы понимать, что esp32 работает, сделаем код, который постоянно зажигает и тушит встроенный светодиод синего цвета. Запускаем это дело в виде асинхронной функции. | ||
Файл <code>src/main.cpp</code>: | |||
<syntaxhighlight lang="c++"> | |||
#include <Arduino.h> | |||
#define LED_PIN 2 | |||
int led_status = HIGH; | |||
void ledUpdate(int v) { | |||
digitalWrite(LED_PIN, v); | |||
} | |||
void ledBlink( void * parameter ) { | |||
const TickType_t xDelay = 1000 / portTICK_PERIOD_MS; | |||
for(;;) { | |||
switch (led_status) | |||
{ | |||
case HIGH: | |||
led_status = LOW; | |||
break; | |||
default: | |||
led_status = HIGH; | |||
break; | |||
} | |||
ledUpdate(led_status); | |||
vTaskDelay(xDelay); | |||
Serial.printf("blink %d\n", led_status); | |||
} | |||
} | |||
void setupLedBlink() { | |||
pinMode(LED_PIN, OUTPUT); | |||
xTaskCreate( | |||
ledBlink, /* Task function. */ | |||
"ledBlink", /* String with name of task. */ | |||
10000, /* Stack size in bytes. */ | |||
NULL, /* Parameter passed as input of the task */ | |||
1, /* Priority of the task. */ | |||
NULL); /* Task handle. */ | |||
} | |||
void setup() { | |||
setupLedBlink(); | |||
} | |||
void loop(){} | |||
</syntaxhighlight> | |||
Сейчас можно залить попробовать скомпилировать прошивку, для этого в нижней панели жмём кнопку [[Файл:Снимок экрана 2023-12-06 205145.jpg]] | |||
Если всё собралось, только можно шить это дело в esp32: | |||
* подключаем esp32 к компу при помощи microUSB data кабеля; | |||
* инициируем заливку прошивки при помощи кнопки [[Файл:Снимок экрана 2023-12-06 205336.jpg]], когда система напишет «Connecting...» — зажимаем кнопку Boot на dev-плате esp32; | |||
* если прошивка залилась успешно, перезагружаем esp32 соответствующей кнопкой, после этого у вас должен замигать синий светодиод на плате. | |||
==Подключаемся к Wi-Fi== | |||
Приступаем к следующему этапу — нам нужно подключить нашу esp32 к wifi (чтобы далее подключиться к modbus tcp от wirenboard) | |||
Добавим к нашему файлу main.cpp следующий код: | |||
<syntaxhighlight lang="c++"> | |||
#include <Arduino.h> | |||
#include <WiFi.h> | |||
#define WIFI_SSID "ssid" | |||
#define WIFI_PASSWORD "password" | |||
void setupWifi() { | |||
WiFi.mode(WIFI_STA); | |||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD); | |||
char emptyMsg[1] = ""; | |||
char pointMsg[2] = "."; | |||
Serial.println(emptyMsg); | |||
// Wait for connection | |||
while (WiFi.status() != WL_CONNECTED) { | |||
delay(500); | |||
Serial.println(pointMsg); | |||
} | |||
Serial.println(emptyMsg); | |||
char connectedToMsg[14] = "Connected to "; | |||
Serial.println(connectedToMsg); | |||
Serial.println(WIFI_SSID); | |||
char ipAddrMsg[30]; | |||
sprintf(ipAddrMsg, "IP address: %s", WiFi.localIP().toString().c_str()); | |||
Serial.println(ipAddrMsg); | |||
} | |||
void setup() { | |||
setupWiFi(); | |||
} | |||
</syntaxhighlight> | |||
а также модифицируем функцию setup: | |||
<syntaxhighlight lang="c++"> | |||
void setup() { | |||
setupWifi(); | |||
} | |||
</syntaxhighlight> | |||
Теперь после прошивки esp32 подключится к вашей wifi сети, IP-адрес можно будет посмотреть на роутере, либо в отладочных сообщения на Serial-порте, который связывает ваш комп с esp32, для этого нажмите кнопку [[Файл:Снимок экрана 2023-12-06 210510.jpg]] на нижней панели. | |||
==Библиотека для работы с адресной лентой== | |||
Далее нам нужно подключить к проекту библиотеку для работы с лентой, они есть разные, я использовал FastLED. | |||
[[Файл:Снимок экрана 2023-12-06 201634.jpg]] | [[Файл:Снимок экрана 2023-12-06 201634.jpg]] | ||
Добавляем в проект | |||
[[Файл:Снимок экрана 2023-12-06 203931.jpg]] | [[Файл:Снимок экрана 2023-12-06 203931.jpg]] | ||
Строка 47: | Строка 158: | ||
#include <FastLED.h> | #include <FastLED.h> | ||
#define | #define DIN_PIN 27 | ||
#define NUM_LEDS 5 | #define NUM_LEDS 5 | ||
#define BRIGHTNESS 50 | #define BRIGHTNESS 50 | ||
#define LED_TYPE | #define LED_TYPE WS2812B | ||
#define COLOR_ORDER GRB | #define COLOR_ORDER GRB | ||
CRGB leds[NUM_LEDS]; | CRGB leds[NUM_LEDS]; | ||
Строка 84: | Строка 195: | ||
void setup() { | void setup() { | ||
delay( 3000 ); // power-up safety delay | delay( 3000 ); // power-up safety delay | ||
FastLED.addLeds<LED_TYPE, | FastLED.addLeds<LED_TYPE, DIN_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip ); | ||
FastLED.setBrightness( BRIGHTNESS ); | FastLED.setBrightness( BRIGHTNESS ); | ||
Строка 241: | Строка 352: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Прошиваемся, в случае успеха у вас должна заработать лента, алгоритм будет менять каждые 5 секунд, всего 12 алгоритмов. | |||
Подробно останавливаться на коде прошивки мы не будем, в Интернете полно разных алгоритмов, их можно добавлять к нашей оснастке, переключать их реже или чаще, можно даже кнопку прикрутить под это дело на один из пинов, тут кому насколько фантазии хватит. | Подробно останавливаться на коде прошивки мы не будем, в Интернете полно разных алгоритмов, их можно добавлять к нашей оснастке, переключать их реже или чаще, можно даже кнопку прикрутить под это дело на один из пинов, тут кому насколько фантазии хватит. | ||
==Подключение к Modbus TCP== | ==Подключение к Modbus TCP== | ||
После настройки | После настройки Wi-Fi мы можем подключаться к mqtt-брокеру контроллера Wiren Board, для этого нужно установить библиотеку, как мы это делали ранее, называется она PubSubClient. | ||
Подключимся к брокеру, для этого добавим немного кода: | |||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
#include <Arduino.h> | #include <Arduino.h> | ||
Строка 332: | Строка 395: | ||
pubSubClient.setServer(MODBUS_SERVER, MODBUS_PORT); | pubSubClient.setServer(MODBUS_SERVER, MODBUS_PORT); | ||
pubSubClient.setCallback(modbusCallback); | pubSubClient.setCallback(modbusCallback); | ||
} | } | ||
Строка 346: | Строка 407: | ||
if (pubSubClient.connect(clientId.c_str())) { | if (pubSubClient.connect(clientId.c_str())) { | ||
Serial.println("connected"); | Serial.println("connected"); | ||
pubSubClient.publish("outTopic", "hello world"); | |||
pubSubClient.subscribe("inTopic"); | |||
} else { | } else { | ||
Serial.print("failed, rc="); | Serial.print("failed, rc="); | ||
Строка 370: | Строка 433: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Что мы здесь видим: мы можем создавать подписку на нужные топики либо сами публиковать какие то значения в нужные нам топики. | |||
В нашем случае понадобится управлять настройками ленты, поэтому нам надо слушать некоторые топики, изменения которых будут влиять на поведение ленты, допустим, мы хотим управлять: | |||
* яркостью | |||
* выключать (яркость 0) | |||
* включать (яркость 50%) | |||
* останавливать и запускать автоматическую смену режима | |||
==Виртуальное устройство в | ==Виртуальное устройство в Wiren Board== | ||
Создадим виртуальное устройство нашей ленты в | Создадим на [[wb-rules]] виртуальное устройство нашей ленты в контроллере Wiren Board: | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
defineVirtualDevice("ws2812b", { | defineVirtualDevice("ws2812b", { | ||
Строка 417: | Строка 480: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Теперь надо константу BRIGHTNESS переделать в переменную, ввести переменную hue_loop, а также подписаться на топики | |||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
#define BRIGHTNESS 50 | #define BRIGHTNESS 50 | ||
Строка 435: | Строка 488: | ||
void modbusCallback(char* topic, byte* payload, unsigned int length) { | void modbusCallback(char* topic, byte* payload, unsigned int length) { | ||
char payloadChar[sizeof(payload)]; | char payloadChar[sizeof(payload)]; | ||
Serial.print("Message arrived ["); | Serial.print("Message arrived ["); | ||
Serial.print(topic); | Serial.print(topic); | ||
Строка 446: | Строка 498: | ||
String payloadStr = String(payloadChar); | String payloadStr = String(payloadChar); | ||
payloadStr.trim(); | |||
if (topic | if (strcmp(topic, "/devices/ws2812b/controls/brightness") == 0) { | ||
BRIGHTNESS = payloadStr.toInt(); | BRIGHTNESS = payloadStr.toInt(); | ||
Serial.printf("brithness set to %d\n", BRIGHTNESS); | |||
} | } | ||
if (topic | if (strcmp(topic, "/devices/ws2812b/controls/hueLoop") == 0) { | ||
if( | if(payloadStr.toInt() == 1) { | ||
hueLoop = true; | hueLoop = true; | ||
} else { | } else { | ||
Строка 462: | Строка 516: | ||
if(!hueLoop) { | if(!hueLoop) { | ||
return; | return; | ||
} | } | ||
... | ... | ||
} | |||
void modbusReconnect() { | |||
// Loop until we're reconnected | |||
while (!pubSubClient.connected()) { | |||
Serial.print("Attempting MQTT connection..."); | |||
// Create a random client ID | |||
String clientId = "ESP32Client-"; | |||
clientId += String(random(0xffff), HEX); | |||
// Attempt to connect | |||
if (pubSubClient.connect(clientId.c_str())) { | |||
Serial.println("connected"); | |||
pubSubClient.subscribe("/devices/ws2812b/controls/brightness"); | |||
pubSubClient.subscribe("/devices/ws2812b/controls/hueLoop"); | |||
} else { | |||
Serial.print("failed, rc="); | |||
Serial.print(pubSubClient.state()); | |||
Serial.println(" try again in 5 seconds"); | |||
// Wait 5 seconds before retrying | |||
delay(5000); | |||
} | |||
} | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Теперь можно управлять runtime-переменными esp32 через | Теперь можно управлять runtime-переменными esp32 через Wiren Board. | ||
[[Файл:Снимок экрана 2023-12-07 095833.jpg]] | [[Файл:Снимок экрана 2023-12-07 095833.jpg]] | ||
Строка 505: | Строка 581: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Теперь после запуска железки и получения ip-адреса будет доступен web-интерфейс (http://192.168.0.10/update), в котором можно выбрать прошивку и залить её по wi-fi, бонусом будет автоматический перезапуск железки после прошивки, то есть она сразу активируется. | |||
[[Файл:Снимок экрана 2023-12-07 102038.jpg]] | |||
Есть и минусы: если вы совершите ошибку в коде и будет возникал kernel panic / fatal, тогда вам уже точно придется тащить железку к компу и прошивать фикс через кабель | |||
==Добавление настроек== | |||
Допустим, мы захотели управлять какой-то настройкой еще, пускай это будет скорость обновления светодиодов. Ранее у нас была определена константа | |||
<syntaxhighlight lang="c++"> | |||
#define UPDATES_PER_SECOND 500 | |||
</syntaxhighlight> | |||
Теперь нам её придётся сделать обычной переменной: | |||
<syntaxhighlight lang="c++"> | |||
int UPDATES_PER_SECOND = 500; | |||
</syntaxhighlight> | |||
Теперь нужно подписать на новый топик: | |||
<syntaxhighlight lang="c++"> | |||
void modbusCallback(char* topic, byte* payload, unsigned int length) { | void modbusCallback(char* topic, byte* payload, unsigned int length) { | ||
char payloadChar[sizeof(payload)]; | char payloadChar[sizeof(payload)]; | ||
Serial.print("Message arrived ["); | Serial.print("Message arrived ["); | ||
Serial.print(topic); | Serial.print(topic); | ||
Строка 619: | Строка 613: | ||
String payloadStr = String(payloadChar); | String payloadStr = String(payloadChar); | ||
payloadStr.trim(); | |||
if (topic | if (strcmp(topic, "/devices/ws2812b/controls/brightness") == 0) { | ||
BRIGHTNESS = payloadStr.toInt(); | BRIGHTNESS = payloadStr.toInt(); | ||
Serial.printf("brithness set to %d\n", BRIGHTNESS); | |||
} | } | ||
if (topic | if (strcmp(topic, "/devices/ws2812b/controls/hueLoop") == 0) { | ||
if( | if(payloadStr.toInt() == 1) { | ||
hueLoop = true; | hueLoop = true; | ||
} else { | } else { | ||
Строка 630: | Строка 626: | ||
} | } | ||
} | } | ||
if (strcmp(topic, "/devices/ws2812b/controls/updatesPerSecond") == 0) { | |||
UPDATES_PER_SECOND = payloadStr.toInt(); | |||
} | |||
} | } | ||
Строка 649: | Строка 641: | ||
if (pubSubClient.connect(clientId.c_str())) { | if (pubSubClient.connect(clientId.c_str())) { | ||
Serial.println("connected"); | Serial.println("connected"); | ||
pubSubClient.subscribe("/devices/ws2812b/controls/brightness"); | |||
pubSubClient.subscribe("/devices/ws2812b/controls/hueLoop"); | |||
pubSubClient.subscribe("/devices/ws2812b/controls/updatesPerSecond"); | |||
} else { | } else { | ||
Serial.print("failed, rc="); | Serial.print("failed, rc="); | ||
Строка 658: | Строка 653: | ||
} | } | ||
} | } | ||
</syntaxhighlight> | |||
Заливаем прошивку в esp32. | |||
Осталось обновить виртуальное устройство в контроллере Wiren Board: | |||
<syntaxhighlight lang="javascript"> | |||
defineVirtualDevice("ws2812b", { | |||
title: "ws2812b", | |||
cells: { | |||
enabled: { | |||
title: "состояние", | |||
type: "switch", | |||
value: false, | |||
}, | |||
brightness: { | |||
title: "яркость", | |||
type: "range", | |||
min: 0, | |||
max: 255, | |||
value: 50, | |||
}, | |||
hueLoop: { | |||
title: "смена оттенков", | |||
type: "switch", | |||
value: true, | |||
}, | |||
updatesPerSecond: { | |||
title: "скорость обновления светодиодов", | |||
type: "range", | |||
min: 10, | |||
max: 1000, | |||
value: 100, | |||
}, | |||
} | |||
}) | |||
</syntaxhighlight> | |||
После применения виртуального устройства можно управлять скорость обновления светодиодов из веб-интерфейса контроллера Wiren Board. | |||
==Паяем на плату== | |||
Понадобится: | |||
* макетная плата размером 40х60; | |||
* две винтовые клеммы на 2 и 3 контакта; | |||
* паяльник 40Вт с тонким жалом; | |||
* припой с флюсом. | |||
[[Image: Photo 2023-12-22 17-04-06.jpg |300px ]] | |||
Девборд в длину занимает почти всю плату - остаётся один ряд, поэтому решил клеммы разместить сбоку | |||
[[Image: Photo 2023-12-22 17-04-07.jpg |300px ]] | |||
Клеммы соединяем через пазы | |||
[[Image: Photo 2023-12-22 17-04-06 2.jpg |300px ]] | |||
Припаиваем к шахтам макетной платы все пять пинов от клемм, а также прихватываем крайние пины девборда. Есть важный нюанс - клеммы нужно расположить таким образом, чтобы было минимальное расстояние до нужных пинов девборда | |||
[[Image: Photo 2023-12-22 17-04-07 (2).jpg |300px ]] | |||
Клеммы расположены прямо напротив нужных пинов, поэтому нет необходимости соединять проводами, напаял шлейфы из олова, от IO27 до клеммы идут два последовательных резистора по 100ом (какие были), скрутку резисторов приаял к шахте, чтобы не болталось ничего. | |||
[[Image: Photo 2023-12-22 17-04-08.jpg |300px ]] | |||
Чтобы всё это упаковать, взял распаечную коробку небольшую, но в итоге совсем чуток не вместился туда, пришлось пока что на стяжках к корпусу блока питания закрепить, потом намучу какой-то подходящий корпус и переселю. | |||
[[Image: Photo 2023-12-22 17-04-34.jpg |300px ]] | |||
==Какие могут быть проблемы== | |||
# изначально лента тестировалась на arduino nano, и на LED_TYPE==WS2812B оно работало странно, но с учетом того что какие-то отдельные светодиоды загорались, я пришёл к выводу что цифра работает, но есть какие-то проблемы с частотой, в итоге заработало на WS2811_400, то есть на частоте 400кгц, однако, в случае с esp32 всё заработало со штатным типом WS2812B; | |||
# важно учитывать тот факт, что нельзя допускать подключения ленты без стабильного плюса, то есть в случае подключения ленты к GND и DIN-пину, есть риск, что пин спалится, чтобы подстраховаться - ставится резистор на 220ом, но всё равно лучше избегать таких вот нештатных ситуаций; | |||
# на самом деле проблемы могут быть самые разнообразные, потому что это DIY, если что пишите в чатике - попробуем разобраться. | |||
==Финальный код== | |||
Исходный код скетча и виртуального устройства на wb-rules смотрите в репозитори на ГитХабе [https://github.com/wirenboard/wb-community/tree/main/firmwares/ws2812b-to-wb ws2812b-to-wb] |