Зарядное устройство для литиевого аккумулятора 12в. Выбор зарядного устройства для литиевых аккумуляторов. Как собрать зарядку для литий-ионных аккумуляторов своими руками

Зарядное устройство для литиевого аккумулятора 12в. Выбор зарядного устройства для литиевых аккумуляторов. Как собрать зарядку для литий-ионных аккумуляторов своими руками
Зарядное устройство для литиевого аккумулятора 12в. Выбор зарядного устройства для литиевых аккумуляторов. Как собрать зарядку для литий-ионных аккумуляторов своими руками

Продолжаем развивать нашу метеостанцию.

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

Мне написал один из наших коллег с вопросом, по какой причине введен сторожевой таймер?

Сторожевой таймер стоит на случай ч.п. Как показывает практика, ENC28J60 не тянет более (если не подводит память) 4 одновременных соединений. Учитывая сколько служебных соединений, постоянно происходит для поддержания работы самой сети, и просто левый трафик, создаваемый всяческими домашними игрушками (например, современные телевизоры, сканируют доступные хосты в сети и открытые у них порты) конструкция попросту уходит в ступор. ENC28J60 не умеет самостоятельно работать с сетевыми протоколами и все реализовано в библиотеках. Возможно дело именно в них.
Проверял все доступные библиотеки и разные модули (вдруг брак), но добиться стабильной работы в течении длительного времени у меня не получилось. Максимальный срок был порядка 3-4 недель.
Именно для этого там крутится "пес" и в случае чего дергает контроллер. После этого проблема ушла.
Также не отрицаю, что возможно в моей домашней сети есть определенные нюансы или проблемы. Но раз проблема была у меня, она может выплыть и у другого человека. Я пока нашел только такое решение.
Насколько мне известно, на чипах от Wiznet (W5100 и выше) этого нет, ну или просто плохо искали.

Переходим к обновлению

Самое главное, мы уходим от чипа ENC28J60 и переходим на W5100 . Я пытался реализовать все на старом чипе, но не хватает памяти микроконтроллера из-за очень больших библиотек для ENC28J60 . При использовании нового чипа, стандартной библиотеки от разработчика и всех внесенных изменений, остается еще более 20% свободной памяти микроконтроллера ATMega328 . А это, новые плюшки!

В этой версии (назовем её второй) добавлена возможность передачи показаний с датчиков по беспроводной связи используя частоту 433 мГц . Сами модули я брал у Китайцев, маркировка XY-MK-5V . Хочу отметить, что качество передачи далеко от совершенства. Возможны потери сигнала, шумы, не возможность одновременной передачи и т.д и т.п. Но их цена (менее $1 за комплект) компенсируют эти недостатки. Скажу Вам по секрету, что именно эти (самые дешевые) модули стоят во многих фирменных метеостанциях для домашнего использования. Ого, неожиданно?

Начнем с базовой станции

Мы переходим на Arduino UNO и Ethernet Shield (первой версии) на базе чипа W5100 . Это бутерброд и описывать его нету смысла. Я опишу только дополнительно задействованные контакты для модулей XY-MK-5V .

Модуль передатчика использует питание 5V , GND (куда без матушки то) и D2 пин на контроллере. Изменить контакт D2 (DATA) можно, используя функцию vw_set_tx_pin из библиотеки vw.

В отличии от предыдущего скетча, в этом задействованы две дополнительные библиотеки:

#include #include

Сам скетч

Скрытый текст

