День #10: ULTRA мигалка! — Адресная RGB-гирлянда

Let it Glow — День 10: ULTRA Blinky!

Сегодня день #10 адвент-календаря «Let it Glow»!

Снова день мигалок — и какой грандиозный праздник света приготовил нам сегодняшний ящик! Внутри маленькой чёрной коробочки вы найдёте адресную RGB-гирлянду с точечными светодиодами — ещё один формат RGB-светодиодов, открывающий нам новые возможности для экспериментов!

До этого вы уже получили диффузные RGB (_GRB_) светодиоды, RGB-кольцо, а теперь эти _сверхмигающие_ RGB-гирлянды. Это значит, что у вас уже есть знания для написания программ для этих огней — но мы покажем вам несколько новых примеров, чтобы дать ещё больше способов поиграть.

ДАВАЙТЕ. МИГАТЬ!


Внимание: некоторые сегодняшние активности содержат быстро мигающий свет, который может быть неподходящим для людей с фотосенсорной эпилепсией.


Содержимое коробки #10

В этой коробке вы найдёте:

  • 1x адресная RGB-гирлянда с 15 светодиодами (dot strand)

Содержимое коробки день 10

Сегодняшние активности

Сегодня мы покажем вам, как подключить гирлянду и адаптировать существующие примеры кода для RGB-светодиодов, чтобы они работали с ней. Мы также дадим вам новые примеры, используя некоторые из ваших компонентов, и несколько идей для реальных проектов!

Прежде чем начать, давайте кратко познакомимся с новым любимым форматом RGB-светодиодов!

Что такое адресная RGB-гирлянда с точечными светодиодами

Эти точечные гирлянды работают точно так же, как LED-кольцо из дня #8, просто в другом формате.

Внутри этих маленьких белых «капелек» находятся такие же адресные светодиоды, которыми ваш код может управлять — они просто имеют гораздо более гибкий формат для встраивания в ваши собственные проекты и украшения.

Специальные гирлянды в вашей коробке имеют всего три провода — 5V, Data IN и GND _(без вывода DOUT)_ — и оснащены джамперными концами, что делает их идеальными для прототипирования на макетной плате.

Сборка цепи

Как всегда, убедитесь, что ваш Pico отключён от USB-кабеля, когда работаете с цепью.

Подготовка макетной платы

Начнём с чистого листа — снимите все компоненты и провода, оставив на макетной плате только ваш Pico.

Установка гирлянды

ВАЖНО! Первое, что нам нужно сделать, — это найти полосатый провод. Один из проводов имеет пунктирные полоски вдоль него, и это вывод 5V. Нам очень важно правильно подключить этот вывод, иначе мы создадим проблемы!

Вот крупный план этих полосок:

Полосатый провод 5V гирлянды

Вставьте этот полосатый вывод 5V в макетную плату с левой стороны, затем установите два других, по порядку (они скреплены вместе), рядом с ним с промежутком между ними (см. изображение ниже).

Теперь у нас много запасных джамперных проводов, так что воспользуемся ими.

  • Подключите левый (полосатый) вывод к VBUS (физический контакт 40)

  • Подключите средний вывод к GPIO2 (физический контакт 4)

  • Подключите правый вывод к GND (физический контакт 3)

Ваша макетная плата должна выглядеть вот так:

День 10 — подключение гирлянды

Установка слайдер-потенциометра

Слайдер — это слишком весело, чтобы не включить его в работу, и он отлично сочетается с нашей гирляндой, поэтому давайте снова поставим его на макетную плату.

Мы устанавливаем его ровно на то же место, что и в день #7, с теми же используемыми контактами.

Вот напоминание о используемых контактах и их расположении:

  • Подключите нижнюю одиночную ножку к выводу земли — мы использовали физический контакт 28

  • Теперь подключите верхнюю левую ножку слайдера к выводу 3.3V — это физический контакт 36

  • Наконец, подключите верхнюю правую ножку слайдера к GPIO28 (физический контакт 34) — наш ADC-вывод

Ваша макетная плата должна выглядеть вот так:

День 10 — полная схема

