Контроль влажности подвала Arduino pro mini

PaulIV
Offline
Зарегистрирован: 11.10.2015

Всем привет.

Проект реально затрагивает за живое... Для многих эта тема актуальна. 

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

Заранее спасибо. 

avgaz
Offline
Зарегистрирован: 23.09.2014

Не подскажите может есть кокой секрет, но мне не удается запустить сразу и NRF и дисплей ISP TFT вот для примера скетч простой

#include <SPI.h>
#include "Ucglib.h"
#include <nRF24L01.h>
#include <RF24.h> // https://github.com/maniacbug/RF24
 
Ucglib_ILI9341_18x240x320_SWSPI ucg(/*sclk=*/ 13, /*data=*/ 11, /*cd=*/ 9 , /*cs=*/ 10, /*reset=*/ 8);
 
const uint64_t pipe = 0xF0F1F2F3F4LL; // идентификатор передачи, "труба"
RF24 radio(6,7); // CE, CSN

void setup(){
  Serial.begin(9600);

  radio.begin();
  delay(2);
  radio.setChannel(9); // канал (0-127)
radio.setDataRate(RF24_1MBPS);
 radio.setPALevel(RF24_PA_HIGH);
 radio.setCRCLength(RF24_CRC_16);
  radio.setRetries(15,15);
  radio.openWritingPipe(pipe); // открываем трубу на передачу.
radio.startListening();

ucg.begin(UCG_FONT_MODE_TRANSPARENT);
  //ucg.begin(UCG_FONT_MODE_SOLID);
  ucg.clearScreen();

}
void loop()
{
   float data[3];
    data[0] = 100; // заполняем массив
     data[1] = 80;
// читаем значение
   radio.powerUp();
   delay(2);
  radio.write(&data, sizeof(data)); 
  
   ucg.setFont(ucg_font_ncenR14r);
  ucg.setColor(255, 255, 255);
  ucg.setColor(255, 0, 0);
  //ucg.setColor(0, 255, 0);
  ucg.setColor(1, 255, 0,0);
  
  ucg.setPrintPos(0,25);
  ucg.print("Hello World!");

  ucg.setPrintPos(1,50);
  ucg.print("Hello World!");
  
  delay(1500);
} 

