Всем привет! Я работаю в компании, которая разрабатывает оборудование для мониторинга нефтяных вышек, майнинговых ферм, ДГУ и прочих скучных вещей. Но в этот раз мы автоматизировали кое-что иное. Итак, встречайте: «Кукинатор 3000» — аппарат по выдаче печенья лучшим сотрудникам.
Аппарат представляет из себя конструкцию из ПВХ и некоторых деталей:
Спираль c приводом (служит для подачи товара), экран, монетоприемник, и мозг всей конструкции — контроллер Wiren Board 6.
Экран
В качестве экрана для WB6 я использую экранный модуль Nextion (NEXTION ENHANCED NX4832K035).
Модуль оснащён AVR-процессором. Имеет 3.5'' TFT дисплей с разрешением 480 * 320, 32Mb встроенной памяти, 1024 byte EEPROM, 8192 byte RAM.
Данное устройство хранит и отображает графику, анимирует элементы интерфейса и обрабатывает нажатия. На контроллер по UART передаются только коды событий, что позволяет легко настраивать интерфейсы различной сложности.
Для загрузки необходимых настроек и графики в экранный модуль предусмотрен визуальный редактор интерфейсов «Nextion Editor». Скачать его можно на официальном сайте itead.
При создании нового проекта необходимо указать модель, положение экрана и кодировку символов. Для корректного отображения кириллицы нужно выбрать iso-8859-5.
Слева расположена панель инструментов. Здесь можно найти самые необходимые элементы для создания интерфейса.
Давайте нажмем на Button. В поле редактирования появится условная кнопка. Растянем её, отредактируем текст и название. Для отображения текста необходимо добавить шрифт. Воспользуемся инструментом «Font Creator» во вкладке tools. В поле «Event» необходимо написать код, который будет воспроизводиться по событиям.
На пример если во вкладке «Touch press event» прописать print «button pressed\r», то при нажатии на кнопку устройство пошлет в UART: button pressed и символ переноса строки.
Описание и примеры кодов можно найти в документации к экрану. Для отладки можно воспользоваться функцией Debug. Данное окно позволяет протестировать все функции прошивки до заливки в сам экран.
Если всё в порядке, то можно подключить дисплей к компьютеру через переходник USB/UART и залить прошивку, нажав на кнопку «Upload».
Но как же нам теперь подружить экран с WB6?
Подключить экран можно либо через тот же переходник USB\UART в один из портов USB на контроллере, либо на прямую к одному из слотов расширения на плате контроллера.
Нас интересуют пины TX, RX, 5V и GND.
После подключения экрана к контроллеру мы будем получать данные о событиях прямо в 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
Теперь наверное самое интересное. Попробуем разобраться, как воплотить фразу «Молодец. Возьми с полки
За основу мы возьмём 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) устройств поблизости.
Эти две строки можно красиво обернуть в bash скрипт, а вывод отправлять в MQTT.
Второй способ- скрипт aioblescan
Для его работы требуется Python 3, желательно 3.5. Плюс этого скрипта в том, то что можно следить за конкретными MAC адресами, а не за всеми мимо проходящими за окном умными часами и смартфонами. Вывод скрипта так же перенаправляем в MQTT, для удобства работы с движком правил.
Теперь мы знаем как взаимодействовать с WB6 при помощи экранного модуля, умеем считать монеты, следить за успехами сотрудников в Битриксе, сканировать ble устройства и их уровень сигнала. Осталось собрать это в одну кучу и прописать какую-то логику. Я не буду расписывать все скрипты данного проекта. Всё будет доступно по ссылке в свободном доступе. Возможно кто-то из вас найдёт для этого всего более полезное и умное применение.
Корпус
Корпус сделан из 10mm вспененного ПВХ. Продаётся такой пластик листами. Умеренно мягкий. Резать можно ножовкой, либо обычным канцелярским ножом по линейке.
В качестве крепежа использовался детский набор металлических деталей «Конструктор» и короткие саморезы. В этом наборе нашлось практически всё необходимое для сборки корпуса.
Привод
Спираль и привод были куплены в интернет магазине Б\У зап. частей для вендинговых аппаратов. Работает от блока питания 24v. Коммутируется через реле модуля расширения WBE2-DO-R6C-1.
Функционал
Функционально похож на обычный вендинговый аппарат. Выдаёт печенье за деньги и не только.
При запуске аппарата экран показывает заставку с логотипом, за тем перекидывает на главный экран, где нам предлагается либо купить печенье, либо сыграть в игру.
Игра для пользователя — бесплатная, но можно установить небольшую цену, для мотивации. Правила просты. Игроку предлагают решить три простые задачки. Если он дает три правильных ответа, то получает приз в виде печенья. Пожалуй это всё что нужно знать об этом пункте. Давайте вернёмся в главное меню и нажмём «Купить печенье».
Далее система предложит выбрать способ оплаты. При выборе способа оплаты «За деньги» на экране появляется окно с ценой и оплаченной суммой, которая увеличивается при внесении денег в монетоприемник.
Как только сумма будет достаточна для совершения покупки, аппарат автоматически закроет это окно и выдаст товар.
Но, как я рассказывал ранее, предусмотрена и другая валюта – это баллы, полученные за успехи сотрудников в системе Bitrix24. Если сотрудник закрывает поставленную ему задачу – ему начисляются баллы. Также баллы может начислить начальник за особенно хорошие результаты.
Чтобы расплатиться баллами, сотруднику нужно выбрать пункт «Купить за баллы», Затем аппарат попытается определить кто перед ним стоит. Для этого он сканирует 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
Еще можно крышку поднять и все печенья забрать.
А вообще можно как тест на собеседовании давать и смотреть каким способом взламывать будут :))