_**Совет**: Вы можете подключить эту гирлянду прямо в отверстия макетной платы рядом с Pico, так как 5V (VBUS), GND и GPIO-вывод достаточно близко друг к другу, однако это добавляет риск неправильного подключения, поскольку видимость менее чёткая._


Активность #1: Простой тест гирлянды

Вы уже знаете, как управлять RGB-светодиодами, поэтому давайте быстро проверим, что всё подключено правильно и работает как надо.

Как и следовало ожидать, мы импортировали библиотеку neopixel, установили номер вывода гирлянды (GPIO2) и задали количество светодиодов (15).

Затем мы показываем красный цвет при низкой яркости (50,0,0) в течение 10 секунд — этого достаточно, чтобы убедиться, что всё работает.

Скопируйте код в Thonny и попробуйте:

# Импорты
from machine import Pin
from neopixel import NeoPixel
import time

# Определяем номер вывода гирлянды (2) и количество светодиодов (15)
strand = NeoPixel(Pin(2), 15)

# Заполняем красным на 10 секунд
strand.fill((50,0,0))
strand.write()
time.sleep(10)

# Выключаем
strand.fill((0,0,0))
strand.write()

Активность #2: Повторное использование (и улучшение) программ для LED-кольца

Классная вещь в нашем адресном кольце и гирлянде — они работают одинаково и оба RGB. Это означает, что вы можете повторно использовать любые примеры программ (и всё, что вы придумали сами), просто задав ссылку на вывод и количество светодиодов.

Возьмём, к примеру, нашу программу «Прыгающий огонь» из дня #8 — всё, что нам нужно сделать, это установить GPIO2 и 15 светодиодов для адресации нашей гирлянды:

strand = NeoPixel(Pin(2), 15)

Однако нам также потребуется изменить все ссылки на 12 светодиодов в коде, потому что в нашей гирлянде 15. Наш код «Прыгающего огня» мог бы быть лучше, если бы мы использовали переменные для количества светодиодов — тогда изменение одного значения исправляло бы его везде при каждом использовании нового типа RGB-светодиодов.

Переменные для GPIO-вывода, количества светодиодов и цвета

Давайте улучшим наш код, чтобы сделать его более удобным для повторного использования!

Мы создадим переменную ledcount, которую установим равной 15 — количеству светодиодов в нашей гирлянде.

Затем изменим строку настройки и функции range, чтобы они использовали эту переменную вместо фиксированного числа — это значит, что нам нужно будет изменить только одно значение для обновления всей программы, что очень удобно, когда программы становятся всё больше и больше.

Мы сделаем то же самое для номера GPIO-вывода с переменной GPIOnumber, чтобы упростить работу при возврате к старому коду позже.

Наконец, мы создали переменную mycolour — мы можем изменить её на любой RGB-код по своему желанию, и это обновит нашу программу там, где мы её использовали.

С этими изменениями запустите обновлённую программу ниже, и теперь у вас есть прыгающий огонь вдоль гирлянды!

# Импорты
from machine import Pin
from neopixel import NeoPixel
import time

# Параметры светодиодов
GPIOnumber = 2
LEDcount = 15

# Переменная цвета
mycolour = 255,0,0

# Определяем номер вывода и количество светодиодов из переменных
strand = NeoPixel(Pin(GPIOnumber), LEDcount)

# Выключаем все светодиоды перед запуском программы
strand.fill((0,0,0))
strand.write()
time.sleep(1)

while True:

    for i in range(LEDcount):

        strand[i] = (mycolour)
        strand.write()

        # Показываем огонь столько времени
        time.sleep(0.09)

        #Очищаем гирлянду в конце каждого цикла
        strand.fill((0,0,0))
        strand.write()

    for i in reversed (range(LEDcount)):

        strand[i] = (mycolour)
        strand.write()

        # Показываем огонь столько времени
        time.sleep(0.09)

        #Очищаем гирлянду в конце каждого цикла
        strand.fill((0,0,0))
        strand.write()

Активность #3: Начало/конец/шаг в range

Давайте попробуем что-то более продвинутое с функцией range, чтобы создать различные узоры на нашей гирлянде.

До сих пор мы использовали функцию range вот так с RGB-цветовыми кодами, что позволяло нам плавно зажигать и гасить светодиод:

