zl程序教程

您现在的位置是:首页 >  Javascript

当前栏目

【立创开源】ESP8266制作的1.44寸TFT显示屏太空人天气时钟(st7735)(增加农历显示)(抄作业)

2023-04-18 16:40:01 时间

前言

之前学习律动灯条的时候买了一块esp8266开发板以及1.44寸的tft屏幕,一直闲置,所以学习制作网上爆火的天气时钟。同时为了便携,制作成可充电版本(typec充电)
请添加图片描述
在这里插入图片描述

软件和硬件都在:!!!!!点击这里!!!!!!

硬件制作

请添加图片描述
因为有现成的esp8266开发板,所以就在画pcb的时候就没有自己做esp8266的外围电路。
在这里插入图片描述

TFT的接线为:在这里插入图片描述
这对应的esp8266开发板的D0那一列。
在这里插入图片描述

对应TFT引脚:
在这里插入图片描述

这里的按键检测电路有问题,原来我以为可以程序设计该引脚为上拉状态,但是参考资料少,所以没找到。
在这里插入图片描述

最好设计为下图
在这里插入图片描述
修改
这里的原理图和pcb都是修改后的,我自己做的板子有点问题,只好飞线处理了。
在这里插入图片描述
在这里插入图片描述
洗板水在学校,所以板子看上去黄黄的,松香没洗。

电源管理

这里尝试一节锂电池降压到3.3v后给板子供电,可能因为显示屏缘故,电压不够,所以用两节锂电池串联降压到5v后,从板子的VIN供电。

充电电路参考: 基于CS5090E的5V升压给两节锂电池8.4v充电电路

这里注意1117降压芯片降压后不能和充电电路的5v连在一起,这样电池降压后又给电池充电,形成了回路。1117受不了,我的冒烟了。所以后面用剪刀把板子的线路划断了,然后飞线走的。上面的原理图和pcb是修改后的

软件抄作业

软件抄作业自这个大佬的:使用esp8266点亮福利屏型号st7735的1.44的TFT屏

软件比较复杂,涉及到物联网的知识,而且arduino编程序都是用的封装好的库,所以不容易理解。包括但不限与json解析,TFT显示,NTP服务器、网络配置等等,比较复杂,我也是抄的作业然后自己修改了一下。软件也放在上面的开源资料里了。

增加了按键控制亮度的问题,实际中发现这个东西是电老虎

  if(digitalRead(D0)==LOW&&key_press==0)
      {    
              lcd_light=lcd_light+20;
              analogWrite(LCD_BL_PIN,50);
      }

可以控制TFT的LED引脚(背光控制引脚)节省电。

API解析

因为农历生日常常记不住,所以我加了一个农历日期显示,这里就需要自己找API接口,来解析。

