На мой взгляд одной из важнейших сопутствующих задач наземной наблюдательной астрономии является контроль астроклимата.

Астроклимат — это совокупность факторов атмосферы, влияющих на качество астрономических наблюдений, путем искажения излучения небесных объектов.

(внимание, под катом достаточно много изображений!)

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

Так же важным фактором является ветер, который может вызывать перемешивания мелких неоднородностей в воздухе, приводя тем самым к расфокусу изображения.

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

Кроме природных факторов существуют и антропогенные. Это засветка от городов, выбросы в атмосферу, локальный нагрев воздуха.

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

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

Я же предлагаю поговорить об одном из наиболее часто меняющемся факторе — облачности. Особенно остро вопрос контроля облачности стоит в случае удаленно управляемой обсерватории, когда невозможно просто выйти на улицу и посмотреть что там на небе.

Некоторую помощь тут может оказать камера обзора неба — чувствительная широкоугольная камера, направленная в зенит.


(снимок с камеры не мой, взят в качестве примера. в кадре присутствует метеор :) )

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

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

Немного теории

Днем солнечное излучение согревает поверхность Земли и все, что на ней находится — здания, дороги, воду и т.д. Вся накопленная энергия впоследствии переизлучается в виде того же тепла (инфракрасного излучения).

Если бы у земли не было бы атмосферы, то вся накопленная энергия без каких-либо препятствий излучалась бы в космос. Но к счастью у нашей планеты есть атмосфера :)

В состав атмосферы входят разнообразные газы, аэрозоли, пылевые частицы и водяной пар. Испускаемое Землей инфракрасное излучение активно поглощается водяным паром, разогревая саму атмосферу (это позволяет поддерживать нашу планету достаточно теплой для существования жизни). Облака, как известно, состоят из водяного пара. Соответственно, чем больше этого пара в атмосфере (больше облаков) — тем выше температура. И наоборот, чем более ясное и чистое небо — тем температура ниже. Как и температура любого другого тела — температура атмосферы (неба) может быть измерена. Говоря о температуре имеют ввиду температуру воздушного столба (точнее конуса, угол раствора которого равен углу «зрения» конкретного датчика). Высота этого столба примерно 10-15 км, т.е. до тропосферы — атмосферного слоя, где «делается» погода.

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

(На высоте 10 км, как думаю, многие знают, фактическая температура может доходить и до -50 градусов цельсия).

Температуру окружающего воздуха в точке установки датчика выбирают в качестве опорной. Чем больше разность между окружающей температурой и измеренной температурой неба — тем небо более ясное. Обычно разность в 20 градусов говорит об очень чистой атмосфере, если же разность меньше пяти градусов — небо наглухо затянуто облаками.

Контактные методы измерения температуры неба тут очевидно не подходят поэтому применяют инфракрасные термометры.

Существуют ручные термометры, подобные изображенному на фотографии ниже.



Это своего рода однопиксельный «тепловизор», угол зрения, которого прежде всего определяется встроенной линзой Френеля.

Можно провести простой эксперимент и направить устройство в небо: на чистый участок и на облако — результат будет заметен сразу.


(image credit: Forrest M. Mims III., mynasadata.larc.nasa.gov)

Конструкция датчика облачности

Разобравшись с температурой неба и измерением, предлагаю к рассмотрению простую конструкцию соответствующего датчика, способного работать в автоматическом автономном режиме.

Сердцем устройства является инфракрасный термометр фирмы Melexis – MLX90614, купить который довольно просто.



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

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

Присутствует так же автономный аналоговый режим, когда на выходе устройства – ШИМ сигнал, со скважностью, зависящей от измеряемой температуры. Может быть, полезно при создании устройств наподобие термостата.

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

Некоторые характеристики устройства



Распиновка



Информационная линия SDA используется так же и для вывода ШИМ сигнала в соответствующем режиме.

По умолчанию устройство должно работать в SMBus режиме, но в моём случае почему-то оказался включен PWM. Это привело к тому, что после подключения я увидел на шине i2c полный хаос.
Для переключения устройства в SMBus режим достаточно кратковременно замкнуть линию SCL на землю в момент подачи питания на устройство. К сожалению, при следующем включении устройство снова окажется в PWM режиме. Для переключения в режим SMBus “навсегда” нужно поменять параметры в EEPROM устройства.

Датчик облачности построен на основе микрокомпьютера Raspberry Pi B первого поколения.