for i in range(255):

Однако вы можете добавить несколько аргументов в эти скобки, чтобы задать значение начала диапазона, значение конца диапазона и шаг для прыжка при каждом цикле.

Компоновка следующая:

for i in range(start, stop, step):

Используя снова RGB-значения в качестве примера, представьте, что мы не хотим гасить светодиод до 0 (выключен), а на самом деле останавливаемся на значении 10, чтобы оставить светодиод включённым. Для этого мы могли бы использовать:

for i in range (255, 10, -1):

Это начинает значение светодиода с 255 (максимальная яркость), заканчивает значение на 10 (низкая яркость), а шаг равен -1, чтобы оно шло вниз от 255 до 10.

Код

Мы создали пример программы именно с этим! Наша гирлянда начинается с низкой интенсивности красного (10,0,0), затем переходит в цикл while.

Цикл while содержит цикл for, который проходит по каждому светодиоду в нашем индексе. Затем мы сразу переходим во вложенный цикл for, который устанавливает красную интенсивность каждого из этих зациклённых светодиодов от 255 до исходного 10 с очень коротким задержкой.

Это даёт эффект постоянно тусклой красной гирлянды с ярким красным светом, пульсирующим сквозь неё! Попробуйте пример ниже и читайте комментарии, чтобы понять, что делает каждая часть:

# Импорты
from machine import Pin
from neopixel import NeoPixel
import time

# Параметры светодиодов
GPIOnumber = 2
LEDcount = 15

# Определяем номер вывода и количество светодиодов из переменных
strand = NeoPixel(Pin(GPIOnumber), LEDcount)

# Список индексов светодиодов
ledindex = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]

# Запускаем гирлянду красным с низкой интенсивностью
strand.fill((10,0,0))
strand.write()
time.sleep(1)

while True:

    # Итерируем по каждому светодиоду
    for led in ledindex:

        # Плавно зажигаем/гасим каждый итерируемый светодиод
        for i in range(255,10,-1):

            strand[led] = (i,0,0) # Используем значение i для R в RGB
            strand.write()
            time.sleep(0.001) # Короткая задержка

…или пульс другого цвета!

Вы также можете добавить пару строк в конец программы, чтобы пульс другого цвета проходил сквозь красную гирлянду.

Сначала измените вложенный цикл for (пульс), чтобы использовать другой цвет. Мы хотели попробовать белый, поэтому установили RGB-цвет на использование i во всех трёх значениях с strand[led] = (i,i,i) — _да, так можно!_

Затем после окончания вложенного цикла for мы можем установить цвет этого итерируемого светодиода обратно на исходный красный с низкой интенсивностью.

Попробуйте и поэкспериментируйте с цветами:

# Импорты
from machine import Pin
from neopixel import NeoPixel
import time

# Параметры светодиодов
GPIOnumber = 2
LEDcount = 15

# Определяем номер вывода и количество светодиодов из переменных
strand = NeoPixel(Pin(GPIOnumber), LEDcount)

# Список индексов светодиодов
ledindex = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]

# Запускаем гирлянду красным с низкой интенсивностью
strand.fill((10,0,0))
strand.write()
time.sleep(1)

while True:

    # Итерируем по каждому светодиоду
    for led in ledindex:

        # Плавно зажигаем/гасим каждый итерируемый светодиод
        for i in range(255,10,-1):

            strand[led] = (i,i,i) # Используем i для всех значений (белый)
            strand.write()
            time.sleep(0.001) # Короткая задержка

        # Возвращаем светодиоду исходный красный цвет
        strand[led] = (10,0,0)
        strand.write()

Активность #4: Статические чётные и нечётные

Мы можем создать _классический старомодный_ праздничный узор красный/зелёный на нашей гирлянде, приказав коду выбрать для нас нечётные и чётные номера светодиодов в нашем индексе.

Для этого мы используем оператор деления по модулю. _Звучит как плохая рок-группа 90-х, правда?_ Давайте объясним, что это такое…

Оператор деления по модулю

В MicroPython оператор деления по модулю — это символ процента %.

Когда мы используем его в коде, он может дать нам остаток от деления двух чисел.