по отдельности все работает как только в скетч совмещаю так все экран не показывает ((

pav2000
Offline
Зарегистрирован: 15.12.2014

У Вас nrf использует аппаратный SPI (это зашито в библиотеке) с ногами 11 12 13.

А дисплей у Вас использует програмный SPI  (Ваша строка инициализации Ucglib_ILI9341_18x240x320_SWSPI ucg(/*sclk=*/ 13, /*data=*/ 11, /*cd=*/ 9 , /*cs=*/ 10, /*reset=*/ 8);)  и тоже с ногами 11  13. Как то не красиво получается. На одних ногах живуи разные SPI.

Замените для дисплея инициализацию Ucglib_ILI9341_18x240x320_HWSPI ucg(/*cd=*/PIN_CD, /*cs=*/PIN_CS, /*reset=*/PIN_RESET); // Аппаратный SPI на дисплей ILI9341 ) и проверьте соответсвие ног.

Должно заработать

avgaz
Offline
Зарегистрирован: 23.09.2014

я пробовал вашу строку со своими пинами, в обще дисплей не работает да же без кода nrf да и с этой строкой пины 11,13 все равно останутся как у nrf я пересадил все пины со своей срокой на другие, заработало но жутко тормозит отрисовка на дисплее и код весь из за этого.

pav2000
Offline
Зарегистрирован: 15.12.2014

avgaz пишет:

я пробовал вашу строку со своими пинами, в обще дисплей не работает да же без кода nrf да и с этой строкой пины 11,13 все равно останутся как у nrf я пересадил все пины со своей срокой на другие, заработало но жутко тормозит отрисовка на дисплее и код весь из за этого.

1. Тормозит дисплей  потому что программный spi.

2. У Вас дисплей подключен через преобразователь уровней (резисторы)? У меня  сначал была микросхема и работал только программный spi я поменял на резисторы и все зарабоатло . Только на дисплей.

3. Общие пины 11 12 13 на хард spi допустимы (так и задумывалось, протокол позволяет вешать несколько приемников на одну шину) , главное что бы пины CS CD были разные и библиотека корректно с ними работала.

4. Нобходимо добится работы отдельно дисплея с моей строкой и моими ногами. Может перепутаны ноги для дисплея cs и cd?

//  НОГИ к которым прицеплена переферия (SPI используется для TFT и NRF24 - 11,12,13)
#define PIN_CS        10                         // TFT дисплей spi CS
#define PIN_CD        9                          // TFT дисплей spi CD
#define PIN_RESET     8                     // TFT дисплей spi Reset  дисплей
#define PIN_CE        7                          // nrf24 ce
#define PIN_CSN       6                          // nrf24 cs

Ucglib_ILI9341_18x240x320_HWSPI ucg(/*cd=*/PIN_CD, /*cs=*/PIN_CS, /*reset=*/PIN_RESET); // Аппаратный SPI на дисплей ILI9341
 
RF24 radio(PIN_CE, PIN_CSN);  // определение управляющих ног

А потом подключить nrf

avgaz
Offline
Зарегистрирован: 23.09.2014

2 у меня подключен по через резисторы 10к вот по такой схеме http://1.bp.blogspot.com/-0Zmb2xr-uEI/U8ZCaSnNQEI/AAAAAAAAMEE/eeV1dRPckL...

может из за этого и не работает апаратный spi, а пока жду такие http://ru.aliexpress.com/item/1PCS-IIC-I2C-Logic-Level-Converter-Bi-Dire...

Thunderix
Offline
Зарегистрирован: 12.09.2015

Я такие преобразователи покупал, а потом плюнул и  переделал всё на 3.3 вольта.

pav2000
Offline
Зарегистрирован: 15.12.2014

avgaz пишет:

2 у меня подключен по через резисторы 10к вот по такой схеме http://1.bp.blogspot.com/-0Zmb2xr-uEI/U8ZCaSnNQEI/AAAAAAAAMEE/eeV1dRPckLs/s1600/schema_conectare_ILI9341_Arduino_ver1m0.png

может из за этого и не работает апаратный spi, а пока жду такие http://ru.aliexpress.com/item/1PCS-IIC-I2C-Logic-Level-Converter-Bi-Directional-Module-5V-to-3-3V/32417113805.html

Мне поледовательные резисторы не нравятся, как то сремно. У меня сделано вот так

"Для согласования уровней 5 вольт ардуино и дисплея используются делители из резисторов 2к+4к. Согласвоания уровне й для nrf24 не требуется."

И еще возможно заказаные детали не помогут (скорости у spi гораздо выше чем у i2c. Фронты будут выглядеть очень страшно)

Почитайте http://forum.arduino.cc/index.php?topic=181679.120 там на картинке все нарисовано.

еще вариант http://dangerousprototypes.com/2015/07/28/2-2-or-2-4-or-2-8-inch-spi-tft... только номиналы резисторов мне не нравятся

avgaz
Offline
Зарегистрирован: 23.09.2014

pav2000 пишет:

"Для согласования уровней 5 вольт ардуино и дисплея используются делители из резисторов 2к+4к. Согласвоания уровне й для nrf24 не требуется."

И еще возможно заказаные детали не помогут (скорости у spi гораздо выше чем у i2c. Фронты будут выглядеть очень страшно)

Почитайте http://forum.arduino.cc/index.php?topic=181679.120 там на картинке все нарисовано.

еще вариант http://dangerousprototypes.com/2015/07/28/2-2-or-2-4-or-2-8-inch-spi-tft... только номиналы резисторов мне не нравятся

хорошо когда резисторы есть (((( у меня в наличии есть 1к, 4.7к, 10к, 100к и так если я возьму два подряд 1к получу 2к, а вот место 4к можно взять 4.7к ??

pav2000
Offline
Зарегистрирован: 15.12.2014

Можно 1+1 и 4.7 к

Или 1 и 1 к если паять линиво. Тоже должно получиться.  Нужно спаять 5 делителей

 

 

Thunderix
Offline
Зарегистрирован: 12.09.2015

pav2000 боюсь спросить, когда будет сервер с етхернетом?, Пора запчастюлички покупать.

avgaz
Offline
Зарегистрирован: 23.09.2014

pav2000 пишет:

Можно 1+1 и 4.7 к

Или 1 и 1 к если паять линиво. Тоже должно получиться.  Нужно спаять 5 делителей

 

 

Попробовал так и так, оба варианта работают, то есть диплей аппарантно работает.

Но тормоза остались просто жуть вот этот код исполняется 6 секунд

#include <SPI.h>
#include "Ucglib.h"
#include <nRF24L01.h>
#include <RF24.h> // https://github.com/maniacbug/RF24
 
Ucglib_ILI9341_18x240x320_HWSPI ucg(/*cd=*/ 5, /*cs=*/6, /*reset=*/ 7); // Аппаратный SPI на дисплей ILI9341

//Ucglib_ILI9341_18x240x320_SWSPI ucg(/*sclk=*/ 6, /*data=*/ 7, /*cd=*/ 4 , /*cs=*/ 3, /*reset=*/ 5);
const uint64_t pipe = 0xF0F1F2F3F4LL; // идентификатор передачи, "труба"
RF24 radio(9,10); // CE, CSN

#include <RTC.h>
RTC	time;

#include "DHT.h"
#define DHTPIN 2  
#define DHTTYPE DHT22 
DHT dht(DHTPIN, DHTTYPE);

void setup(){
  Serial.begin(9600);
  radio.begin();
  delay(2);
  radio.setChannel(9); // канал (0-127) 
  radio.setDataRate(RF24_1MBPS);// скорость передачи
  radio.setPALevel(RF24_PA_HIGH); // мощность передатчика
  radio.openWritingPipe(pipe); // открываем трубу на передачу.
  
time.begin(RTC_DS3231);

dht.begin();
  
  //ucg.begin(UCG_FONT_MODE_TRANSPARENT);
  ucg.begin(UCG_FONT_MODE_SOLID);
  ucg.clearScreen();
  ucg.setRotate270();
  ucg.setScale2x2();

}
void loop()
{
   float data[3];
     data[0] = dht.readTemperature(); 
     data[1] = dht.readHumidity();

   radio.powerUp();
   delay(2);
  radio.write(&data, sizeof(data)); 
 ucg.setFontMode(UCG_FONT_MODE_SOLID);
 ucg.setFont(ucg_font_ncenR18r);
//  ucg.setFont(ucg_font_fub25);
 ucg.setPrintPos(50,30);
  ucg.setColor(255, 255, 255);
  ucg.print(time.gettime("H:i:s"));
   ucg.setFont(ucg_font_ncenR14r);
  ucg.setPrintPos(20,100);
   ucg.print(data[0]);
   ucg.print("C  ");
   ucg.print(data[1]);
   ucg.print("%");
  
  
  //delay(1500);
} 

и ещё вопрос как вы столько много кода запехнули в про мини? у меня только вот это занимает 30кб из 32кб ((( да же шрифт не сменить на более большой не влезает просто

pav2000
Offline
Зарегистрирован: 15.12.2014

1. По скорости. 

Я использую библиотеку не ПОЗНЕЕ 1.01. В более старших версиях  изменен механизм вывода шрифта - применяют сжатие при хранении - места меньше скорость меньше. Я не использовал функцию SetScale2x2.

У меня тоже вывод не быстр но не 6 секунд. Для проверки залейте скейчт из этой ветки и оцените скорость.

2. По месту - особого криминала не вижу. Я не пользовался библиотекой часов, может она много места занимает. Отключайте отдельные куски и выясните что жрет место.   Дисплей съедает 10-12 кБ  радио 2-3 кб  начальный код 2 кБ.  Так я выяснил что sprintf  съедает 12-15 кБ места.

Возможно подгружает лишние шрифты (хотя этого не должно быть)

3.  Разработка сетевого  головного блока.  К сожалению времени сейчас не много, плюс еще есть проекты. Для меня сейчас главным проектом являетя блок контроля за тепловым насосом (сейчас его в основном пилю).  Но про головной блок не забыл. Уже все спаял и откорпусил, освоил рисование графиков. Сейчас размышляю как наиболее правильно хранить html код в программе. Для затравки картинка (на значения не смотрим так демо работает):

4. В новогодние каникулы у нас стояли морозы (до -25). Наконец прикрутил твердотельное реле на обогреватель. А то в погребе температура была близак +2.  У у меня там домашние заготовки и картошка хранится. Как оказалось, во время-)).

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

/*
Описание
1. Часы реального времени. Данные приходят от головного блока. Потом обновлются по таймеру (). Пришедшие данные имеют более высокий приоритет
1.1 Установить библиотеку  Time library - https://github.com/PaulStoffregen/Time  
У нее не удачное имя (винды регистронезависимы и имя совпадает с системным -- среда глючит)
переименовать в stmTime  директорию, файлы и ссылки в них !!!
*/

#pragma GCC optimize ("-Os")
//#pragma pack(push, 1)     // выравнивание по одному байту ????????

#include <SPI.h>
#include <EEPROM.h>
#include <avr/wdt.h>
#include <leOS.h>      // Шедуллер задач
#include <dht.h>       // Дачик влажности и температуры
#include "Ucglib.h"    // ВНИМАНИЕ использовать библиотеку не познее 1.01 справка https://code.google.com/p/ucglib/wiki/
#include "rusFont.h"   // Русские шрифты
#include "nRF24L01.h"  // Беcпроводной модуль надо использовать библиотеку http://tmrh20.github.io/RF24
#include "RF24.h"      // Беcпроводной модуль используются не стандартные функции https://github.com/TMRh20

// - ОПЦИИ -------------------------------
//#define DEBUG                                   // Отладочную  информацию в ком порт посылает  
//#define DEMO                                      // Признак демонстрации - данные с датчиков генерятся рандом
#define BEEP                                      // Использовать пищалку
#define RADIO                                     // Признак использования радио модуля
#define VERSION "Ver. 0.75 05/01/16"              // Текущая версия
#define ID             0x22                       // уникально Идентификатор устройства (тип) - старшие 4 бита, вторые (младшие) 4 бита серийный номер устройства
#if ID == 0x21                                    // Место установки блока - отоображается как заголовок колонки в таблице. Не более 6 символов а то не влезает  
  #define LABEL "Подпол"           
#elif ID == 0x22     
  #define LABEL "Подвал"                       
#else 
  #define LABEL "none"      
#endif


// Макросы для работы с портами  скорость и место
#define SetOutput(port,bit)       DDR ## port |= _BV(bit)
#define SetInput(port,bit)        DDR ## port &= ~_BV(bit)
#define SetBit(port,bit)          PORT ## port |= _BV(bit)
#define ClearBit(port,bit)        PORT ## port &= ~_BV(bit)
#define WritePort(port,bit,value) PORT ## port = (PORT ## port & ~_BV(bit)) | ((value & 1) << bit)
#define ReadPort(port,bit)        (PIN ## port >> bit) & 1
#define PullUp(port,bit)          { SetInput(port,bit); SetBit(port,bit); }
#define Release(port,bit)         { SetInput(port,bit); ClearBit(port,bit); }
// Мои макросы
#define MOTOR_BIT                 0            // бит мотора в packet.flags
#define HEAT_BIT                  1            // бит калорифера в packet.flags
#define ABS_H_BIT                 2            // бит кодирования влажности абс или % в packet.flags
#define MODE_BIT                  5            // первый бит режима в packet.flags
//#define MASK_MODE_BITS            0xe0         // маска для выделения номера режима в packet.flags
#define SET_MODE_BITS             packet.flags |= (settingRAM.mode<<MODE_BIT)  // запись номера режима в packet.flags
#define FLAG_ABS_H_ON             packet.flags |= (1<<ABS_H_BIT)   // бит ABS_H_BIT установить в 1
#define FLAG_ABS_H_OFF            packet.flags &= ~(1<<ABS_H_BIT)  // бит ABS_H_BIT установить в 0
#define FLAG_ABS_H_CHECK          packet.flags & (1<<ABS_H_BIT)    // бит ABS_H_BIT проверить на 1

#define FLAG_MOTOR_ON             packet.flags |= (1<<MOTOR_BIT)   // бит мотора установить в 1
#define FLAG_MOTOR_OFF            packet.flags &= ~(1<<MOTOR_BIT)  // бит мотора установить в 0
#define FLAG_MOTOR_CHECK          packet.flags & (1<<MOTOR_BIT)    // бит мотора проверить на 1
#define MOTOR_ON                  { WritePort(C,0,HIGH); FLAG_MOTOR_ON;  }   // включить мотор
#define MOTOR_OFF                 { WritePort(C,0,LOW) ; FLAG_MOTOR_OFF; }   // выключить мотор

#define FLAG_HEAT_ON              packet.flags |= (1<<HEAT_BIT)   // бит калорифера установить в 1
#define FLAG_HEAT_OFF             packet.flags &= ~(1<<HEAT_BIT)  // бит калорифера установить в 0
#define FLAG_HEAT_CHECK           packet.flags & (1<<HEAT_BIT)    // бит калорифера проверить на 1
#define HEAT_ON                   { WritePort(C,2,HIGH); FLAG_HEAT_ON;  }   // включить калорифер
#define HEAT_OFF                  { WritePort(C,2,LOW); FLAG_HEAT_OFF; }   // выключить калорифер


// - КОНСТАНТЫ --------------------------------------
#define dH_OFF          5                      // Гистерезис абсолютной влажности в сотых грамма на куб
#define dT_OFF          11                      // Гистерезис температуры в сотых градуса
#define TEMP_LOW       150                     // Температура подвала критическая - может быть замораживание (в сотых градуса) - система выключается и включается нагреватель
// СИСТЕМАТИЧЕСКИЕ ОШИБКИ ДАТЧИКОВ для каждого ID свой. ОШИБКИ ДОБАВЛЯЮТСЯ!!
#if ID == 0x21      // ОТКАЛИБРОВАНО не менять
    #define TOUT_ERR      +10                  // Ошибка уличного датчика температуры в сотых долях градуса
    #define TIN_ERR       +50                  // Ошибка домового датчика температуры в сотых долях градуса
    #define HOUT_ERR      -0                   // Ошибка уличного датчика влажности в сотых долях %
    #define HIN_ERR       +50                  // Ошибка домового датчика влажности в сотых долях %
#elif ID == 0x22     // ОТКАЛИБРОВАНО не менять
    #define TOUT_ERR      +50                  // Ошибка уличного датчика температуры в сотых долях градуса +50
    #define TIN_ERR       +50                  // Ошибка домового датчика температуры в сотых долях градуса +50
    #define HOUT_ERR      -230+60                 // Ошибка уличного датчика влажности в сотых долях %         -260
    #define HIN_ERR       +490                 // Ошибка домового датчика влажности в сотых долях %         +480
#else 
    #define TOUT_ERR      0                    // Ошибка уличного датчика температуры в сотых долях градуса
    #define TIN_ERR       0                    // Ошибка домового датчика температуры в сотых долях градуса
    #define HOUT_ERR      0                    // Ошибка уличного датчика влажности в сотых долях %
    #define HIN_ERR       0                    // Ошибка домового датчика влажности в сотых долях %
#endif

// - ВРЕМЕНА ---------------------------------------
#ifdef DEMO                                     // Для демо все быстрее и случайным образом
    #define NUM_SAMPLES      2                  // Число усреднений измерений датчика ТОЛЬКО целые тысячи для точности часов
    #define TIME_SCAN_SENSOR 2000               // Время опроса датчиков мсек, для демки быстрее
    #define TIME_PRINT_CHART 4000               // Время вывода точки графика мсек, для демки быстрее
    #define TIME_HOUR        50000              // Число мсек в часе, для демки быстрее   
#else   
   #define NUM_SAMPLES      10                  // Число усреднений измерений датчика
   #define TIME_SCAN_SENSOR 3000                // Время опроса датчиков мсек ТОЛЬКО целые тысячи для точности часов
   #define TIME_PRINT_CHART 300000              // Время вывода точки графика мсек
   #define TIME_HOUR        3600000             // Число мсек в часе
#endif


#define RESET_TFT_KEY   9999                    // Очень длительное нажатие на клавишу мсек, сброс контроллера дисплея и появляется основной экран данные сохраняются
#define LONG_KEY        2000                    // Длительное нажатие кнопки мсек, появляется Экран инфо
#define SHORT_KEY       100                     // Короткое нажатие кнопки более мсек
#define RESET_TEMP_KEY  4000                    // Время нажатия кнопки более мсек для сброса температур
#define NRF24_CHANEL  100                       // Номер канала nrf24

//  НОГИ к которым прицеплена переферия (SPI используется для TFT и NRF24 - 11,12,13)
#define PIN_HEAT      16                         // Ножка куда повешен калорифер A2 (port C2)
#ifdef BEEP
    #define PIN_BEEP  15                         // Ножка куда повешена пищалка A1 (port C1)
#endif
#define PIN_RELAY     14                         // Ножка на которую повешено реле (SSR) вентилятора - аналоговый вход A0 через резистор 470 ом (port C0)
#define PIN_CS        10                         // TFT дисплей spi CS
#define PIN_CD        9                          // TFT дисплей spi CD
#define PIN_RESET     8                          // TFT дисплей spi Reset  дисплей
#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                          // Кнопка, повешена на прерывание, что бы ресурсов не тратить (port D3)
#define PIN_IRQ_NRF24 2                          // Ножка куда заведено прерывание от NRF24 (пока не используется)

// Настройки
#define NUM_SETTING    7                        // Число вариантов настроек 
#define BLOCK_OFF      0                        // Выключено (вариант настроек)
#define HOOD_ON        1                        // Режим вытяжки (вариант настроек)
#define COOLING        2                        // Режим охлаждение (вариант настроек)

#include   "stmTime.h"     // Time library - https://github.com/PaulStoffregen/Time
//#define TZ "UTC+3"       // Часовой пояс
unsigned long tt=0;        // Время пришедшее от головного блока если 0 то вроемя не приходило ничего не выводим
// АЦП ----------------------------------------
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;                       // Время нажатия копки
long time_key_res_temp=-1;                      // время нажатия кнопки для сброса температур  -1 кнопка не нажата
long time_key_res_tft=-1;                       // время нажатия кнопки для сброса дисплея  -1 кнопка не нажата
byte last_error=100;                            // Предыдущая ошибка чтения датчиков

 struct type_setting_eeprom                     // Структура для сохранения данных в eeprom
 {
     byte mode = 0;                             // Какой режим работы блока
     unsigned long hour_unit=0;                 // мото часы блок измеряется в интервалах вывода данных = NUM_SAMPLES*TIME_SCAN_SENSOR
     unsigned long hour_motor=0;                // мото часы мотора измеряется в интервалах вывода данных = NUM_SAMPLES*TIME_SCAN_SENSOR
     unsigned long hour_heat=0;                 // мото часы нагревателя измеряется в интервалах вывода данных = NUM_SAMPLES*TIME_SCAN_SENSOR
     int16_t  tOutMin=5555,tInMin=5555;         // Минимальные температуры за период температуры в сотых градуса !!! место экономим
     int16_t  tOutMax,tInMax=-5555;             // Максимальные температуры за период температуры в сотых градуса. (Наблюдаю глюк при tOutMax=-5000)
     int16_t tick_eeprom=0;                     // Переменная для хранения времени записи статистики раз в один час хранится в NUM_SAMPLES*TIME_SCAN_SENSOR сравнение с TIME_HOUR
     
  }; 
type_setting_eeprom settingRAM;                 // Рабочая копия счетчиков в памяти
type_setting_eeprom settingEEPROM EEMEM;        // Копия счетчиков в eeprom - туда пишем 

// Пакет передаваемый, используется также для хранения результатов. 
 struct type_packet_NRF24   // Версия 2.4!! адаптация для stm32 Структура передаваемого пакета 32 байта - 32 максимум
    {
        byte id=ID;                             // Идентификатор типа устройства - старшие 4 бита, вторые (младшие) 4 бита серийный номер устройства
        byte DHT_error;                         // Ошибка разряды: 0-1 первый датчик (00-ок) 2-3 второй датчик (00-ок) 4 - радиоканал     
        int16_t   tOut=-500,tIn=500;            // Текущие температуры в сотых градуса !!! место экономим
        uint16_t  absHOut=123,absHIn=123;       // Абсолютные влажности в сотых грамма на м*3 !!! место экономим
        uint16_t  relHOut=123,relHIn=123;       // Относительные влажности сотых процента !!! место экономим
        uint8_t   flags=0x00;                   // байт флагов  
                                                // 0 бит - мотор включен/выключен 
                                                // 1 бит - нагреватель включен/выключен
                                                // 2 бит -[1 - dH_min задается в сотых грамма на м*3] [0 - dH_min задается в ДЕСЯТЫХ процента от absHIn]
                                                // 3-4 - пока пусто
                                                // 5-7 - номер настройки = settingRAM.mode до 8 настроек, надо передавать, что бы на приемнике восстановить
        uint8_t   dH_min;                       // Порог включения вентилятора по РАЗНИЦЕ абсолютной влажности в сотых грамма на м*3 или в ДЕСЯТЫХ % см flags:2
        uint8_t   T_min;                        // Порог выключения вентилятора по температуре в ДЕСЯТЫХ долях градуса, только положительные значения
        uint8_t   count=0;                      // циклический счетчик отправленных пакетов нужен что бы на приемнике проверять качество связи
        char note[14] = LABEL;                  // Примечание не более 13 байт + "0" байт Русские буквы в два раза меньше т.к. UTF-8
    } packet; 
    
struct type_sensors                             // структура для усреднения измерений
{
       int  num=0;                              // сколько отсчетов уже сложили не болле NUM_SAMPLES
       long  sum_tOut=0,sum_tIn=0;              // Сумма для усреднения Текущие температуры в сотых градуса !!! место экономим
       long  sum_relHOut=0,sum_relHIn=0;        // Сумма для усреднения Относительные влажности сотых процента !!! место экономим
       int  tOut=-5000,tIn=5000;                // Текущие температуры в сотых градуса !!! место экономим
       int  absHOut=55555,absHIn=55555;         // Абсолютные влажности в сотых грамма на м*3 !!! место экономим
       int  relHOut=55555,relHIn=55555;         // Относительные влажности сотых процента !!! место экономим
} sensors;
    
// Массивы для графиков
uint8_t tOutChart[120];
uint8_t tInChart[120];
uint8_t absHOutChart[120];
uint8_t absHInChart[120];
uint8_t posChart=0;       // Позиция в массиве графиков - начало вывода от 0 до 120-1
uint8_t TimeChart=0;      // Время до вывода очередной точки на график. 
bool ChartMotor=false;    // Признак работы мотора во время интервала графика если мотор был включен на любое время (даже одно измерение) то на графике фон меняется в этом месте
bool ChartHeat=false;     // Признак работы нагревателя во время интервала графика если нагреватель был включен на любое время (даже одно измерение) то на графике фон меняется в этом месте

#ifdef  RADIO    // Радио модуль NRF42l  
   RF24 radio(PIN_CE, PIN_CSN);  // определение управляющих ног
   bool send_packet_ok=false;    // признак удачной отправки последнего пакета 
   unsigned long err_ask=0;      // число не полученных ответов
#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);         //  выбор скорости RF24_250KBPS RF24_1MBPS RF24_2MBPS
   radio.setPALevel(RF24_PA_MAX);           // выходная мощность передатчика
   radio.setChannel(NRF24_CHANEL);          //тут установка канала
   radio.setCRCLength(RF24_CRC_16);         // использовать контрольную сумму в 16 бит
   radio.setAutoAck(true);                  // выключить аппаратное потверждение
// radio.enableDynamicPayloads();           // разрешить Dynamic Payloads
   radio.enableAckPayload();                // Разрешить ответ приемника AckPayload
   radio.setRetries(50,10);                  // Количество пауза и количество повторов 
  // Рекомендуют первые 2-4 байта адреса устанавливать в E7 или 18 он проще детектируется чипом
  radio.openWritingPipe(0xE7E7E7E7E1LL);    // передатчик 
  radio.openReadingPipe(1,0xE7E7E7E7D2LL);  // приемник
  radio.startListening();
#endif
  #ifdef BEEP
     SetOutput(C,1);                      //  Настройка ноги для динамика
     WritePort(C,1,LOW);
  #endif
  SetInput(D,3);                        //  Включена кнопка
  WritePort(D,3,HIGH);
  SetOutput(C,0);                       //  Подключить Реле
  WritePort(C,0,LOW);
  SetOutput(C,2);                       //  Подключить Калорифер
  WritePort(C,2,LOW);
  
  pinMode(PIN_DHT22a, OUTPUT);          //  Датчик DHT22 #1 
  digitalWrite(PIN_DHT22a, HIGH);  
  pinMode(PIN_DHT22b, OUTPUT);          //  Датчик DHT22 #2
  digitalWrite(PIN_DHT22b, HIGH);  
  reset_ili9341();                      // сброс дисплея
  readEeprom();                         // Прочитать настройки 
  byte i=ReadPort(D,3);
  if (i==0)                  // Если при включении нажата кнопка то стираем Eeprom
   {  
      settingRAM.mode=0;
      settingRAM.hour_unit=0;
      settingRAM.hour_motor=0;
      settingRAM.hour_heat=0;
      settingRAM.tOutMin=5000;
      settingRAM.tInMin=5000; 
      settingRAM.tOutMax=-5000;
      settingRAM.tInMax=-5000;   
      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();
  }  
  boot_test();         // Тестирование исполнительных устройств
  wdt_enable(WDTO_8S); // Сторожевой таймер Для тестов не рекомендуется устанавливать значение менее 8 сек.
  // Запуск задач по таймеру
  myOS.begin();
  myOS.addTask(measurement,TIME_SCAN_SENSOR);    // Измерение 
  attachInterrupt(1, scanKey, CHANGE);           // КНОПКА Прерывания по обоим фронтам
  print_static();                                // распечатать таблицу
  Setting();                                     // Применить настройки
  measurement();                                 // Считать данные
  #ifdef BEEP
     beep(300);
     delay(300);
     beep(600);
  #endif
resetKey();
}

void loop()
{
 wdt_reset();                        // Сбросить сторожевой таймер
// Обработка нажатия кнопки
// 1. Высший приоритет - сброс контроллера дисплея если он зависнeт - держать долго сброс пройдет отпустить кнопку
if ((flagKey==true)&&(pressKey!=true))
{
if (time_key_res_tft==-1) time_key_res_tft=millis();     // первый вход - нажата кнопка запомнить время нажатия
   else if (millis()>time_key_res_tft+RESET_TFT_KEY)     // кнопка нажата более RESET_TFT_KEY сек сбрасываем дисплей
    {
    reset_ili9341();               // сброс дисплея 
    ucg.setColor(255, 255, 255);
    print_StrXY(10,100,F("Сброс контроллера дисплея ILI9341 . . ."));
    print_StrXY(10,129,F("Кнопку нужно отпустить"));
    delay(1000);
    reset_ili9341();               // сброс дисплея   
    print_static();                // распечатать таблицу
    Setting();                     // Применить настройки
    print_data();                  // вывод усредненных значений 
    print_status();                // панель состояния
    printChart();                  // показать график
    infoScreen=false;              // нет информационного экрана
    #ifdef BEEP
       beep(400);                  // Звук нажатия на клавишу
    #endif
    resetKey();
   }  
}
// 2. Второй приоритет - сброс памяти температур работает только в информационном экране
if ((flagKey==true)&&(pressKey!=true)&&(infoScreen==true)) // Во время информационнного экрана нажата кнопка - подготовка и стирание температур min max
{  
   if (time_key_res_temp==-1) time_key_res_temp=millis(); // первый вход - нажата кнопка для стирания температур 
   else if (millis()>time_key_res_temp+RESET_TEMP_KEY)    // кнопка нажата более RESET_TEMP_KEY сек сбрасываем температуры
          {
          settingRAM.tOutMin= 5000;
          settingRAM.tInMin=  5000; 
          settingRAM.tOutMax=-5000;
          settingRAM.tInMax= -5000;   
          writeEeprom();                                 // Сбросили температуры сохраняем в EEPROM  
          #ifdef BEEP
               beep(500);                                // Звук нажатия на клавишу
          #endif
          printInfo();                                   // Обновить экран с новыми температурами
          resetKey();
          }
} 
else  // 3. Остальной обработчик нажатия на кнопок - Кнопку надо нажать и отпустить
if ((flagKey!=false)&&(pressKey==true))   // Кнопка была нажата и отпущена
  {
//  if (flagKey!=false)                    // Обработчик кнопки
  if ((time_key>=LONG_KEY)&&(infoScreen!=true))  // Длительное нажатие кнопки и информационный экран не показан
   printInfo();                                 // Вывод информационного экрана
   else 
    if (time_key >= SHORT_KEY)                   // Короткое нажатие кнопки все остальное игнорируется
     { 
        #ifdef BEEP
          beep(30);                             // Звук нажатия на клавишу
        #endif
       if (infoScreen==true)  clearInfo();                    // если информационный экран показан то стереть его
       else {if (settingRAM.mode >= NUM_SETTING) settingRAM.mode=0; //  Кольцевой счетчик настроек
               else settingRAM.mode++;                      // В противном случае следующая настройка
               Setting(); }  
     }
  resetKey();                                                //  Сброс состояния кнопки
 }
}

void resetKey(void)  // Сброс состояния кнопки
{
  flagKey=false;                                             
  pressKey=false;
  time_key_res_temp=-1;   // сброс состояния сброса температур
  time_key_res_tft=-1;    // сброс состояния сброса дисплея
}  

void print_static()  // Печать статической картинки 
{  
  int i;
   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);
  for (i=0;i<5;i++) ucg.drawHLine(0,25+23*i,320-1);
  ucg.drawVLine(200-4,25,24+23*3);
  ucg.drawVLine(260,25,24+23*3);

  // Заголовки таблиц
  ucg.setColor(255, 255, 0);
  // В зависимости от id разные надписи - привязка местоположения блока к ID
  
//  print_StrXY(180+30-9,25+0+18,F(LABEL));
  ucg.setPrintPos(180+30-9,25+0+18); 
  ucg.print(packet.note); 
  
  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(210, 210, 210);
//  ucg.drawHLine(1,240-2,130);
//  ucg.drawVLine(1,135,105);
//  ucg.drawHLine(10+154,240-2,130);
//  ucg.drawVLine(10+154,135,105);
  print_StrXY(10,135+0,F("Температура")); 
  print_StrXY(20+154,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"));
  
  // Горизонтальная шкала по часам
//  ucg.setColor(255, 255, 0);
  for(i=0;i<=120;i=i+12)
    {
      ucg.drawPixel(4+i,239);
      ucg.drawPixel(4+i,238);
      ucg.drawPixel(167+i,239);
      ucg.drawPixel(167+i,238);
     } 
   sei();
}

void print_status(void) // Печать панели статуса Значки на статус панели
{
  byte temp;
  if (infoScreen==true) return;        // если отображен информационный экран то ничего не выводим  
   cli();
 // 1. печать ошибки чтения датчиков
   print_error_DHT();
 // 2. Признак включения мотора или нагревателя
  if (FLAG_MOTOR_CHECK)         ucg.setColor(0, 240, 0); 
  else if (FLAG_HEAT_CHECK)     ucg.setColor(240, 0, 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);
  // 4.  Вывод времени на экран
  if (tt>0) // выводим если было первоначальная установка от головного блока 
     {
      ucg.setColor(0, 0, 180);  // 
      ucg.drawBox(180, 0, 45, 23);
      ucg.setColor(250, 250, 0);
      ucg.setPrintPos(180,19);
      temp=hour(tt);
      if (temp<10) ucg.print("0");
      ucg.print(temp);
      ucg.print(":");
      temp=minute(tt);
      if (temp<10) ucg.print("0");
      ucg.print(temp);
     }
  #endif
  sei();
}  

void print_error_DHT(void) // Печать ошибки чтения датчиков выводится при каждом чтении датчика
{
  if (infoScreen==true) return;        // если отображен информационный экран то ничего не выводим  
 // 1. печать ошибки чтения датчиков
  if (packet.DHT_error!=last_error)        // если статус ошибки поменялся то надо вывести если нет то не выводим - экономия время и нет мерцания
  {
      cli();
      last_error=packet.DHT_error; 
      ucg.setColor(0, 0, 180);         // Сначала стереть
      ucg.drawBox(280, 0, 36, 18);
      ucg.setPrintPos(280,18); 
      ucg.setFontMode(UCG_FONT_MODE_TRANSPARENT);
      if (packet.DHT_error>0) 
        { 
        ucg.setColor(255, 100, 100); 
        print_StrXY(280,18,F("0x"));
        ucg.print( hex(packet.DHT_error >> 4));
        ucg.print( hex(packet.DHT_error & 0x0f)); 
        }
      else  { ucg.setColor(200, 240, 0);   ucg.print(F(" ok ")); } 
     sei(); 
   }   
} 
//  вывод на экран данных (то что меняется)
void print_data()
{ 
  // Статистика по моточасам, время ведется в интервалах вывода данных = NUM_SAMPLES*TIME_SCAN_SENSOR а потом пересчитывается в часы при выводе.
settingRAM.hour_unit++;
if (FLAG_MOTOR_CHECK) settingRAM.hour_motor++;  // если мотор включен
if (FLAG_HEAT_CHECK)  settingRAM.hour_heat++;   // если нагреватель включен

 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;
uint8_t tmp;
// Работаем через кольцевой буфер
// Добавить новую точку в кольцевой буфер
     // Температура в доме. диапазон -25 . . . +25 растягиваем на 100 точек
     if (packet.tIn<=-2500) tInChart[posChart]=0;           // Если температура меньше -25 то округляем до -25
     else  if (packet.tIn>=2500)  tInChart[posChart]=100-1;   // Если температура больше 25  то округляем до 25
      else tInChart[posChart]=(packet.tIn+2500)/50;    // внутри -25...+25 растягиваем в два раза
   
    if (ChartMotor==true) tInChart[posChart]|=0x80;     // Признак включения мотора- старший бит в 1 - цвет фона на графике меняется
     ChartMotor=false;

     // Температура на улице. диапазон -25 . . . +25 растягиваем на 100 точек
     if (packet.tOut<=-2500) tOutChart[posChart]=0;         // Если температура меньше -25 то округляем до -25
     else  if (packet.tOut>=2500)  tOutChart[posChart]=100-1; // Если температура больше 25  то округляем до 25
      else tOutChart[posChart]=(packet.tOut+2500)/50;  // внутри -25...+25 растягиваем в два раза

    if (ChartHeat==true) tOutChart[posChart]|=0x80;     // Признак включения нагревателя- старший бит в 1 - цвет фона на графике меняется
     ChartHeat=false;

     // Абсолютная влажность в доме диапазон от 0 до 20 грамм на кубометр, растягиваем на 100 точек
     if (packet.absHIn>=2000) absHInChart[posChart]=100-1;
     else absHInChart[posChart]=packet.absHIn/20;   // внутри 0...20 растягиваем в пять  раз в сотых % по этому делем не на 100 а на 20

     // Абсолютная влажность на улицу диапазон от 0 до 20 грамм на кубометр, растягиваем на 100 точек
     if (packet.absHOut>=2000) absHOutChart[posChart]=100-1;
     else absHOutChart[posChart]=packet.absHOut/20;   // внутри 0...20 растягиваем в пять раз,  в сотых % по этому делем не на 100 а на 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;
    
     // нарисовать фон в зависимости от статуса мотора
     if  (tOutChart[x]>=0x80) ucg.setColor(90, 60, 0);     // Нагреватель был ключен - бледно желтый
     else if (tInChart[x]>=0x80) ucg.setColor(0, 60, 90);  // Мотор был ключен - бледно синий
     else                     ucg.setColor(0, 0, 0);       // все выключено
      
     ucg.drawVLine(5+120-i,237-100,100); 
     ucg.drawVLine(5+120-i+162,237-100,100);  
     
     ucg.setColor(180, 180, 180);  
     if (i%5==0) // Пунктирные линии графика
     {
       ucg.drawPixel(5+120-i,236-10-1);
       ucg.drawPixel(5+120-i,236-50-1);
       ucg.drawPixel(5+120-i,236-90-1);
       
       ucg.drawPixel(5+120-i+162,236-25-1);
       ucg.drawPixel(5+120-i+162,236-50-1);
       ucg.drawPixel(5+120-i+162,236-75-1);  
     } 
     
     // Вывести новую точку
     tmp=tInChart[x]&0x7f;   // Отбросить старший разряд - признак включения мотора
     if ((tmp==0)||(tmp==100))   ucg.setColor(255, 255, 255); else ucg.setColor(255, 100, 100); 
     ucg.drawPixel(5+120-i,236-tmp);

     tmp=tOutChart[x]&0x7f;   // Отбросить старший разряд - признак включения калорифера   
     if ((tmp==0) || (tmp==100)) ucg.setColor(255, 255, 255); else ucg.setColor(100, 255, 100); 
     ucg.drawPixel(5+120-i,236-tmp);
     
     if (absHInChart[x]==100) ucg.setColor(255, 255, 255); else ucg.setColor(255, 100, 100); 
     ucg.drawPixel(5+120-i+162,236-absHInChart[x]);
 
     if (absHOutChart[x]==100) ucg.setColor(255, 255, 255); else ucg.setColor(100, 255, 100); 
     ucg.drawPixel(5+120-i+162,236-absHOutChart[x]);
       }
     sei(); 
   } 
 if (posChart<120-1) posChart++; else posChart=0;            // Изменили положение в буфере и Замкнули буфер
} 

// ---ПЕРЕДАЧА ДАННЫХ ЧЕРЕЗ РАДИОМОДУЛЬ -----------------------------
#ifdef  RADIO    // Радио модуль NRF42l
void send_packet()
{       radio.powerUp();
        radio.stopListening();     // Остановить приемник
        delay(2);
        cli();
          radio.writeBlocking(&packet,sizeof(packet),200);  // Writes 1 payload to the buffers
          send_packet_ok=radio.txStandBy();
          if ( radio.isAckPayloadAvailable() )   // Ждем получения -- основной блок передает текущее время
          radio.read(&tt,sizeof(tt));            //... и имеем переменную tt с временем от приемника.
        sei(); 
         if (send_packet_ok!=true) err_ask++;            // квитанция не получена добавить счетчик
         #ifdef BEEP
           if (send_packet_ok==true) beep(30);           // Пакет передан успешно
         #endif
         #ifdef  DEBUG  
           if (send_packet_ok==true)  Serial.println(F("Packet sending ++++++++++"));
           else                       Serial.println(F("Packet NOT sending -----------"));
         #endif   
        radio.startListening();    // Включить приемник
        packet.count++;            // при переполнении сам сбросится
 }  
#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.DHT_error=readDHT(PIN_DHT22a);   // ПЕРВЫЙ ДАТЧИК ДОМ  Новый пакет, сбросить все ошибки и прочитать первый датчик
 #ifdef  DEMO
   DHT.temperature=packet.tIn/100+random(-20,30)/10.0; 
   if (DHT.temperature>20) DHT.temperature=19;
   if (DHT.temperature<-10) DHT.temperature=-9;
   DHT.humidity=packet.relHIn/100+(float)random(-5,8);
   if (DHT.humidity>96) DHT.humidity=90;
   if (DHT.humidity<1) DHT.humidity=10;
   packet.DHT_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)+HIN_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.DHT_error);
    #endif
 
 packet.DHT_error=packet.DHT_error+16*readDHT(PIN_DHT22b);// ВТОРОЙ ДАТЧИК УЛИЦА  ошибки в старшие четыре бита
 #ifdef  DEMO
   DHT.temperature=packet.tOut/100+random(-20,30)/10.0; 
   if (DHT.temperature>30) DHT.temperature=19;
   if (DHT.temperature<-30) DHT.temperature=-9;
   DHT.humidity=packet.relHOut/100+(float)random(-5,8);
   if (DHT.humidity>96) DHT.humidity=90;
   if (DHT.humidity<1)  DHT.humidity=10;
   packet.DHT_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.DHT_error);
    #endif   
 
 print_error_DHT();    // Вывод ошибки чтения датчика при каждом чтении контроль за качеством связи с датчиками
 
 if (packet.DHT_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_data();                             // вывод усредненных значений 
         print_status();                           // панель состояния
         
         // Обновляем максимум и минимум температур в EEPROM
         // Cделано не честно - обновляем раз в 30 секунд (NUM_SAMPLES*TIME_SCAN_SENSOR) а пишем раз в час, могут пропасть данные при сбросе
         if (settingRAM.tInMax<packet.tIn) settingRAM.tInMax=packet.tIn;
         else if (settingRAM.tInMin>packet.tIn) settingRAM.tInMin=packet.tIn;
         
         if (settingRAM.tOutMax<packet.tOut) settingRAM.tOutMax=packet.tOut;
         else if (settingRAM.tOutMin>packet.tOut) settingRAM.tOutMin=packet.tOut;
 
         // Пишем в EEPROM не каждый раз!! экономим ресурс записей 100000 мин.
         settingRAM.tick_eeprom++;
         if (((long)settingRAM.tick_eeprom*NUM_SAMPLES*TIME_SCAN_SENSOR)>=(long)TIME_HOUR) // пора писать eeprom
            { writeEeprom();  settingRAM.tick_eeprom=0;  } 

             // Запись статистики в EEPROM
         if (FLAG_MOTOR_CHECK) ChartMotor=true;    // Признак того что надо показывать включение мотора на графике
         if (FLAG_HEAT_CHECK) ChartHeat=true;      // Признак того что надо показывать включение нагревателя на графике 
         
         TimeChart++;
         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
             } 
        }
    #ifdef  RADIO       // Радио модуль NRF42l 
        // Часы работают если только был получено время от головного блока (хотя бы один раз) в противном случае они даже не показываются на экране
        if (tt>0) tt=tt+TIME_SCAN_SENSOR/1000;       // Обновить текущее время - что бы часы шли и без связи с базовым блоком  
    #endif 
    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,key1; 
    cli(); 
    key=ReadPort(D,3);                                // Прочитать кнопку 0 - нажата 
    delay(25);                                        // Задержка для подавления дребезга контактов
    key1=ReadPort(D,3);                               // читаем еще раз кнопку
    if (key!=key1) {resetKey();return;}               // если значения не равны то ложное срабатывание выходим
    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(void)
{
int tmp=0;
// 0.  Проверить замораживание подвала НАГРЕВАТЕЛЬ 
if (packet.tIn<=TEMP_LOW) { MOTOR_OFF; HEAT_ON; return;}   // Контроль от промораживания подвала по идеи здесь надо включать калорифер
if ((FLAG_HEAT_CHECK)&&(packet.tIn>TEMP_LOW+dT_OFF+50)) HEAT_OFF;    // Выключить калорифер когда температура поднимится на 0.5 градуса

// 1. Режимы не зависящие от влажности и температуры ВЫСШИЙ приоритет
if ((settingRAM.mode==BLOCK_OFF)&&(~FLAG_MOTOR_CHECK))  return;
if ((settingRAM.mode==BLOCK_OFF)&&(FLAG_MOTOR_CHECK))  { MOTOR_OFF ; return;}
if ((settingRAM.mode==HOOD_ON )&&(FLAG_MOTOR_CHECK))   return;
if ((settingRAM.mode==HOOD_ON )&&(~FLAG_MOTOR_CHECK))  { MOTOR_ON  ; return;}

// 2. Режим охлаждения (второй приоритет) температура внутри больше 10 градусов темература снаружи меньше на 2 градуса чем внутри, на влажность не смотрим
if (settingRAM.mode==COOLING)          // Режим охлаждение
  {
    if ((~FLAG_MOTOR_CHECK)&&(packet.tIn>(packet.T_min*10))&&((packet.tIn-packet.tOut)>packet.dH_min)) // dH_min используется не штатно для температуры
       {MOTOR_ON; return;}            // мотор выключен, температура выше установленной и снаружи температура ниже на 2 градуса  то ВКЛЮЧЕНИЕ мотора
    if ((FLAG_MOTOR_CHECK)&&(packet.tIn<=packet.tOut))   
       {MOTOR_OFF; return;}        // мотор включен и темература внутри ниже наружней то ВЫКЛЮЧЕННИЕ мотора
   return;                             // изменений нет выходим    
  } 
// 3. В режиме осушения - проверка на достижение минимальной температуры помещения в режиме осушения - СРОЧНО ВЫКЛЮЧИТЬ  третий приоритет
if (packet.tIn<=(packet.T_min*10))
   {
     if (~FLAG_MOTOR_CHECK)   return;      // Мотор уже выключен, выходим
     else  { MOTOR_OFF; return;}           // выключить и выйти
   } 
   
// 4. Режимы зависящие от температуры и влажности низший приоритет (что осталось)
// Расчитываем разность срабатывания по влажности
if (FLAG_ABS_H_CHECK)   tmp=packet.dH_min;                 // Если режимы используют абсолютную разность в сотых грамма на куб
else tmp=(int)(packet.absHIn*(packet.dH_min/10)/100);      // Если режимы используют ДЕСЯТЫЕ % от абсолютной разности внутренней температуры 

 if ((~FLAG_MOTOR_CHECK)&&(packet.tIn>(packet.T_min*10))&&((packet.absHIn-tmp)>packet.absHOut)) 
        {MOTOR_ON; return;}        // мотор выключен, темература выше критической, абс влажность с наружи меньше  то ВКЛЮЧЕНИЕ мотора
 if ((FLAG_MOTOR_CHECK)&&((packet.tIn<=(tmp+dT_OFF))||(packet.absHIn<(packet.absHOut+dH_OFF)))) 
        {MOTOR_OFF; return;}       // мотор включен и темература ниже критической или абс влажность внутри ниже  то ВЫКЛЮЧЕННИЕ мотора
} 