Конечно, можно было бы обойтись простейшим avr микроконтроллером, но в моём случае датчик является частью более сложного прибора – универсальной allsky камеры, о которой я еще обязательно напишу. В своём проекте я использую дистрибутив Raspbian Jessie с ядром версии 4.4. Все нижеописанное справедливо для этой версии платы и для этой версии ОС.

В сети есть большое количество информации о подключении MLX90614 к микроконтроллерам и проблем тут обычно не возникает, а вот касательно Raspberry информации маловато и можно запросто встретить разнообразные грабли. Надеюсь, что эта статья поможет кому-то не наступить на них :)

Итак, подключается все очень просто.



Конденсатор C1 – керамический, его применение обязательно.

Резисторы R1 и R2 – 4K7, опциональные, т.к. в Raspberry Pi есть свои подтяжки на i2c шине.
Но eсли линия к mlx достаточно длинная – резисторы лучше поставить. В моём случае совсем рядом на шине висит еще одно устройство, в котором так же есть такие резисторы, поэтому для mlx я не ставил подтяжек. Я использую трехвольтовую версию mlx90614 поэтому в данном случае питание поступает от линии 3.3 вольта. В случае же пятивольтовой может потребоваться согласование уровней дабы не повредить Raspberry.

SMBus

Хотелось бы отдельно сказать про шины SMBus и I2C. Обе шины, в нашем случае (напряжение питания 3.3 вольта), электрически и сигнально совместимы, так что с MLX90614 можно работать как с обычным i2c устройством. Есть так же отличия в максимальных рабочих скоростях, но и этим в данном случае можно пренебречь.

Работа с устройством

Для Raspberry Pi существует два основных способа общаться с i2c устройствами – используя аппаратную i2c шину, посредством драйвера i2c_bcm2708 и библиотеки libi2c-dev или же используя популярную библиотеку bcm2835 которая программно эмулирует i2c протокол, с нужным интервалом дергая те же GPIO2 и GPIO3. По умолчанию i2c адрес устройства – 0x5A.

Забегая вперед скажу, что с bcm2835 проблем не было никаких и датчик MLX90614 заработал сразу, но этот способ мне не нравился, зачем программно эмулировать имеющееся оборудование на компьютере с весьма ограниченными ресурсами. Было принято решение работать через драйвер i2c_bcm2708.

Первым делом следует убедиться, что модуль i2c_bcm2708 загружен, выполнив команду lsmod, если модуля нет в списке – необходимо его загрузить командой

sudo modprobe i2c_bcm2708 

и затем добавить строчку i2c_bcm2708 в конец файла /etc/modules, это необходимо для того что бы модуль загружался при старте системы.

После загрузки модуля станут доступны два устройства — /dev/i2c-0 и /dev/i2c-1
Первый относится к нулевой шине i2c, второй соответственно к первой. В случае Raspberry Pi первых поколений – нулевая шина не распаяна на плате, первая же выведеная на гребенку GPIO, поэтому вся работа идет через /dev/i2c-1

Теперь если запустить команду

i2cdetect -y1

(поставляется в пакете libi2c-dev, y 1 – номер i2c шины) можно увидеть следующее (при условии, что у нас больше нет никаких i2c устройств).



Девайс с адресом 5a – наш MLX90614. Если же вы видите тут просто хаотичный массив из чисел – ваш mlx работает в ШИМ режиме, что бы переключиться — снимите питание с устройства, прижмите линию SCL к «земле» и вновь подайте питание, после чего SCL можно отпустить. После этого устройство должно переключиться в SMBus режим и вывод i2cdetect станет корректным.
Далее я покажу как можно поменять параметры в EEPROM и исправить эту ситуцию.

Работа с устройством очень проста. Пишем простейшую программу на С

Простейший пример
// Необходимые заголовочные файлы
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <linux/i2c-dev.h>

///