Например:

  • Мы знаем, что 12 делённое на 5 = 2.4

  • Мы хотим знать, сколько раз можно получить 5 из 12, и что остаётся после этого

  • Мы можем получить 5 два раза из 12, и это оставляет нам 2 в остатке

Мы можем использовать модуль, чтобы найти этот остаток для нас, вот так (мы ввели это в оболочку):

>>> 12 % 5
2

Как оператор деления по модулю помогает с гирляндами?!

Мы можем использовать оператор деления по модулю, чтобы узнать, делится ли число на 2. Все чётные числа делятся на два и оставляют 0 в качестве остатка, поэтому этот тест скажет нам, является ли число чётным.

Мы можем приказать нашему коду посмотреть на индекс светодиода из нашего списка и сказать нам, делится ли он на 2 (_«остаток равен 0»_). Затем мы можем использовать операторы if, чтобы сказать _«делай это, если чётное»_ и _«делай это, если нет»_.

Код

Пример кода ниже включает список для нашего индекса светодиодов вместе с обычными строками настройки.

Нам не нужен цикл while, так как мы просто показываем статические цвета. Вместо этого мы используем цикл for для итерации по каждому светодиоду в нашем индексе.

Внутри этого цикла for находятся оператор if и оператор else.

  • Оператор if говорит _«для каждого индекса светодиода, по которому мы итерируем, если мы делим его на 2, остаток равен 0?»_

  • Если да, это означает, что число чётное, и эти чётные светодиоды загорятся красным

  • Оператор else захватывает всё, что не является чётным (нечётные числа), и зажигает их зелёным

Просто, правда? Попробуйте код ниже и убедитесь сами:

# Импорты
from machine import Pin
from neopixel import NeoPixel
import time

# Параметры светодиодов
GPIOnumber = 2
LEDcount = 15

# Определяем номер вывода и количество светодиодов из переменных
strand = NeoPixel(Pin(GPIOnumber), LEDcount)

# Переменные цветов
red = 255,0,0
green = 0,255,0

# Список индексов светодиодов
ledindex = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]

# Выключаем все светодиоды перед запуском программы
strand.fill((0,0,0))
strand.write()
time.sleep(1)

for led in ledindex:

    if (led % 2) == 0: #Если индекс светодиода чётный
        strand[led] = (red)

    else: # Если нет (нечётные числа)
        strand[led] = (green)

    strand.write()

Активность #5: Мигающие чётные и нечётные

Мы можем сделать пример выше более _ярким_, чередуя нечётные/чётные светодиоды между красным и зелёным. Столь же старомодно, просто немного веселее!

Нам не нужно много менять для этого — просто нужно:

  • Поместить наши циклы for внутрь цикла while

  • Добавить второй цикл for, чтобы сделать противоположные цвета к первому

  • Добавить задержку между каждым циклом

Ничего из этого не является для вас новым — это просто ещё один цикл for после первого, так что ознакомьтесь с примером ниже и комментариями к коду, затем попробуйте:

# Импорты
from machine import Pin
from neopixel import NeoPixel
import time

# Параметры светодиодов
GPIOnumber = 2
LEDcount = 15

# Определяем номер вывода и количество светодиодов из переменных
strand = NeoPixel(Pin(GPIOnumber), LEDcount)

# Переменные цветов
red = 255,0,0
green = 0,255,0

# Список индексов светодиодов
ledindex = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]

# Выключаем все светодиоды перед запуском программы
strand.fill((0,0,0))
strand.write()
time.sleep(1)

while True:

    # Первый цикл: чётные красные и нечётные зелёные
    for led in ledindex:

        if (led % 2) == 0: #Если индекс светодиода чётный
            strand[led] = (red)

        else: # Если нет (нечётные числа)
            strand[led] = (green)

        strand.write()

    # Задержка для отображения цветов перед их сменой
    time.sleep(0.5)

    # Второй цикл: чётные зелёные и нечётные красные
    for led in ledindex:

        if (led % 2) == 0: #Если индекс светодиода чётный
            strand[led] = (green)

        else: # Если нет (нечётные числа)
            strand[led] = (red)

        strand.write()

    # Задержка для отображения цветов перед их сменой
    time.sleep(0.5)

