之前在测试开发板附带的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 开发板。这个很过程很简单。


    1.jpg

    在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一起进行测试了。