- 2024-09-01
-
加入了学习《【Follow me第二季第1期】+ CPE基于VS-CODE PIO的都功能挂饰开发》,观看 【Follow me第二季第1期】+ CPE基于VS-CODE PIO的都功能挂饰开发
-
加入了学习《【Follow me第二季第1期】全部任务演示短视频》,观看 【Follow me第二季第1期】任务演示视频
-
上传了资料:
【Follow me第二季第1期】+ CPE基于VS-CODE PIO的都功能挂饰开发 源码
-
发表了主题帖:
【Follow me第二季第1期】+ CPE基于VS-CODE PIO的多功能挂饰开发(完整项目)
本帖最后由 genvex 于 2024-9-3 10:57 编辑
本项目将包括以下内容:
入门任务、开发环境搭建
VS-CODE -platformIO 编程环境(有坑),入门任务很高阶。
任务一、炸街灯光秀
多种炫酷灯光效果随意切换
任务二、环境温度和光线强度监测
光温效果立竿见影
任务三、谜之接近检测
红外传感器的逆袭
进阶任务、不倒翁优雅实现
Atan2关键函数深度解析
创意任务、精彩挂饰
灯光秀+midi音乐播放器(助攻生日趴)
项目简介
Circuit Playground Express(下文简称CPE)基于ATSAMD21微控制器,采用32位ARM® Cortex®-M0+内核。ATSAMD21采用先进的电源管理技术,电流消耗极低。它可以由USB、“AAA”电池组或Lipoly电池供电。可通过内置USB快速连接进行编程,无需专用电缆或适配器。非常适合用于电子产品和编程,能够让创意充分发挥。
本项目基于CPE的功能挂饰开发,包括编程环境搭建、多个任务的实现等内容。在编程环境搭建中,通过解决初始代码编译报错问题,最终在VSCODE的platformIO平台上成功为CPE新建项目。在任务实现方面,完成了炸街灯光秀、环境温度和光线强度监测、谜之接近检测、不倒翁优雅实现以及精彩挂饰等任务,如利用灯珠实现多种灯光效果切换、监测环境数据并在灯珠上显示、通过红外脉冲判断物体接近、感受重心变化实现不倒翁功能以及融合midi播放和灯光秀等。
全部任务内容在一个套代码中实现,无需分次上传。
如果不想看后面详细展开看以下精简版内容也可以获取本文主要核心内容:
本项基于VS - CODE PIO的Circuit Playground Express(CPE)多功能挂饰开发项目,
具体内容包括:
1. 项目简介:
• CPE基于ATSAMD21微控制器,采用32位ARM Cortex - M0 +内核,可由多种方式供电,适合电子产品和编程。 • 项目包括编程环境搭建和多个任务的实现,全部任务内容在一个套代码中实现。
2. 编程环境搭建:
• CPE支持多种编程环境,本项目选择在VSCODE的platformIO平台上搭建,但初始代码编译报错。
• 通过对比Seeed Xiao ATSAMD21项目,发现是vscode - PIO提供的Adafruit TinyUSB Library有问题,最终通过替换Seeed的TinyUSB驱动解决。 • CPE在该编程环境下编译上传速度快。
3. 任务实现:
• 灯光秀:利用CPE上的10颗灯珠实现多种炫酷灯光效果切换,通过函数数组和“RainbowCycleDemo”类实现灯光模式切换。
• 环境温度和光线强度监测:光线强度传感器连接到模拟引脚A8,温度传感器使用NTC热敏电阻器,通过复杂的计算公式获取温度,将光照强度和温度数值映射到灯珠上进行简易显示。
• 接近检测:通过sendIRPulse()函数发送红外脉冲,根据A10的模拟量判断物体接近,实现距离检测和相应的显示及警报功能。
• 进阶任务、不倒翁实现:用CPE感受重心变化,通过螺丝孔和龟苓膏塑料盖等制作不倒翁,核心关键代码中使用atan2函数计算极角,解决了atan在x值接近0时不稳定的问题。
• 创意任务、精彩挂饰:实现midi音乐播放器和灯光秀的融合,对midi音频数据序列进行解码,根据音符字符串播放对应的音符。
4. 心得体会:
• 项目开始觉得简单,实际很有趣,得益于充分的调研和CPE的配置提升。
• 用类把所有任务内容用一套代码实现,按键B实现功能切换,按键A实现功能内部模式切换,通过时间判断实现midi播放和灯光秀同时进行。 • CPE背后Adafruit团队的板卡支持库和案例通俗易懂。
5. 不足之处:篇幅有限,不能呈现实验过程细节,可下载代码包中的免编译固件进行体验测试。 代码包中有firmware.bin固件,可双击reset键进入bootloader模式,将firmware.bin文件放在新弹出的虚拟优盘上验证功能。 参考资料包括多个相关网站链接。
谢谢,阅读!
一、编程环境搭建
CPE当前支持通过Microsoft MakeCode,circuitpython,Arduino等编程环境开发。 MakeCode适合儿童编程启蒙课程,circuitpython是Adafruit公司主打的编程生态,非常完善和强大,很多功能都封装好,入门学习比较顺滑。Arduino 依仗全球的创客生态,只有你没想到的,没有你做不到的。 因为Circuit Playground Express采用的ATSAMD21微控制器也是一款成熟的产品了,跟国内的ESP32同台竞技好多年,在VSCODE上的platformIO平台上肯定有支持。PIO的底层还是Arduino,但是继承了VSCODE所有资产,做一些复杂的内容也可以从容应对。 根据Circuit Playground Express关键词就可以在PIO上新建一个项目。
如果不出意外,肯定出意外了。 (见下图)初始代码编译测试就报错,百思不得其姐,差点就放弃了。经过几天休养,灵光一动,采用同样芯片的Seeed Xiao ATSAMD21肯定是好用的吧,这么多人用,CPE 是几乎没有人用PIO来玩的,所以有bug也没人发现。于是新建了基于Seeed Xiao ATSAMD21项目,编译顺利通过,但demo代码上传到CPE,却没有反应,这也正常呀,Xiao的引脚跟CPE的管脚设置是不同的。 但给到了一个出路,就是Xiao的支持环境是正常的。
后来根据报错提示,最终确认是vscode -PIO 提供的Adafruit TinyUSB Library有问题(可能是一些低级的错误)用Xiao的支持库所以把Seeed的TinyUSB 驱动拿过来放到对应的目录上,借尸还魂,可以完美解决。还想去给PIO提个意见那啥的,算了,作业都还没完成。 (替换文件夹在附件链接细找)
不足1秒就完成编译上传了,ESP32什么时候享受过这样的待遇,太快了。
任务一、炸街灯光秀
CPE上有10颗灯珠,开始的时候对它还是挺嫌弃的,怎么不搞12颗呢,后来发现就这么10颗灯珠就可以玩出花来了,可以做出很多炫酷的灯效。
下面先来看看灯光秀。
先用一个函数数组灯光效果函数放进去,就像有彩虹灯效、旋转灯效、闪光灯等函数)
构造了“RainbowCycleDemo”类,这个类继承自“Demo”类。“loop”方法能在程序的主循环中频繁被调用。然后,根据“mode”的值,从“ligthList”数组中选择对应的灯光类型函数并调用它,从而实现不同灯光效果的切换展示。“modePress”方法则最终被按键A调用,实现了灯光模式内循环切换。
任务二、环境温度和光线强度监测
光线强度传感器连接到模拟引脚A8。在Arduino编程环境取值在0和1023之间,对于大多数室内光线水平来说,大约300的读数很常见。
温度传感器使用NTC热敏电阻器(村田NCP15XH103F03RC)。虽然它不是全集成温度传感器,但具有线性输出,但可以根据模拟引脚#A9的模拟电压轻松计算出温度。就是数值计算起来比较复杂。温度计算公式:
float Adafruit_CircuitPlayground::temperature(void) {
// Thermistor test
double reading;
reading = analogRead(CPLAY_THERMISTORPIN);
// Serial.print("Thermistor reading: "); Serial.println(reading);
// convert the value to resistance
reading = ((1023.0 * SERIESRESISTOR) / reading);
reading -= SERIESRESISTOR;
// Serial.print("Thermistor resistance: "); Serial.println(reading);
double steinhart;
steinhart = reading / THERMISTORNOMINAL; // (R/Ro)
steinhart = log(steinhart); // ln(R/Ro)
steinhart /= BCOEFFICIENT; // 1/B * ln(R/Ro)
steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15); // + (1/To)
steinhart = 1.0 / steinhart; // Invert
steinhart -= 273.15; // convert to C
return steinhart;
}
我们需要做的主要工作是将光照强度和温度数值映射到灯珠上进行简易显示。
首先,通过CircuitPlayground.lightSensor()获取当前的光照强度值,并存储在变量light中。然后,使用lerp的线性插值函数,把光照量程映射 9号灯珠(初始值)和 5号灯珠(结束值),灯光颜色选择为蓝色。
根据温度设置左侧像素点颜色:类似地,通过CircuitPlayground.temperature()获取当前温度值,并存储在变量tempC中。再次使用lerp函数根据温度值tempC、温度量程映射到映射5号灯珠(初始值)和 0号灯珠(结束值),温度演示颜色选择红色。
任务三、谜之接近检测
void sendIRPulse()
{
for (int i = 0; i < 32; i++)
{
digitalWrite(irTransmitterPin, HIGH);
delayMicroseconds(13);
digitalWrite(irTransmitterPin, LOW);
delayMicroseconds(13);
}
}
void displayDistance(int distance)
{
int ledCount = map(distance, minDistance, safeDistance, 0, 9); // 将距离值映射到0-10的LED数量
Serial.print("Distance: ");
Serial.print(distance);
Serial.print(", LED Count: ");
Serial.println(ledCount);
for (int i = 0; i < 10; i++)
{
if (i < ledCount)
{
CircuitPlayground.setPixelColor(i, 0, 255, 0);
}
else
{
CircuitPlayground.setPixelColor(i, 0);
}
}
}
void checkForIntrusion(int distance)
{
if (distance > SAFE_DISTANCE)
{
Serial.println("Intrusion detected!");
playAlertTone();
}
}
inline void looping()
{
if (millis() - timeElapsed > 300)
{
sendIRPulse();
// CircuitPlayground.irSend.send(MY_PROTOCOL,MY_POWER,MY_BITS);
distance = analogRead(irReceiverPin); // 读取红外传感器的值
if (firstFlag)
{
minDistance = distance;
firstFlag = false;
safeDistance = minDistance + 50;
}
Serial.print("Received distance: ");
Serial.println(distance);
displayDistance(distance);
checkForIntrusion(distance);
timeElapsed = millis();
}
}
这个实验成果与否就取决于这个函数,在读取说明书上的接近传感器引脚的数值之前必须进行的操作,尝试过其他发射方式不成功。
sendIRPulse()函数每次调用时生成一系列32个红外脉冲,每个脉冲持续26微秒,具有50%的占空比(13微秒高电平,13微秒低电平)。通过红外脉冲实现测距的原理主要基于红外线的发射和接收之间的时间差。这种方法通常称为脉冲时间飞行法(Time of Flight, ToF)。然而这里我们需要根据A10的模拟量来大约的判断有物体接近,这不是一种很常见的用法,都是玩具,我觉得也不一定要这么较真呀。
(彩蛋,当我们把手靠近CPE的时候,蜂鸣器会发出biubiu的声音,还会喊“danger,danger”)
进阶任务、不倒翁优雅实现
不倒翁的意思是用CPE来感受重心的变化,利用10颗灯珠通过重心极角指映射到对应点亮灯珠。
不倒翁制作过程
得益于塑壳背后别有用心的预留了一个M4还是多少的螺丝孔,这个穿孔所用螺丝比较容易获取,例如相机底座的固定螺丝也是同款的。在龟苓膏塑料盖中心开一孔让螺丝穿过,边缘也开一个小孔让电池线头穿过,不要太靠塑料盖边缘,这样不美观。然后电池舱藏在龟苓膏罐里头,用些纸皮固定,由于电池比较重,固定电池的位置比其他地方重,造成了重心偏离中心的情况,正好符合不倒翁的工作模式。
virtual void loop() {
static float X, Y, Z;
X = CircuitPlayground.motionX();
Y = CircuitPlayground.motionY();
Z = CircuitPlayground.motionZ();
static int old_x, old_y;
/*滤波*/ //一阶低通滤波器,减少灯光因为加速度传感器的漂移产出的闪烁。
X = old_x * 0.6 + X * 0.4;
old_x = X;
Y = old_y * 0.6 + Y * 0.4;
old_y = Y;
Serial.print("X: "); // 左右倾斜是 x
Serial.print(X);
Serial.print(" Y: "); // 前后倾斜是 y
Serial.print(Y);
Serial.print(" Z: "); // 水平轴向;
Serial.println(Z);
// atan2函数返回的角度范围是-π到π,通过乘以180/PI转换为度,并加上180,使得角度范围变为0到360。
// map函数用于将角度映射到LED索引,确保角度和LED索引之间的对应关系是线性的。
float theta = atan2(Y, X) * 180 / PI + 180; // originally the value range is -pi - pi, add 180 for easy understand.
// in order to understand the relationship between the angle and lights ,we need to watch the change of the r and thera.
Serial.print("theta:");
Serial.println(theta);
int led_point2 = map(theta, 0, 360, 7, 17); // the zero point the Led 7, in order to map the numbers of the leds,7+10 =17 ,return the zero point.
CircuitPlayground.clearPixels(); // Set all pixel colors to 'off'
uint32_t offset = millis() / 100;//等过的渐变速度以100毫秒为单位发生变化。
for (int i = 0; i < 3; i++)
{
CircuitPlayground.strip.setPixelColor((led_point2 + i) % 10, CircuitPlayground.colorWheel((((led_point2 + i) * 256 / 10) + offset) & 255));
}
CircuitPlayground.strip.show();
delay(10);
}
核心关键代码
float theta = atan2(y_,x_ )*180/PI+180; // 计算极角,还原到0-360便于理解。在完成这个实验,我认为最精彩的部分莫过于使用了使用 atan2这个函数可以完美解决atan在x值接近0时不稳定的现象,数学还得好好学。
atan2是功能解析:
一、明确的参数顺序和结果范围
参数顺序明确:atan2(y, x)接收两个参数,先指定纵坐标的值y,再指定横坐标的值x。这种明确的参数顺序使得在调用函数时不容易出错,并且在复杂的计算中更容易理解和追踪参数的来源。
结果范围清晰:该函数返回值的范围是从 -π 到 π。这个固定的范围使得结果具有确定性,方便进行后续的角度计算和处理。无论是在平面几何、图形绘制还是物理模拟等领域,都能准确地确定角度,避免了因角度范围不明确而导致的错误。
二、处理特殊情况准确
处理零值情况:当 x 为零且 y 为正时,atan2 返回 π/2;当 x 为零且 y 为负时,返回 -π/2。这种准确的处理方式在涉及到垂直方向的角度计算时非常有用,避免了因分母为零而产生的计算错误。
处理坐标轴上的情况:当 y 为零时,根据 x 的正负返回 0 或 ±π。这使得在处理与坐标轴平行的情况时,能够准确地确定角度,为图形绘制、向量分析等提供了准确的角度信息。
三、在二维向量和坐标计算中表现出色
二维向量的角度计算:在处理二维向量时,atan2 可以方便地计算向量与 x 轴正方向之间的夹角。这在计算机图形学中非常重要,例如在绘制图形时确定物体的方向、进行旋转操作等。通过 atan2 可以快速准确地计算出向量的角度,从而实现各种图形变换和动画效果。
2. 坐标转换和定位:在地理信息系统、游戏开发等领域,经常需要进行坐标转换和定位。atan2 可以用于计算两点之间的方位角,从而确定物体在平面上的相对位置。例如,在游戏中可以根据玩家和目标的坐标计算出玩家面向目标的角度,以便进行角色的转向和攻击。
难点说明:
int led_point2 = map(theta, 0, 360, 7-17); //
为什么映射都(7-17)?因为0度的时候正好是7号的位置,后面超过10点的灯珠我们用取余的方法,让它们又回到了正常的位置,这是没有实验现象辅助会有点难理解。
创意任务、精彩挂饰
实现非阻塞的播放功能和灯光秀功能的完美融合。用一套的非常规播放逻辑来实现midi音符解码。这里提供了几首midi曲目,有我最爱的《我爱和我的祖国》(又红又专),还有这次特别制作的生日歌,可以在小伙伴生日的时候掏出来,活跃气氛,没有蜡烛,灯光秀顶上,临时救个场也不是不可以的。
Midi 曲目的数据格式:
const char *my_people_my_country = "#,#,A#5,A#5,A#5,A#5,C6,C6,C6,...#”
每个音符使用逗号进行分隔,接下来,函数会根据设定的节拍时间(音调的播放持续时间)判断是否到达了一个节拍点。如果到达了节拍点,函数会解析字符串中的音符,并播放对应的音符。
音符的识别使用了map容器来快速根据音符字符串查询到对应的音符频率数值,具体实现方式查看源码。
std::map<String, int> tones = {
{"C0", 16}, {"C#0", 17}, {"D0", 18},,,,{"A#9", 14917}, {"B9", 15804}};
解析字符串中的音符的逻辑是函数会移动指针到下一个逗号的位置,以准备播放下一个音符。如果还有逗号存在,函数会继续播放下一个音符, 如再未检测到逗号,表明播放结束,歌曲播放结束后会检测是否循环播放。
可用于转换 onlinesequencer.net schematic format 的乐谱
页:onlinesequencer.net
心得体会
本次任务开始的时候觉得项目太简单了,可能学不到啥东西,后来发现这个东西还是非常有趣的。 主要得益于在采购阶段做好了充分的调研工作,买买买我是专业的,给CPE精心配置了一个水晶外壳(塑料的),立马提升了一个档次,灯光秀就更加出彩了,然后花了巨资购买了一条粗犷的挂绳(还是一条typeC充电线)来搭配CPE,这样就可以用一种另类的方式出街啦,成为这条街最亮D仔。
另外,在代码方面,用类把所有以上任务内容用一套代码实现,按键B实现功能切换,按键A实现功能内部模式切换。代码思路是非阻塞式运行(拒绝使用delay),因为简单尝试了freeRTos的移植,好像问题多多,折中通过时间判断完成了midi播放和灯光秀同时进行。 任务中也不乏亮点例如不倒翁功能的实现。
总体来说,任务还是比较轻松愉悦的,这里主要功劳还是CPE背后Adafruit团队的板卡支持库和案例做得非常通俗易懂,没有八股文代码那么晦涩难懂(我没有说idf哈),所以一个板卡成功与否,除了硬件性能,板卡的支持库也是非常重要的。
该项目给今年的夏天留下浓重的色彩,怀着不舍之情,结束了这次活动的学习,让我继续砥砺前行,迎接更加猛烈的风雨!
不足之处就是篇幅有限不能把实验过程的细节一一呈现,喜欢的朋友可以下载代码包中的免编译固件,进行体验测试。
代码包中有firmware.bin 固件,可以验证功能。
而已双击reset键进入bootloader模式,报firmware.bin 文件放在新弹出的虚拟优盘上。
代码包
参考资料:
信息地址
https://github.com/adafruit/Adafruit_CircuitPlayground
https://wiki.seeedstudio.com/Seeeduino-XIAO/
https://learn.adafruit.com/adafruit-circuit-playground-express/overview
https://learn.adafruit.com/circuit-playground-hourglass/basic-hourglass
https://learn.adafruit.com/circuit-playground-bluefruit-brake-light
https://learn.adafruit.com/circuit-playground-bike-light
- 2024-07-23
-
回复了主题帖:
UC8151/IL0373 电子纸显示驱动
好东西
- 2024-02-01
-
回复了主题帖:
【DigiKey创意大赛】多通道微型气相色谱采集单元-作品提交
数字电位器 用了哪个芯片?
-
回复了主题帖:
出色完成任务奖励名单|得捷Follow me 第2期活动
正好去买一个龙年版蓝罐曲奇,嗯,就这么定了了
- 2023-12-21
-
回复了主题帖:
【DigiKey“智造万物,快乐不停”创意大赛】7,TTS功能实现
牛
-
回复了主题帖:
【DigiKey“智造万物,快乐不停”创意大赛】5,在树莓派上搭建YOLOv8
服
- 2023-12-17
-
回复了主题帖:
【得捷Follow me第3期】+ 基于lvgl图形库驱动的多功能桌面摆件(完整项目)
HonestQiao 发表于 2023-12-16 21:22
做的真不错,很有技术含量!
谢谢大佬谬赞
- 2023-12-15
-
回复了主题帖:
【得捷Follow me第3期】+ 基于lvgl图形库驱动的多功能桌面摆件(完整项目)
秦天qintian0303 发表于 2023-12-15 16:41
LVGL的图形效果真不错,明年要接触一下
过几天就明年了,马上学起来。
-
发表了主题帖:
【得捷Follow me第3期】+ 基于lvgl图形库驱动的多功能桌面摆件(完整项目)
本帖最后由 genvex 于 2023-12-15 20:02 编辑
本项目将包括以下内容:
任务一、安装及使用Micropython---(任务1)
Micropython真香定律。
任务二、基于SSD1306移植lvgl ----(任务2)
使用lvgl驱动oled_ssd1036,还很流畅。
任务三、音乐相册---(任务3)
一边播放midi,一边翻相册。
任务四、苹果风时钟---(任务4)
网络同步时间,保证不迟到
任务五、天气预报小站----(任务4)
天气预报站+ gif动画。
五、加速度数据采集---(任务5)
附加内容
一、温湿度监控器
高仿温湿度度传感器,数据还同步写入SD卡。
二、声音频谱分析
声音快速傅立叶分析,声音可视化
三、lvgl手表表盘
高端大气上档次的表盘。
一、安装及使用Micropython---(任务1)
利用Arduino环境会自带esptool工具进行烧录。将bin文件放入到Arduino所在的esptool工具相同的目录下:
下载最新版本固件:https://micropython.org/download/ESP32_GENERIC_C3/
esptool.exe --chip esp32c3 --port COM71 erase_flash
在esptool目录下运行cmd命令行
并运行如下命令:
esptool.exe --chip esp32c3 --port COM71 --baud 921600 --before default_reset --after hard_reset --no-stub write_flash --flash_mode dio --flash_freq 80m 0x0 ESP32_GENERIC_C3-20231005-v1.21.0.bin
其中,COM71需要替换为电脑实际使用的串口号,ESP32_GENERIC_C3-20231005-v1.21.0.bin替换为实际使用的MicroPython固件文件名。
在Thonny打开 XiaoC3。
下面直接使用Thonny连接ESP32C3运行 Hello World程序,看到了新鲜热辣的固件里面的内容,看到了Micorpython也一直在进步,我们祝贺它,赶紧亲上一口,然后88了。
二、基于SSD1306移植lvgl ----(任务2)
Xiao-expansion-board拓展了多个grove接口,同时还配备了一个入门级别的ssd1306_oled,按照常规ssd1306就该使用u8g2这样的功耗上古神器来驱动,但随着很多高性能嵌入式开发板出现了,图形驱动更新迭代的速度也加快了,驱动ssd1306也有了更多选择。我们值得拥有最好的, 本项目中使用LovyanGFX图形驱动库来驱动ssd1306。LovyanGFX库受TFT_eSPI启发,深度改造而来,是很多大众创客的挚爱,它不单只可以驱动常见的LCD屏幕,同时还可以支持一些oled屏幕,使用这个库使得最简单入门级别的ssd1306 oled屏幕也可以共享LCD屏幕才有的作图函数。LVGL(Light and Versatile Graphics Library)的出现给嵌入式开发在最终产品呈现上提供了一种更优雅的解决方案。LVGL相对于传统图形库更适合用于嵌入式系统,它在性能、资源占用和灵活性方面都有一定的优势。本项目的最大的亮点就是使用lvgl驱动了ssd1306,又因lvgl具有非常优越的跨平台可移植性,ssd1306的应用会更加精彩。
{ //进行总线控制的设置。
auto cfg = _bus_instance.config(); // 获取总线设置的结构。
// I2C设置
cfg.i2c_port = 0; // (0 or 1)
cfg.freq_write = 400000; // 写速
cfg.freq_read = 400000; // 收速
cfg.pin_sda = 6; // SDA引脚编号
cfg.pin_scl = 7; // SCL引脚编号
cfg.i2c_addr = 0x3C; // I2C地址
_bus_instance.config(cfg); // 将设定值从吃谧芟呱稀�
_panel_instance.setBus(&_bus_instance); // 把总线设置在面板上。
}
Lvgl驱动的核心刷图函数
static void lvgl_begin(void)
{
// #define DISP_BUF_SIZE (LVGL_HOR_RES * 100)
lcd.init();
lcd.setRotation(2);
// lcd.setBrightness(100);
lv_init();
static lv_disp_draw_buf_t draw_buf;
size_t DISP_BUF_SIZE = sizeof(lv_color_t) * (LVGL_HOR_RES * 20); // best operation.
// static lv_color_t *buf1 = (lv_color_t *)heap_caps_malloc(
// DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_SPIRAM);
// static lv_color_t *buf2 = (lv_color_t *)heap_caps_malloc(
// DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_SPIRAM);
static lv_color_t *buf1 = (lv_color_t *)heap_caps_malloc( // most fast operation.
DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
static lv_color_t *buf2 = (lv_color_t *)heap_caps_malloc(
DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
lv_disp_draw_buf_init(&draw_buf, buf1, buf2, DISP_BUF_SIZE); /*Initialize the display buffer*/
/*-----------------------------------
* Register the display in LVGL
*----------------------------------*/
static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
/*Set up the functions to access to your display*/
/*Set the resolution of the display*/
disp_drv.hor_res = LVGL_HOR_RES;
disp_drv.ver_res = LVGL_VER_RES;
/*Used to copy the buffer's content to the display*/
disp_drv.flush_cb = my_disp_flush;
/*Set a display buffer*/
disp_drv.draw_buf = &draw_buf;
/*Finally register the driver*/
oled = lv_disp_drv_register(&disp_drv);
}
成功移植后运行了benkmark 测试驱动的90fps的优越成绩。
三、音乐相册---(任务3)
定制了一个MIDI音乐解码库,实现了midi音乐自由,实现了背景定时切换、长按按键切换音乐,极大的丰富了改设备的可玩性。 一曲“我和我的祖国”让享受沉浸式的爱国教育,洗涤了我狂躁的心灵。
void MonitorModel::play(int tempo, uint8_t looping, const char *song) // the key function here.
{
static unsigned long music_tick = millis(); // caculate the time.
if (song && this->song != song) // the most import change here.
{
this->setSong(song);
}
if (this->paused)
{ // check if stop by the control
return;
}
if (millis() - music_tick >= tempo) // 结束的时候 超过了 1节拍的时长。
{ // where dose the tempo come from. how low will it play. the tempo is fixed, by the song.
// if (this->p && *(this->p))
if (this->p)
{
char note[4];
sscanf(this->p, "%[^,]", note); // difficult point here.
// Serial.println(note);
this->playNote(note);
this->p = strchr(this->p, ','); // the pointer arrives at the next","
if (this->p)
{ // not empty(still has "," in the sorn list) ,will keep going to next tone.
++(this->p); // next char of the ",",the real freq index string.
}
}
else // outer judgement.
{
if (looping)
{ // repeat the song or not !
this->p = this->song;
}
else
{
this->stopped = 1; // move on till the end of the song
// noTone(this->pinNumber);
speaker.mute();
Serial.println("End");
}
}
music_tick = millis();
}
}
播放音乐关键函数实现了按照指定的节奏播放音乐,并支持暂停、切歌(长按用户按键)和循环播放的功能。为了实现非阻塞的播放功能(非阻塞的意思是可以一边上班一边摸鱼),函数会记录当前时间,用于检查是否需要切换到新的音调。最主要的功能是对midi音频数据序列进行解码。
Midi 曲目的数据格式:
const char *my_people_my_country = "#,#,A#5,A#5,A#5,A#5,C6,C6,C6,...#”
每个音符使用逗号进行分隔,接下来,函数会根据设定的节拍时间(音调的播放持续时间)判断是否到达了一个节拍点。如果到达了节拍点,函数会解析字符串中的音符,并播放对应的音符。
音符的识别使用了map容器来快速根据音符字符串查询到对应的音符频率数值,具体实现方式查看源码。
std::map<String, int> tones = {
{"C0", 16}, {"C#0", 17}, {"D0", 18},,,,{"A#9", 14917}, {"B9", 15804}};
解析字符串中的音符的逻辑是函数会移动指针到下一个逗号的位置,以准备播放下一个音符。如果还有逗号存在,函数会继续播放下一个音符, 如再未检测到逗号,表明播放结束,歌曲播放结束后会检测是否循环播放。
这一套的播放逻辑非常巧妙有趣,用到容器来实现midi音符解码,是一套midi音乐播放的可行解决方案,根据esp32所具备的freeRTOS特性,还可以继续开发同时播放不同音频的功能。
四、苹果风时钟--- (任务4)
开机后实现连接网络进行本地时间同步,苹果偏平风格的时钟图形界面通过lvgl的简单代码划线、圆角矩形、标签功能可以轻松实现,可谓lvgl真正的入门必学操作。时间的刷新和跳动的冒号作为秒针指示是通过两个定时器来完成的,这样时钟就不受其他程序的干扰可以自顾自的跑起来。
void TimerController::onViewLoad() {
// model.init();
// view.create();
view.load(); // setup the lvgl display enviroment.
updateTimer = lv_timer_create( // start the timers.
[](lv_timer_t* timer) {
TimerController* controller = (TimerController *)timer->user_data;
controller->update(); // get the time
},
1000,
this
);
tickTimer = lv_timer_create(
[](lv_timer_t* timer) {
TimerController* controller = (TimerController *)timer->user_data;
controller->updateSegLabel(); // show the second flashing effect.
},
1000,
this
);
}
五、天气预报小站----(任务4)
不能实现时钟和天气预报的开发板不是一块好的开发板
------鲁迅
通过高德地图提供的玩具天气信息服务站点获取到的json格式数据,然后用ArduinoJson库对其进行解析,提取关键信息。
{"status":"1","count":"1","info":"OK","infocode":"10000","lives":[{"province":"广东","city":"佛山市","adcode":"440600","weather":"阴","temperature":"20","winddirection":"北","windpower":"≤3","humidity":"75","reporttime":"2023-11-29 09:00:59","temperature_float":"20.0","humidity_float":"75.0"}]}
void WeatherModel::getWeather()
{
if (WiFi.status() != WL_CONNECTED)
{
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.println("Connecting to WiFi...");
}
}
// 定义一个全局变量作为连接成功的标志
bool connection_success = false;
Serial.println("Connected to WiFi");
HTTPClient http;
http.begin("https://restapi.amap.com/v3/weather/weatherInfo?city=" + cityCode + "key=" + userKey);
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK)
{
String payload = http.getString();
Serial.println(payload);
DynamicJsonDocument doc(1024);
deserializeJson(doc, payload);
this->live = doc["lives"][0];
this->temperature = live["temperature_float"].as<String>();
this->humidity = live["humidity"].as<String>();
this->city = live["city"].as<String>();
this->weather = live["weather"].as<String>();
this->winddirection = live["winddirection"].as<String>();
this->windpower = live["windpower"].as<String>();
// // Serial.print("城市:");
// Serial.println(live["city"].as<String>());
// Serial.print("天气:");
// Serial.println(live["weather"].as<String>());
// Serial.print("温度:");
// Serial.println(live["temperature"].as<String>());
// Serial.print("湿度:");
// Serial.println(live["humidity"].as<String>());
}
else
{
Serial.printf("HTTP request failed with error %d\n", httpCode);
}
http.end();
// WiFi.disconnect();
}
天气数据的展示方式采用文字向上滚动播放形式,在文字旁边放一个gif小动画,为整个版面增添了不少活力(太空人大家也看腻),LVGL的使用gif的优点 在于动画上的实现不需要再逐帧提前页面再转换,直接一个gif转化就可以了,另外 本身自带很多动画过程,使得页面更加生动。
五、加速度数据采集---(任务5)
实现一个简单的加速度数据采集界面,其中包含了一个图表和一个标签。首先创建了一个根对象,并对其进行设置,包括大小、位置和样式,创建了一个图表对象,用于显示加速度传感器的数据,并提供了图表和标签的布局和样式设置。
void MpuView::create()
{
/** screen */
lv_obj_t *root = lv_obj_create(NULL);
// lv_obj_add_style(ui.root, &style_scr, 0);
// lv_obj_set_flex_flow(root, LV_FLEX_FLOW_ROW); // arrange as row.
lv_obj_clear_flag(root, LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_size(root, LV_HOR_RES, LV_VER_RES);
lv_obj_center(root);
lv_obj_set_style_pad_all(root, 0, 0);
lv_obj_set_style_bg_color(root, lv_color_black(), 0);
lv_obj_set_style_bg_opa(root, LV_OPA_100, 0);
ui.root = root;
// lv_obj_set_flex_grow(ui.city, 1);
static lv_style_t style;
lv_style_init(&style);
/*Make a gradient*/
lv_style_set_bg_color(&style, lv_color_black());
// lv_style_set_border_opa(&style, LV_OPA_100);
lv_style_set_bg_opa(&style, LV_OPA_100);
lv_obj_t *chart = lv_chart_create(root);
// lv_obj_remove_style_all(game_station);
lv_obj_set_size(chart, LV_PCT(100), LV_PCT(80));
lv_obj_align(chart, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_obj_add_style(chart, &style, 0);
// lv_obj_move_background(game_station);
// lv_obj_clear_flag(chart, LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_CLICKABLE);
// lv_obj_set_style_pad_all(chart, 0, 0);
lv_obj_set_style_border_color(chart, lv_color_black(), 0);
lv_obj_set_style_radius(chart, 0, 0);
lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_Y, 5, 3, 5, 2, true, 10);
lv_chart_set_point_count(chart, 100);
lv_chart_set_div_line_count(chart, 0, 0);
ui.chart = chart;
lv_chart_set_type(chart, LV_CHART_TYPE_LINE); /*Show lines and points too*/
lv_obj_set_style_size(chart, 0, LV_PART_INDICATOR);
/*Add two data series*/
ser1 = lv_chart_add_series(chart, lv_color_white(), LV_CHART_AXIS_PRIMARY_Y);
ser2 = lv_chart_add_series(chart, lv_color_white(), LV_CHART_AXIS_SECONDARY_Y);
lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, -200,200);
lv_chart_set_range(chart,LV_CHART_AXIS_SECONDARY_Y,-200,200);
lv_obj_t *labelaccX = lv_label_create(root);
lv_obj_set_size(labelaccX, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_label_set_text_fmt(labelaccX, "X:%.2f,Y:%.2f", 0.25, 0.25);
lv_obj_align(labelaccX, LV_ALIGN_TOP_LEFT, 0, 0);
lv_obj_set_style_text_color(labelaccX, lv_color_white(), 0);
lv_obj_set_style_text_font(labelaccX, &lv_font_montserrat_16, 0);
ui.labelaccX = labelaccX;
}
使用到的传感器是MPU6886 加速计
附加内容
一、温湿度监控器--
在视图加载时初始化定时器,并设置定时器的回调函数,以便定期执行相关操作。其中包括刷新数据、更新环境信息和将数据写入 SD 卡等。updateTimer 定时器每隔 100 毫秒触发一次回调函数,用于刷新屏幕上显示数据。updateTH 定时器每隔 500 毫秒触发一次回调函数,用于更新环境数据。dataWriteTimer 定时器每隔 1000 毫秒触发一次回调函数,用于将数据写入 SD 卡。
void EnviromentController::onViewLoad() {
// model.init(); // will affect the program, if it load many times.
// view.create();
view.load();
updateTimer = lv_timer_create(
[](lv_timer_t* timer) {
EnviromentController* controller = (EnviromentController *)timer->user_data;
controller->flashData();
},
100,
this
);
updateTH = lv_timer_create(
[](lv_timer_t* timer) {
EnviromentController* controller = (EnviromentController *)timer->user_data;
controller->model.dataUpdate();
},
500,
this
);
}
环境监测程序框架图
成功将温湿度数据写入SD卡。
(2)声音可视化
声音可视化的主要内容是将声音信号分解成各种频率的成分,通过傅里叶变换计算得到浮点型数组,然后根据条件判断更新柱状图的值和峰值,采用一阶滞后的平滑处理,使得柱状图的变化更加平稳。图形的绘制关键环节在于把频谱对象作为一个容器进行绘制,绘图没有采用 lvgl常规的使用预设的作图函数,而是采用底层的绘图方法。
static void spectrum_draw_event_cb(lv_event_t *e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_DRAW_POST) {
lv_obj_t *obj = lv_event_get_target(e);
lv_draw_ctx_t *draw_ctx = lv_event_get_draw_ctx(e);
lv_draw_rect_dsc_t draw_rect_dsc;
lv_draw_rect_dsc_init(&draw_rect_dsc);
draw_rect_dsc.bg_opa = LV_OPA_100;
draw_rect_dsc.bg_color = lv_color_white();
lv_draw_line_dsc_t draw_line_dsc;
lv_draw_line_dsc_init(&draw_line_dsc);
draw_line_dsc.width = 2;
draw_line_dsc.color = lv_color_white();
for (int i = 0; i < SAMPLE_SIZE; i++) {
lv_area_t _rect;
_rect.x1 = i * x_step;
_rect.x2 = i * x_step + 3;
_rect.y1 = CENTER_Y - int(bar_chart[i] / 2);
_rect.y2 = CENTER_Y + int(bar_chart[i] / 2);
lv_draw_rect(draw_ctx, &draw_rect_dsc, &_rect);
lv_point_t above_line[2];
/* upside line always 2 px above the bar */
above_line[0].x = i * x_step;
above_line[0].y = CENTER_Y - int(bar_chart_peaks[i] / 2) - 2;
above_line[1].x = i * x_step + 3;
above_line[1].y = CENTER_Y - int(bar_chart_peaks[i] / 2) - 2;
lv_draw_line(draw_ctx, &draw_line_dsc, &above_line[0],
&above_line[1]);
lv_point_t blow_line[2];
/* under line always 2 px below the bar */
blow_line[0].x = i * x_step;
blow_line[0].y = CENTER_Y + int(bar_chart_peaks[i] / 2) + 2;
blow_line[1].x = i * x_step + 3;
blow_line[1].y = CENTER_Y + int(bar_chart_peaks[i] / 2) + 2;
lv_draw_line(draw_ctx, &draw_line_dsc, &blow_line[0],
&blow_line[1]);
}
}
}
我们采用一个振幅更新函数(bar_value_update)来处理频谱和屏幕大小适配的工作,它接受一个傅里叶变换计算得到的浮点型数组mag,循环遍历mag数组的每隔两个元素,计算它们的平均值ave,并将其与窗口高度进行比较得到柱状图的值。整体逻辑是计算加权平均值,然后根据条件判断,更新柱状图的值和峰值,并且采用了一阶滞后的平滑处理,使得柱状图的变化更加平稳。而图形的绘制关键环节在于把频谱对象作为一个容器进行绘制(spectrum_draw_event_cb),绘图没有采用lvgl常规的使用预设的作图函数,而是采用底层的绘图方法。绘图实际发生在LV_EVENT_DRAW_POST(绘图结束后)事件,会对频谱对象进行绘制操作。
整个过程中使用了绘制矩形(lv_draw_rect)、绘制线条(lv_draw_line)机制等。矩形图代表瞬时的频谱强度,线条用于代表频谱峰值的滞后响应,由于线条的宽度是2 像素,所以看起来也是一个小长方体。它通过循环遍历一个大小为频谱分析结果数据SAMPLE_SIZE的数组,绘制矩形和两条线条,其中bar_chart和bar_chart_peaks是用于确定柱状图高度的数据数组。快速地在spectrum_obj对象上绘制柱状图,并通过柱状图的高度数据进行实时更新。即使是在非黑即白的Oled屏上也能够响应及时,产生了良好的动态效果。
经过 FFT解析的音乐就像是一幅绚丽多彩的音乐画卷,它将原本复杂的声音信号分解成各种频率的成分,音乐呈现出细腻而有序的频谱,其内在结构和情感深度被剖析得清晰可见,歌曲分解成了千百种细微的声音色彩,每一个频谱点都像是音乐的灵魂之窗,让音乐中各种音调、节奏和情感以细腻而深刻的方式展现,让人们仿佛能够聆听到音符之间跳动的光芒,感受到音乐灵魂的律动与魅力。我们可以感受到音符的跳跃、旋律的起伏、以及音乐整体的魅力,交织出令人陶醉的音乐画面,我们可以看到音符和音阶之间独特的关系,感受到音乐中隐藏的无限可能性,仿佛打开了音乐的魔法之匣,使我们得以窥见音乐中隐藏的美妙之处。
(3)圆屏功能展示
完成了圆屏的lvgl驱动和手表功能,效果炸裂。
void showCurrentTime(lv_timer_t *timer)
{
struct tm info;
getLocalTime(&info);
lv_img_set_angle(img_hour, (info.tm_hour * 300 + info.tm_min * 5) % 3600);
lv_img_set_angle(img_minute, info.tm_min * 60 + info.tm_sec);
lv_img_set_angle(img_second, info.tm_sec * 60);
#if smooth
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, img_second);
lv_anim_set_exec_cb(&a, sec_img_cb);
lv_anim_set_values(&a, (info.tm_sec * 60) % 3600,
(info.tm_sec + 1) * 60);
lv_anim_set_time(&a, 1000);
lv_anim_start(&a);
#endif
}
void setup()
{
Serial.begin(115200); // prepare for possible serial debug
Serial.println("XIAO round screen - LVGL_Arduino");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.println("Connecting to WiFi...");
}
configTime(GMT_OFFSET_SEC, DAY_LIGHT_OFFSET_SEC, NTP_SERVER1, NTP_SERVER2);
struct tm timeinfo;
if (!getLocalTime(&timeinfo))
{
Serial.println("No time available (yet)");
return;
}
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
lv_init();
lv_xiao_disp_init();
lv_xiao_touch_init();
tf.init();
// tf.readFile("/file.txt");
// lv_fs_if_init();
lv_fs_test();
lv_obj_t *gif = lv_gif_create(lv_scr_act());
lv_gif_set_src(gif, &fighters);
lv_obj_center(gif);
lv_obj_t *connecting = lv_label_create(gif);
lv_label_set_text(connecting, "Connecting to WiFi...");
lv_obj_center(connecting);
long n = 3000;
while (n--)
{
lv_timer_handler();
}
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(10);
lv_timer_handler();
Serial.println("Connecting to WiFi...");
}
bg = lv_img_create(lv_scr_act());
lv_img_set_src(bg, &watch_bg);
lv_img_set_zoom(bg, 310);
lv_obj_center(bg);
img_hour = lv_img_create(bg);
lv_img_set_src(img_hour, &hour);
lv_obj_set_align(img_hour, LV_ALIGN_CENTER);
lv_img_set_zoom(img_hour, 310);
lv_img_set_antialias(img_hour, true);
img_minute = lv_img_create(bg);
lv_img_set_src(img_minute, &minute);
lv_img_set_zoom(img_minute, 310);
lv_obj_set_align(img_minute, LV_ALIGN_CENTER);
// lv_img_set_antialias(img_minute,true);
img_second = lv_img_create(bg);
lv_img_set_src(img_second, &second);
lv_img_set_zoom(img_second, 310);
lv_obj_set_align(img_second, LV_ALIGN_CENTER);
struct tm info;
getLocalTime(&info);
lv_img_set_angle(img_hour, (info.tm_hour * 300 + info.tm_min * 5) % 3600);
lv_img_set_angle(img_minute, info.tm_min * 60 + info.tm_sec);
lv_img_set_angle(img_second, info.tm_sec * 60);
timer1 = lv_timer_create(showCurrentTime, 1000, NULL);
}
全文总结:
本文的创新点和亮点包括:
(1)使用LovyanGFX图形驱动库驱动SSD1306,实现了非阻塞的音频播放,以及将温湿度数据写入SD卡。
(2)声音可视化和光照亮度监测系统的实现,使得设备具有更高的实用性和趣味性。
(3)音乐相册功能的实现,通过定制MIDI音乐解码库,实现了背景定时切换、长按按键切换音乐等功能,丰富了设备的可玩性。
(4)天气预报小站的实现,通过高德地图提供的天气信息服务站点获取到的json格式数据,然后用ArduinoJson库对其进行解析,提取关键信息,实现了当天天气预报滚动播放。
(同时引发的问题是 玩得太投入了,导致大卷赛 还没开始工作,可能导致翻车!!!)
(代码自取)
主任务源码
声音频谱分析
圆屏表盘
参考资料:
https://micropython.org/download/ESP32_GENERIC_C3/
https://wiki.seeedstudio.com/Seeeduino-XIAO-Expansion-Board/
https://wiki.seeedstudio.com/get_start_round_display/
https://www.eeworld.com.cn/huodong/digikey_follow_me/
https://www.espressif.com.cn/en/support/download/other-tools
https://www.seeedstudio.com/XIAO-p-4812.html
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json
-
加入了学习《followme3——基于lvgl多功能摆件的设计》,观看 followme3完整介绍内容
-
加入了学习《followme第三期作业》,观看 followme第3期作业
- 2023-12-14
-
上传了资料:
followme3 主任务
-
上传了资料:
Seeed圆屏的lvgl驱动和手表功能
-
上传了资料:
xiaoC3_S3声音频谱分析
-
加入了学习《Follow me第二期任务展示视频》,观看 Follow me第二期任务展示视频
- 2023-10-29
-
回复了主题帖:
【DigiKey“智造万物,快乐不停”创意大赛】 物料开箱 ---SenseCAP Indicator 赏析
dql2016 发表于 2023-10-29 13:55
针不错,内置电池吗
米有的,摆件
- 2023-10-28
-
发表了主题帖:
【DigiKey“智造万物,快乐不停”创意大赛】 物料开箱 ---SenseCAP Indicator 赏析
本帖最后由 genvex 于 2023-10-28 15:44 编辑
这次 【DigiKey“智造万物,快乐不停”创意大赛】,第二批审核通过,公告说带有电池的设备,无法发货,所以在选择设备上伤透了脑筋,因为dJ网络的页面嘛,跟我们习惯的某宝,某东的感受差太远,不过买买买的乐趣 也是参加活动的一部分。
今天我来晒个单:
SenseCAP Indicator是一款由Seeed Technology公司开发的智能传感器,旨在服务智慧农业、智慧城市、智慧林业等领域的工业物联网需求,以及其他环境感知场景。SenseCAP Indicator使用ESP32和RP2040两种微控制器驱动,支持Wi-Fi/BLE/LoRa通信。配备ESP32和RP2040微控制器,支持Wi-Fi/BLE/LoRa通信,可以轻松连接到具有丰富gpio的其他外设。
一、核心功能
双MCU和丰富的GPIO:配备强大的ESP32S3和RP2040双MCU以及 两个Grove传感器接口,这个两个接口可以同时连接到这个两个MCU上,能不能同时读取就不知道了,但理解上,最好还是别同时一起去工作。我够买的这个版本没有集成温湿度、二氧化碳传感器,但是集成了Semtech SX1262 LoRa®芯片,那么就是说我的这个装备可以实现远程传输数据的能力。
让人好奇的是这两个MCU之间是怎么传输数据的? SP32和RP2040使用串口通信,采用COBS通信协议。就是经过特殊封装的串口来传输数据的,那么传输的速度不会非常大,其中ESP32S3是连接在高清屏幕上的,它负责刷屏,那么副mcu可以专门用来采集数据、加工数据,然后后将处理好的结果发送给esp32S3,虽然平时我们只要其中一个MCU就可以玩得飞起,那么现在有两个高性能的MCU同时可以使用,那么给我们的想象空间将会很大。
二、SenseCAP Indicator内置的液晶显示屏。
这款产品最惊艳的地方就是它有个4寸的高清液晶显示屏(ST7701 module) 。屏幕的效果跟手机屏幕一样的感受,屏幕是硬玻璃,配合触摸屏使用非常流畅顺滑,这是我见过屏幕显示效果最好的开发板没有之一。 可以说,我就是奔着它的这块屏幕来的。
三、多种安装方式:
外壳使用IP66等级和封装在抗紫外线的情况下,以确保坚固耐用,在户外应用,可以即时显示监测到的环境参数数值,无需连接到其他设备或平台。可以通过不同的安装方式灵活部署,例如壁挂、固定底座或放置在桌面上等。这样用户可以根据需要选择最适合的安装方式。
四、开发环境:
我们可以分别对两个MCU进行编程,非常神奇的地方是当你用typec线接入电脑后,会出现两个串口设备,通过选择不同的串口来分别对这两个MCU进行编程。目前ESP32S3核提供的是Espressif IDF5.0+LVGL的编程环境,Arduino的编程环境还差触摸屏还没有大神来驱动。
它使用了PCA9535芯片的I2C IO扩展模块,屏幕通过CS和RESET两个引脚连接到ESP32S3微控制器,并与PCA9535 I2C扩展器相连,RP2040通过ESP32S3的UART接口的引脚20和引脚19连接。因此,如果将Sensecap指示器插入计算机,将看到两个串行端口,一个用于RP2040,另一个用于ESP32S3。带有信息USB-SERIAL CH340的端口是与ESP32S3连接的端口。
因为它的屏幕好些管脚是接到一个IO扩展IC上的,所以不是那么好驱动。 RP2040则可以简单使用Arduino进行编程。
小结:
这么看来设备本身的性能非常出色,就是价格稍微贵了些,但是基于这么漂亮的屏幕和外壳,这个价格还是正常合理的。由于国内还没有大范围推广,感觉我是第一个吃螃蟹的人。
不足之处,就是没有声音传感器,没有喇叭,没有办法开发语音类的项目,有喇叭就可以播放MP3啦,希望Seeed后续可以继续升级这款产品。
那么这么好的一手牌,会不会被我打烂呢!(一边码字,一边收拾包袱,随时准备跑路)