Активность #6: Гирлянда со слайдером!

Давайте вернём слайдер-потенциометр в код и используем его для запуска интересной программы с нашей гирляндой.

С нашим LED-кольцом мы использовали слайдер для затемнения светодиодов и управления скоростью мигания, но мы думаем, что было бы интереснее скользить светодиодами вверх и вниз по гирлянде!

Для этого нам нужно снова заняться математикой, разделив аналоговый диапазон на количество светодиодов. Поехали!

Разбор кода

Нам, конечно, нужно снова импортировать ADC, а также настроить правильный ADC-вывод для слайдера, как мы делали раньше (GPIO28).

Теперь мы хотим, чтобы наш слайдер с диапазоном 0 и 65535 управлял нашими светодиодами, которых у нас 15. Поэтому нам нужно преобразовать аналоговый диапазон в диапазон светодиодов.

В нашей настройке мы используем следующую строку, чтобы разделить аналоговый диапазон (65535) на количество светодиодов (15), давая нам переменную (LEDdivision), которую мы можем использовать для деления нашего показания:

# Делим аналоговый диапазон на количество светодиодов
LEDdivision = (65535/LEDcount)

Наш цикл while затем считывает аналоговое значение со слайдера, которое мы затем делим на нашу переменную LEDdivision, чтобы получить (почти) номер светодиода, который мы можем использовать в коде. Однако нам нужно округлённое число, поэтому мы одновременно применяем функцию round.

Мы решили сделать всё это в одной строке на этот раз, а не считывать значение отдельно, а затем манипулировать им (мы расставили пробелы, чтобы было легче читать — MicroPython не возражает):

reading = round( (potentiometer.read_u16()) / LEDdivision )

Как это работает? Вот рабочий пример:

  • 65535 делённое на 15 = 4369

  • Так что _примерно_ каждые 4369 аналогового диапазона дадут нам 1, чтобы зажечь один из наших светодиодов

  • Мы считываем значение со слайдера, которое находится между 0 и 65535

  • Допустим, значение 15000 — мы делим это число на 4369, которое получили ранее = 3.43

  • Мы округляем это 3.43, получая 3

  • Затем мы говорим нашему коду зажечь столько светодиодов

Мы используем два цикла for, чтобы задать, какие светодиоды должны быть включены, а какие — выключены, поскольку нашему коду нужно снова выключать светодиоды при скольжении обратно вниз.

Первый цикл простой — он использует значение из вычисления выше как диапазон, и мы включаем эти светодиоды с цветом:

# Задаём, какие светодиоды должны быть ВКЛЮЧЕНЫ
for ledon in range (reading):
    ring[ledon] = (0,0,255)

Второй цикл for использует параметры начало/конец/шаг, которые мы рассмотрели ранее, чтобы помочь ему определить все светодиоды, которые должны быть ВЫКЛЮЧЕНЫ.

Мы используем значение reading как начало, а количество светодиодов как конец, с шагом 1. Это даёт нам все светодиоды, которые не должны быть включены.

# Задаём, какие светодиоды должны быть ВЫКЛЮЧЕНЫ
for ledoff in range ((reading),LEDcount,1):
    ring[ledoff] = (0,0,0)

Всё, что осталось сделать, — это записать данные в LED-полосу после того, как оба цикла for установили светодиоды для освещения, с небольшой задержкой для обеспечения плавного движения светодиодов при скольжении.

Полная программа ниже — попробуйте:

# Импорты
from machine import Pin, ADC
from neopixel import NeoPixel
import time

# Параметры светодиодов
GPIOnumber = 2
LEDcount = 15

# Определяем номер вывода и количество светодиодов
ring = NeoPixel(Pin(GPIOnumber), LEDcount)

# Настраиваем слайдер на ADC-вывод 28
potentiometer = ADC(Pin(28))

# Убеждаемся, что полоса очищена перед началом
ring.fill((0,0,0))
ring.write()
time.sleep(1)

# Делим аналоговый диапазон на количество светодиодов
LEDdivision = (65535/LEDcount)

