Тонкий клиент vs Orange Pi

Это не прямое сравнение, а скорее опыт дилетанта на примере двух одноплатных компьютеров. Вообще мой дилетантский стаж большой как по отношению к компьютерам, так и к электронике.

Что касается электроники, то тянется он с времен СССР, когда я еще школьником, посещал радиотехнический кружок. Потом уже позже добавились компьютеры. Так на лабораторных довелось опробовать аналоговую вычислительную машину. Как она сохранилась непонятно, в то время уже продавались электронные калькуляторы.

Потом познакомился с СМ ЭВМ и с совместимыми с IBM XT компьютерами. На своем дилетантском уровне испробовал различные языки программирования Фортран, Паскаль, Си и др. Сейчас остановился на Python, наверно потому что человек ленив по природе, а Python способствует ленивому программированию или вернее дает возможность ленивому программировать.

Вернемся к теме. Начало истории банальное, на очередной распродаже был куплен Orange Pi PC (OPi). Опробован на простеньких программах и потом лежал без дела. Тут случилась COVID пандемия. И как многие, уехал я с семьей на дачу.

В город тем не менее возвращались, особенно после того как на работу стали пускать. На дачу хорошо возвращаться пока лето и тепло, но наступила осень и температура упала. При это желание ездить на дачу, как это было раньше, не пропало. Но приезжать в дом, где всего +5 градусов не комфортно. Хорошо бы перед приездом включать электрическое отопление, вот она задача для OPi.

Конечно, можно использовать недельный таймер, чтоб перед выходными включать отопление. А если никто не поедет или приедет раньше? Есть и другие решения, как то: WiFi розетки, GSM розетки и т.п. Все это не очень подходит для нашей конкретной дачи. В дачный сезон, а это до конца октября мобильный интернет скорее мертв чем жив. Да и вообще GSM сигнал слабый, с телефоном надо искать место, чтобы позвонить, а на модем вешать внешнюю антенну.

Таким образом, лучше делать самому. Задача вроде простая, надо подключить к OPi USB модем с антенной и управлять с помощью SMS. Для минимизации усилий стал искать готовое ПО. Попался на глаза «Home Assistant»(HA) и очень удачно, он же написан на Python. На первый взгляд, HA просто то что нужно, там есть всё. Управление по SMS — ерунда, можно развернуть настоящий «умный» дом. Стал знакомится более подробно. Оказалось, для настройки HA знать Python не надо, нужен YAML, что обидно. Не все радужно оказалось и с поддержкой OPi.

Пробный запуск с подключенным датчиком DHT22 показал, что он не работает… Первая мысль, может неисправен, попробовал другой — безрезультатно. Пришлось подключить осциллограф, и смотреть сигналы на DHT22. Обнаружил лишний импульс, который сбивал работу датчика. Стал смотреть исходную программу которая управляет датчиком (библиотека Adafruit). Поправил код, в основном методом тыка, и заработало.

По крайней мере в 30% случаев считывание показаний было удачным. Наверно это неплохо для не Real Time OS, для DHT22 все временные параметры создаются программными задержками. В чем причина, что на OPi датчик не работал? Может особенности архитектуры OPi. У DHT22 свой протокол, а если бы использовал стандартные интерфейсы I2C, SPI и т.п. может проблемы и не было бы. Почему так, что только у меня не работал датчик, не знаю.

В интернете, других жалоб не нашел. Для себя сделал вывод, что используемые в HA библиотеки не всегда подходят для OPi, и «заточены» в основном под Raspberry. Но все же покупать специально Raspberry не стал.

Решил продолжать, и тут случилось у нас жаркое лето 2021 года и OPi не выдержал, хоть и был с радиатором. Что-то случилось с одним из стабилизаторов напряжений. Что же погоревал и стал искать новый компьютер. Даже смотрел на Raspberry, но цена не гуманная. Потом подумал, а зачем покупать эти «фруктово-ягодные» компьютеры, может взять обычный.

