平台介绍
本次使用的音频处理平台,是基于乐鑫官方模块ESP32-S2-Mini-1制作的。该模块由一颗ESP32S2的处理器,和一颗可以运行在80M的QPI上的Flash以及一些外围电路组成。ESP32S2是一颗单核的Xtensa32的处理器,能运行在240MHz的主频上。官方提供了esp-idf和Arduino等开发框架,本次我在这使用esp-idf作为开发框架开发。
模块可以广泛应用于下面的一些领域:
- 通用低功耗 IoT 传感器 Hub
- 通用低功耗 IoT 数据记录器
- USB 设备
- 语音识别
- 图像识别
- Mesh 网络
- 家庭自动化
- 智慧楼宇
- 工业自动化
- 健康/医疗/看护
- Wi-Fi 玩具
- 可穿戴电子产品
- 零售 & 餐饮
- 智能 POS 应用
OLED原理介绍
OLED(Organic Light-Emitting Diode),又称为有机电激光显示、有机发光半导体(Organic Electroluminescence Display,OLED)。OLED属于一种电流型的有机发光器件,是通过载流子的注入和复合而致发光的现象,发光强度与注入的电流成正比。OLED在电场的作用下,阳极产生的空穴和阴极产生的电子就会发生移动,分别向空穴传输层和电子传输层注入,迁移到发光层。当二者在发光层相遇时,产生能量激子,从而激发发光分子最终产生可见光。
项目目标
-
利用OLED显示
-
显示当前本地的时间、温度和气象信息
设计思路
ESP32功能的核心以及大多可拓展模块都是基于其WIFI连接这一功能实现的,而本地气象台这一功能的本质就是通过WIFI从HTTP网页获知本人所在地的气象数据,经过类似爬虫原理的数据解析,从网页上的信息中筛选出我们需要的地区,时间,气温,天气等信息,并通过OLED显示出来。
具体实现过程
这里用ssd-1306的OLED驱动芯片,并通过软件模拟IIC使OLED能够应对复杂多变的输出结果的要求。
void ssd1306_oled_WrDat(unsigned char data)
{
unsigned char i=8;
//ssd1306_oled_CS=0;;
gpio_set_level(SSD1306_DC,1);;
gpio_set_level(SSD1306_SCL,0);;
vTaskDelay(1 / portTICK_RATE_MS/1000);
// delay_1us();
while(i--)
{
if(data&0x80){gpio_set_level(SSD1306_SDA,1);}
else{gpio_set_level(SSD1306_SDA,0);}
gpio_set_level(SSD1306_SCL,1);
vTaskDelay(1 / portTICK_RATE_MS/1000);
// delay_1us();
//asm("nop");
gpio_set_level(SSD1306_SCL,0);;
data<<=1;
}
//ssd1306_oled_CS=1;
}
void ssd1306_oled_WrCmd(unsigned char cmd)
{
unsigned char i=8;
//ssd1306_oled_CS=0;;
gpio_set_level(SSD1306_DC,0);;
gpio_set_level(SSD1306_SCL,0);;
vTaskDelay(1 / portTICK_RATE_MS/1000);
// delay_1us();
while(i--)
{
if(cmd&0x80){gpio_set_level(SSD1306_SDA,1);}
else{gpio_set_level(SSD1306_SDA,0);;}
gpio_set_level(SSD1306_SCL,1);;
vTaskDelay(1 / portTICK_RATE_MS/1000);
// delay_1us();
gpio_set_level(SSD1306_SCL,0);;
cmd<<=1;;
}
//ssd1306_oled_CS=1;
}
void ssd1306_oled_Set_Pos(unsigned char x, unsigned char y)
{
ssd1306_oled_WrCmd(0xb0+y);
ssd1306_oled_WrCmd(((x&0xf0)>>4)|0x10);
ssd1306_oled_WrCmd((x&0x0f));
}
void ssd1306_oled_Fill(unsigned char bmp_data)
{
unsigned char y,x;
for(y=0;y<8;y++)
{
ssd1306_oled_WrCmd(0xb0+y);
ssd1306_oled_WrCmd(0x01);
ssd1306_oled_WrCmd(0x10);
for(x=0;x<X_WIDTH;x++)
ssd1306_oled_WrDat(bmp_data);
}
}
void ssd1306_oled_CLS(void)
{
unsigned char y,x;
for(y=0;y<8;y++)
{
ssd1306_oled_WrCmd(0xb0+y);
ssd1306_oled_WrCmd(0x01);
ssd1306_oled_WrCmd(0x10);
for(x=0;x<X_WIDTH;x++)
ssd1306_oled_WrDat(0);
}
}
void ssd1306_oled_PutPixel(unsigned char x,unsigned char y)
{
unsigned char data1;
ssd1306_oled_Set_Pos(x,(unsigned char)(y>>3));
data1 =(unsigned char)(0x01<<(y%8));
ssd1306_oled_WrCmd((unsigned char)(0xb0+(y>>3)));
ssd1306_oled_WrCmd((unsigned char)(((x&0xf0)>>4)|0x10));
ssd1306_oled_WrCmd((unsigned char)((x&0x0f)|0x00));
ssd1306_oled_WrDat(data1);
}
void ssd1306_oled_Rectangle(unsigned char x1,unsigned char y1,unsigned char x2,unsigned char y2,unsigned char gif)
{
unsigned char n;
ssd1306_oled_Set_Pos(x1,y1>>3);
for(n=x1;n<=x2;n++)
{
ssd1306_oled_WrDat(0x01<<(y1%8));
if(gif == 1) vTaskDelay(50 / portTICK_RATE_MS);
}
ssd1306_oled_Set_Pos(x1,y2>>3);
for(n=x1;n<=x2;n++)
{
ssd1306_oled_WrDat(0x01<<(y2%8));
if(gif == 1) vTaskDelay(5 / portTICK_RATE_MS);
}
}
void ssd1306_oled_P6x8Str(unsigned char x,unsigned char y, char ch[])
{
unsigned char c=0,i=0,j=0;
while (ch[j]!='\0')
{
c =ch[j]-32;
if(x>126){x=0;y++;}
ssd1306_oled_Set_Pos(x,y);
for(i=0;i<6;i++)
ssd1306_oled_WrDat(F6x8[c][i]);
x+=6;
j++;
}
}
void ssd1306_oled_P8x16Str(unsigned char x,unsigned char y, char ch[])
{
unsigned char c=0,i=0,j=0;
while (ch[j]!='\0')
{
c =ch[j]-32;
if(x>120){x=0;y++;}
ssd1306_oled_Set_Pos(x,y);
for(i=0;i<8;i++)
ssd1306_oled_WrDat(F8X16[c*16+i]);
ssd1306_oled_Set_Pos(x,y+1);
for(i=0;i<8;i++)
ssd1306_oled_WrDat(F8X16[c*16+i+8]);
x+=8;
j++;
}
}
这里的代码段大部分都是收集资料所得,而非手动编制,我也是耗费大量精力才获取到通过ssd-1306驱动芯片和IIC驱动OLED的方法。
网络连接模块
if(err != 0 || res == NULL) {
ESP_LOGE(TAG, "DNS lookup failed err=%d res=%p", err, res);
vTaskDelay(1000 / portTICK_PERIOD_MS);
continue;
}
/* Code to print the resolved IP.
Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code */
addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", inet_ntoa(*addr));
s = socket(res->ai_family, res->ai_socktype, 0);
if(s < 0) {
ESP_LOGE(TAG, "... Failed to allocate socket.");
freeaddrinfo(res);
vTaskDelay(1000 / portTICK_PERIOD_MS);
continue;
}
ESP_LOGI(TAG, "... allocated socket");
if(connect(s, res->ai_addr, res->ai_addrlen) != 0) {
ESP_LOGE(TAG, "... socket connect failed errno=%d", errno);
close(s);
freeaddrinfo(res);
vTaskDelay(4000 / portTICK_PERIOD_MS);
continue;
}
这里给出对DNS服务以及socket套接字的连接及其检查的代码段,其他服务的相应功能实现与其大同小异。
数据的解析并输出
if(json_weather!=NULL)
{
printf("json_weather\r\n");
cJSON *json_weather_results = cJSON_GetObjectItem(json_weather,"results");
if(json_weather_results != NULL)
{
printf("json_weather_results json\r\n");
cJSON *json_weather_array = cJSON_GetArrayItem(json_weather_results,0);
if(json_weather_array != NULL)
{
printf("json_weather_array json array\r\n");
cJSON *json_weather_location = cJSON_GetObjectItem(json_weather_array,"location");
if(json_weather_location!=NULL)
{
cJSON *json_weather_city = cJSON_GetObjectItem(json_weather_location,"name");
if(json_weather_city!=NULL)
printf("city:%s\r\n",json_weather_city->valuestring);
strcpy(city,json_weather_city->valuestring);
}
cJSON *json_weather_now = cJSON_GetObjectItem(json_weather_array,"now");
if(json_weather_now!=NULL)
{
cJSON *json_weather_text = cJSON_GetObjectItem(json_weather_now,"text");
if(json_weather_now!=NULL)
printf("weather:%s\r\n",json_weather_text->valuestring);
strcpy(weather_name,json_weather_text->valuestring);
cJSON *json_weather_temperature = cJSON_GetObjectItem(json_weather_now,"temperature");
if(json_weather_temperature!=NULL)
printf("temperature :%s\r\n",json_weather_temperature->valuestring);
strcpy(temperature,json_weather_temperature->valuestring);
}
}
}
}
else
{
printf("json_weather\r\n");
}
数据的解析如上述所说,主要是通过json形式获取,并从中提取出相应的城市,时间,天气,温度的信息,数据的输出比较简单,在此略过。
结果展示
最终,通过上述思路及其实现过程,实现了对于城市,日期,温度以及天气的输出。
遇到的困难及问题解决方案
在配置ESP32-S2的开发环境以的过程中,我就遇到了很大的困难。用老师所讲到的方法配置环境,更换了两台机器,重新安装了很多次Git,esp-idf也无济于事,总是在安装编译工具链的时候Python遇到Traceback错误,最终也只能无奈放弃了这一条路径。最终,通过在网络上大量搜集资料,访问论坛,利用VSCode环境成功将ESP32-S2的开发环境搭建了下来,通过后续开发和测试,也证明了环配置是正确的。
在使OLED输出正确信息的过程中,我也遭遇了一些困难。也是在网络上多方查找资料,试验了多个方法,才选择了对我前期对信息筛选的代码有较高适配性的一种方法。在这里用ssd-1306的OLED驱动芯片,并通过软件模拟IIC使OLED能够应对复杂多变的输出结果的要求。
未来的计划与建议
对于ESP32-S2-mini1这块板子,实现本地气象台仅仅是利用了其WIF模块以及USB接口,对于其更为丰富的音频信号处理等功能没有涉及。在这之外,此芯片在物联网方面的应用也极为广泛。因此,这块板子的开发还有很多方面是值得我去继续尝试的。同时,我也会继续学习嵌入式系统及FPGA开发的知识,涉猎更多更广泛的芯片,实现更多的功能。