while True:

    # Считываем значение
    # Делим значение на LEDdivision
    # Округляем значение
    reading = round( (potentiometer.read_u16()) / LEDdivision )

    # Задаём, какие светодиоды должны быть ВКЛЮЧЕНЫ
    for ledon in range (reading):
        ring[ledon] = (0,0,255)

    # Задаём, какие светодиоды должны быть ВЫКЛЮЧЕНЫ
    for ledoff in range ((reading),LEDcount,1):
        ring[ledoff] = (0,0,0)

    # Записываем данные
    ring.write()
    time.sleep(0.1)

…и использование random делает всё веселее!

Мы можем немного изменить пример выше, чтобы заполнить гирлянду случайными цветами вместо фиксированного синего значения — _потому что функция random — лучшее, что есть в светодиодах!_

Она создаёт отличный эффект, потому что постоянно циклируется с задержкой 0.1 секунды — это означает, что вы можете скользить светодиодами, пока они мерцают множеством разных цветов, когда включены.

Всё, что нам нужно сделать, — импортировать random, позаимствовать код случайного RGB-цвета из Дня #7 (Активность #4), поместить его в наш цикл while, затем установить первый цикл for на использование переменных r, g и b для цвета светодиода.

Вот код:

# Импорты
from machine import Pin, ADC
from neopixel import NeoPixel
import time
import random

# Параметры светодиодов
GPIOnumber = 2
LEDcount = 15

# Определяем номер вывода и количество светодиодов
ring = NeoPixel(Pin(GPIOnumber), LEDcount)

# Настраиваем слайдер на ADC-вывод 28
potentiometer = ADC(Pin(28))

# Убеждаемся, что полоса очищена перед началом
ring.fill((0,0,0))
ring.write()
time.sleep(1)

# Делим аналоговый диапазон на количество светодиодов
LEDdivision = (65535/LEDcount)

while True:

    # Считываем значение
    # Делим значение на LEDdivision
    # Округляем значение
    reading = round( (potentiometer.read_u16()) / LEDdivision )

    # Создаём переменные случайного RGB-кода
    r = random.randint(0,255)
    g = random.randint(0,255)
    b = random.randint(0,255)

    # Задаём, какие светодиоды должны быть ВКЛЮЧЕНЫ
    for ledon in range (reading):
        ring[ledon] = (r,g,b) # Используем переменные случайного RGB

    # Задаём, какие светодиоды должны быть ВЫКЛЮЧЕНЫ
    for ledoff in range ((reading),LEDcount,1):
        ring[ledoff] = (0,0,0)

    # Записываем данные
    ring.write()
    time.sleep(0.1)

_**Совет**: Если последняя активность оказалась для вас слишком яркой, просто уменьшите максимальный диапазон интенсивности случайных переменных до меньшего числа, например до 50. Например, **r = random.randint(0,50)**_


День #10 завершён!

Есть ли что-нибудь веселее RGB-светодиодов?!

Сегодня мы показали вам больше способов работы с адресными светодиодами, включая несколько новых приёмов и примеров, которые вы можете применить к вашему LED-кольцу и любым другим адресным светодиодам, с которыми вы будете работать в будущем.

Нам особенно нравятся эти гибкие гирлянды — они могут использоваться для создания отличных декораций и световых проектов:

  • Засуньте их в стеклянную банку для классного светильника в спальне

  • Приклейте их за монитором или под столом с помощью голубого Blu-tack

  • Оберните их вокруг фигур, чтобы создать оригинальные украшения

  • …или просто поместите их на небольшую ёлку!

Краткое содержание — сегодня вы:

  • Создали схему с адресной RGB-гирляндой

  • Научились повторно использовать программы и узнали лучшие практики для упрощения этого (с переменными)

  • Расширили свои знания о функции range с аргументами начало/конец/шаг

  • Познакомились с оператором деления по модулю

  • Использовали математику для преобразования диапазонов (аналоговый диапазон в диапазон светодиодов)

  • Повторно использовали множество навыков из предыдущих дней _(включая функцию random, потому что она потрясающая!)_

Завтра нас ждёт ещё один управляющий компонент, дающий нам ещё больше способов управлять нашими мигалками для создания ещё более классных проектов!

До встречи!