// Вывод информации о настройках и сохрание индекса настроек в eeprom ---------------------------------
void Setting()
{ // Настройка
  cli();
  ucg.setColor(0, 100, 255);
  ucg.setFontMode(UCG_FONT_MODE_SOLID);
  ucg.setPrintPos(0,25+18); 
  switch (settingRAM.mode)
        {
        case  BLOCK_OFF: ucg.print(F("Выключено              ")); packet.dH_min=255; packet.T_min=255; FLAG_ABS_H_ON;  break; 
        case  HOOD_ON:   ucg.print(F("Режим вытяжки         "));  packet.dH_min=0;   packet.T_min=0;   FLAG_ABS_H_ON;  break; 
        case  COOLING:   ucg.print(F("Охлаждение T>10 dT>2"));    packet.dH_min=200; packet.T_min=100; FLAG_ABS_H_ON;  break; // dH_min используется не штатно для температуры     
        case  3:         ucg.print(F("Осушение T>+3 dH>0.3 "));   packet.dH_min=30;  packet.T_min=30;  FLAG_ABS_H_ON;  break;
        case  4:         ucg.print(F("Осушение T>+3 dH>5% "));    packet.dH_min=50;  packet.T_min=30;  FLAG_ABS_H_OFF; break;
        case  5:         ucg.print(F("Осушение T>+4 dH>0.6 "));   packet.dH_min=60;  packet.T_min=40;  FLAG_ABS_H_ON;  break;
        case  6:         ucg.print(F("Осушение T>+4 dH>10% "));  packet.dH_min=100; packet.T_min=40;  FLAG_ABS_H_OFF; break;
        case  7:         ucg.print(F("Осушение T>+5 dH>0.8 "));   packet.dH_min=80;  packet.T_min=50;  FLAG_ABS_H_ON;  break;
        } 
 writeEeprom();       // Запись в EEPROM  новых настроек
 SET_MODE_BITS;       // В пакет для передачи добавили смену режима
 MOTOR_OFF;           // Поменяли настройки - отключить мотор, пусть заново настройки сработают если потребуется
 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(F("  ")); // Стереть хвост от предыдущего числа
} 