char n_nongli[30]   ="";
char n_shengxiao[1]   ="";
String nongli_show;
void nongli_get()
{
     const char* hosts = "lunarapi.top"; 
   // http://lunarapi.top/lunar/getbydate?date=2022-08-02
    // Connect to API
      Serial.print("连接nongli服务器::::");
      Serial.println(hosts);
      // Use WiFiClient class to create TCP connections
      WiFiClient nongclient;
      const int httpPort = 80;
      if (!nongclient.connect(hosts, httpPort)) {
        Serial.println("连接失败");
        return;
      }
      // We now create a URI for the request
   //增加公历日期到url中
     String date;
     date=String(year())+"-";
     if(month()<10)
     {
      date+="0"+String(month())+"-";
     }
     else
     {
      date+=String(month())+"-";
     }
     if(day()<10)
     {
      date+="0"+String(day());
     }
      else
     {
      date+=String(day());
     }
     //
      Serial.println(date);
      String url = "/lunar/getbydate?date="+date;
     
      Serial.print("Requesting URL: ");
      Serial.println(url);
      // This will send the request to the server
      nongclient.print(String("GET ") + url + " HTTP/1.1
" +
                   "Host: " + hosts + "
" +
                   "Connection: close

");
      delay(1000);
     
      // Read all the lines of the reply from server and print them to Serial
      String nong_data;
      while (nongclient.available()) 
      {
        String line = nongclient.readStringUntil('
');
        nong_data += line;
      }
      
      nongclient.stop();
      Serial.println();
      Serial.println("关闭连接");
      // Convert to JSON
      String json_nong_data;
      int jsonIndex;

      for (int i = 0; i < nong_data.length(); i++) 
      {
        if (nong_data[i] == '{') {
          jsonIndex = i;
          break;
        }
      }
      // Get JSON data
      json_nong_data = nong_data.substring(jsonIndex);
      Serial.println();

      Serial.println(json_nong_data);
    // String input;

    StaticJsonDocument<512> doc;
    //char state = 0;
    deserializeJson(doc, json_nong_data);
    JsonObject data = doc["data"];
    strcpy(n_nongli, data["lunarDateTime"]); //农历2021年3月2日
    nongli_show=n_nongli;
    Serial.println("nongli: ");
    Serial.println(nongli_show); 
}

网上都是利用的心知天气的API,但是访问几次就被限制了,或者还要花钱买,我找到另外一个不要钱的,但是访问时需要在url上加上公历的日期。例子如下
http://lunarapi.top/lunar/getbydate?date=2022-08-02

TFT农历显示:

    nongli_get();
    clk.createSprite(120, 18);
    clk.fillSprite(0x0000);
    clk.loadFont(nonglifont_15); 
    clk.setTextDatum(ML_DATUM);
    clk.setTextColor(0xFFFF, 0x0000);
    clk.drawString("农历:"+nongli_show,1,9);
    clk.pushSprite(5,108);
    clk.deleteSprite();
    clk.unloadFont(); //释放加载字体资源

这里又涉及到字库的问题了,需要下载一个软件,同时还要转码。
在这里插入图片描述
具体操作在:TFT_eSPI添加各种字库

ESP8266之TFT_eSPI库的自定义字体

上图软件在放在开头的资源包里面了。

结尾

时间不够,没有时间去画一个3D的外壳了。

通过做这个,对与网络API接口应用有了一个新的理解,同时因为不学网页开发,所以对json解析也不是很清楚,模仿别人代码试着解析理解了一下。

有兴趣可以把esp8266开发板和TFT集合在一块PCB板子上,就会更加小巧了。

arduino还是比较强大的,有各种库,但是不是专攻的话,不容易理解,因为封装的太好了,而且查找源文件还要到本地的库里面找。

最重要的是,编程不通过,可能是库的版本不对,我把所有库(比较大100多M)也一起发到资源包里面。还是编程不方便。

纠正一下,可能软件上传错了版本:背光引脚程序中要改成D2
在这里插入图片描述

下面是软件:
主要的NTP时间获取:

oid digitalClockDisplay()
{
  
  clk.setColorDepth(8);

  /***中间时间区***/
  //时分
  clk.createSprite(75, 28);
  clk.fillSprite(bgColor);
  clk.loadFont(FxLED_32);
  clk.setTextDatum(ML_DATUM);
  clk.setTextColor(timehmfontColor, bgColor);
  clk.drawString(hourMinute(),1,14,7); //绘制时和分
  clk.unloadFont();
  clk.pushSprite(10,19);
  clk.deleteSprite();
  
  //秒
  clk.createSprite(40, 28);
  clk.fillSprite(bgColor);
  
  clk.loadFont(FxLED_32);
  clk.setTextDatum(ML_DATUM);
  clk.setTextColor(timesfontColor, bgColor); 
  clk.drawString(":"+num2str(second()),1,14);
  
  clk.unloadFont();
  clk.pushSprite(83,19);
  clk.deleteSprite();
  /***中间时间区***/

  /***底部***/
  clk.loadFont(zkyyt12);
  clk.createSprite(30, 16);
  clk.fillSprite(bgColor);

  //星期
  clk.setTextDatum(ML_DATUM);
  clk.setTextColor(weekfontColor, bgColor);
  clk.drawString(week(),1,8);
  clk.pushSprite(2,89);
  clk.deleteSprite();
  
  //月日
  clk.createSprite(49,16);
  clk.fillSprite(bgColor);
  clk.setTextDatum(ML_DATUM);
  clk.setTextColor(monthfontColor, bgColor);  
  clk.drawString(monthDay(),1,8);
  clk.pushSprite(36,89);
  clk.deleteSprite();
  
  clk.unloadFont();
  /***底部***/

  
}

//星期
String week(){
  String wk[7] = {"日","一","二","三","四","五","六"};
  String s = "周" + wk[weekday()-1];
  return s;
}

//月日
String monthDay(){
  String s = String(month());
  s = s + "月" + day() + "日";
  return s;
}
//时分
String hourMinute(){
  String s = num2str(hour());
  backLight_hour = s.toInt();
  s = s + ":" + num2str(minute());
  return s;
}

String num2str(int digits)
{
  String s = "";
  if (digits < 10)
    s = s + "0";
  s = s + digits;
  return s;
}

void printDigits(int digits)
{
  Serial.print(":");
  if (digits < 10)
    Serial.print('0');
  Serial.print(digits);
}
//------------------------------------------------------------------------------------


//NTP部分的代码,包含2个函数------------------------------------------------------------

/*-------- NTP code ----------*/

const int NTP_PACKET_SIZE = 48; // NTP时间在消息的前48字节中
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets

time_t getNtpTime()
{
  IPAddress ntpServerIP; // NTP server's ip address

  while (Udp.parsePacket() > 0) ; // discard any previously received packets
  //Serial.println("Transmit NTP Request");
  // get a random server from the pool
  WiFi.hostByName(ntpServerName, ntpServerIP);
  //Serial.print(ntpServerName);
  //Serial.print(": ");
  //Serial.println(ntpServerIP);
  sendNTPpacket(ntpServerIP);
  uint32_t beginWait = millis();
  while (millis() - beginWait < 1500) {
    int size = Udp.parsePacket();
    if (size >= NTP_PACKET_SIZE) {
      Serial.println("可以呀,小伙子,NTP同步成功啦!!!");
      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];
      //Serial.println(secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR);
      return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
    }
  }
  //ESP.restart(); //时间获取失败直接重启
  Serial.println("NTP同步失败,别气馁,下次会成功的...");
  return 0; // 无法获取时间时返回0
}

// 向NTP服务器发送请求
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();
}

