做了一个傻瓜时钟
接上网,加上电,即可工作。不用担心走时不准,也不用担心中途掉电,是不是很贴心的小傻瓜! 有图有真相 托米 发表于 2016-3-28 01:06 static/image/common/back.gif有图有真相
调试好后的图片。 介绍下让大家学习学习把呗 商业项目么?为毛细节都没有,连图都还得问了才贴上来啊。。。。 不好意思,因傻瓜时钟在不断升级中,所有没有来得及回复。现在自己终于对傻瓜时钟的功能比较满意了,所以将制作思路和遇到的问题一并与大家分享。
新的傻瓜时钟具备的功能:自动上网获取日期、星期、时间、天气情况、最高最低和当前温度,且每小时自动更新,数据更新期间,屏幕不闪烁,走时不间断。
制作思路:
一、时间的获取:采取UDP报文方式,从时间服务器time.nist.gov处获取时间,之后Arduino启动TIME2自行计时,定时校对,以便走时准确。
二、日期、天气等的获取:采用WEB方式,从百度"www.baidu.com"服务器中获取数据,定时更新,确保天气预报、当前温度等更新及时。
三、显示:采用2.4寸图形LCD显示,以便用图形显示天气情况(阴、晴、雨等),确保显示直观、美观。
选用的方案:
第一套方案:选用Arduino+W5100+Adafruit_TFTLCD硬件。这是我第一次制作采用的方案,建议大家也不要去试了。原因有三:一是不美观,始终有一根网线连着,移动不方便,看起来也很别扭;二是屏幕有闪烁。因为数据显示、数据获取的工作都由Arduino完成,尤其是上网查询数据需占用Arduino很多时间,所以每次数据更新时,屏幕会闪烁。
第二套方案:选用Arduino+ESP8266+Adafruit_TFTLCD硬件。这是我现在采用的方案,克服以上所有缺点,所以建议大家采用。
遇到的问题:
一、如何获取网页中汉字?我们可以打开百度,搜索“天气预报”,会看到类似下面这个页面。
如“周四4月14日 农历三月初八”等字样,我们要从中获取 “4月14日”中的"4"和"14"非常简单,Arduino例程中就有,用如下语句就行了。
if (client.find("class=\"op_weather4_twoicon_date\">")){
y=client.parseInt();
r=client.parseInt();
}
但如何获取“周四”中的汉字“四”,包括“三月初八“中的三和八,这就是我遇到的第一问题。
二、 如何获取天气情况的问题。好象和上面的问题有些类似,但仔细想想好象更难,天气情况的有:”阴、小雨、阴转小到中雨“等表述,字数不一定,长短不固定,好像更不好查。
三、如何优化程序,让上网获取数据和计时、显示相对独立,互不影响。
今天就到这儿吧,下次继续。
把程序接线方式都发上来吧 好的,直接上程序。第一套方案的程序:
/*
/* Arduino UNO+w5100+Adafruit_TFTLCD图形LCD显示屏
/* 功能:每天晚上24时9秒,自动软复位,上http://time.nist.gov网校准时间,并在图形显示屏上显示
*/
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <Adafruit_GFX.h>
#include <Adafruit_TFTLCD.h>
#include <MsTimer2.h>
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
unsigned int localPort = 8888; // local port to listen for UDP packets
char timeServer[] = "time.nist.gov"; // time.nist.gov NTP server
char server[] = "www.baidu.com"; ////
EthernetClient client;////
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
EthernetUDP Udp;
#define LCD_CS A3 // Chip Select goes to Analog 3
#define LCD_CD A2 // Command/Data goes to Analog 2
#define LCD_WR A1 // LCD Write goes to Analog 1
#define LCD_RD A0 // LCD Read goes to Analog 0
#define LCD_RESET A4 // Can alternately just connect to Arduino's reset pin
#defineBLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW0xFFE0
#define WHITE 0xFFFF
Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);
unsigned long s;//小时
unsigned long f;//分钟
unsigned long m;//秒
unsigned long s1;//小时数字变化
unsigned long f1;//
unsigned long m1;//
boolean bj=1;//
boolean sj=1;//小时数字变化标记,以便消隐
boolean fj=1;//分钟数字变化标记
boolean mj=1;//秒数字变化标记
int temperature=0;
int tempe=0;
char strvalue;//用于存放天气状况的值,区别不同天气主要是看
//https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/aladdin/img/new_weath/bigicon/8.png中".png"前的数字
//这个数字从1-24,即最小1个字符,最大2个字符
void setup()
{
Serial.begin(9600);
while (!Serial) {
;
}
tft.reset();
uint16_t identifier = tft.readID();
identifier=0x9341;
tft.begin(identifier);
tft.setRotation(1);//设置显示屏显示的方向,1代表旋转一个90度,如果是2则旋转2个90度,即180度
tft.setTextSize(4);tft.setTextColor(WHITE);
if (Ethernet.begin(mac) == 0) {
//Serial.println("Failed to configure Ethernet using DHCP");
for (;;)
;
}
Udp.begin(localPort);
MsTimer2::set(1000, flash); // 每秒调用一次 flash函数进行计时
tft.fillScreen(0x0056);//设置屏幕底色
}
void(*resetFunc)(void)=0;//系统重启命令,必须放在loop前面,否则会报错
void loop()
{
sw();
while(1){
xsfm();
if((s==0 && f==0 && m==9)||(s==1 && f==10 && m==2)||(s==5 && f==30 && m==2)||(s==14 && f==10 && m==2)||
(temperature==tempe)){
MsTimer2::stop();//temperature==tempe主要是为了检查下载温度值的有效性,因为最低、最高温度不可能相等
delay(1300);
resetFunc();//软启动系统,达到自动校时的目的
delay(30);
}
}
}
void htx1(int x,int y){//多云,以下参数是按照x=50,y=180设计的
tft.fillCircle(x,y,20,WHITE);//中上大圆
tft.fillCircle(x-20,y+10,13,WHITE);//左下中圆
tft.fillCircle(x+20,y+10,10,WHITE);//右下小圆
tft.fillCircle(x+20,y+10,7,0x0056);//擦除右下小圆内的线
tft.fillCircle(x-20,y+10,10,0x0056);//擦除左下中圆内的线
tft.fillCircle(x, y,17, 0x0056);//擦除中上大圆内的线
}
void htx2(int x,int y){//睛
for(int i=0;i<8;i+=1){
tft.drawLine(x,y, cos(i*0.7853982)*30+x,sin(i*0.7853982)*30+y, WHITE);//以x,y点为圆心,每45度画一条长度为30的直线
delay(2);
}
tft.fillCircle(x,y,20,0x0056);//擦除中间的线条
delay(2);
tft.fillCircle(x,y,15,WHITE);//画太阳的轮廓
tft.fillCircle(x,y,12,0x0056);//擦除太阳中间的色块
}
void htx3(int x,int y){//雪
for(int i=0;i<8;i+=1){
tft.drawLine(x-20,y-10, cos(i*0.7853982)*12+x-20,sin(i*0.7853982)*12+y-10, WHITE);//以x,y点为圆心,每45度画一条长度为30的直线
tft.drawLine(x+10,y+10, cos(i*0.7853982)*12+x+10,sin(i*0.7853982)*12+y+10, WHITE);
delay(2);
}
}
void htx4(int x,int y){//雨加雪
tft.drawLine(x-20,y-20,x-35,y+20,WHITE);
tft.fillCircle(x-24,y,5,0x0056);
for(int i=0;i<8;i+=1){
tft.drawLine(x+10,y, cos(i*0.7853982)*20+x+10,sin(i*0.7853982)*20+y, WHITE);
delay(2);
}
}
void htx5(int x,int y){//雨
tft.drawLine(x-18,y-20,x-33,y+20,WHITE);
tft.drawLine(x-20,y-20,x-35,y+20,WHITE);
tft.fillCircle(x-25,y,5,0x0056);
tft.drawLine(x,y-20,x-15,y+20,WHITE);
tft.drawLine(x+2,y-20,x-13,y+20,WHITE);
tft.fillCircle(x-4,y,5,0x0056);
tft.drawLine(x+22,y-20,x+7,y+20,WHITE);
tft.drawLine(x+20,y-20,x+5,y+20,WHITE);
tft.fillCircle(x+16,y,5,0x0056);
}
void htx6(int x,int y){//阴有小雨
tft.fillCircle(x,y-10,18,WHITE);//中上大圆
tft.fillCircle(x-18,y,11,WHITE);//左下中圆
tft.fillCircle(x+18,y,8,WHITE);//右下小圆
tft.fillCircle(x+18,y,6,0x0056);//擦除右下小圆内的线
tft.fillCircle(x-18,y,9,0x0056);//擦除左下中圆内的线
tft.fillCircle(x, y-10,16, 0x0056);//擦除中上大圆内的线
tft.fillCircle(x-5,y+18,3,WHITE);//擦除左下中圆内的线
tft.fillCircle(x+15,y+25,3,WHITE);//擦除左下中圆内的线
}
void sw(){//上网自动校准时间
if (client.connect(server, 80)) {
// Serial.println("connected");
client.println("GET /baidu?word=天气预报 HTTP/1.0");
client.println();
}
else {
//Serial.println("connection failed");
resetFunc();//软启动系统,达到重新上网的目的
}
if (client.connected()) {
//__________________________________________________________________________________//读取星期和日期
if (client.find("class=\"op_weather4_twoicon_date\">")){//表示从网页源代码的class="op_weather4_twoicon_date">处开始读
for(int i=0;i<118;i++){
Serial.println(client.read());
delay(1);
} //将读取指针移到“周二”字样的“周”字处
delay(1);
int a=client.read();//读取“周二”字样“二”处的汉字的第一个字节
int b=client.read();//读取“周二”字样“二”处的汉字的第二个字节
int c=client.read();//读取“周二”字样“二”处的汉字的第三个字节
int d=a+b+c;//将代表汉字的三个字节相加,以简便的方法判断是星期几
String xc="";
switch(d){
case 540://星期一
xc="Mon";
break;
case 554://星期二
xc="Tues";
break;
case 549://星期三
xc="Wed";
break;
case 539://星期四
xc="Thur";
break;
case 562://星期五
xc="Fri";
break;
case 535://星期六
xc="Sat";
break;
case 546://星期日
xc="Sun";
break;
default:
break;
}
int mm=client.parseInt();//源代码开始处向下读,出现的第1个阿拉伯数字,定义为月份变量。
int dd=client.parseInt();//源代码开始处向下读,出现的第2个阿拉伯数字,定义为日期变量。
int cm=client.parseInt();//源代码开始处向下读,出现的第2个阿拉伯数字,定义为当前温度。
tft.setCursor(20, 30); tft.print(mm); tft.print("/"); tft.print(dd);//
tft.setCursor(150, 30); tft.print(xc);//tft.print("Week ");
}
//__________________________________________________________________________________//读取天气状况
if (client.find("/www/aladdin/img/new_weath/bigicon/")){
char tq1=client.read();//读取第一个字符
// Serial.println(tq1);
strvalue=tq1;//取出第一个字符放在数组中
char tq2=client.read();//再读取一个字符
if(isDigit(tq2)){//判断这个字符是“0-9”的字符
strvalue=tq2;//如果是“0-9”之间的字符则放放数组
strvalue=0;//最后在数组中加一个终止字符0
}
else{
strvalue=0;//如果第二个读数不是“0-9”之间的字符,则数组中加一个终止字符0
}
int ln=atoi(strvalue);//将数组转化成int
//Serial.println(ln+2);//试验是否转化成功
if (ln==1||ln==2||ln==5||ln==6||ln==7){//表示晴
htx2(70,190);
}
if (ln==3||ln==4){//表示多云
htx1(70,190);
}
if (ln==8||ln==9||ln==10){//表示小雨、阴有雨
htx6(70,190);
}
if (ln==11||ln==12||ln==13){//表示中雨、暴雨、雷阵雨
htx5(70,190);
}
/*
if (ln==14){//表示雨+雪
htx4(70,190);
}
if (ln==17||ln==18||ln==19||ln==20){//表示小雪中雪暴雪
htx3(70,190);
} */
}
//__________________________________________________________________________________//读取最低、最高温度
if (client.find("class=\"op_weather4_twoicon_temp\">")){
tempe=client.parseInt();
temperature=client.parseInt();
tft.setCursor(140, 180);tft.setTextColor(WHITE); //tft.print("Temp ");
tft.print(temperature); tft.print("-"); tft.print(tempe);
tft.setCursor(260, 160); tft.setTextSize(3); tft.print(".");tft.setCursor(280, 180); tft.setTextSize(4); tft.print
("C");//
}
client.stop();
client.flush();
delay(1200);
//__________________________________________________________________________________//从服务器读取时分秒
sendNTPpacket(timeServer); // 给http://time.nist.govsend 服务器发送NTP包,请求下载时间
delay(1000);
if ( Udp.parsePacket() ) {//假如收到了数据包
Udp.read(packetBuffer, NTP_PACKET_SIZE);
unsigned long highWord = word(packetBuffer, packetBuffer);//时间戳开始于40字节,4个字节转换成长整数
unsigned long lowWord = word(packetBuffer, packetBuffer);
unsigned long secsSince1900 = highWord << 16 | lowWord;//这就是1900年到现在的NTP时间(单位为秒)
const unsigned long seventyYears = 2208988800UL;//1900年1月1日到1970年1月1日的秒数
unsigned long epoch = secsSince1900 - seventyYears;//1970年1月1日到现在的秒数
s=(epoch% 86400L) / 3600; //每天86400秒,余数中有一个3600秒就是一个小时
s=s+8;//北京时间是东八区,与格林治天文时间正好差8个小时
if (s>=24) {
s=s-24;
}
f=(epoch% 3600) / 60; //每分钟3600秒,除以60分钟的余数就为分钟
m=epoch % 60; //60的余数即为当前时间的秒数
}
bj=0;
MsTimer2::start();
}
}
void flash(){//Arduino计时
m=m+1;
mj=0;
if(m==60){
m=0;
f=f+1;
fj=0;
if(f==60) {
s=s+1;
sj=0;
f=0;
if(s==24){
s=0;
}
}
}
}
void xsfm(){//显示时分秒
tft.setTextSize(5);
xs();//显示小时
tft.print(":");
xf();//显示分钟
tft.print(":");
xm(); //显示秒
}
void xs(){//显示时
if(sj==0){
tft.fillRect(40, 100, 100,40, 0x0056); //在显示小时的位置画一个与底色相同的长方形,从而达到清屏的目的
sj=1;
}
if(sj==1){
tft.setCursor(40,100);tft.setTextColor(WHITE);
if(s<10){
tft.print("0"); tft.print(s);}
else
{tft.print(s); }
}
}
void xf(){//显示分
if(fj==0){
tft.fillRect(130,100, 100,40, 0x0056); //在显示分钟的位置画一个与底色相同的长方形,从而达到清屏的目的
fj=1;
}
if(fj==1){
tft.setCursor(130, 100);tft.setTextColor(WHITE);
if(f<10){
tft.print("0"); tft.print(f);}
else
{tft.print(f); }
}
}
void xm(){//显示秒
if(mj==0){
tft.fillRect(220, 100, 100,40, 0x0056); //在显示秒数字的位置画一个与底色相同的长方形,从而达到清屏的目的
mj=1;
}
if(mj==1){
tft.setCursor(220, 100);tft.setTextColor(WHITE);
if(m<10){
tft.print("0"); tft.print(m);}
else
{tft.print(m); }
}
}
unsigned long sendNTPpacket(char* address)
{
memset(packetBuffer, 0, NTP_PACKET_SIZE);
packetBuffer = 0b11100011; // LI, Version, Mode
packetBuffer = 0; // Stratum, or type of clock
packetBuffer = 6; // Polling Interval
packetBuffer = 0xEC;// Peer Clock Precision
packetBuffer= 49;
packetBuffer= 0x4E;
packetBuffer= 49;
packetBuffer= 52;
Udp.beginPacket(address, 123);
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}
这个方案有几点不是太好,一是月日周从网页中提取,一旦网页改版,就会出错。二是校对时间时,系统必须重启。第二套方案,就把以上问题解决了。下次再发第二套的程序吧! 面对面辅导毕业设计,或代为设计和撰写论文,限西安市区内!联系电话:13379261398,价格从优! 学习学习学习学习
页:
[1]