之前在测试开发板附带的wifi板的时候,出了状况。迫不得已,改用ESP8266这个已经过时的模块来获得网络时间和所在地的天气状况,虽然失去学习使用CYW43012得机会。使用ESP8266的优点也是显而易见的,使用Arduino开发环境,很多复杂的处理,通过Arduino的支持库,可以更容易方便实现,而且稳定可靠。使用ESP8266的话,和PSoC 6 的通讯,我考虑使用串口实现。在这之前,先弄好ESP8266那边的处理。就是获取网络时间和天气预报信息。网络时间,是通过阿里云服务获取的。而天气情报,是通过心知天气获取的。两者都是通过Web API调用实现的。心知天气,可以自己注册一个账户,获取免费机会。ESP8266端的程序如下:

  1. /**

  2. * 上位机通过串口发送指令给本机,根据指令,本机恢复对应的信息

  3. * 串口使用RX/TX引脚

  4. * 指令:qw+0x0d0a   回复qw=今日的平均气温 + 0x0d0a,形如:tm=21:45:10 + 0x0d0a

  5. *      tm+0x0d0a   回复tm=当时的时间 + 0x0d0a,形如:qw=25 + 0x0d0a

  6. */

  7. #include <SPI.h>

  8. #include <Wire.h>

  9. #include <Adafruit_GFX.h>

  10. #include <Adafruit_SSD1306.h>

  11. #include <NTPClient.h>

  12. #include <ESP8266WiFi.h>

  13. #include "WiFiUdp.h"

  14. #include "ESP8266TimerInterrupt.h"

  15. #include <ArduinoJson.h>

  16. #define LOGO_HEIGHT   16

  17. #define LOGO_WIDTH    16

  18. #define BUILTIN_LED 16    // D0 - GPIO16,输出信号

  19. // 接线备注:SCL(GPIO5---D1), SDA(GPIO4---D2)

  20. // OLED分辨率

  21. #define SCREEN_WIDTH 128 // OLED display width, in pixels

  22. #define SCREEN_HEIGHT 64 // OLED display height, in pixels

  23. #define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)

  24. // OLED IIC访问地址

  25. #define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32

  26. const char *ssid     = "***";

  27. const char *password = "***";

  28. const char *host = "ip-api.com"; // 访问的域名

  29. String comRecvStr = "";     // 串口收到的数据

  30. bool comRecvCrLfFlag = false;     // 串口收到换行符

  31. WiFiUDP ntpUDP;

  32. NTPClient timeClient(ntpUDP,"ntp1.aliyun.com",60*60*8,30*60*1000);

  33. WiFiClient client; //创建一个网络对象

  34. // 定时中断时间常数

  35. #define TIMER_INTERVAL_MS       1000

  36. volatile bool statusLed = false;

  37. volatile uint32_t lastMillis = 0;

  38. // Init ESP8266 timer 1

  39. ESP8266Timer ITimer;

  40. Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

  41. // 温度信息串

  42. String tempinfo="25";

  43. /*

  44. 每当新数据进入硬件串行接收时,就会发生SerialEvent。这

  45. 例程在每次loop()运行之间运行,因此在循环内使用delay可以

  46. 延迟响应。可能有多个字节的数据可用。

  47. */

  48. void serialEvent() {

  49. }

  50. // 定时器中断

  51. void IRAM_ATTR TimerHandler() {

  52. }

  53. // 初始化OLED驱动

  54. void init_oled(void) {

  55. Serial.println("<<<init_oled begin");

  56. // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally

  57. // 初始化IIC OLED12864设备

  58. if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {

  59. Serial.println(F("SSD1306 allocation failed"));

  60. for(;;); // Don't proceed, loop forever

  61. }

  62. // 设置字体

  63. display.setTextSize(2);      // 2倍大小

  64. display.setTextColor(SSD1306_WHITE); // Draw white text

  65. display.cp437(true);         // Use full 256 char 'Code Page 437' font

  66. Serial.println(">>>init_oled end");

  67. }

  68. // 定时器中断

  69. void init_interrupt(void) {

  70. if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler)) {

  71. lastMillis = millis();

  72. Serial.print(F("Starting ITimer OK, millis() = ")); Serial.println(lastMillis);

  73. } else {

  74. Serial.println(F("Can't set ITimer correctly. Select another freq. or interval"));

  75. }

  76. }

  77. /**************************************************

  78. * 函数名称:GET_Weather

  79. * 函数功能:http访问获取天气数据

  80. * 参数说明:无

  81. **************************************************/

  82. unsigned long getTime = 0;  //获取网络天气和时间

  83. bool getWeather(void) {

  84. String json = "";  //接收到的数据

  85. String url = "https://api.seniverse.com/v3/weather/now.json?key=*****&location=*****&language=zh-Hans&unit=c";

  86. String urlDat = "&start=0&days=3";


  87. if (!client.connect("116.62.81.138", 80))  {

  88. Serial.println("天气服务器连接失败");

  89. return false;

  90. }   

  91. // 发送请求报文

  92. client.print(String("GET ") + url + " HTTP/1.1\r\n" +  //请求行  请求方法 + 请求地址 + 协议版本

  93. "Host: " + host + "\r\n" +                //请求头部

  94. "Connection: close\r\n" +                //处理完成后断开连接

  95. "\r\n" +                                 //空行

  96. urlDat);                                 //请求数据            

  97. delay(100);

  98. //接收数据

  99. while(client.available()) {         

  100. String line = client.readStringUntil('\r');

  101. json += line;

  102. }

  103. client.stop();      //断开与服务器连接以节约资源

  104. // 解析JSON,获取气温信息

  105. StaticJsonDocument<200> doc;

  106. DeserializationError error = deserializeJson(doc, json);

  107. if (error) {

  108. Serial.print(F("deserializeJson() failed: "));

  109. Serial.println(error.f_str());

  110. return false;

  111. } else {

  112. Serial.println(json);

  113. Serial.println(doc["results"].as<String>());

  114. Serial.println(doc["results"][0].as<String>());

  115. JsonObject results_0 = doc["results"][0];

  116. JsonObject results_0_location = results_0["location"];

  117. String name = results_0_location["name"].as<String>();

  118. Serial.println(name);

  119. Serial.println(results_0["last_update"].as<String>());

  120. JsonObject results_0_now = results_0["now"];

  121. Serial.println("天气" + results_0_now["text"].as<String>()); // 晴

  122. Serial.println("温度" + results_0_now["temperature"].as<String>() + "度"); // 晴

  123. tempinfo = results_0_now["temperature"].as<String>();

  124. }


  125. // 正常获取天气信息

  126. return true;

  127. }

  128. // 每天接收天气情报的标志,每天6点时重新获取,0点清除

  129. bool weatherFlag = false;

  130. void setup(){

  131. //DeserializationError error;

  132. //StaticJsonDocument<200> doc;

  133. 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"}]}";


  134. Serial.begin(115200);

  135. WiFi.begin(ssid, password);

  136. while ( WiFi.status() != WL_CONNECTED ) {

  137. delay ( 500 );

  138. Serial.print ( "." );

  139. }

  140. timeClient.begin();

  141. // 初始化OLED驱动

  142. init_oled();

  143. // 清除屏幕

  144. display.clearDisplay();

  145. }

  146. char chr=0;

  147. void loop() {

  148. int old_sec=-1;

  149. int cur_sec=0;

  150. // 刷新时间

  151. timeClient.update();

  152. //Serial.println(timeClient.getFormattedTime());

  153. cur_sec=timeClient.getSeconds();

  154. if (cur_sec != old_sec) {

  155. display.clearDisplay();

  156. // 为了使时间居中显示

  157. display.setTextSize(2);

  158. display.setCursor(0, 0);     // 起始坐标

  159. display.write(' ');

  160. display.write(timeClient.getFormattedTime().c_str());

  161. display.display();

  162. old_sec=cur_sec;

  163. }

  164. // 每天6:00:00 ~ 6:00:10之间,获取天气信息

  165. if (weatherFlag==false && timeClient.getHours() == 6 && timeClient.getMinutes() == 0 && timeClient.getSeconds()<10) {

  166. if (getWeather() == true) {

  167. // 成功获得天气信息,设置标志

  168. weatherFlag = true;

  169. }

  170. } else if (timeClient.getHours() == 0 && timeClient.getMinutes() == 0) {

  171. weatherFlag = false;

  172. }

  173. // 检查串口集令,如果收到所要天气情报的指令,将获得的天气信息发出去,晴朗与否,气温

  174. while (Serial.available() > 0) {

  175. chr = char(Serial.read());

  176. if (chr==0x0d) {

  177. Serial.println(comRecvStr);

  178. if (comRecvStr.equalsIgnoreCase("qw")) {

  179. // 上位机索要气温信息

  180. // display.println("\ntq");

  181. // display.display();

  182. // 发送气温信息

  183. Serial.print("qw=");

  184. Serial.print(tempinfo);

  185. Serial.print("\n");

  186. } else if (comRecvStr.equalsIgnoreCase("tm")) {

  187. // 上位机索要时间信息

  188. Serial.print("tm=");

  189. Serial.print(timeClient.getFormattedTime().c_str());

  190. Serial.print("\n");

  191. }

  192. comRecvStr="";

  193. }  else {

  194. comRecvStr += chr;

  195. }

  196. }

  197. }


代码中涉及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一起进行测试了。