// Вывод строки константы в координаты 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(5, 5, 320-1-10, 240-1-10);
  ucg.setColor(0, 50, 250);
  ucg.drawFrame(5+5, 5+5, 320-1-20, 240-1-20);
  
  ucg.setFontMode(UCG_FONT_MODE_TRANSPARENT);
  ucg.setColor(0, 200, 10);
  print_StrXY(35,12+16,F("ОСУШИТЕЛЬ на Arduino Pro Mini"));
  
  ucg.setColor(0, 50, 50);
  print_StrXY(5+10,15+16*2,F("1 Напряжение питания В.")); 
  print_floatXY(5+222,15+16*2,readVcc()/1000.0);
 
  print_StrXY(5+10,15+16*3,F("2 Температура блока гр.")); 
  print_floatXY(5+222,15+16*3,GetTemp());
 
  print_StrXY(5+10,15+16*4,F("3 Мото часы блока")); 
  ucg.setPrintPos(5+222,15+16*4); 
  ucg.print(settingRAM.hour_unit/(TIME_HOUR/(NUM_SAMPLES*TIME_SCAN_SENSOR))); 
  
  print_StrXY(5+10,15+16*5,F("4 Мото часы вентилятора")); 
  ucg.setPrintPos(5+222,15+16*5); 
  ucg.print(settingRAM.hour_motor/(TIME_HOUR/(NUM_SAMPLES*TIME_SCAN_SENSOR))); 
  
  print_StrXY(5+10,15+16*6,F("5 Мото часы нагревателя")); 
  ucg.setPrintPos(5+222,15+16*6); 
  ucg.print(settingRAM.hour_heat/(TIME_HOUR/(NUM_SAMPLES*TIME_SCAN_SENSOR)));  
 
  print_StrXY(5+10,15+16*7,F("6 Мах температуры in/out")); 
  ucg.setPrintPos(5+222,15+16*7);
  ucg.print((float)settingRAM.tInMax/100,1); 
  ucg.print(F("/"));
  ucg.print((float)settingRAM.tOutMax/100,1); 
  
  print_StrXY(5+10,15+16*8,F("7 Мin температуры in/out")); 
  ucg.setPrintPos(5+222,15+16*8);
  ucg.print((float)settingRAM.tInMin/100,1); 
  ucg.print(F("/"));
  ucg.print((float)settingRAM.tOutMin/100,1); 
  
  print_StrXY(5+10,15+16*9,F("8 Канал NRF24l01+")); 
  ucg.setPrintPos(5+222,15+16*9); 
  ucg.print(NRF24_CHANEL); 
  
  print_StrXY(5+10,15+16*10,F("9 Гистерезис T/absH   ")); 
  ucg.print((float)dT_OFF/100.0);
  ucg.print(F("/"));
  ucg.print((float)dH_OFF/100.0);
  
