Машина отзывов на Omega 2

Приветствую всех! Главная идея этой статьи, пошагово продемонстрировать создание устройства для сбора отзывов на миникомпьютере с 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!