Pull to refresh

Измеряем температуру: TEMPer + Python + Windows

Reading time 5 min
Views 23K
Всем привет.

На написание поста сподвигла, казалось бы, тривиальная задача — мониторинг температуры в серверной. На эту тему существует довольно много различных решений (например, повесить видеокамеру и градусник перед ней), но большинство из крутых систем мониторинга, автоматического управления кондиционерами и т.п. стоят приличных денег. Отличие же предложенного варианта — бюджет. Около 250 российских рублей и немножко мозгов (бесценно).

Решение задачи началось, естественно, с поиска датчика температуры. Дешевого, потому что дорогих много, а руководству объяснять долго. Так же, в целях экономии, был выбран вариант USB-датчика (LAN — дороже). Где у нас самые дешевые датчики? Правильно, на ebay. Заходим туда и по запросу «usb thermometer» быстро находим вот такую игрушку, как на картинке. Заказываем, оформляем, ждем доставки.

На этом финансовые траты заканчиваются и начинается работа мозгами.
В комплекте с датчиком идет компакт-диск с программой TEMPer 24.4.4. Несмотря на внушительный номер версии, на это чудо программистской мысли смотреть без слез невозможно. Да, в ней есть куча функций: график температуры, отправка сообщений по email, msn и skype, лог в текстовый файл, установка критических температур для тревоги. И оно все даже работает. Нет только самого важного — запуска в режиме сервиса или командной строки. А значит — нет вменяемой возможности автоматизации (парсинг логов не в счет).

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

Поиск в интернете легко выводит на модуль для python, называемый temper-python. Отлично! Вот уже и нормальный софт с кроссплатформенным интерпретатором. Фиг там. Этот модуль работает только под linux. И не зря в заголовок вынесена другая операционка. Мой сервер приложений работает на Windows. Но это же python! Значит, скрипт наверняка можно переделать для работы в Windows. Этим и займемся.

Сначала вставим сам датчик в usb-порт компьютера (сервера). Для эксперимента этого достаточно, но на практике гораздо лучше дополнить датчик выносной подставочкой-удлинителем. Ее можно заказать вместе с датчиком из Китая, купить в любом магазине или найти в любой серверной — как трофей от различных передатчиков или флешек. Windows автоматически поставит на датчик стандартные драйверы и определит его как «Составное USB устройство».
Мне было все равно, с каким питоном работать, поэтому я взял самую последнюю доступную версию — 3.4.1. Собственно, если для кого-то это принципиально, переделать скрипт под младшую версию совсем не проблема — кое-где скобочки и кавычки убрать. Скачиваем, ставим, радуемся: www.python.org.

Сразу же к питону нужно поставить библиотеку для работы с USB. Тоже никаких проблем нет. Называется она pyusb. Берем ее со страницы проекта на sourceforge.net. В архиве есть файл setup.py. Запускаем его из командной строки:

cd C:\Downloads\pyusb-1.0.0a2
C:\Python34\python.exe setup.py install

Pyusb работает с системным библиотеками доступа к usb — libusb 0.1, libusb 1.0, libusbx, libusb-win32 и OpenUSB. Для пользователей Windows доступен вариант libusb-win32, который тоже нужно установить и настроить. Скачиваем архив со страницы проекта на sourceforge.net. И пусть вас не смущает win32 в названии — драйвер отлично работает под x64.

Распаковываем, заходим в папку bin и запускаем файл inf-wizard.exe. Эта программа автоматически сгенерирует папку драйверов под конкретное устройство и установит их.

В списке устройств для установки драйвера необходимо выбрать TEMPerV1.4 (Interface 1). Нажимаем Next, соглашаемся со всем, с чем можно согласиться и доходим до окна сохранения драйверов. Здесь лучше создать отдельную папку для temper'a, куда утилитка сложит все необходимые драйверы. После этого будет предложено установить созданный драйвер. Делаем это кнопкой Install Now...

Если все прошло успешно, то "Составное USB устройство" в Диспетчере устройств исчезнет, а появится TEMPerV1.4 (Interface 1).