На известной площадке где торгуют всяким разным б/у — был куплен Тонкий Клиент (ТК) модель TONK1811 по цене 1200 руб. Габариты несколько больше чем у OPi, но все равно ТК достаточно небольшой.


Что ж мы получаем за эти деньги:

  • Готовый корпус
  • 5 шт. USB2 против 3 шт.
  • Установленный радиатор, пассивное охлаждение
  • Часы реального времени
  • Блок питания
  • ОЗУ 1 Гб
  • Одно-ядерный процессор Atom N270
  • 10/100/1000 Ethernet
  • Интерфейсы — 1 x DVI-I (DB-15) — 1 х PS/2

Из бесполезного… прилагался DOM диск 1Гб с WinXP. WiFi не было, но у меня завалялся модуль, который замечательно подошел. Есть еще SATA — можно подключить SSD до 32Гб. Пока такой по сходной цене не нашел. Поэтому установил OS Debian на USB флэшку. Работает довольно шустро пожалуй даже шустрее OPi.

Может возникнуть резонный вопрос, куда же я воткну датчик DHT22, который я ранее подключал с таким трудом, т. е. где же GPIO? Есть решение — это модуль FT232H (можно купить на Али). Модуль известный, но его в основном используют как мост USB-JTAG. FT232H же может больше, это еще мост USB на I2C, SPI, UART и GPIO.

Библиотеки Python есть готовые у Аdafruit. К сожалению, в HA поддержки нет. Может сейчас уже появилась, я не проверял. Это было последней каплей и решил я с HA расстаться. Мне ж много не надо, напишу все сам на Python. Тем более, что основные «кирпичики» — библиотеки есть. Благодаря распространенности языка оказывается все уже придумано до нас, главное найти. По факту использовал библиотеки Аdafruit и Gammu.

Для начала проверил работоспособность FT232H. Подключал датчики: HTU21d, BMP280, т. е. проверил интерфейсы I2C, SPI — работают. Пробовал адресный светодиод, да и просто светодиод — без проблем. Мне же надо включать внешние устройства — радиаторы отопления, т. е. достаточно GPIO. Для этого взял, по числу потребителей, 4-х канальный радиопередатчик на 433 МГц (TX118SA-4) и к нему приемники с реле.

Приходит сигнал с передатчика реле включается. Проблема в том, что в такой схеме нет обратной связи, т. е. на стороне ТК неизвестно включено или выключено реле. Получил приемник сигнал или нет. Поэтому алгоритм работы решил поменять и дополнил схему сторожевым таймером, на время примерно 7 мин. За основу взял эту схему, найденную в интернете.

https://habr.com/ru/post/645491/image

Теперь в схеме два реле. Первое реле приемника сбрасывает таймер, а реле таймера включает нагрузку.

Нагрузка это радиаторы отопления с мощность около 700 Вт. Реле добыты из сгоревшего ИБП. Передатчик, с интервалом примерно 5 мин, посылает сигнал и взбадривает сторожевой таймер. Если время прошло больше то нагрузка выключается. Питается приемник от монолитного маломощного блока питания на 12В.

Передатчик проверялся на тестовой плате с подключением к FT232H.

Модуль терпелив к 3,3В и 5В.

К сожалению все по времени затянулось и наступила зима. Поездки на дачу прекратились. Необходимость в подогреве отпала. Появилось время добавить еще что то.
Можно, как я уже пробовал, подключить датчик (температура, влажность, давление) и мониторить пространство рядом с ТК, но это мало информативно. Потом вспомнил, что есть у меня погодная станция Oregon WMR88, которая теоретически, может передавать свои данные по USB.

Попытка найти в интернете, что то подходящее на Python не увенчалась успехом. Вернее есть готовые завершенные решения, а так чтоб где то взять python код и вставить себе — нет. Впрочем, оказалось сделать самому не сложно. Для протокол обмена с HID устройствами есть библиотека hid.

Сложнее с протоколом данных самой станции т.к. производитель не раскрывает его, но народ в интернете, что то раскопал, а я воспользовался. Большая часть взята тут.