#include #include #include #include #include #include #include #include #define DHTTYPE DHT22 #define DHTPIN 5 DHT dht(DHTPIN, DHTTYPE); byte mac = {0x54, 0x34, 0x31, 0x31, 0x31, 0x31}; char server = "narodmon.ru"; int port = 8283; IPAddress ip(192,168,0,201); EthernetClient client; BMP085 dps = BMP085(); long Temperature = 0, Pressure = 0; float H, dP, dPt; bool interval = true; EasyTransferVirtualWire ET; struct SEND_DATA_STRUCTURE{ byte ID; // Идентификатор устройства int Temperature; // Температура float Pressure; // Давление float Humidity; // Влажность float dewPoint; // Точка росы/инея }; SEND_DATA_STRUCTURE broadcast; void setup() { // Инициализация сторожевого таймера (Watchdog timer) wdt_disable(); delay(8000); wdt_enable(WDTO_8S); // Инициализация консоли Serial.begin(9600); // Инициализация датчика DHT dht.begin(); // Инициализация модуля 433 мГц ET.begin(details(broadcast)); vw_set_ptt_inverted(true); vw_set_tx_pin(2); vw_setup(2000); // Стартуем сеть, если не дождались данных с DHCP сервера то // присваеваем себе адрес самостоятельно if (Ethernet.begin(mac) == 0) Ethernet.begin(mac, ip); // Инициализация 1-Wire Wire.begin(); delay(200); // Инициализация BMP180 с корректировкой высоты // dps.init(MODE_STANDARD, 3200, true); // Инициализация BMP180 dps.init(); Serial.println(Ethernet.localIP()); // Отправляем первые данные сразу после включения устройства send_info(true); } // dewPoint function NOAA // reference (1) : http://wahiduddin.net/calc/density_algorithms.htm // reference (2) : http://www.colorado.edu/geography/weather_station/Geog_site/about.htm double dewPoint(double celsius, double humidity) { // (1) Saturation Vapor Pressure = ESGG(T) double RATIO = 373.15 / (273.15 + celsius); double RHS = -7.90298 * (RATIO - 1); RHS += 5.02808 * log10(RATIO); RHS += -1.3816e-7 * (pow(10, (11.344 * (1 - 1/RATIO))) - 1) ; RHS += 8.1328e-3 * (pow(10, (-3.49149 * (RATIO - 1))) - 1) ; RHS += log10(1013.246); // factor -3 is to adjust units - Vapor Pressure SVP * humidity double VP = pow(10, RHS - 3) * humidity; // (2) DEWPOINT = F(Vapor Pressure) double T = log(VP/0.61078); // temp var return (241.88 * T) / (17.558 - T); } void send_info(bool eth) { bool fail = true; while(fail) { // Пытаемся считать данные с датчика влажности DHT до тех пор, пока не получим // результат. В 90% случаев все работает нормально, но нам нужны 100% if((H = dht.readHumidity()) >= 0) { // Получение влажности и температуры с датчика BMP180 dps.getPressure(&Pressure); dps.getTemperature(&Temperature); // Подсчитываем точку росы, если температура на улице выше 0 градусов Цельсия // и ожидаем результат выше 0, в противном случае выводим 0. Это необходимо // чтобы не вводить в заблуждения в зимее время года. // dP = Temperature>0?((dPt=dewPoint(Temperature*0.1, H))<0?0:dPt):0; dP = dewPoint(Temperature*0.1, H); // Отправляем данные в эфир 433 мГц broadcast.ID = 1; broadcast.Temperature = floor(Temperature*0.1); broadcast.Pressure = floor(Pressure/133.3*10)/10; broadcast.Humidity = floor(H*10)/10; broadcast.dewPoint = floor(dP*10)/10; ET.sendData(); delay(250); if(eth) { // Подключаемся к серверу "Народный мониторинг" if(client.connect(server, port)) { // Начинаем передачу данных // адрес_устройства_в_проекте, имя_устройства, GPS широта, GPS долгота client.print(F("#fe-31-31-0e-5a-3b#Arduino Uno#71.344699#27.200014\n")); // Температура client.print(F("#T0#")); client.print(Temperature*0.1); client.print(F("#Температура\n")); // Давление client.print("#P1#"); client.print(Pressure/133.3); client.print(F("#Давление\n")); // Влажность client.print("#H1#"); client.print(H); client.print(F("#Влажность\n")); // Точка росы\инея client.print("#T1#"); client.print(dP); client.print((dP <= 0)? F("#Точка инея\n"):F("#Точка росы\n")); //client.print(F("#Точка росы\n")); // Отправляем конец телеграммы client.print("##"); // Даем время отработать Ethernet модулю и разрываем соединение delay(250); client.stop(); } } // Останавливаем цикл, если передача завершена fail = !fail; break; } delay(250); } } void loop() { // Каждые 4 секунды сбрасываем сторожевой таймер микроконтроллера // Каждые 6 минут отправляем данные на "Народный мониторинг" // Каждые 30 секунд отсылаем данные в эфир 433 if(!(millis()%1000)) wdt_reset(); if(!(millis()%360000)) send_info(true); if(!(millis()%30000)) send_info(false); }

К самим модулям необходимо добавить антенну. Для 433 мГц достаточно обычного медного провода длинной 17 см . Без антенны можете забыть о нормальной работе.

Переходим к самой важной части этого обновления - локальная беспроводная станция

Для её реализации (на коленке) я использовал аналог Arduino NANO (на базе ATMega328 ) и TFT дисплей на чипе ST7735S с разрешением 128 x 160

Скрытый текст



Распиновка дисплей -> контроллер

============================= LED | 3.3V SCK | SCK (13) SDA | MOSI (11) A0 | DC (9) RESET | RST (8) CS | CS (10) GND | GND VCC | 5V ============================

Модуль приемник подключается также как передатчик, только DATA к пину D7 .

Пару снимков, как это выглядит:

Скрытый текст

Скетч приемника

Скрытый текст

