Прогноз в телефоне каждый раз заставляет разблокировать экран, найти виджет и прочитать цифру — три шага в ботинках у двери. Лента в прихожей отвечает на тот же вопрос одним взглядом: тёплый жёлтый — ясно, серо-голубая — куртка, синяя — зонт в руку. Никакого экрана: индикатор висит на виду и красится сам, ещё до того, как вы дотянулись до телефона.
В этом уроке лента учится краситься по коду из Serial: 1 — солнце, 2 — облака с просветами, 3 — дождь. Числа читает Serial.parseInt, сценарии рисуют fill_solid и покраской секциями. Слова-команды вроде raduga и kometa, которые набирает человек, мы уже делали в уроке про выбор эффекта по Serial — здесь другой случай: коды шлёт машина, и протокол ради этого сжимается до одного байта смысла.
И честная карта проекта: в симуляторе вы печатаете коды руками и смотрите, как красится лента; в настоящем приборе эти коды будет отдавать вторая плата — ESP32, вытащившая прогноз из API погоды. Приёмник на Arduino при этом не меняется ни на байт: в этом и смысл разделять «добычу данных» и «рисовку».
Протокол: три числа вместо JSON
Почему числа, а не слова: когда по линии общается машина, каждая буква — расход. Строка solnce — шесть символов, код 1 — один; разбор у чисел проще, опечататься в цифре сложнее, а главное — parseInt не требует буферизации и терминатора: механику parseInt с таймаутом мы разбирали в отдельном уроке. Три кода покрывают бытовой прогноз: 1 — ясно, 2 — облака, 3 — дождь. Протокол растёт без перестройки: 4 — гроза, 5 — снег, и старый приёмник их просто отклонит с подсказкой, пока вы не научите его новым веткам.
Головное правило протокола: на каждое валидное число — подтверждение в порт, на невалидное — подсказка со списком. Молчащий приёмник превращает настройку в угадайку: отправитель не понимает, дошло ли вообще что-то. Это тот же принцип, что в уроке про командный парсер: отвечай всегда, даже когда отвечать нечем.
#include <FastLED.h> #define LED_PIN 6#define NUM_LEDS 16 CRGB leds[NUM_LEDS]; void paintWeather(int code) { if (code == 1) { // ясно: тёплый жёлтый на всю ленту fill_solid(leds, NUM_LEDS, CRGB(255, 200, 40)); } if (code == 2) { // облака: серо-голубое, вторая половина светлее fill_solid(leds, NUM_LEDS, CRGB(110, 125, 150)); for (int i = NUM_LEDS / 2; i < NUM_LEDS; i++) { leds[i] = CRGB(185, 195, 215); // просветы } } if (code == 3) { // дождь: синий и две светлые «капли» fill_solid(leds, NUM_LEDS, CRGB(30, 60, 200)); leds[3] = CRGB(170, 190, 255); leds[11] = CRGB(170, 190, 255); } FastLED.show();} void setup() { Serial.begin(9600); FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS); FastLED.setBrightness(200); Serial.println("Kody: 1 sun, 2 clouds, 3 rain");} void loop() { if (Serial.available() > 0) { int code = Serial.parseInt(); if (code >= 1 && code <= 3) { paintWeather(code); Serial.print("code="); Serial.println(code); // подтверждение отправителю } else { Serial.println("Net takogo koda: 1, 2 ili 3"); } }}Три детали, на которых держится приёмник. Первая: читаем только после Serial.available() > 0 — иначе parseInt будет по тысяче миллисекунд ждать в каждом проходе loop(), и лента отзовётся с секундным лагом. Вторая: parseInt по таймауту возвращает 0, поэтому ноль — не код погоды, а знак «цифр не нашёл»; проверка диапазона 1-3 отрезает его вместе со всем мусором. Третья: подтверждение code= в порт стоит две строки и экономит вечера — глядя на монитор, вы всегда знаете, что приёмник услышал именно 2, а не 22 от дрогнувшей руки.
Цвета сценариев я держу как CRGB-тройки, а не оттенки HSV: погодная палитра постоянна, её не нужно крутить и анимировать, а тройка (255, 200, 40) выглядит одинаково и в коде, и на ленте. Просветы в облаках — покраска секцией: после fill_solid базовым цветом цикл от середины ленты перекрашивает пиксели светлым; так собираются любые «зоны» — закат у горизонта, полоса грозы, территория под окном. Оба инструмента разобраны в уроке про CRGB и fill_solid и уроке про HSV-колесо.
| Код | Сценарий | Что на ленте | Ответ в Serial |
|---|---|---|---|
| 1 | Ясно | тёплый жёлтый CRGB(255, 200, 40) на всю ленту | code=1 |
| 2 | Облака | серо-голубая заливка, вторая половина светлее | code=2 |
| 3 | Дождь | синий с двумя светлыми каплями | code=3 |
| 0 или мусор | смена не подтверждена | лента не тронута | подсказка со списком кодов |
Откуда числа придут на живой плате
В настоящем приборе цепочка выглядит так: ESP32 раз в десять-пятнадцать минут стучится HTTP-запросом в API погоды, разбирает ответ библиотекой ArduinoJson и печатает в Serial одно число — код сценария. Обе половины уже расписаны в курсе: урок про OpenWeatherMap и LCD собирает запрос и разбор JSON, а разбор GET-ответа по частям показывает, что реально лежит в строке ответа сервера. У OpenWeatherMap коды сгруппированы: 800 — ясно, 801-804 — облака, 500-599 — дождь (нумерацию проверяйте по актуальной документации API — она нумерует погодные условия группами). Ваша задача — ужать группу до одного байта сценария: любая 5xx превращается в тройку.
Между платами — три провода UART на 9600 бод, как в уроке про связь двух Arduino: TX одной в RX другой, земля общая. Одна грабля на стыке: ESP32 — плата на 3,3 В, а ленте WS2812 для уверенной единицы подавай пять; без согласования уровней картинка начинается с рваных цветов на первых пикселях. Подробно — в уроке про согласование 3,3 В и 5 В.
И почему не наоборот — почему не Uno тянет API. На Uno нет Wi-Fi из коробки, а с Ethernet-шилдом HTTPS-запрос всё равно не выйдет: криптографии для TLS в двух килобайтах оперативки не хватает. Разделение труда честное: ESP32 добывает, Arduino рисует — у каждой платы работа по силам, и интерфейс между ними остаётся одним байтом.
Расширять сценарии тут одно удовольствие, потому что протокол уже держит рамку. Код 4 — гроза: тёмно-синий фон fill_solid и случайная белая вспышка на двух-трёх пикселях раз в несколько сотен миллисекунд. Код 5 — снег: тусклые белые пиксели, которые медленно ползут вниз по ленте сдвигом. А вторым числом можно передавать интенсивность: 31 — морось, 32 — ливень, и лента красит не только «что», но и «сколько» — ровно так растут настоящие протоколы: комната за комнатой, без перестройки фундамента.
- 1. Почему в протоколе числа, а не слова вроде solnce?
- 2. Что вернёт Serial.parseInt, если в буфере только буквы, а цифр нет?
- 3. Как в скетче сделаны «просветы» в облаках?
- 4. Зачем приёмник печатает code= в ответ на валидный код?
- 5. Кто в реальном проекте вытянет прогноз из API погоды и почему?
- Коды 1-2-3 — протокол для машины: один байт смысла, parseInt и проверка диапазона вместо буферизации и терминаторов
- Ноль по таймауту parseInt — не код погоды: available() перед чтением и диапазон 1-3 отрезают мусор и секундные лаги
- Сценарии — постоянные CRGB-тройки; зоны вроде просветов делаются покраской секций после fill_solid
- На каждое число — подтверждение code= в порт: отправитель должен знать, что дошло и принято
- В реальном приборе ESP32 добывает прогноз из API (группы кодов 800, 801-804, 5xx), Arduino только красит — приёмник не меняется
Частые вопросы по теме урока
- Почему в протоколе числа, а не слова вроде solnce?
- Для человека мы писали слова в уроке про эффект по Serial; для машины достаточно байта смысла. parseInt буквы, кстати, умеет — просто пропускает их, что и тянет за собой таймауты. Коды шлёт машина: меньше байт, проще разбор через parseInt, не страшны опечатки
- Что вернёт Serial.parseInt, если в буфере только буквы, а цифр нет?
- Как и на живой плате, движок ждёт таймаут и возвращает 0: ноль в этом протоколе означает «цифр не нашёл», поэтому проверка диапазона 1-3 обязательна. 0 — по истечении таймаута 1000 мс
- Как в скетче сделаны «просветы» в облаках?
- После fill_solid базовым серо-голубым цикл от середины ленты перекрашивает пиксели светлым CRGB(185, 195, 215): так собирается любой двухцветный сценарий по секциям. Второй половине ленты красится свой, более светлый цвет
- Зачем приёмник печатает code= в ответ на валидный код?
- Протокол без ответов превращает настройку в угадайку: одна строка code=2 в мониторе показывает, что услышал приёмник, — и что 22 вместо двойки он бы отклонил. Чтобы отправитель знал, что код дошёл и принят
- Кто в реальном проекте вытянет прогноз из API погоды и почему?
- HTTPS-клиенту нужны криптография и память, которых в двух килобайтах оперативки Uno нет; ESP32 с ArduinoJson уже проходил разбор ответов OpenWeatherMap. Приёмник на Uno при этом не меняется. ESP32: HTTP и разбор JSON у него по силам, а Uno с HTTPS не справляется