https://habr.com/ru/post/471232/- Python
- Разработка под Linux
Доброго времени суток, хабровчане!
Небольшое предисловие
Намедни я стал счастливым владельцем одного из самых маленьких одноплатников, работающим на LEDE, и первое, что захотелось сделать (после моргания светодиодом) — домашняя метеостанция, к которой можно получить доступ из любой точки. Первым делом было решено снимать данные о температуре, влажности и давлении. Для этого были выбраны купленные ранее датчики
DHT11 И
LPS3311AP (фото под катом).
После короткого гугления выяснилось, что если первое семейство хорошо задокументировано и имеет множество библиотек для работы, то вот выбранный мной барометр не столь популярен и кроме самописной библиотеки (пусть и очень качественной) под Arduino магазина, в котором и был приобретён датчик (не реклама, просто дань уважения), ничего найти не удалось.
Какой выбор остаётся?
- Собрать прослойку на микроконтроллёре ATmega328, прошить, залить готовый код и читать с неё. Очень увлекательно, но звучит как попытка собрать велосипед для дальнейшего использования в качестве костыля.
- Читать «вручную» с I2C, опираясь на официальный даташит. Попробовал, это возможно, но плодить bash-скрипты не хотелось и не казалось методологически верным.
- Написать библиотеку, чтобы работать с датчиком так, как хочется.
Если интересно, как шло и что из этого получилось, то добро пожаловать под кат.
Первый вопрос, с которым я столкнулся — выбор языка. Из того, что работает на этом одноплатнике без танцев с бубном — C++ и Python. Так как в дальнейшем я хочу положить этот проект в основу умного дома для себя, то выбор пал на последний.
Из-за эффекта утёнка следующий вопрос (какую версию) я не поднимал и сразу же поставил облегчённый python3 и библиотеку для работы с I2C шиной.
Дальше были часы курения сначала
даташита, потом —
software guide от компании производителя. Они позволили быстро подготовить сенсор к работе (хотя софтверный гайд оказался более полным, в том числе в части рекомендации проверять завершения измерения и «перезагрузки» сенсора для более точного сбора.
Первая сложность, с которой я столкнулся — это чтение данных. Так как данные с термометра передаются двумя байтами, а давления — тремя, то необходимо получить несколько байт и объединить их в одно большое число. Но python по умолчанию преобразует hex в int, и простая конкатенация не работает. Преобразование int в hex возвращает строку, которая прекрасно объединяется, но при этом не преобразуется обратно в число. Выход? Можно было бы подключить поддержку с-типов, но возиться, равно как забивать память дополнительной библиотекой, не хотелось, поэтому было решено написать функцию на 7 (на самом деле 8, если считать словарь) строк для перевода псевдобайтстроки в число.
Hex containing string to ints_t_h = { '0' : 0, '1' : 1, '2' : 2, '3' : 3,
'4' : 4, '5' : 5, '6' : 6, '7' : 7,
'8' : 8, '9' : 9, 'a' : 10, 'b' : 11,
'c' : 12, 'd' : 13, 'e' : 14, 'f' : 15 }
def __string_to_int(self, hex_string):
l = len(hex_string) - 1
res = 0
for h in hex_string:
res += s_t_h[h] * (16 ** l)
l -= 1
return res
Полагаю, здесь может быть резонно задан вопрос «а зачем читать из словаря?» ответ прост — очевидных решений по приведению типа мне в голову не пришло, если у знатоков есть ответ на этот вопрос — буду рад внести изменения.
Вторая сложность, которая меня поставила в тупик, это трактовка данных. Оба указанных выше документа дают весьма простые формулы для преобразования:
Pout(mbar)=(PRESS_OUT_H & PRESS_OUT_L & PRESS_OUT_XL)[dec] / 4096
для давления;
T(degC) = 42.5 + (Temp_OUTH & TEMP_OUT_L)[dec] / 480
для температуры.
Что радует, они сразу предлагают метрическую систему.
Однако, первый подход упорно давал мне результат 0x2F8000, что означает 760 миллибар или примерно 585 мм рт. ст. Для высоты в 130 метров над уровнем моря это явно маловато. Перепроверка кода, перечитывание информации об измерении давления, перезагрузка датчика и игры с точностью ничего не дали. А вот повторное изучение гайдлайна помогло — стабильные 760 мбар трактуются как сигнал, что датчик неисправен. Читатели, которые внимательно посмотрели на приложенное фото в начале статьи могут убедиться, что датчик, более того, в принципе отсутствует на чипе :) На моё счастье, я запасливо купил сразу два таких.
Фото исправного чипа и правильной работы
Маленький чёрный квадратик в центре — это и есть измерительная пластина HCLGA-16L, «сердце» сенсора.
После этого открытия стало очевидным, что надо бы сделать проверку на исправность. так родилась функция:
working_checkBROKEN_MARKER = 0x2f8000/4096
def __working_check(self, address):
c1 = self.__read_pressure(address)
c2 = self.__read_pressure(address)
c3 = self.__read_pressure(address)
if c1 == c2 == c3 == BROKEN_MARKER:
return True
else:
return False
Для того, чтобы избежать редкого, но всё же потенциально вероятного случая ложного срабатывания, показания снимаются трижды и только после этого выносится вердикт о неработоспособности. Возможно избыточно использовать константу в таком коде, но хороший тон следует соблюдать всегда, когда это возможно.
Тут же в голову пришла мысль, что не плохо бы при инициализации проверять, верно ли указан адрес на шине, и была добавлена функция
__deviceAdressCheck, проверяющая регистр
WHO_AM_I и ожидающая получить заветное число 0хВВ.
На этом, кажется, интересных моментов больше нет. Полный
код выложен на гитхабе и доступен для использования и модификации.