В папке bin/amd64 дистрибутива libusb-win32 есть утилита testlibusb-win.exe. Запустим ее, чтобы убедиться, что нужное нам устройство видно системе. Если все хорошо и устройство видно, остается самое интересное — скрипт на python.

Как уже написано выше, за основу я взял модуль temper-python. Модуль этот навороченный, который нужно устанавливать, в котором есть конфиг для поправок, выбор Цельсия или Фаренгейта, и даже поддержка SNMP. Это все круто, но мне не нужно. Поэтому упростим все до одного скрипта, выводящего в консоль температуру. Кроме того, в temper-python есть еще и куча строк, которые нужны только в linux, a в windows попросту не работают.

Начнем с задания глобальных переменных:
import sys, usb

VENDOR_ID = 0x0c45
PRODUCT_ID = 0x7401

TIMEOUT = 5000
OFFSET = 0

INTERFACE = 1
REQ_INT_LEN = 8
ENDPOINT = 0x82

COMMANDS = {
    'temp': b'\x01\x80\x33\x01\x00\x00\x00\x00',
    'ini1': b'\x01\x82\x77\x01\x00\x00\x00\x00',
    'ini2': b'\x01\x86\xff\x01\x00\x00\x00\x00',
}

По VENDOR_ID и PRODUCT_ID скрипт найдет среди всех устройств Temper и будет использовать его. TIMEOUT и OFFSET в общем-то, понятны без объяснения — таймаут доступа к датчику и поправка температуры. Поправка нужна, так как дешевый китайский датчик может ошибаться в пределах нескольких градусов, но это достаточно определить один раз по комнатному термометру и забыть.

Оставшиеся переменные относятся к api датчика и их трогать не надо.

Теперь самое главное — класс для обращения к датчику.

class TemperDevice(object):
	def __init__(self, device):
		self._device = device
		
	def get_temperature(self):
		if self._device is None:
			return "Device not ready"
		else:
			try:
				self._device.set_configuration()
				ret = self._device.ctrl_transfer(bmRequestType=0x21, bRequest=0x09, wValue=0x0201, wIndex=0x00, data_or_wLength=b'\x01\x01', timeout=TIMEOUT)
				
				self._control_transfer(COMMANDS['temp'])
				self._interrupt_read()
				self._control_transfer(COMMANDS['ini1'])
				self._interrupt_read()
				self._control_transfer(COMMANDS['ini2'])
				self._interrupt_read()
				self._interrupt_read()
				self._control_transfer(COMMANDS['temp'])
				data = self._interrupt_read()
				self._device.reset()

				temp = (data[3] & 0xFF) + (data[2] << 8)
				temp_c = temp * (125.0 / 32000.0)
				temp_c = temp_c + OFFSET
				
				return temp_c
			except usb.USBError as err:
				if "not permitted" in str(err):
					return "Permission problem accessing USB."
				else:
					return err
			except:
				return "Unexpected error:", sys.exc_info()[0]
				raise
			
	def _control_transfer(self, data):
		ret = self._device.ctrl_transfer(bmRequestType=0x21, bRequest=0x09, wValue=0x0200, wIndex=0x01, data_or_wLength=data, timeout=TIMEOUT)
		
	def _interrupt_read(self):
		data = self._device.read(ENDPOINT, REQ_INT_LEN, interface=INTERFACE, timeout=TIMEOUT)
		return data

_control_transfer отвечает за передачу команд датчику, _interrupt_read — за чтение ответа. В get_temperature мы, собственно, все эти команды отправляем, читаем ответ и преобразуем набор цифр в человекопонятную температуру.

Чтобы все это заработало при запуске, несколько строк для инициализации:
dev = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID)
TDev = TemperDevice(dev)
output = TDev.get_temperature()
print(output)

Первая строчка ищет наш TEMPer среди устройств, вторая и третья — запрашивают температуру.

Вот, собственно, и все. Проверить работу скрипта можно стандартной командой в консоли:

C:\Python34\python.exe temper.py

На экран будет выведена температура с датчика. Что с ней делать — пусть каждый сам для себя решает.
Tags:
Hubs:
+11
Comments 13
Comments Comments 13

Articles