基于esp32-s2实现本地气象台/温度计
利用OLED显示当前本地的时间、温度和气象信息系统能够自动校时,开机后自动调节到准确的时间(年、月、日、时、分、秒
标签
嵌入式系统
显示
网络与通信
2022寒假在家练
奈奎斯特不稳定
更新2022-03-03
哈尔滨理工大学
1186

本项目使用esp32-s2-mini模组,操作系统采用esp-idf自带的freertos操作系统,用到spi,按键等外设实现本地气象台/温度计。

硬件介绍OLED

OLED(Organic Light-Emitting Diode),又称为有机电激光显示、有机发光半导体(Organic Electroluminescence Display,OLED)。OLED属于一种电流型的有机发光器件,是通过载流子的注入和复合而致发光的现象,发光强度与注入的电流成正比。OLED在电场的作用下,阳极产生的空穴和阴极产生的电子就会发生移动,分别向空穴传输层和电子传输层注入,迁移到发光层。当二者在发光层相遇时,产生能量激子,从而激发发光分子最终产生可见光。

esp32-s2-mini

ESP32-S2 是一款高度集成、高性价比、低功耗、主打安全的单核 Wi-Fi SoC,具备强大的功能和丰富的 IO 接口。内置Xtensa® 单核 32 位 LX7 微处理器,支持高达 240 MHz 的时钟频率。ESP32-S2-MINI-1 和 ESP32-S2-MINI-1U 是通用型 Wi-Fi MCU 模组,功能强大,具有丰富的外设接口,可用于 可穿戴电子设备、智能家居等场景。

思路

流程图

FsY46GJtDNGjUAtVm_d_zjvzFa2n

此次项目的思路非常简单,和我之前的一个wio terminal的项目非常相似。将主要思路分为三大块:OLED显示,网络数据获取和按键扫描。这三个思路其中OLED显示和网络数据获取放在了一个任务中,按键扫描单独开了一个任务,主要原因是其中的按键消抖需要大量的延时,这时候可以处理很多的其他的任务。

主要代码及其说明按键扫描

void key_scan_task(void *param)
{
  while (1)
  {

    if (digitalRead(button1) == LOW)
    {            // 按键1换音源
      delay(80); //去抖
      if (digitalRead(button1) == LOW)
      { // 若按键1被按下
        dis.print_log("key_task", "botton1_pressed!");
        if (oledHandle != NULL)
        {
          vTaskDelete(oledHandle);
          xTaskCreatePinnedToCore(OLED_time, "OLEDTASK", 2048, NULL, 3, &oledHandle, tskNO_AFFINITY);
          configASSERT(oledHandle);
        }
        while (digitalRead(button1) == LOW)
          vTaskDelay(100 / portTICK_RATE_MS); //等待抬起;
      }
    }

    if (digitalRead(button2) == LOW)
    {            // 按键1换音源
      delay(80); //去抖
      if (digitalRead(button2) == LOW)
      { // 若按键1被按下
        dis.print_log("key_task", "botton1_pressed!");
        if (oledHandle != NULL)
        {
          vTaskDelete(oledHandle);
          xTaskCreatePinnedToCore(OLED_weather, "OLEDTASK", 2048, NULL, 3, &oledHandle, tskNO_AFFINITY);
          configASSERT(oledHandle);
        }
        while (digitalRead(button2) == LOW)
          vTaskDelay(100 / portTICK_RATE_MS); //等待抬起;
      }
    }

    if (digitalRead(button3) == LOW)
    {            // 按键1换音源
      delay(80); //去抖
      if (digitalRead(button3) == LOW)
      { // 若按键1被按下
        dis.print_log("key_task", "botton1_pressed!");
        while (digitalRead(button3) == LOW)
          vTaskDelay(100 / portTICK_RATE_MS); //等待抬起;
      }
    }

    if (digitalRead(button4) == LOW)
    {            // 按键1换音源
      delay(80); //去抖
      if (digitalRead(button4) == LOW)
      { // 若按键1被按下
        dis.print_log("key_task", "botton1_pressed!");
        while (digitalRead(button4) == LOW)
          vTaskDelay(100 / portTICK_RATE_MS); //等待抬起;
      }
    }

    vTaskDelay(100 / portTICK_RATE_MS); //等待抬起;
  }
}

通过常见的按键扫描代码,实现OLED任务的切换。

OLED显示

OLED的显示,我将相应的代码放入了对应的文件中,主要代码如下

显示天气数据

void show_weather(const String &w_Answer)
{
	int jsonIndex = 0;
	// find the avaliable data,with out the begin
	for (unsigned int i = 0; i < w_Answer.length(); i++)
	{
		if (w_Answer[i] == '{')
		{
			jsonIndex = i;
			break;
		}
	}
	const String jsonAnswer = w_Answer.substring(jsonIndex);

	DynamicJsonDocument doc(2048);

	deserializeJson(doc, jsonAnswer);

	// Serial.println(doc);

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

	JsonObject temp_day = results_0["daily"][0];
	const String text_datetemp_num = temp_day["date"];				  //当天时间
	const String text_daytemp_num = temp_day["text_day"];			  //晚上气象代码
	const String hightemp_num = temp_day["high"];					  //当天最高温度
	const String lowtemp_num = temp_day["low"];						  //降水概率,范围temp_num~1temp_numtemp_num,单位百分比(目前仅支持国外城市)
	const String wind_directiontemp_num = temp_day["wind_direction"]; //风向文字
	const String wind_speedtemp_num = temp_day["wind_speed"];		  //风速,单位km/h(当unit=c时)、mph(当unit=f时)
	const String wind_scaletemp_num = temp_day["wind_scale"];		  //风力等级
	const String humiditytemp_num = temp_day["humidity"];

	u8g2.clearBuffer();
	u8g2.setFontDirection(0);
	u8g2.setFont(u8g2_font_ncenB08_tr);
	u8g2.setCursor(0, 8);
	// u8g2.print(&timeinfo, "%Y.%m.%d");
	u8g2.print("SuZhou " + text_datetemp_num);
	u8g2.setFont(u8g2_font_ncenB10_te);
	u8g2.setCursor(0, 12 * 2);
	u8g2.print(text_daytemp_num);
	u8g2.setCursor(0, 12 * 3);
	u8g2.print("T  : " + String((lowtemp_num.toInt() + hightemp_num.toInt()) / 2));
	u8g2.setCursor(0, 12 * 4);
	u8g2.print("Wi : " + wind_directiontemp_num + " , " + wind_scaletemp_num);
	u8g2.setCursor(0, 12 * 5);
	u8g2.print("H  : " + humiditytemp_num);

	u8g2.nextPage();
}

显示时间

void show_time(String weather)
{
	static int oledsec = -1;
	struct tm timeinfo;
	char str[64] = {0};
	if (!getLocalTime(&timeinfo))
	{
		Serial0.println("display -> Failed to obtain time"); //时间获取失败
		return;
	}
	if (oledsec == timeinfo.tm_sec)
		return;
	oledsec = timeinfo.tm_sec;
	//成功则输出时间
	u8g2.clearBuffer();
	u8g2.setFontDirection(0);
	u8g2.setFont(u8g2_font_ncenB08_te);
	u8g2.setCursor(16, 10);
	// u8g2.print(&timeinfo, "%Y.%m.%d");
	u8g2.print("SuZhouCity");
	u8g2.setFont(u8g2_font_ncenB18_tr);
	u8g2.setCursor(10, 38);
	u8g2.print(&timeinfo, "%H:%M:%S");
	u8g2.setFont(u8g2_font_ncenB10_tf);
	u8g2.setCursor(14, 53);
	// u8g2.print(weather);
	u8g2.print(&timeinfo, "%Y-%m-%d");

	u8g2.nextPage();
}

以上代码都是放在了带有延时的死循环任务中,其中时间显示的循环为100ms左右,而天气显示则是以一个小时为周期刷新的任务。这两个任务通过第一个任务——按键检测切换。

遇到的问题

遇到的问题主要是开发环境的选择。之前我没有对esp32-s2开发的经历,所以对开发环境有所纠结:arduino,esp-idf,micropython,circuitpython等都是不错的编译平台。我花了十天的时间对这几个平台进行了尝试,也遇到了或多或少不同的问题。

platformio

首先是platformio平台,因为之前有机会使用platformio开发esp32-s模组,所以我就开始尝试使用platformio使用arudino的framework开发esp32-s2模组,结果发现没有arduino的framework选择,只有IDF框架的选择。

FinRZBYfASrbd8-knqeJ47D9sPNe

从网上搜集了一圈资料之后,我确定esp32-s2模组的arduino框架在github托管的项目中没有合并到develop分支,后来在其arduino分支找到了带arduino框架的esp-s2框架的项目,但是介于其项目没有合并到主分支,所以我打算尝试别的框架。

FlCk9qPWxz9u2no_0q-xH9YPd29w

IDF框架

我又从espressif官网的IDF框架尝试搭建其环境, 更具网页指导我通过命令行配置好了esp的开发环境,并且成功接收到串口发送的数据后,我个人认为使用命令行编译,确实没有现成的IDE开发来的方便。这时候我又想着尝试一些别的编程方式。

platformio也有提供IDF框架的插件,但是,我使用的时候不知道是什么问题,后来经过网上冲浪后尝试了几个方法,最后通过回溯之前的版本的方法。解决了问题。

circuitpython

由于本次使用的平台是esp32-s2-mini,circuitpython可以直接使用USB口进行通讯,所以我选择circuitpython而不是micropython,但是没有对应的固件,所以我每个固件都试了一下,最终锁定了这个固件,将bootloader通过串口烧入到esp32-s2可以在USB连接的端口可以看到一个u盘,接下来把需要的固件.uf2烧入,就可以得到circuitpython的运行。下图是我当时零时做的USB转USB的线,废了我两条typc的线啊。

最终我还是选择使用platformio的arduino框架,在编译的时候发现缺少资源,原来是应为develop_branch没有加入基本的库,所以我在旁边的资源中复制了基本的资源库“stdint.h”等。终于再重新配置.ini文件重新编译,通过。

[env:MagTag_v0.1]
platform = espressif32
platform_packages = 
	toolchain-xtensa32s2
	framework-arduinoespressif32@https://github.com/espressif/arduino-esp32.git#2.0.0-alpha1
framework = arduino
board = esp32dev
board_build.mcu = esp32s2
board_build.partitions = huge_app.csv
build_unflags = 
	-DARDUINO_ESP32_DEV
	-DARDUINO_VARIANT="esp32"
build_flags = 
	-DARDUINO_MAGTAG29_ESP32S2
	-DARDUINO_SERIAL_PORT=1
	-DARDUINO_VARIANT="adafruit_magtag29_esp32s2"
monitor_speed = 115200
lib_deps = 
	olikraus/U8g2@^2.32.7
	bblanchon/ArduinoJson@^6.19.0
	pu2clr/PU2CLR RDA5807@^1.0.5

成果展示 时间显示

FqSJ2DcqFZBnuG8ddPASqbM4RYNZ

气象显示FgprlTO4Zgt3TFgkJnkEFqlDJxHx未来计划,使用心得

因为esp32是面向物联网的单片机,在未来,我希望使用UDP协议或者TCP协议实现基于互联网的示波器功能等。在使用过esp32-s2平台后,我意识到以后我有可能会遇到更多比较新颖的芯片,资料会相对较少,我还需要巩固计算机基础。

附件下载
main.cpp
firmware.bin
团队介绍
团队成员
奈奎斯特不稳定
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号