一、局域网应用—炫酷RGB
相关技术点:
- ArduinoJson库
- TCP Server服务
- STA模式
- 一键配网
内容:
手机、ESP8266均连接在同一个路由器wifi,8266作为服务端,手机作为客户端,手机再通过wifi给8266发送数据,8266接收到数据再把数据发给Arduino(串口通信),Arduino解析数据达到控制效果
实现:
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;
}
}