【得捷电子Follow me第2期】 任务4:分任务1:日历&时钟——完成一个可通过互联网更新的万年历时钟,并显示当地的天气信息。
开发工具:Vscode+platformio 。使用arduino进行进行编程。
配置文件:platformio.ini
[env:adafruit_feather_esp32s3_tft]
platform = espressif32
board = adafruit_feather_esp32s3_tft
framework = arduino
monitor_speed = 115200
upload_speed = 1500000
board_build.f_cpu = 240000000L
board_build.f_flash = 80000000L
board_build.flash_mode = dio
build_flags =
-DCORE_DEBUG_LEVEL=3
-Iinclude
lib_deps =
adafruit/Adafruit GFX Library @ ^1.11.8
adafruit/Adafruit ST7735 and ST7789 Library @ ^1.10.3
arduino-libraries/NTPClient @ ^3.2.1
bblanchon/ArduinoJson @ ^6.21.3
adafruit/Adafruit BMP280 Library @ ^2.6.8
玩了好久的CPY,想再尝试一下Arduino。板子上缺失的BMP280芯片,从淘宝上购买了一块焊上了,手艺不好,搞得屏幕黄了一块。既然装上了BMP280就做个简单的日历/时钟+天气展示吧!
作为时钟+天气,那么首先就要联网。通过互联网获取这些内容最为容易。通过一个单独的任务处理连接wifi的功能。连接完成wif后,与互联网进行校时。这里自动校时使用了NTPClient这个库。
#include "wifi_conn.h"
/**
* Task: monitor the WiFi connection and keep it alive!
*
* When a WiFi connection is established, this task will check it every 10 seconds
* to make sure it's still alive.
*
* If not, a reconnect is attempted. If this fails to finish within the timeout,
* the ESP32 will wait for it to recover and try again.
*/
WiFiUDP ntpUDP;
// NTPClient timeClient(ntpUDP);
NTPClient timeClient(ntpUDP, "ntp1.aliyun.com", 60 * 60 * 8, 30 * 60 * 1000);
// NTPClient timeClient(ntpUDP,"ntp.tuna.tsinghua.edu.cn");
void keepWiFiAlive(void *parameter)
{
for (;;)
{
if (WiFi.status() == WL_CONNECTED)
{
// Serial.print(WiFi.localIP());
// Serial.print(" ");
// Serial.println(timeClient.getFormattedTime());
vTaskDelay(10000 / portTICK_PERIOD_MS);
continue;
}
ESP_LOGE(TAG, "[WIFI] Connecting");
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_NETWORK, WIFI_PASSWORD);
unsigned long startAttemptTime = millis();
// Keep looping while we're not connected and haven't reached the timeout
while (WiFi.status() != WL_CONNECTED &&
millis() - startAttemptTime < WIFI_TIMEOUT_MS)
{
}
// When we couldn't make a WiFi connection (or the timeout expired)
// sleep for a while and then retry.
if (WiFi.status() != WL_CONNECTED)
{
ESP_LOGE(TAG, "[WIFI] FAILED");
vTaskDelay(WIFI_RECOVER_TIME_MS / portTICK_PERIOD_MS);
continue;
}
timeClient.begin();
timeClient.setTimeOffset(28800); //+1区,偏移3600,+8区,偏移3600*8
timeClient.update();
log_i("[WIFI] Connected: %s %s", (WiFi.localIP()).toString(), timeClient.getFormattedTime());
Serial.print(WiFi.localIP());
Serial.print(" ");
Serial.println(timeClient.getFormattedTime());
}
}
联网后,需要获取天气信息。天气信息使用独立的任务来循环进行。每5分钟获取一次。天气信息从心知天气获取。心知天气获取到的是json数据,这里json数据解析学习了论坛中老师的帖子!然后还有个BMP280芯片,这个芯片能够获得当前环境的温度和气压。通过BMP280的库,很方便地通过IIC总线方式获得了温度(摄氏度),气压(帕斯卡)。该库还提供了一个计算高度的方法,也拿来用用,不过计算出的高度有点怪,貌似是用气压直接换算出来的。
有了天气信息还需要展示信息,这里使用了第三方的st7789的库进行展示。使用第三方库驱动屏幕简单了很多,不用去写麻烦的寄存器了!
最后是将所有收集到到的信息一股脑地展示到tft屏幕上去,从上到下依次是 :
第一行:日历信息:年月日 时分秒。
第二行:当前气压(千帕),和温度(摄氏度)。
第三行:当前高度(米)(这里测出来我这里是低于海平面)
第四行:所处的城市。和当前天气预报的温度值(摄氏度)。
第五行:天气情况。
这里代码还是有些乱,有空了需要将功能模块化!
#include <Arduino.h>
#include "WiFi.h"
#include <Adafruit_GFX.h> // Core graphics library
#include "Adafruit_ST7789.h"
#include "wifi_conn.h"
#include <ArduinoJson.h>
#include <Adafruit_BMP280.h>
Adafruit_BMP280 bmp; // I2C
// 心知天气HTTP请求所需信息
String reqUserKey = "xxxx"; // 私钥
String reqLocation = "dongguan"; // 城市
String reqUnit = "c"; // 摄氏/华氏
uint32_t weather_query_delay = 0;
//心知天气
const char *host = "api.seniverse.com"; // 将要连接的服务器地址
const int httpPort = 80; // 将要连接的服务器端口
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
void Tft_Init(void)
{
// turn on backlite
pinMode(TFT_BACKLITE, OUTPUT);
digitalWrite(TFT_BACKLITE, HIGH);
// turn on the TFT / I2C power supply
pinMode(TFT_I2C_POWER, OUTPUT);
digitalWrite(TFT_I2C_POWER, HIGH);
delay(10);
// initialize TFT
tft.init(135, 240); // Init ST7789 240x135
tft.setRotation(3);
tft.fillScreen(ST77XX_BLACK);
Serial.println(F("TFT ST7789 240* 135 Initialized"));
tft.setCursor(20, 50);
tft.setTextColor(ST77XX_BLUE);
tft.setTextSize(3);
tft.println("Follow me 2");
}
// 利用ArduinoJson库解析心知天气响应信息
String results_0_now_text_str;
int results_0_now_temperature_int;
void parseInfo(WiFiClient client)
{
const size_t capacity = JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1) + 2 * JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(6) + 230;
DynamicJsonDocument doc(capacity);
deserializeJson(doc, client);
JsonObject results_0 = doc["results"][0];
JsonObject results_0_now = results_0["now"];
const char *results_0_now_text = results_0_now["text"];
const char *results_0_now_code = results_0_now["code"];
const char *results_0_now_temperature = results_0_now["temperature"];
results_0_now_text_str = results_0_now["text"].as<String>();
int results_0_now_code_int = results_0_now["code"].as<int>();
results_0_now_temperature_int = results_0_now["temperature"].as<int>();
Serial.println(F("======Weahter Now======="));
Serial.print(F("Weather Now: "));
Serial.print(results_0_now_text_str);
Serial.print(F(" "));
Serial.println(results_0_now_code_int);
Serial.print(F("Temperature: "));
Serial.print(results_0_now_temperature_int);
Serial.print("C ");
Serial.println(timeClient.getFormattedTime());
Serial.println(F("========================"));
tft.fillScreen(ST77XX_BLACK);
tft.setCursor(0, 74);
tft.setTextColor(ST77XX_ORANGE);
tft.setTextSize(2);
tft.println("DONG GUAN");
tft.setCursor(0, 100);
tft.setTextColor(ST77XX_RED);
tft.print(results_0_now_text_str);
tft.print(" ");
tft.setTextSize(3);
tft.setTextColor(ST77XX_GREEN);
tft.setCursor(150, 80);
tft.print(String(results_0_now_temperature_int));
tft.println("C");
tft.setCursor(10, 24); //显示BMP280信息
tft.setTextSize(2);
float f = bmp.readTemperature(); //摄氏度
float P = bmp.readPressure() / 1000.0; // kPa
float A = bmp.readAltitude(1013.25); //米
char strbuf[64];
sprintf(strbuf,"P:%.fkPa T:%.1fC ",P,f);
tft.println(strbuf);
sprintf(strbuf,"H:%.fM",A);
tft.setCursor(10, 48);
tft.println(strbuf);
}
// 向心知天气服务器服务器请求信息并对信息进行解析
void httpRequest(String reqRes)
{
WiFiClient client;
// 建立http请求信息
String httpRequest = String("GET ") + reqRes + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n";
Serial.println("");
Serial.print("Connecting to ");
Serial.print(host);
// 尝试连接服务器
if (client.connect(host, 80))
{
Serial.println(" Success!");
// 向服务器发送http请求信息
client.print(httpRequest);
Serial.println("Sending request: ");
Serial.println(httpRequest);
// 获取并显示服务器响应状态行
String status_response = client.readStringUntil('\n');
Serial.print("status_response: ");
Serial.println(status_response);
// 使用find跳过HTTP响应头
if (client.find("\r\n\r\n"))
{
Serial.println("Found Header End. Start Parsing.");
}
weather_query_delay = 1000 * 60 * 5;
// 利用ArduinoJson库解析心知天气响应信息
parseInfo(client);
}
else
{
weather_query_delay = 3000;
Serial.println(" connection failed!");
}
//断开客户端与服务器连接工作
client.stop();
}
void Task_Weather_Get(void *pvParameters)
{
(void)pvParameters;
// 建立心知天气API当前天气请求资源地址
String reqRes = "/v3/weather/now.json?key=" + reqUserKey +
+"&location=" + reqLocation +
"&language=en&unit=" + reqUnit;
for (;;)
{
// 向心知天气服务器服务器请求信息并对信息进行解析
httpRequest(reqRes);
Serial.println("Next Weather Query will be " + String(weather_query_delay / 1000 / 60) + " min later");
vTaskDelay(weather_query_delay / portTICK_PERIOD_MS);
}
}
void setup(void)
{
Serial.begin(115200);
Tft_Init();
if (!bmp.begin())
{
Serial.println(F("Could not find a valid BMP280 sensor, check wiring!"));
while (1)
;
}
/* Default settings from datasheet. */
bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */
Adafruit_BMP280::SAMPLING_X2, /* Temp. oversampling */
Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */
Adafruit_BMP280::FILTER_X16, /* Filtering. */
Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */
xTaskCreatePinnedToCore(keepWiFiAlive, "keepWiFiAlive", 1024 * 4, NULL, 2, NULL, ARDUINO_RUNNING_CORE);
vTaskDelay(5000 / portTICK_PERIOD_MS);
xTaskCreatePinnedToCore(Task_Weather_Get, "Task_Weather_Get", 1024 * 4, NULL, 1, NULL, 0);
}
void loop(void)
{
timeClient.update();
unsigned long epochTime = timeClient.getEpochTime();
//将epochTime换算成年月日
struct tm *ptm = gmtime((time_t *)&epochTime);
// int monthDay = ptm->tm_mday;
// Serial.print(ptm->tm_year);
// Serial.print(" ");
// Serial.print(ptm->tm_wday);
// Serial.print(" ");
char strbuf[64];
sprintf(strbuf, "%d/%02d/%02d %s", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, timeClient.getFormattedTime());
tft.fillRect(0, 0, 240, 16, ST77XX_BLACK);
tft.setCursor(5, 1);
tft.setTextColor(ST77XX_CYAN);
tft.setTextSize(2);
tft.print(strbuf);
// tft.setCursor(136, 0);
// tft.print(timeStamp);
// delay(1000);
delay(1000);
}
在使用Arduino做开发过程中,freertos还是挺有意思的。ESP32集成了freertos操作系统,这样在开发过程中不同的功能模块使用不同的任务去调用。并且Arduino中的loop方法,本质上也是一个freertos任务。
到了活动到期倒数2天时知道了个噩耗。凑单的模块是不返的!纳尼!哭死在马桶上!哎!汇总一下帖子吧!
任务1:Adafruit ESP32-S3 TFT Feather 控制屏幕显示中文
本帖最后由 aramy 于 2023-10-13 08:31 编辑