Правда передача данных очень долгая, чтобы получить информацию со всех датчиков может потребоваться от нескольких минут до десятков. Зато получаю как локальные данные так и с выносных датчиков.

Кроме температуры и влажности есть еще датчики дождя и ветра. Код всей программы не привожу, т. к. это в основном компиляция из примеров к библиотекам. В общем код находится в процессе перманентной модификации, без толковых комментариев. Приведу программу работы с погодной станцией, может кому то пригодится python версия. Код использовался для тестирования работы станции.

Python версия

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Программа считывания данных с погодной станции WMR88
за основу взят код из https://github.com/barnybug-archive/wmr100
@author: jury
"""
import hid
import time
import timeit

def read_wmr88():    
    # Код производителя
    vid = 0x0fde
    pid = 0xca01
    
    try:
        device = hid.device()
        device.open(vid, pid)
        device.set_nonblocking(1)
        # Init
        init = [0x20, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00]
        rw = device.write(bytes(init))
        inp =  [0x01, 0xd0, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00]
        time.sleep(0.05)
        rw = device.write(bytes(inp))
        dat = read_usb(device)
    except IOError as ex:
        device.close()
        print("Ошибка соединения")
    return dat

def read_usb(dev):
    time_out = 10
    print("Read the data")
    dd = []    
    tmi = timeit.default_timer()
    while timeit.default_timer()-tmi < time_out:
        d = dev.read(8,10)
        if len(d) != 0:
            dd.append(d)
            tmi = timeit.default_timer()
        else:
            time.sleep(0.5)
    print("Closing the device")
    dev.close()
    return dd

def parse_data(dat):
    m1 = []
    m2 = []
    m3 = []
    for i in dat:
        for b in i[1:1+i[0]]:
            m1.append(b)
    for i in m1:
        if i == 0xff:
            if len(m2) > 1:
                if sum(m2[:-2]) == (m2[-2] + (m2[-1] << 8)):
                    m3.append(m2)
                    m2 = []
            continue
        else:
            m2.append(i)
    return m3


def get_sensors(dat, full):
    for i in dat:    
        if i[1] == 0x42: # Thermo-Hygrometer (10 bytes)
            t = i[0] >> 4
            battery = t >> 2
            t_trend = t & 0x03
            sensor = i[2] & 0x0f # номер сесора
            st = i[2] >> 4
            h_trend = (st >> 2) & 0x03  # Humidity Trend
            h_trend -= 1
            temp = (i[3] + ((i[4] & 0x0f) << 8)) / 10.0
            if ((i[4] >> 4) == 0x8):
                temp = -temp        
            humidity = i[5]
            dewpoint = (i[6] + ((i[7] & 0x0f) << 8)) / 10.0  # точка росы
            if ((i[7] >> 4) == 0x8):
                dewpoint = -dewpoint  # точка росы
            if sensor == 0:
                weather["Temperature"][sensor] = (temp, dewpoint, trend[t_trend], lev_battery[battery])
                weather["Humidity"][sensor] = (humidity, trend[h_trend])
                full |= 0x01
            else:
                weather["Temperature"][sensor] = (temp, dewpoint, trend[t_trend], lev_battery[battery])
                weather["Humidity"][sensor] = (humidity, trend[h_trend])
                full |= 0x02
            continue
            
        if i[1] == 0x46: # Barometer (6 bytes)
            pressure = (i[2] & 0x0f) + ((i[3] & 0x0f) << 8)
            frcast = i[3] >> 4
            alt_pressure = i[4] + ((i[5] & 0x0f) << 8)
            pre_frcast = i[5] >> 4
            weather["Pressure"] = (pressure,forecast[pre_frcast],alt_pressure,forecast[frcast])
            full |= 0x04
            continue
            
        if i[1] == 0x60: # Clock (10 bytes)
            power = i[0] >> 4
            powered = power >> 3
            battery = (power & 0x4) >> 2
            rf = (power & 0x2) >> 1
            level = power & 0x1        
            mi = i[4]
            hr = i[5]
            dy = i[6]
            mo = i[7]
            yr = i[8] + 2000
            weather["Clock"] = (hr,mi,dy,mo,yr,powered,level,rf)
            full |= 0x08
            continue
        
        if i[1] == 0x48: # Anemometer (9 bytes)
            wind_dir = i[2] & 0xf
            power = i[2] >> 4
            wind_speed = i[4] / 10.0
            low_speed = i[5] >> 4
            high_speed = i[6] << 4
            avg_speed = round((high_speed + low_speed) / 10.0,1)
            weather["Wind"] = (lev_battery[power],wind_speed,low_speed,high_speed,power,avg_speed,windies[wind_dir])
            full |= 0x10
            continue

        if i[1] == 0x41: # Rain Gauge (15 bytes)            
            sensor = i[2] & 0x0f
            power = i[2] >> 4
            rate = i[3]        
            hour = round(((i[5] << 8) + i[4]) * 25.4 / 100.0,1) # /* mm */
            day = round(((i[7] << 8) + i[6]) * 25.4 / 100.0,1) # /* mm */
            total = round(((i[9] << 8) + i[8]) * 25.4 / 100.0,1) # /* mm */
            s_mi = i[10]
            s_ho = i[11]
            s_da = i[12]
            s_mo = i[13]
            s_yr = i[14] + 2000
            weather["Rain"] = (lev_battery[power],rate,hour,day,total,s_mi,s_ho,s_da,s_mo,s_yr)
            full |= 0x20
            continue
        
    return full
 
#######
#  MAIN  #

full = 0
trend = ["растущий","устойчивый","падающий"]
lev_battery = ["заряжена","разряжена"]
weather = {'Pressure':[], 'Temperature':[[],[]], 'Humidity':[[],[]],'Clock':[],'Wind':[], "Rain":[]}
forecast = ["Переменная облачность","Облачно","Дождь","Солнечно","Ясно","Снег"] 
windies = ["северный", "северо-северо-восточный", "северо-восточный", "восточно-восточно-северный", "восточный", "осточно-восточно-южный", "юго-восточный", "юго-юго-восточный", "южный", "юго-юго-западный", "юго-западный", 
           "западно-западно-южный", "западный", "западно-западно-северный", "северо-западный", "северо-северо-западный"]
tmi = timeit.default_timer()
while full != 0x3f:
    out = read_wmr88()    
    if out is not None:
        dat = parse_data(out)        
    else:
        break
    full = get_sensors(dat, full)
    print("Процент выполнения:", round(100*full/63,2))
    time.sleep(5)
    
print("Общее время:",timeit.default_timer()-tmi)    
print(weather)

Планы на будущее. Видео монитор использовать не планируется, а узнать, например, температуру на улице хотелось бы. Сейчас все сохраняется в файл. Можно организовать вывод информации по сети через web server, здравствуй НА, но нет… может потом :). Также есть мысль передавать температуру и прочее через SMS. Основные потербители такой информации, конечно те кто находится на даче.

Кроме, так сказать, зрительного канала информации есть же еще слух. Можно использовать синтезатор речи. Весьма неплох, например RHVoice и для него есть python библиотека. Для запуска синтеза нужно какое то управление извне.

Я выбрал IR пульт, с ним можно управлять удаленно. Там же где и ТК, был куплен модуль OVU4003/00, в системе определяется как «Philips (or NXP) eHome Infrared Receiver». Для управления подобными устройствами есть LIRC, однако в данном случае это не нужно. Можно воспользовался библиотекой evdev. Пока эти дополнения опробовал только на «большом» компьютере с Linux Mint, надеюсь и с ТК проблем не возникнет. Пока смущает система на флэшке. Еще USB модем, который иногда «отваливается», вероятно из-за нехватки питания. Планирую его подключать с доп. питанием.

В заключении могу сказать, что в моем случае, ТК оказался ничем не хуже OPi, а по комплектации даже лучше. Специально не проверял и могу только предполагать, что из-за давней истории система x86 имеет некоторое преимущество, например, библиотек, программ больше, чем для ARM. Я наверно ретроград, но мне x86 система как то привычней.

 

Источник

Читайте также