WIFI模块(ESP8266)学习5


一、局域网应用—炫酷RGB

相关技术点:

  • ArduinoJson库
  • TCP Server服务
  • STA模式
  • 一键配网

内容:

手机、ESP8266均连接在同一个路由器wifi,8266作为服务端,手机作为客户端,手机再通过wifi给8266发送数据,8266接收到数据再把数据发给Arduino(串口通信),Arduino解析数据达到控制效果

实现:

APP源码地址

8266端代码:

/**
* 功能:wifi RGB 8266端
*       加入SmartConfig功能
**/
#include <ESP8266WiFi.h>

#define MAX_SRV_CLIENTS 3   //最大同时联接数,即你想要接入的设备数量,8266tcpserver只能接入五个
#define LED 2
  
WiFiServer server(8266);    //你要的端口号,随意修改,范围0-65535
WiFiClient serverClients[MAX_SRV_CLIENTS];
int flag = HIGH;//默认当前灭灯
  
void setup() {
  Serial.begin(115200);
  pinMode(LED,OUTPUT);
  digitalWrite(LED, HIGH);

  if(!autoConfig()){              //连接WIFI,先是自动连接,超过20秒自动连接失败,开始smartConfig()
    smartConfig();
    Serial.print("Connecting to WiFi");
    while (WiFi.status() != WL_CONNECTED) {
       delay(500);
       Serial.print(".");
    }
  }
  
  delay(1000);
  digitalWrite(LED, LOW);
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());      //WiFi.localIP()返回8266获得的ip地址
  server.begin();
  server.setNoDelay(true);  //加上后才正常些
  //使能软件看门狗的触发间隔
  ESP.wdtEnable(5000);
}
  
void loop() {
  uint8_t index;
  if (server.hasClient()){
        for (index = 0; index < MAX_SRV_CLIENTS; index++){
            if (!serverClients[index] || !serverClients[index].connected()){
                if (serverClients[index]) serverClients[index].stop();//未联接,就释放
                serverClients[index] = server.available();//分配新的
                continue;
            }
        }
        //8266tcpserver只能接入五个  超出的需要释放
        WiFiClient serverClient = server.available();
        if (serverClient){
          serverClient.stop();
        }
  }
  
  for (index = 0; index < MAX_SRV_CLIENTS; index++){
        if (serverClients[index] && serverClients[index].connected()){
            //处理客户端发过来的数据
            if (serverClients[index].available()){
                while (serverClients[index].available()) 
                    //把数据发送给mega
                    Serial.write(serverClients[index].read());
            }
        }
   }

   if(Serial.available()>0){
      char ch = Serial.read();
      //收到ardunio发过来的进入smartconfig模式的命令
      if(ch == '1'){
        smartConfig();
        delay(1000);
        digitalWrite(LED, LOW);
        Serial.println("IP address: ");
        Serial.println(WiFi.localIP());//WiFi.localIP()返回8266获得的ip地址
      }
   }

   //喂狗
   ESP.wdtFeed();
}

/**
* 自动连接20s 超过之后自动进入SmartConfig模式
*/
bool autoConfig(){
  WiFi.mode(WIFI_AP_STA);     //设置esp8266 工作模式
  WiFi.begin();
  delay(2000);                //刚启动模块的话 延时稳定一下
  Serial.println("AutoConfiging ......");
  for(int index=0;index<20;index++){
    int wstatus = WiFi.status();
    if (wstatus == WL_CONNECTED){
      Serial.println("AutoConfig Success");
      Serial.print("SSID:");
      Serial.println(WiFi.SSID().c_str());
      Serial.print("PSW:");
      Serial.println(WiFi.psk().c_str());
      return true;
    }else{
      Serial.print(".");
      delay(1000);
      flag = !flag;
      digitalWrite(LED, flag);
    } 
  }
  Serial.println("AutoConfig Faild!");
  return false;
}

