esp8266-weather-station/源码/WeatherStation/WeatherStation.ino

603 lines
14 KiB
Arduino
Raw Normal View History

2023-07-10 14:05:07 +08:00
#include <Esp8266Gzip.h>
/*
* 2023.7.10 zhb1190
* 1gzip响应的处理
* 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();
}