Очередной МЕТЕО. Но рабочий

rw3ar
Offline
Зарегистрирован: 15.05.2017

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

Работает на Arduino Mega

Измеряет:

- три температуры

- атмосферное давление

- влажность воздуха

Пишет всё это на SD.

Время корректирует по GPS (если ловит, что не всегда).

На дисплее 4х20 отображает:

- три температуры,

- давление

- влажность

- текущее время

- последнее время по GPS.

На запрос по СМС вида:

- *t отсылает строку метеоданных

- "money" - отсылает текущий баланс (запросив его у провайдера).

Система более-менее (не без погрешностей со временем) работает примерно года 3 в деревенском доме. Чаще всего в полностью автономном режиме.

 

rw3ar
Offline
Зарегистрирован: 15.05.2017

Скетч далёк от идеала (особенно в части времени), но повторюсь - система работает, и меня в целом устраивает.

//***************************************************************
//получение СМС с балансом СИМ-карты модуля SIM900
// соединяем:  MEGA    SIM900
//              17       0      (TX)
//              16       1      (RX)
//              G        G 
//              9        9
//
// на SIM900 перемычками J11 J12 выбираем D00 D01
// для получения баланса посылаем с планшета (+79853056321) 
// на SIM900 (+7ХХХХХХХХХХ - номер используемой устройством СИМ-карты) СМС со словом << money >>
//
// монитором последовательного порта можно контролировать процесс
//
//  19 to GPS Module TX*
//  18 to GPS Module RX*
//
//
//  и печать в файл на SD-карте
//
//           UNO       MEGA
//   CS -   pin 4       53
//   MOSI - pin 5       51
//   MISO - pin 6       50
//   CLK -  pin 7       52
//
//****************************************************************

#include <Arduino.h>
#include <SoftwareSerial.h>
#include <SFE_BMP180.h>
#include <TinyGPS.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <avr/pgmspace.h>
#include "RTClib.h"
//#include "RTCInt.h"
#include <SPI.h>
#include <SD.h>
#include "dht.h"
#include <OneWire.h>
#include <DallasTemperature.h>

 TinyGPS gps;
     
 void gpsdump(TinyGPS &gps);
 
#define DS1307_ADDRESS 0x68         // устанавливаем таймер
#define BMP180_ADDRESS 0x77         // устанавливаем датчик давления

#define GSMRXPIN 11
#define GSMTXPIN 10

//#define GPSRXPIN 19
//#define GPSTXPIN 18

#define DHTPIN 3                     // вывод, к которому подключается датчик
#define DHTTYPE DHT22                // DHT 22  (AM2302)

// SoftwareSerial mySerialGSM(GSMTXPIN, GSMRXPIN);    //  TX, RX  GSM   MEGA

LiquidCrystal_I2C lcd(0x3F, 20, 4);  // Устанавливаем дисплей

OneWire  ds(A1);                     // подключен DS18B20 через I2S к A1 пину (резистор к питанию на 4.7к для одного датчика, и 3.3 кОм для двух - обязателен)

DHT dht(DHTPIN, DHTTYPE);

RTC_DS1307 RTC;                       // Creation of the Real Time Clock Object

SFE_BMP180 pressure;                  // You will need to create an SFE_BMP180 object, here called "pressure":

//const int chipSelect = 10;            // for UNO  
const int chipSelect = 53;            // for MEGA


byte led = 13;       //**********************************

double BMP180, T18B20, T1, T11,  T2, P;   // переменные для датчиков давления и температуры
float H, TDH22;
bool J; 
char bufT[9], bufD[11], string[160], stringD[160]; 
byte cfg, cfgA, cfgB;
char status;
char str_tempT1[7];
char str_tempT11[7];
char str_tempT2[7];
char str_tempP[7];
char str_tempH[7];
uint8_t hh, mm, ss;            // час, минута, секунда
uint8_t ddd, mmm;              // день месяца, месяц
uint16_t yyy;                  // год

