Изначально, я хотел написать обучающую статью по экрану Nextion, но потом понял, что этого добра навалом в Интернете и желание само собой улетучилось. Вдохновленный одним известным в наших кругах человеком, я решил собрать себе устройство с мониторингом событий на своем youtube канале. Для этого я решил собрать комплект из миникомпьютера Omega 2 и экрана Nextion. В дальнейшем, я смогу изменить задачу этой конструкции, а сейчас душа требует свершений. Я настолько вдохновился, что и ролик сделал, внизу к статье его прикрепил. Приступаем к работе.
Давайте сразу приведу схематичный вид устройства.
Все просто. К самой омеге подпаиваем USB разъем для флешки и выход со второго UART'а. Блок питания 5 В, для получения 3.3 В ставим конвертор напряжения. Я использовал LM317, но это не лучший вариант, лучше - LM2596 или MP2307. К экрану Nextion подпаиваем 5 В.
Далее я буду делать с омегой всякое. Если вы только начинаете разбираться с микрокомпьютерами, то советую посмотреть мои предыдущие видео по омеге:
Подключаем к Omega 2 датчик температуры и влажности HIH6130 и работаем с облаком Azure: https://youtu.be/kabWcZriVCE
Честный обзор миникомпьютера OMEGA 2: https://youtu.be/7MDKTXhi5lI
Теперь надо подключить к Omega 2 ненужную флешку, чтобы увеличить на 5 см объем памяти для установки различных пакетов. Как это сделать очень просто рассказано по следующим ссылкам:
1. Монтирование флешки: https://goo.gl/eqoKZM
2. Создание swap файла для распаковки некоторых пакетов: https://goo.gl/C69tVD
Swap файл увеличит объем оперативной памяти omega для распаковки некоторых пакетов при установке (например http модуль python'а не установиться без swap файла - проверено).
Теперь будем устанавливать все приложения! В данном моменте я уже считаю, что вы включили омегу, подключились к ней по SSH (пароль - onioneer) и к вашей wifi точке доступа с помощью команды wifisetup. Для начала надо обновить информацию о пакетах:
opkg update
Далее устанавливаем MC (файловый менеджер), NANO (текстовый редактор), PYTHON и PIP:
opkg install mc nano python python-pip
Далее поставим пакет для общения через UART в питоне:
opkg install python-serial
и кучу всяких библиотек которые нам понадобятся:
pip install google-auth google-auth-oauthlib google-auth-httplib2 requests httplib2
Теперь открываем Nextion Editor. И тут опять же есть, что посмотреть по экранам Nextion, чтобы набраться знаний:
Что могут дисплеи Nextion? Обзор возможностей, примеры использования: https://youtu.be/gNqT4LRnNC4
Графический замок на дисплее Nextion HMI: https://youtu.be/p-PbTNAlY8c
Нам необходимо реализовать 2 странички. На первой будет выводиться информация по вашему youtube каналу, на второй будет выводиться время, дата и погода в вашем любимом городе. Я решил выводить следующую информацию: общее количество подписчиков, прирост подписчиков за час, последние пять комментариев на канале. Для этого надо поставить два числовых поля и 5 текстовых. При нажатии на любую часть экрана интерфейс должен переходить на следующую страничку сразу реализуем это, добавив в события Press Event всех элементов на странице следующий код:
page time
На страничке со временем ставим два числовых поля, в которых будут выводиться часы и минуты и текстовые поля двоеточия, температуры, даты и состояния погоды. И так же добавляем код перехода на другую страницу по нажатию.
Теперь пора писать супер код на питоне, скачиваете архив из описания статьи и открываете там два файла minutes.py и hours.py. Начнем со скрипта, который будет запускаться каждую минуту.
#!/usr/bin/python # -*- coding: utf-8 -*- import httplib2 import os import sys import serial import time from datetime import datetime import requests from apiclient.discovery import build from apiclient.errors import HttpError from oauth2client.client import flow_from_clientsecrets from oauth2client.file import Storage from oauth2client.tools import argparser, run_flow # The CLIENT_SECRETS_FILE variable specifies the name of a file that contains # the OAuth 2.0 information for this application, including its client_id and # client_secret. CLIENT_SECRETS_FILE = "client_secrets.json" # This OAuth 2.0 access scope allows for full read/write access to the # authenticated user's account and requires requests to use an SSL connection. YOUTUBE_READ_WRITE_SSL_SCOPE = "https://www.googleapis.com/auth/youtube.force-ssl" API_SERVICE_NAME = "youtube" API_VERSION = "v3" # This variable defines a message to display if the CLIENT_SECRETS_FILE is # missing. MISSING_CLIENT_SECRETS_MESSAGE = "WARNING: Please configure OAuth 2.0" # Authorize the request and store authorization credentials. def get_authenticated_service(args): flow = flow_from_clientsecrets(CLIENT_SECRETS_FILE, scope=YOUTUBE_READ_WRITE_SSL_SCOPE, message=MISSING_CLIENT_SECRETS_MESSAGE) storage = Storage("youtube-api-snippets-oauth2.json") credentials = storage.get() if credentials is None or credentials.invalid: credentials = run_flow(flow, storage, args) # Trusted testers can download this discovery document from the developers page # and it should be in the same directory with the code. return build(API_SERVICE_NAME, API_VERSION, http=credentials.authorize(httplib2.Http())) args = argparser.parse_args() service = get_authenticated_service(args) def print_results(results): print(results) # Build a resource based on a list of properties given as key-value pairs. # Leave properties with empty values out of the inserted resource. def build_resource(properties): resource = {} for p in properties: # Given a key like "snippet.title", split into "snippet" and "title", where # "snippet" will be an object and "title" will be a property in that object. prop_array = p.split('.') ref = resource for pa in range(0, len(prop_array)): is_array = False key = prop_array[pa] # Convert a name like "snippet.tags[]" to snippet.tags, but handle # the value as an array. if key[-2:] == '[]': key = key[0:len(key)-2:] is_array = True if pa == (len(prop_array) - 1): # Leave properties without values out of inserted resource. if properties[p]: if is_array: ref[key] = properties[p].split(',') else: ref[key] = properties[p] elif key not in ref: # For example, the property is "snippet.title", but the resource does # not yet have a "snippet" object. Create the snippet object here. # Setting "ref = ref[key]" means that in the next time through the # "for pa in range ..." loop, we will be setting a property in the # resource's "snippet" object. ref[key] = {} ref = ref[key] else: # For example, the property is "snippet.description", and the resource # already has a "snippet" object. ref = ref[key] return resource # Remove keyword arguments that are not set def remove_empty_kwargs(**kwargs): good_kwargs = {} if kwargs is not None: for key, value in kwargs.iteritems(): if value: good_kwargs[key] = value return good_kwargs print("Start.../n") #open UART ser = serial.Serial('/dev/ttyS1',9600) print(ser.name) #get weather def weather(): s_city = "Irkutsk,RU" city_id = 2023469 appid = "ваш id с сайта погоды" try: res = requests.get("http://api.openweathermap.org/data/2.5/weather", params={'id': city_id, 'units': 'metric', 'lang': 'ru', 'APPID': appid}) data = res.json() # print "conditions: ", data['weather'][0]['description'] ser.write('condition.txt="'+data['weather'][0]['description'].encode('koi8-r')+'"') ser.write('\xFF') ser.write('\xFF') ser.write('\xFF') # print datetime.fromtimestamp(int(data['dt'])) # print "Temp:", data['main']['temp'] ser.write('grad.txt="'+str(data['main']['temp'])+'"') ser.write('\xFF') ser.write('\xFF') ser.write('\xFF') # print "davlenie", data['main']['pressure'] # print "Vlajnost", data['main']['humidity'] except Exception as e: print("Exception (weather):", e) pass #get trend of subcribes def send_fucking_number(number): file=open("save_subs.txt") save_subs_str=file.read() file.close() old_number=int(save_subs_str) print old_number ser.write('dsub.val='+str(number-old_number)) ser.write('\xFF') ser.write('\xFF') ser.write('\xFF') #get count of subs def channels_list_by_id(service, **kwargs): kwargs = remove_empty_kwargs(**kwargs) # See full sample for function results = service.channels().list(**kwargs).execute() subs_count=int(results['items'][0]['statistics']['subscriberCount']) ser.write('sub_count.val=') ser.write(str(subs_count)) ser.write('\xFF') ser.write('\xFF') ser.write('\xFF') send_fucking_number(subs_count) #get of list of comments def comment_threads_list_all_threads_by_channel_id(service, **kwargs): kwargs = remove_empty_kwargs(**kwargs) # See full sample for function results = service.commentThreads().list( **kwargs ).execute() #print_results(results['items'][0]['snippet']['topLevelComment']['snippet']['textOriginal']) ser.write('t0.txt="'+results['items'][0]['snippet']['topLevelComment']['snippet']['textOriginal'][0:127].encode('koi8-r').replace('"', '$')+'"') ser.write('\xFF') ser.write('\xFF') ser.write('\xFF') # print_results(results['items'][1]['snippet']['topLevelComment']['snippet']['textOriginal']) ser.write('t1.txt="'+results['items'][1]['snippet']['topLevelComment']['snippet']['textOriginal'][0:127].encode('koi8-r').replace('"', '$')+'"') ser.write('\xFF') ser.write('\xFF') ser.write('\xFF') # print_results(results['items'][2]['snippet']['topLevelComment']['snippet']['textOriginal']) ser.write('t2.txt="'+results['items'][2]['snippet']['topLevelComment']['snippet']['textOriginal'][0:127].encode('koi8-r').replace('"', '$')+'"') ser.write('\xFF') ser.write('\xFF') ser.write('\xFF') # print_results(results['items'][3]['snippet']['topLevelComment']['snippet']['textOriginal']) ser.write('t3.txt="'+results['items'][3]['snippet']['topLevelComment']['snippet']['textOriginal'][0:127].encode('koi8-r').replace('"', '$')+'"') ser.write('\xFF') ser.write('\xFF') ser.write('\xFF') # print_results(results['items'][4]['snippet']['topLevelComment']['snippet']['textOriginal']) ser.write('t4.txt="'+results['items'][4]['snippet']['topLevelComment']['snippet']['textOriginal'][0:127].encode('koi8-r').replace('"', '$')+'"') ser.write('\xFF') ser.write('\xFF') ser.write('\xFF') subs_count=0 channels_list_by_id(service, part='snippet,contentDetails,statistics', id='ваш id') comment_threads_list_all_threads_by_channel_id(service, part='snippet,replies', allThreadsRelatedToChannelId='ваш id') weather()
Первая часть скрипта была нагло скопирована с примеров работы с youtube API на github'е: https://github.com/youtube/api-samples/tree/master/python
Там можно подсмотреть и работу с другими функциями сайта.
Функция ser.write() отправляет в uart данные. Чтобы вывести в числовое поле экрана значение мы должны отправить команду следующего вида:
n0.val=X0xFF0xFF0xFF
где n0 имя числового поля, X значение, которое мы хотим отправить.
Для текстового поля:
t0.txt="строка"0xFF0xFF0xFF
где t0 имя текстового поля.
Любая команда завершается тремя байтами FF, поэтому при отправке комментариев, я заменяю в них двойные кавычки на знак доллара, чтобы не было ошибок.
Собственно, чтобы у вас заработали эти скрипты вы должны получить youtube key: http://code.google.com/apis/youtube/dashboard/
и пройти регистрацию на сайте погоды и получить там key: https://home.openweathermap.org/users/sign_up
Так же, в архиве я прикрепил скрипт search_city.py. Откройте его и вставьте ваш key с сайта погода и впишите свой город, чтобы узнать его идентификатор. Этот идентификатор, потом нужно скопировать в функцию weather в переменную city_id.
Чтобы скрипты запускались автоматически я решил использовать cron. Для этого я сделал два простеньких bash скрипта вида
cd /root
python minutes.py
Переходим в папку со скриптами и запускаем скрипт. После этого открываем таблицу задач:
nano /etc/crontabs/root
И создаем две задачи:
1-59/1 * * * * * /root/boot_min.sh
0 * * * * * /root/boot_h.sh
Первая команда запускает каждую минуту с первой по 59 минуты скрипт boot_min.sh, вторая запускает в 0 минуту скрипт boot_h.sh. Главное не забыть сделать эти скрипты запускаемыми chmod +x /root/boot-min.sh
После перезагрузки данные автоматически начнут обновляться.
Купить миникомпьютер Omega 2 в России удобней всего на сайте Амперо: http://ampero.ru/collection/omega-2 Лично я советую присмотреться к вариантам с маркировкой S (Omega 2S и Omega 2S Plus). У них исполнение SMD, а значит их удобней паять и больше полезных выводов.
Как я и говорил, все просто.Основная сложность для начинающего (при наличии соответствующих железок) это умение обращаться с Linux. Однако в современных реалиях радиолюбительства это надо исправлять, на что и направлена эта статья.
К статье я прикрепил архив, в котором лежат все исходники и файлики, которые я использовал. Если появятся вопросы или предложения пишите, постараюсь на них ответить.
Прикрепленные файлы:
- omega_youtube.zip (479 Кб)
Комментарии (1) | Я собрал (0) | Подписаться
Для добавления Вашей сборки необходима регистрация