极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 21078|回复: 9

做了一个傻瓜时钟

[复制链接]
发表于 2016-3-27 22:42:06 来自手机 | 显示全部楼层 |阅读模式
接上网,加上电,即可工作。不用担心走时不准,也不用担心中途掉电,是不是很贴心的小傻瓜!
回复

使用道具 举报

发表于 2016-3-28 01:06:41 | 显示全部楼层
有图有真相
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-4-2 09:03:00 | 显示全部楼层
托米 发表于 2016-3-28 01:06
有图有真相

调试好后的图片。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

发表于 2016-4-2 10:11:14 | 显示全部楼层
介绍下让大家学习学习把呗
回复 支持 反对

使用道具 举报

发表于 2016-4-2 10:31:47 | 显示全部楼层
商业项目么?为毛细节都没有,连图都还得问了才贴上来啊。。。。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-4-14 11:31:51 | 显示全部楼层
   不好意思,因傻瓜时钟在不断升级中,所有没有来得及回复。现在自己终于对傻瓜时钟的功能比较满意了,所以将制作思路和遇到的问题一并与大家分享。
   新的傻瓜时钟具备的功能:自动上网获取日期、星期、时间、天气情况、最高最低和当前温度,且每小时自动更新,数据更新期间,屏幕不闪烁,走时不间断。
      制作思路:
            一、时间的获取:采取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();
}
但如何获取“周四”中的汉字“四”,包括“三月初八“中的三和八,这就是我遇到的第一问题。
            二、 如何获取天气情况的问题。好象和上面的问题有些类似,但仔细想想好象更难,天气情况的有:”阴、小雨、阴转小到中雨“等表述,字数不一定,长短不固定,好像更不好查。
            三、如何优化程序,让上网获取数据和计时、显示相对独立,互不影响。
      今天就到这儿吧,下次继续。





本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

发表于 2016-4-16 10:48:44 | 显示全部楼层
把程序接线方式都发上来吧
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-4-17 13:14:25 | 显示全部楼层
好的,直接上程序。第一套方案的程序:
/*
/* 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

#define  BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#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[3];//用于存放天气状况的值,区别不同天气主要是看
//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[0]=tq1;//取出第一个字符放在数组中
    char tq2=client.read();//再读取一个字符
    if(isDigit(tq2)){//判断这个字符是“0-9”的字符
    strvalue[1]=tq2;//如果是“0-9”之间的字符则放放数组
    strvalue[2]=0;//最后在数组中加一个终止字符0
    }
    else{
       strvalue[1]=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[40], packetBuffer[41]);//时间戳开始于40字节,4个字节转换成长整数
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    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[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;
  Udp.beginPacket(address, 123);
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

这个方案有几点不是太好,一是月日周从网页中提取,一旦网页改版,就会出错。二是校对时间时,系统必须重启。第二套方案,就把以上问题解决了。下次再发第二套的程序吧!
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-5-29 08:55:34 来自手机 | 显示全部楼层
面对面辅导毕业设计,或代为设计和撰写论文,限西安市区内!联系电话:13379261398,价格从优!
回复 支持 反对

使用道具 举报

发表于 2016-5-31 00:07:58 | 显示全部楼层
学习学习学习学习
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|联系我们|极客工坊

GMT+8, 2026-6-14 09:57 , Processed in 0.040439 second(s), 25 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表