int main()
{
	int fdev = open("/dev/i2c-1", O_RDWR);  // открываем i2c шину

	if (fdev < 0) {
		fprintf(stderr, "Failed to open I2C interface %s Error: %s\n", dev_path, strerror(errno));
		return -1;
	}

	unsigned char i2c_addr = 0x5A;

	// устанавливаем адрес подчиненного устройства, в нашем случае 0x5A
	if (ioctl(fdev, I2C_SLAVE, i2c_addr) < 0) {
		fprintf(stderr, "Failed to select I2C slave device! Error: %s\n", strerror(errno));
		return -1;
	}

	// включаем проверку контрольных сумм, дабы не было проблем
	if (ioctl(fdev, I2C_PEC, 1) < 0) {
		fprintf(stderr, "Failed to enable SMBus packet error checking, error: %s\n", strerror(errno));
		return -1;
	}


	// пробуем что-нибудь спросить у устройства, отправив SMBus READ запрос

	i2c_data data;
	char command = 0x06; // команда 0x06 означает прочитать значение термопарного датчика.

	struct i2c_smbus_ioctl_data sdat = {
		.read_write = I2C_SMBUS_READ, 
		.command = command,
		.size = I2C_SMBUS_WORD_DATA,
		.data = &data
	};

	if (ioctl(fdev, I2C_SMBUS, &sdat) < 0) {
		fprintf(stderr, "Failed to perfom I2C_SMBUS transaction, error: %s\n", strerror(errno));
		return -1;
	}

	// выполняем вычисление значения температуры, как описано в даташите
	double temp = (double) data.word;
	temp = (temp * 0.02)-0.01;
	temp = temp - 273.15;

	// печатаем результат в цельсиях
	printf("Tamb = %04.2f\n", temp);

	return 0;
}


Компилируем:

gcc test.c -l  -o test

Запускаем:

sudo ./test

… и получаем ошибку “Failed to perfom I2C_SMBUS transaction, error: bad message

Это ответ от mlx90614, устройство не понимает наш запрос.

В попытках разобраться в чем дело я решил взять логический анализатор и посмотреть как происходит обмен с устройством.

В даташите приведен пример нормального обмена по шине SMBus, чтение, как и в нашем случае.


Логический анализатор же показал следующую картину



Видно что после выполнения команды write и отправки данных добавляется ненужный стоп бит (красная точка), перед тем как отправить запрос на чтение. Это как бы разбивает нашу одиночную посылку на два отдельных неполноценных пакета. Разумеется устройство не понимает такой запрос.

Попробовав повторить все то же самое с программной библиотекой bcm2835 я увидел, что все отрабатывает корректно.



Стало быть я как-то не так использовал api аппаратного драйвера. В итоге, после определенного ковыряния в коде ядра и копания на форуме Rasperry Pi выяснилось, что для того что бы все заработало в драйвере должен быть активирован так называемый комбинированный режим записи-чтения. В этом режиме драйвер не разбивает один пакет с двумя командами чтения-записи на два независимых. Чтобы его активировать необходимо от рута выполнить команду:

echo -n 1 > /sys/module/i2c_bcm2708/parameters/combined

Запись нуля в combined соответственно выключает этот режим.

Теперь, после включения режима, если еще раз запустить наш предидущий пример – мы должны получить ответ.

sudo ./test
Tamb = 19.4

Все работает! Теперь можно писать полноценную утилу для работы с устройством.

В даташите хорошо описаны все адреса EEPROM и RAM для чтения-записи значений и параметров.



Как не трудно догадаться – регистр PWCTRL позволяет включать и выключать тот самый режим ШИМ.

Описание битов регистра из даташита.



Соответственно что бы выключить режим ШИМ надо необходимо 1-ый бит регистра PWCTRL установить в 0.

Чтение значений температур происходит из оперативной памяти устройствами



Как видим отсюда можно прочесть термопарный, первый и второй (если имеется) канал ИК датчика, в виде сырых данных и в виде температуры.

Составим заголовочный файл с необходимыми адресами, mlx_addrs.h

mlx_addrs.h
// RAM
#define MLX90614_RAWIR1 0x04
#define MLX90614_RAWIR2 0x05
#define MLX90614_TA 0x06
#define MLX90614_TOBJ1 0x07
#define MLX90614_TOBJ2 0x08

// EEPROM
#define MLX90614_TOMAX 0x20
#define MLX90614_TOMIN 0x21
#define MLX90614_PWMCTRL 0x22
#define MLX90614_TARANGE 0x23
#define MLX90614_EMISS 0x24
#define MLX90614_CONFIG 0x25
#define MLX90614_ADDR 0x2E
#define MLX90614_ID1 0x1C
#define MLX90614_ID2 0x1D
#define MLX90614_ID3 0x1E
#define MLX90614_ID4 0x1F


И полный исходный код приложеня для работы с устройством MLX90614.

mlx90614.c

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <getopt.h>
#include <stdlib.h>
#include <linux/i2c-dev.h>
#include <errno.h>
#include "mlx_addrs.h"