初始化和主循环:

void setup() {
 
         Serial.begin(115200);
  
         EEPROM.begin(1024);
        
          if(EEPROM.read(BL_addr)>0&&EEPROM.read(BL_addr)<100)
          LCD_BL_PWM = EEPROM.read(BL_addr); 
  
  
         pinMode(LCD_BL_PIN, OUTPUT);
          analogWrite(LCD_BL_PIN, 150);
    
         pinMode(Button, INPUT);
        
         tft.begin(); /* TFT init */
         tft.invertDisplay(1);//反转所有显示颜色:1反转,0正常
         tft.fillScreen(0x0000);
         tft.setTextColor(TFT_WHITE, 0x0000);
         // 设置屏幕显示的旋转角度,参数为:0, 1, 2, 3
        // 分别代表 0°、90°、180°、270°
        //根据实际需要旋转
          tft.setRotation(4); 
          
          TJpgDec.setJpgScale(1);
          TJpgDec.setSwapBytes(true);
          TJpgDec.setCallback(tft_output);

          targetTime = millis() + 1000; 
        
          Serial.print("正在连接WIFI ");
          Serial.println(ssid);
          WiFi.begin(WiFi.SSID().c_str(),WiFi.psk().c_str());
          
          TJpgDec.setJpgScale(1);
          TJpgDec.setSwapBytes(true);
          TJpgDec.setCallback(tft_output);
          tft.fillScreen(0x0000);
          while (WiFi.status() != WL_CONNECTED) 
          {
            loading(70);  
              
            if(loadNum>=94)
            {
              SmartConfig();   
              break;
            }
          }
          delay(10); 
          while(loadNum < 94) //让动画走完
          { 
            loading(1);
          }
        
          Serial.print("本地IP: ");
          Serial.println(WiFi.localIP());
          //Serial.println("启动UDP");
          Udp.begin(localPort);
          //Serial.print("端口号: ");
          //Serial.println(Udp.localPort());
          //Serial.println("等待同步...");
          setSyncProvider(getNtpTime);
          setSyncInterval(setNTPSyncTime*60); //NTP网络同步频率,单位秒。
         
 

          //绘制一个视口

          tft.fillScreen(0x0000);
          tft.fillRoundRect(0,0,128,128,0,bgColor);//实心矩形

        
          //绘制线框
          tft.drawFastHLine(0,0,128,xkColor);

        
          tft.drawFastHLine(0,18,128,xkColor);
          tft.drawFastHLine(0,106,128,xkColor);
          
          tft.drawFastVLine(80,0,18,xkColor);
          
          tft.drawFastHLine(0,88,128,xkColor);
          
          tft.drawFastVLine(32,88,18,xkColor);
          tft.drawFastVLine(85,88,18,xkColor);
          int TCityCODE = 0;
          for(int cnum=5;cnum>0;cnum--)
          {          
            TCityCODE = TCityCODE*100;
            TCityCODE += EEPROM.read(CC_addr+cnum-1); 
            delay(5);
          }
          if(TCityCODE>=101000000 && TCityCODE<=102000000) 
            cityCode = String(TCityCODE);  
          else
           getCityCode();  //获取城市代码                  
          getCityWeater();    
         // get_Bstation_follow();
         // fanspush();  
          nonli_show();       
        
           pinMode(D0,INPUT);
           
}
unsigned long nongliTime = 0;
unsigned long weaterTime = 0;
void loop() {
              key_state();
              
                if (now() != prevDisplay) 
              {
                prevDisplay = now();
                digitalClockDisplay();
              }
 
             if(millis() - weaterTime > 300000){ //5分钟更新一次天气 millis()五十天后溢出
                weaterTime = millis();
                getCityWeater();   
               // nonli_show();     
              }
               if(millis() - nongliTime > 21600000){ //5分钟更新一次天气
                nongliTime = millis();  
                nonli_show();     
              }

     scrollBanner();
     weatherWarning();
     imgAnim();
     Serial_set();

  }