/**
* 开启SmartConfig功能
*/
void smartConfig()
{
  WiFi.mode(WIFI_STA);
  delay(2000);
  Serial.println("Wait for Smartconfig");// 等待配网
  WiFi.beginSmartConfig();
  while (1){
    Serial.print(".");
    delay(500);
    flag = !flag;
    digitalWrite(LED, flag);
    
    if (WiFi.smartConfigDone()){
      //smartconfig配置完毕
      Serial.println("SmartConfig Success");
      Serial.print("SSID:");
      Serial.println(WiFi.SSID().c_str());
      Serial.print("PSW:");
      Serial.println(WiFi.psk().c_str());
      WiFi.mode(WIFI_AP_STA);     //设置esp8266 工作模式
      WiFi.setAutoConnect(true);  // 设置自动连接
      break;
    }
  }
}

Arduino端代码:(接收8266信息,JSON解析)

/**
* 功能:wifi lamp arduino端
**/
#include <SoftwareSerial.h>
#include <ArduinoJson.h>
  
const unsigned long BAUD_RATE = 115200;                   // serial connection speed
const size_t MAX_CONTENT_SIZE = 50; 
const size_t t_bright=1,t_color=2,t_frequency=3,t_switch=4;
 
//#define UNO      //uncomment this line when you use it with UNO board
#define MEGA    //uncomment this line when you use it with MEGA board
 
#ifdef UNO
 SoftwareSerial mySerial(10,11);
#endif
  
#ifdef UNO
#define WifiSerial  Serial
#define MyDebugSerial mySerial
#endif
   
#ifdef MEGA
#define WifiSerial Serial1
#define MyDebugSerial Serial
#endif  
 
//该条语句用于使能DEBUG输出信息,屏蔽掉就不会输出debug调试信息
#define DEBUG
//该条语句用于使能是共阴RGB  屏蔽掉就是共阳RGB
//#define COMMON_GND
 
#ifdef DEBUG
#define DBGLN(message)    MyDebugSerial.println(message)
#else
#define DBGLN(message)
#endif
 
#ifdef UNO 
#define PIN_RED 3 //red 引脚
#define PIN_GREEN 5 //green 引脚
#define PIN_BLUE 6 //blue 引脚
#define PIN_ENABLE 9  //使能引脚 pwm控制亮度
#define PIN_KEY 7// 按键
#else
#define PIN_RED 2
#define PIN_GREEN 3
#define PIN_BLUE 4
#define PIN_ENABLE 5
#define PIN_KEY 6  
#endif 
 
int red = 0,green = 0,blue = 0;
int type = 4;//当前模式 1亮度 2颜色 3呼吸 4开关
int frequency = 1;//频率
int switch_status = 1;//关闭 or 开启
int bright = 1;//亮度
 
char response[MAX_CONTENT_SIZE];
int fadeValue = 0;//当前亮度
bool isAdd = true;//是否是从暗到亮
 
// 定义记录按键当前状态的变量
int state_btn;
// 定义记录按键最近一次状态变化的变量,并初始化状态为LOW。
int lastButtonState = LOW;
// 定义记录最近一次抖动的时间变量,并初始化时间为0毫秒。
long lastDebounceTime = 0;
// 定义延迟抖动的时间变量
long debouncdDelay = 60;
  
/**
* @Desc 初始化操作
*/
void setup() {
  pinMode(PIN_RED, OUTPUT);
  pinMode(PIN_GREEN, OUTPUT);
  pinMode(PIN_BLUE, OUTPUT);
  pinMode(PIN_ENABLE, OUTPUT);
  pinMode(PIN_KEY,INPUT);
  
  WifiSerial.begin(BAUD_RATE);
  #ifdef DEBUG
    #ifdef UNO
      MyDebugSerial.begin(9600);//软串口9600稳定
    #else
      MyDebugSerial.begin(BAUD_RATE);
    #endif
  #endif
  DBGLN("Arduino Init End");
}
  
/**
* @Desc  主函数
*/
void loop() {
 
  if(WifiSerial.available()>0){
    clrEsp8266ResponseBuffer();
    int data_size = ReceiveMessage(response, sizeof(response));
    if(data_size>0){
      //开始解析数据
      parseData(response);
    }
  }
 
  if(type == t_frequency){
    //呼吸灯效果
    breatheRGB(frequency);
  }
  checkButton();
}
 
