#include /* * 2023.7.10 zhb1190 * 1、增加对gzip响应的处理 * 2、修改气象站源码的稳定性 * * * 0.96 气象站 第五版 配套源码 需要配合wifi_link_tool配网工具 地址:https://github.com/bilibilifmk/wifi_link_tool 所需库: u8g2 ArduinoJson time(以提供) by:发明控 */ //#define SERIAL_DEBUG #define FS_CONFIG //兼容1.1.x版本库 #include #include #include #ifndef SERIAL_DEBUG #include #include "zlt.h" #endif #include #include #include #include #include #include #ifdef U8X8_HAVE_HW_SPI #include #endif #ifdef U8X8_HAVE_HW_I2C #include #endif String keys = "xxxxx"; // 接口地址:https://console.qweather.com/app/index String dq = "xxxx"; //填入城市编号 获取编号 https://where.qweather.com/index.html static const uint8_t D1 = 1; static const uint8_t D2 = 3; static const uint8_t D3 = 0; static const uint8_t D4 = 2; #define sck D1 /* 屏幕 */ #define sda D2 const int CONFIG_HTTPPORT = 80; const int CONFIG_HTTPSPORT = 443; //const uint8_t fingerprint[20] = { 0xB8, 0x93, 0x1E, 0xBD, 0xCB, 0xCD, 0xCE, 0xE5, 0xC5, 0x9F, 0x08, 0x43, 0xA8, 0x6C, 0x8C, 0xD0, 0xF7, 0xCB, 0x1E, 0x42 }; #ifndef SERIAL_DEBUG U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2( U8G2_R0, /* clock=*/ sck, /* data=*/ sda, /* reset=*/ U8X8_PIN_NONE ); #endif int col = 999; String diqu = ""; int tq = 0; String pm2 = ""; String wendu = ""; String tim = "00:00"; String dat = "2019/01/01"; unsigned long previousMillis = 0; const long interval = 30000; /* 30秒更新屏幕 */ unsigned long previousMillis2 = 0; const long interval2 =1800000; /* 半小时更新天气 */ int one = 0; int pcboot=0; //pc请求识别 IPAddress timeServer( 120, 25, 115, 20 ); /* 阿里云ntp服务器 如果失效可以使用 120.25.115.19 120.25.115.20 */ #define STD_TIMEZONE_OFFSET +8 /* 设置中国 */ const int timeZone = 8; /* 修改北京时区 */ WiFiUDP Udp; unsigned int localPort = 8266; /* 修改udp 有些路由器端口冲突时修改 */ int servoLift = 1500; void setup() { WiFiServer HTTPserver( CONFIG_HTTPPORT ); WiFiServerSecure HTTPSserver( CONFIG_HTTPSPORT ); #ifdef SERIAL_DEBUG Serial.begin( 115200 ); #endif /* * 开启串口 * ///////////////////////////////////////////////////////基础设置////////////////////////////////////////////////////////// */ rstb = D3; /* 重置io */ stateled = D4; /* 指示灯io */ Hostname = "oled气象站"; /* 设备名称 允许中文名称 不建议太长 */ wxscan = true; /* 是否被小程序发现设备 开启意味该设备具有后台 true开启 false关闭 */ #ifndef SERIAL_DEBUG u8g2.begin(); u8g2.enableUTF8Print(); u8g2.setFont( u8g2_font_unifont_t_chinese2 ); u8g2.setFontDirection( 0 ); u8g2.clearBuffer(); u8g2.setCursor( 0, 15 ); u8g2.print( "开机" ); u8g2.setCursor( 0, 30 ); u8g2.print( "WiFi to Link" ); u8g2.sendBuffer(); #endif webServer.on("/pc", pc);//pc请求 load(); /* 初始化WiFi link tool */ #ifdef SERIAL_DEBUG Serial.println( "设置 UDP" ); #endif Udp.begin( localPort ); #ifdef SERIAL_DEBUG Serial.print( "Local port: " ); Serial.println( Udp.localPort() ); Serial.println( "正在等待同步" ); #endif if ( link() ) { #ifndef SERIAL_DEBUG u8g2.setFont( u8g2_font_unifont_t_chinese2 ); u8g2.setFontDirection( 0 ); u8g2.clearBuffer(); u8g2.setCursor( 0, 15 ); u8g2.print( "开机" ); u8g2.setCursor( 0, 30 ); u8g2.print( "WiFi to Link" ); u8g2.setCursor( 0, 45 ); u8g2.print( "OK" ); u8g2.setCursor( 0, 60 ); u8g2.print( WiFi.localIP() ); u8g2.sendBuffer(); #endif sjfx(); delay( 1000 ); #ifndef SERIAL_DEBUG u8g2.sendBuffer(); u8g2.clearBuffer(); u8g2.drawXBMP(0,0,128,64,logo); u8g2.sendBuffer(); #endif delay(1000); setSyncProvider( getNtpTime ); shuaxin(); }else{ #ifndef SERIAL_DEBUG u8g2.setFont( u8g2_font_unifont_t_chinese2 ); u8g2.setFontDirection( 0 ); u8g2.clearBuffer(); u8g2.setCursor( 45, 15 ); u8g2.print( "配网" ); u8g2.setCursor( 0, 30 ); u8g2.print( "AP TO Link:" ); u8g2.setCursor( 0, 45 ); u8g2.print( "wifi_link_tool" ); u8g2.setCursor( 0, 60 ); u8g2.print( "IP:6.6.6.6" ); u8g2.sendBuffer(); #endif delay( 5000 ); #ifndef SERIAL_DEBUG u8g2.clearBuffer(); u8g2.drawXBMP( 0, 0, 128, 64, wx ); u8g2.sendBuffer(); #endif } } void loop() { pant(); /* WiFi link tool 服务维持函数 请勿修改位置 */ if ( link() ) { unsigned long currentMillis = millis(); if ( currentMillis - previousMillis >= interval ) { previousMillis = currentMillis; shuaxin(); //xx(); } unsigned long currentMillis2 = millis(); if ( currentMillis2 - previousMillis2 >= interval2 ) { previousMillis2 = currentMillis2; one = 0; sjfx(); } } } void sjfx() { xx(); delay( 1000 ); pm(); delay( 1000 ); col = tq; #ifdef SERIAL_DEBUG Serial.println( "当前地区: " + diqu ); Serial.println( "天气代码: " + tq); Serial.println( "体感温度: " + wendu ); Serial.println( "PM2.5: " + pm2 ); #endif } void shuaxin() { if(pcboot==0) { { String zov = ""; if (hour() < 10) { zov = "0"; } if (minute() < 10) { tim = zov + String( hour() ) + ":0" + String( minute() ); }else{ tim = zov + String( hour() ) + ":" + String( minute() ); } dat = String( year() ) + "/" + String( month() ) + "/" + String( day() ); #ifdef SERIAL_DEBUG Serial.print( tim ); /* 输出当前网络分钟 */ Serial.print(" "); Serial.print( dat ); /* 输出当前日期 */ Serial.println(); #endif #ifndef SERIAL_DEBUG u8g2.clearBuffer(); #endif } /* */ tubiao(); #ifndef SERIAL_DEBUG { u8g2.setFont(u8g2_font_ncenB18_tf); u8g2.setFontDirection(0); u8g2.setCursor(65,53); u8g2.print(wendu); //温度 u8g2.setFont(u8g2_font_6x10_tf); u8g2.setFontDirection(0); u8g2.setCursor(60,64); u8g2.print(dat); //日期 u8g2.drawXBMP(100,32,25,25,col_ssd); u8g2.setFont(u8g2_font_ncenR12_tf); u8g2.setFontDirection(0); u8g2.setCursor(0,48); u8g2.print("pm2.5"); u8g2.setCursor(10,62); u8g2.print(pm2); //pm2.5 浓度 u8g2.setFont(u8g2_font_fub25_tf); u8g2.setFontDirection(0); u8g2.setCursor(40,32); u8g2.print(tim); u8g2.sendBuffer(); } #endif } else { pcboot=0; } } void tubiao( ){ #ifndef SERIAL_DEBUG const unsigned char* icon = col_999; if (col==100||col==150){ //晴 icon = col_100; } else if (col==102||col==101){ icon = col_102; }else if (col==103||col==153){ icon = col_103;//晴间多云 }else if (col==104||col==154){ icon = col_104;//阴 } else if (col>=300&&col<=301){ icon = col_301;//阵雨 }else if (col>=302&&col<=303){ icon = col_302;//雷阵雨 }else if (col==304){ icon = col_304;//冰雹 }else if (col==399||col==314||col==305||col==306||col==307||col==315||col==350||col==351){ icon = col_307;//雨 } else if ((col>=308&&col<=313)||(col>=316&&col<=318)){ icon = col_310;//暴雨 }else if ((col>=402&&col<=406)||col==409||col==410||col==400||col==401||col==408||col==499||col==456){ icon = col_401;//雪 }else if (col==407||col==457){ icon = col_407;//阵雪 }else if (col>=500&&col<=502){ icon = col_500;//雾霾 }else if (col>=503&&col<=508){ icon = col_503;//沙尘暴 }else if (col>=509&&col<=515){ icon = col_509;;//不适宜生存 } u8g2.drawXBMP(0,0,40,40,icon); #endif } void pc() { #if 0 pcboot=1; String clk=webServer.arg("clk"); String cpu=webServer.arg("cpu"); String ram=webServer.arg("ram"); String cput=webServer.arg("cput"); webServer.arg("cpuv"); #ifndef SERIAL_DEBUG u8g2.clearBuffer(); u8g2.drawXBMP(0,0,128,64,pctp); u8g2.setFont(u8g2_font_crox5hb_tf); u8g2.setFontDirection(0); u8g2.setCursor(33,25); u8g2.print(cpu); //cpu u8g2.setCursor(33,57); u8g2.print(cput); //cput u8g2.setCursor(95,57); u8g2.print(ram); //ram u8g2.setFont(u8g2_font_ncenR10_tf); u8g2.setFontDirection(0); u8g2.setCursor(96,16); u8g2.print(clk); //mhz u8g2.setCursor(94,30); u8g2.print("MHz"); u8g2.sendBuffer(); #endif webServer.send(200, "text/plain", "ojbk"); #endif } /* //////////////////////////////////////////////////天气数据 */ void xx() { uint32_t outlen = 512; uint8_t* buff = (uint8_t*)malloc(outlen); if(!buff) { #ifdef SERIAL_DEBUG Serial.println("malloc memory failed"); #endif return; } if(https_get_gzip("https://devapi.qweather.net/v7/weather/now?gzip=n&location="+dq+"&key="+keys, buff, outlen)) { #ifdef SERIAL_DEBUG Serial.printf("Get Data %s\n", buff); #endif } else { free(buff); return; } DynamicJsonBuffer jsonBuffer(1024); JsonObject& res_json = jsonBuffer.parseObject(buff); String r1=res_json["basic"]["location"];//地区 int r2=res_json["now"]["icon"];//天气 String r3=res_json["now"]["feelsLike"];//体感温度 jsonBuffer.clear(); diqu = r1; /* 地区 */ tq = r2; /* 天气 */ wendu = r3.toInt(); /* 体感温度 */ free(buff); } void pm() { uint32_t outlen = 1024; uint8_t* buff = (uint8_t*)malloc(outlen); if(!buff) { #ifdef SERIAL_DEBUG Serial.println("malloc memory failed"); #endif return; } if(https_get_gzip("https://devapi.qweather.net/v7/air/now?gzip=n&location="+dq+"&key="+keys, buff, outlen)) { #ifdef SERIAL_DEBUG Serial.printf("Get Data %s\n", buff); #endif } else { free(buff); return; } DynamicJsonBuffer jsonBuffer(1024); JsonObject& res_json = jsonBuffer.parseObject(buff); String r1=res_json["now"]["pm2p5"];//pm25 jsonBuffer.clear(); if ( r1 != "" ) { pm2 = r1; /* pm2.5 */ } free(buff); } /* ////////////////////////////////////////////////////////////////解包设置////////////////////////////////////////////////////////////////////////////////////////////////////////////// */ void digitalClockDisplay() { /* */ #ifdef SERIAL_DEBUG Serial.print( hour() ); #endif printDigits( minute() ); #ifdef SERIAL_DEBUG Serial.println(); #endif } void printDigits( int digits ) { #ifdef SERIAL_DEBUG Serial.print( ":" ); #endif if ( digits < 10 ) { #ifdef SERIAL_DEBUG Serial.print( '0' ); #endif } #ifdef SERIAL_DEBUG Serial.print( digits ); #endif } bool https_get_gzip(String url, uint8_t* outbuf, uint32_t& outlen) { uint8_t* buff = 0; int readed = 0; { std::unique_ptrclient(new BearSSL::WiFiClientSecure); client->setInsecure(); HTTPClient https; #ifdef SERIAL_DEBUG Serial.println("HTTP begin"); #endif if (https.begin(*client, url)) { #ifdef SERIAL_DEBUG Serial.println("HTTP GET..."); #endif int httpCode = https.GET(); #ifdef SERIAL_DEBUG Serial.printf("HTTP Code %d\n", httpCode); #endif int len = 0; if (httpCode == HTTP_CODE_OK) { len = https.getSize(); #ifdef SERIAL_DEBUG Serial.printf("HTTP body len %d\n", len); #endif } else { #ifdef SERIAL_DEBUG Serial.println("HTTP GET error"); #endif https.end(); return false; } buff = (uint8_t*)malloc(sizeof(uint8_t)*(len+1)); memset(buff, 0, len+1); while (https.connected() && (len > 0 || len == -1)) { // read up to 128 byte int c = client->readBytes(buff+readed, std::min((size_t)len, (size_t)(len-1-readed))); #ifdef SERIAL_DEBUG Serial.printf("readBytes: %d\n", c); #endif if (!c) { #ifdef SERIAL_DEBUG Serial.println("read timeout"); #endif readed = 0; break; } readed+=c; if (len > 0) { len -= c; } } #ifdef SERIAL_DEBUG Serial.printf("HTTP buff len %d\n", readed); #endif https.end(); } else { #ifdef SERIAL_DEBUG Serial.printf("[HTTPS]请求链接失败\n"); #endif return false; } } if(outlen<=readed) { #ifdef SERIAL_DEBUG Serial.println("out buf is too small"); #endif return false; } if(readed) { memset(outbuf, 0, outlen); int result = 0; #ifdef SERIAL_DEBUG Serial.println(ESP.getFreeHeap()); #endif result=Esp8266Gzip::decompress_gzip(buff, readed, outbuf, outlen,outlen); if(result>0){ //Serial.write(outbuf,outlen); } else { #ifdef SERIAL_DEBUG Serial.printf("gzip decode ret %d\n", result); #endif outlen = 0; return false; } free(buff); return true; } return false; } const int NTP_PACKET_SIZE = 48; byte packetBuffer[NTP_PACKET_SIZE]; time_t getNtpTime() { while ( Udp.parsePacket() > 0 ) ; #ifdef SERIAL_DEBUG Serial.println( "连接时间服务器" ); #endif sendNTPpacket( timeServer ); uint32_t beginWait = millis(); while ( millis() - beginWait < 1500 ) { int size = Udp.parsePacket(); if ( size >= NTP_PACKET_SIZE ) { Serial.println( "时间服务器应答" ); Udp.read( packetBuffer, NTP_PACKET_SIZE ); 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); } } #ifdef SERIAL_DEBUG Serial.println( "No NTP Response :-(" ); #endif return(0); } void sendNTPpacket( IPAddress &address ) { memset( packetBuffer, 0, NTP_PACKET_SIZE ); packetBuffer[0] = 0b11100011; packetBuffer[1] = 0; packetBuffer[3] = 0xEC; packetBuffer[12] = 49; packetBuffer[13] = 0x4E; packetBuffer[14] = 49; packetBuffer[15] = 52; Udp.beginPacket( address, 123 ); Udp.write( packetBuffer, NTP_PACKET_SIZE ); Udp.endPacket(); }