#include #include #include #include int x, y; int w = 128, h = 160; int size; // 433 EasyTransferVirtualWire ET; struct SEND_DATA_STRUCTURE{ byte ID; // Идентификатор устройства int Temperature; // Температура float Pressure; // Давление float Humidity; // Влажность float dewPoint; // Точка росы/инея }; SEND_DATA_STRUCTURE broadcast; int Log_Temperature = -1; float Log_Pressure = -1; float Log_Humidity = -1; float Log_dewPoint = -1; // TFT #define cs 10 #define dc 9 #define rst 8 char Temperature, Pressure, Humidity, dewPoint; String info; TFT TFTscreen = TFT(cs, dc, rst); void setup(){ Serial.begin(9600); // Инициализация модуля 433 мГц ET.begin(details(broadcast)); vw_set_ptt_inverted(true); vw_set_rx_pin(7); vw_setup(2000); vw_rx_start(); // Инициализация и начальная настройка дисплея TFTscreen.begin(); TFTscreen.setRotation(2); TFTscreen.background(0, 0, 0); // Рисуем статические элементы // 1. Заходите к нам в гости TFTscreen.stroke(255, 255, 255); TFTscreen.setTextSize(1); TFTscreen.text(" ", 10, 10); // 2. Описание показаний с датчиков TFTscreen.text("mmHg", w/2+5, 80); TFTscreen.text("%", w/2+5, 100); TFTscreen.text("C", w/2+5, 120); broadcast.Temperature = 0; broadcast.Pressure = 0; broadcast.Humidity = 0; broadcast.dewPoint = 0; TFTPrint(); } void loop(){ if(ET.receiveData()){ if(broadcast.ID == 1) TFTPrint(); /* Serial.println(broadcast.Temperature); Serial.println(broadcast.Pressure); Serial.println(broadcast.Humidity); Serial.println(broadcast.dewPoint); Serial.println(); */ } } void changes(int size, int x, int y, bool up, bool clear = false) { if(clear) TFTscreen.stroke(0, 0, 0); else { changes(size, x, y, !up, true); TFTscreen.stroke((up)?0:255, 0, (up)?255:0); } if((size%2) == 0) size++; while(size > 0) { TFTscreen.line(x, y, x+(size--), y); ++x, (up)?--y:++y, --size; } /* while(size > 0) { TFTscreen.line(x, y, (up)?x+size-1:x, (up)?y:y+size-1); ++x, ++y, --size; } */ } int x_center(int w, int length, int size) { return floor((w-length*(size*5)+size*2)/2); } int x_alignment_right(int w, int length, int size) { return ceil(w-length*(size*5)+size*2); } void TFTPrint() { size = 3; // ================================================================================== // Вывод показаний температуры // ================================================================================== if(broadcast.Temperature != Log_Temperature) { TFTscreen.setTextSize(size); // Затираем устаревшие данные String info = String(Log_Temperature); info.concat(" C"); if(Log_Temperature > 0) info = "+"+info; info.toCharArray(Temperature, info.length()+1); TFTscreen.stroke(0, 0, 0); TFTscreen.text(Temperature, x_center(w, info.length()+1, size), 35); // Выводим новые показания info = String(broadcast.Temperature); info.concat(" C"); if(broadcast.Temperature > 0) info = "+"+info; info.toCharArray(Temperature, info.length()+1); // Меняем цвет значения температуры в зависимости от самой температуры int r, g = 0, b; if(broadcast.Temperature > 0) { r = map(broadcast.Temperature, 0, 40, 255, 150); // Красный b = map(broadcast.Temperature, 0, 40, 30, 0); // Изменяем оттенок для более наглядного перехода через ноль } else { r = map(broadcast.Temperature, -40, 0, 0, 30); // Изменяем оттенок для более наглядного перехода через ноль b = map(broadcast.Temperature, -40, 0, 150, 255); // Синий } TFTscreen.stroke(b, g, r); // ВНИМАНИЕ: в библиотеке перепутаны позиции цветов, место RGB используется BGR! TFTscreen.text(Temperature, x_center(w, info.length()+1, size), 35); } size = 1; // ================================================================================== // Вывод показаний давления // ================================================================================== if(broadcast.Pressure != Log_Pressure) { TFTscreen.setTextSize(size); // Затираем устаревшие данные info = String(Log_Pressure); info.toCharArray(Pressure, info.length()); TFTscreen.stroke(0, 0, 0); TFTscreen.text(Pressure, x_alignment_right(w/2-5, info.length(), size), 80); // Выводим новые показания info = String(broadcast.Pressure); info.toCharArray(Pressure, info.length()); TFTscreen.stroke(255, 255, 255); TFTscreen.text(Pressure, x_alignment_right(w/2-5, info.length(), size), 80); changes(10, 106, 85, (broadcast.Pressure > Log_Pressure)?true:false); } else { changes(10, 106, 85, true, true); changes(10, 106, 85, false, true); } // ================================================================================== // Вывод показаний влажности // ================================================================================== if(broadcast.Humidity != Log_Humidity) { TFTscreen.setTextSize(size); // Затираем устаревшие данные info = String(Log_Humidity); info.toCharArray(Humidity, info.length()); TFTscreen.stroke(0, 0, 0); TFTscreen.text(Humidity, x_alignment_right(w/2-5, info.length(), size), 100); // Выводим новые показания info = String(broadcast.Humidity); info.toCharArray(Humidity, info.length()); TFTscreen.stroke(255, 255, 255); TFTscreen.text(Humidity, x_alignment_right(w/2-5, info.length(), size), 100); changes(10, 106, 105, (broadcast.Humidity > Log_Humidity)?true:false); } else { changes(10, 106, 105, true, true); changes(10, 106, 105, false, true); } // ================================================================================== // Вывод показаний точки росы\инея // ================================================================================== if(broadcast.dewPoint != Log_dewPoint) { TFTscreen.setTextSize(size); // Затираем устаревшие данные info = String(Log_dewPoint); info.toCharArray(dewPoint, info.length()); TFTscreen.stroke(0, 0, 0); TFTscreen.text(dewPoint, x_alignment_right(w/2-5, info.length(), size), 120); // Выводим новые показания info = String(broadcast.dewPoint); info.toCharArray(dewPoint, info.length()); TFTscreen.stroke(255, 255, 255); TFTscreen.text(dewPoint, x_alignment_right(w/2-5, info.length(), size), 120); changes(10, 106, 125, (broadcast.dewPoint > Log_dewPoint)?true:false); } else { changes(10, 106, 125, true, true); changes(10, 106, 125, false, true); } // Обновляем значения в логах для последующего сравнения показаний Log_Temperature = broadcast.Temperature; Log_Pressure = broadcast.Pressure; Log_Humidity = broadcast.Humidity; Log_dewPoint = broadcast.dewPoint; }

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