/**
* 读取串口缓冲区里面的数据
*/
int ReceiveMessage(char* content, size_t maxSize){
  //不用 readBytes 因为比较耗时
  size_t length = WifiSerial.readBytesUntil('}',content, maxSize);
  content[length] = '}';
  content[++length] = 0;
  DBGLN(content);
  return length;
}
 
/**
     * @Desc 解析json
     * 有三种
     * 1.亮度控制页面(0 暗 1正常 2亮)
     * {
     *     "t": 1,
     *     "bb": 2
     * }
     * 2.颜色控制页面
     * {
     *     "t": 2,
     *     "cr": 154,
     *     "cg": 147,
     *     "cb": 255
     * }
     * 3.呼吸灯控制页面(0 慢呼吸 1正常 2快)
     * {
     *    "t": 3,
     *    "gf": 1
     * }
     * 4.开关控制(0关闭 1开启)
     * {
     *    "t": 4,
     *    "ss": 1
     * } 
     **/
bool parseData(char* content) {
//    -- 根据我们需要解析的数据来计算JSON缓冲区最佳大小
//   如果你使用StaticJsonBuffer时才需要
//    const size_t BUFFER_SIZE = 1024;
//   在堆栈上分配一个临时内存池
//    StaticJsonBuffer<BUFFER_SIZE> jsonBuffer;
//    -- 如果堆栈的内存池太大,使用 DynamicJsonBuffer jsonBuffer 代替
  DynamicJsonBuffer jsonBuffer;
   
  JsonObject& root = jsonBuffer.parseObject(content);
   
  if (!root.success()) {
    Serial.println("JSON parsing failed!");
    return false;
  }
    
  type = root["t"];
  switch(type){
    case t_bright:
         bright = root["bb"];
         brightRGB(bright);
         break;
    case t_color:
         red = root["cr"];
         green = root["cg"];
         blue = root["cb"];
         colorRGB(red,green,blue);
         break;
    case t_frequency:
         frequency = root["gf"];
         break;
    case t_switch:
         switch_status = root["ss"];
         bool enable = switch_status == 1;
         switchRGB(enable);
         break;     
  }
  return true;
}
 
/**
* 控制灯亮度
*/
void brightRGB(int bright){
  int level = bright%3;
  int bright_level;
  switch(level){
    case 0://暗  50
      bright_level = 50;
      break;
    case 1://正常 100
      bright_level = 100;
      break;
    case 2://亮  200
      bright_level = 200;
      break;
  }
    #ifdef COMMON_GND
     //共地
     analogWrite(PIN_ENABLE,bright_level);
    #else
     analogWrite(PIN_ENABLE,255-bright_level);
    #endif
}
 
/**
* 控制RGB颜色
*/
void colorRGB(int red, int green, int blue){
  #ifdef COMMON_GND
     analogWrite(PIN_RED,constrain(red,0,255));
     analogWrite(PIN_GREEN,constrain(green,0,255));
     analogWrite(PIN_BLUE,constrain(blue,0,255));
  #else
     analogWrite(PIN_RED,constrain(255-red,0,255));
     analogWrite(PIN_GREEN,constrain(255-green,0,255));
     analogWrite(PIN_BLUE,constrain(255-blue,0,255));
  #endif
}
 
/**
* 控制亮灭
*/
void switchRGB(bool enable){
  if(enable){
    //打开
    #ifdef COMMON_GND
     //共地
     analogWrite(PIN_ENABLE,255);
    #else
     analogWrite(PIN_ENABLE,0);
    #endif
  }else{
    //关闭
    #ifdef COMMON_GND
     //共地
     analogWrite(PIN_ENABLE,0);
    #else
     analogWrite(PIN_ENABLE,255);
    #endif
  }
}
 
/**
* 呼吸灯
*/
void breatheRGB(int frequency){
  int level = frequency%3;
  int f_level;
  switch(level){
    case 0://慢  50
      f_level = 3;
      break;
    case 1://正常 100
      f_level = 10;
      break;
    case 2://快  200
      f_level = 20;
      break;
  }
  if(isAdd){
    //递增方向
     fadeValue +=f_level;
     if(fadeValue>=255){
       fadeValue = 255;
       isAdd =false;
     }
  }else{
    //递减方向
    fadeValue -=f_level;
     if(fadeValue<=0){
       fadeValue = 0;
       isAdd =true;
     }
  }
  analogWrite(PIN_ENABLE,fadeValue);
  delay(20);
}
 
