Arduino-ESP8266-HttpClient

/**********************************************************************/
/* 学习例程:  关于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");
}
  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值