#ifndef DEMO     // Уже места не хватает в DEMO. В DEMO кусок ниже не показывается и код сокращается
  print_StrXY(5+10,15+16*11,F("10 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.drawHLine(40,18+16*11,320-1-80);
 
  ucg.setColor(0, 0, 150);
  print_StrXY(5+10,19+16*12,F("СБРОС - Вкл. при нажатой кнопке.")); 
 
  ucg.setColor(250,80,80);
  print_StrXY(5+10,19+16*13,F(VERSION)); 
  
  #ifdef  RADIO     // Радио модуль NRF42l вывести число ошибок ask
     ucg.setColor(0, 150, 10);
     ucg.print(F(" AskErr:"));
     ucg.print(err_ask);
  #endif
  sei();
 #endif   // Сокращение кода
  #ifdef BEEP
   beep(40);
  #endif
 
} 

void clearInfo()  // Стереть информационный экран
{
      infoScreen=false;
      last_error=100;         // Признак обновления ошибки
      cli();
      ucg.setColor(0, 0, 0);  // залить черным
      ucg.drawBox(5, 5, 320-1-10, 240-1-10);
      print_static();
      Setting();  
      print_data();                            
      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)); 
   if ((settingRAM.mode>(NUM_SETTING-1))||(settingRAM.mode<0)) settingRAM.mode=0;        // гарантированно попадаем в диапазон
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, LOW);  
  delay(100);
  digitalWrite(PIN_RESET, HIGH);  
  ucg.begin(UCG_FONT_MODE_TRANSPARENT);
  ucg.setFont(my14x10rus);  