//*****  пременные для датчиков DS18B20 ****//
 byte present = 0;                           //
 byte data[12], dataA[12], dataB[12];        //
 //******************************************//

 DeviceAddress addr = {0x28, 0xFF, 0x13, 0xDA, 0x80, 0x16, 0x04, 0x25};     
 DeviceAddress addrB = {0x28, 0xFF, 0x89, 0xCE, 0x71, 0x16, 0x04, 0x3F};
//*****************************************//

// создаём недостающие символы
uint8_t bukva_G[8] = {B01100, B10010, B10010, B01100, B00000, B00000, B00000};  // градус
//uint8_t bukva_1[8] = {B00000, B00000, B00100, B01100, B00100, B00100, B01110};  // маленькая единичка
//uint8_t bukva_2[8] = {B00000, B00000, B01100, B10010, B00010, B01000, B11110};  // маленькая двойка
// //uint8_t bukva_3[8] = {B00100, B01110, B10101, B00100, B00100, B00100, B00100};  // стрелка вверх
// //uint8_t bukva_4[8] = {B00100, B00100, B00100, B00100, B10101, B01110, B00100};  // стрелка вниз
// //uint8_t bukva_5[8] = {B00100, B01110, B10101, B00100, B10101, B01110, B00100};  // стрелка вверх-вниз



void setup() 
{
   Serial.begin(9600);  
   Serial1.begin(9600);
   Serial2.begin(9600);
   
  // RTC.adjust(DateTime(__DATE__, __TIME__));   // установка времени от коипа почему-то не работаеит
   
  delay(100);  
  pinMode(led, OUTPUT);
  digitalWrite(led, LOW);
   
//  Serial.println("GSM NEOWAY promote site istarik.ru");


 pinMode(9, OUTPUT);
 digitalWrite(9,LOW);
 delay(1000);
 digitalWrite(9,HIGH);
 delay(2000);
 digitalWrite(9,LOW);
 delay(5000);

  Serial.println();
  Serial.println("Turn on AOH:");
  Serial2.println("AT+CLIP=1");  //включить АОН
  delay(500);
  Serial.println("Text format sms:");
  Serial2.println("AT+CMGF=1"); // текстовый формат SMS
  delay(500);
  Serial.println("Mode GSM:");
  Serial2.println("AT+CSCS=\"GSM\"");  // кодировка текста - GSM
  delay(500);
  Serial.println("SMS to terminal:");
  Serial2.println("AT+CNMI=2,2,0,0,0"); // вывод смс в консоль
  delay(500);
  
  
  Wire.begin();
  RTC.begin();
  RTC.isrunning();
  dht.begin();

  lcd.init();
  lcd.backlight();      // Включаем подсветку дисплея
 // lcd.setCursor(0, 0);

  // Initialize the sensor (it is important to get calibration values stored on the device).
  pressure.begin();

  lcd.createChar(0, bukva_G);       // создаем символ и записываем их в память LCD
 // lcd.createChar(1, bukva_1);     // создаем символ и записываем их в память LCD
 // lcd.createChar(2, bukva_2);     // создаем символ и записываем их в память LCD
 //// lcd.createChar(3, bukva_3);   // создаем символ и записываем их в память LCD
 //// lcd.createChar(4, bukva_4);   // создаем символ и записываем их в память LCD
 //// lcd.createChar(5, bukva_5);   // создаем символ и записываем их в память LCD


  pinMode(53, OUTPUT);
  SD.begin(53);
  
  J = true;

 // void powerUpOrDown()  //***** включение модуля SIM900 (pin9 модуля соединён с pin9 Меги) *****************
//{
// pinMode(9, OUTPUT);
// digitalWrite(9,LOW);
// delay(1000);
// digitalWrite(9,HIGH);
// delay(2000);
// digitalWrite(9,LOW);
// delay(5000);
//}
  
}