// buffer for data reading or writing
typedef union i2c_smbus_data i2c_data;


static int DEBUG_MODE = 0;

extern const char* __progname;
///

int get_device(const int bus_num, const unsigned char i2c_addr)
{
        char dev_path[11] = { 0 };

        // construct path to i2c device
        snprintf(dev_path, 11, "/dev/i2c-%i", bus_num);

        if (DEBUG_MODE) {
                fprintf(stderr, "Opening i2c interface %s\n", dev_path);
        }

        int fdev = open(dev_path, O_RDWR);

        if (fdev < 0) {
                fprintf(stderr, "Failed to open I2C interface %s Error: %s\n", dev_path, strerror(errno));
                return -1;
        }

        if (DEBUG_MODE) {
                fprintf(stderr, "Setting up slave address 0x%02X\n", i2c_addr);
        }

        // set addr of the slave i2c device
        if (ioctl(fdev, I2C_SLAVE, i2c_addr) < 0) {
                fprintf(stderr, "Failed to select I2C slave device! Error: %s\n", strerror(errno));
                return -1;
        }





        // enable checksums
        if (ioctl(fdev, I2C_PEC, 1) < 0) {
                fprintf(stderr, "Failed to enable SMBus packet error checking, error: %s\n", strerror(errno));
                return -1;
        }

        return fdev;
}

int talk_to_device(const int fdev, const int read, const char command, i2c_data* data)
{
        // initialize i2c_smus structure for combined write/read request to device
        struct i2c_smbus_ioctl_data sdat = {
                .read_write = (read ? I2C_SMBUS_READ : I2C_SMBUS_WRITE), // set operation type: read or write
                .command = command,             // set command, i.e. register number
                .size = I2C_SMBUS_WORD_DATA,   // set data size, note: mlx supports only WORD
                .data = data    // pointer to data
        };

        if (DEBUG_MODE) {
                fprintf(stderr, "Perfoming %s request to device, command = 0x%02X\n"
                                                , (read ? "I2C_SMBUS_READ" : "I2C_SMBUS_WRITE"), command);
        }

        // perfom combined request to device
        if (ioctl(fdev, I2C_SMBUS, &sdat) < 0) {
                fprintf(stderr, "Failed to perfom I2C_SMBUS transaction, error: %s\n", strerror(errno));
                return -1;
        }

        if (DEBUG_MODE) {
                fprintf(stderr, "Ok, got answer from device\n");
        }

        return 0;
}

int check_args(const int bus_num, const unsigned char i2c_addr)
{
        if (bus_num > 1 || bus_num < 0) {
                fprintf(stderr, "Invalid bus number %i, please select 0 or 1\n", bus_num);
                return -1;
        }

        if (i2c_addr == 0) {
                fprintf(stderr, "Invalid i2c device address, please set proper address of the MLX\n");
                return -1;
        }

        return 0;
}

int read_data_from_sensor(const int fdev, const char command)
{
        i2c_data data;

        if (talk_to_device(fdev, 1, command, &data) < 0) {
                return  -1;
        }

        double temp = 0;

        switch (command) {
                case MLX90614_TA:
                case MLX90614_TOBJ1:
                case MLX90614_TOBJ2:
                        temp = (double) data.word;
                        temp = (temp * 0.02)-0.01;
                        temp = temp - 273.15;
                        printf("%s = %04.2f\n", (command == MLX90614_TA ? "Tamb" : "Tobj"), temp);

                        break;

                case MLX90614_EMISS:
                        printf("Emissivity correction coefficient = %i\n", data.word);
                        break;

                case MLX90614_PWMCTRL:
                        if (!(data.word & (1 << 1))) {
                                printf("PWM mode - disabled\n");
                        } else {
                                printf("PWM mode - enabled\n");
                                printf("In order to disable pwm mode - pull down SCL for >=1.2 ms and change EEPROM setting.\n");
                        }

                        break;
        }

        return 0;
}

