Решаем изначальную задачу убрать выпадение конденсата в подвалах путем создания контроля влажности подвала на Arduino pro mini.
Есть в загородном доме два подвала которые гидроизолированы от земли (нет поступления воды из грунта).
Зимой на стенах появляется конденсат, предметы покрываются влагой. Температура в подвалах не опускается ниже 0…+2 градусов, т.е. вода не замерзает. Это достигнуто утеплением стен подвала снаружи ЭППС и нагревом пола подвала (он тепло не изолирован) землей.
1. Принцип работы
Идея контроля влажности подвала была подсмотрена на geektimes.ru. Вся идея состоит в том чтобы измерить температуру и относительную влажность в подвале и на улице, на основании температуры и относительной влажности рассчитать абсолютную влажность и принять решение о включении вытяжного вентилятора в подвале. Теорию для расчета мы расскажем в следующих публикациях.
2. Выбор элементной базы
В качестве контроллера было выбрано Arduino Pro Mini. Это мой второй проект (первый – контроль работы теплового насоса был сделан на uno). Изучив предлагаемые контроллеры Arduino пришел к выводу что существует всего три варианта:
- Arduino DUE (отдельная песня и мне кажется тупик),
- Arduino MEGA,
- все остальное (сильно схожие контроллеры на одном чипе 328).
Задача достаточно простая, по этому выбор пал на Arduino Pro Mini (не на мегу). В pro mini был прошит загрузчик optiboot 5 версии, это дало возможность использования сторожевого таймер и увеличением максимального размера кода.
Устройство индикации – до этого были испытаны монохромные дисплеи 4х20 символов и графический 128х64 точки на 9372. Появилось желание подключить дисплей цветной ЖК. В виду малого количества ножек у МК было использовано подключение по SPI . Ebay – имеет большое количество предложений на контроллере ILI9341 с размерами диагонали 1.8-2.8 дюйма и разрешением 320х240 точек. В эту конструкцию был куплен дисплей 2.4 дюйма.
Т.к. устройство будет устанавливаться в подвале то для наблюдения я хочу сделать выносной пульт со связью по радиоканалу. Для организации радиоканала были использованы модули на основе nrf24. Планируется система типа “умного дома” каждый блок (а сейчас есть уже два блока в подвалах) посылает пакет о своем состоянии в головное устройство.
Исполнительное устройство
В начале был использован классический одно канальный релейный шилд для ардуино. Корпус проектировался под него (есть даже выступы для крепления). После установки устройства в подвале был выявлен косяк – зависание контроллера дисплея при выключении вентилятора (25 вт). Сразу были увеличены конденсаторы по питанию, поставлен LC фильтр по 5 вольтам, дисплей был переведен на питание 3.3 вольта от внешнего стабилизатора (он уже был и использовался для питания nrf24). Все эти меры ничего не дали.
Затем возникла идея сброса контроллера дисплея при зависании. Программный сброс обеспечил. А вот как читать регистры дисплея так и не разобрался (типа прочел регистр все ок не прочел надо перегружать). Был организован периодический сброс – раз в час. Решение кривое. Все это делалось за городом из того что было.
Решение меня не удовлетворило. Пришлось везти блок в Москву, там было установлено твердотельное реле Sharp S202S02 ( с зеро кросс блоком) и помехо-гасящий конденсатор на 0.1 мкф. После этого устройство заработало без зависаний.
Используется датчик температуры и влажности DHT22, штатное включение, подтягивающий резистор 5 кОм, длина провода до 6 метров (что испробовал). Для согласования уровней 5 вольт ардуино и дисплея используются делители из резисторов 2к+4к. Согласования уровней для nrf24 не требуется.
3. Программа
Скетч, т.е. программа целиком ниже:
#include <SPI.h> #include <EEPROM.h> #include <avr/wdt.h> #include <leOS.h> // Шедуллер задач #include <dht.h> // Дачик влажности и температуры #include "Ucglib.h" // справка <a href="https://code.google.com/p/ucglib/wiki/" rel="nofollow">https://code.google.com/p/ucglib/wiki/</a> #include "rusFont.h" // Русские шрифты #include "nRF24L01.h" // Беcпроводной модуль #include "RF24.h" // Беcпроводной модуль //#define DEBUG // Отладочную информацию в ком порт посылает //#define DEMO // Признак демонстрации - данные с датчиков генерятся данные #define BEEP // Использовать пищалку #define RADIO // Признак использования радио модуля #define dH_OFF 15 // Гистерезис абсолютной влажности в сотых грамма на куб #define dT_OFF 25 // Гистерезис температуры в сотых градуса #define TIME_SCAN_KEY 120 // Время опроса кнопок мсек #ifdef DEMO // Для демо все быстрее и случайным образом #define NUM_SAMPLES 2 // Число усреднений измерений датчика #define TIME_SCAN_SENSOR 2000 // Время опроса датчиков мсек, для демки быстрее #define TIME_PRINT_CHART 8000 // Время вывода точки графика мсек, для демки быстрее #define TIME_HOUR 50000 // Число мсек в часе, для демки быстрее #else #define NUM_SAMPLES 5 // Число усреднений измерений датчика #define TIME_SCAN_SENSOR 3000 // Время опроса датчиков мсек #define TIME_PRINT_CHART 300000 // Время вывода точки графика мсек #define TIME_HOUR 3600000 // Число мсек в часе #endif #define VERSION "Version: 0.51 03/08/15" // Текущая версия #define ID 0x22 // уникально Идентификатор устройства - старшие 4 бита, вторые (младшие) 4 бита серийный номер устройства #define NUM_SETTING 6 // Число вариантов настроек - 1 #define MOTOR_OFF 0 // Мотор выключен #define MOTOR_ON 1 // Мотор включен #define LONG_KEY 2000 // Длительное нажатие кнопки мсек, появляется Экран инфо #define SHORT_KEY 100 // Короткое нажатие кнопки более мсек #define NRF24_CHANEL 100 // Номер канала nrf24 // СИСТЕМАТИЧЕСКИЕ ОШИБКИ ДАТЧИКОВ для ID 0x21 #define TOUT_ERR 40 // Ошибка уличного датчика температуры в сотых долях градуса #define TIN_ERR 40 // Ошибка домового датчика температуры в сотых долях градуса #define HOUT_ERR -30 // Ошибка уличного датчика влажности в сотых долях % #define HIN_ERR +30 // Ошибка домового датчика влажности в сотых долях % // НОГИ к которым прицеплена переферия (SPI используется для TFT и NRF24 - 11,12,13) #ifdef BEEP #define PIN_BEEP 15 // Ножка куда повешена пищалка #endif #define PIN_RELAY 14 // Ножка на которую повешено реле (SSR) вентилятора - аналоговый вход A0 через резистор 470 ом #define PIN_CS 10 // TFT дисплей spi #define PIN_CD 9 // TFT дисплей spi #define PIN_RESET 8 // TFT дисплей spi #define PIN_CE 7 // nrf24 ce #define PIN_CSN 6 // nrf24 cs #define PIN_DHT22a 5 // Первый датчик DHT22 IN ДОМ #define PIN_DHT22b 4 // Второй датчик DHT22 OUT УЛИЦА #define PIN_KEY 3 // Кнопка, повешена на прерывание, что бы ресурсов не тратить #define PIN_IRQ_NRF24 2 // Ножка куда заведено прерывание от NRF24 (не используется) // АЦП ---------------------------------------- const long ConstADC=1126400; // Калибровка встроенного АЦП (встроенный ИОН) по умолчанию 1126400 дальше измеряем питание и смотрим на дисплей Ucglib_ILI9341_18x240x320_HWSPI ucg(/*cd=*/PIN_CD, /*cs=*/PIN_CS, /*reset=*/PIN_RESET); // Аппаратный SPI на дисплей ILI9341 leOS myOS; // многозадачность dht DHT; // Датчики bool infoScreen=false; // Признак отображениея иформационного экрана 1 - на экран ничего не выводится кроме информационного экрана bool flagKey=false; // Флаг нажатия клавиши bool pressKey=false; // Флаг необходимости обработки кнопки unsigned long time_key=0; // Время нажатия копки byte last_error=100; // Предыдущая ошибка<packet.error struct type_setting_eeprom // Структура для сохранения данных в eeprom { byte fStart = 0; // Какой набор настроек используется unsigned long hour_unit; // мото часы блок измеряется в интервалах вывода графика TIME_PRINT_CHART unsigned long hour_motor; // мото часы мотор измеряется в интервалах вывода графика TIME_PRINT_CHART }; volatile type_setting_eeprom settingRAM; // Рабочая копия счетчиков в памяти type_setting_eeprom settingEEPROM EEMEM; // Копия счетчиков в eeprom - туда пишем // пакет передаваемый, используется также для хранения результатов. struct type_packet_NRF24 // Структура передаваемого пакета 32 байта - это максимум { byte id=ID; // Идентификатор типа устройства - старшие 4 бита, вторые (младшие) 4 бита серийный номер устройства int tOut=-5000,tIn=-5000; // Текущие температуры в сотых градуса !!! место экономим int absHOut=55555,absHIn=55555; // Абсолютные влажности в сотых грамма на м*3 !!! место экономим int relHOut=55555,relHIn=55555; // Относительные влажности сотых процента !!! место экономим byte error; // Ошибка разряды: 0-3 первый датчик (00-ок) 4-7 второй датчик (00-ок) byte dH; // Порог включения вентилятора по абсолютной влажностив сотых грамма на м*3 int T; // Порог выключения вентилятора по температуре в сотых градуса bool motor=false; // Статус вентилятора char note[12] = "Arduino Pro"; // Примечание не более 11 байт + 0 } packet; struct type_sensors // структура для усреднения { int num; // сколько отсчетов уже сложили не болле NUM_SAMPLES long sum_tOut=-5000,sum_tIn=-5000; // Сумма для усреднения Текущие температуры в сотых градуса !!! место экономим long sum_relHOut=55555,sum_relHIn=55555; // Сумма для усреднения Относительные влажности сотых процента !!! место экономим int tOut=-5000,tIn=-5000; // Текущие температуры в сотых градуса !!! место экономим int absHOut=55555,absHIn=55555; // Абсолютные влажности в сотых грамма на м*3 !!! место экономим int relHOut=55555,relHIn=55555; // Относительные влажности сотых процента !!! место экономим } sensors; // Массивы для графиков byte tOutChart[120]; byte tInChart[120]; byte absHOutChart[120]; byte absHInChart[120]; byte posChart=0; // Позиция в массиве графиков - начало вывода от 0 до 120-1 byte TimeChart=0; // Время до вывода очередной точки на график. #ifdef RADIO // Радио модуль NRF42l RF24 radio(PIN_CE, PIN_CSN); //определение управляющих ног const uint64_t pipes[2] = { 0xF0F0F0F0E1LL,0xF0F0F0F0D2LL}; bool send_packet_ok=false; // признак удачной отправки последнего пакета #endif /////////////////////////////////////////////////////////////////////////////////////////////////////////// // ПРОГРАММА /////////////////////////////////////////////////////////////////////////////////////////////////////////// void setup(){ #ifdef DEBUG Serial.begin(9600); Serial.println(F("DEBUG MODE")); #ifdef BEEP Serial.println(F("BEEP ON")); #else Serial.println(F("BEEP OFF")); #endif #ifdef RADIO Serial.println(F("RADIO ON")); #else Serial.println(F("RADIO OFF")); #endif #endif reset_sum(); #ifdef RADIO // Радио модуль NRF42l radio.begin(); radio.setDataRate(RF24_250KBPS); // выбор скорости radio.setPALevel(RF24_PA_MAX); radio.setChannel(NRF24_CHANEL); //тут установка канала radio.setCRCLength(RF24_CRC_16); // radio.setAutoAck(false); // выключить аппаратное потверждение // radio.enableDynamicPayloads(); // разрешить Dynamic Payloads // radio.enableAckPayload(); // разрешить AckPayload radio.setRetries(15,15); radio.openWritingPipe(0xF0F0F0F0E1LL); radio.openReadingPipe(1,0xF0F0F0F0D2LL); radio.startListening(); #endif #ifdef BEEP pinMode(PIN_BEEP, OUTPUT); // Настройка ноги для динамика digitalWrite(PIN_BEEP, LOW); #endif pinMode(PIN_KEY, INPUT); // Включена кнопка digitalWrite(PIN_KEY, HIGH); pinMode(PIN_RELAY, OUTPUT); // Реле digitalWrite(PIN_RELAY, LOW); pinMode(PIN_DHT22a, OUTPUT); // Датчик 1 digitalWrite(PIN_DHT22a, HIGH); pinMode(PIN_DHT22b, OUTPUT); // Датчик 2 digitalWrite(PIN_DHT22b, HIGH); reset_ili9341(); // сброс дисплея readEeprom(); // Прочитать настройки if (digitalRead(PIN_KEY)==0) // Если при включении нажата кнопка то стираем все { int i; settingRAM.fStart=0; settingRAM.hour_unit=0; settingRAM.hour_motor=0; ucg.setColor(255, 255, 255); print_StrXY(10,50,F("Сброс настроек и счетчиков")); for(i=0;i<3;i++) { delay(1000); ucg.print(F(" .")); } writeEeprom(); // Запись в EEPROM delay(1000); ucg.clearScreen(); } wdt_enable(WDTO_8S); // Сторожевой таймер Для тестов не рекомендуется устанавливать значение менее 8 сек. // Запуск задач по таймеру myOS.begin(); myOS.addTask(measurement,TIME_SCAN_SENSOR); // Измерение attachInterrupt(1, scanKey, CHANGE); // КНОПКА Прерывания по обоим фронтам print_static(); // распечатать таблицу Setting(); // Применить настройки measurement(); // Считать данные #ifdef BEEP beep(100); #endif } void loop() { // Обработка нажатия кнопки if (pressKey==true) // Кнопка { myOS.pauseTask(measurement); // Остановить задачи if (time_key > LONG_KEY) // Длительное нажатие кнопки printInfo(); // Вывод информационного экрана else if (time_key > SHORT_KEY) // Короткое нажатие кнопки { #ifdef BEEP beep(30); // Звук нажатия на клавишу #endif if (infoScreen==true) clearInfo(); // если информационный экран то стереть его else { if (settingRAM.fStart >= NUM_SETTING) settingRAM.fStart=0; // Кольцевой счетчик настроек else settingRAM.fStart++; // В противном случае следующая настройка Setting(); } } flagKey=false; // Подготовка к следующему нажатию time_key=0; pressKey=false; myOS.restartTask(measurement); // Запустить задачи } } void print_static() // Печать статической картинки { cli(); // Заголовок ucg.setColor(0, 0, 180); // ucg.drawBox(0, 0, 320-1, 23); ucg.setColor(250, 250, 250); ucg.setFontMode(UCG_FONT_MODE_TRANSPARENT); print_StrXY(2,19,F("ОСУШИТЕЛЬ ID: 0x")); ucg.print( hex(packet.id >> 4)); ucg.print( hex(packet.id&0x0f)); #ifdef DEMO ucg.print(F(" demo")); #endif // Таблица для данных ucg.setColor(0, 200, 0); ucg.drawHLine(0,25,320-1); ucg.drawHLine(0,25+23*1,320-1); ucg.drawHLine(0,25+23*2,320-1); ucg.drawHLine(0,25+23*3,320-1); ucg.drawHLine(0,25+23*4,320-1); ucg.drawVLine(200-4,25,24+23*3); ucg.drawVLine(260,25,24+23*3); // Заголовки таблиц ucg.setColor(255, 255, 0); print_StrXY(180+30,25+0+18,F("Дом")); print_StrXY(250+20,25+0+18,F("Улица")); print_StrXY(0,25+23*1+18,F("Температура градусы C")); print_StrXY(0,25+23*2+18,F("Относительная влаж. %")); print_StrXY(0,25+23*3+18,F("Абсолют. влаж. г/м*3")); // Графики ucg.setColor(200, 200, 200); ucg.drawHLine(1,240-1,130); ucg.drawVLine(1,135,105); ucg.drawHLine(10+150,240-1,130); ucg.drawVLine(10+150,135,105); print_StrXY(10,135+0,F("Температура")); print_StrXY(20+150,135+0,F("Абс. влажность")); // надписи на графиках print_StrXY(128,154,F("+20")); print_StrXY(135,194,F("0")); print_StrXY(128,233,F("-20")); print_StrXY(296,164,F("15")); print_StrXY(296,194,F("10")); print_StrXY(296,223,F("5")); sei(); } void print_status() // Печать панели статуса Значки на статус панели { if (infoScreen==true) return; // если отображен информационный экран то ничего не выводим cli(); // 1. печать ошибки чтения датчиков print_error_DHT(); // 2. Признак включения мотора if (packet.motor==true) ucg.setColor(0, 240, 0); else ucg.setColor(0, 40, 0); ucg.drawBox(290-32, 5, 14, 14); #ifdef RADIO // 3. Признак удачной передачи информации по радиоканалу if (send_packet_ok==true) ucg.setColor(0, 240, 0); else ucg.setColor(0, 40, 0); ucg.setPrintDir(3); ucg.setFontMode(UCG_FONT_MODE_TRANSPARENT); print_StrXY(290-40,20,F(">>")); ucg.setPrintDir(0); ucg.setFontMode(UCG_FONT_MODE_SOLID); #endif sei(); } void print_error_DHT() // Печать ошибки чтения датчиков выводится при каждом чтении датчика { if (infoScreen==true) return; // если отображен информационный экран то ничего не выводим // 1. печать ошибки чтения датчиков if (packet.error!=last_error) // если статус ошибки поменялся то надо вывести если нет то не выводим - экономия время и нет мерцания { cli(); last_error=packet.error; ucg.setColor(0, 0, 180); // Сначала стереть ucg.drawBox(290, 0, 26, 18); ucg.setPrintPos(290,18); ucg.setFontMode(UCG_FONT_MODE_TRANSPARENT); if (packet.error>0) { ucg.setColor(255, 100, 100); print_StrXY(280,19,F("0x")); ucg.print( hex(packet.error >> 4)); ucg.print( hex(packet.error & 0x0f)); } else { ucg.setColor(200, 240, 0); ucg.print(F("ok")); } sei(); } } // вывод на экран данных (то что меняется) void print_data() { if (infoScreen==true) return; // если отображен информационный экран то ничего не выводим cli(); // Печать значений для дома ucg.setFontMode(UCG_FONT_MODE_SOLID); ucg.setColor(250, 0, 100); // Цвет ДОМА print_floatXY(200+0,25+23*1+18,((float)packet.tIn)/100); print_floatXY(200+0,25+23*2+18,((float)packet.relHIn)/100); print_floatXY(200+0,25+23*3+18,((float)packet.absHIn)/100); ucg.setColor(0, 250, 100); // Цвет УЛИЦЫ print_floatXY(260+4,25+23*1+18,((float)packet.tOut)/100); print_floatXY(260+6,25+23*2+18,((float)packet.relHOut)/100); print_floatXY(260+6,25+23*3+18,((float)packet.absHOut)/100); sei(); } // Печать графика на экране, добавляется одна точка и график сдвигается void printChart() { byte i,x=0; byte tInLast,absHInLast,tOutLast,absHOutLast; // Статистика по моточасам, время ведется в тиках графика а потом пересчитывается в часы при выводе. settingRAM.hour_unit++; if (packet.motor==true) settingRAM.hour_motor++; // Работаем через кольцевой буфер // Добавить новую точку в кольцевой буфер // Температура в доме. диапазон -25 . . . +25 растягиваем на 100 точек tInLast=tInChart[posChart]; // Сохранить точку для стирания на графике if (packet.tIn<=-2500) tInChart[posChart]=0; // Если температура меньше -25 то округляем до -25 if (packet.tIn>=2500) tInChart[posChart]=100; // Если температура больше 25 то округляем до 25 if ((packet.tIn>-2500)&&(packet.tIn<2500)) tInChart[posChart]=((2*packet.tIn)+5000)/100; // внутри -25...+25 растягиваем в два раза // Температура на улице. диапазон -25 . . . +25 растягиваем на 100 точек tOutLast=tOutChart[posChart]; // Сохранить точку для стирания на графике if (packet.tOut<=-2500) tOutChart[posChart]=0; // Если температура меньше -25 то округляем до -25 if (packet.tOut>=2500) tOutChart[posChart]=100; // Если температура больше 25 то округляем до 25 if ((packet.tOut>-2500)&&(packet.tOut<2500)) tOutChart[posChart]=((2*packet.tOut)+5000)/100; // внутри -25...+25 растягиваем в два раза // Абсолютная влажность в доме диапазон от 0 до 20 грамм на кубометр, растягиваем на 100 точек absHInLast=absHInChart[posChart]; // Сохранить точку для стирания на графике if (packet.absHIn>=2000) absHInChart[posChart]=100; else absHInChart[posChart]=(5*packet.absHIn)/100; // внутри 0...20 растягиваем в пять раз // Абсолютная влажность на улицу диапазон от 0 до 20 грамм на кубометр, растягиваем на 100 точек absHOutLast=absHOutChart[posChart]; // Сохранить точку для стирания на графике if (packet.absHOut>=2000) absHOutChart[posChart]=100; else absHOutChart[posChart]=(5*packet.absHOut)/100; // внутри 0...20 растягиваем в пять раз if (infoScreen==false) // если отображен информационный экран то ничего не выводим { cli(); for(i=0;i<120;i++) // График слева на право { // Вычислить координаты текущей точки x в кольцевом буфере. Изменяются от 0 до 120-1 if (posChart<i) x=120+posChart-i; else x=posChart-i; ucg.setColor(0, 0, 0); // Стереть предыдущую точку if (x-1<0) ucg.drawPixel(5+120-i,237-tInLast); else ucg.drawPixel(5+120-i,237-tInChart[x-1]); if (x-1<0) ucg.drawPixel(5+120-i,237-tOutLast); else ucg.drawPixel(5+120-i,237-tOutChart[x-1]); if (x-1<0) ucg.drawPixel(6+120-i+158,237-absHInLast); else ucg.drawPixel(6+120-i+158,237-absHInChart[x-1]); if (x-1<0) ucg.drawPixel(6+120-i+158,237-absHOutLast); else ucg.drawPixel(6+120-i+158,237-absHOutChart[x-1]); // Вывести новую точку if ((tInChart[x]==0)||(tInChart[x]==100)) ucg.setColor(255, 255, 255); else ucg.setColor(250, 0, 100); ucg.drawPixel(5+120-i,237-tInChart[x]); if (absHInChart[x]==100) ucg.setColor(255, 255, 255); else ucg.setColor(250, 0, 100); ucg.drawPixel(6+120-i+158,237-absHInChart[x]); if ((tOutChart[x]==0) || (tOutChart[x]==100)) ucg.setColor(255, 255, 255); else ucg.setColor(0, 250, 100); ucg.drawPixel(5+120-i,237-tOutChart[x]); if (absHOutChart[x]==100) ucg.setColor(255, 255, 255); else ucg.setColor(0, 250, 100); ucg.drawPixel(6+120-i+158,237-absHOutChart[x]); } // Пунктирные линии графика ucg.setColor(100, 100, 100); for(i=1;i<=120;i=i+5) { ucg.drawPixel(5+120-i,237-10); ucg.drawPixel(5+120-i,237-50); ucg.drawPixel(5+120-i,237-90); ucg.drawPixel(6+120-i+158,237-25); ucg.drawPixel(6+120-i+158,237-50); ucg.drawPixel(6+120-i+158,237-75); } sei(); } if (posChart<120-1) posChart++; else posChart=0; // Изменили положение в буфере и Замкнули буфер } // ---ПЕРЕДАЧА ДАННЫХ ЧЕРЕЗ РАДИОМОДУЛЬ ----------------------------- void send_packet() { #ifdef RADIO // Радио модуль NRF42l radio.stopListening(); // Остановить приемник send_packet_ok = radio.write(&packet,sizeof(packet)); #ifdef BEEP if (send_packet_ok==true) beep(40); #endif #ifdef DEBUG if (send_packet_ok==true) Serial.println(F("Packet sending ++++++++++")); else Serial.println(F("Packet NOT sending -----------")); #endif radio.startListening(); // Включить приемник #endif } // Чтение датчика возвращает код ошибки: // DHTLIB_OK 0 // DHTLIB_ERROR_CHECKSUM 1 // DHTLIB_ERROR_TIMEOUT 2 // DHTLIB_ERROR_CONNECT 3 // DHTLIB_ERROR_ACK_L 4 // DHTLIB_ERROR_ACK_H 5 byte readDHT(byte pin) { delay(5); cli(); byte err=-1*DHT.read22(pin); // Чтение датчика sei(); return err; } // Измерение и обработка данных чтение датчиков -------------------------------------------------------------------------------- void measurement() { myOS.pauseTask(measurement); // Обязательно здесь, а то датчики плохо читаются мешает leos wdt_reset(); // Сбросить сторожевой таймер packet.error=readDHT(PIN_DHT22a); // ПЕРВЫЙ ДАТЧИК ДОМ Новый пакет, сбросить все ошибки и прочитать первый датчик #ifdef DEMO DHT.temperature=packet.tIn/100+random(-20,25)/10.0; if (DHT.temperature>20) DHT.temperature=19; if (DHT.temperature<-10) DHT.temperature=-9; DHT.humidity=packet.relHIn/100+(float)random(-5,7); if (DHT.humidity>96) DHT.humidity=90; if (DHT.humidity<1) DHT.humidity=10; packet.error=0; // в Демо режиме // DHT.temperature=3.0; // DHT.humidity=21.0; #endif sensors.tIn=(int)(DHT.temperature*100.0)+TIN_ERR; // Запомнить результаты для суммирования sensors.relHIn=(int)(DHT.humidity*100.0)+HOUT_ERR; #ifdef DEBUG Serial.print(F("Sensor read samples:")); Serial.println(sensors.num); Serial.print(F("IN T="));Serial.print(sensors.tIn);Serial.print(F(" H=")); Serial.print(sensors.relHIn); Serial.print(F(" error=")); Serial.println(packet.error); #endif packet.error=packet.error+16*readDHT(PIN_DHT22b);// ВТОРОЙ ДАТЧИК УЛИЦА ошибки в старшие четыре бита #ifdef DEMO DHT.temperature=packet.tOut/100+random(-40,45)/10.0; if (DHT.temperature>30) DHT.temperature=29; if (DHT.temperature<-30) DHT.temperature=-29; DHT.humidity=packet.relHOut/100+random(-10,12); if (DHT.humidity>96) DHT.humidity=90; if (DHT.humidity<1) DHT.humidity=10; packet.error=0; // в Демо режиме // DHT.temperature=-10.0; // DHT.humidity=40.0; #endif sensors.tOut=(int)(DHT.temperature*100.0)+TOUT_ERR; // Запомнить результаты для суммирования sensors.relHOut=(int)(DHT.humidity*100.0)+HOUT_ERR; #ifdef DEBUG Serial.print(F("OUT T="));Serial.print(sensors.tOut);Serial.print(F(" H=")); Serial.print(sensors.relHOut); Serial.print(F(" error=")); Serial.println(packet.error); #endif print_error_DHT(); // Вывод ошибки чтения датчика при каждом чтении контроль за качеством связи с датчиком if (packet.error==0)// Если чтение без ошибок у ДВУХ датчиков копим сумму для усреднения { sensors.sum_tIn=sensors.sum_tIn+sensors.tIn; sensors.sum_relHIn=sensors.sum_relHIn+sensors.relHIn; sensors.sum_tOut=sensors.sum_tOut+sensors.tOut; sensors.sum_relHOut=sensors.sum_relHOut+sensors.relHOut; sensors.num++; } // набрали в сумме нужное число отсчетов рассчитываем усреднение и выводим if (sensors.num>=NUM_SAMPLES) // Пора усреднять и выводить значения { // вычисление средних значений packet.tIn=sensors.sum_tIn/NUM_SAMPLES; packet.relHIn=sensors.sum_relHIn/NUM_SAMPLES; packet.tOut=sensors.sum_tOut/NUM_SAMPLES; packet.relHOut=sensors.sum_relHOut/NUM_SAMPLES; reset_sum(); // Сброс счетчиков и сумм // вычисление абсолютной влажности packet.absHIn=(int)(calculationAbsH((float)(packet.tIn/100.0),(float)(packet.relHIn/100.0))*100.0); packet.absHOut=(int)(calculationAbsH((float)(packet.tOut/100.0),(float)(packet.relHOut/100.0))*100.0); #ifdef DEBUG Serial.println(F("Average value >>>>>>>>>>")); Serial.print(F("IN T="));Serial.print(packet.tIn);Serial.print(F(" H=")); Serial.print(packet.relHIn); Serial.print(F(" abs H=")); Serial.println(packet.absHIn); Serial.print(F("OUT T="));Serial.print(packet.tOut);Serial.print(F(" H=")); Serial.print(packet.relHOut); Serial.print(F(" abs H=")); Serial.println(packet.absHOut); #endif #ifdef RADIO // Радио модуль NRF42l send_packet(); // Послать данные #endif CheckON(); // Проверка статуса вентилятора print_status(); // панель состояния print_data(); // вывод усредненных значений if ((long)((long)TimeChart*TIME_SCAN_SENSOR*NUM_SAMPLES)>=(long)TIME_PRINT_CHART) // проврека не пора ли выводить график { printChart(); TimeChart=0; // Сдвиг графика и вывод новой точки #ifdef DEBUG Serial.println(F("Point add chart ++++++++++++++++++++")); #endif #ifdef BEEP // beep(50); #endif } else TimeChart++; } myOS.restartTask(measurement); // Пустить задачи } // Функция переводит относительную влажность в абсолютную // t-температура в градусах Цельсия h-относительная влажность в процентах float calculationAbsH(float t, float h) { float temp; temp=pow(2.718281828,(17.67*t)/(t+243.5)); return (6.112*temp*h*2.1674)/(273.15+t); } // Сканирование клавиш ------------------------------------------ void scanKey() { byte key; cli(); key=digitalRead(PIN_KEY); // Прочитать кнопку 0 - нажата if ((key==0)&&(flagKey==false)) // Если кнопка была нажата запомнить время и поставить флаг нажатия { flagKey=true; // Кнопка нажата ждем обратного фронта time_key=millis(); // Время нажатия запомнили } if ((key==1)&&(flagKey==true)) // Если кнопка была отжата { time_key=millis()-time_key; // Рассчитать время нажатия pressKey=true; } sei(); } // Проверка статуса вытяжки, не пора ли переключится void CheckON() { cli(); if (packet.motor==false) // Вентилятор выключен { if (settingRAM.fStart!=MOTOR_OFF) if (settingRAM.fStart==MOTOR_ON) {packet.motor=true; digitalWrite(PIN_RELAY, HIGH);} // Все время включен else if (( packet.tIn>=packet.T )&& ((packet.absHIn-packet.dH)> packet.absHOut)) {packet.motor=true; digitalWrite(PIN_RELAY, HIGH);} // ВКЛЮЧЕНИЕ } else // Вентилятор включен { if (settingRAM.fStart!=MOTOR_ON) if (settingRAM.fStart==MOTOR_OFF) {packet.motor=false; digitalWrite(PIN_RELAY, LOW);} // Все время выключен else if((packet.tIn<=packet.T-dT_OFF)||(packet.absHIn-packet.absHOut<packet.dH-dT_OFF)) {packet.motor=false; digitalWrite(PIN_RELAY, LOW);} // ВЫКЛЮЧЕНИЕ } sei(); } // Вывод информации о настройках и сохрание индекса настроек в eeprom --------------------------------- void Setting() { // Настройка cli(); ucg.setColor(0, 100, 255); ucg.setPrintPos(0,25+0+18); switch (settingRAM.fStart) { case MOTOR_OFF: ucg.print(F("Выключено ")); packet.dH=255;packet.T=255; break; case MOTOR_ON: ucg.print(F("Режим вытяжки ")); packet.dH=0; packet.T=0; break; case 2: ucg.print(F("Включение T>+2 dH>0.2")); packet.dH=20; packet.T=200; break; case 3: ucg.print(F("Включение T>+2 dH>0.4")); packet.dH=40; packet.T=200; break; case 4: ucg.print(F("Включение T>+3 dH>0.3")); packet.dH=30; packet.T=300; break; case 5: ucg.print(F("Включение T>+3 dH>0.5")); packet.dH=50; packet.T=300; break; case 6: ucg.print(F("Включение T>+4 dH>0.6")); packet.dH=30; packet.T=600; break; } writeEeprom(); // Запись в EEPROM CheckON(); // Возможно надо включить мотор print_status(); // панель состояния sei(); } // Вывод float с одним десятичным знаком в координаты x y // для экономии места void print_floatXY(int x,int y, float v) { ucg.setPrintPos(x,y); ucg.print(v,2); ucg.print(" "); // Стереть хвост от предыдущего числа } // Вывод строки константы в координаты x y // для экономии места void print_StrXY(int x,int y, const __FlashStringHelper* b) { ucg.setPrintPos(x,y); ucg.print(b); } void printInfo() // Окно с информацией о блоке, появляется при длительном нажатии на кнопку { infoScreen=true; cli(); ucg.setColor(250, 250, 250); // ucg.drawBox(10, 10, 320-1-20, 240-1-20); ucg.setColor(0, 50, 250); ucg.drawFrame(10+5, 10+5, 320-1-20-10, 240-1-20-10); ucg.setFontMode(UCG_FONT_MODE_TRANSPARENT); ucg.setColor(0, 150, 10); print_StrXY(35,18+15,F("ОСУШИТЕЛЬ на Arduino Pro Mini")); ucg.setColor(0, 50, 50); print_StrXY(10+10,15+17*2,F("1 Напряжение питания В.")); print_floatXY(10+230,15+17*2,readVcc()/1000.0); print_StrXY(10+10,15+17*3,F("2 Температура блока гр.")); print_floatXY(10+230,15+17*3,GetTemp()); print_StrXY(10+10,15+17*4,F("3 Свободная память байт")); ucg.setPrintPos(10+230,15+17*4); ucg.print(freeRam()); print_StrXY(10+10,15+17*5,F("4 Мото часы блока")); ucg.setPrintPos(10+230,15+17*5); ucg.print(settingRAM.hour_unit/(TIME_HOUR/TIME_PRINT_CHART)); print_StrXY(10+10,15+17*6,F("5 Мото часы вентилятора")); ucg.setPrintPos(10+230,15+17*6); ucg.print(settingRAM.hour_motor/(TIME_HOUR/TIME_PRINT_CHART)); print_StrXY(10+10,15+17*7,F("6 Канал NRF24l01+")); ucg.setPrintPos(10+230,15+17*7); ucg.print(NRF24_CHANEL); print_StrXY(10+10,15+17*8,F("7 Гистерезис абс. влажности")); print_floatXY(10+255,15+17*8,(float)dH_OFF/100.0); print_StrXY(10+10,15+17*9,F("8 Гистерезис температуры")); print_floatXY(10+255,15+17*9,(float)dT_OFF/100.0); print_StrXY(10+10,15+17*10,F("9 ERR Т/Н in:")); ucg.print(TIN_ERR); ucg.print(F("/")); ucg.print(HIN_ERR); ucg.print(F(" out:")); ucg.print(TOUT_ERR); ucg.print(F("/")); ucg.print(HOUT_ERR); ucg.setColor(0, 0, 150); print_StrXY(10+10,16+17*11,F("СБРОС - Вкл. при нажатой кнопке.")); ucg.setColor(250,80,80); print_StrXY(10+10,20+21+18*10,F(VERSION)); sei(); } void clearInfo() // Стереть информационный экран { infoScreen=false; last_error=100; // Признак обновления ошибки cli(); ucg.setColor(0, 0, 0); // залить черным ucg.drawBox(10, 10, 320-1-20, 240-1-20); print_static(); Setting(); printChart(); sei(); } // Чтение свободной памяти -------------------------------------------------------------------- int freeRam () { extern int __heap_start, *__brkval; int v; return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); } // Чтение внутреннего датчика температуры --------------------------------------- double GetTemp(void) { unsigned int wADC; double t; sei(); // Должны быть разрешены прерывания ADMUX = (_BV(REFS1) | _BV(REFS0) | _BV(MUX3)); ADCSRA |= _BV(ADEN); delay(20); ADCSRA |= _BV(ADSC); while (bit_is_set(ADCSRA,ADSC)); wADC = ADCW; t = (wADC - 324.31 ) / 1.22; return (t); } // Чтение напряжения питания ---------------------------------------------- long readVcc() { long result; // Read 1.1V reference against AVcc ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); delay(2); // Wait for Vref to settle ADCSRA |= _BV(ADSC); // Convert while (bit_is_set(ADCSRA,ADSC)); result = ADCL; result |= ADCH<<8; result = ConstADC / result; // Back-calculate AVcc in mV return result; } // Запись счетчиков в Eeprom -------------------------------------------------- void writeEeprom() { cli(); eeprom_write_block((const void*)&settingRAM, (void*) &settingEEPROM, sizeof(settingRAM)); sei(); } // Чтение счетчиков из Eeprom -------------------------------------------------- void readEeprom() { cli(); eeprom_read_block((void*)&settingRAM, (const void*) &settingEEPROM, sizeof(settingRAM)); sei(); } void reset_sum() // Сброс счетчиков накоплений { sensors.num=0; // Рассчитать величину усреднения sensors.sum_tOut=0; sensors.sum_tIn=0; sensors.sum_relHOut=0; sensors.sum_relHIn=0; } char hex(byte x) // Функция для вывода в hex { if(x >= 0 && x <= 9 ) return (char)(x + '0'); else return (char)('a'+x-10); } bool reset_ili9341(void) { pinMode(PIN_RESET, OUTPUT); // Сброс дисплея сигнал активным является LOW // digitalWrite(PIN_RESET, HIGH); digitalWrite(PIN_RESET, LOW); delay(100); digitalWrite(PIN_RESET, HIGH); // Дисплей ucg.begin(UCG_FONT_MODE_TRANSPARENT); ucg.setFont(my14x10rus); // ucg.setFont(myfont); // ucg.setRotate90(); ucg.setRotate270(); ucg.clearScreen(); } #ifdef BEEP void beep(int x) // Пищать х мсек { digitalWrite(PIN_BEEP, HIGH); delay(x); digitalWrite(PIN_BEEP, LOW); } #endif
файл фонтов
/ Русский шрифт 10х14 ------------------------------------------------------ const ucg_fntpgm_uint8_t my14x10rus[4157] UCG_SECTION(".progmem.my14x10") = { 0,11,15,0,255,14,3,35,6,248,32,255,0,15,255,14, 0,0,0,0,8,0,0,2,14,14,4,1,0,64,192,192, 192,192,192,192,192,192,128,0,64,192,128,6,5,5,7,0, 9,68,204,204,204,136,10,14,28,11,0,0,8,128,8,128, 17,0,17,0,127,192,17,0,17,0,34,0,34,0,255,128, 34,0,34,0,68,0,68,0,9,14,28,10,0,0,8,0, 8,0,59,128,123,0,200,0,200,0,232,0,107,0,11,128, 9,128,9,128,111,0,238,0,8,0,8,14,14,9,0,0, 97,179,214,102,12,12,24,24,48,48,102,107,205,134,9,14, 28,10,0,0,56,0,124,0,108,0,108,0,108,0,56,0, 56,128,109,128,199,0,194,0,199,0,237,128,124,128,56,0, 2,5,5,3,0,10,128,192,192,192,64,5,14,14,6,0, 0,24,48,96,96,192,192,192,192,192,192,96,96,48,24,5, 14,14,6,0,0,192,96,48,48,24,24,24,24,24,24,56, 48,96,192,7,7,7,8,0,4,146,214,124,16,124,214,146, 8,10,10,10,0,1,8,24,24,24,127,254,24,24,24,16, 4,6,6,5,0,0,112,96,96,64,192,192,8,2,2,9, 0,5,127,254,3,3,3,4,0,0,224,160,224,8,14,14, 9,0,0,3,3,6,6,12,12,24,24,48,48,96,96,192, 192,9,14,28,10,0,0,62,0,127,0,227,128,197,128,197, 128,197,128,201,128,201,128,209,128,209,128,209,128,227,128,127, 0,62,0,9,14,28,10,0,0,12,0,28,0,60,0,124, 0,8,0,4,0,12,0,12,0,8,0,4,0,12,0,12, 0,127,128,255,128,9,14,28,10,0,0,126,0,255,0,195, 128,193,128,1,128,3,128,63,0,126,0,224,0,192,0,192, 0,192,0,223,128,191,0,9,14,28,10,0,0,126,0,255, 0,195,128,1,128,1,128,3,0,58,0,119,0,3,128,1, 128,1,128,3,128,255,0,126,0,9,14,28,10,0,0,2, 0,6,0,14,0,30,0,62,0,118,0,230,0,198,0,251, 128,247,0,6,0,6,0,6,0,4,0,9,14,28,10,0, 0,127,128,127,128,96,0,96,0,96,0,110,0,111,0,3, 128,1,128,1,128,1,128,195,0,255,0,124,0,9,14,28, 10,0,0,31,0,127,0,96,0,192,0,192,0,192,0,222, 0,223,0,195,128,193,128,193,128,227,0,127,0,60,0,9, 14,28,10,0,0,127,128,255,128,0,0,3,0,3,0,6, 0,6,0,12,0,12,0,24,0,24,0,48,0,48,0,32, 0,9,14,28,10,0,0,58,0,119,0,227,128,193,128,193, 128,99,0,54,0,111,0,227,128,193,128,193,128,227,128,119, 0,46,0,9,14,28,10,0,0,60,0,255,0,231,0,195, 128,193,128,193,128,225,128,253,128,125,128,1,128,3,128,7, 0,127,0,252,0,3,11,11,4,0,2,224,160,224,0,0, 0,0,0,224,160,224,4,13,13,5,0,0,112,80,112,0, 0,0,0,0,112,80,112,96,192,9,11,22,10,0,1,1, 128,3,128,15,0,28,0,120,0,224,0,120,0,28,0,15, 0,3,128,1,128,9,6,12,10,0,4,127,128,255,0,0, 0,0,0,127,128,255,0,9,11,22,10,0,1,192,0,224, 0,120,0,60,0,15,0,3,128,15,0,60,0,120,0,224, 0,192,0,8,14,14,9,0,0,116,238,135,3,3,6,14, 56,48,48,0,0,48,48,9,11,22,10,0,1,62,0,65, 0,128,128,154,128,166,128,162,128,162,128,166,128,155,0,64, 0,63,128,9,14,28,10,0,0,252,0,254,0,199,0,195, 128,193,128,193,128,193,128,253,128,253,128,193,128,193,128,193, 128,193,128,129,0,9,14,28,10,0,0,94,0,223,0,195, 128,193,128,193,128,195,128,255,0,255,0,195,128,193,128,193, 128,195,128,223,0,190,0,9,14,28,10,0,0,14,0,63, 0,115,128,97,128,192,0,192,0,192,0,192,0,192,0,192, 0,96,0,112,0,63,128,15,0,9,14,28,10,0,0,238, 0,111,0,99,128,97,128,97,128,97,128,97,128,97,128,97, 128,97,128,97,128,99,128,111,0,238,0,9,14,28,10,0, 0,95,128,223,0,192,0,192,0,192,0,192,0,223,0,222, 0,192,0,192,0,192,0,192,0,223,128,191,0,9,14,28, 10,0,0,95,128,223,0,192,0,192,0,192,0,192,0,223, 0,222,0,192,0,192,0,192,0,192,0,192,0,128,0,9, 14,28,10,0,0,63,128,127,128,225,128,192,0,192,0,192, 0,192,0,207,128,223,128,193,128,193,128,225,128,127,128,62, 0,9,14,28,10,0,0,129,0,193,128,193,128,193,128,193, 128,193,128,223,128,223,128,193,128,193,128,193,128,193,128,193, 128,64,128,8,14,14,10,1,0,254,127,24,24,24,24,24, 24,24,24,24,24,254,127,9,14,28,10,0,0,31,128,63, 128,1,128,1,128,1,128,1,128,1,128,1,128,1,128,1, 128,1,128,195,128,255,0,62,0,9,14,28,10,0,0,65, 128,195,128,199,0,206,0,220,0,216,0,216,0,216,0,216, 0,220,0,206,0,199,0,195,128,65,128,9,14,28,10,0, 0,64,0,192,0,192,0,192,0,192,0,192,0,192,0,192, 0,192,0,192,0,192,0,192,0,255,128,255,0,9,14,28, 10,0,0,193,128,227,128,247,128,247,128,213,128,193,128,213, 128,221,128,221,128,201,128,193,128,193,128,193,128,129,0,9, 14,28,10,0,0,225,0,225,128,241,128,241,128,249,128,217, 128,221,128,205,128,205,128,197,128,197,128,193,128,193,128,128, 128,9,14,28,10,0,0,46,0,111,0,227,128,193,128,193, 128,193,128,193,128,193,128,193,128,193,128,193,128,227,128,123, 0,58,0,9,14,28,10,0,0,254,0,255,0,195,128,193, 128,193,128,195,128,223,0,222,0,192,0,192,0,192,0,192, 0,192,0,128,0,10,15,30,10,0,255,46,0,111,0,227, 128,193,128,193,128,193,128,193,128,193,128,193,128,193,128,193, 128,227,0,123,128,58,192,0,192,9,14,28,10,0,0,126, 0,255,0,195,128,193,128,193,128,195,128,223,0,220,0,206, 0,199,0,195,128,193,128,193,128,129,0,9,14,28,10,0, 0,62,0,127,0,224,0,192,0,192,0,224,0,118,0,27, 0,3,128,1,128,1,128,3,128,255,128,127,0,9,14,28, 10,0,0,255,0,127,128,0,0,12,0,12,0,12,0,12, 0,12,0,12,0,12,0,12,0,12,0,12,0,4,0,9, 14,28,10,0,0,64,128,193,128,193,128,193,128,193,128,193, 128,193,128,193,128,193,128,193,128,193,128,99,0,127,0,62, 0,9,14,28,10,0,0,227,128,99,0,99,0,99,0,34, 0,54,0,54,0,54,0,20,0,28,0,28,0,28,0,8, 0,8,0,10,14,28,11,0,0,64,64,192,192,192,192,192, 192,192,192,192,192,204,192,204,192,204,192,222,192,222,192,211, 192,193,192,128,192,9,14,28,10,0,0,193,128,193,128,193, 128,99,0,99,0,50,0,56,0,28,0,14,0,103,0,99, 0,193,128,193,128,193,128,10,14,28,10,0,0,192,192,192, 192,97,128,97,128,51,0,63,0,30,0,12,0,8,0,4, 0,12,0,12,0,12,0,8,0,9,14,28,10,0,0,127, 128,255,128,1,128,3,128,7,0,6,0,4,0,16,0,48, 0,112,0,224,0,192,0,255,128,255,0,5,14,14,6,0, 0,248,192,192,192,192,192,192,192,192,192,192,192,192,248,9, 14,28,10,0,0,192,0,96,0,96,0,48,0,48,0,24, 0,24,0,12,0,12,0,6,0,6,0,3,0,3,0,1, 128,5,14,14,6,0,0,248,24,24,24,24,24,24,24,24, 24,24,24,24,248,9,6,12,10,0,8,8,0,28,0,54, 0,99,0,193,128,128,128,10,1,2,10,0,255,255,192,4, 3,3,5,0,12,224,96,48,8,11,11,9,0,0,124,127, 3,3,59,123,227,195,199,255,123,9,13,26,10,0,0,64, 0,192,0,192,0,192,0,192,0,222,0,223,0,195,128,193, 128,193,128,195,0,255,0,222,0,8,11,11,9,0,0,30, 63,115,224,192,192,192,224,240,127,30,9,13,26,10,0,0, 0,128,1,128,1,128,1,128,1,128,61,128,125,128,225,128, 193,128,193,128,227,128,127,128,61,128,8,11,11,9,0,0, 60,126,231,195,195,223,222,192,227,127,62,7,13,13,8,0, 0,62,124,96,96,252,248,96,96,96,96,96,96,32,8,13, 13,9,0,0,63,127,227,195,195,195,227,123,51,3,3,127, 254,8,13,13,9,0,0,64,192,192,192,222,223,195,195,195, 195,195,195,130,2,13,13,3,0,0,64,192,128,64,192,192, 192,192,192,192,192,192,128,5,14,14,6,0,255,16,24,24, 8,48,120,24,24,24,24,24,24,120,240,8,13,13,9,0, 0,64,192,192,198,198,204,216,216,216,204,198,199,131,2,14, 14,3,0,0,64,192,192,192,192,192,192,192,192,192,192,192, 192,128,9,11,22,10,0,0,91,0,219,128,201,128,201,128, 201,128,201,128,201,128,201,128,201,128,193,128,129,0,8,11, 11,9,0,0,94,223,195,195,195,195,195,195,195,195,130,8, 11,11,9,0,0,52,118,227,195,195,195,195,195,227,118,52, 8,11,11,9,0,0,252,254,199,195,199,222,220,192,192,192, 128,9,13,26,9,0,255,63,0,127,0,227,0,195,0,195, 0,195,0,251,0,123,0,3,0,3,0,3,128,3,128,3, 128,7,11,11,8,0,0,92,222,224,224,192,192,192,192,192, 192,128,8,11,11,9,0,0,62,127,192,192,240,102,15,3, 3,254,124,6,13,13,7,0,0,32,96,96,252,248,96,96, 96,96,96,96,124,60,8,11,11,9,0,0,65,195,195,195, 195,195,195,195,227,123,58,8,11,11,9,0,0,129,129,195, 195,102,102,102,36,60,24,24,9,11,22,10,0,0,128,128, 193,128,201,128,201,128,201,128,201,128,193,128,221,128,247,128, 227,128,65,0,8,11,11,9,0,0,195,102,102,52,24,24, 24,52,102,102,195,8,12,12,9,0,255,193,227,99,102,110, 44,12,24,24,48,240,224,8,11,11,9,0,0,127,255,7, 6,12,0,48,96,224,255,254,7,14,14,8,0,0,14,28, 24,24,24,48,224,224,48,24,24,24,28,14,2,16,16,5, 1,255,192,192,192,192,192,192,192,192,192,192,192,192,192,192, 192,192,8,14,14,9,0,0,224,112,24,24,24,12,7,7, 12,24,24,24,112,224,10,5,10,11,0,4,48,192,120,192, 204,192,199,128,195,0,5,13,13,6,0,1,248,136,136,136, 136,136,136,136,136,136,136,136,248,8,11,11,9,0,0,252, 254,199,195,199,222,220,192,192,192,128,8,11,11,9,0,0, 30,63,115,224,192,192,192,224,240,127,30,8,11,11,9,0, 0,127,254,24,24,24,24,24,24,24,24,16,8,12,12,9, 0,255,193,227,99,102,110,44,12,24,24,48,240,224,8,12, 12,9,0,255,126,255,219,219,219,219,90,24,24,24,24,16, 8,11,11,9,0,0,195,102,102,52,24,24,24,52,102,102, 195,8,12,12,9,0,255,132,198,198,198,198,198,198,198,198, 254,255,3,8,11,11,9,0,0,65,195,195,195,231,127,63, 3,3,3,2,8,11,11,9,0,0,130,195,195,211,219,219, 219,219,219,203,255,8,12,12,9,0,255,130,195,195,211,219, 219,219,219,218,200,255,3,8,11,11,9,0,0,192,224,96, 96,108,110,103,99,103,126,124,8,11,11,9,0,0,130,195, 195,195,219,221,207,199,207,253,251,8,11,11,9,0,0,64, 192,192,192,220,222,199,195,199,254,252,8,11,11,9,0,0, 124,254,198,3,27,59,3,3,6,254,120,9,11,22,10,0, 0,71,0,207,128,205,128,205,128,221,128,221,128,205,128,205, 128,205,128,207,128,135,0,8,11,11,9,0,0,63,127,227, 195,227,123,59,51,51,115,226,9,14,28,10,0,0,252,0, 254,0,199,0,195,128,193,128,193,128,193,128,253,128,253,128, 193,128,193,128,193,128,193,128,129,0,9,14,28,10,0,0, 223,128,223,0,192,0,192,0,192,0,192,0,222,0,223,0, 195,128,193,128,193,128,195,128,255,0,254,0,9,14,28,10, 0,0,94,0,223,0,195,128,193,128,193,128,195,128,255,0, 255,0,195,128,193,128,193,128,195,128,223,0,190,0,9,14, 28,10,0,0,223,0,223,128,192,0,192,0,192,0,192,0, 192,0,192,0,192,0,192,0,192,0,192,0,192,0,128,0, 9,14,28,10,0,0,11,0,27,0,59,0,115,0,99,0, 99,0,99,0,99,0,99,0,99,0,123,0,251,128,193,128, 193,128,9,14,28,10,0,0,95,128,223,0,192,0,192,0, 192,0,192,0,223,0,222,0,192,0,192,0,192,0,192,0, 223,128,191,0,10,14,28,11,0,0,64,64,192,192,196,192, 204,192,204,192,109,128,109,128,109,128,109,128,204,192,204,192, 204,192,200,192,128,128,9,14,28,10,0,0,122,0,251,0, 131,128,1,128,1,128,3,0,58,0,123,0,3,128,1,128, 1,128,131,128,251,0,120,0,9,14,28,10,0,0,67,128, 195,128,199,128,199,128,199,128,205,128,205,128,205,128,217,128, 217,128,217,128,209,128,209,128,193,0,9,14,28,10,0,0, 91,128,219,128,215,128,199,128,199,128,205,128,205,128,205,128, 217,128,217,128,217,128,209,128,209,128,193,0,9,14,28,10, 0,0,65,128,195,128,199,0,206,0,220,0,216,0,216,0, 216,0,216,0,220,0,206,0,199,0,195,128,65,128,9,14, 28,10,0,0,220,0,222,0,199,0,195,128,193,128,193,128, 193,128,193,128,193,128,193,128,193,128,193,128,193,128,193,128, 9,14,28,10,0,0,193,128,227,128,247,128,247,128,213,128, 193,128,213,128,221,128,221,128,201,128,193,128,193,128,193,128, 129,0,9,14,28,10,0,0,129,0,193,128,193,128,193,128, 193,128,193,128,223,128,223,128,193,128,193,128,193,128,193,128, 193,128,64,128,9,14,28,10,0,0,46,0,111,0,227,128, 193,128,193,128,193,128,193,128,193,128,193,128,193,128,193,128, 227,128,123,0,58,0,9,14,28,10,0,0,223,128,223,128, 193,128,193,128,193,128,193,128,193,128,193,128,193,128,193,128, 193,128,193,128,193,128,129,0,9,14,28,10,0,0,254,0, 255,0,195,128,193,128,193,128,195,128,223,0,222,0,192,0, 192,0,192,0,192,0,192,0,128,0,9,14,28,10,0,0, 14,0,63,0,115,128,97,128,192,0,192,0,192,0,192,0, 192,0,192,0,96,0,112,0,63,128,15,0,9,14,28,10, 0,0,255,0,127,128,0,0,12,0,12,0,12,0,12,0, 12,0,12,0,12,0,12,0,12,0,12,0,4,0,9,14, 28,10,0,0,129,0,193,128,193,128,193,128,193,128,125,128, 61,128,1,128,1,128,1,128,193,128,225,128,127,0,62,0, 10,14,28,11,0,0,63,0,127,128,237,192,204,192,204,192, 237,192,109,128,45,0,12,0,12,0,12,0,12,0,12,0, 4,0,9,14,28,10,0,0,193,128,193,128,193,128,99,0, 99,0,50,0,56,0,28,0,14,0,103,0,99,0,193,128, 193,128,193,128,10,15,30,10,0,255,130,0,195,0,195,0, 195,0,195,0,195,0,195,0,195,0,195,0,195,0,195,0, 195,0,223,0,223,128,1,192,9,14,28,10,0,0,129,0, 193,128,193,128,193,128,193,128,193,128,225,128,125,128,61,128, 1,128,1,128,1,128,1,128,0,128,10,14,28,11,0,0, 128,128,192,192,192,192,192,192,200,192,204,192,204,192,204,192, 204,192,204,192,196,192,192,64,223,128,95,192,11,15,30,11, 0,255,128,128,192,192,192,192,192,192,200,192,204,192,204,192, 204,192,204,192,204,192,196,192,192,64,223,128,95,192,0,224, 9,14,28,10,0,0,224,0,224,0,96,0,96,0,96,0, 96,0,110,0,111,0,99,128,97,128,97,128,99,128,127,0, 62,0,10,14,28,11,0,0,128,128,192,192,192,192,192,192, 192,192,192,192,220,192,222,192,199,64,195,64,195,64,199,64, 254,192,124,192,9,14,28,10,0,0,128,0,192,0,192,0, 192,0,192,0,192,0,222,0,223,0,195,128,193,128,193,128, 195,128,255,0,254,0,9,14,28,10,0,0,62,0,127,0, 227,128,193,128,193,128,1,128,29,128,29,128,1,128,1,128, 193,128,227,128,127,0,62,0,10,14,28,11,0,0,71,0, 207,128,221,192,216,192,216,192,216,192,248,192,248,192,216,192, 216,192,216,192,221,192,207,128,135,0,9,14,28,10,0,0, 63,128,127,128,225,128,193,128,193,128,225,128,125,128,61,128, 29,128,57,128,113,128,225,128,193,128,129,0,8,11,11,9, 0,0,124,127,3,3,59,123,227,195,199,255,123,8,11,11, 9,0,0,7,31,56,112,102,207,195,195,231,126,60,8,11, 11,9,0,0,92,222,198,198,220,222,195,195,199,222,188,8, 11,11,9,0,0,254,255,192,192,192,192,192,192,192,192,128, 10,11,22,11,0,0,31,128,31,128,25,128,49,128,49,128, 49,128,1,128,127,192,255,192,192,192,192,192,8,11,11,9, 0,0,60,126,231,195,195,223,222,192,227,127,62,9,11,22, 10,0,0,64,128,201,128,201,128,107,0,54,0,54,0,107, 0,201,128,201,128,201,128,129,0,8,11,11,9,0,0,116, 246,195,7,126,62,6,3,7,254,124,8,11,11,9,0,0, 65,195,195,199,207,223,219,211,195,195,130,8,11,11,9,0, 0,89,219,211,199,207,223,219,211,195,195,130,8,11,11,9, 0,0,71,207,204,220,216,216,216,220,206,199,67,8,11,11, 9,0,0,27,59,115,227,195,195,195,195,195,195,195,9,11, 22,10,0,0,65,0,227,128,247,128,247,128,213,128,213,128, 213,128,213,128,193,128,193,128,129,0,8,11,11,9,0,0, 65,195,195,251,251,195,195,195,195,195,130,8,11,11,9,0, 0,52,118,227,195,195,195,195,195,227,118,52,8,11,11,9, 0,0,95,223,195,195,195,195,195,195,195,195,130,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255}; // ---------------------------------------------------------------------------
У меня есть достаточно богатый опыт программирования (с 89 года), но все это касается полноценных компьютеров, последнее время занимаюсь разработкой управляющих систем на arm под самосборной допиленной Linux, пишу либо на голом с или qt. Опыта программирования МК не имел, но старался перенести свой опыт на них.
Использую шедуллер задач leos ver 1 (первая версия использует таймер вторая использует сторожевой таймер), это “обертка” таймера для организации периодического вызова функций (у меня вызов опроса датчика).
Сторожевой таймер используется для борьбы с зависанием МК (я их не наблюдал но пусть будет).
Для работы с дисплеем используется библиотека ucglib версии не позднее 1.02. D более поздних версиях переработан формат шрифтов и есть проблемы с русификацией в кодировке UTF-8, допиливать было лень.
Для nrf24 используется библиотека от маньяка.
Для чтения датчиков DHT используется самая свежая библиотека с официального сайта. Там кодов ошибок больше.
Реализован подсчет мото часов блока и вентилятора.Кнопка повешена на прерывание и позволяет менять настройки включения вентилятора, смотреть информацию об устройстве (длительное нажатие) и сбрасывать счетчики (нажатие при включении).
Реализован вывод ошибок чтения DHT22 (верхний правый угол цифры), с чтением были проблемы, после оптимизации кода они ушли. На графике выводится не весь диапазон темперур (вывод -20…+20) для увеличения разрешающей способности графика в области допустимых темератру в подвале (0…+20).
4. Конструктив
Корпус напечатан на 3D-принтере (нижняя часть склеена из двух частей). Очень удобно, все отверстия сразу сделаны, только заусецы счистить и можно использовать. Получилось достаточно компактно. Вся электроника кроме входных цепей распаена на макетке, проводом (по времени один вечер).
Замеченные косяки:
- На дисплее появляются “не штатные” точки на графике (скоре всего помехи по SPI после перерисовке экрана они проподают),
- Кнопка иногода “не четко” срабатывает проблемы с длительным нажатием.
- nrf24 в блоке ожидает “квитанции” о приеме пакета иногда “квиатнция” не приходит (30-40%) но пакет доставляется до приемного устройства.
- Уличный датчик DHT-22 показания влажности прыгают утром (ветер, солнце?). Датчик менял.
Конструктивная критика приветствуется, особенно по коду.
5. Фото готового устройства
Ниже привожу фото готового устройства: