Приветствую всех! Главная идея этой статьи, пошагово продемонстрировать создание устройства для сбора отзывов на миникомпьютере с linux. Кратко я рассказывал и показывал об этом устройстве вот в этом ролике:
В этой статье вы сможете найти файлы для повторения конструкции. Главное условие моего ТЗ было использование миникомпьютера Omega2 от Onion. Хотя по факту, вы можете использовать любой миникомпьютер с linux на борту (Raspberry, orange, banana…) без кардинальных изменений в изложенных ниже файлах.
Начать логичней всего с эскиза. Заказчик всегда прав, а он попросил меня сделать устройство с сенсорным экраном, на котором крутится интерфейс оставления отзыва. Мне сразу вспомнились терминалы из fallout 3, но мне захотелось показать все внутренности, поэтому родилась следующая конструкция. Так как у Omega 2 нет видео интерфейса, то я решил использовать свой экран Nextion максимально размера из возможных. Плюс у него ещё и емкостной тачпад, что гораздо лучше резистивного.
Чтобы не растягивать статью я привел стадии работы над корпусом на одной картинке. Изначально я набросал макет в SketchUp, потом перешел к картонному моделированию. Так как толщина гофрированного картона около 4 мм мне удалось максимально приблизить осязание корпуса к итоговому виду. Я понял, что корпус получается слишком громоздкий и были проведены работы по упрощению конструкции. В конечном счете просчитался с методикой соединения оргстекла. Лучше использовать не простые пазы, а соединение типа шип-паз. Конструкция была бы более жесткой и склеилась бы проще. Склеивал герметиком, чтобы соединение было гибким и корпус пережил транспортировку до заказчика.
Но тем не менее я получил корпус, в который смонтировал миникомпьютер Omega 2, i2s карточку для звука (тут рассказывал про неё: https://habr.com/ru/post/413623/), конвертер питания на 3.3 вольта, блок питания на 5 вольт и усилитель с динамиком. На лицевую панель встал сам экран Nextion. Ниже получившиеся схема (да, я немного наркоман креативный и мне нравиться рисовать схемы карандашом)
Соединяем Rx и Tx выводы дисплея c Rx1 и Tx1 Omega 2. Подключаем выводы SDO, WD, CLK к I2S аудио карте. И соединяем все провода питания.
Конвертер питания на базе микросхемы LM2596, можно заменить любым с током больше ампера. Усилитель обычный D класс на микросхема PAM8403. 5 вольтовый блок питания требуется от двух ампер и выше, я взял сильно с запасом, чтобы он сильно не грелся.
На этом прелюдия была закончена и можно было приступать к настройке миникомпьютера и написании скриптов.
Настройка Omega2
У меня была идея — Omega 2 будет подключена к wifi роутеру и иметь через него доступ в интернет, но в тот wifi который она раздает этот интернет не должен просачиваться. Для этого я отключил маскардинг в настройках файрвола. Открываем файл настроек:
vi /etc/config/firewall
Далее в файле находим строчку option masq ‘1’ и 1 меняется на 0.
(не забываем, чтобы сохранить изменения и выйти из vim надо ввести :wq)
Теперь с помощью утилитки passwd меняем пароль root’a, чтобы никто из посетителей не залез в настройки и все нам не сломал. Ну и конечно меняем пароль на точку доступа омеги в файле /etc/config/wireless (стандартный пароль 12345678).
После чего нам необходимо установить все пакеты. Подключаемся к wifi с помощью wifisetup и обновляемся:
opkg update
Потом устанавливаем:
opkg install python python-pip python-pyserial php7 php7-cgi php7-mod-sqlite3 python-sqlite3
Я начал с веб версии сайта. Набросал в силу моих скромных познаний простенькую форму и обработчик, который всё это запихивает в базу данных sqlite. Получившийся пример веб морды лежит в архиве проекта. Там вообще никакой магии, открыли файл базы данных и запихнули значения из всех полей, закрыли базу и запустили аудиофайл (спасибо за ваш отзыв и так далее). Я выбрал sqlite по причине легкости и универсальности решения, базу устанавливать и разворачивать не надо и библиотеки для работы есть под все, ну или наверно почти под все.
Интерфейс Nextion
Прежде чем душить питона писать питоновский скрипт, нужно было написать интерфейс для экрана. Экран у нас умный. Может всякое, кроме нормальных шрифтов! Сколько уж времени прошло! Кто-то даже реверс-инжинирит формат шрифтов: https://github.com/hagronnestad/nextion-font-editor
Проект интерфейса я так же закинул в архив статьи. Делаем страничку приветствия, страничку ввода оценки и ввод отзыва. Где мог, надписи сделал изображениями, чтобы глаза не рвало на части. Ну и при действиях ввода экран отправляет в UART всякое, что потом можно распарсить на стороне Omega 2. Компилируем прошивку и скидываем по сети (winSCP использую я).
Прошивка проходить без особых проблем с помощью вот этого скрипта: https://github.com/omerbeg/nextionupload/blob/master/nextionupload.py
Необходимо только правильно подправить адрес интерфейса: ‘/dev/ttyS1’ и модель экрана ‘NX8048K070’ После чего можно скармливать скрипту прошивку python nextionupload.py proshibka.tft и наблюдать процесс загрузки.
Скрипт парсера
Скрипт для анализирования данных, которые приходят со второго UART’а Omega 2, непрерывно смотрит данные с порта и сравнивает его с заданной последовательностью (у меня начало отправления пакетов «<be>», последовательность записал в кавычки). Далее скрипт подразумевает определенные данные и ловит их при помощи нескольких циклов for. В конце, если приходит «<res>», то значит записываем все в базу и воспроизводим аудиофайл. И я добавил параллельным процессом таймер, который отвечает за сброс режима ввода данных, если пользователь начал что-то заполнять и ему стало лень. Пройдет 120 секунд и скрипт сбросит интерфейс на главную страницу page main и перейдет в режим ожидания. Приведу полный листинг, пожалуй.
import serial import signal import subprocess import time import sqlite3 as lite import os import pty port = serial.Serial("/dev/ttyS1",9600) master, slave = os.openpty() def signal_handler(signum, frame): port.write('page main') port.write('xFF') port.write('xFF') port.write('xFF') raise Exception("Error time!") def paket_start(): paketik = [] paketik_2 = [] for i in range(3): y = port.read(2) signal.alarm(0) signal.alarm(30) paketik.append(y[1]) print(y[1]) for i in range(3): y = port.read(3) signal.alarm(0) signal.alarm(30) paketik.append(y[2]) print(y[2]) for i in range(4): stroka = "" print("ya tut") y = port.read(2) signal.alarm(0) signal.alarm(30) flag = 0 while flag == 0: y = port.read(1) stroka += y print("jopa") if y == "<": y = port.read(1) if y == "s": y = port.read(1) if y == "t": y = port.read(1) if y == ">": flag =1 stroka = stroka[0:-1] stroka = stroka.decode('koi8-r') paketik_2.append(stroka) y = port.read(5) print(paketik) print(paketik_2) signal.alarm(0) if y == "<res>": p = subprocess.Popen("mpg123 -f 32 -C /root/audio.mp3", stdin=master, shell=True) print("end") conn = lite.connect('/www/php/temp.sqlite') print("end") cursor = conn.cursor() print("end") cursor.execute('INSERT INTO review ( "reviewAll", "reviewSize", "reviewPrice", "reviewService", "reviewTime" , "reviewPlace", "whatdislike", "whatlike", "whatelse", "advice","email") VALUES (?,?,?,?,?,?,?,?,?,?,?)', (int(paketik[0]), int(paketik[1]), int(paketik[2]), int(paketik[3]), int(paketik[4]), int(paketik[5]), paketik_2[0], paketik_2[1], paketik_2[2], paketik_2[3],'nextion')) print("end") conn.commit() print("end") conn.close() print(y) signal.signal(signal.SIGALRM, signal_handler) while True: try: signal.alarm(120) print("popa") x = 0 x = port.read(1) if x == "<": x = port.read(1) if x == "b": x = port.read(1) if x == "e": x = port.read(1) if x == ">": paket_start() except lite.Error as e: print("Database error: %s" % e) except: print("Error!") time.sleep(0.5)
Теперь мы умеем собирать отзывы и записывать их в базу данных, но надо же её куда-то выгружать? Для этого я использовал вот этот скрипт: https://github.com/kshcherban/gdrive_uploader Регистрируете аккаунт, получаете данные для приложения и скрипт может загружать файлы на google диск. Работу с этими вещами разбирал вот в этом ролике: https://youtu.be/34uwOyyiYCM, когда делал timelapse machine (кажется я зациклился на создании машин…) 15 Гб дискового пространства должно хватить с запасом, на несколько лет.
Ну и теперь автозапуск. В rc.local я вписал строчку для активации i2s пинов при включении:
omega2-ctrl gpiomux set i2s i2s
Основные скрипты запускаются cron’ом:
0 18 * * * python /root/base_upload.py /www/php/base.sqlite
*/1 * * * * /root/auto.sh
Первое задание запускает скрипт загрузки базы на google диск в 18:00 каждый день. Второе задание каждую минуту запускает sh скрипт
# !/bin/bash ps | grep "python /root/start.py" | grep -v grep || python /root/start.py
Здесь выполняется поиск процесса (утилитой ps) со скриптом, который парсит UART данные, и если он запущен, то ничего не происходит, но если такого процесса нет, то он запускается. Вещь очень удобная, советую всем, что имеет дело с linux’овыми миникомпами сохранить этот простенькую конструкцию.
Надеюсь изложенная мной информация была вам полезна и интересна. Задавайте ваши вопросы в комментариях, буду отвечать по возможности. 73!