/**********************************************************************/
/* 学习例程: 关于esp8266 TCP_HttpClient
学习目的: HttpClient 连接步骤及ESP8266WiFiClient 库相关函数
学习日期: 20200709
*/
/**********************************************************************/
//#include <Arduino.h>
#include<ESP8266WiFi.h> //WIFI 头文件
#include <U8g2lib.h> //第三方库
#include <Wire.h> //I2C库头文件
#include <EEPROM.h> //EEPROM
#include <ArduinoJson.h>
#include <ESP8266HTTPClient.h>
/*
url可以有以下几种形态
1. http://192.168.1.12/test.html
2. http://user:password@192.168.1.12/test.html
3. http://user:password@192.168.1.12:8888/test.html
*/
/*
typedef enum {
WL_NO_SHIELD = 255, // for compatibility with WiFi Shield library
WL_IDLE_STATUS = 0,
WL_NO_SSID_AVAIL = 1,
WL_SCAN_COMPLETED = 2,
WL_CONNECTED = 3,
WL_CONNECT_FAILED = 4,
WL_CONNECTION_LOST = 5,
WL_DISCONNECTED = 6
} wl_status_t;
*/
/*
/// HTTP codes see RFC7231
typedef enum {
HTTP_CODE_CONTINUE = 100,
HTTP_CODE_SWITCHING_PROTOCOLS = 101,
HTTP_CODE_PROCESSING = 102,
HTTP_CODE_OK = 200,
HTTP_CODE_CREATED = 201,
HTTP_CODE_ACCEPTED = 202,
HTTP_CODE_NON_AUTHORITATIVE_INFORMATION = 203,
HTTP_CODE_NO_CONTENT = 204,
HTTP_CODE_RESET_CONTENT = 205,
HTTP_CODE_PARTIAL_CONTENT = 206,
HTTP_CODE_MULTI_STATUS = 207,
HTTP_CODE_ALREADY_REPORTED = 208,
HTTP_CODE_IM_USED = 226,
HTTP_CODE_MULTIPLE_CHOICES = 300,
HTTP_CODE_MOVED_PERMANENTLY = 301,
HTTP_CODE_FOUND = 302,
HTTP_CODE_SEE_OTHER = 303,
HTTP_CODE_NOT_MODIFIED = 304,
HTTP_CODE_USE_PROXY = 305,
HTTP_CODE_TEMPORARY_REDIRECT = 307,
HTTP_CODE_PERMANENT_REDIRECT = 308,
HTTP_CODE_BAD_REQUEST = 400,
HTTP_CODE_UNAUTHORIZED = 401,
HTTP_CODE_PAYMENT_REQUIRED = 402,
HTTP_CODE_FORBIDDEN = 403,
HTTP_CODE_NOT_FOUND = 404,
HTTP_CODE_METHOD_NOT_ALLOWED = 405,
HTTP_CODE_NOT_ACCEPTABLE = 406,
HTTP_CODE_PROXY_AUTHENTICATION_REQUIRED = 407,
HTTP_CODE_REQUEST_TIMEOUT = 408,
HTTP_CODE_CONFLICT = 409,
HTTP_CODE_GONE = 410,
HTTP_CODE_LENGTH_REQUIRED = 411,
HTTP_CODE_PRECONDITION_FAILED = 412,
HTTP_CODE_PAYLOAD_TOO_LARGE = 413,
HTTP_CODE_URI_TOO_LONG = 414,
HTTP_CODE_UNSUPPORTED_MEDIA_TYPE = 415,
HTTP_CODE_RANGE_NOT_SATISFIABLE = 416,
HTTP_CODE_EXPECTATION_FAILED = 417,
HTTP_CODE_MISDIRECTED_REQUEST = 421,
HTTP_CODE_UNPROCESSABLE_ENTITY = 422,
HTTP_CODE_LOCKED = 423,
HTTP_CODE_FAILED_DEPENDENCY = 424,
HTTP_CODE_UPGRADE_REQUIRED = 426,
HTTP_CODE_PRECONDITION_REQUIRED = 428,
HTTP_CODE_TOO_MANY_REQUESTS = 429,
HTTP_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
HTTP_CODE_INTERNAL_SERVER_ERROR = 500,
HTTP_CODE_NOT_IMPLEMENTED = 501,
HTTP_CODE_BAD_GATEWAY = 502,
HTTP_CODE_SERVICE_UNAVAILABLE = 503,
HTTP_CODE_GATEWAY_TIMEOUT = 504,
HTTP_CODE_HTTP_VERSION_NOT_SUPPORTED = 505,
HTTP_CODE_VARIANT_ALSO_NEGOTIATES = 506,
HTTP_CODE_INSUFFICIENT_STORAGE = 507,
HTTP_CODE_LOOP_DETECTED = 508,
HTTP_CODE_NOT_EXTENDED = 510,
HTTP_CODE_NETWORK_AUTHENTICATION_REQUIRED = 511
} t_http_codes;
*/
/*
bool begin(WiFiClient &client, const String& url); //封装请求Url 解析url以获得所有参数,默认port是80端口
bool begin(WiFiClient &client, const String& host, uint16_t port, const String& uri = "/", bool https = false);
void setReuse(bool reuse); // keep-alive //封装标准请求头keep-alive
void setUserAgent(const String& userAgent);//封装标准请求头User-Agent
void setAuthorization(const char * user, const char * password); // 封装标准请求头Authorization
void setAuthorization(const char * auth);
void setTimeout(uint16_t timeout); //设置请求超时
void end(void);//结束请求
// Redirections
void setFollowRedirects(bool follow) __attribute__ ((deprecated));
void setFollowRedirects(followRedirects_t follow);
void setRedirectLimit(uint16_t limit); // max redirects to follow for a single request
bool setURL(const String& url); // handy for handling redirects
void useHTTP10(bool usehttp10 = true); // http协议版本
/// request handling
int GET(); //GET请求
int POST(const uint8_t* payload, size_t size); //POST请求
int POST(const String& payload);
int PUT(const uint8_t* payload, size_t size); //PUT 请求
int PUT(const String& payload);
int PATCH(const uint8_t* payload, size_t size);
int PATCH(const String& payload);
int sendRequest(const char* type, const String& payload);
int sendRequest(const char* type, const uint8_t* payload = NULL, size_t size = 0);
int sendRequest(const char* type, Stream * stream, size_t size = 0);
void addHeader(const String& name, const String& value, bool first = false, bool replace = true);
/// Response handling
void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // 设置需要收集的响应头
String header(const char* name); // get request header value by name // 获取具体响应头参数值
String header(size_t i); // get request header value by number // 获取第index个响应头参数值
String headerName(size_t i); // get request header name by number //获取第i个响应头名字
int headers(); // get header count //获取收集响应头个数
bool hasHeader(const char* name); // check if header exists //判断是否存在某一个响应头
int getSize(void); //获取响应数据
const String& getLocation(void); // Location header from redirect if 3XX //
WiFiClient& getStream(void); //获取响应数据的流
WiFiClient* getStreamPtr(void); //获取响应数据的流
int writeToStream(Stream* stream); //获取响应数据的流,并写到其他流对象
const String& getString(void);
static String errorToString(int error); //获取请求失败响应信息
bool beginInternal(const String& url, const char* expectedProtocol);
void disconnect(bool preserveClient = false);
void clear();
int returnError(int error);
bool connect(void);
bool sendHeader(const char * type);
int handleHeaderResponse(); //处理响应头数据
int writeToStreamDataBlock(Stream * stream, int len);
*/
#define LED D9
long debouncdDelay = 5000; //延时间隔
long lastDebounceTime = 0; //最近记录的一次时间
U8G2_SSD1306_128X64_NONAME_1_SW_I2C u8g2(U8G2_R0, /* clock=*/ D6, /* data=*/ D5, /* reset=*/ U8X8_PIN_NONE); // ESP32 Thing, pure SW emulated I2C
//U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ D6, /* data=*/ D5); // ESP32 Thing, HW I2C with pin remapping
//定义 AP 的名称及密码
#define AP_SSID "vivoX20A"
#define AP_PASSWORD "12345678"
// 配置热点的参数信息(IP,网关,子掩码)
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
const unsigned long BAUD_RATE = 115200; // serial connection speed
const unsigned long HTTP_TIMEOUT = 5000; // max respone time from server
IPAddress local_IP(192, 168, 4, 22);
IPAddress gateway(192, 168, 4, 9);
IPAddress subnet(255, 255, 255, 0);
const char* HOST = "http://api.seniverse.com";
const char* APIKEY = "S5w3L6_qi2SBdUnIT"; //API KEY
const char* CITY = "zhengzhou";
const char* LANGUAGE = "zh-Hans";//zh-Hans 简体中文 会显示乱码
const char *keys[] = {"Content-Length","Content-Type","Connection","Date"};//需要收集的响应头的信息
// 我们要从此网页中提取的数据的类型
struct WeatherData {
char city[16];//城市名称
char weather[32];//天气介绍(多云...)
char temp[16];//温度
char udate[32];//更新时间
};
HTTPClient http;
String GetUrl;
String response;
WeatherData weatherData;
struct config_type
{
char stassid[32];
char stapsw[64];
};
config_type config;
bool flag = 0; //读取EEPROM联网,是否成功标志位
uint8_t WifiEEPROM_Buff[50] = {0};
uint8_t Time_val = 0;
/*
保存参数到EEPROM
*/
void saveConfig()
{
Serial.println("Save config!");
Serial.print("stassid:");
Serial.println(config.stassid);
Serial.print("stapsw:");
Serial.println(config.stapsw);
EEPROM.begin(1024);
uint8_t *p = (uint8_t*)(&config);
for (int i = 0; i < sizeof(config); i++)
{
EEPROM.write(i, *(p + i));
}
EEPROM.commit();
}
/*
从EEPROM加载参数
*/
void loadConfig()
{
EEPROM.begin(1024);
uint8_t *p = (uint8_t*)(&config);
for (int i = 0; i < sizeof(config); i++)
{
*(p + i) = EEPROM.read(i);
}
EEPROM.commit();
Serial.println("-----Read config-----");
Serial.print("stassid:");
Serial.println(config.stassid);
Serial.print("stapsw:");
Serial.println(config.stapsw);
}
void Connect_STA(const char *wifiname, const char *psw)
{
WiFi.begin(wifiname, psw);
//判断网络状态是否连接上,没连接上就延时500ms,并且打出一个点,模拟连接过程
//笔者扩展:加入网络一直都连不上 是否可以做个判断,由你们自己实现
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Time_val++;
DebugPrint(".");
DebugPrint(WiFi.status());
if (Time_val > 20)
{
Time_val = 0;
flag = 0;
return;
}
}
if (WiFi.status() == WL_CONNECTED)
{
flag = 1;
//输出mac地址
DebugPrintln(String("Connected, mac address: ") + WiFi.macAddress().c_str());
//输出station IP地址,这里的IP地址由DHCP分配
DebugPrintln(String("Connected, IP address: ") + WiFi.localIP().toString());
//输出子网掩码地址
DebugPrintln(String("Subnet mask: ") + WiFi.subnetMask().toString());
//输出网关 IP地址
DebugPrintln(String("Gataway IP: ") + WiFi.gatewayIP().toString());
//输出hostname
DebugPrintln(String("Default hostname: ") + WiFi.hostname());
//设置新的hostname
WiFi.hostname("Station_host_1");
DebugPrintln(String("New hostname: ") + WiFi.hostname());
//输出SSID
DebugPrintln(String("SSID: ") + WiFi.SSID());
//输出psk
DebugPrintln(String("psk: ") + WiFi.psk());
//输出BSSID
DebugPrintln(String("BSSID: ") + WiFi.BSSIDstr());
//输出RSSI
DebugPrintln(String("RSSI: ") + WiFi.RSSI() + " dBm");
}
}
void smartConfig()
{
WiFi.mode(WIFI_STA);
Serial.println("\r\nWait for Smartconfig");
delay(2000);
// 等待配网
WiFi.beginSmartConfig();
while (1)
{
Serial.print(".");
delay(500);
Time_val++;
if (Time_val > 20)
{
Time_val = 0;
flag = 0;
break;
}
if (WiFi.smartConfigDone())
{
Serial.println("SmartConfig Success");
Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
WiFi.setAutoConnect(true); // 设置自动连接
strcpy(config.stassid, WiFi.SSID().c_str());
strcpy(config.stapsw, WiFi.psk().c_str());
saveConfig();
break;
}
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP().toString());
}
void setup() {
// put your setup code here, to run once:
pinMode(LED, OUTPUT); //初始化led io
digitalWrite(LED, LOW); //配置io输出为低电平
pinMode(D5, OUTPUT); // SSD1306 SDA
pinMode(D6, OUTPUT); // SSD1306 SCL
digitalWrite(D5, 0);
digitalWrite(D6, 0);
DebugBegin(BAUD_RATE); //配置串口
loadConfig(); //读取账号和密码
Connect_STA(config.stassid, config.stapsw);
if (!flag)
{
DebugPrint("****************上次没有配网成功,执行一键配网功能*************************" );
DebugPrint("/n");
smartConfig();
}
u8g2.begin(); // 显示屏初始化配置
u8g2.enableUTF8Print(); // 使print支持UTF8字集
//https://api.seniverse.com/v3/weather/now.json?key=SHnyEIl0fFSGhteeZ&location=beijing&language=zh-Hans&unit=c
//拼接get请求url 博哥后面考虑看看是否可以封装一个方法来用用 不需要自己一个个拼装这个url
GetUrl = String(HOST) + "/v3/weather/now.json?key=";
GetUrl += APIKEY;
GetUrl += "&location=";
GetUrl += CITY;
GetUrl += "&language=";
GetUrl += LANGUAGE;
//设置超时
http.setTimeout(HTTP_TIMEOUT);
//设置请求url
http.begin(GetUrl);
//以下为设置一些头 其实没什么用 最重要是后端服务器支持
http.setUserAgent("esp8266");//用户代理版本
http.setAuthorization("esp8266", "boge"); //用户校验信息
}
void loop() {
// put your main code here, to run repeatedly:
ESP.wdtFeed(); //喂狗
if (millis() - lastDebounceTime > debouncdDelay)
{
lastDebounceTime = millis(); //重新赋值上一次时间
//心知天气 发送http get请求
int httpCode = http.GET();
if (httpCode > 0) {
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
//判断请求是否成功
if (httpCode == HTTP_CODE_OK) {
//读取响应内容
response = http.getString();
DebugPrintln("Get the data from Internet!");
DebugPrintln(response);
//解析响应内容
if (parseUserData(response, &weatherData)) {
//打印响应内容
printUserData(&weatherData);
}
}
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_5x8_tf);
u8g2.setFontDirection(0);
u8g2.drawStr(0, 8, " zhengzhou:");
u8g2.drawStr(0, 18, " AP_PSW:");
} while ( u8g2.nextPage() );
digitalWrite(D9, !digitalRead(D9)); //配置io输出为 读取当前IO口状态的反向值
}
}
/**
@Desc 解析数据 Json解析
数据格式如下:
{
"results": [
{
"location": {
"id": "WX4FBXXFKE4F",
"name": "北京",
"country": "CN",
"path": "北京,北京,中国",
"timezone": "Asia/Shanghai",
"timezone_offset": "+08:00"
},
"now": {
"text": "多云",
"code": "4",
"temperature": "23"
},
"last_update": "2017-09-13T09:51:00+08:00"
}
]
}
*/
bool parseUserData(String content, struct WeatherData* weatherData) {
// -- 根据我们需要解析的数据来计算JSON缓冲区最佳大小
// 如果你使用StaticJsonBuffer时才需要
// const size_t BUFFER_SIZE = 1024;
// 在堆栈上分配一个临时内存池
// StaticJsonBuffer<BUFFER_SIZE> jsonBuffer;
// -- 如果堆栈的内存池太大,使用 DynamicJsonBuffer jsonBuffer 代替
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.parseObject(content);
if (!root.success()) {
DebugPrintln("JSON parsing failed!");
return false;
}
//复制我们感兴趣的字符串
strcpy(weatherData->city, root["results"][0]["location"]["name"]);
strcpy(weatherData->weather, root["results"][0]["now"]["text"]);
strcpy(weatherData->temp, root["results"][0]["now"]["temperature"]);
strcpy(weatherData->udate, root["results"][0]["last_update"]);
// -- 这不是强制复制,你可以使用指针,因为他们是指向“内容”缓冲区内,所以你需要确保
// 当你读取字符串时它仍在内存中
return true;
}
// 打印从JSON中提取的数据
void printUserData(const struct WeatherData* weatherData) {
DebugPrintln("Print parsed data :");
DebugPrint("City : ");
DebugPrint(weatherData->city);
DebugPrint(", \t");
DebugPrint("Weather : ");
DebugPrint(weatherData->weather);
DebugPrint(",\t");
DebugPrint("Temp : ");
DebugPrint(weatherData->temp);
DebugPrint(" C");
DebugPrint(",\t");
DebugPrint("Last Updata : ");
DebugPrint(weatherData->udate);
DebugPrintln("\r\n");
}
Arduino-ESP8266-HttpClient
最新推荐文章于 2025-03-02 11:49:17 发布