//   ucg.setRotate90();
  ucg.setRotate270();
  ucg.clearScreen();
}

#ifdef BEEP  
  void beep(int x)  // Пищать х мсек
  {
    WritePort(C,1,HIGH);
    delay(x);
    WritePort(C,1,LOW);
  } 
#endif

void boot_test(void)
{
  int i;
  ucg.clearScreen();
  ucg.setColor(255, 255, 255);
  print_StrXY(10,50,F("Тест вентилятора"));
  MOTOR_ON;
      for(i=0;i<5;i++)
        {
         delay(1000);
         ucg.print(F(" ."));
        }
  MOTOR_OFF;      
  delay(1000);      
  #if ID == 0x21   // Только у 21 блока установлен нагреватель
      ucg.setColor(255, 255, 255);
      print_StrXY(10,70,F("Тест нагревателя"));
      HEAT_ON;
          for(i=0;i<5;i++)
            {
             delay(1000);
             ucg.print(F(" ."));
            }
      HEAT_OFF;   
  #endif  
  ucg.clearScreen();        
}

Внимание! Тест обогревателя делается только для id 0х21 (у меня в подвале нет обогревателя 0х22).

 

 

starcom
Offline
Зарегистрирован: 11.01.2016

Привет. Проект действительно шикарный! Но вот такая просьба,

раз уж он в таких муках рождался с тяжелой отладкой и настройкой,

нельзя ли хоть на эскизик схемы взгянуть?

miaua
Offline
Зарегистрирован: 20.08.2015

Ну какая там схема? Все понятно из назначений пинов в программе.

Вот такая ...nano

Thunderix
Offline
Зарегистрирован: 12.09.2015

Вот смастерил платку. Питание одно на 3.3 вольта. Три дня тестирую вроде все нормально.

BogAD
Offline
Зарегистрирован: 30.01.2016

РЕСПЕКТ за проект!

Давно мысля мучает реализовать сие устройство, но...нет практики программирования МК.

Можно нетоторые воросы по проекту?

Pav2000, твои строки расчета абсолютной влажности из программы:

temp=pow(2.718281828,(17.67*t)/(t+243.5));
return (6.112*temp*h*2.1674)/(273.15+t);
 
Соглассно твоей ссылки http://www.pogoda.by/glossary/?nd=3&id=24
над водой — E= 6.1121exp (18.678 – t / 234.5) t / (257.14 + t);
над льдом — E*= 6.1115exp (23.036 – t / 333.7) t / (279.82 + t), где t — температура воздуха [°C].
Что то не въезжаю, можешь пояснить, некоторые константы не сходятся..
 
И еще... 
Интересует, почему не использовать понятие "давление насыщенного пара". Я даже гратик проинтерполировал...
И последнее. 
На STM32 не рассматривал возможность реализации? 
Хочу реализовать на одном камне в гараже сразу на 3 объекта контроля (Гараж, цоколь и погреб)
Имеется Stm32f103rbt6 с 2,8 Tft сенсорным экраном
Осталось добавить связь (если необходимо) и несколько оптореле.
Да и согласование с 3,3В не требуется...
ps От сервомашинок для вращения шибкерных заслонок не хочу отказываться. 
 
pav2000
Offline
Зарегистрирован: 15.12.2014

miaua пишет:

Ну какая там схема? Все понятно из назначений пинов в программе.

Спасибо за схему miaua.  А то на листочке рисовать не кошерно, а в программу надо осваивать.  И в моем понимании надо схему сразу с разводчиком плат (что то типа орла). Но до орла пока руки не доходят.

Thunderix пишет:

Вот смастерил платку. Питание одно на 3.3 вольта. Три дня тестирую вроде все нормально.

Хорошо получилось -)), осталось только откорпусить.

Вопросы по реализации

1. Питание 3.3  Ардуино работает на 8 мГц или разогнали до 16 мГц (такое возможно, но выходит за рамки даташита, может пострадать стабильность)?

2. Источник питания будет встроенный или выносной?

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

BogAD пишет:

Pav2000, твои строки расчета абсолютной влажности из программы:

temp=pow(2.718281828,(17.67*t)/(t+243.5));
return (6.112*temp*h*2.1674)/(273.15+t);
 
Соглассно твоей ссылки http://www.pogoda.by/glossary/?nd=3&id=24
над водой — E= 6.1121exp (18.678 – t / 234.5) t / (257.14 + t);
над льдом — E*= 6.1115exp (23.036 – t / 333.7) t / (279.82 + t), где t — температура воздуха [°C].
Что то не въезжаю, можешь пояснить, некоторые константы не сходятся..
И еще... 

Интересует, почему не использовать понятие "давление насыщенного пара". Я даже гратик проинтерполировал...

 
Я указывал в первом посте другую ссылку -  https://carnotcycle.wordpress.com/2012/08/04/how-to-convert-relative-humidity-to-absolute-humidity/
 
Прикол - Буржуины сделали ссылку на мой проект, как вычислили не понятно.
 
Кажется ссылка http://www.pogoda.by/glossary/?nd=3&id=24 упоминалась на хабре.
Почему расходятся - потому что это эмпирическая формула и она может выглядеть поразному. Формула должна описывать определенную кривую.  Точной формулы нет.
 
А смысл использования насыщенног пара.  Дополнительный параметр?
 

BogAD пишет:

И последнее. 
На STM32 не рассматривал возможность реализации? 
Хочу реализовать на одном камне в гараже сразу на 3 объекта контроля (Гараж, цоколь и погреб)
Имеется Stm32f103rbt6 с 2,8 Tft сенсорным экраном
Осталось добавить связь (если необходимо) и несколько оптореле.
Да и согласование с 3,3В не требуется...
ps От сервомашинок для вращения шибкерных заслонок не хочу отказываться.

 

 
Нет на STM32  реализацию не рассмаривал. Хотя сейчас плавно переползаю на maple mini.
Сейчас сделано один объект - один блок. Но есть головное устройство (maple mini) куда стекаются все данные. У меня сейчас работает два блока (подпол и подвал) и на головном блоке можно смотреть оба объекта.  В структуре передаваемого пакета есть идентификатор блока.
 
По мне проще блок сделать чем провода тянуть и надежность повысится. Может ног не хватить.
Уж если портировать то на esp8266  с управлением через web. Но  куда эти блоки тогда деть -))
 
Сейчас тоже думаю для подпола закрытие приточки при выключении вытяжного вентилятора (много тепла выдувает). ИХМО  б.у. привод белимо с возвратной пружиной с авито (около 2 тыс рублей) будет интереснее и надежнее чем сервомашинки. Вешать параллельно вентилятору.
 
 
 
 
 
 

 

Thunderix
Offline
Зарегистрирован: 12.09.2015

1. Ардуино про 16 мгц. Перепрошил загрузчик и пины. Работает на 3.3 вольта. Проверяю стабильность еще не зависал ни разу.

2. Источник питания будет встроенный.

Калибровками еще не игрался т.к. силовую часть еще не доделал, заодно датчики "приработаются".

LanSWAT
Offline
Зарегистрирован: 08.10.2015

Thunderix пишет:

Не компилица чтото.

 

stmTime\stmTime.cpp.o: In function `breakTime(unsigned long, tmElements_t&)': C:\Program Files\Arduino\libraries\stmTime/stmTime.cpp:151: multiple definition of `breakTime(unsigned long, tmElements_t&)' stmTime\DateStrings.cpp.o:C:\Program Files\Arduino\libraries\stmTime/DateStrings.cpp:151: first defined here stmTime\stmTime.cpp.o: In function `breakTime(unsigned long, tmElements_t&)': C:\Program Files\Arduino\libraries\stmTime/stmTime.cpp:151: multiple definition of `refreshCache(unsigned long)' stmTime\DateStrings.cpp.o:C:\Program Files\Arduino\libraries\stmTime/DateStrings.cpp:151: first defined here stmTime\stmTime.cpp.o: In function `breakTime(unsigned long, tmElements_t&)': C:\Program Files\Arduino\libraries\stmTime/stmTime.cpp:151: multiple definition of `hour(unsigned long)' stmTime\DateStrings.cpp.o:C:\Program Files\Arduino\libraries\stmTime/DateStrings.cpp:151: first defined here

Добрый день. Та же беда - как вы побороли? У меня список еще длинее.

Thunderix
Offline
Зарегистрирован: 12.09.2015

Я отключил эту библиотеку и усё.

LanSWAT
Offline
Зарегистрирован: 08.10.2015

Thunderix пишет:
Я отключил эту библиотеку и усё.

 Отлично. Все получилось. Проект отличный, но есть пара вопросов к спецам:

1.На каком питании стоит остановиться 5 или 3,3В.

2.есть ли у кого схема в более крупном масштабе.

Заранее спасибо.

Thunderix
Offline
Зарегистрирован: 12.09.2015

1. Разницы я не обнаружил.

2. Я схему не рисовал. Она вся в скетче есть.

Olymp
Offline
Зарегистрирован: 09.01.2016

pav2000 пишет:
там было установлено твердотельное реле шарп S202S02

Добрый день!

Подскажите пожалуйста, как правильно подключить данное реле к arduino, я так понял, что управляющее напряжение этого реле 1.2-1.4. Из фотографий устройства я не смог понять.

Спасибо!

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

Ловите : http://www.mikrocontroller.net/topic/214135

И почитайте где-то как задают ток через диоды ( про закон Ома)

Olymp
Offline
Зарегистрирован: 09.01.2016

