Занимаясь на выходных разборкой и сортировкой скопившегося имущества, практически внезапно обнаружил давно забытые детали. Ну как забытые … я конечно помнил, что они где-то есть, но уж очень давно не держал их в руках. Ну и плюс ко мне в руки попали платы на 8051 микроконтроллере, со встроенным приемопередатчиком. Просто так баловаться не интересно, поэтому необходимо совместить приятное с полезным.
Скажу сразу, просто еще одни часы делать мне не интересно – один раз я уже делал аж 2 года назад, до сих пор работают у тещи. Внутри ардуинка и sure 3208
Что у нас из приятного образуется? Во-первых, более большой и цветной индикатор.
В старых часах был одноцветный (только красный) индикатор, в котором была кучка светодиодов, расположенных 32 рядами по 8 штук. В новых будет аналогичная матрица, только умеющая уже 3 цвета и с матрицей светодиодов 64 на 16. Из-за размера светодиодов (в старой 5мм, в новой 3мм) разница в размерах не очень большая.
Во-вторых, использованная в первых часах микросхема DS1307 обладает небольшим уводом времени. Примерно минуту в неделю. Нет, я там приделал кнопки для коррекции времени, но это же несерьезно в 21м-то веке. Ловить всякие радиосигналы точного времени для меня безсмысленно, ибо лень. А вот подцепиться к WiFi и сбегать за точным временем в интернет к специализированному серверу – вот это да, уже стоит подумать.
И наконец, просто часы – это не интересно. Так как основное их место жительство будет кухня, то надо заставить их что-нибудь делать полезное. Курс доллара там показывать и радио играть. Или еще какую полезную работу выполнять. Список полезной работы будет уточняться по мере того, как я буду откапывать запчасти 🙂
Итак, пока часы. Схемы рисовать не буду, потому что 99% всего будет состоять из соединения проводками.
Для начала берем 2 индикатора sure 3216 и соединяем их приложенными к ним же проводами. Потом берем arduino leonardo и wifi shield. Все родное, итальянское, поэтому соединяем все в кучу. Достаем из пакетика мастеркитовский MP1095 и опять же соединяем проводками с ардуинкой. Все подписанное к подписанному (SDA к SDA, SCL к SCL и так далее).
И наконец соединяем полученный бутерброд из 3216 к ардуинке. На пины А0-А3. В нижеприведенном куске кода любой ардуинщик разберется, что куда подключается.
//Analog In 0 as PF7
//Analog In 1 as PF6
//Analog In 2 as PF5
//Analog In 3 as PF4
ht1632c ledMatrix = ht1632c(&PORTF, 7, 6, 5, 4, GEOM_32x16, 2);
ht1632c::ht1632c(const uint8_t data, const uint8_t wr, const uint8_t clk, const uint8_t cs, const uint8_t geometry, const uint8_t number)
И теперь добавляем магию, сиречь программирование. Я не стал сильно раздумывать, поэтому тупо собрал в одну кучу примеры общения с RTC, с NTP, c WiFi и с индикатором.
#include <ht1632c.h> #include <Wire.h> #include <RealTimeClockDS1307.h> #include <SPI.h> #include <WiFi.h> #include <WiFiUdp.h> ht1632c dm = ht1632c(&PORTF, 7, 6, 5, 4, GEOM_32x16, 2); int status = WL_IDLE_STATUS; char ssid[20] = "multik"; // your network SSID (name) char pass[20] = "MyVerySecretPassword"; // your network password int timeshift=3; // GMT offset unsigned int localPort = 2390; // local port to listen for UDP packets IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets WiFiUDP Udp; char clock[4]; int clockset=0; // does need to set clock from NTP? int clockprocess=0; // we start asking clock server? void setup() { Serial.begin(9600); while (!Serial) { ; // wait for serial port to connect. Needed for Leonardo only } // check for the presence of the shield: while (WiFi.status() == WL_NO_SHIELD) Serial.println("NOWIFI"); String fv = WiFi.firmwareVersion(); if ( fv != "1.1.0" ) Serial.println("FIRM"); status = WiFi.begin(ssid, pass); // start connecting dm.clear(); dm.setfont(FONT_8x16B); RTC.switchTo24h(); } void loop() { if(clockset==0) { //Serial.println("Setting clock"); if(status==WL_CONNECTED) // ok, we online { //Serial.println("WiFi con"); if(clockprocess==0) { Udp.begin(localPort); sendNTPpacket(timeServer); // send an NTP packet to a time server clockprocess=1; } else { if ( Udp.parsePacket() ) { //Serial.println("packet received"); Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer //the timestamp starts at byte 40 of the received packet and is four bytes, // or two words, long. First, esxtract the two words: unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); // combine the four bytes (two words) into a long integer // this is NTP time (seconds since Jan 1 1900): unsigned long secsSince1900 = highWord << 16 | lowWord; //Serial.print("Seconds since Jan 1 1900 = " ); //Serial.println(secsSince1900); // now convert NTP time into everyday time: // Serial.print("Unix time = "); // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: const unsigned long seventyYears = 2208988800UL; // subtract seventy years: unsigned long epoch = secsSince1900 - seventyYears; // print Unix time: //Serial.println(epoch); // print the hour, minute and second: //Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT) //Serial.print((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day) int h=(epoch % 86400L) / 3600; RTC.setHours(h+timeshift); //Serial.print(h);Serial.print(':'); //if ( ((epoch % 3600) / 60) < 10 ) { // In the first 10 minutes of each hour, we'll want a leading '0' //Serial.print('0'); //} int m=(epoch % 3600) / 60; //Serial.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute) RTC.setMinutes(m); //Serial.print(m);Serial.print(':'); //if ( (epoch % 60) < 10 ) { // In the first 10 seconds of each minute, we'll want a leading '0' //Serial.print('0'); //} int s=epoch % 60; //Serial.println(s); //Serial.println(epoch % 60); // print the second RTC.setSeconds(s); RTC.setClock(); clockset=1; } } } // else // { // Serial.println("WiFi NOT con"); // } } else { // Serial.println("Clock already setted"); } int x=0; int clr=GREEN; delay(1000); RTC.readClock(); sprintf(clock, "%02d", RTC.getHours()); // RTC.getMinutes(),RTC.getSeconds()); dm.setfont(FONT_8x16B); dm.putchar(x,0,clock[0],GREEN,0,BLACK); dm.putchar(x+9,0,clock[1],GREEN,0,BLACK); // first : dm.plot(x+18,4,clr); dm.plot(x+18,4+1,clr); dm.plot(x+18+1,4,clr); dm.plot(x+18+1,4+1,clr); dm.plot(x+18,4+4,clr); dm.plot(x+18,4+1+4,clr); dm.plot(x+18+1,4+4,clr); dm.plot(x+18+1,4+1+4,clr); sprintf(clock, "%02d", RTC.getMinutes()); //,RTC.getSeconds()); dm.putchar(x+21,0,clock[0],GREEN,0,BLACK); dm.putchar(x+30,0,clock[1],GREEN,0,BLACK); // second : dm.plot(x+39,4,clr); dm.plot(x+39,4+1,clr); dm.plot(x+39+1,4,clr); dm.plot(x+39+1,4+1,clr); dm.plot(x+39,4+4,clr); dm.plot(x+39,4+1+4,clr); dm.plot(x+39+1,4+4,clr); dm.plot(x+39+1,4+1+4,clr); sprintf(clock, "%02d", RTC.getSeconds()); dm.putchar(x+42,0,clock[0],GREEN,0,BLACK); dm.putchar(x+51,0,clock[1],GREEN,0,BLACK); dm.sendframe(); //Serial.println(clock); } // send an NTP request to the time server at the given address unsigned long sendNTPpacket(IPAddress& address) { //Serial.println("1"); // 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) //Serial.println("2"); 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; //Serial.println("3"); // 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 //Serial.println("4"); Udp.write(packetBuffer, NTP_PACKET_SIZE); //Serial.println("5"); Udp.endPacket(); //Serial.println("6"); }
В итоге получилось вот это
И видео
Что получилось в итоге? Часы запускаются и пытаются подключиться к WiFi с заранее заданным SSID и паролем. Как только им это удается, то они запрашивают NTP сервер о точном времени и как только получают ответ, обновляют соответствующие значения в RTC. А пока все это крутится, то часы просто берут уже сохраненное ранее время и показывают его.
Просто и дешево. Но для дальнейшей работы необходимо немного расширить функционал и научиться обрабатывать ошибки. К примеру, если WiFi есть, а интернета в нем нет и так далее. Ну и предусмотреть … скажем так, подключение внешних устройств, в качестве которых будет как минимум одна Raspberry Pi плата. Опять же, фотодиод для замера освещенности надо поставить …
В общем, есть что писать в следующем посте 🙂