/**
* 检查按键功能
*/
void checkButton(){
  int buttonState = digitalRead(PIN_KEY);//读取当前按键状态
  if(buttonState != lastButtonState){
     //如果按键发生了变化  则重新设置最近一次抖动的时间
     //方法millis()可以获取当前时间,单位统一为毫秒。
     lastDebounceTime = millis();  
  }
   
  // 判断按键按下状态时间间隔是否大于延迟抖动的时间长度。
  if(millis()-lastDebounceTime>debouncdDelay){
    // 判断当前的按键状态是否和之前有所变化
    if(buttonState != state_btn){
       // 如果发生了变化,
       // 则更新按键状态变量。
       state_btn = buttonState;
       if(state_btn == HIGH){
        //再次确认是否真的按下了按键
         DBGLN("smartconfig");
         WifiSerial.write('1');
       }
    }
  }
  // 更新按键最近一次状态变化的变量
  lastButtonState = buttonState;
}
 
void clrEsp8266ResponseBuffer(void){
    memset(response, 0, MAX_CONTENT_SIZE);      //清空
}

二、OLED显示天气屏

相关技术点:

  • ArduinoJson V5库
  • TCP Client
  • STA模式
  • 一键配网
  • U8G2 OLED库

内容:

向心知天气请求当地城市天气情况,并在OLED上显示天气图标、温度值以及城市名称

心知天气参考wiki

天气编码wiki

/**
* 功能:OLED显示天气屏
**/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <U8g2lib.h>

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

#define LED D4
#define DEBUG //是否开启debug功能

#ifdef DEBUG
#define DebugPrintln(message)    Serial.println(message)
#else
#define DebugPrintln(message)
#endif

#ifdef DEBUG
#define DebugPrint(message)    Serial.print(message)
#else
#define DebugPrint(message)
#endif

#define WEATHER_CODE_DAY_SUN "0" //晴(国内城市白天晴)
#define WEATHER_CODE_NIGHT_SUN "1" //晴(国内城市夜晚晴)
#define WEATHER_CODE_DAY_SUN1 "2" //晴(国外城市白天晴)
#define WEATHER_CODE_NIGHT_SUN2 "3" //晴(国外城市夜晚晴)
#define WEATHER_CODE_CLOUDY "4" //多云
#define WEATHER_CODE_DAY_PARTLY_CLOUDY "5" //白天晴间多云
#define WEATHER_CODE_NIGHT_PARTLY_CLOUDY "6" //夜晚晴间多云
#define WEATHER_CODE_DAY_MOSTLY_CLOUDY "7" //白天大部多云
#define WEATHER_CODE_NIGHT_MOSTLY_CLOUDY "8" //夜晚大部多云
#define WEATHER_CODE_OVERCAST "9" //阴
#define WEATHER_CODE_SHOWER "10" //阵雨
#define WEATHER_CODE_THUNDERSHOWER "11" //雷阵雨
#define WEATHER_CODE_THUNDERSHOWER_WITH_HAIL "12" //雷阵雨伴有冰雹
#define WEATHER_CODE_LIGHT_RAIN "13" //小雨
#define WEATHER_CODE_MODERATE_RAIN "14" //中雨
#define WEATHER_CODE_HEAVY_RAIN "15" //大雨
#define WEATHER_CODE_STORM "16" //暴雨
#define WEATHER_CODE_HEAVY_STORM "17" //大暴雨
#define WEATHER_CODE_SEVERE_STORM "18" //特大暴雨
#define WEATHER_CODE_ICE_RAIN "19" //冻雨
#define WEATHER_CODE_SLEET "20" //雨夹雪
#define WEATHER_CODE_SNOW_FLURRY "21" //阵雪
#define WEATHER_CODE_LIGHT_SNOW "22" //小雪
#define WEATHER_CODE_MODERATE_SNOW "23" //中雪
#define WEATHER_CODE_HEAVY_SNOW "24" //大雪
#define WEATHER_CODE_SNOW_STORM "25" //暴雪

#define SUN_DAY 0
#define SUN_NIGHT 1
#define SUN_CLOUD 2
#define CLOUD 3
#define RAIN 4
#define THUNDER 5

//声明方法
bool autoConfig();
void smartConfig();
bool sendRequest(const char* host, const char* cityid, const char* apiKey);
bool skipResponseHeaders();
void readReponseContent(char* content, size_t maxSize);
void stopConnect();
void clrEsp8266ResponseBuffer(void);
bool parseUserData(char* content, struct UserData* userData);
void drawWeatherSymbol(u8g2_uint_t x, u8g2_uint_t y, uint8_t symbol);
void drawWeather(uint8_t symbol, int degree);
  
const unsigned long BAUD_RATE = 115200;// serial connection speed
const unsigned long HTTP_TIMEOUT = 5000;               // max respone time from server
const size_t MAX_CONTENT_SIZE = 500;                   // max size of the HTTP response
const char* host = "api.seniverse.com";
const char* APIKEY = "wcmquevztdy1jpca";        //API KEY
const char* city = "guangzhou";
const char* language = "zh-Hans";//zh-Hans 简体中文  会显示乱码

int flag = HIGH;//默认当前灭灯
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

WiFiClient client;
char response[MAX_CONTENT_SIZE];
char endOfHeaders[] = "\r\n\r\n";

long lastTime = 0;
// 请求服务间隔
long Delay = 20000;

// 我们要从此网页中提取的数据的类型
struct UserData {
  char city[16];//城市名称
  char weather_code[4];//天气现象code(多云...)
  char temp[5];//温度
};

/**
* @Desc 初始化操作
*/
void setup() {
  Serial.begin(BAUD_RATE);
  pinMode(LED,OUTPUT);
  digitalWrite(LED, HIGH);

  WiFi.disconnect();
  if(!autoConfig()){
    smartConfig();
    DebugPrint("Connecting to WiFi");//写几句提示,哈哈
    while (WiFi.status() != WL_CONNECTED) {
    //这个函数是wifi连接状态,返回wifi链接状态
       delay(500);
       DebugPrint(".");
    }
  }
  
  delay(1000);
  digitalWrite(LED, LOW);
  DebugPrintln("IP address: ");
  DebugPrintln(WiFi.localIP());//WiFi.localIP()返回8266获得的ip地址
  lastTime = millis();
  u8g2.begin();  
  u8g2.enableUTF8Print();
  //使能软件看门狗的触发间隔
  ESP.wdtEnable(5000);
}
  
/**
* @Desc  主函数
*/
void loop() {
  while (!client.connected()){
     if (!client.connect(host, 80)){
         flag = !flag;
         digitalWrite(LED, flag);
         delay(500);
         //喂狗
         ESP.wdtFeed();
     }
  }

  if(millis()-lastTime>=Delay){
   //每间隔20s左右调用一次
     lastTime = millis();
     if (sendRequest(host, city, APIKEY) && skipResponseHeaders()) {
       clrEsp8266ResponseBuffer();
       readReponseContent(response, sizeof(response));
       UserData userData;
       if (parseUserData(response, &userData)) {
          showWeather(&userData);
       }
     }
  }
  
   //喂狗
   ESP.wdtFeed();
}

/**
* 自动连接20s 超过之后自动进入SmartConfig模式
*/
bool autoConfig(){
  WiFi.mode(WIFI_AP_STA);     //设置esp8266 工作模式
  WiFi.begin();
  delay(2000);//刚启动模块的话 延时稳定一下
  DebugPrintln("AutoConfiging ......");
  for(int index=0;index<10;index++){
    int wstatus = WiFi.status();
    if (wstatus == WL_CONNECTED){
      DebugPrintln("AutoConfig Success");
      DebugPrint("SSID:");
      DebugPrintln(WiFi.SSID().c_str());
      DebugPrint("PSW:");
      DebugPrintln(WiFi.psk().c_str());
      return true;
    }else{
      DebugPrint(".");
      delay(500);
      flag = !flag;
      digitalWrite(LED, flag);
    } 
  }
  DebugPrintln("AutoConfig Faild!");
  return false;
}

/**
* 开启SmartConfig功能
*/
void smartConfig()
{
  WiFi.mode(WIFI_STA);
  delay(1000);
  DebugPrintln("Wait for Smartconfig");
  // 等待配网
  WiFi.beginSmartConfig();
  while (1){
    DebugPrint(".");
    delay(200);
    flag = !flag;
    digitalWrite(LED, flag);
    
    if (WiFi.smartConfigDone()){
      //smartconfig配置完毕
      DebugPrintln("SmartConfig Success");
      DebugPrint("SSID:");
      DebugPrintln(WiFi.SSID().c_str());
      DebugPrint("PSW:");
      DebugPrintln(WiFi.psk().c_str());
      WiFi.mode(WIFI_AP_STA);     //设置esp8266 工作模式
      WiFi.setAutoConnect(true);  // 设置自动连接
      break;
    }
  }
}

/**
* @发送请求指令
*/
bool sendRequest(const char* host, const char* cityid, const char* apiKey) {
  // We now create a URI for the request
  //心知天气
  String GetUrl = "/v3/weather/now.json?key=";
  GetUrl += apiKey;
  GetUrl += "&location=";
  GetUrl += city;
  GetUrl += "&language=";
  GetUrl += language;
  // This will send the request to the server
  client.print(String("GET ") + GetUrl + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
  DebugPrintln("create a request:");
  DebugPrintln(String("GET ") + GetUrl + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n");
  delay(1000);
  return true;
}
 
/**
* @Desc 跳过 HTTP 头,使我们在响应正文的开头
*/
bool skipResponseHeaders() {
  // HTTP headers end with an empty line
  bool ok = client.find(endOfHeaders);
  if (!ok) {
    DebugPrintln("No response or invalid response!");
  }
  return ok;
}
 
/**
* @Desc 从HTTP服务器响应中读取正文
*/
void readReponseContent(char* content, size_t maxSize) {
  size_t length = client.readBytes(content, maxSize);
  delay(100);
  DebugPrintln("Get the data from Internet!");
  content[length] = 0;
  DebugPrintln(content);
  DebugPrintln("Read data Over!");
  client.flush();//这句代码需要加上  不然会发现每隔一次client.find会失败
}
  
// 关闭与HTTP服务器连接
void stopConnect() {
  client.stop();
}
 
void clrEsp8266ResponseBuffer(void){
    memset(response, 0, MAX_CONTENT_SIZE);      //清空
}

bool parseUserData(char* content, struct UserData* userData) {
//    -- 根据我们需要解析的数据来计算JSON缓冲区最佳大小
//   如果你使用StaticJsonBuffer时才需要
//    const size_t BUFFER_SIZE = 1024;
//   在堆栈上分配一个临时内存池
//    StaticJsonBuffer<BUFFER_SIZE> jsonBuffer;
//    -- 如果堆栈的内存池太大,使用 DynamicJsonBuffer jsonBuffer 代替
  DynamicJsonBuffer jsonBuffer;
  
  JsonObject& root = jsonBuffer.parseObject(content);
  
  if (!root.success()) {
    Serial.println("JSON parsing failed!");
    return false;
  }
   
  //复制我们感兴趣的字符串
  strcpy(userData->city, root["results"][0]["location"]["name"]);
  strcpy(userData->weather_code, root["results"][0]["now"]["code"]);
  strcpy(userData->temp, root["results"][0]["now"]["temperature"]);
  //  -- 这不是强制复制,你可以使用指针,因为他们是指向“内容”缓冲区内,所以你需要确保
  //   当你读取字符串时它仍在内存中
  return true;
}

/**
 * 根据天气接口返回的数据判断显示
 */
void showWeather(struct UserData* userData){
  if(strcmp(userData->weather_code,WEATHER_CODE_DAY_SUN) == 0 
  || strcmp(userData->weather_code,WEATHER_CODE_DAY_SUN1) == 0){
    drawWeather(SUN_DAY,userData->temp,userData->city);
  }else if(strcmp(userData->weather_code,WEATHER_CODE_NIGHT_SUN) == 0 
  || strcmp(userData->weather_code,WEATHER_CODE_NIGHT_SUN2) == 0 ){
    drawWeather(SUN_NIGHT,userData->temp,userData->city);
  }else if(strcmp(userData->weather_code,WEATHER_CODE_DAY_PARTLY_CLOUDY) == 0 
  || strcmp(userData->weather_code,WEATHER_CODE_NIGHT_PARTLY_CLOUDY) == 0 ){
    drawWeather(SUN_CLOUD,userData->temp,userData->city);
  }else if(strcmp(userData->weather_code,WEATHER_CODE_CLOUDY) == 0 
  || strcmp(userData->weather_code,WEATHER_CODE_DAY_MOSTLY_CLOUDY) == 0 
  || strcmp(userData->weather_code,WEATHER_CODE_NIGHT_MOSTLY_CLOUDY) == 0 
  || strcmp(userData->weather_code,WEATHER_CODE_OVERCAST) == 0){
    drawWeather(CLOUD,userData->temp,userData->city); 
  }else if(strcmp(userData->weather_code,WEATHER_CODE_SHOWER) == 0 
  || strcmp(userData->weather_code,WEATHER_CODE_LIGHT_RAIN) == 0 
  || strcmp(userData->weather_code,WEATHER_CODE_MODERATE_RAIN) == 0 
  || strcmp(userData->weather_code,WEATHER_CODE_HEAVY_RAIN) == 0 
  || strcmp(userData->weather_code,WEATHER_CODE_STORM) == 0
  || strcmp(userData->weather_code,WEATHER_CODE_HEAVY_STORM) == 0 
  || strcmp(userData->weather_code,WEATHER_CODE_SEVERE_STORM) == 0){
    drawWeather(RAIN,userData->temp,userData->city);
  }else if(strcmp(userData->weather_code,WEATHER_CODE_THUNDERSHOWER) == 0 
  || strcmp(userData->weather_code,WEATHER_CODE_THUNDERSHOWER_WITH_HAIL) == 0){
    drawWeather(THUNDER,userData->temp,userData->city);
  }else{
    drawWeather(CLOUD,userData->temp,userData->city); 
  }
}

void drawWeather(uint8_t symbol, char* degree,char* city)
{
  DebugPrintln(city);
  u8g2.clearBuffer();         // clear the internal memory
  //绘制天气符号
  drawWeatherSymbol(0, 48, symbol);
  //绘制温度
  u8g2.setFont(u8g2_font_logisoso32_tf);
  u8g2.setCursor(48+3, 42);
  u8g2.print(degree);
  u8g2.print("°C");   // requires enableUTF8Print()
  u8g2.setFont(u8g2_font_unifont_t_chinese3);

  u8g2_uint_t strWidth = u8g2.getUTF8Width(city);
  u8g2_uint_t displayWidth = u8g2.getDisplayWidth();
  
  u8g2.setCursor(displayWidth - strWidth - 5, 60);
  u8g2.print(city);
  u8g2.sendBuffer();        // transfer internal memory to the display
}

/**
 * 绘制天气符号
 */
void drawWeatherSymbol(u8g2_uint_t x, u8g2_uint_t y, uint8_t symbol)
{
  // fonts used:
  // u8g2_font_open_iconic_embedded_6x_t
  // u8g2_font_open_iconic_weather_6x_t
  // encoding values, see: https://github.com/olikraus/u8g2/wiki/fntgrpiconic
  switch(symbol)
  {
    case SUN_DAY://太阳
      u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
      u8g2.drawGlyph(x, y, 69); 
      break;
    case SUN_NIGHT://太阳
      u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
      u8g2.drawGlyph(x, y, 66); 
      break;  
    case SUN_CLOUD://晴间多云
      u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
      u8g2.drawGlyph(x, y, 65); 
      break;
    case CLOUD://多云
      u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
      u8g2.drawGlyph(x, y, 64); 
      break;
    case RAIN://下雨
      u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
      u8g2.drawGlyph(x, y, 67); 
      break;
    case THUNDER://打雷
      u8g2.setFont(u8g2_font_open_iconic_embedded_6x_t);
      u8g2.drawGlyph(x, y, 67);
      break;      
  }
}

三、WIFI小车

相关技术点:

WebSocket Client 全双工通信

WebSocket Server 全双工通信

内容:

  • NodeMcu作为AP。手机控制端作为STA,连接AP
  • NodeMcu作为WebSocketServer端,手机作为WebSocketClient端
  • 两者建立WS通信,手机往NodeMcu发送控制命令
  • NodeMcu控制电机

实现:

/*
 *功能:wifi小车 ESP8266
 */

#include <ESP8266WiFi.h>
#include <WebSocketsServer.h>
#include <ESP8266WebServer.h> 
#include <ArduinoJson.h>

#define AP_SSID "WiFiCar" //这里改成你的AP名字
#define AP_PSW  "12345678" //这里改成你的AP密码 8位以上

#define IN1 D1 // 7 6 右轮
#define IN2 D2 
#define IN3 D3 // 5 4 左轮
#define IN4 D4

#define LEFT "3" //左转编码
#define RIGHT "4"//右转编码
#define GO "1"//前进编码
#define BACK "2"//后退编码
#define STOP "0"//停止编码

#define USE_SERIAL Serial

IPAddress local_IP(192,168,4,25);
IPAddress gateway(192,168,4,1);
IPAddress subnet(255,255,255,0);

WebSocketsServer webSocket = WebSocketsServer(81);

void initSys();
void initAP();
void initWS();
void initCar();
void stopCar();
void turnLeft();
void turnRight();
void go();
void back();
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length);

void setup() {
  // put your setup code here, to run once:
  initSys();
  initAP();
  initWS();
  initCar();
}

void loop() {
  // put your main code here, to run repeatedly:
  webSocket.loop();
  //喂狗
  ESP.wdtFeed();
}

void initSys(){
  USE_SERIAL.begin(115200);
  USE_SERIAL.println();
}

void initAP(){
  //配置AP信息
  WiFi.mode(WIFI_AP);
  WiFi.softAPConfig(local_IP, gateway, subnet);
  boolean result = WiFi.softAP(AP_SSID, AP_PSW);
  if(!result){
     ESP.restart();
  }
}

void initWS(){
   // start webSocket server
   webSocket.begin();
   webSocket.onEvent(webSocketEvent);
}

void initCar(){
  pinMode(IN1,OUTPUT);
  pinMode(IN2,OUTPUT);
  pinMode(IN3,OUTPUT);
  pinMode(IN4,OUTPUT);  
}

void stopCar(){
  //默认全是低电平 停止状态
  digitalWrite(IN1,LOW);      
  digitalWrite(IN2,LOW);
  digitalWrite(IN3,LOW);   
  digitalWrite(IN4,LOW);  
}

/**
* 左转
*/
void turnLeft(){
  digitalWrite(IN1,HIGH);      
  digitalWrite(IN2,LOW);         //右轮前进
  digitalWrite(IN3,LOW);      
  digitalWrite(IN4,LOW);         //左轮不动
}

/**
* 右转
*/
void turnRight(){
  digitalWrite(IN1,LOW);      
  digitalWrite(IN2,LOW);         //右轮不动
  digitalWrite(IN3,HIGH);      
  digitalWrite(IN4,LOW);         //左轮前进
}

/**
* 前进
*/
void go(){
  digitalWrite(IN1,HIGH);      
  digitalWrite(IN2,LOW);         //右轮前进
  digitalWrite(IN3,HIGH);      
  digitalWrite(IN4,LOW);         //左轮前进
}

/**
* 倒车
*/
void back(){
  digitalWrite(IN1,LOW);      
  digitalWrite(IN2,HIGH);        //右轮后退
  digitalWrite(IN3,LOW);      
  digitalWrite(IN4,HIGH);        //左轮后退
}

void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
    switch(type) {
        case WStype_DISCONNECTED:
            USE_SERIAL.printf("[%u] Disconnected!\n", num);
            break;
        case WStype_CONNECTED: {
            IPAddress ip = webSocket.remoteIP(num);
            USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
            // send message to client
            webSocket.sendTXT(num, "Connected");
        }
            break;
        case WStype_TEXT:
            USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);

            // 缺少控制逻辑

            break;
    }

}

文章作者: 旧时南风
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 旧时南风 !
评论
  目录