int write_data_to_sensor(const int fdev, const char command, const unsigned short write_arg)
{
        i2c_data msg;

    // get current value of the register
        if (talk_to_device(fdev, 1, command, &msg) < 0) {
                return  -1;
        }

        unsigned short current_val = msg.word;

    if (DEBUG_MODE) {
        fprintf(stderr, "EEPROM cell = 0x%02X current value = 0x%04X\n", command, current_val);
    }

        msg.word = 0x0;

        if (DEBUG_MODE) {
                fprintf(stderr, "Erasing EEPROM cell = 0x%02X\n", command);
        }

    // provide some time for device
        usleep(1000);

        if (talk_to_device(fdev, 0, command, &msg) < 0) {
                fprintf(stderr, "Unable to erase EEPROM cell\n");
                return -1;
        }

        // delay between eeprom erasing and writing new value
    // without this delay writing to device may fail
        usleep(5000);

        if (command == MLX90614_ADDR) {
                msg.word = 0xFFFF;
                msg.word = msg.word << 8 | write_arg;  // MLX devices uses LSByte only for address, other bits are ignored
    } else if(command == MLX90614_PWMCTRL) {
                if (write_arg) {   // enable PWM bit
                        current_val |= (1 << 1);
                } else {     //disable PWM bit
                        current_val &= ~(1 << 1);
                }

                msg.word = current_val;
        } else {
                msg.word = write_arg;
        }

        if (DEBUG_MODE) {
                fprintf(stderr, "Trying to store value = 0x%04X to the EEPROM cell = 0x%02X\n", msg.word, command);
        }

        if (talk_to_device(fdev, 0, command, &msg) < 0) {
                fprintf(stderr, "Unable to write to EEPROM\n");
                return -1;
        }

        usleep(5000);

        if (command == MLX90614_ADDR) {
                printf("MLX device address succesfully changed to 0x%X\n", msg.word);
                printf("Please, power off and power on again the device to apply changes\n");
        } else if (command == MLX90614_EMISS) {
                printf("Warning! Emissivity correction coefficient was changed to %i\n", msg.word);
        } else if (command == MLX90614_PWMCTRL) {
                printf("PWM mode is now %s\n", (write_arg ? "enabled" : "disabled"));
        }

        return 0;
}

void show_usage()
{
        printf("Usage\n");
        printf("\t%s --bus [0-1] --i2c_addr [0x00-0x7F] command|command=values wflag\n", __progname);
        printf("\n");
        printf("\t\t-b, --bus\t\t- set i2c bus number (0 for Raspbery PI model A, 1 for Raspberry PI model B, default is 0)\n");
        printf("\t\t-c, --i2c_addr\t\t- set slave device address (default = 0x5A)\n");
        printf("\t\t-r, --new_addr=ADDR\t- set new i2c ADDR for the device\n");
        printf("\t\t-w, --write\t\t- perfom writing to the device (wflag)\n");
        printf("\t\t-i, --get_ir_temp\t- get temperature in C from the infrared sensor\n");
        printf("\t\t-a, --get_ambient_temp\t- get temperature in C from the PTAT element\n");
        printf("\t\t-e, --emissivity_coefficient\t- get value of the emissivity coefficient\n");
        printf("\t\t--emissivity_coefficient=VALUE\t- set new VALUE for emissivity coefficient, use with --write argument\n");
        printf("\t\t-p, --pwm_mode\t\t- check current state of the PWM\n");
        printf("\t\t--pwm_mode=1|0\t\t- disable (0) or enable (1) PWM mode, use with --write argument\n");
}