trembo пишет:

Ловите : http://www.mikrocontroller.net/topic/214135

И почитайте где-то как задают ток через диоды ( про закон Ома)

Спасибо, это видел, но как то не доверился, похоже достаточно сопротивления на 330-390 Ом.

Тут через 1кОм подключают:

http://hobby-experiment.blogspot.ru/2014/08/sso-1-solid-state-ac-outlet....

 

Viacheslav77
Offline
Зарегистрирован: 16.05.2016

pav2000 пишет:

Добрался до паяльника. Поставил RC цепь на кнопку. После этого кнопка стала работать хорошо.

Почему дребезг контактов не реализован программно? Это ж вроде основа разработки микроэлектроники? Пару-тройку байтов на переменные, конечно, съест, зато цена - условный 0. Можно реализовать нажатие и отпускание, можно оперативно менять период подавления.

 

Buldakov
Offline
Зарегистрирован: 17.01.2016

Маленький вопрос , если можно.

Есть у меня градусник Лт-300. Из нескольких штук выбрал самый лучший. с точностью 0.01 градуса в диапазоне от -50 до +150 град. Решил я им дома померить температуру воздеха. На уровне стола она была 25 град. опустил я градусник на 20 см вниз температура изменилась на 0.5 градуса. Поскольку я своему градуснику могу верить решил проверить то же самое с водой в ведре нагретом до 60 град. Там гуляние температуры оказалось еще больше.

Теперь к данной теме.

Вы уверены , что температурный датчик у вас линеен? И всем ли циферкам у вас можно верить? А по влажности еще больше проблем. Если откроем психометричекую таблицу то изменение в показаниях сухого и влажного термометра на 0.5 град. даст нам погрешность по влажности порядка 5 проц. А вы выводите с точностью до 0.01 Это получается что показания могут врать в пределах 500 единиц младшего разряда. Ну обычно так не делают. Выводите температуру для точного термометра с точностью 0.1 град для неточного с точностью 1 град. А влажность так тем более с точностью 1 процент. Ну не может она измеряться более точно простыми датчиками.

Thunderix
Offline
Зарегистрирован: 12.09.2015

А почему СТРОЕМ не ходите :)

pav2000
Offline
Зарегистрирован: 15.12.2014

Первый абзац повящен неравномерности температуры в объеме. Это понятно и очевидно. Методы получения "правильной" температуры известны или усреднение многих датчиков или размещение датчика где то по середине объема.

Кажется в этой ветке упоминалось порядок размещения датчиков.

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

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

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

Как то так.

 

 

Alenka_x
Offline
Зарегистрирован: 01.07.2016

Здравствуйте!
Для реализации проекта по автоматизации и мониторингу климатических условий (температура, влажность) мы ищем  разработчика с опытом. Если Вам интересно - пишите  alenka_x@inbox.ru.

VladMaster
Offline
Зарегистрирован: 14.04.2016
Здравствуйте,работаю на этом поприще с 16 лет,ориентируюсь во всех средах и языках программирования.Сейчас интересны проекты на платах Arduino(вся линейка).Портфолио насыщенно сильными и интересными заказами,впрочем, за дело!На данном vlad.spbstu@mail.ru почтовом адресе либо при личной переписке ВК вам будут даны бесплатные консультации по любым интересующимся вам вопросам,не только лично мною,но и моими коллегами,соответственно заказы будут также обрабатываться по мере поступления,вам вскоре будет прислан ответ,и соответственно уже нами произведённый анализ вашего ТЗ , после чего обговаривается условия нашего долгосрочного сотрудничества. 
Ждём с нетерпением,готов приступить к выполнению 24/7.
russo
Offline
Зарегистрирован: 20.11.2014

VladMaster пишет:

Здравствуйте,работаю на этом поприще с 16 лет,ориентируюсь во всех средах и языках программирования.Сейчас интересны проекты на платах Arduino(вся линейка).Портфолио насыщенно сильными и интересными заказами,впрочем, за дело!На данном vlad.spbstu@mail.ru почтовом адресе либо при личной переписке ВК вам будут даны бесплатные консультации по любым интересующимся вам вопросам,не только лично мною,но и моими коллегами,соответственно заказы будут также обрабатываться по мере поступления,вам вскоре будет прислан ответ,и соответственно уже нами произведённый анализ вашего ТЗ , после чего обговаривается условия нашего долгосрочного сотрудничества. 
Ждём с нетерпением,готов приступить к выполнению 24/7.

Ну выложите интересный проэкт подобного направления. Вам 17 лет? Пока это только слова рекламы. И не флудите в чужих темах.

fedusiv
Offline
Зарегистрирован: 16.09.2016

Здравствуйте! Не компилится проект, выдает ошибки:

sketch_sep16a:68: error: 'dht' does not name a type
 
 dht DHT;                                        // Датчики
 
sketch_sep16a:459: error: expected primary-expression before '.' token
 
   byte err=-1*DHT.read22(pin); // Чтение датчика
sketch_sep16a:483: error: expected primary-expression before '.' token
 
      sensors.tIn=(int)(DHT.temperature*100.0)+TIN_ERR;  // Запомнить результаты для суммирования
 
Помогите пожалуйста, кто сталкивался. Спасибо.
Olymp
Offline
Зарегистрирован: 09.01.2016

fedusiv пишет:

Здравствуйте! Не компилится проект, выдает ошибки:

sketch_sep16a:68: error: 'dht' does not name a type
 
 dht DHT;                                        // Датчики
 
sketch_sep16a:459: error: expected primary-expression before '.' token
 
   byte err=-1*DHT.read22(pin); // Чтение датчика
sketch_sep16a:483: error: expected primary-expression before '.' token
 
      sensors.tIn=(int)(DHT.temperature*100.0)+TIN_ERR;  // Запомнить результаты для суммирования
 
Помогите пожалуйста, кто сталкивался. Спасибо.

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