城市信息获取:

//CityCode文件夹内的js文件搜索可得

String cityCode = "101250101";


// 发送HTTP请求并且将服务器响应通过串口输出
void getCityCode(){
 String URL = "http://wgeo.weather.com.cn/ip/?_="+String(now());
  //创建 HTTPClient 对象

HTTPClient httpClient;
httpClient.begin(wificlient,URL); 
  
  //设置请求头中的User-Agent
  httpClient.setUserAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1");
  httpClient.addHeader("Referer", "http://www.weather.com.cn/");
 
  //启动连接并发送HTTP请求
  int httpCode = httpClient.GET();
  Serial.print("Send GET request to URL: ");
  Serial.println(URL);
  
  //如果服务器响应OK则从服务器获取响应体信息并通过串口输出
  if (httpCode == HTTP_CODE_OK) {
    String str = httpClient.getString();
    
    int aa = str.indexOf("id=");
    if(aa>-1)
    {
       //cityCode = str.substring(aa+4,aa+4+9).toInt();
       cityCode = str.substring(aa+4,aa+4+9);
       Serial.println(cityCode); 
       getCityWeater();
    }
    else
    {
      Serial.println("获取城市代码失败");  
    }
    
    
  } else {
    Serial.println("请求城市代码错误:");
    Serial.println(httpCode);
  }
 
  //关闭ESP8266与服务器连接
  httpClient.end();
}

其他相关代码在资源包里,整体挺多的,放不下了。