Задача: ежедневно получать данные о температуре в помещении и на улице. Поскольку помещение находится далековато, то последующая отсылка смс. Буду рад любым советам по оптимизации кода. Это мой первый проект на ардуино. Да и не программист я по жизни ни разу Собрано из: - непосредственно Arduino - два датчика температуры DHT11 - GPRS Shield
// подключаем библитотеку датчика #include <dht.h> // подключаем библиотеку GPRS модуля #include <SoftwareSerial.h> // заводим serial-соединение на 7 и 8 цифровых входах SoftwareSerial gprsSerial(7, 8); // прописываем объекты - два сенсора // внешний DHT TempOut = DHT(); // и внутренний DHT TempIn = DHT(); void setup () { gprsSerial.begin(19200); //методом attach объявляем к какому контакту подключен сенсор. //нулевой контакт для внешнего TempOut.attach(A0); // и первый для внутреннего TempIn.attach(A1); // после подачи питания ждем 30 секунд до готовности сенсора delay(30000); // Шлем тестовое сообщение для проверки работоспособности // А то мало ли, антенна отвалилась... // Устанавливает текстовый режим для SMS-сообщений gprsSerial.print("AT+CMGF=1\r"); delay(100); // даём время на усваивание команды // Устанавливаем адресата: телефонный номер в международном формате gprsSerial.println("AT + CMGS = \"+7хххххххххх\""); delay(100); // Пишем текст сообщения gprsSerial.println("System status OK"); delay(100); // Отправляем Ctrl+Z, обозначая, что сообщение готово gprsSerial.println((char)26); } void loop () { delay(28800000); // Этой задержкой устанавливаем, когда начнется работа программы // скажем, запускаю устройство я днем, а меня интересуют ночные температуры. 8 часов char MsgAll[80]; // сообщение сводное char msgIn[40]; char msgOut[40]; String msgInS = String(msgIn); String msgOutS = String(msgOut); String MsgAllS = String(MsgAll); SensOutRead: // метка для цикла goto TempOut.update(); //обновляем показания датчика if (TempOut.getLastError() == DHT_ERROR_OK) // Проверяем на наличие ошибок при считывании с датчика. { // Если ошибок нет, снимаем показания и переходим к внутреннему датчику sprintf(msgOut, "Temp.Out = %dC,Hum.Out = %d%%; ", TempOut.getTemperatureInt(), TempOut.getHumidityInt()); goto SensInRead; } // Если есть ошибки, то считываем заново. else; { delay(1000); // задержка необходима, иначе шлет постоянно ошибки // 100 миллисекунд мало - проверял goto SensOutRead; } SensInRead: TempIn.update(); if (TempIn.getLastError() == DHT_ERROR_OK) { sprintf(msgIn, "Temp.In = %dC,Hum.In = %d%%", TempIn.getTemperatureInt(), TempIn.getHumidityInt()); goto OutpData; } else; { delay(1000); goto SensInRead; } OutpData: MsgAllS = msgOutS + msgInS; // "склеиваем одно большое // сообщение из двух маленьких delay(28800000); // Устанавливаю, когда слать смс-ку. //Мало счастья среди ночи подрываться к мобильнику и смотреть: //"А сколько у меня там градусов ниже нуля?" 8 часов // Устанавливает текстовый режим для SMS-сообщений gprsSerial.print("AT+CMGF=1\r"); delay(100); // даём время на усваивание команды // Устанавливаем адресата: телефонный номер в международном формате gprsSerial.println("AT + CMGS = \"+7хххххххххх\""); delay(100); // Пишем текст сообщения gprsSerial.println(MsgAllS); delay(100); // Отправляем Ctrl+Z, обозначая, что сообщение готово gprsSerial.println((char)26); delay(28800000); //8 часов }
Круть! По коду: предлагаю избавиться от `goto` и инкапсулировать чтение сенсора в функцию: Код (Text): // подключаем библитотеку датчика #include <dht.h> // подключаем библиотеку GPRS модуля #include <SoftwareSerial.h> // заводим serial-соединение на 7 и 8 цифровых входах SoftwareSerial gprsSerial(7, 8); // прописываем объекты - два сенсора // внешний DHT TempOut = DHT(); // и внутренний DHT TempIn = DHT(); void setup () { gprsSerial.begin(19200); //методом attach объявляем к какому контакту подключен сенсор. //нулевой контакт для внешнего TempOut.attach(A0); // и первый для внутреннего TempIn.attach(A1); // после подачи питания ждем 30 секунд до готовности сенсора delay(30000); // Шлем тестовое сообщение для проверки работоспособности // А то мало ли, антенна отвалилась... // Устанавливает текстовый режим для SMS-сообщений gprsSerial.print("AT+CMGF=1\r"); delay(100); // даём время на усваивание команды // Устанавливаем адресата: телефонный номер в международном формате gprsSerial.println("AT + CMGS = \"+7хххххххххх\""); delay(100); // Пишем текст сообщения gprsSerial.println("System status OK"); delay(100); // Отправляем Ctrl+Z, обозначая, что сообщение готово gprsSerial.println((char)26); } void readDHT(DHT* sensor, char* out) { sensor->update(); //обновляем показания датчика while (sensor->getLastError() != DHT_ERROR_OK) { delay(1000); // задержка необходима, иначе шлет постоянно ошибки // 100 миллисекунд мало - проверял sensor->update(); //обновляем показания датчика } sprintf(out, "Temp.Out = %dC,Hum.Out = %d%%; ", sensor->getTemperatureInt(), sensor->getHumidityInt()); } void loop() { delay(28800000); // Этой задержкой устанавливаем, когда начнется работа программы // скажем, запускаю устройство я днем, а меня интересуют ночные температуры. 8 часов char MsgAll[80]; // сообщение сводное char msgIn[40]; char msgOut[40]; String msgInS = String(msgIn); String msgOutS = String(msgOut); String MsgAllS = String(MsgAll); readDHT(&TempOut, msgOut); readDHT(&TempIn, msgIn); MsgAllS = msgOutS + msgInS; // "склеиваем одно большое // сообщение из двух маленьких delay(28800000); // Устанавливаю, когда слать смс-ку. //Мало счастья среди ночи подрываться к мобильнику и смотреть: //"А сколько у меня там градусов ниже нуля?" 8 часов // Устанавливает текстовый режим для SMS-сообщений gprsSerial.print("AT+CMGF=1\r"); delay(100); // даём время на усваивание команды // Устанавливаем адресата: телефонный номер в международном формате gprsSerial.println("AT + CMGS = \"+7хххххххххх\""); delay(100); // Пишем текст сообщения gprsSerial.println(MsgAllS); delay(100); // Отправляем Ctrl+Z, обозначая, что сообщение готово gprsSerial.println((char)26); delay(28800000); //8 часов }
Нублин. "Сразу видно - высшее образование!" (с) Я б до такого не додумался ни в жисть! Спасибо за совет.
Всё то же самое, что и вы, но другими словами. Это называется рефакторинг. Момент первый. `goto` сильно усложняет чтение программы, поэтому считается дурным тоном. Всякий `goto` можно заменить на конструкцию из if'ов и while'ов. У вас было: Код (Text): SensOutRead: // метка для цикла goto TempOut.update(); //обновляем показания датчика if (TempOut.getLastError() == DHT_ERROR_OK) // Проверяем на наличие ошибок при считывании с датчика. { // Если ошибок нет, снимаем показания и переходим к внутреннему датчику sprintf(msgOut, "Temp.Out = %dC,Hum.Out = %d%%; ", TempOut.getTemperatureInt(), TempOut.getHumidityInt()); goto SensInRead; } // Если есть ошибки, то считываем заново. else { delay(1000); // задержка необходима, иначе шлет постоянно ошибки // 100 миллисекунд мало - проверял goto SensOutRead; } SensInRead: // ... Этот код для чтения одного сенсора залезает в «чужой» код для другого сенсора посредством метки и если захочется поменять блоки местами, что-то подкрутить, придётся задумываться как они будут стыковаться. Думать сложно, поэтому код можно переписать, чтобы он был отдельным понятным блоком. Вы ведь имели в виду: читаем пока не вычитаем и затем пихаем всё в строку? Так и пишем: Код (Text): TempOut.update(); //обновляем показания датчика while (TempOut.getLastError() != DHT_ERROR_OK) { delay(1000); // задержка необходима, иначе шлет постоянно ошибки // 100 миллисекунд мало - проверял TempOut.update(); //обновляем показания датчика } sprintf(out, "Temp.Out = %dC,Hum.Out = %d%%; ", TempOut.getTemperatureInt(), TempOut.getHumidityInt()); Далее, вы читаете показания с двух датчиков. Делаете это одинаковым образом. Разница лишь в экземпляре датчика и строке в которую вы кладёте результат. Что если датчиков станет 3? Или 33? Придётся копипастить код, вытягивая его в большущий рулон. А что если затем нужно будет поменять формат строки с показаниями? Придётся 33 раза делать изменения. Это называется нарушением принципа DRY (Don't Repeat Yourself). Чтобы всё исправить достаточно поместить повторяющийся код в функцию. Что мы и делаем: Код (Text): void readDHT(DHT* sensor, char* out) { sensor->update(); //обновляем показания датчика while (sensor->getLastError() != DHT_ERROR_OK) { delay(1000); // задержка необходима, иначе шлет постоянно ошибки // 100 миллисекунд мало - проверял sensor->update(); //обновляем показания датчика } sprintf(out, "Temp.Out = %dC,Hum.Out = %d%%; ", sensor->getTemperatureInt(), sensor->getHumidityInt()); } Эта функция считывает показания с какого-то DHT-датчика и кладёт результат в какую-то строку. О каких именно датчиках и строках идёт речь, мы определяем при вызове функции: Код (Text): [FONT=Consolas] readDHT(&TempOut, msgOut); readDHT(&TempIn, msgIn);[/FONT] Вопросы могут вызвать символы * и &. Дело в том, что в функцию нам следует передавать указатель на датчик, а не сам датчик. Так мы говорим, что хотим работать с чем-то уже существующим, просто по псевдониму. В данном случае псевдоним называется `sensor`. & перед именем переменной означает «получить псевдоним». -> после `sensor`, означает то же, что и точка в обычном случае, но для указателей нужно использовать ->. Так уж повелось, такие правила языка C++
"Всю ночь перечитывал пейджер. Много думал..." Спасибо за внимание Обещаю найти время и во всем разобраться Только, чур, потом не обижаться на идиотские вопросы Кстати, а не заглянете на мой пост про GPRS Shield... Может можно и без этих бесконечных делэев обойтись, если часики то запустить... Дурацкая привычка: если что-то можно улучшить, то его надо улучшить.
Какая-то странная фигня.... Проект то выложил. Протестировал на коротких задержках порядка 30000 мс. Все работало. А теперь запустил с большими - нет смс-ки. Запустил прибор около 8 вечера. Ближе к 12 дня рассчитывал получить сообщение. Нету. Почему? Ноликов лишних написал? Или ардуина таких больших чисел боится? Помогайте, гуру, великие и ужасные
Теоретически вроде понял все. Надо самому какую-нибудь функцию написать, чтоб убедиться наверняка. Что понял Спасибо.
Да. Максимальное значение int — 32767 (2^15 - 1). Поэтому для больших задержек вам нужно использовать тип long. У него максимум более 2 миллиардов. Но функция delay принимает int. Поэтому, храните в `long` переменной сколько ещё осталось поспать, а в цикле спите, скажем, по секунде и уменьшайте значение того сколько осталось поспать на секунду после каждого пробуждения.
В итоге получилось как-то так. Будет похолоднее, поеду ставить прибор на "боевое дежурство" Код (Text): // подключаем библитотеку датчика #include <dht.h> // подключаем библиотеку GPRS модуля #include <SoftwareSerial.h> // заводим serial-соединение на 7 и 8 цифровых входах SoftwareSerial gprsSerial(7, 8); // прописываем объекты - два сенсора // внешний DHT TempOut = DHT(); // и внутренний DHT TempIn = DHT(); void setup () { gprsSerial.begin(19200); //методом attach объявляем к какому контакту подключен сенсор. //нулевой контакт для внешнего TempOut.attach(A0); // и первый для внутреннего TempIn.attach(A1); // после подачи питания ждем 30 секунд до готовности сенсора delay(30000); // Шлем тестовое сообщение для проверки работоспособности // А то мало ли, антенна отвалилась... // Устанавливает текстовый режим для SMS-сообщений gprsSerial.print("AT+CMGF=1\r"); delay(100); // даём время на усваивание команды // Устанавливаем адресата: телефонный номер в международном формате gprsSerial.println("AT + CMGS = \"+79857690401\""); delay(100); // Пишем текст сообщения gprsSerial.println("System status OK"); //Serial.println("System status OK"); delay(100); // Отправляем Ctrl+Z, обозначая, что сообщение готово gprsSerial.println((char)26); } void Sleep(int H) //функция для установки длительной задержки. параметр H - время в часах, { long Sleep = (H * 3600000); int Start; while (Sleep > 32000) { Start = 32000; delay(Start); Sleep = Sleep - Start; } Start = Sleep; delay(Start); } void readDHT(DHT* sensor, char* MsgTemp) //функция считаывния показаний { sensor->update(); //обновляем показания датчика while (sensor->getLastError() != DHT_ERROR_OK) { delay(1000); // задержка необходима, иначе шлет постоянно ошибки // 100 миллисекунд мало - проверял sensor->update(); //обновляем показания датчика } sprintf(MsgTemp, "Temp=%dC,Hum=%d%%;", sensor->getTemperatureInt(), sensor->getHumidityInt()); } void loop () { char msgIn[40]; char msgOut[40]; String msgInS = String(msgIn); String msgOutS = String(msgOut); String MsgAllS; Sleep(11); // время от времени запуска до времени считывания. считываем около 4 утра. readDHT(&TempIn, msgIn); readDHT(&TempOut, msgOut); MsgAllS = "Out:" + msgOutS + " In:" + msgInS; Sleep(10); // Этот параметр не меняем gprsSerial.print("AT+CMGF=1\r"); delay(100); // даём время на усваивание команды // Устанавливаем адресата: телефонный номер в международном формате gprsSerial.println("AT + CMGS = \"+79857690401\""); delay(100); // Пишем текст сообщения gprsSerial.println(MsgAllS); delay(100); // Отправляем Ctrl+Z, обозначая, что сообщение готово gprsSerial.println((char)26); Sleep(3); // Сумма всех параметров "H" трех "слипов" равна 24 }
а часы победить не удалось? а то мне тоже надо время, а вешать дополнительно внешние часы не хочется, когда есть уже на GPRS-модуле