Как мне показалось, дизайн это та часть проекта, которая отнимает большую часть времени!

Скрытый текст

Часть данных сфабрикованы для отображения некоторых элементов дизайна.

Артефакты на дисплее, это пыль и прочая грязь скопившаяся за долго время нахождения дисплея в... где то там, ... ну там, не помню откуда его достал! Отстаньте!

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

  1. x_center
  2. x_alignment_right

Первая производит центровку текста, а вторая выравнивание по правой части указанной зоны. Все вычисления производятся относительно размеров заданного текста, исходя из выражения 1 size = 1PX х 1PX сегмента шрифта.

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

Кстати, цвет и оттенок основной температуры изменяется в зависимости от самой температуры. Довольно спорное решение, но на мой взгляд, визуально комфортное. Я некоторое время бился над ней, и понял, что значения в функции stroke , объекта TFT дисплея, указаны в неверном порядке. BGR место RGB . Это ошибка разработчика, ну или я что-то не понимаю.

PS : Все довольно интересно, но на мой взгляд заслуживает дальнейшего развития. Чем и займемся через какое то время.

Введение

Приветствую всех, сегодня хочу рассказать вам о небольшой подпрограмме или утилите, кому как удобней, под названием «Монитор порта». Если вы уже знакомы с Arduino IDE то вы не раз с ней сталкивались и догадались о чем пойдет речь. Но не торопитесь уходить, вам точно будет что прочесть, так как мы пройдемся от основ до полного разбора ее работы.

И так, «Монитор порта» это небольшая подпрограмма Arduino IDE, предназначенная для приёма-передачи данных из-в Arduino. Ввиду того, что Arduino IDE не имеет никаких средств отладки скетчей, это единственное средство проверить все ли работает верно, и так как надо.

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

Саму форму «Монитора порта» можно разбить на три части: верхняя , центральная и нижняя .

  1. В верхней части расположено поле ввода, в которое пользователь может вписать те данные, которые он хочет отправить в Arduino. Это может быть как текст, так и цифры (подробнее о формате передачи я расскажу чуть ниже). По кнопке «Отправить» как вы уже догадались, подпрограмма отправляет введённые данные в COM-порт и далее в Arduino.
  2. В центральной же части расположено текстовое поле, в которое выводиться вся информация, полученная из COM-порта, т.е. то, что было отправлено из Arduino.
  3. В нижней части формы, расположены дополнительные настройки, а именно:
  • «Автопрокрутка» - Удобна в том случае, когда вы хотите видеть всегда свежую информацию, полученную из Arduino. Центральная часть автоматически пролистывается в самый низ поля.
  • «Постфикс» - Выпадающий список, в котором можно выбрать один из вариантов постфикса, т.е. когда вы нажимаете кнопку «Отправить» в верхней части формы, к данным которые вы ввели в поле, будут добавлены еще несколько байт, какие читайте ниже.
  • «Скорость» - Выпадающий список, в котором необходимо выбрать скорость передачи данных в бодах.

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

Передача данных из Arduino в «Монитор порта»

Начнем мы с Arduino.

Для того чтобы передать данные из Arduino в «Монитор порта» необходимо в функции setup() проинициализировать класс Serial вызвав его метод begin попутно передав скорость и настройки COM-порта.

Синтаксис.

Serial.begin(скорость, настройки);

  • Где скорость - это скорость передачи данных бод в секунду.
  • В настройках (не обязательный параметр) же указывается, сколько информационных бит в байте, а также количество стоповых бит и добавлять ли бит четности. По умолчанию если не передать настройки (а они нам не нужны в 99.9% случаев), класс Serial возьмёт стандартные настройки, а именно:
  1. Количество информационных бит в байте - 8.
  2. Количество стоповых бит - 1.
  3. Бит четности - Без бита четности.

Что такое стартовый бит, информационный бит, бит четности, почему скорость измеряется в бодах, я расскажу вам в другой статье, иначе переварить такой большой объём информации, а статья получиться не маленькая, будет не просто. Сейчас же мы будем пользоваться стандартными настройками, где скорость равна 9600 бод в секунду, а настройки Arduino будет выбирать автоматически.

Итого у нас получилось следующее:

Void setup() { // Инициализируем (запускаем) класс Serial вызывая его метод begin указывая скорость 9600 бод в секунду. Serial.begin(9600); }

После того как все готово, отправить данные можно используя методы print и println того же класса Serial.

Void loop() { // Отправляем «Hellow World!». Serial.print("Hellow World!"); // Отправляем «Hellow World!» включая спецсимволы, означающие переход на новую строку. Serial.println("Hellow World!"); // Спим одну секунду. delay(1000); }

Таким образом, в мониторе порта мы будем видеть строку, состоящую из двух «Hellow World!» каждую секунду (см. фото выше ).

Класс Serial содержит и другие методы для работы с монитором порта, но, как правило, методов print и println достаточно в большинстве случаев.

Получение данных отправленных из «Монитора порта» в Arduino

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

Поможет нам с этим все тот же класс Serial и его метод readString .

И так по команде «HIGH » мы будем включать светодиод, а по команде «LOW » будем выключать.

Для того чтобы отправить команду, необходимо в поле расположенном в верхней части формы набрать текст, и нажать кнопку «Отправить». Получать команды мы будем при помощи упомянутого метода readString.

Итого у Вас должно получиться следующее.

Void setup() { Serial.begin(9600); pinMode(13, OUTPUT); } void loop() { // Получаем команды на включение-выключение светодиода на 13 порту. String str = Serial.readString(); // Если команда «HIGH» то включаем светодиод. if (str == "HIGH") digitalWrite(13, HIGH); // Если команда «LOW» то выключаем светодиод. if (str == "LOW") digitalWrite(13, LOW); // Спим 1 секунду. delay(1000); }

Вот так, без особых мучений можно создать N-е количество команд и обрабатывать их в Arduino.

Формат передачи данных

Напоследок, мы поговорим о том, в каком же виде происходит обмен информацией между «Монитором порта» и Arduino. Не смотря на то, какие данные вы передаете из Arduino в монитор порта, все они конвертируются в строку формата ASCII (см. таблицу ), то же самое происходит и при отправке данных из монитора порта в Arduino. Т.е. отправив число десять, вы на самом деле отправляете не число, а строку, состоящую из двух символов, символ единицы и символ нуля.

К примеру, отправив один байт равный 128, на самом деле монитор порта получит три байта.

  • Байт 49 - Символ единицы в кодировке ASCII.
  • Байт 50 - Символ двойки.
  • Байт 56 - Символ восьмерки.

При выполнении метода println мы получим и вовсе 5 байт. Где первые три будут описанные выше, а два дополнительных это байт CR (13) и LF(10), означающие перевод на новую строку.

Посмотрев таблицу символов ASCII, многие заметят, что в ней присутствует символы только латиницы, в следствии чего отправив к примеру "Привет мир!", в мониторе порта мы получим какие то кракозябры. Пользуясь случаем хочу прорекламировать , написанный авторами портала, который понимает не только надписи на Русском, но и на других языках.

И вот теперь прочтя все, что я тут написал, наконец, можно рассказать что такое «Постфикс», выпадающий список в нижней части монитора порта.

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

Например, написав в поле для отправки число 128 и нажав на кнопку «Отправить» мы получим следующее:

  • В постфиксе выбрано «Нет конца строки» - Монитор порта отправит только то что было введено в поле для отправки, не добавляя ничего.
  • В постфиксе выбрано «NL (Новая строка)» - Монитор порта отправит то, что было введено в поле для отправки, и добавит байт LF (10).
  • В постфиксе выбрано «CR (Возврат каретки)» - Монитор порта отправит то, что было введено в поле для отправки, и добавит байт CR (10).
  • В постфиксе выбрано «NL & CR» - Монитор порта отправит то, что было введено в поле для отправки, и добавит два байт CR (13) и LF (10).

На этом все. Пишите в комментариях, что вам не понятно, и в новых статьях мы попробуем расписать это более подробно.

Ну а следующая статья будет о том, как написать свой монитор порта на Delphi.

Успехов вам и удачи.

Пожалуйста, включите javascript для работы комментариев.

У нас в организации развёрнут сервер Zabbix для мониторинга работоспособности серверов и АРМов. Из-за особенностей техпроцесса оборудование «размазано» по нескольким помещениям и разнесено по территории предприятия. Естественно, вместе с основными параметрами компьютеров (работает/не работает) хочется контролировать и микроклимат в серверных. При этом, как обычно, возможности весьма ограничены, и «выбить» значительные средства на сложные системы мониторинга температуры (к ним я отношу и платы управления с термодатчиками для стоечных ИБП APC) - это отдельный квест.

В основной серверной всё просто - установлена одна такая плата (закуплена давным-давно предшественником вместе с основным оборудованием), воткнут APC-шный термодатчик, заведён агент в Заббиксе, всё работает по SNMP. Скучно:) На мониторинг удалённой аппаратной оборудования нет, средств тоже - см. выше. Поэтому было решено проявить смекалку, сэкономить бюджет и заодно прокачать новый навык путём конструирования простого и дешёвого «наколенного» решения, вписывающегося, тем не менее, в существующую инфраструктуру мониторинга Zabbix.

Необходимые компоненты:

  • Основа системы - Arduino Nano V3
  • Модуль локальной сети (ethernet-shield)
  • И, собственно, цифровой датчик температуры на базе DS18B20
Общая стоимость компонентов - $10 с доставкой.

Сборка устройства не составляет труда. Сетевой модуль надевается на основную плату «бутербродом», термодатчик припаивается к его пинам. Подключение датчика: красный +5 В, чёрный - земля, жёлтый - данные; между +5V и Data припаиваем подтягивающий резистор 4,7 кОм.

Пин для данных выбирается с учётом пинов, используемых сетевым модулем (D10 – SS; D11 – MOSI; D12 – MISO; D13 – SCK; D2 – IRQ).

Грабли: в прототипе устройства столкнулся с конфликтом - данные о температуре выдавались случайным образом, «через два на третий». Причиной оказалось то, что я прицепил термодатчик на пин 2, который, как потом нашёл на просторах интернета, используется сетевым модулем для генерации прерывания при поступлении пакета. Переставил на 4-й - заработало как часы.

После сборки аппаратной части переходим к программной.

Устройство будет работать в сети и притворяться заббикс-агентом, для этого ему нужен MAC и IP-адрес. Решаем, как удобнее - жёстко зашить при программировании, генерировать MAC из адреса температурного датчика и получать IP по DHCP, и т.д. Я пошёл по простейшему пути и захардкодил оба параметра.

Протокол обмена с заббикс-сервером описан в документации . Наше устройство будет откликаться на две команды - agent.ping и env.temp (здесь оставлен простор для дальнейшего творчества, можно привязать любой из модулей расширения, доступных для ардуино - хоть датчик влажности, хоть освещённости - да что душе угодно). На все остальные команды оно будет ругаться отвечать стандартным ответом, понятным заббикс-серверу.

Для тех, кто начинает с нуля (как я) - программирование Arduino выполняется с помощью Arduino IDE , установка и настройка которой элементарны. Для работы компонентов необходимы библиотеки UIPEthernet и OneWire, которые устанавливаются и подключаются к проекту через меню Скетч - Подключить библиотеку - Управлять библиотеками…
Если у вас будут другие компоненты (например, сетевой модуль не на enc28j60, а на другом чипе) - понадобятся и другие библиотеки!

Код работы с сетевым модулем и с датчиком температуры - типовой, из интернета, с некоторыми допущениями и упрощениями.

После заливки кода в контроллер и подключения ethernet-кабеля проверяем из консоли:

$ zabbix_get -s 192.168.4.5 -k agent.ping 1 $ zabbix_get -s 192.168.4.5 -k env.temp 23.12 $ zabbix_get -s 192.168.4.5 -k bla-blah ZBX_NOTSUPPORTED
Грабли: выложенная на zabbix.com скомпилированная версия zabbix_get для Windows устарела и использует другой протокол (с заголовком ZBXD\x01 в запросе сервера). Линуксовая версия актуальна и протокол соответствует приведенному коду.

Всё работает, как и задумано. В админке заббикса создаём новый хост с выбранным IP, в нём - два ключа, Numeric (unsigned) agent.ping и Numeric (float) env.temp, наслаждаемся работой. Графики, триггеры - всё как обычно.

Питание устройства - через родной USB. Корпус - по желанию: подходящая пластиковая коробочка, термоусадка, синяя изолента.

Разрешение датчика - примерно 0.06 (точнее, 1/16) °С, точность - при погружении в таящий снег показал 0.19 °С (может, опустился бы и ниже, но снега было мало и он весь быстро растаял). Считаю, для устройства стоимостью 10 долларов и описанных целей - более чем достаточно.

Скетч

#include #include byte mac = { 0xDE, 0x05, 0xB6, 0x27, 0x39, 0x19 }; // random MAC byte ip = { 192, 168, 4, 5 }; // IP address in local network String readString = String(20); byte addr; OneWire ds(4); // DS18B20 at pin 4 EthernetServer server(10050); // Zabbix port void setup() { Ethernet.begin(mac, ip); server.begin(); ds.search(addr); } void loop() { byte data; float celsius; readString = ""; if (EthernetClient client = server.available()) { while (client.connected()) { if (client.available()) { char c = client.read(); if (c == "\n") // end of query from zabbix server { client.print("ZBXD\x01"); // response header if (readString == "agent.ping") { byte responseBytes = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, "1"}; client.write(responseBytes, 9); } else if (readString == "env.temp") { ds.reset(); ds.select(addr); ds.write(0x44); // start conversion with regular (non-parasite!) power delay(1000); ds.reset(); ds.select(addr); ds.write(0xBE); // read Scratchpad data = ds.read(); data = ds.read(); int16_t raw = (data << 8) | data; celsius = (float)raw / 16.0; byte responseBytes = {(byte) String(celsius).length(), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; client.write(responseBytes, 8); client.print(celsius); } else { byte responseBytes = {0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; client.write(responseBytes, 8); client.print("ZBX_NOTSUPPORTED"); } break; } else if (readString.length() < 20) { readString = readString + c; } } } delay(10); client.stop(); } }

Когда узнал о народном мониторинге, о возможности быть частью этой системы мониторинга погоды, меня охватил азарт, и была сделана простенькая метеостанция на ардуино нано и ethernet модуле.
Передавала она на сайт температуру, давление и влажность. Работала худо бедно, иногда подвисала, необходимо было перезагружать. Потом сдох датчик влажности, и я занялся поиском альтернативного устройства. Долго поглядывал на esp8266, но знал о ней очень немного. Останавливало то, что код для нее надо было писать в незнакомой мне среде esplorer на незнакомом мне языке. Здесь уже публиковался подобного устройства, устройство работало с условно бесплатной прошивкой от Homes Smart, такое решение меня не устроило. Позднее я узнал, что esp8266 можно вполне успешно подружить с arduinoIDE. Я начал усиленно гуглить, и в результате догуглился до устраивающих меня результатов.
Надеюсь, статья окажется полезной. Думаю, ее можно расценивать как обзор-руководство по сборке.

NodeMcu это своего рода отладочная плата для esp8266 (в моем понимании). С помощью нее удобно делать прототипы устройств на макетных платах. Удобно заливать прошивку из ArduinoIDE, все происходит автоматически. Естественно, необходимо ArduinoIDE к работе с esp8266. Подготовив рабочую среду сразу набросал схему, это было не сложно.
Датчик давления и температуры bmp180 подключен по i2c к SCL(gpio05) и SDA(gpio04).
Датчик влажности и температуры dht22 подключен к gpio02
Дисплей 1602 с припаянным i2c адаптером так же посажен на scl и cda, параллельно с датчиком давления.
Питание датчиков 3.3в, питание дисплея 5в.

Фоторезистор подключен к пину А0, он управляет яркостью дисплея, чтобы в ночи дисплей не светился слишком ярко. Питанием подсветки заведует gpio14, на котором реализован программный ШИМ. После транзисторного каскада питание подсветки подходит непосредственно к пятачку А (анод) на дисплее. он находится рядом со светодиодом. Со spi адаптера дисплея необходимо снять перемычку, которая отвечает за подсветку.

Фото прототипа





Схему и печатку приложу в конце.

Сложнее было с кодом.

//ардуино 181, либы схоронил #include #include #include #include #include LiquidCrystal_I2C lcd(0x27, 16, 2); #include #include #include #include #define debug true // вывод отладочных сообщений #define postingInterval 300000 // интервал между отправками данных в миллисекундах (5 минут) #define DHTPIN 2 // dht на gpio02 // Uncomment the type of sensor in use: //#define DHTTYPE DHT11 // DHT 11 #define DHTTYPE DHT22 // DHT 22 (AM2302) //#define DHTTYPE DHT21 // DHT 21 (AM2301) // Lib instantiate DHT dht(DHTPIN, DHTTYPE); Adafruit_BMP085 bmp; /**/unsigned long lastConnectionTime = 0; // время последней передачи данных /**/String Hostname; //имя железки - выглядит как ESPAABBCCDDEEFF т.е. ESP+mac адрес. float dhttemp; // буферная переменная для хранения температуры от dht22 int dhthum; // буферная переменная для хранения влажности от dht22 int topwm; // хранит значение для шима яркости дисплея void wifimanstart() { // Волшебная процедура начального подключения к Wifi. // Если не знает к чему подцепить - создает точку доступа ESP8266 и настроечную таблицу http://192.168.4.1 // Подробнее: https://github.com/tzapu/WiFiManager WiFiManager wifiManager; wifiManager.setDebugOutput(debug); wifiManager.setMinimumSignalQuality(); if (!wifiManager.autoConnect("ESP8266")) { if (debug) Serial.println("failed to connect and hit timeout"); delay(3000); //reset and try again, or maybe put it to deep sleep ESP.reset(); delay(5000); } if (debug) Serial.println("connected..."); } void setup() { // pinMode(14, OUTPUT); // gpio14 будет шимить подсветку дисплея Hostname = "ESP"+WiFi.macAddress(); Hostname.replace(":",""); Serial.begin(115200); // инициализация экрана lcd.begin(4, 5); // sda=gpio04, scl=gpio05 lcd.backlight(); // инициализация датчика температуры и давления bmp180 dht.begin(); if (!bmp.begin()) // если датчик не обнаружен, сообщаем об этом в компорт и на дисплей { Serial.println("Could not find BMP180 or BMP085 sensor at 0x77"); lcd.clear(); lcd.print("BMP180 FAILED"); while (1) {} } WiFi.hostname(Hostname); wifimanstart(); Serial.println(WiFi.localIP()); Serial.println(WiFi.macAddress()); Serial.print("Narodmon ID: "); Serial.println(Hostname); // выдаем в компорт мак и айпишник железки, так же выводим на дисплей lcd.clear(); lcd.setCursor(0, 0); lcd.print(WiFi.localIP()); lcd.setCursor(0, 1); lcd.print(Hostname); lastConnectionTime = millis() - postingInterval + 15000; //первая передача на народный мониторинг через 15 сек. } void WriteLcdTemp (void){ // заполнение дисплея. происходит каждые 5 минут после получения данных с датчиков lcd.clear(); lcd.setCursor(1, 0); lcd.print("T1 "); lcd.setCursor(4, 0); lcd.print(bmp.readTemperature()); lcd.setCursor(8,0); lcd.print(" P "); lcd.setCursor(11,0); lcd.print(bmp.readPressure()/133.3); lcd.setCursor(1, 1); lcd.print("T2 "); lcd.setCursor(4, 1); lcd.print(dhttemp); lcd.setCursor(8,1); lcd.print(" H "); lcd.setCursor(11,1); lcd.print(dhthum); lcd.setCursor(13,1); lcd.print("%"); } bool SendToNarodmon() { // Собственно формирование пакета и отправка. WiFiClient client; String buf; buf = "#" + Hostname + "#ESP_YOBA" + "\r\n"; // заголовок И ИМЯ, которое будет отображаться в народном мониторинге, чтоб не палить мак адрес dhttemp=dht.readTemperature(); // сохравняем в буферные переменные данные с dht22, чтобы удобно было оперировать dhthum=dht.readHumidity(); buf = buf + "#T1#" + String(bmp.readTemperature()) + "\r\n"; //температура bmp180 buf = buf + "#T2#" + String(dhttemp) + "\r\n"; //температура dht22 buf = buf + "#H1#" + String(dhthum) + "\r\n"; //влажность buf = buf + "#P1#" + String(bmp.readPressure()) + "\r\n"; //давление buf = buf + "##\r\n"; // закрываем пакет if (!client.connect("narodmon.ru", 8283)) { // попытка подключения Serial.println("connection failed"); lcd.clear(); lcd.print("connect failed"); return false; // не удалось; } else { WriteLcdTemp(); client.print(buf); // и отправляем данные if (debug) Serial.print(buf); while (client.available()) { String line = client.readStringUntil("\r"); // если что-то в ответ будет - все в Serial Serial.print(line); } } return true; //ушло } void loop() { //автояркость topwm=map(analogRead(A0), 0, 1023, 10, 950); analogWrite(14, topwm); delay(100);// нужна, беж делея у меня не подключался к вайфаю if (millis() - lastConnectionTime > postingInterval) {// ждем 5 минут и отправляем if (WiFi.status() == WL_CONNECTED) { // ну конечно если подключены if (SendToNarodmon()) { lastConnectionTime = millis(); }else{ lastConnectionTime = millis() - postingInterval + 15000; }//следующая попытка через 15 сек }else{ lastConnectionTime = millis() - postingInterval + 15000; Serial.println("Not connected to WiFi"); lcd.clear(); lcd.print("No WiFi");}//следующая попытка через 15 сек } yield(); // что за команда - фиг знает, но ESP работает с ней стабильно и не глючит. }

Что умеет устройство?
Устройство производит измерение температуры, давления (датчик bmp180) и влажности (датчик dht22), отображает показания на дисплее, и отправляет на сайт народного мониторинга.
При первом после прошивки включении, или при отсутствии знакомых wifi сетей устройство прикидывается точкой доступа открытого типа с именем ESP8266.

В com порт устройство шлет это:

Необходимо подключиться с телефона, ноутбука или планшета к точке с именем ESP8266, и пройти по адресу 192.168.4.1
Откроется такая страничка:


Нажимаем кнопку Configure Wifi, и попадаем на такую страничку


Далее выбираем свою сеть, вводим пароль, жмем Save. Готово, устройство само перезагружается, и начинает работать. Сначала на дисплей и в com порт выводится ip адрес и mac адрес устройства с префиксом ESP, его надо использовать в качестве id датчика при регистрации на сайте народного мониторинга, а через 15 секунд на дисплей, в com порт, и на сайт народного мониторинга выводятся первые показания с датчиков.
T1 - температура с bmp180
T2 - температура с dht22
P - атмосферное давление. На дисплей выводится в мм. рт. ст., на народный мониторинг идет в не помню каких единицах, преобразуется в мм. рт. ст. автоматически.
Н - влажность в процентах.


В строчках Т2 и Н ересь, потому что не подключен датчик dht22

В таком виде конструкция проработала пару недель, пока я потихоньку продумывал готовое устройство.
Готовое устройство решил собирать на модуле (такой же стоит на nodemcu)
Быстренько развел и вытравил плату.




Код менять не пришлось вообще. Номера портов для датчиков те же, необходимо только подключить голую esp8266 по типовой схеме, чтобы она могла работать, и можно было заливать прошивку. 3.3 вольта получил с помощью стабилизатора ams 1117 3.3v.
Питается устройство от телефонной зарядки 5в. 2А. Но и одного ампера, думаю, будет достаточно.


Для прошивки вывел отдельный 3-pin разъем с контактами RX, TX и GND.

Прошивал готовое устройство с помощью адаптера usb - uart. Плюс его в том, что на нем имеется переключатель 5v-3.3v, хотя знающие ребята говорят, что этот переключатель работает только для питания, а уровень rx и tx не меняется. Но у меня все работало без преобразования уровней, наверное потому, что esp8266 толерантна к 5v на rx tx, хоть и работает от 3.3.
Так же на готовой плате был предусмотрен переключатель для ввода в режим прошивки (красный ползунковый, нижний ползунок для ввода в режим прошивки. Верхний ползунок я подключил на gpio12, на всякий случай, возможно для переключения режимов индикации на будущее, а пока он не задействован. Можно не мудрить, и просто поставить перемычку между gpio0 и GND)

Для прошивки нужно подключить usb-uart преобразователь таким образом:
esp rx - uart tx
esp tx - uart rx
esp gnd - uart gnd

Затем подключить gpio0 к земле с помощью нижнего ползунка на переключателе, нажать кнопку «Reset» на плате (находится сверху), в arduinoIDE выбрать нужный com port, скорость загрузки (115200 для большинства плат esp8266), и нажать кнопку «Загрузка».
После окончания загрузки устройство начнет работать, переключатель прошивки нужно возвратить в положение OFF, иначе при следующей перезагрузке или отключении питания устройство опять войдет в режим прошивки.

Вспомним сдохший датчик dht22. Симптомы - постоянно показывает влажность 99.9, в сухую погоду может показывать меньше. Сдох он осенью 2016 года, исправно проработав всю весну и лето. Была неделя постоянных дождей, и мне на электропочту свалилось письмо от народного мониторинга, что, мол, ваш датчик несколько дней подряд показывает одно и то же. Я не стал менять датчик, а просто спрятал его из общего доступа до недавнего момента.
Возможной причиной помирания считаю тесный корпус. В качестве его основы было использовано яйцо от киндерсюрприза. Был разработан новый, более технологичный и просторный корпус из подрозетника и заглушки канализационной трубы. Вот оба корпуса рядом:


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

схема и внешний вид




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

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

В собранном виде

Испытание влажностью пройдено успешно. Датчик жив.

Это был полезный опыт для меня, я наконец то пощупал esp8266, и получил стабильно работающее полезное мне устройство.
Надеюсь кому то пригодится эта статья. Если возникнут трудности или вопросы, пишите в личку, отвечу.

Планирую купить +121 Добавить в избранное Обзор понравился +108 +214