Очередной МЕТЕО. Но рабочий
- Войдите на сайт для отправки комментариев
Вс, 03/10/2021 - 17:49
Коллеги, рискну поделиться рабочим проектом самодельной простенькой метеостанции.
Работает на Arduino Mega
Измеряет:
- три температуры
- атмосферное давление
- влажность воздуха
Пишет всё это на SD.
Время корректирует по GPS (если ловит, что не всегда).
На дисплее 4х20 отображает:
- три температуры,
- давление
- влажность
- текущее время
- последнее время по GPS.
На запрос по СМС вида:
- *t отсылает строку метеоданных
- "money" - отсылает текущий баланс (запросив его у провайдера).
Система более-менее (не без погрешностей со временем) работает примерно года 3 в деревенском доме. Чаще всего в полностью автономном режиме.
Скетч далёк от идеала (особенно в части времени), но повторюсь - система работает, и меня в целом устраивает.
//*************************************************************** //получение СМС с балансом СИМ-карты модуля 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 } }как ты думаешь, нахрена людям в готовом коде закомментированные куски? Типа этого
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//}Если ты код не поленишься почистить, текст станет меньше
loop() длиной в 340 строчек, я щитаю, просто шикарен, никто так не умеет. Для сравнения, я счас Радио делаю на Атмега8, вот мой loop(). Весь. Больше туда ничего вставляться не будет.
void loop() { TuningPot.Read(); // почитать потенциометр настройки Timers.Run(); // тикнуть таймерами Radio.ReadStatus(); // прочитать статус Радиомодуля btnLight.Read(); // Прочитать кнопку включения подсветки if (MessageList) Dispatch(MessageList.GetMessage()); // если ктонить из них прислал сапщение - обработать его }Да, конечно. Можно почистить. И delay'ем я с тех пор научился не пользоваться...
Да, текст станет компактнее. Код красивее. Безусловно.
Но... я ведь не хвастаться сюда пришёл, и красоваться дыртаньяном... Я просто делюсь рабочим кодом. Древним, лохматым... Рабочим, пусть и не красивым. Мне, к примеру, очень много пользы приносят чужие именно "кривые" коды... Может, и мой кому сгодится. Выдернет что-то полезное, и сделает лучше.
А за совет - спасибо.
Нууу, какбэ это щитается хорошим тоном, ничего лишнего.
Нуу, какбэ да. Считается. У программистов. Оные готовы за лишнюю строку тухлыми тапками закидать насмерть. И я их даже где-то понимаю, да. Когда заказчику с тугим кошельком... или там прохфессору в курсовой... тем более - в дипломе. Или друг перед дружкой повыёживаться. Это так, достойно вполне.
Но тут, всё-таки, несколько иная ситуё... ситуа... ладно, проехали. Тут форум, где такое обилие тем (чаще пустых) про метео и охрану безумных домов, что мне показалось - будет полезно для кого-то посмотреть древний, кривой, лохматый (в смысле строк и комментов), но вот уже несколько лет работающий самописный код. Не, как в учебнике. Ну просто ради разнообразия. На фоне Гуров (с Большой Буквы) с одной стороны, и "поможите кто чем может"(с) с другой.
))
Ну это из серии, а давайте я вас говном накормлю, для разнообразия, невкусно, канеш, дак зато бесплатно, даже, мошт, и полезно кому будет.
А по существу мысли есть?
А то, боюсь, про рюшечки-плюшечки мало кому интересно. Конкретно - что в этом супе не так? Что мусор в листинге - это понятно, но "на скоростные качества влияния не оказывает"(с) - работать будет одинаково, что с мусором, что без. Вот со временем я где-то накосячил, работает кривовато. То есть пока GPS ловится нормально, то всё хорошо, а вот если нет, и если сигнал GPS поймался как-то не так - может совершенно фантастические циферки выдать.
Ну и календарь я поленился писать, в итоге дата в интервале от 00:00 до 03:00 не правильная. А как обойтись без самописного календаря - знаний не хватает.
Не то, чтобы я просил себе какой-то помощи в этом вопросе (меня-то несколько лет всё в этой штуке более-менее устраивает), но вообще было бы полезно. Во всяком случае, гораздо полезнее вылизывания листинга ради фэншуя.
Что же до дерьма, так ведь никто силой не кормит - "не нравится, не ешь"(с)
За критику спасибо.