void loop() 
{
  
  byte i;
  byte present = 0;
  byte type_s;
 // float celsius, fahrenheit;
  
  //****************************** получаем время от DS1307 *******************************************
  DateTime now = RTC.now();

  // читаем время и записываем его в буфер
  hh = now.hour();
  mm = now.minute();
  ss = now.second();
  sprintf(bufT, "%02d:%02d:%02d", hh, mm, ss );

 // Serial.println(bufT);
  
   // читаем дату и записываем его в буфер
  ddd = now.day();
  mmm = now.month();
  yyy = now.year();
  sprintf(bufD, "%02d.%02d.%02d", ddd, mmm, yyy );
 
 //**********************************************************************************************

 //***********************получение GPS-данных*************************************************

  
  bool newdata = false;
  unsigned long start = millis();
   // Every 5 seconds we print an update
   //   while (millis() - start < 5000) 
    // Every 0.4 seconds we print an update
     while (millis() - start < 400) 
     
  {
    if (Serial1.available()) 
    {
      char c = Serial1.read();
     // Serial.print(c);  // uncomment to see raw GPS data
      if (gps.encode(c)) 
      {
        newdata = true;
        break;  // uncomment to print new data immediately!
      }
    }
  }
  if (newdata) 
  {
     gpsdump(gps);
  }
  else
  {
      lcd.setCursor(0, 3);
      lcd.print(" last");
   } 
  //*********************************************************************************************  
  
  //*********************** вывод времени и даты на дисплей 2004 **********************************
  
  //*********** Выводим на экран дату **********************
  //lcd.setCursor(0, 2);
  //lcd.print(bufD);
  
  //*********** Выводим на экран время *********************
   lcd.setCursor(12, 2);
   lcd.print(bufT);
  //********************************************************************************************* 
   
  
   //********* получаем температуру с датчик DS18B20 **************************
   
   //************A = 0x28, 0xFF, 0x13, 0xDA, 0x80, 0x16, 0x04, 0x25 *********************************
 
  ds.reset();
  ds.select(addr);  
  ds.write(0x44, 1);        // начало коммуникации

  present = ds.reset();
  ds.select(addr);
  ds.write(0xBE);           // читаем значение

   for ( i = 0; i < 9; i++)
  { 
   // смотрим 9 байтов
    data[i] = ds.read();
   }
  
  // Преобразуем получненный данные в температуру
  // Используем int16_t тип, т.к. он равен 16 битам
  // даже при компиляции под 32-х битный процессор
    int16_t raw = (data[1] << 8) | data[0];
   // byte cfg = (data[4] & 0x60);
    cfg = (data[4] & 0x60);
    
    raw = raw & ~3;
   
    T1 = (float)raw / 16.0;
    
     
   //  Serial.println(T1);
  
  //*************** B = 0x28, 0xFF, 0x89, 0xCE, 0x71, 0x16, 0x04, 0x3F  ********************************
  
  ds.reset();
  ds.select(addrB);  
  ds.write(0x44, 1);       // начало коммуникации

  present = ds.reset();
  ds.select(addrB);
  ds.write(0xBE);        // читаем значение

   for ( i = 0; i < 9; i++)
  { 
   // смотрим 9 байтов
    dataB[i] = ds.read();
   }
  
   // Преобразуем получненный данные в температуру
  // Используем int16_t тип, т.к. он равен 16 битам
  // даже при компиляции под 32-х битный процессор
    int16_t rawB = (dataB[1] << 8) | dataB[0];
   // byte cfg = (data[4] & 0x60);
    cfgB = (dataB[4] & 0x60);
    
    rawB = rawB & ~3;
   
    T11 = (float)rawB / 16.0;
 
    //Serial.println(T11);
    
    
  //************** получаем температуру и давление с датчика BMP180 *************************
  status = pressure.startTemperature();
  if (status != 0)
  {                                            // Wait for the measurement to complete:
    delay(status);
    status = pressure.getTemperature(BMP180);
    if (status != 0)
  {
      status = pressure.startPressure(3);
      if (status != 0)
      {                                        // Wait for the measurement to complete:
        delay(status);
        status = pressure.getPressure(P, BMP180);
      }
    }
    P = ((P * 0.0295333727) * 25.4);            // выражаем давление в мм ртутного столба
  }
  
 //*************** ВЫВОД ПОКАЗАНИЙ на дисплей 2004  ********************

  //******************  выводим температуру в позицию 1 *************
   
  //**************** выводим температуру с датчиа DS18B20 ********************
   
  lcd.setCursor(5, 0);
  lcd.print("       ");
  lcd.setCursor(0, 0);
  lcd.print("t1"); 
  lcd.print("= ");

  if (T11 > 0)
  {
   lcd.print("+");
  }

  lcd.print(T11, 1);
  lcd.write(0);
  lcd.print(" ");

//****************************************************************************** 


  //************************  выводим температуру в позицию 2 *******************
   
  //************************* выводим температуру с датчиа DS18B20 *************
 
  lcd.setCursor(5, 1);
  lcd.print("       ");
  lcd.setCursor(0, 1);
  lcd.print("t2"); 
  lcd.print("= ");

  if (T1 > 0)
  {
   lcd.print("+");
  }

  lcd.print(T1, 1);
  lcd.write(0);
  lcd.print(" ");

  //*********************************************************

   //******** получаем температуру и влажность с датчика DTH22 *****************
  // считывание температуры или влажности занимает примерно 250 мс!
  // считанные показания могут отличаться от актуальных примерно на 2 секунды (это очень медленный датчик)
  H = dht.readHumidity();
  TDH22 = dht.readTemperature();                 // Считывание температуры в цельсиях
 
  
  //********************** выводим температуру в позицию 3 ***************
  
  //************************* выводим температуру с датчиа DHT22 ********
  T2 = TDH22;

  lcd.setCursor(0, 2);
//  lcd.print("t");
//  lcd.write(2);
  lcd.print("t3");
  lcd.print("= ");

   if (T2 > 0)
  {
    lcd.print("+");
   }

  lcd.print(T2, 1);
  lcd.write(0);
  lcd.print(" ");
  //********************************************************************
 
 //********* выводим давление с датчиа BMP180 ****************************
  lcd.setCursor(12, 0);
  lcd.print("P= ");
  lcd.print(P, 1);
  
  //********* выводим влажность с датчиа DHT22 ***********************
  lcd.setCursor(12, 1);
  lcd.print("H= ");
  lcd.print(H, 1);
  lcd.print("%");
  lcd.print(" ");
 //*********************************************************************************************************************
      
  
 //********************************* записываем данные на карту памяти *************************************************
 //***** писать будем один раз в полчаса *********

  //*********** готовим строку для вывода в файл ****************
  //***** переводим данные из double и float в символы ****** 
  // 4 is mininum width, 2 is precision; float value is copied onto str_temp 
  dtostrf(T1, 4, 2, str_tempT1);
  dtostrf(T11, 4, 2, str_tempT11);
  dtostrf(T2, 4, 2, str_tempT2);
  dtostrf(P, 5, 2, str_tempP);
  dtostrf(H, 4, 2, str_tempH);

 //************************* формируем строку ******************************
   sprintf(stringD,"%02d.%02d.%02d  %02d:%02d:%02d  Tout=%s  Ttube=%s  Troom=%s  P=%s  Hroom=%s", ddd, mmm, yyy, hh, mm, ss, str_tempT11, str_tempT1, str_tempT2, str_tempP, str_tempH);
 // Строка готова, можно её посмотреть:
 // Serial.println(stringD);
 // delay(100);

 //********** формируем имя файла *******************************
   File myFile;
    char NameOfFile[6] = {0,0,0,0,0,0}; 
  // формируем имя файла
     sprintf(NameOfFile, "%02d_%02d_%02d.txt", (yyy - 2000), mmm, ddd);
//**************************************************************
  
  //***** писать будем один раз в полчаса *********
 if ((mm == 0 or mm == 30) and (ss < 10 ) and (J == true ))   //************ выбираем нулевую или тридцатую минуту получаса, **************
                                                              // и проверяем - писали ли уже в этом цикле - J==0 говорит, что уже писали
 {                                                
        // ************ если условие выполняется, то пишем строку данных в файл ***************************************** 
  
        //****************************** работа с картой памяти *****************************************
    
    myFile = SD.open(NameOfFile, FILE_WRITE); //********** открываем файл ***************************
      
   
       //******* пишем строку данных в файл ***********************
   myFile.println(stringD);
  
        //********** закрываем файл ***************************
   myFile.close();
    
        //*************************************************** окончание записи в файл **************************************
    J = false;                // отмечаем, что один раз уже писали, запрещаем в этом цикле писать вторично (J==false это запрет записи)
  }
    else                      //  если условие не выполняется (в этом цикле уже писали), то ничего не пишем,
    {                         //  разрешаем запись на будущее (J==true это разрешение записи), 
    J = true;                 //  и ждём следующую нулевую или тридцатую минуту           
    } 

//*********************************************************************************************

 //****************************работаем с GSM-модулем**********************************
  
   delay(500);
   
 if(Serial2.available()) //если модуль что-то послал
  {  

    char ch = ' ';     
    String val = "";     
    
    while(Serial2.available()) 
     {  
       ch = Serial2.read();
       val += char(ch); //собираем принятые символы в строку
       delay(500);
     }

    Serial.print("Neo send> ");
    Serial.println();
    Serial.println(val);
    
    if(val.indexOf("+CMT") > -1) // если есть входящее sms
     {

        if(val.indexOf("*t") > -1) // смотрим, что за команда
       {  
        smsMeteo(String(stringD), String("+7XXXXXXXXXX")); // ВПИШИТЕ ВАШ НОМЕР
       }       
         
      if(val.indexOf("money") > -1) // смотрим, что за команда
       {  
         delay(500);
         Serial2.println("ATD#100#;");
       }     
     } 

    if(val.indexOf("+CUSD") > -1) // если есть входящее sms
     { 
      if(val.indexOf("Balance") > -1) // смотрим, что за команда
       {  
         delay(500);
       
         val = val.substring(val.indexOf("Balance"),val.indexOf("r")); 
           sms(String(val), String("+7XXXXXXXXXX"));        // ВПИШИТЕ ВАШ НОМЕР
       }     
     }         
  }
 //**************************************************************************************************
 
 
 }


