Светодиодные часы 2.0: температура
В прошлой статье «Arduino: светодиодные часы с синхронизацией времени» многие просили добавить отображение температуры с термодатчика DS18B20. Вот, наконец-то выкладываю код.

Всё основное подключается также, как и в предыдущей статье по ссылке, поэтому повторять не буду. Дополнительно нам понадобится цифровой термодатчик DS18B20 и резистор на 4.7 кОм. Схема его подключения такая:

DS18B20:
Распиновка:

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

Ну и собственно сам код:
// Скетч для Arduino. Светодиодные часы с синхронизацией времени и отображением температуры // Версия 2.0 (15.11.2015) // // Автор: Гладышев Дмитрий (2014-2015) // http://student-proger.ru/2015/11/svetodiodnye-chasy-2-0-temperatura #include <SPI.h> //для Ethernet-shield #include <Ethernet.h> // --//-- #include <OneWire.h> //для температурного датчика #include <EthernetUdp.h> #include <Wire.h> //I2C #include <DS1307RTC.h> //RTC #include <Time.h> #include <Metro.h> //для задания интервалов времени (аля Cron) #define Debug false //режим отладки //**************************************************************************************** byte mac[] = { 0xDE, 0xAD, 0xBE, 0x00, 0x00, 0x00 }; //MAC-адрес Arduino byte clock_addr[] = {0x39, 0x3a, 0x3b, 0x3d}; //адреса PCF8574 часов #define DS_PIN 2 //пин подключения термодатчика #define DS_UPDATE_INTERVAL 150000 //частота чтения данных с датчика (2.5 минуты) #define CLOCK_SHOW 15000 //продолжительность отображения часов #define TEMP_SHOW 5000 //продолжительность отображения температуры IPAddress timeServer(132, 163, 4, 101); // IP-адрес NTP сервера #define timeZone 7 // Временная зона //**************************************************************************************** unsigned int localPort = 53; Metro ClockModeSwitch = Metro(15000); //Первое переключение показаний через 15 секунд byte ClockMode = 0; // 0 - часы, 1 - температура Metro TimeSync = Metro(86400000); //Частота синхронизации времени Metro TempRead = Metro(5000); //Первое чтение с датчика через 5 секунд после запуска char clockbuf[4]; OneWire ds(DS_PIN); EthernetClient client2; EthernetUDP Udp; bool DotTimeState = false; byte CountSensors; unsigned long lastTimeUpdate = 0; int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract; //для нужд преобразования данных от температурного датчика byte CurTemp=100; int CurSign; void setup() { if (Debug) { Serial.begin(9600); } for (int i=0; i<4; i++) { clockbuf[i]='-'; } UpdateClock(); // Ethernet connection: do { delay(1000); } while (Ethernet.begin(mac) == 0); Wire.begin(); //Узнаём количество термодатчиков CountSensors = DsCount(); Udp.begin(localPort); //Запрашиваем время с NTP сервера setSyncProvider(getNtpTime); //Если время получили успешно, то записываем данные в RTC if (timeStatus() != timeNotSet) { RTC.set(now()); } } //============================== void loop() { tmElements_t tm; if (ClockModeSwitch.check() == 1) //Переключение режима показа { ClockMode++; if (ClockMode >= 2) { ClockMode = 0; } switch (ClockMode) { case 0: ClockModeSwitch.interval(15000); break; case 1: ClockModeSwitch.interval(5000); break; } } if (TimeSync.check() == 1) //Синхронизация по времени { //Запрашиваем время с NTP сервера setSyncProvider(getNtpTime); //Если время получили успешно, то записываем данные в RTC if (timeStatus() != timeNotSet) { RTC.set(now()); } } if (client2.available()) { char c = client2.read(); } if (TempRead.check() == 1) //Чтение с датчика { TempRead.interval(DS_UPDATE_INTERVAL); //Сбрасываем поиск датчиков (кол-во нам уже известно) ds.reset_search(); //Теперь в цикле опрашиваем все датчики сразу for (int j=0; j<CountSensors; j++) { byte i; byte present = 0; byte data[12]; byte addr[8]; if ( !ds.search(addr)) { ds.reset_search(); return; } ds.reset(); ds.select(addr); ds.write(0x44,1); delay(1000); //Время на конвертацию present = ds.reset(); ds.select(addr); ds.write(0xBE); for ( i = 0; i < 9; i++) // we need 9 bytes { data[i] = ds.read(); } LowByte = data[0]; HighByte = data[1]; TReading = (HighByte << 8) + LowByte; SignBit = TReading & 0x8000; // test most sig bit if (SignBit) // negative { TReading = (TReading ^ 0xffff) + 1; // 2's comp } Tc_100 = (6 * TReading) + TReading / 4; // multiply by (100 * 0.0625) or 6.25 Whole = Tc_100 / 100; // separate off the whole and fractional portions Fract = Tc_100 % 100; CurTemp = round(Tc_100 / 100); CurSign = SignBit; } } if (millis() - lastTimeUpdate > 500) { lastTimeUpdate = millis(); if (((ClockMode==0)||(CurTemp==100))&&(RTC.read(tm))) { DotTimeState=!DotTimeState; clockbuf[0] = (char)(tm.Hour/10)+'0'; if (tm.Hour<10) { clockbuf[0] = ' '; } clockbuf[1] = (char)(tm.Hour%10)+'0'; clockbuf[2] = (char)(tm.Minute/10)+'0'; clockbuf[3] = (char)(tm.Minute%10)+'0'; UpdateClock(); } else { DotTimeState=false; if (CurSign) { clockbuf[0] = '-'; } else { clockbuf[0] = ' '; } clockbuf[1] = (char)(abs(CurTemp)/10)+'0'; if (abs(CurTemp)<10) { clockbuf[1] = ' '; } clockbuf[2] = (char)(abs(CurTemp)%10)+'0'; clockbuf[3] = '°'; UpdateClock(); } } } //================================================ //Количество термодатчиков на шине int DsCount() { int count=0; bool thatsall = false; byte addr[8]; do { if ( !ds.search(addr)) { ds.reset_search(); thatsall = true; } count++; } while(!thatsall); return (count-1); } void UpdateClock() { for (int i=0; i<4; i++) { Wire.beginTransmission(clock_addr[i]); Wire.write(SegmentValue(clockbuf[i])); Wire.endTransmission(); } } byte SegmentValue(char k) { byte u; switch (k) { case '0': u=B1111110; break; case '1': u=B0110000; break; case '2': u=B1101101; break; case '3': u=B1111001; break; case '4': u=B0110011; break; case '5': u=B1011011; break; case '6': u=B1011111; break; case '7': u=B1110000; break; case '8': u=B1111111; break; case '9': u=B1111011; break; case '-': u=B0000001; break; case ' ': u=B0000000; break; case 'E': u=B1001111; break; case 'U': u=B0111110; break; case 'u': u=B0011100; break; case 'I': u=B0110000; break; case 'i': u=B0010000; break; case 'O': u=B1111110; break; case 'o': u=B0011101; break; case 'P': u=B1100111; break; case 'A': u=B1110111; break; case 'S': u=B1011011; break; case 'F': u=B1000111; break; case 'H': u=B0110111; break; case 'J': u=B0111100; break; case 'L': u=B0001110; break; case 'C': u=B1001110; break; case 'B': u=B1111111; break; case 'b': u=B0011111; break; case 'd': u=B0111101; break; case '°': u=B1100011; break; default: u=B0000000; } byte t=0; //переворачиваем биты for (byte i=0; i<7; i++) { if ((u&(1<<i))!=0) { t=t|(1<<(6-i)); } } u=t; u=u<<1; if (DotTimeState) { u=u+1; } return u; } /*-------- NTP code ----------*/ const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets time_t getNtpTime() { while (Udp.parsePacket() > 0) ; // discard any previously received packets if (Debug) { Serial.println("Transmit NTP Request"); } sendNTPpacket(timeServer); uint32_t beginWait = millis(); while (millis() - beginWait < 1500) { int size = Udp.parsePacket(); if (size >= NTP_PACKET_SIZE) { if (Debug) { Serial.println("Receive NTP Response"); } Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer unsigned long secsSince1900; // convert four bytes starting at location 40 to a long integer secsSince1900 = (unsigned long)packetBuffer[40] << 24; secsSince1900 |= (unsigned long)packetBuffer[41] << 16; secsSince1900 |= (unsigned long)packetBuffer[42] << 8; secsSince1900 |= (unsigned long)packetBuffer[43]; return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR; } } if (Debug) { Serial.println("No NTP Response :-("); } return 0; // return 0 if unable to get the time } // send an NTP request to the time server at the given address void sendNTPpacket(IPAddress &address) { // set all bytes in the buffer to 0 memset(packetBuffer, 0, NTP_PACKET_SIZE); // Initialize values needed to form NTP request // (see URL above for details on the packets) packetBuffer[0] = 0b11100011; // LI, Version, Mode packetBuffer[1] = 0; // Stratum, or type of clock packetBuffer[2] = 6; // Polling Interval packetBuffer[3] = 0xEC; // Peer Clock Precision // 8 bytes of zero for Root Delay & Root Dispersion packetBuffer[12] = 49; packetBuffer[13] = 0x4E; packetBuffer[14] = 49; packetBuffer[15] = 52; // all NTP fields have been given values, now // you can send a packet requesting a timestamp: Udp.beginPacket(address, 123); //NTP requests are to port 123 Udp.write(packetBuffer, NTP_PACKET_SIZE); Udp.endPacket(); } /*-------- NTP code END ----------*/
Смотрим строки 19-29:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0x00, 0x00, 0x00 }; //MAC-адрес Arduino byte clock_addr[] = {0x39, 0x3a, 0x3b, 0x3d}; //адреса PCF8574 часов #define DS_PIN 2 //пин подключения термодатчика #define DS_UPDATE_INTERVAL 150000 //частота чтения данных с датчика (2.5 минуты) #define CLOCK_SHOW 15000 //продолжительность отображения часов #define TEMP_SHOW 5000 //продолжительность отображения температуры IPAddress timeServer(132, 163, 4, 101); // IP-адрес NTP сервера #define timeZone 7 // Временная зона
mac — MAC-адрес Ethernet-shield. В принципе, можете оставить таким же.
clock_addr — адреса микросхем PCD8574 на шине I2C. Если не меняли, то должны быть такими же. Определить адреса можно с помощью скетча i2c_scanner, который есть в конце статьи.
timeServer — IP-адрес NTP-сервера.
timeZone — номер часового пояса.
DS_PIN — номер порта, к которому подключен датчик DS18B20.
DS_UPDATE_INTERVAL — частота обновления данных с датчика (в миллисекундах).
CLOCK_SHOW — время отображения часов (в миллисекундах).
TEMP_SHOW — время отображения температуры (в миллисекундах).
