esp8266-weather-station/源码/WeatherStation/WeatherStation.ino
2023-07-10 14:05:07 +08:00

603 lines
14 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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();
}