void smsMeteo(String stringD, String phone)  // отправка СМС метеоданными
{
  Serial.println("Start SMS send");
  Serial2.println("AT+CMGS=\"" + phone + "\"");
  delay(500);
  Serial2.print(stringD);
  delay(500);
  Serial2.print((char)26);
  delay(500);
  Serial.println("SMS send OK");
  delay(500);
}

//void smsBalance(String text, String phone)  // отправка СМС с балансом модема
void sms(String text, String phone)
{
  Serial.println("Start SMS send");
  Serial2.println("AT+CMGS=\"" + phone + "\"");
  delay(500);
  Serial2.print(text);
  delay(500);
  Serial2.print((char)26);
  delay(500);
  Serial.println("SMS send OK");
  delay(500);
}


void gpsdump(TinyGPS & gps)

{ 
   unsigned long date, time;
   int year;
   byte month, day, hour, minute, second;
   gps.crack_datetime(&year, &month, &day, &hour, &minute, &second);
   
   hour = hour + 3;
   if (hour > 23)
   hour = (hour - 24);
    
  //  if((hh > 3) & (hh <24))
   
 //   if((hour > 3) & (hour <24))
 // {
 //   RTC.adjust(DateTime(year, month, day, hour, minute, second));  // This line sets the RTC with an explicit date & time
 // }
  
  //***************************** вывод GPS-времени на дисплей *****************************
    
    lcd.setCursor(0, 3);
    lcd.print(" GPS ");
    
    lcd.setCursor(6, 3);
     
    lcd.print(day);  
    lcd.print("."); 
    if (month < 10)
    lcd.print("0");
    lcd.print(month);  
     
    lcd.setCursor(12, 3); 
    if (hour < 10)
    lcd.print("0");
    lcd.print(hour);  
    lcd.print(":"); 
    
    if (minute < 10)
    lcd.print("0");
    lcd.print(minute); 
    lcd.print(":"); 
    
    if (second <10)
    lcd.print("0");
    lcd.print(second);
   
    if((hour > 3) & (hour <24))
  {
    RTC.adjust(DateTime(year, month, day, hour, minute, second));  // This line sets the RTC with an explicit date & time
  }
    
}

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

