Zerynth Lamp: умный IoT-светильник на NeoPixel + Zerynth App (Python)
Компоненты и материалы
- Zerynth Shield (мультисенсорная плата) — 1 шт.
- Плата управления (на выбор, Zerynth мультиплатформенна):
- Arduino Due
- STM32 Nucleo F401RE
- Particle Photon (Wi-Fi встроен)
- UDOO Dual
- Flip&Click (Mikroelektronika)
- Лента/кольцо NeoPixel (Adafruit) — 1 шт.
- Wi-Fi Shield (Adafruit CC3000) — если плата без Wi-Fi (Photon не нужен).
- Корпус лампы (стекло или 3D-печать) — 1 шт.
Софт
- Zerynth Studio (Python IDE, кроссплатформенная, облачная синхронизация) — скачать
- Zerynth App (мобильный интерфейс для IoT) — скачать
О проекте
Умное освещение — драйвер IoT и домашней автоматизации. Руководство: управление NeoPixel лентой через мобильное приложение Zerynth App — цветовая палитра + выбор анимаций.
Шаг 1: Материалы
(Список выше. Фото компонентов в оригинале.)
Шаг 2: Сборка
- Установите Wi-Fi Shield и Zerynth Shield на плату (на Photon — только Zerynth Shield на dédié коннекторы).
- Подключите NeoPixel к порту на Zerynth Shield.
- Поместите всё в корпус лампы.
Шаг 3: Программирование (Zerynth Studio — Python!)
- Подключите плату, сделайте «Virtualize» (установка VM).
- Создайте проект: 3 файла + project.md —
main.py,animation.py,template.html. - Скопируйте код ниже.
- Uplink (загрузка) на плату.
После включения лампа коннектится к заданной Wi-Fi сети. Откройте Zerynth App — найдет «Zerynth Lamp», откроет интерфейс: Color Picker + Dropdown анимаций (Little Snakes, Spinner, Rainbow, Pulse) + ползунок скорости.
Код
main.py
import streams
from bcm43362 import bcm43362 as wifi_driver
from wireless import wifi
import animation
from toishield import toishield
from zerynthapp import zerynthapp
streams.serial()
# Wi-Fi
try:
wifi_driver.auto_init()
print("Establishing Link...")
wifi.link("SSID", wifi.WIFI_WPA2, "password") # <-- ВСТАВЬТЕ СВОИ
print("Ok!")
except Exception as e:
print(e)
# HTML-шаблон в флеш
new_resource("template.html")
# Zerynth App callbacks (JS -> Python)
def change_color(r, g, b):
animation.setup_color(r, g, b)
def change_animation(n):
animation.setup_anim(n)
def change_speed(n):
animation.setup_anim_speed(n)
vp = zerynthapp.ZerynthApp("Zerynth Lamp", "Try me!", "resource://template.html")
vp.on("change_color", change_color)
vp.on("change_animation", change_animation)
vp.on("change_speed", change_speed)
vp.run() # отдельный поток
# Запуск анимации на пине D6, 24 LED
animation.start(D6, 24)
template.html (UI в приложении)
<html>
<head>
<zerynth/> <zerynth-jquery/> <zerynth-jquery-mobile/> <zerynth-jqwidgets/>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div data-role="page">
<div data-role="header"><h1>Zerynth Lamp</h1></div>
<div role="main" class="ui-content" style="text-align:center">
<div style="margin: 3px; float: left;" id="dropDownButton">
<div style="padding: 3px;"><div id="colorPicker"></div></div>
</div>
<button class="ui-btn ui-btn-inline ui-shadow" onclick="ZerynthApp.call('change_color',cur_color.r,cur_color.g,cur_color.b)">Colorize!</button>
<form>
<div data-role="fieldcontain">
<label for="animselect">Animation:</label>
<select name="animselect" id="animselect">
<option value="0">Little Snakes</option>
<option value="1">Spinner</option>
<option value="2">Rainbow</option>
<option value="3">Pulse</option>
</select>
</div>
</form>
<form>
<label for="speedslider">Animation Speed:</label>
<input type="range" name="speedslider" id="speedslider" data-highlight="true" min="0" max="100" value="50">
</form>
</div>
<div data-role="footer">Powered by Zerynth (www.zerynth.com)</div>
</div>
<script>
var cur_color = {r: 0x54, g: 0x9a, b: 0x97};
// ... jqxColorPicker init, event handlers -> ZerynthApp.call(...)
</script>
</body>
</html>
animation.py (логика эффектов)
from neopixel import ledstrips as neo
import threading
lock = threading.Lock()
color = [0x54, 0x9a, 0x97] # дефолтный цвет
anim = 0
anim_speed = 50
leds = layer0 = layer1 = layer2 = None
npins = 0
stopped = False
stopcolor = [0xff, 0xff, 0xff]
animation_coefficients = [0, 0.2588, 0.5, 0.7071, 0.8660, 0.9659, 1, 0.9659, 0.8660, 0.7071, 0.5, 0.2588]
rainbow = [(0xff,0,0), (0xff,0x7f,0), (0xff,0xff,0), (0,0xff,0), (0,0,0xff), (0x4b,0,0x82), (0x8f,0,0xff)]
def setup_anim(n):
global layer0, layer1, layer2, anim
lock.acquire()
leds.clear(); layer2.clear(); layer0.clear(); layer1.clear()
n = n % 4
if n == 0: # Little Snakes
layer0[0]=layer0[1]=layer0[2]=(100,0,0)
layer1[0]=layer1[1]=layer1[2]=(0,100,0)
elif n == 1: # Spinner
for x in range(npins//2):
layer0[x] = (100//(2*x+1), 0, 0)
layer1[npins-x-1] = (0, 100//(2*x+1), 0)
layer2.clear()
elif n == 2: # Rainbow
layer1.clear()
pstep = 0
for x in range(npins):
step = x * len(rainbow) / npins
rx = (rainbow[int(step)][0] + rainbow[int(pstep)][0]) // 4
gx = (rainbow[int(step)][1] + rainbow[int(pstep)][1]) // 4
bx = (rainbow[int(step)][2] + rainbow[int(pstep)][2]) // 4
layer0[x] = (rx, gx, bx)
pstep = step
elif n == 3: # Pulse
layer0.clear(); layer1.clear()
anim = n
lock.release()
def setup_anim_speed(n):
global anim_speed
anim_speed = n
def setup_color(r, g, b):
global color
color[0], color[1], color[2] = r, g, b
def animate_background(delay):
global color
step = 0
while True:
if (anim == 3 or anim == 0) and not stopped:
lock.acquire()
layer2.setall(int(color[0]*animation_coefficients[step]/2),
int(color[1]*animation_coefficients[step]/2),
int(color[2]*animation_coefficients[step]/2))
lock.release()
step = (step + 1) % len(animation_coefficients)
else:
lock.acquire()
layer2.clear(); layer2.setall(stopcolor[0], stopcolor[1], stopcolor[2])
lock.release()
sleep(delay + 500 - 5*anim_speed)
def animate_foreground(delay):
while True:
if not stopped:
lock.acquire()
if anim == 0: layer0.lshift(); layer1.rshift()
elif anim == 1: layer0.rshift(); layer1.rshift()
elif anim == 2: layer0.rshift(); layer1.rshift()
elif anim == 3: layer0.lshift(); layer1.lshift()
lock.release()
else:
lock.acquire(); layer0.clear(); layer1.clear(); lock.release()
sleep(delay + 100 - anim_speed)
def start(pin, numpins):
global leds, layer0, layer1, layer2, npins
npins = numpins
leds = neo.LedStrip(pin, numpins)
layer0 = neo.LedStrip(pin, numpins)
layer1 = neo.LedStrip(pin, numpins)
layer2 = neo.LedStrip(pin, numpins)
setup_anim(0)
setup_anim_speed(50)
thread(animate_background, 500)
thread(animate_foreground, 50)
while True:
leds.clear()
lock.acquire()
leds.merge(layer0); leds.merge(layer1)
leds.merge(layer2, neo.first_color) # фон только где прозрачно
lock.release()
leds.on()
sleep(50)
def stop(r, g, b):
global stopped, stopcolor
stopcolor = [r, g, b]
stopped = True
def resume():
global stopped
stopped = False
setup_anim(anim)
Шаг 4: Расширение возможностей
Zerynth Shield имеет: тач-сенсор, IR LED, микрофон, датчик света, температуру. Используйте их для реакции лампы на окружение (хлопок, освещение, температуру). Код простой и комментирован. Вопросы — на форуме Zerynth.
Схема
Фото схемы подключения (JPG) в разделе Schematics оригинала.
Производственный процесс
- Распространение электрического сигнала со скоростью света: мысленный эксперимент на 186 000 миль
- Газовые фонари: эволюция, дизайн и современное производство
- Лазерный указатель: дизайн, производство и обзор безопасности
- Фонарик: от изобретения к современным инновациям
- Галогенные лампы: технологии, история и будущее — экспертный обзор
- Светофоры: инженерия, история и будущие инновации
- От огня к нити накаливания: эволюция и производство современной лампы накаливания
- Создание многодатчиковой системы мониторинга температуры и света с Raspberry Pi и DS18B20
- Умный светофор: адаптивное уличное освещение на базе IoT
- Цифровой датчик света — управление светодиодом по освещённости на Windows 10 IoT Core