603 lines
14 KiB
C++
603 lines
14 KiB
C++
|
||
#include <Esp8266Gzip.h>
|
||
|
||
/*
|
||
* 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 <wifi_link_tool.h>
|
||
#include <ArduinoJson.h>
|
||
#include <Arduino.h>
|
||
|
||
#ifndef SERIAL_DEBUG
|
||
#include <U8g2lib.h>
|
||
#include "zlt.h"
|
||
#endif
|
||
|
||
#include <WiFiClientSecure.h>
|
||
|
||
#include <Time.h>
|
||
#include <TimeLib.h>
|
||
#include <WiFiUdp.h>
|
||
#include <WiFiClientSecure.h>
|
||
|
||
#include <WiFiClientSecureBearSSL.h>
|
||
|
||
#ifdef U8X8_HAVE_HW_SPI
|
||
#include <SPI.h>
|
||
#endif
|
||
#ifdef U8X8_HAVE_HW_I2C
|
||
#include <Wire.h>
|
||
#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_ptr<BearSSL::WiFiClientSecure>client(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();
|
||
}
|