как ты думаешь, нахрена людям в готовом коде закомментированные куски?  Типа этого

175  // void powerUpOrDown()  //***** включение модуля SIM900 (pin9 модуля соединён с pin9 Меги) *****************
176 //{
177 // pinMode(9, OUTPUT);
178 // digitalWrite(9,LOW);
179 // delay(1000);
180 // digitalWrite(9,HIGH);
181 // delay(2000);
182 // digitalWrite(9,LOW);
183 // delay(5000);
184 //}
 

Если ты код не поленишься почистить, текст станет меньше

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

loop() длиной в 340 строчек, я щитаю, просто шикарен, никто так не умеет.  Для сравнения, я счас Радио делаю на Атмега8, вот мой loop().  Весь. Больше туда ничего вставляться не будет. 

void loop()
{
    TuningPot.Read();   // почитать потенциометр настройки
    Timers.Run();       // тикнуть таймерами
    Radio.ReadStatus(); // прочитать статус Радиомодуля 
    btnLight.Read();    // Прочитать кнопку включения подсветки

    if (MessageList) Dispatch(MessageList.GetMessage()); // если ктонить из них прислал сапщение - обработать его
}

 

rw3ar
Offline
Зарегистрирован: 15.05.2017

Да, конечно. Можно почистить. И delay'ем я с тех пор научился не пользоваться...

