![](https://habrastorage.org/webt/kh/e9/2m/khe92mi1t6q68vhqpcgle2f50cw.jpeg)
Всем привет! Я работаю в компании, которая разрабатывает оборудование для мониторинга нефтяных вышек, майнинговых ферм, ДГУ и прочих скучных вещей. Но в этот раз мы автоматизировали кое-что иное. Итак, встречайте: «Кукинатор 3000» — аппарат по выдаче печенья лучшим сотрудникам.
Аппарат представляет из себя конструкцию из ПВХ и некоторых деталей:
Спираль c приводом (служит для подачи товара), экран, монетоприемник, и мозг всей конструкции — контроллер Wiren Board 6.
![](https://habrastorage.org/webt/td/5c/ss/td5csslpot3ey_uuzx0yvirjd90.gif)
![Нажмите для увеличения изображения](https://habrastorage.org/webt/cc/39/cu/cc39cuim2tu0o8nine6dmo9r0ok.jpeg)
![Нажмите для увеличения изображения](https://habrastorage.org/webt/zc/ll/vn/zcllvnbavuzknmcx-vkecvjlp9m.jpeg)
![Нажмите для увеличения изображения](https://habrastorage.org/webt/mw/3p/w0/mw3pw0garxwkyjp8orka5ymrcqo.jpeg)
![Нажмите для увеличения изображения](https://habrastorage.org/webt/pb/tc/kx/pbtckxlynifmqbzfun4heex-4m0.jpeg)
Экран
В качестве экрана для WB6 я использую экранный модуль Nextion (NEXTION ENHANCED NX4832K035).
![](https://habrastorage.org/webt/hw/6j/9d/hw6j9dvuwg6wqxkr9kwp3cp9upk.jpeg)
Модуль оснащён AVR-процессором. Имеет 3.5'' TFT дисплей с разрешением 480 * 320, 32Mb встроенной памяти, 1024 byte EEPROM, 8192 byte RAM.
Данное устройство хранит и отображает графику, анимирует элементы интерфейса и обрабатывает нажатия. На контроллер по UART передаются только коды событий, что позволяет легко настраивать интерфейсы различной сложности.
Для загрузки необходимых настроек и графики в экранный модуль предусмотрен визуальный редактор интерфейсов «Nextion Editor». Скачать его можно на официальном сайте itead.
![Нажмите для увеличения изображения](https://habrastorage.org/webt/x6/u_/eb/x6u_ebycvwunndp6nshvmcqizki.png)
При создании нового проекта необходимо указать модель, положение экрана и кодировку символов. Для корректного отображения кириллицы нужно выбрать iso-8859-5.
![Нажмите для увеличения изображения](https://habrastorage.org/webt/nh/sw/m1/nhswm1fhm3dfn99dll_kzqsr4iq.png)
![Нажмите для увеличения изображения](https://habrastorage.org/webt/qt/fe/q-/qtfeq-bai9ghnfgimb4ucysox8y.png)
Слева расположена панель инструментов. Здесь можно найти самые необходимые элементы для создания интерфейса.
![Нажмите для увеличения изображения](https://habrastorage.org/webt/7u/ef/da/7uefdanvxiu9o4ebff_cxuedhia.png)
Давайте нажмем на Button. В поле редактирования появится условная кнопка. Растянем её, отредактируем текст и название. Для отображения текста необходимо добавить шрифт. Воспользуемся инструментом «Font Creator» во вкладке tools. В поле «Event» необходимо написать код, который будет воспроизводиться по событиям.
На пример если во вкладке «Touch press event» прописать print «button pressed\r», то при нажатии на кнопку устройство пошлет в UART: button pressed и символ переноса строки.
![Нажмите для увеличения изображения](https://habrastorage.org/webt/zm/te/k_/zmtek_kvf6e-mb8cxxoddkt6a6m.png)
Описание и примеры кодов можно найти в документации к экрану. Для отладки можно воспользоваться функцией Debug. Данное окно позволяет протестировать все функции прошивки до заливки в сам экран.
![Нажмите для увеличения изображения](https://habrastorage.org/webt/aa/gj/6a/aagj6ajoz26f2oiswoxpdxr0_ce.png)
Если всё в порядке, то можно подключить дисплей к компьютеру через переходник USB/UART и залить прошивку, нажав на кнопку «Upload».
![Нажмите для увеличения изображения](https://habrastorage.org/webt/oi/lf/mk/oilfmkojzwprppfkk5yza0lrpk4.png)
Но как же нам теперь подружить экран с WB6?
Подключить экран можно либо через тот же переходник USB\UART в один из портов USB на контроллере, либо на прямую к одному из слотов расширения на плате контроллера.
Нас интересуют пины TX, RX, 5V и GND.
![Нажмите для увеличения изображения](https://habrastorage.org/webt/jy/zz/tm/jyzztmnldc_my-wqwjvo4tgwavk.jpeg)
После подключения экрана к контроллеру мы будем получать данные о событиях прямо в Serial порт.
Для того, чтобы как-то реагировать на эти события, давайте напишем небольшой скрипт, который будет слать все данные о событиях в MQTT топик. Для начала создадим файл в корневой папке контроллера и назовём его serial.sh
#!/bin/bash
#Инициализируем порт /dev/ttyMOD3
stty -F /dev/ttyMOD3 ospeed 9600 ispeed 9600 raw clocal -parenb -echo cs8
CR="$(echo -e '\r')"
exec 4<> /dev/ttyMOD3
cat <&4 | while :
do
IFS="$CR" read -r line
case "$line" in
quit*)
break
;;
*)
if [[ -n "$line" ]]; then
echo $line
#Полученные строки шлём в MQTT
mosquitto_pub -t /devices/screen/controls/raw/meta/type -r -m text
mosquitto_pub -t /devices/screen/controls/raw/on -r -m "$line"
fi
;;
esac
done
Если вы всё сделали правильно, то после запуска скрипта при нажатии на кнопку — в веб интерфейсе контроллера вы увидите топик с данными, которые посылает экранный модуль.
На контроллерах Wiren Board предусмотрена система правил — wb-rules. Правила создаются через веб-интерфейс и пишутся на простом Javascript-подобном языке.
В веб интерфейсе WB6 зайдите во вкладку Scripts и создайте новый скрипт, нажав на кнопку «New…», назовите его «screen_button.js» или как-нибудь еще. Далее напишем правило: При изменении топика screen/raw (именно в него мы шлем данные с экрана) будут происходить некие действия. В данном случае при нажатии на экран мы увидим в отладочном окне веб интерфейса сообщение «Нажата кнопка»:
defineVirtualDevice("screen", { // создаем виртуальный девайс
title: "nextion screen",
cells: {
raw: {
type: "text",
value: ""
},
}
});
defineRule("screen_button", {
whenChanged: "screen/raw",
then: function(newValue, devName, cellName) {
log(newValue);
if (newValue == "button pressed") { //Если получили текст от экрана «button pressed»
log("Нажата кнопка"); //Тут описываем наши действия на событие.
} else if (newValue == "button released") {
log("Не нажата кнопка");
}
dev["screen"]["raw"] = ""; //Очищаем топик.
}
});
Отлично. С тем как обрабатывать нажатия на экране мы разобрались. Теперь поговорим о том, как нам управлять экраном с WB6. Например изменить текст кнопки. Экран принимает команды по UART. Список и описание команд можно найти в документации к экрану.
Для определения конца команды экран ждёт три спец символа (\xff\xff\xff). Для упрощения задачи в будущем: создадим в корневой папке bash скрипт. Назовём его text.sh:
#!/bin/bash
TEXT="";
UNIT="";
while [ -n "$1" ]
do
case "$1" in
-a) UNIT=$2;;
-b) TEXT=$2;;
esac
shift
done
echo -en "$UNIT.txt=\"$TEXT\"\xff\xff\xff" > /dev/ttyMOD3
exit 0
Теперь для того, чтобы изменить текст кнопки нам достаточно вызвать скрипт с аргументами:
bash /mnt/data/root/text.sh -a b0 -b ‘Some Text’
Где b0 – это имя кнопки на экране, а Some Text – желаемый текст.
Пример использования в движке правил WB6:
var txt1 = "some text";
runShellCommand("bash /mnt/data/root/text.sh -a b0 -b '" + txt1+ "'");
Подобным образом можно изменять отображаемую на данный момент страницу на экране, текст на различных элементах, переменные и другие параметры. Не буду расписывать тут весь функционал экранного модуля. Для нашего проекта вполне достаточно уже написанной выше информации.
Монетоприемник
В качестве монетоприемника я использую китайский вариант, найденный на просторах Aliexpress рублей за 400.
С подключением всё просто. +12v и импульсный выход, который нужно подключить к модулю расширения «Сухой контакт» WBE2-DI-DR-3. Далее для определения монетки нам необходимо всего лишь посчитать импульсы. Для этого создадим в движке правил новый скрипт:
var timeout_ms = 1 * 200;
var timer_id = null;
var timerTime = Date.now();
var timerLast = 0;
var countMoney = 0;
defineRule("money", {
whenChanged: "wb-gpio/MOD1_IN2",
then: function(newValue, devName, cellName) {
if (newValue) {
timerLast = Date.now()
if ((timerLast - timerTime) >= 500) { //первый импульс?
timerTime = Date.now();
countMoney = 1;
} else {
timerTime = Date.now(); // второй и последующие импульсы
countMoney++;
}
if (timer_id) {
clearTimeout(timer_id);
}
timer_id = setTimeout(function() { // через определенное время смотрим что получилось
if (countMoney == 5) {
log("10 рублей")
} else if (countMoney == 4) {
log("5 рублей")
} else if (countMoney == 3) {
log("2 рубля")
} else if (countMoney == 2) {
log("1 рубль")
}
timer_id = null;
}, timeout_ms);
}
}
});
Довольно простое правило. Возможно не самое точное.
Bitrix24
Теперь наверное самое интересное. Попробуем разобраться, как воплотить фразу «Молодец. Возьми с полки
![Нажмите для увеличения изображения](https://habrastorage.org/webt/m3/tt/yk/m3ttykeum6-qm1pjvbqxa2golh0.png)
За основу мы возьмём bitrix24-python-sdk.
Устанавливаем SDK на контроллер командой
pip install bitrix24-python-sdk
Далее создадим python скрипт в корневой папке, назовём его bitrix.py:
#!/usr/bin/python3
import pprint
from bitrix24 import bitrix24
import sys
import subprocess
b24_webhook_key = 'some_key' #Сюда вписать свой ключ, созданный в админке битрикса.
b24_webhook_user = 1 #Вписать ID пользователя - того кто создал ключ.
b24_domain = 'some_domain' #Вписать домен компании.
page = 1
user_id = [16, 10, 6, 55, 8, 14, 67, 20] # список пользователей, за которыми собираемся следить
ord_status = 5 # 5 - завершенные задачи
order_list = 0
total_orders = 0
bx24 = bitrix24.Bitrix24(b24_domain, webhook_key=b24_webhook_key, webhook_user=b24_webhook_user)
def get_order_list_len(_user_id):
result_ = bx24.call(
'task.item.list',
{'ORDER': {'DEADLINE': 'desc'}},
{'FILTER': {'RESPONSIBLE_ID': _user_id,'REAL_STATUS': ord_status}},
{'PARAMS': {'NAV_PARAMS': {'nPageSize': 50, 'iNumPage': page}}}
)
if result_:
return (len(result_['result']))
for user in user_id:
while True:
order_list = get_order_list_len(user)
total_orders = total_orders + order_list
page = page + 1
if(order_list < 50):
subprocess.call("mosquitto_pub -t /devices/%s/controls/orders_finished/on -r -m %s" % (user, total_orders), shell=True) # Отправляем всё в MQTT.
page = 1
total_orders = 0
break
Данный скрипт запрашивает все завершенные задачи заданного списка юзеров, считает их и посылает количество в mqtt топики. Далее нам остаётся только сравнить полученное количество с предыдущими значениями и если новые данные больше, то добавить баллы сотруднику за проделанную работу.
Bluetooth
Для сканирования BLE устройств я использую Bluez. Программа уже установлена на контроллер. Для того чтобы сканировать ble устройства достаточно ввести несколько команд в консоль:
hciconfig hci0 up
hcitool lescan --duplicates
Однако нам этого не достаточно. Желательно видеть уровень сигнала. Для этого установим дополнительную утилиту hcidump:
apt-get install bluez-hcidump
И дополним нашу команду:
hciconfig hci0 up
hcitool lescan --duplicates | hcidump -a | grep -E 'bdaddr|RSSI'
На выходе получим MAC адреса и RSSI bluetooth (ble) устройств поблизости.
![Нажмите для увеличения изображения](https://habrastorage.org/webt/eg/st/ar/egstarnarzrsym0nfutrfwhawhc.png)
Эти две строки можно красиво обернуть в bash скрипт, а вывод отправлять в MQTT.
Второй способ- скрипт aioblescan
Для его работы требуется Python 3, желательно 3.5. Плюс этого скрипта в том, то что можно следить за конкретными MAC адресами, а не за всеми мимо проходящими за окном умными часами и смартфонами. Вывод скрипта так же перенаправляем в MQTT, для удобства работы с движком правил.
![Нажмите для увеличения изображения](https://habrastorage.org/webt/co/bk/cl/cobkcltejo3kbwvhpk-rduxlgxk.png)
Теперь мы знаем как взаимодействовать с WB6 при помощи экранного модуля, умеем считать монеты, следить за успехами сотрудников в Битриксе, сканировать ble устройства и их уровень сигнала. Осталось собрать это в одну кучу и прописать какую-то логику. Я не буду расписывать все скрипты данного проекта. Всё будет доступно по ссылке в свободном доступе. Возможно кто-то из вас найдёт для этого всего более полезное и умное применение.
Корпус
Корпус сделан из 10mm вспененного ПВХ. Продаётся такой пластик листами. Умеренно мягкий. Резать можно ножовкой, либо обычным канцелярским ножом по линейке.
![Нажмите для увеличения изображения](https://habrastorage.org/webt/jx/nc/7e/jxnc7ef0lwmeow3j4fhlyz4--gs.jpeg)
В качестве крепежа использовался детский набор металлических деталей «Конструктор» и короткие саморезы. В этом наборе нашлось практически всё необходимое для сборки корпуса.
![Нажмите для увеличения изображения](https://habrastorage.org/webt/h0/5m/jl/h05mjlqrtr82bk4krmbjkeyetzq.jpeg)
Привод
Спираль и привод были куплены в интернет магазине Б\У зап. частей для вендинговых аппаратов. Работает от блока питания 24v. Коммутируется через реле модуля расширения WBE2-DO-R6C-1.
![Нажмите для увеличения изображения](https://habrastorage.org/webt/es/fa/ar/esfaaryqjf8rslpblwesr3fcrgm.png)
![Нажмите для увеличения изображения](https://habrastorage.org/webt/be/oa/kt/beoaktz2glv7ksdvod0n4ehkn5e.png)
![Нажмите для увеличения изображения](https://habrastorage.org/webt/v7/bn/0p/v7bn0pxipe6w-bi328ugegtuh3e.png)
Функционал
Функционально похож на обычный вендинговый аппарат. Выдаёт печенье за деньги и не только.
![](https://habrastorage.org/webt/oy/kw/vh/oykwvhvrnx0sscx1kktd6fhcej8.gif)
При запуске аппарата экран показывает заставку с логотипом, за тем перекидывает на главный экран, где нам предлагается либо купить печенье, либо сыграть в игру.
Игра для пользователя — бесплатная, но можно установить небольшую цену, для мотивации. Правила просты. Игроку предлагают решить три простые задачки. Если он дает три правильных ответа, то получает приз в виде печенья. Пожалуй это всё что нужно знать об этом пункте. Давайте вернёмся в главное меню и нажмём «Купить печенье».
Далее система предложит выбрать способ оплаты. При выборе способа оплаты «За деньги» на экране появляется окно с ценой и оплаченной суммой, которая увеличивается при внесении денег в монетоприемник.
Как только сумма будет достаточна для совершения покупки, аппарат автоматически закроет это окно и выдаст товар.
Но, как я рассказывал ранее, предусмотрена и другая валюта – это баллы, полученные за успехи сотрудников в системе Bitrix24. Если сотрудник закрывает поставленную ему задачу – ему начисляются баллы. Также баллы может начислить начальник за особенно хорошие результаты.
![Нажмите для увеличения изображения](https://habrastorage.org/webt/cm/ng/ox/cmngoxpzdzbmt3hxh9xc9l9whac.png)
![Нажмите для увеличения изображения](https://habrastorage.org/webt/p6/bw/yr/p6bwyraqciiu2v0tu371qypzfxq.png)
![Нажмите для увеличения изображения](https://habrastorage.org/webt/kk/df/7d/kkdf7dnz548knhk5p17_21zj6nu.png)
![Нажмите для увеличения изображения](https://habrastorage.org/webt/wg/9w/cl/wg9wcltq4yk9ogjh-jik8kt0ogc.png)
Чтобы расплатиться баллами, сотруднику нужно выбрать пункт «Купить за баллы», Затем аппарат попытается определить кто перед ним стоит. Для этого он сканирует Bluetooth устройства вокруг себя и если он видит MAC адрес устройства принадлежащего сотруднику и занесенного в базу данных, а также уровень сигнала достаточно мощный, что бы полагать, что он стоит рядом, то на экране появятся данные о состоянии счета сотрудника, его имя и предложение оплатить печенье баллами.
Если же системе не удалось определить ни одного знакомого устройства, то он выдаст предупреждение и предложит выбрать своё имя из списка и затем ввести свой пин-код.
В случае верного ввода откроется то самое окно с данными о состоянии счета пользователя и стоимости продукта. При подтверждении покупки со счета пользователя списывается необходимая сумма баллов и аппарат выдает товар.
На этом всё. Данный проект создан исключительно в образовательных целях, и чтобы показать, что области применения контроллера Wiren Board ограничены лишь фантазией. Все файлы и скрипты можно найти по ссылке.
Комментарии (19)
GeMir
23.08.2018 10:45если он видит MAC адрес устройства принадлежащего сотруднику
Поощрение краж устройств у коллег?
Killer feature осталась не реализованной :(
…denis-19
23.08.2018 14:34Кукинатор 3000
Почему 3000 ??
Сколько пачек печенек помещается в корпус?
Есть счетчик окончания пачек и сигнализирование типа «пустой контейнер скоро» будет?
Что туда еще можно положить более интересное не думали, может шоколадки или зефирки?berezuev
23.08.2018 16:11«пустой контейнер скоро» будет
Эта фраза в уме читается голосом магистра Йоды :)
GeMir
23.08.2018 14:49Почему 3000
Шутка. Отсылка к Pip-Boy 2000 и схожим названиям «инновационных» и «незаменимых» продуктов.
Сколько пачек печенек помещается в корпус?
Судя по спирали на фото — 6.
snuk182
24.08.2018 10:06mukoladerevlo
24.08.2018 10:35надо бы еще смотровое окошко для печенек сделать) аппарат не знает о наличии печенья, неохота тратить деньги/баллы впустую. Аппарат прикольный, конструктор зачет)
MahmudS
24.08.2018 10:35А почему 6/2*(2+1) не дает 9? Ведь операции умножения/деления равноценны, и идут по порядку?
p_fox
24.08.2018 18:01Наверно потому что уравнение записано неверно.
Видя 6/2**(2+1) мы по-умолчанию читаем это как (6/2)*(2+1), а на самом деле там 6/(2*(2+1)).
Либо надо рисовать нормальную вертикальную дробь, либо ставить недостающие скобки.
igormwd
24.08.2018 10:35} else if (countMoney == 2) { log("1 рубль") }
Т.е. получается если накидать 10ть десятикопеечных (или вообще копеечных) монеток — можно купить печеньку?Munrexio Автор
24.08.2018 10:37Нет, считаются не монетки, а импульсы, которые выдаёт монетоприёмник.
Монетоприёмник сам определяет номинал монеты. И в зависимости от номинала выдаёт определённое количество импульсов контроллеру. Контроллер их считает и переводит в рубли.
Sly_tom_cat
А почему контроллер то в корпус не спрятали?
Крайне странное решение — все прикрыть а контроллер вывесить наружу.
Нет, для отладки — понятно — нужно, но в законченном виде это планируется внутрь корпуса убрать или нет?
alexey-m-ukolov
На контроллере и в названии компании здесь на Хабре используются одни и те же буквы в том же порядке. Полагаю, это и есть ответ на ваш вопрос.
Gryphon88
Возможно, так они выявляют толковых сотрудников, которые с помощью кнопки и пары перемычек не будут играть ни в какие игры или тратить деньги/баллы, а подменять сигнал с монетоприёмника или напрямки командовать двигателем.
Sly_tom_cat
А что там искать:
Коммутируй привод прямо на блок и только успевай печеньки ловить.
fizikdaos
Еще можно крышку поднять и все печенья забрать.
А вообще можно как тест на собеседовании давать и смотреть каким способом взламывать будут :))