вы подключили библиотеку (#include "DHT.h")?

покажите строку, в которой вы рассказываете, на каком пине ваш датчик (DHT dht(DHTPIN, DHTTYPE);)

serand
Offline
Зарегистрирован: 19.05.2016

Добрый день! Тоже не компилируется. .Выложил ссылку на скрин.Помогите,не могу понять в чем дело. Не компилируется DHT

https://yadi.sk/i/9qFQV5QqxTtHL

serand
Offline
Зарегистрирован: 19.05.2016

Кто нибудь дайте переработанную библиотеку DHT,А то по ходу у меня из за нее не компилируется

serand
Offline
Зарегистрирован: 19.05.2016

HELP!!!Не компилируется. Дайте ссылку на правильную библиотеку DHT

Viacheslav77
Offline
Зарегистрирован: 16.05.2016

#include "DHT.h"

#define DHTPIN 2     // what digital pin we're connected to

// Uncomment whatever type you're using!
//#define DHTTYPE DHT11   // DHT 11
#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
//#define DHTTYPE DHT21   // DHT 21 (AM2301)

// Connect pin 1 (on the left) of the sensor to +5V
// NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1
// to 3.3V instead of 5V!
// Connect pin 2 of the sensor to whatever your DHTPIN is
// Connect pin 4 (on the right) of the sensor to GROUND
// Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor

// Initialize DHT sensor.
// Note that older versions of this library took an optional third parameter to
// tweak the timings for faster processors.  This parameter is no longer needed
// as the current DHT reading algorithm adjusts itself to work on faster procs.
DHT dht(DHTPIN, DHTTYPE);

Не вникал, топик читал год назад, на что обратил внимание - выделил жирным

v-da
Offline
Зарегистрирован: 17.08.2016

На перевый взгляд, думаю это дисплей такой: https://www.itead.cc/display/nextion.html

pav2000
Offline
Зарегистрирован: 15.12.2014

Это не такой дисплей, используется простой дисплей 2.8 дюйма на чипе ili9341. Со spi выходом.

Библиотека днт вроде стандартная.  Ничего не дорабатывал в ней.

 

garri671
Offline
Зарегистрирован: 05.04.2017

Доброго времени суток, уважаемые мастера! Подскажите какие модули небходимы для реализации проекта: контроль температуры в помещении и передача данных по радиоканалу (wi-fi) на ПК (сервер). Первый пункт собрал на ардуино нано 3 с dht12. А вот какой модуль  для передачи данных и как это все прикрутить? Подскажите начинающему. Заранее благодарен.

Olymp
Offline
Зарегистрирован: 09.01.2016

Для передачи по вай-фай - ESP-8266.

Для измерения температуры и её передачи достаточно одного этого модуля, нано будет лишним.

garri671
Offline
Зарегистрирован: 05.04.2017

Спасибо, но данного модуля нет в FLProg. Как его программировать?

Olymp
Offline
Зарегистрирован: 09.01.2016

garri671 пишет:

Спасибо, но данного модуля нет в FLProg. Как его программировать?

Попробуйте начать с этого:

https://esp8266.ru/arduino-ide-esp8266/

 

Виктор2345
Offline
Зарегистрирован: 09.04.2017

Здравствуйте.

Нам нужно или сделать или организовать изготовление аналогичной установки. С Вас программная часть и его отладка. Просим Вас помочь. Работа оплачиваемая. Пишите 9217175@gmail.com/

Спасибо

storm134
Offline
Зарегистрирован: 14.04.2017

Здравствуйте. Хочу предложить Вам вынести все шрифты и текстовые константы, а так же данные ЕЕПРОМ во внешнюю ЕЕПРОМ большего объема. Например 24lc256. Подгружаться  данные бelen непосредственно перед использованием в массив 32 байта. Что очень экономит место как на самой флешь контроллера, так  и оперативку.   Адреса начала констант по #define.  Будет марока с планированием памяти и записью всего объема в ЕЕПРОм при прошивке , но при работе программы сложностей нет.

Приведу пример кода где это используется.

 

#include <Wire.h>
#include <DallasTemperature.h>
#include <SoftwareSerial.h>
#include "RTClib.h"
#include <MsTimer2.h>

#define EEPROM_I2C 0x50         // Адрес памяти 24LC256

#define MASTER "+79635431264"
//  0-1  Для подключения компьютера или синезуба. Управление и диагностика.
#define DS18B20 2
#define PLAY 3
#define VOX_OFF 4
#define CH_UP1 5
#define CH_DN1 6
#define MAIN_POWER 7
#define M_RX 8
#define M_TX 9
#define OPEN_DET 10
#define RELAY_1 11
#define BATTERY A1

// 18-19 i2c RTC  для определения времени.
//  now.dayOfTheWeek() 6 и 7 выходные дни.
//  DateTime now = rtc.now();


// Разбивка ЕЕПРОМ
#define ALARM_TEMP 1        // 4
#define LOW_TEMP 5          // 4
#define ATI 64              // 3     ATI
#define CSQ 67              // 6     AT+CSQ
#define CLIP 73             // 9     AT+CLIP=1
#define CMGF 82             // 9     AT+CMGF=1
#define CSCS 91             // 13    AT+CSCS="GSM" 
#define CNMI 104            // 11    AT+CNMI=2,2
#define PB 115              // 8     +PBREADY
#define CMT 126             // 4     +CMT
#define CMGS 130            // 9     AT+CMGS="
#define CMGS1 140           // 1     "
#define CUSD 141            // 9     AT+CUSD=1
#define ATD 150             // 3     ATD
#define CMGD 153            // 11    AT+CMGD=0,4
#define CSQ1 168            // 4     +CSQ
#define RING 172            // 4     RING
#define INFO 176            // 4     info
#define BALANCE 180         // 5     #105#
#define MOVE 185            // 4     move
#define MAX_TEMP 189        // 8     maxtemp:
#define MIN_TEMP 197        // 8     mintemp:
#define MSG1 205            // 17    Couldn't find RTC
#define MSG2 222            // 22 Find RTC, start timing

#define INIT_MODEM 1047     // 11    INIT MODEM
// ------------------------------------------------------------



SoftwareSerial mGSM(M_RX, M_TX);  // Цепляем софт UART для модема на пины
OneWire oneWire(DS18B20);         // Цепляем датчик температуры
DallasTemperature ds(&oneWire);   // -//-
RTC_DS1307 rtc;                   // Цепляем часики
DateTime now;                     // Тут данные с часиков


boolean MAIN_POWER_FLAG = true;
boolean LOW_BATTERY_FLAG = true;
boolean OPEN_FLAG = true;
boolean TEMP_FLAG = true;

int voltage;
int ch = 0;

String val = "";

unsigned char rdata[32];

float temperature;

void setup() {
  // while (!Serial); // for Leonardo/Micro/Zero
  analogReference(EXTERNAL);  // Опорное будет 2,5в с TL431.
  Serial.begin(9600);
  mGSM.begin(9600);


  pinMode(PLAY, OUTPUT);
  pinMode(VOX_OFF, OUTPUT);
  pinMode(CH_UP1, OUTPUT);
  pinMode(CH_DN1, OUTPUT);
  pinMode(RELAY_1, OUTPUT);

  pinMode(OPEN_DET, INPUT);
  pinMode(MAIN_POWER, INPUT);


  while (!rtc.begin()) {
    readEEPROM(EEPROM_I2C, MSG1, rdata, 17);
    Serial.println((char*)rdata);
    memset(rdata, 0, sizeof(rdata));
    _delay_ms(2000);
  }

  ds.begin();



  MsTimer2::set(60000, get_interupt); // 1 min period
  MsTimer2::start();
  readEEPROM(EEPROM_I2C, MSG2, rdata, 22);
  Serial.println((char*)rdata);
  memset(rdata, 0, sizeof(rdata));
  _delay_ms(1000);
  initModem();

  get_interupt();

  // Получаем напряжение
  detect_low_power();

}

void loop() {

  detect_power_off();
  detect_low_power();
  detect_uart();
  detect_open();

}



// ----------- Получение текущего времени и температуры-----
void get_interupt() {
  now = rtc.now();
  ds.requestTemperatures();
  temperature = ds.getTempCByIndex(0);
  Serial.println(F("Temp: "));
  Serial.println(temperature);
}
//-----------------------------------------------------------

void detect_open() {

  if (digitalRead(OPEN_DET) == HIGH && OPEN_FLAG == false)
  {
    //  тут отправка SMS
    sms(F("OTKRITIE KRISHKI!!!"), MASTER);
  }
  if (digitalRead(OPEN_DET) == LOW && OPEN_FLAG == true)
  {
    OPEN_FLAG = false;
  }

}

void detect_temp() {
  char str;
  readEEPROM(EEPROM_I2C, ALARM_TEMP, rdata, 4);
  str = (char*)rdata;
  if (temperature > ((float)str) && TEMP_FLAG == false) {
    sms("Visokaya temperatura!", MASTER);
    TEMP_FLAG = true;
  }
  memset(rdata, 0, sizeof(rdata));

  readEEPROM(EEPROM_I2C, LOW_TEMP, rdata, 4);
  str = (char*)rdata;
  if (temperature < ((float)str) && TEMP_FLAG == true)
    TEMP_FLAG = false;
  str = "";
  memset(rdata, 0, sizeof(rdata));

}


void detect_power_off() {

  // --------- Проверка питания от сети --------------------
  if (digitalRead(MAIN_POWER) == LOW && MAIN_POWER_FLAG == false)
  {
    //  тут отправка SMS
    sms(F("OTKLUCHENIE PITANIYA!!!"), MASTER);
  }
  if (digitalRead(MAIN_POWER) == HIGH && MAIN_POWER_FLAG == true)
  {
    MAIN_POWER_FLAG = false;
  }

}
// -------------------------------------------------------


//--------- Определить состояние питания батареи ------------

void detect_low_power() {

  voltage = analogRead(A1);
  map(voltage, 0, 1023, 0, 4.2);

  // Проверяем питание, устанавливаем флаг
  if (digitalRead(MAIN_POWER) == HIGH) {
    MAIN_POWER_FLAG = false;
  } else {
    MAIN_POWER_FLAG = true;
    if (voltage < 3.1 && LOW_BATTERY_FLAG == false) {
      LOW_BATTERY_FLAG = true;
      sms(F("NIZKIY ZARYAD!!!"), MASTER);
    }
    if (voltage > 3.1 && LOW_BATTERY_FLAG == true) {
      LOW_BATTERY_FLAG = false;
    }
  }
}
//-----------------------------------------------------------

//--------------- Инициализация модема ----------------------
void initModem() {

  readEEPROM(EEPROM_I2C, INIT_MODEM, rdata, 11);
  Serial.println((char*)rdata);
  memset(rdata, 0, sizeof(rdata));

  readEEPROM(EEPROM_I2C, ATI, rdata, 3);
  mGSM.println((char*)rdata);
  memset(rdata, 0, sizeof(rdata));
  _delay_ms(100);

  readEEPROM(EEPROM_I2C, CSQ, rdata, 6);
  mGSM.println((char*)rdata);
  memset(rdata, 0, sizeof(rdata));
  _delay_ms(100);

  readEEPROM(EEPROM_I2C, CLIP, rdata, 9);
  mGSM.println((char*)rdata);
  memset(rdata, 0, sizeof(rdata));
  _delay_ms(100);

  readEEPROM(EEPROM_I2C, CMGF, rdata, 9);
  mGSM.println((char*)rdata);
  memset(rdata, 0, sizeof(rdata));
  _delay_ms(100);

  readEEPROM(EEPROM_I2C, CSCS, rdata, 13);
  mGSM.println((char*)rdata);
  memset(rdata, 0, sizeof(rdata));
  _delay_ms(100);

  readEEPROM(EEPROM_I2C, CNMI, rdata, 11);
  mGSM.println((char*)rdata);  //отображение смс в терминале сразу после приема (без этого сообщения молча падают в память) "AT+CNMI=2,2"
  memset(rdata, 0, sizeof(rdata));
}
//-------------------------------------------------------------

/*
    Очистка СМС на модеме
*/
void clearsms() {

  readEEPROM(EEPROM_I2C, CMGD, rdata, 11);
  mGSM.println((char*)rdata);
  memset(rdata, 0, sizeof(rdata));
}

/*
    Функция постраничного чтения строк в EEPROM 24LC256
*/
void readEEPROM(int deviceaddress, unsigned int eeaddress,
                unsigned char* data, unsigned int num_chars)
{
  unsigned char i = 0;
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddress >> 8));   // MSB
  Wire.write((int)(eeaddress & 0xFF)); // LSB
  Wire.endTransmission();
  Wire.requestFrom(deviceaddress, num_chars);

  while (Wire.available()) data[i++] = Wire.read();

}

/*
    Функция 1 байтовой записи в EEPROM 24LC256
*/

void writeEEPROMbyte(int deviceaddress, unsigned int eeaddress, byte data )
{
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddress >> 8));   // MSB
  Wire.write((int)(eeaddress & 0xFF)); // LSB
  Wire.write(data);
  Wire.endTransmission();

  _delay_ms(6);
}

byte readEEPROMbyte(int deviceaddress, unsigned int eeaddress )
{
  byte rdata = 0xFF;

  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddress >> 8));   // MSB
  Wire.write((int)(eeaddress & 0xFF)); // LSB
  Wire.endTransmission();
  Wire.requestFrom(deviceaddress, 1);
  if (Wire.available()) rdata = Wire.read();
  return rdata;
}

/*
    Функция отправки СМС
*/

void sms(String text, String phone)  //процедура отправки СМС
{
  readEEPROM(EEPROM_I2C, CMGS, rdata, 9);
  mGSM.println((char*)rdata);
  mGSM.print(phone);
  memset(rdata, 0, sizeof(rdata));

  readEEPROM(EEPROM_I2C, CMGS1, rdata, 1);
  mGSM.println((char*)rdata);
  memset(rdata, 0, sizeof(rdata));
  _delay_ms(500);

  mGSM.print(text);
  _delay_ms(500);

  mGSM.print((char)26);
  _delay_ms(1000);

}


void detect_uart() {

  if (mGSM.available()) {              //есть данные от GSM модуля
    _delay_ms(200);                        //выждем, чтобы строка успела попасть в порт целиком раньше чем будет считана
    while (mGSM.available()) {         //сохраняем входную строку в переменную val
      ch = mGSM.read();
      val += char(ch);
      _delay_ms(10);
    }

    Serial.println(val);                    // дублируем сообщение в терминал

    readEEPROM(EEPROM_I2C, PB, rdata, 8);
    if (val.indexOf((char*)rdata) > -1) initModem(); // Если модем вдруг ребутнулся - проинициализируем снова.
    memset(rdata, 0, sizeof(rdata));          // Стираем массив

    /* ----- определение факта приема СМС и сравнение номера(ов) с заданным(и)*/
    readEEPROM(EEPROM_I2C, CMT, rdata, 4);
    if (val.indexOf((char*)rdata) > -1) {       //если обнаружен СМС (для определения звонка вместо "+CMT" вписать "RING")
      memset(rdata, 0, sizeof(rdata));    // Стираем массив
      if (val.indexOf(MASTER) > -1) {         //если СМС от хозяина
        // Сообщение от нашего номера

        readEEPROM(EEPROM_I2C, INFO, rdata, 4);
        if (val.indexOf((char*)rdata) > -1) {      // если обнаружено кодовое слово info
          memset(rdata, 0, sizeof(rdata));

          info();          // дествия при обнаружении ключевого info
        }

        // тут другие условия

      } else {
        // Сообщение не от нашего номера
      }

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

      val = ""; // стираем буфер
}
  }
}


void info() {

}

   

russo
Offline
Зарегистрирован: 20.11.2014

По моему уже пора на https://geektimes.ru/post/277928/ переходить,

не нужно колхозить с уровнями напряжений и памяти хватит на расширение проекта

цена 2 бакса

sat306
Offline
Зарегистрирован: 19.04.2017

Код не компелируется ругается на dht.h, делаеш его с большей буквы DHT, новые ошибки сыпяться, где копать?

Алексей Р
Offline
Зарегистрирован: 28.06.2017

Добрый день. Есть ли что то подобное работающие на одной ардуино и на 2 или 4 строчном десплее