之前在测试开发板附带的wifi板的时候,出了状况。迫不得已,改用ESP8266这个已经过时的模块来获得网络时间和所在地的天气状况,虽然失去学习使用CYW43012得机会。使用ESP8266的优点也是显而易见的,使用Arduino开发环境,很多复杂的处理,通过Arduino的支持库,可以更容易方便实现,而且稳定可靠。使用ESP8266的话,和PSoC 6 的通讯,我考虑使用串口实现。在这之前,先弄好ESP8266那边的处理。就是获取网络时间和天气预报信息。网络时间,是通过阿里云服务获取的。而天气情报,是通过心知天气获取的。两者都是通过Web API调用实现的。心知天气,可以自己注册一个账户,获取免费机会。ESP8266端的程序如下:
/**
* 上位机通过串口发送指令给本机,根据指令,本机恢复对应的信息
* 串口使用RX/TX引脚
* 指令:qw+0x0d0a 回复qw=今日的平均气温 + 0x0d0a,形如:tm=21:45:10 + 0x0d0a
* tm+0x0d0a 回复tm=当时的时间 + 0x0d0a,形如:qw=25 + 0x0d0a
*/
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <NTPClient.h>
#include <ESP8266WiFi.h>
#include "WiFiUdp.h"
#include "ESP8266TimerInterrupt.h"
#include <ArduinoJson.h>
#define LOGO_HEIGHT 16
#define LOGO_WIDTH 16
#define BUILTIN_LED 16 // D0 - GPIO16,输出信号
// 接线备注:SCL(GPIO5---D1), SDA(GPIO4---D2)
// OLED分辨率
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
// OLED IIC访问地址
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
const char *ssid = "***";
const char *password = "***";
const char *host = "ip-api.com"; // 访问的域名
String comRecvStr = ""; // 串口收到的数据
bool comRecvCrLfFlag = false; // 串口收到换行符
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP,"ntp1.aliyun.com",60*60*8,30*60*1000);
WiFiClient client; //创建一个网络对象
// 定时中断时间常数
#define TIMER_INTERVAL_MS 1000
volatile bool statusLed = false;
volatile uint32_t lastMillis = 0;
// Init ESP8266 timer 1
ESP8266Timer ITimer;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// 温度信息串
String tempinfo="25";
/*
每当新数据进入硬件串行接收时,就会发生SerialEvent。这
例程在每次loop()运行之间运行,因此在循环内使用delay可以
延迟响应。可能有多个字节的数据可用。
*/
void serialEvent() {
}
// 定时器中断
void IRAM_ATTR TimerHandler() {
}
// 初始化OLED驱动
void init_oled(void) {
Serial.println("<<<init_oled begin");
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
// 初始化IIC OLED12864设备
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
// 设置字体
display.setTextSize(2); // 2倍大小
display.setTextColor(SSD1306_WHITE); // Draw white text
display.cp437(true); // Use full 256 char 'Code Page 437' font
Serial.println(">>>init_oled end");
}
// 定时器中断
void init_interrupt(void) {
if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler)) {
lastMillis = millis();
Serial.print(F("Starting ITimer OK, millis() = ")); Serial.println(lastMillis);
} else {
Serial.println(F("Can't set ITimer correctly. Select another freq. or interval"));
}
}
/**************************************************
* 函数名称:GET_Weather
* 函数功能:http访问获取天气数据
* 参数说明:无
**************************************************/
unsigned long getTime = 0; //获取网络天气和时间
bool getWeather(void) {
String json = ""; //接收到的数据
String url = "https://api.seniverse.com/v3/weather/now.json?key=*****&location=*****&language=zh-Hans&unit=c";
String urlDat = "&start=0&days=3";
if (!client.connect("116.62.81.138", 80)) {
Serial.println("天气服务器连接失败");
return false;
}
// 发送请求报文
client.print(String("GET ") + url + " HTTP/1.1\r\n" + //请求行 请求方法 + 请求地址 + 协议版本
"Host: " + host + "\r\n" + //请求头部
"Connection: close\r\n" + //处理完成后断开连接
"\r\n" + //空行
urlDat); //请求数据
delay(100);
//接收数据
while(client.available()) {
String line = client.readStringUntil('\r');
json += line;
}
client.stop(); //断开与服务器连接以节约资源
// 解析JSON,获取气温信息
StaticJsonDocument<200> doc;
DeserializationError error = deserializeJson(doc, json);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return false;
} else {
Serial.println(json);
Serial.println(doc["results"].as<String>());
Serial.println(doc["results"][0].as<String>());
JsonObject results_0 = doc["results"][0];
JsonObject results_0_location = results_0["location"];
String name = results_0_location["name"].as<String>();
Serial.println(name);
Serial.println(results_0["last_update"].as<String>());
JsonObject results_0_now = results_0["now"];
Serial.println("天气" + results_0_now["text"].as<String>()); // 晴
Serial.println("温度" + results_0_now["temperature"].as<String>() + "度"); // 晴
tempinfo = results_0_now["temperature"].as<String>();
}
// 正常获取天气信息
return true;
}
// 每天接收天气情报的标志,每天6点时重新获取,0点清除
bool weatherFlag = false;
void setup(){
//DeserializationError error;
//StaticJsonDocument<200> doc;
String json = "{"results":[{"location":{"id":"*****","name":"*****","country":"CN","path":"*******","timezone":"Asia/Shanghai","timezone_offset":"+08:00"},"now":{"text":"晴","code":"1","temperature":"26"},"last_update":"2024-07-15T23:07:40+08:00"}]}";
Serial.begin(115200);
WiFi.begin(ssid, password);
while ( WiFi.status() != WL_CONNECTED ) {
delay ( 500 );
Serial.print ( "." );
}
timeClient.begin();
// 初始化OLED驱动
init_oled();
// 清除屏幕
display.clearDisplay();
}
char chr=0;
void loop() {
int old_sec=-1;
int cur_sec=0;
// 刷新时间
timeClient.update();
//Serial.println(timeClient.getFormattedTime());
cur_sec=timeClient.getSeconds();
if (cur_sec != old_sec) {
display.clearDisplay();
// 为了使时间居中显示
display.setTextSize(2);
display.setCursor(0, 0); // 起始坐标
display.write(' ');
display.write(timeClient.getFormattedTime().c_str());
display.display();
old_sec=cur_sec;
}
// 每天6:00:00 ~ 6:00:10之间,获取天气信息
if (weatherFlag==false && timeClient.getHours() == 6 && timeClient.getMinutes() == 0 && timeClient.getSeconds()<10) {
if (getWeather() == true) {
// 成功获得天气信息,设置标志
weatherFlag = true;
}
} else if (timeClient.getHours() == 0 && timeClient.getMinutes() == 0) {
weatherFlag = false;
}
// 检查串口集令,如果收到所要天气情报的指令,将获得的天气信息发出去,晴朗与否,气温
while (Serial.available() > 0) {
chr = char(Serial.read());
if (chr==0x0d) {
Serial.println(comRecvStr);
if (comRecvStr.equalsIgnoreCase("qw")) {
// 上位机索要气温信息
// display.println("\ntq");
// display.display();
// 发送气温信息
Serial.print("qw=");
Serial.print(tempinfo);
Serial.print("\n");
} else if (comRecvStr.equalsIgnoreCase("tm")) {
// 上位机索要时间信息
Serial.print("tm=");
Serial.print(timeClient.getFormattedTime().c_str());
Serial.print("\n");
}
comRecvStr="";
} else {
comRecvStr += chr;
}
}
}复制代码
代码中涉及Key等私人信息的地方用*****代替,要换成天气预报网站给你的Key。具体设置可以参阅网站提供的使用样例。
ESP8266的调试在ArduinoIDE上就可以很方便地实现。按照设想,当PSoC 6 开发板这一侧需要获取时间的时候,向ESP8266模块由串口发出请求指令(tm+回车换行字符),ESP8266端发送Web-API调用,获取时间后,再回传给PSoC 6 开发板。需要获取天气预报信息的时候,PSoC 6 开发板向ESP8266模块由串口发出请求指令(qw+回车换行字符),ESP8266端发送Web-API调用,获取天气后,再回传给PSoC 6 开发板。这个很过程很简单。
在PSoC 6 开发板这一端,为了保证时间的准确性,考虑每隔一分钟发送一个查询当前时间的请求。这个这个请求周期由定时器产生。
// 声明一个全局定时器变量
static rt_timer_ttimer1;
/* 创建定时器 1 周期定时器, 参数3为毫秒单位 */
// 建立一个以分钟为单位的定时
timer1 = rt_timer_create("timer1", timeout1,
RT_NULL, 60000,
RT_TIMER_FLAG_PERIODIC);
/* 启动定时器 1 */
if(timer1 != RT_NULL) rt_timer_start(timer1);
接下来在学习RTT下的串口接收处理。一旦调通了,就可以配合ESP8266一起进行测试了。