int main(int argc, char **argv)
{
        int bus_num = 0;
        unsigned char i2c_addr = MLX90614_I2CADDR;

        int op_read = 1;
        int write_arg_set = 0;

        unsigned char command = 0x00;
        unsigned short write_arg = 0x00;

        static struct option long_options[] = {
                { "help", no_argument, NULL, 'h' },
                { "bus", required_argument, NULL, 'b' },
                { "i2c_addr", required_argument, NULL, 'c' },
                { "new_addr", required_argument, NULL, 'r'},
                { "write", no_argument, NULL, 'w' },
                { "get_ir_temp", no_argument, NULL, 'i' },
                { "get_ambient_temp", no_argument, NULL, 'a' },
                { "emissivity_coefficient", optional_argument, NULL, 'e'},
                { "pwm_mode", optional_argument, NULL,'p'},
                { "debug", no_argument, NULL, 'd' }
        };

        int option_index = 0;
        int opt = getopt_long(argc, argv, "hbc:r:wiae:p:d", long_options, &option_index);

        while (opt != -1) {
                switch (opt) {
                        case 'h':
                                show_usage();
                                return 0;

                        case 'b':
                                bus_num = atoi(optarg);
                                break;

                        case 'c':
                                i2c_addr = strtol(optarg, NULL, 16);
                                break;

                        case 'r':
                                write_arg = strtol(optarg, NULL, 16);;
                                write_arg_set = 1;
                                command = MLX90614_ADDR;
                                break;

                        case 'w':
                                op_read = 0;
                                break;

                        case 'i':
                                command = MLX90614_TOBJ1;
                                break;

                        case 'a':
                                command = MLX90614_TA;
                                break;

                        case 'e':
                                command = MLX90614_EMISS;

                                if (optarg) {
                                        write_arg = atoi(optarg);
                                        printf("%i\n", write_arg);
                                        write_arg_set = 1;
                                }

                                break;

                        case 'p':
                                command = MLX90614_PWMCTRL;

                                if (optarg) {
                                        write_arg = atoi(optarg);
                                        write_arg_set = 1;
                                }

                                break;

                        case 'd':
                                DEBUG_MODE = 1;
                                break;

                        default:
                                show_usage();
                                abort();
                }

                opt = getopt_long(argc, argv, "bc:wiaep", long_options, &option_index);
        }

        if (check_args(bus_num, i2c_addr) < 0) {
                return -1;
        }

        if (!op_read && (command == MLX90614_TOBJ1 || command == MLX90614_TA)) {
                fprintf(stderr, "Read only data!\n");
                return -1;
        }

        if (!op_read && !write_arg_set) {
                fprintf(stderr, "Plese set parameter value for writing\n");
                return -1;
        }

        int fdev = get_device(bus_num, i2c_addr);

        if (fdev < 0) {
                return -1;
        }

        int res;

        if (op_read) {
                res = read_data_from_sensor(fdev, command);
        } else {
                res = write_data_to_sensor(fdev, command, write_arg);
        }

        close(fdev);

        return res;
}


Makefile
CC := gcc
PROGRAM = read_mlx90614
SRC := mlx90614.c
CFLAGS := -Wall -std=gnu99
TARGET_DIR := /opt/allsky/bin

all: $(PROGRAM)

$(PROGRAM): $(OBJECTS)

$(CC) $(CFLAGS) $(SRC) $(LDFLAG) -o $(PROGRAM)

install:
    mkdir -p $(TARGET_DIR)
    cp $(PROGRAM) $(TARGET_DIR)
    cp dht_to_db.sh $(TARGET_DIR)

clean:
    rm -fr $(PROGRAM) $(PROGRAM).o



Собираем и запускаем:

make

Чтение температуры с ИК датчика, i2c шина 1, i2c адресс 0x5A:

./read_mlx90614 --bus 1 --i2c_addr 0x5a -i
Tobj = 21.3

Чтение температуры c термопарного датчика:

./read_mlx90614 --bus 1 --i2c_addr 0x5a -a
Tamb = 19.4

Работа с режимом ШИМ. Узнать текущий режим:

./read_mlx90614 --bus 1 --i2c_addr 0x5a -p
PWM mode - enabled

Выключить режим ШИМ:

./read_mlx90614 --bus 1 --i2c_addr 0x5a --pwm_mode=1 -w

Выключить режим ШИМ:

./read_mlx90614 --bus 1 --i2c_addr 0x5a --pwm_mode=0 -w

Так же есть дополнительный аргумент --debug, включающий режим отладки, это позволяет наглядно увидеть все взаимодействие с устройством.

./read_mlx90614 --bus 1 --i2c_addr 0x5a --pwm_mode=1 -w –debug

   Opening i2c interface /dev/i2c-1
   Setting up slave address 0x5A
   Perfoming I2C_SMBUS_READ request to device, command = 0x22
   Ok, got answer from device
   EEPROM cell = 0x22 current value = 0x0201
   Erasing EEPROM cell = 0x22
   Trying to store value = 0x0203 to the EEPROM cell = 0x22
   PWM mode is now enabled

Т.к. ИК окошко mlx90614 выполнено герметичным – нет необходимости в дополнительной гидроизоляции для уличного применения устройства.

Вот так датчик смонтирован у меня, на корпусе allsky камеры.



Замер температуры неба производится каждые 5 минут, данные записываются в MySQL базу данных.

В последствии значение температуры неба так же накладывается на ночной снимок камеры.



Температура неба -1.30 градуса цельсия, хорошее ясное летнее небо.
Поделиться с друзьями
-->