Да, текст станет компактнее. Код красивее. Безусловно.

Но... я ведь не хвастаться сюда пришёл, и красоваться дыртаньяном... Я просто делюсь рабочим кодом. Древним, лохматым... Рабочим, пусть и не красивым. Мне, к примеру, очень много пользы приносят чужие именно "кривые" коды... Может, и мой кому сгодится. Выдернет что-то полезное, и сделает лучше.

А за совет - спасибо.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Нууу, какбэ это щитается хорошим тоном, ничего лишнего. 

rw3ar
Offline
Зарегистрирован: 15.05.2017

Нуу, какбэ да. Считается. У программистов. Оные готовы за лишнюю строку тухлыми тапками закидать насмерть. И я их даже где-то понимаю, да. Когда заказчику с тугим кошельком... или там прохфессору в курсовой... тем более - в дипломе. Или друг перед дружкой повыёживаться. Это так, достойно вполне.

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

))

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

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

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

rw3ar
Offline
Зарегистрирован: 15.05.2017

А по существу мысли есть?

А то, боюсь, про рюшечки-плюшечки мало кому интересно. Конкретно - что в этом супе не так? Что мусор в листинге - это понятно, но "на скоростные качества влияния не оказывает"(с) - работать будет одинаково, что с мусором, что без. Вот со временем я где-то  накосячил, работает кривовато. То есть пока GPS ловится нормально, то всё хорошо, а вот если нет, и если сигнал GPS поймался как-то не так - может совершенно фантастические циферки выдать.

Ну и календарь я поленился писать, в итоге дата в интервале от 00:00 до 03:00 не правильная. А как обойтись без самописного календаря - знаний не хватает.

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

Что же до дерьма, так ведь никто силой не кормит - "не нравится, не ешь"(с)

За критику спасибо.