Комментарии (16)


  1. Rumlin
    27.01.2017 21:37

    off Маркировка какая-то есть на корпусе? Как указано отличие 3 и 5 вольтовых версий? /off


    1. elenbert
      27.01.2017 21:43
      +1

      Да, на корпусе есть маркировка
      image
      Там много чего может быть написано, но главное там — это трехбуквенная серия (буквы B и A), по которой и различаются все версии.
      В даташите, в самом начале, подробно расписано чем они все (кроме питающих напряжений) отличаются.


      1. Rumlin
        27.01.2017 22:26

        Разобрал неисправный термометр MIEO HT706 (нет значений — на экране символы «Lo») — внешне такой же элемент, размером менее 5 мм, но без маркировки. Впаян на плату с шелкографией у выводов «TP+ TR- TP- TR+». Была мысль проверить жив ли и где нибудь использовать…


        1. elenbert
          27.01.2017 22:49

          Посмотрите на какие выводы приходит земля и питание. Если вдруг совпадет с распиновкой mlx — может быть это он и есть или какой-то китайский клон.
          Я бы в любом случае потыкался бы в оставшиеся два вывода осциллографом…


          1. Rumlin
            28.01.2017 22:00

            Оказалось чистый аналог. TP — расшифровывается как «thermopile»(термоэлемент), а TR- как «терморезистор».

            Схема термометра MIEO HT706 близка к референсу http://www.hycontek.com/wp-content/uploads/APD-SD18009_EN.pdf
            Судя по всему thermopile можно проверить только подключив к ОУ — подозреваю что очень низкое напряжение выдает.


  1. himch
    27.01.2017 21:44

    off
    Загуглил allsky:

    http://www.allskycam.com/u.php?u=578

    Это комета?


    1. elenbert
      27.01.2017 21:51

      На кадре в статье — метеор. Что бы комета появилась на снимке allsky камеры она должна быть очень близкой и яркой.
      По слабым объектам такая камера не работает, как правило.


      1. himch
        27.01.2017 21:54

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


        1. elenbert
          27.01.2017 22:03

          Простите, подумал вы о снимке в статье. По вашей ссылке на краях поля очень сильные искажения, так что яркие объекты вытягиваются. Это скорее всего просто яркая звезда.
          Посмотрите внимательно, там еще много таких «комет», только более слабых.
          Такая большая комета — событие :)


          1. himch
            27.01.2017 22:09

            Да, наверное — хвост «кометы» не поворачивается вместе с ночным небом и направлен строго от центра поля зрения.

            Камера у вас обычная цифровая?..


            1. elenbert
              27.01.2017 22:11

              Уточнил, сейчас в этой области неба очень яркий объект — Венера, «хвост» — искажение оптики/купола.

              Для ночной съемки неба я использую популярную астрокамеру QHY5-IIM. Это достаточно толковая и чувствительная CMOS камера. На неё установлен слегка переделанный широкоугольный объектив от камеры видеонаблюдения. Но об этом я еще отдельно буду писать.


  1. hippieua
    28.01.2017 05:04

    Отлично, то что я давно собирался сделать и до чего никак не доходили руки. AllSky камера конечно хорошо, но датчик будет намного лучше.


  1. CombaSoft
    05.02.2017 21:57

    У вас на снимке север-юг и восток-запад отзеркалены — несколько усложняет восприятие.
    Как думаете бороться со снегом на датчике?


    1. elenbert
      05.02.2017 22:00

      Само устройство обогревается, так что коробка всегда теплая. В зимнее время можно «топить» сильнее (автоматически разумеется).
      Так что особо много снега не должно намерзать, тем более снег в наших краях не самый частый гость. Но если вдруг совсем засыпет — придется конечно руками почистить, благо устройство у меня в шаговой доступности.


  1. ugsm
    05.02.2017 22:00

    если у датчика угол обзора (пятно) 90 градусов, что показывает измеренная температура? «Среднее по больнице», максимальную/минимальную в пятне, либо есть какое-то подобие диаграммы направленности (как у антенн), когда в центре значимость больше, по краям меньше? Вопрос не только (и не столько) для оценки облачности неба, сколько по прямому назначению датчика — измерению температуры объектов.


    1. elenbert
      05.02.2017 22:07

      В моём случае меряется «среднее по больнице» для всего слоя атмосферы.
      У датчика есть подобие диаграммы направленности, пик чувствительности — в нуле градусов.
      На 90 градусах его чувствительность составляет 50%, есть еще небольшой захват на 120 градусах, но там уже менее 10%.