- 2024-12-06
-
发表了主题帖:
【Follow me第二季第4期】任务三:学习并调试PDM麦克风,通过串口打印收音数据和音频
本帖最后由 mingzhe123 于 2024-12-6 23:05 编辑
1、麦克风简介
自19世纪末现代麦克风引入以来,由于其在消费、医疗和汽车应用中的广泛和不断发展的应用,麦克风取得了巨大的进步。
Johann Philipp Reis 和 Alexander Graham Bell 被认为是第一批麦克风的发明者。这些早期原型产生的电信号幅度低,频率范围有限。因此,声音质量很低,设备几乎无法重现清晰的语音。功能性麦克风的发展归功于 Thomas Edison、Emile Berliner 和 David Edward Hughes。他们的碳麦克风后来主导了市场。Edison 和 Berliner 分别宣布了他们的发明(当时称为发射器),并在美国争夺专利权。在英国,Hughes 向伦敦皇家学会展示了类似的设备,并创造了“麦克风”一词,尽管他没有申请专利。电信行业迅速认识到麦克风在其系统中的潜力,并推动了技术创新。碳麦克风的各种变体在20世纪20年代到80年代的电话中被广泛使用。随着电信和音乐产业的快速发展,其他形式的转换机制不断被开发和应用于电信系统。电容麦克风于1916年引入,目前在市场上占主导地位。最新的种类是基于光学和自旋电子的麦克风。
麦克风中使用了多种转换机制来将声波转换为电信号,如电磁(电动)、压阻、压电、光学、自旋电子和电容。
第一种机制是电磁麦克风。它由一个在固定磁场中移动的线圈组成,产生交流电,即电输出。线圈连接到根据声学输入振动的薄膜片。碳麦克风和带状麦克风是这种类型的变体。电磁麦克风由于重的膜片和线圈的缓慢振动速度而存在灵敏度问题。
第二种机制称为压阻麦克风。它的工作原理如下:在声学膜片上有四个电阻连接在惠斯登电桥中。当声波引起的压力作用于膜片时,膜片发生偏转。相应地,四个电阻的应力相关值也发生变化。惠斯登电桥根据这些电阻值之间的差异产生输出电压。压阻麦克风的缺点是动态范围和灵敏度相对较低,但仍然被用于许多应用中。
第三种机制称为压电麦克风。它利用压电原理将声波的机械振动转换为电信号。为此,研究人员通常使用氮化铝和氧化锌作为制造薄膜片的压电材料。由于这种材料的刚性,这种麦克风最初用于放大乐器的接触振动。由于其先进性能,它具有更多样化的应用。
第四种机制是光学或光纤麦克风,它使用光源照射薄膜片。光检测器用于检测光的强度和波长。当声波使膜片振动时,记录并进一步转换为电信号的原始光源和反射光源之间的差异。光学麦克风的主要优点是不受电噪声和电磁干扰的影响。缺点是检测系统的复杂性,导致成本较高。它在医疗应用和高保真录音中有着利基市场。
第五种机制是自旋电子麦克风,基于磁阻转换。它旨在解决困扰压阻麦克风的低灵敏度问题。自旋应变计传感器(SGS)取代了声学膜片顶部的电阻。这种自旋SGS基于磁隧道结效应,具有很高的灵敏度。
第六种机制称为电容麦克风,它通过可移动和固定板之间的电容变化来工作。薄膜片代表可移动板。声波使其振动,从而成比例地改变电容值。需要一个电压源来将板偏置到固定电压。驻极体麦克风是一种特殊类型的电容麦克风,通过驻极材料在其板之间保持永久偏置。由于其良好的性能、低成本和易于制造,驻极体麦克风成为最具商业制造的麦克风类型,在其生产高峰期每年生产超过十亿个单位。
麦克风制造中的主要技术进步是微机电系统(MEMS)电容麦克风的引入。MEMS麦克风的关键部分是一个微型硅芯片,它结合了声学传感器和处理电子设备。MEMS麦克风的优点包括尺寸小、成本低、性能高、制造工艺成熟、环境耐受性好等。因此,它们已成为智能手机、平板电脑、智能手表、助听器、可穿戴设备和物联网(IoT)设备的理想选择。
2、MEMS麦克风
MEMS麦克风的核心是一个微型电容器,由一个可移动的振膜和一个固定的背板组成。振膜对声波产生的压力敏感,并且在声波的作用下发生振动。背板通常具有多个小孔,以允许空气通过并减少气流对振膜的阻力。当振膜振动时,振膜与背板之间的距离变化,从而导致电容值的变化。这种电容变化被转换为电信号,并通过电子电路进行处理和放大。下图显示了典型MEMS电容麦克风的结构示意图。
具体来说,MEMS电容麦克风通常包含以下几个主要部分:
振膜:通常由硅、氮化硅或聚合物材料制成,具有良好的机械强度和灵敏度。
背板:通常是一个带有孔洞的刚性结构,允许声波通过,并且通常由硅或金属材料制成。
腔室:提供振膜和背板之间的空气间隙,影响麦克风的频率响应和灵敏度。
偏置电压:施加在振膜和背板之间,用于检测电容变化。
那么MEMS 麦克风如何工作?
其实,模拟与数字麦克风输出。所有麦克风都以模拟音频信号开始,并使用前置放大器(有时称为缓冲器)将音频提升到可用但仍然很低的水平。
对于模拟输出的MEMS麦克风,将增强信号直接发送到输出。有两种输出类型——单端和差分。差分系统有两个输出,彼此相位差 180 度。模拟麦克风有三个或四个引脚:电源、公共(接地)和一个或两个输出,具体取决于输出是单端还是差分。
电源始终由单个正电源提供。这会在输出端产生直流偏移,该偏移应由电容器去耦,如下图所示。
电源电压通常在 1.8 和 3.5 V 之间,典型的直流偏移在 0.8 到 1.5 V 之间。
而对于数字输出MEMS麦克风。MEMS 麦克风执行模数 (A/D) 转换,将放大的模拟音频信号转换为数字信号。大多数使用delta-sigma 转换来产生 PDM(脉冲密度调制)输出,如图 3下图所示。
脉冲密度调制。当音频信号较高时,高脉冲(蓝色)具有较高的密度。
脉冲密度(即逻辑高脉冲的百分比)与电压成正比。这不是你通常认为的数字,因为没有创建数字词,只是脉冲。尽管通常使用微处理器程序或音频 CODEC(编码器/解码器),但只需将脉冲流通过低通滤波器即可对其进行解码。
大多数数字输出 MEMS 麦克风有五个引脚,如图 4下图所示:
电源
共同点)
输出
时钟输入
L/R(左/右)选择
L/R 选择如何工作?如果连接为高电平(左),则 A/D 输出在时钟变高后发送。如果低,则数据跟随低时钟转换。这样,左右输出可以通过同一条数据线发送。
一些麦克风使用最初由飞利浦半导体(现为恩智浦半导体)创建的I2S(Inter-IC Sound)标准。与 PDM 一样,它有一个时钟和 L/R 选择输入,但输出是数字字,而不是调制脉冲。同样,与 PDM 一样,它可以通过微处理器软件或 I2S CODEC 解码。此外,它不能被低通滤波器解码。
那么什么是I2S标准?什么是PDM标准?
I2S——Inter-IC Sound,Integrated Interchip Sound,又称集成电路内置音频总线,是飞利浦公司为数字音频设备之间的音频数据传输而制定的一种总线标准。采用了独立的导线传输时钟与数据信号的设计,通过将数据和时钟信号分离,避免了因时差诱发的失真。
1. I2S总线构成和连接方式
支持全双工和半双工模式;支持主、从模式;
I2S总线一般由1根系统时钟线和3根信号线组成:
MCLK:称为主时钟,也叫系统时钟(Sys Clock),一般为了使系统间能够更好地同步时增加MCLK。
SCLK(BCLK):串行时钟SCLK,也叫位时钟(BCLK),即对应数字音频的每一位数据,SCLK都有1一个脉冲。
LRCK:帧时钟LRCK,也称(WS),用于切换左右声道的数据。
SDATA(SD):就是用二进制补码表示的音频数据。最高位拥有固定的位置,而最低位的位置则是依赖于数据的有效位。
2. 采样率,LRCK、BCLK、MCLK频率对应关系
I2S的比特率即确定了在I2S数据线上的数据流和I2S的时钟信号频率。
I2S比特率 = 每个声道的比特数 × 声道数目 × 音频采样频率
采样:
就是间隔一定得时间对模拟信号做一个取样。比如间隔0.001秒采一个样,这样采样频率(Fs)就是:
Fs = 1 / 0.001 = 1KHz
要多大采样率才可以还原一个模拟信号,这个涉及到数字信号处理原理(奈奎斯特定理)。采样频率Fs大于信号中最高频率fmax 的2倍时(Fs > 2fmax ),采样之后的数字信号完整地保留了原始信号中的信息。
人耳听到的音频范围是20Hz~20KHz,如果对人耳听力范围内的音频数字化,fmax = 20KHz,Fs > 40KHz的采样率44.1KHz就是高保真了。它能覆盖人耳听到的所有声音。
MLCK:
主时钟(独立映射),在I2S配置为主模式,寄存器SPI_I2SPR的MCKOE位为“1”时,作为输出额外的时钟信号引脚使用。输出时钟信号的频率预先设置为256 × Fs,其中Fs是音频信号的采样频率。
MCLK的频率 = 128或者256或者512 * 采样频率
SCLK:
SCLK的频率 = 声道数 * 采样频率 * 采样位数
LRCK:
LRCK的频率 = 采样频率
3. I2S、左对齐、右对齐的含义
随着技术的发展,在统一的I2S硬件接口下,出现了多种不同的I2S数据格式,可分为:
①I2S Philips标准 ②左对齐(MSB)标准 ③右对齐(LSB,也叫日本格式、普通格式)标准
PDM(Pulse Density Modulation)
特点:
PDM是一种脉冲密度调制技术,通过高频脉冲表示音频信号的密度,常用于低功耗、低成本的音频设备,如MEMS麦克风。PDM信号在接收端需要通过滤波和解码才能转换为标准的PCM格式音频数据。
信号线说明:
PDM是一种最常见的数字麦克风接口。这种接口允许两个麦克风共享一个公共的时钟与数据线
时钟线(CLK):
提供用于调制和解调的基准时钟,PDM数据是同步于这个时钟进行传输的。
作用:保证PDM信号的时序同步。例如,在智能手机中,时钟线驱动麦克风和音频处理器的同步工作,确保音频数据的准确传输。
数据线(DATA):
传输密度调制的脉冲信号,表示音频信号的高低频成分。
作用:传输实际的PDM音频数据。例如,在便携式录音设备中,数据线传输从麦克风采集到的高频脉冲信号,代表音频信号的瞬时值。
应用举例:
智能手机:PDM接口用于连接低功耗MEMS麦克风,音频信号通过时钟线和数据线传输到音频处理器,进行语音识别和通话,适合长时间待机的智能手机应用。
3、MP34DT06JTR
在Arduino® Nano RP2040 Connect中使用的麦克风型号为MP34DT06JTR。MP34DT06J MEMS麦克风是一款超紧凑、低功耗、全向、数字化的MEMS音频传感器,采用电容传感元件和IC接口。这种低失真数字麦克风提供64dB信噪比(SNR)和-26dBFS±1dB灵敏度。该麦克风的电容感测元件能够检测声波,并使用专门的硅微加工工艺制造。MP34DT06J麦克风的特点是采用CMOS工艺制造的IC接口,允许并提供PDM格式的外部数字信号。
MP34DT06J麦克风工作在-40°C至85°C的扩展工作温度范围内,顶部端口,smd兼容,emi屏蔽HCLGA封装。典型的应用包括移动终端、笔记本电脑、便携式媒体播放器、VoIP、语音识别、A/V电子学习设备、游戏/虚拟现实输入设备、数字静止/视频摄像机和防盗系统。
在RP2040开发板中,其硬件连接如下图所示:
4、驱动程序
代码的核心功能:
static const char channels = 1;
- 设置输出通道的数量。
static const int frequency = 20000;
- 将采样频率设置为 20 KHz。
short sampleBuffer[512]
- 缓冲区读取样本,每个样本为 16 位。
while (!Serial)
- 在打开串行监视器之前阻止程序运行。
PDM.begin(channels, frequency)
- 初始化 PDM 库。
Serial.print(sampleBuffer[i])
- 将样本打印到串行监视器/绘图仪。
#include <PDM.h>
static const char channels = 1;
static const int frequency = 20000;
short sampleBuffer[512];
volatile int samplesRead;
void setup() {
Serial.begin(115200);
while (!Serial);
PDM.onReceive(onPDMdata);
if (!PDM.begin(channels, frequency)) {
Serial.println("Failed to start PDM!");
while (1);
}
}
void loop() {
if (samplesRead) {
for (int i = 0; i < samplesRead; i++) {
if (channels == 2) {
Serial.print("L:");
Serial.print(sampleBuffer[i]);
Serial.print(" R:");
i++;
}
Serial.println(sampleBuffer);
}
samplesRead = 0;
}
}
void onPDMdata() {
int bytesAvailable = PDM.available();
PDM.read(sampleBuffer, bytesAvailable);
samplesRead = bytesAvailable / 2;
}
查看串口打印的波形数据
将代码下载到 Arduino Nano RP2040 Connect。
打开 串口监视器,设置波特率为 115200。
使用 Arduino IDE 串口绘图器(Serial Plotter),观察音频数据的波形变化,如下图所示,下图为1KHz正弦波声音产生的波形数据。
5、总结
Arundio自带的函数库,大大降低了开发难度,我们只需要调用相应的API函数即可配置麦克风的采样频率,配合开发板的多种数据传输功能,可以实现很多有意思的功能。
6、演示视频[localvideo]4a060be3dd22b0776e791986da6842c2[/localvideo]
- 2024-12-01
-
回复了主题帖:
【Follow me第二季第4期】任务二:学习IMU基础知识,调试IMU传感器,通过串口打印...
视频中应该是调用意法半导体官方的arduino库https://github.com/stm32duino
-
发表了日志:
【Follow me第二季第4期】任务二:学习IMU基础知识,调试IMU传感器,通过串口打印...
-
发表了主题帖:
【Follow me第二季第4期】任务二:学习IMU基础知识,调试IMU传感器,通过串口打印...
本帖最后由 mingzhe123 于 2024-12-1 12:16 编辑
IMU原理及选型介绍
所谓IMU(Inertial Measurement Unit)是指惯性测量单元,利用惯性的变化测量物体加速运动和旋转。其中目前主流的惯性测量器件是MEMS(微机电)传感器,它将用于机械结构缩小至纳米尺度,可测量机器人加速度与旋转角速度。后面讨论的内容应该可以归为捷联式惯导系统(Strapdown Inertial Navigation System),即测量单元直接安装于被测物体上,与平台式惯导系统区别开来。
需要注意的是,IMU或者说六轴(6Dof)传感器讨论的是三轴加速度计和三轴角速度计(俗称陀螺仪),与AHRS(航姿参考系)或者说9轴(9Dof)传感器是有区别的,后者增加了磁力计,以地磁场作为参考。但是在地面机器人运动并且夹杂大量电机磁干扰的情况下,引入微弱的地磁作为绝对航向并不是一个好主意。
原理
当物体运动的惯性改变时,根据牛顿第二定律实际上物体内部会有相对的作用力,我们可以想象一个加速度计的结构如上图,在整个系统加速或减速时弹簧会有不同程度的伸长或压缩,通过测量弹簧的所连接的物体的位移可以测量出实际加速度的大小,当然这其中也包含重力加速度。在目前MEMS工艺制造的传感器中这些结构都被缩得非常小,以至于测量的位置可以视为被缩在同一点上。
而角速度计的原理类似,但是测量原理是令质量块不断在轴的径向上产生移动,探测其切向偏移测量科氏力大小,从而推出角速度的大小。
性能
通常情况下,这两种传感器都有一般传感器所拥有的性能指标,例如不论是机械制造的误差还是A/D采样带来的偏置,满刻度误差,以及采样数据的噪声还有抗震性能,当然这些也会受到温度和输入电压的影响。有些误差的校正会在后续的校准中提到。
IMU参数
作为一种电子元器件,基本的参数都可以在其数据手册(datasheet)中查到,这些参数说明了IMU的性能范围,可以为选型提供参考,这里列举一些常用的参数
量程和灵敏度(sensitivity):代表最大角速度或加速度范围和取样精度,通常有数字接口的IMU都会有这项
敏感度温漂(sensitivity change over temperature/ sensitivity temperature drift):敏感度随温度的改变,通常用百分比或者FS/K(满刻度每度)
零偏(zero-rate offset/level):IMU中尤其针对角速度计的一个重要参数,它代表了角速度计/加速计在静止时仍然输出的大小,是后续IMU校准中重点讨论的参数。我们希望它越小越好,以避免积分时带来的累计误差。通常目前IMU给出的典型值都是在1deg/s以上。
温度漂移(zero-rate offset change over temperature):零偏随温度改变的大小,同样是角速度计的重要参数,越小代表IMU的零偏随温度影响越小。
噪声系数(noise):代表输出噪声与带宽的关系,单位一般为(ug/√Hz或者dps/√Hz),直观的说就是影响对真实信号的辨识程度。
交叉轴敏感度(cross-axis sensitivity):通常情况下三个轴的输出应该都是独立的,但实际上可能出现跨轴影响输出的现象,这个参数就是用于衡量各轴之间的影响程度
加速度敏感度(g-sensitivity):角速度计特有的参数,用于衡量加速度对角速度计输出的影响大小,通常单位为dps/g
零偏不稳定性:主要针对角速度计的参数,代表零偏在较长时间下的随机游走
IMU选型
目前可以找到的IMU厂商有以下几个:
TDK InvenSense:MEMS加速度计,角速度计,磁力计,六轴/九轴厂商,有广泛使用的IMU MPU6050,价位中等(也有更贵的高性能的ICM系列)
ST(意法半导体):MEMS加速度计,角速度计以及六轴厂商,主要为智能手机市场提供IMU,例如有LSM6DS3TR,价位偏低
Bosch(博世):MEMS加速度计,角速度计,磁力计,六轴/九轴厂商,主要为机器人和无人机市场提供IMU,六轴传感器的有BMI系列,价位偏高
ADI:老牌半导体公司,有加速度计和六轴,主要用于汽车和军事领域,性能最高但价位也是极高。当然也有低端的加速计ADXL35x系列
QST(上海矽睿):磁力计,加速度计以及六轴的国产厂商,有QMI8658/QMI8610等六轴传感器
选型过程自然要比较性能,当然主要看零偏,温漂以及噪声。考虑到价格因素,ADI的六轴这个选项基本可以舍弃。高性能的一档有ICM4x6xx,BMI0xx,QMI86xx和LSM6Dx的性能略逊一筹。
截止目前,已知官方有库存的只有ST和QST。在疫情的背景下,选用国产的IMU或许更能确保稳定供应。
Arduino Nano RP2040 Connect -- LSM6DSOX
开发板使用的是LSM6DSOXTR是一款高性能的6轴惯性测量单元(IMU),由STMicroelectronics(意法半导体)制造。集成了3轴数字加速度计和3轴数字陀螺仪,采用系统级封装技术,具有出色的性能和低功耗特性。提供SPI、I²C和MIPI I3C串行接口,支持主处理器数据同步。是一款功能强大、性能卓越的6轴IMU。其手册特征如下图所示。意法半导体为其封装了传感器专用库-STM32duino X-NUCLEO-IKS01A3库,使用Arduino编程只需调用其库即可。
IMU编程与调试
Arduino® Nano RP2040 Connect代码:
/* WIRING
In order to use the Adafruit lsm6dsox sensor with a ST nucleo board,
plug Nucleo "+3.3V" to AdafruitLSM6DOX "VIN",
plug Nucleo "GND" to AdafruitLSM6DOX "GND",
plug Nucleo "SCL"(D15) to AdafruitLSM6DOX "SCL",
plug Nucleo "SDA"(D14) to AdafruitLSM6DOX "SDA".*/
#include "LSM6DSOXSensor.h"
#include <Arduino.h>
// Declare LSM6DSOX sensor. Sensor address can have 2 values LSM6DSOX_I2C_ADD_L (corresponds to 0x6A I2C address) or LSM6DSOX_I2C_ADD_H (corresponds to 0x6B I2C address)
// On Adafruit lsm6dsox sensor, LSM6DSOX_I2C_ADD_L is the default address
LSM6DSOXSensor lsm6dsoxSensor = LSM6DSOXSensor(&Wire, LSM6DSOX_I2C_ADD_L);
int flag;
long last_update_time;
// Timer variables
unsigned long lastTime = 0;
unsigned long timerDelay = 1000;
void setup() {
Serial.begin(115200);
Serial1.begin(115200); // opensserial port, sets data rate to 115200 bps
while(Serial1.read()>= 0){}//clear serialbuffer
pinMode(LED_BUILTIN,OUTPUT);
digitalWrite(LED_BUILTIN,LOW);
Wire.begin();
// Default clock is 100kHz. LSM6DSOX also supports 400kHz, let's use it
Wire.setClock(400000);
// Init the sensor
lsm6dsoxSensor.begin();
// Enable accelerometer and gyroscope, and check success
if (lsm6dsoxSensor.Enable_X() == LSM6DSOX_OK && lsm6dsoxSensor.Enable_G() == LSM6DSOX_OK) {
Serial.println("Success enabling accelero and gyro");
} else {
Serial.println("Error enabling accelero and gyro");
}
// Read ID of device and check that it is correct
uint8_t id;
lsm6dsoxSensor.ReadID(&id);
if (id != LSM6DSOX_ID) {
Serial.println("Wrong ID for LSM6DSOX sensor. Check that device is plugged");
} else {
Serial.println("Receviced correct ID for LSM6DSOX sensor");
}
// Set accelerometer scale at +- 2G. Available values are +- 2, 4, 8, 16 G
lsm6dsoxSensor.Set_X_FS(2);
// Set gyroscope scale at +- 125 degres per second. Available values are +- 125, 250, 500, 1000, 2000 dps
lsm6dsoxSensor.Set_G_FS(125);
// Set Accelerometer sample rate to 208 Hz. Available values are +- 12.0, 26.0, 52.0, 104.0, 208.0, 416.0, 833.0, 1667.0, 3333.0, 6667.0 Hz
lsm6dsoxSensor.Set_X_ODR(208.0f);
// Set Gyroscope sample rate to 208 Hz. Available values are +- 12.0, 26.0, 52.0, 104.0, 208.0, 416.0, 833.0, 1667.0, 3333.0, 6667.0 Hz
lsm6dsoxSensor.Set_G_ODR(208.0f);
}
void IMU_update(int interval)
{
// Read accelerometer
uint8_t acceleroStatus;
lsm6dsoxSensor.Get_X_DRDY_Status(&acceleroStatus);
if (acceleroStatus == 1) { // Status == 1 means a new data is available
int32_t acceleration[3];
lsm6dsoxSensor.Get_X_Axes(acceleration);
// Plot data for each axis in mg
Serial.print("AccelerationX=");
Serial.print(acceleration[0]);
Serial.print("mg, AccelerationY=");
Serial.print(acceleration[1]);
Serial.print("mg, AccelerationZ=");
Serial.print(acceleration[2]);
Serial.println("mg");
if (millis() - last_update_time > interval)
{
if (acceleration[1] > 1000)
{
//encoder_diff--;
flag = 1;
Serial.print("flag=1");
Serial1.write(49);
digitalWrite(LED_BUILTIN,LOW);
}
else if (acceleration[1] < -1000)
{
// encoder_diff++;
flag = 2;
Serial.print("flag=2");
Serial1.write(50);
digitalWrite(LED_BUILTIN,HIGH);
}
else
{
flag = 0;
}
last_update_time = millis();
}
}
// Read gyroscope
uint8_t gyroStatus;
lsm6dsoxSensor.Get_G_DRDY_Status(&gyroStatus);
if (gyroStatus == 1) { // Status == 1 means a new data is available
int32_t rotation[3];
lsm6dsoxSensor.Get_G_Axes(rotation);
// Plot data for each axis in milli degrees per second
Serial.print("RotationX=");
Serial.print(rotation[0]);
Serial.print("mdps, RotationY=");
Serial.print(rotation[1]);
Serial.print("mdps, RotationZ=");
Serial.print(rotation[2]);
Serial.println("mdps");
}
}
void loop() {
IMU_update(200);
}
PICO2端代码:
int val;
void setup() {
Serial1.setRX(17);
Serial1.setTX(16);
Serial1.begin(115200); // opensserial port, sets data rate to 9600 bps
while(Serial.read()>= 0){}//clear serialbuffer
pinMode(25,OUTPUT);
}
void loop() {
if (Serial1.available() > 0) {
delay(100); // 等待数据传完
int numdata = Serial1.available();
val=Serial1.read();
Serial1.println(val);
if(val==49)
{
Serial.println("Test OK");
Serial.println(val);
// delay(2000);
digitalWrite(25, LOW);
}
if(val==50)
{
Serial.println("Test OK!!!");
Serial.println(val);
digitalWrite(25, HIGH);
}
while(Serial1.read()>=0){} //清空串口缓存
}
}
上位机打印的IMU数据:
总结:
实现IMU的控制与数据读取还是十分简单的,只需调用官方Arduino库即可,这大大降低了开发者的开发难度。
演示视频:
[localvideo]32888557c56d34de4aa82d0d0364dcf8[/localvideo]
- 2024-11-30
-
发表了日志:
任务一:搭建环境并开启第一步Blink三色LED / 串口打印Hello DigiKey & EEWorld ...
-
发表了主题帖:
【Follow me第二季第4期】任务一:搭建环境并开启第一步Blink三色LED / 串口打印He...
本帖最后由 mingzhe123 于 2024-12-1 12:14 编辑
2024年得捷电子和eeworld论坛联合举办的Follow me活动第二季已经进行到第4期,有幸再次参见,趁着这次周末有时间,将任务做了。
对于Arduino,并不是很陌生,之前也是用ESP32、ESP8266做了不少好玩的东西。但对于主控为树莓派系列的MCU,但使用Arduino开发还是第一次接触,虽然我也有树莓派4
本次任务除了申请官方指定的Arduino® Nano RP2040 Connect,还申请了另外一块开发板RASPBERRY PI PICO 2,帮助更好的完成任务,并碰撞出更多的灵感。
开发板简介
Arduino Nano RP2040 Connect开发板集成了 Raspberry Pi Foundation 开发的 RP2040 微控制器与 u-blox NINA-W102 Wi-Fi 和蓝牙无线电模块,以及一个丰富的先进传感器甚至能够支持人工智能算法。事实上,该板包括一个支持语音激活功能的数字麦克风、一个六轴惯性运动传感器 (IMU)、一个小型 RGB LED 和广泛可用的闪存 (16 MB),甚至能够满足最苛刻的要求应用程序。
新板从 Raspberry Pi RP2040 继承了对 MicroPython 和 C/C++ 语言的编程支持,两者均基于为 Raspberry Pi Pico 板开发的 SDK。与 Arduino 系列中的所有其他板一样,最新添加的产品支持本地编程环境,例如著名的 Arduino IDE(现在为 2.0 版)。
框架图
开发板框图如图 1 所示。核心由 RP2040 微控制器表示,能够通过经典的 USB 连接与主机开发和调试环境进行通信,并通过四通道 SPI 串行高速存储器与 16-MB 外部闪存进行通信。速度接口。3.3V 电源由 MP2322 稳压器提供,输入源可从 USB 端口 (V USB ) 或外部电源 (V IN ) 中选择。NINA W102 Wi-Fi/蓝牙模块通过 I 2连接到 microC、SPI、UART接口,连接RGB LED。至于其他传感器,MEMS麦克风通过脉冲密度调制(PDM)数字音频接口连接到micro,而六轴运动传感器和认证模块通过I 2 C总线连接。以 12 MHz 运行的外部 MEMS 振荡器提供时钟脉冲。
图 1:开发板框图
组件
该板的主要组件在图 2 中突出显示。首先,我们有 RP2040 芯片,这是一个基于 ARM Cortex M0+ 的 32 位双核微控制器,工作频率为 133 MHz,并配有一个集成的 264-KB SRAM 存储器。该 MCU 的卓越性能和高效率使其能够支持使用 TinyML、TensorFlow Lite 或 Edge Impulse 开发的机器学习算法。除了对 MicroPython 的全面支持(从 2021 年 7 月开始提供)外,该板还为机器视觉项目提供免费的 OpenMV 许可证。RP2040 微控制器提供的功能包括:
直接内存访问控制器
USB 1.1 控制器和 PHY,具有主机和设备支持
8 个可编程 IO 状态机
用于扩展外设支持的可编程 IO
具有内部温度传感器的四通道 ADC,0.5-MS/s,12 位转换
SWD调试
两个片上 PLL,用于生成 USB 和内核时钟
多种低功耗模式支持
USB 1.1 主机/设备
内部稳压器提供核心电压
先进的高性能总线/先进的外围总线
图 2:电路板的主要组成部分
在 MCU 之后,此卡的第二个优势无疑是连接性,它通过 u-blox NINA W102 无线电模块实现,这是一种廉价且易于适应的设备。该模块还增加了四个模拟 GPIO(RP2040 只有四个模拟引脚),使总数达到八个,与其他 Arduino Nano 板一致。配备集成天线的无线电模块基于双核 Xtensa LX6 CPU,可以使用 SWD 接口和位于板背面的特殊焊盘独立于 RP2040 进行编程。在实践中,NINA W102 模块使用与 ESP32 模块相同的 CPU,ESP32 模块是最受创客欢迎的硬件平台之一,还有 Arduino 和 Raspberry Pi。RGB LED 连接到无线电模块,可以使用为模块本身开发的相同库 (WiFiNINA) 驱动。物联网应用中使用的连接需要高度的安全性,以便在所有操作条件下保持传输数据的一致性,并防止来自外部的潜在攻击。为此,设计人员采用了功能强大的加密协处理器,即已在 Arduino MKR 系列板上使用的 Microchip ATECC608A。加密协处理器的特点是功耗特别低,提供对安全启动的支持、对非对称签名的硬件支持、验证、密钥协商、对对称算法(SHA-256 和 HMAC、AES-128)的硬件支持以及网络关键管理支持。物联网应用中使用的连接需要高度的安全性,以便在所有操作条件下保持传输数据的一致性,并防止来自外部的潜在攻击。为此,设计人员采用了功能强大的加密协处理器,即已在 Arduino MKR 系列板上使用的 Microchip ATECC608A。加密协处理器的特点是功耗特别低,提供对安全启动的支持、对非对称签名的硬件支持、验证、密钥协商、对对称算法(SHA-256 和 HMAC、AES-128)的硬件支持以及网络关键管理支持。物联网应用中使用的连接需要高度的安全性,以便在所有操作条件下保持传输数据的一致性,并防止来自外部的潜在攻击。为此,设计人员采用了功能强大的加密协处理器,即已在 Arduino MKR 系列板上使用的 Microchip ATECC608A。加密协处理器的特点是功耗特别低,提供对安全启动的支持、对非对称签名的硬件支持、验证、密钥协商、对对称算法(SHA-256 和 HMAC、AES-128)的硬件支持以及网络关键管理支持。设计人员包括一个功能强大的加密协处理器,即已在 Arduino MKR 系列板上使用的 Microchip ATECC608A。加密协处理器的特点是功耗特别低,提供对安全启动的支持、对非对称签名的硬件支持、验证、密钥协商、对对称算法(SHA-256 和 HMAC、AES-128)的硬件支持以及网络关键管理支持。设计人员包括一个功能强大的加密协处理器,即已在 Arduino MKR 系列板上使用的 Microchip ATECC608A。加密协处理器的特点是功耗特别低,提供对安全启动的支持、对非对称签名的硬件支持、验证、密钥协商、对对称算法(SHA-256 和 HMAC、AES-128)的硬件支持以及网络关键管理支持。
传感器设备包括 ST LSM6DSOX 六轴 IMU,它将 3D 加速度计和 3D 陀螺仪与专用机器学习核心相结合。该组件主要用于“永远在线”操作要求特别低功耗的移动市场,具有以下技术特性:
3D 陀螺仪,±2-/±4-/±8-/±16-g 满量程
3D 加速度计,±125-/±250-/±500-/±1,000-/±2,000-dps 满量程
先进的计步器、计步器和计步器
显着运动检测、倾斜检测
标准中断:自由落体、唤醒、6D/4D 定向、单击和双击
可编程有限状态机:加速度计、陀螺仪和外部传感器
机器学习核心
嵌入式温度传感器
该设备可以使用专用的 Arduino LSM6DSOX 库轻松编程。
另一个相关的传感器是全向数字麦克风,可用于声音激活、音频控制,甚至人工智能语音识别。MP34DT05 麦克风实时捕捉和分析声音,可用于为任何项目创建语音界面。ST MP34DT05-A 是一款超紧凑、低功耗、全向、数字 MEMS 麦克风,内置电容感应元件和 I2C 接口。能够检测声波的传感元件采用 MEMS 工艺制造,而 IC 接口采用 CMOS 工艺制造,可在外部以 PDM 格式提供数字信号。MP34DT05-A 是一款低失真数字麦克风,具有 64-dB 的信噪比和 –26-dBFS ±3-dB 的灵敏度。可以使用专用的 Arduino PDM 库对该设备进行编程。
关于可编程 I/O 引脚,该板提供 8 个模拟输入引脚和 22 个数字 I/O 引脚(其中 20 个可配置为 PWM 发生器或外部中断)。引脚 13 上还提供了一个内置用户 LED。图 3显示了 Arduino Nano RP2040 连接板的完整引脚排列,该板也可提供预安装的插头引脚。引脚布局井井有条,所有模拟和电源引脚都在左侧接头上,数字引脚在右侧接头上。如图 1 所示,RP2040 MCU 支持 UART、SPI 和 I 2 C 通信。
图 3:电路板引脚排列
Raspberry Pi Pico 2是一款树莓派官方设计的低成本,高性能的微控制器开发板,具有灵活数字接口。硬件上,采用 Raspberry Pi 官方自主研发的 RP2350 微控制器芯片,搭载了双ARM Cortex M33或Hazard3处理器,高达 150MHz 的运行频率,内置了 520KB 的 SRAM 和 4MB 的内存,还板载有多达 26 个多功能的 GPIO 引脚。软件上,可选择树莓派提供的 C/C++ SDK,或者使用 MicroPython 进行开发。
其主要特点包括:
RP2350 微控制器芯片
双 Cortex-M33 或 Hazard3 处理器,最高频率可达 150MHz
520KB 的 SRAM 和 4MB 的板载闪存
USB 1.1,支持设备和主机模式
低功耗睡眠和休眠模式
通过 USB 大容量存储进行拖放编程
26× 多功能 GPIO 引脚,其中 3 个可用于 ADC
2× SPI, 2× I2C, 2× UART, 3× 12 位 500ksps 模数转换器(ADC),24× 可控 PWM 通道
2× 定时器,带 4 个警报,1× AON 定时器
温度传感器
3 × 可编程 IO (PIO) 块,总共 12 个状态机,用于自定义外设支持
灵活、用户可编程的高速 IO
可以模拟 SD 卡和 VGA 等接口
引脚图和设计文件
开发环境搭建
话不多说,开始进行开发环境搭建。
在arduino IDE中配置RP2040开发环境
2.1】打开arduino IDE,点击File,然后点击首选项
2.2】将链接 https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json 复制到其他开发板管理器地址中
单击“确定”
2.3】进入开发板管理器中 安装RP2040。在搜索框中搜索RP2040,点击IN STALLED,弹出的窗口,点击OK,等待安装完成(我这个示先安装过)
3】安装完成之后就可以使用Arduino® Nano RP2040 Connect与RASPBERRY PI PICO 2了。
任务一基础:
搭建环境并开启第一步Blink三色LED / 串口打印Hello DigiKey & EEWorld!
首先了解一下硬件资源,板子上自带了5颗LED灯,包括一颗电源灯,还有一颗RGB灯,最后一颗为通信指示灯。本次任务我适用RGB灯,其电路如下图,但是这几个脚直接接到内置的wifi模块上,查看资料后发现,可以直接通过封装好的库函数WiFiNINA调用其控制函数控制其RGB灯。
串口部分基础任务中直接走虚拟串口与PC直接通信。这部分和软件调试使用同一个usb口,因此不需要额外增加电路。
基础代码:
#include <WiFiNINA.h>
//#include <Arduino.h>
void setup() {
Serial.begin(115200);
pinMode(LEDB, OUTPUT);
pinMode(LEDR,OUTPUT);
pinMode(LEDG,OUTPUT);
digitalWrite(LEDB,LOW);
digitalWrite(LEDR,LOW);
digitalWrite(LEDG,LOW);
Serial.println("The task began!!!");
Serial.println();
}
void loop() {
delay(2000);
digitalWrite(LEDB, HIGH);
digitalWrite(LEDG, LOW);
Serial.println("Hello DigiKey & EEWorld!");
delay(2000);
digitalWrite(LEDB, LOW);//状态翻转
digitalWrite(LEDG, HIGH);
}
任务一进阶:
任务一进阶适用PICO2开发板与Arduino Nano RP2040 Connect,其主要原理是PICO2的按键通过串口控制Arduino Nano RP2040 Connect开发板上的RGB 灯,其系统架构图如下图所示:
其主要代码如下:
PICO2代码:
int val;
const int BUTTON_PIN = 21; // the number of the pushbutton pin
const int SHORT_PRESS_TIME = 500; // 500 milliseconds
// Variables will change:
int lastState = LOW; // the previous state from the input pin
int currentState; // the current reading from the input pin
unsigned long pressedTime = 0;
unsigned long releasedTime = 0;
char key_flag = 0;
void setup() {
Serial1.setRX(17);
Serial1.setTX(16);
Serial1.begin(115200); // opensserial port, sets data rate to 9600 bps
while(Serial.read()>= 0){}//clear serialbuffer
pinMode(25,OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
Serial1.write(49);
}
void Key(void) {
delay(1);
}
void loop() {
delay(10);
// read the state of the switch/button:
currentState = digitalRead(BUTTON_PIN);
if(lastState == HIGH && currentState == LOW) // button is pressed
pressedTime = millis();
else if(lastState == LOW && currentState == HIGH) { // button is released
releasedTime = millis();
long pressDuration = releasedTime - pressedTime;
if( pressDuration < SHORT_PRESS_TIME )
Serial.println("A short press is detected");
key_flag = !key_flag;
if(key_flag) {
Serial1.write(49);
Serial.println("49");
digitalWrite(25,LOW);
} else {
Serial1.write(50);
Serial.println("50");
digitalWrite(25,HIGH);
}
}
// save the the last state
lastState = currentState;
}
Arduino Nano RP2040 Connect代码:
#include <WiFiNINA.h>
int val;
void setup() {
Serial.begin(115200); // opensserial port, sets data rate to 9600 bps
Serial1.begin(115200); // opensserial port, sets data rate to 9600 bps
while(Serial1.read()>= 0){}//clear serialbuffer
while(Serial.read()>= 0){}//clear serialbuffer
pinMode(LEDB, OUTPUT);
pinMode(LEDR,OUTPUT);
pinMode(LEDG,OUTPUT);
digitalWrite(LEDB,LOW);
digitalWrite(LEDR,LOW);
digitalWrite(LEDG,LOW);
Serial.println("The pro task began!!!");
Serial.println();
}
void loop() {
if (Serial1.available() > 0) {
delay(100); // 等待数据传完
int numdata = Serial1.available();
val=Serial1.read();
Serial1.println(val);
if(val==49)
{
Serial.println("Test OK");
Serial.println(val);
// delay(2000);
digitalWrite(LEDB, HIGH);
Serial.println("Hello DigiKey & EEWorld!");
digitalWrite(LEDG, HIGH);
}
if(val==50)
{
Serial.println("Test OK!!!");
Serial.println(val);
// delay(2000);
digitalWrite(LEDB, LOW);
Serial.println("Turn off the lights!");
digitalWrite(LEDG, LOW);
}
while(Serial1.read()>=0){} //清空串口缓存
}
}
总结:
树莓派的MCU,通过arduino来开发难度降低了很多,用户只需要调动封装好的库函数即可。但是有一点要吐槽一下,本来是个及其简单的程序,适用arduino IDE开发却需要很长的编译时间,实在不敢恭维,后期有时间尝试一下VS Code。
任务视频:
[localvideo]232ff75a18e8790122d50b866d442195[/localvideo]
- 2024-10-27
-
回复了主题帖:
【2024 DigiKey 创意大赛】室内智能照明灯设计-完结篇
秦天qintian0303 发表于 2024-10-27 12:27
ESP32 S3做服务器是不是有点性能弱啊
数据量不大。功能比较初级。如果在家里布局智能家居,这个只能作为一个子节点
- 2024-10-26
-
发表了主题帖:
【2024 DigiKey 创意大赛】室内智能照明灯设计-完结篇
本帖最后由 mingzhe123 于 2024-10-29 20:27 编辑
【2024 DigiKey 创意大赛】室内智能照明灯设计
背景介绍
物联网技术是指通过互联网将各种设备、传感 器、控制器等连接在一起,形成一个能够实时互通信息的网络。物联网技术的基本原理是通过传感器采集设备产生的数据,并通过控制器进行处理和控制,最终将数据传输到互联网上,实现设备之间的智能交互。物联网技术的核心特点是实时性、数据共享、智能化和互联性。物联网技术的实时性意味着设备和传感器能够即时收集和传输数据,使得用户可以实时了解设备状态和环境情况。数据共享可 以使不同设备之间共享数据,实现更高效的资源利用和协同工作。智能化指的是物联网设备具备处理和分析数据的能力,可以根据用户需求自动调整设备工作模式。互联性表示物联网技术可以实现设备与设备、设备与人之间的无缝连接和交互,实现更强大的功能和用户体验。物联网技术广泛应用于智能家居、智能交通、智慧城市等领域,为人们的生活带来便利和智能化。
室内智能照明灯设计
室内照明灯系统硬件如图2.1所示分,为三部分:基于ESP32 S3的服务端、基于ESP32 PICO的传感器端与小爱音箱。其中,ESP32 S3服务端使用的是乐鑫的ESP32 S3 LCD EV开发板。如图2.2所示,主要负责与小爱音箱通信,接收传感器端数据、LCD实时显示传感器数据以及灯光的控制;ESP32 PICO传感器端为单独设计的ESP32板卡,如图2.3所示,主要功能为采集MPU6050数据与SHT30数据应通过BLE传输到服务端;小爱音箱主要是与服务端实现语音交互功能。
图2.1 系统架构图
图2.2ESP32 S3 LCD EV
图2.3 传感器端板块
室内照明灯系统软件为服务端代码与传感器端代码。服务端代码基于Arduino并通过FreeRTOS实现多任务处理,如图2.4所示。传感器端则基于Arduino框架完成传感器数据采集与蓝牙传输,如图2.5所示。
服务端代码:
#include "BLEDevice.h"
#include <Arduino.h>
#include <lvgl.h>
#include <ESP_Panel_Library.h>
#include <ESP_IOExpander_Library.h>
// #include <examples/lv_examples.h>
//#include <demos/lv_demos.h>
#define BLINKER_WIFI
#define BLINKER_MIOT_LIGHT
#include <Blinker.h>
#include <Adafruit_NeoPixel.h>
char auth[] = "044be84a71f9";
char ssid[] = "C_WIFI";
char pswd[] = "C88888888";
\
ESP_Panel *panel = NULL;
// 蓝牙相关定义
// Default Temperature is in Celsius
// Comment the next line for Temperature in Fahrenheit
#define temperatureCelsius
// BLE Server name (the other ESP32 name running the server sketch)
#define bleServerName "ESP32_HOME"
/* UUID's of the service, characteristic that we want to read*/
// BLE Service
static BLEUUID bmeServiceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59");
// BLE Characteristics
#ifdef temperatureCelsius
// Temperature Celsius Characteristic
static BLEUUID temperatureCharacteristicUUID("cba1d466-344c-4be3-ab3f-189f80dd7518");
#else
// Temperature Fahrenheit Characteristic
static BLEUUID temperatureCharacteristicUUID("f78ebbff-c8b7-4107-93de-889a6a06d408");
#endif
// Humidity Characteristic
static BLEUUID humidityCharacteristicUUID("ca73b3ba-39f6-4ab3-91ae-186dc9577d99");
// LEDSTATE Characteristic
static BLEUUID LEDCharacteristicUUID("553FF727-642D-493B-9D41-62AED7808700");
// Flags stating if should begin connecting and if the connection is up
static boolean doConnect = false;
static boolean connected = false;
// Address of the peripheral device. Address will be found during scanning...
static BLEAddress *pServerAddress;
// Characteristicd that we want to read
static BLERemoteCharacteristic *temperatureCharacteristic;
static BLERemoteCharacteristic *humidityCharacteristic;
static BLERemoteCharacteristic *LEDStateCharacteristic; // Led控制存储变量
// Activate notify
const uint8_t notificationOn[] = {0x1, 0x0};
const uint8_t notificationOff[] = {0x0, 0x0};
// Variables to store temperature and humidity
String temperatureChar;
String humidityChar;
String LEDStateChar;
String Humidity = "00000";
String Temper = "00000";
lv_obj_t *title_label;
lv_obj_t *temp_label;
lv_obj_t *humi_label;
String Humidity_text;
String Temper_test;
// Flags to check whether new temperature and humidity readings are available
boolean newTemperature = false;
boolean newHumidity = false;
boolean newLEDState = false;
int flag = 2;
#define LV_BUF_SIZE (ESP_PANEL_LCD_H_RES * 20)
#define RGB_1 "RGBKey"
BlinkerRGB WS2812(RGB_1);
#define PIN 4
#define NUMPIXELS 1
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_RGB + NEO_KHZ800);
uint8_t colorR, colorG, colorB;
uint8_t colorW; // 获取亮度
uint8_t colorT; // 获取色温
uint8_t cold; // 白光输出值
uint8_t warm; // 黄光输出值
bool wsState;
bool COLORM; // 彩灯模式
bool READINGM; // 阅读模式
uint8_t wsMode = BLINKER_CMD_MIOT_DAY;
/*定义信号量*/
SemaphoreHandle_t lvgl_mux = NULL; // LVGL mutex
/*线程入口声明*/
void TaskAPPStart();
void ThreadBlinkerEntry(void *pvParameters); // Binker线程入口
void ThreadLVGLEntry(void *pvParameters); // LVGL线程入口
void ThreadSensorEntry(void *pvParameters); // Sensor线程入口
/*定义线程句柄*/
TaskHandle_t ThreadBlinker; // Blinker线程
TaskHandle_t ThreadLVGL; // LVGL线程
TaskHandle_t ThreadSensor; // Sensor线程
void pixelShow() // RGB控制函数
{
pixels.setBrightness(colorW);
for (int i = 0; i < NUMPIXELS; i++)
{
pixels.setPixelColor(i, colorR, colorG, colorB);
}
pixels.show();
}
void LedControl(float x, int y) // CW控制函数(X:亮度、Y:色温)
{
y = 100 - y;
cold = x / 100 * y; // cold_tmp是PWM冷灯珠的占空比
warm = x / 100 * (100 - y); // warm_tmp是PWM冷灯珠的占空比:
}
void ws2812_callback(uint8_t r_value, uint8_t g_value, uint8_t b_value, uint8_t bright_value) // RGB2812回调函数
{
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
BLINKER_LOG("R value: ", r_value);
BLINKER_LOG("G value: ", g_value);
BLINKER_LOG("B value: ", b_value);
BLINKER_LOG("Rrightness value: ", bright_value);
colorR = r_value;
colorG = g_value;
colorB = b_value;
colorW = bright_value;
pixelShow();
}
uint32_t getColor()
{
uint32_t color = colorR << 16 | colorG << 8 | colorB;
return color;
}
void miotPowerState(const String &state) // 用户自定义电源类操作的回调函数:
{
BLINKER_LOG("need set power state: ", state);
if (state == BLINKER_CMD_ON)
{
digitalWrite(LED_BUILTIN, HIGH);
BlinkerMIOT.powerState("on");
BlinkerMIOT.print();
wsState = true;
if (colorW == 0)
colorW = 255;
}
else if (state == BLINKER_CMD_OFF)
{
digitalWrite(LED_BUILTIN, LOW);
BlinkerMIOT.powerState("off");
BlinkerMIOT.print();
wsState = false;
colorW = 0;
}
pixelShow();
}
void miotColor(int32_t color) // 用户自定义颜色设置的回调函数:
{
BLINKER_LOG("need set color: ", color);
colorR = color >> 16 & 0xFF;
colorG = color >> 8 & 0xFF;
colorB = color & 0xFF;
BLINKER_LOG("colorR: ", colorR, ", colorG: ", colorG, ", colorB: ", colorB);
pixelShow();
BlinkerMIOT.color(color);
BlinkerMIOT.print();
}
void miotMode(uint8_t mode) // 用户自定义模式设置的回调函数:
{
BLINKER_LOG("need set mode: ", mode);
if (mode == BLINKER_CMD_MIOT_DAY)
{ // 日光(10,25,41)
// Your mode function
COLORM = 0;
colorR = 10;
colorG = 255;
colorB = 41;
}
else if (mode == BLINKER_CMD_MIOT_NIGHT)
{ // 月光 自定义
// Your mode function
COLORM = 0;
COLORM = 0;
colorR = 0;
colorG = 0;
colorB = 0;
}
else if (mode == BLINKER_CMD_MIOT_COLOR)
{ // 彩光
// Your mode function
COLORM = 1;
}
else if (mode == BLINKER_CMD_MIOT_WARMTH)
{ // 温馨
// Your mode function
COLORM = 0;
colorR = 10;
colorG = 255;
colorB = 41;
}
else if (mode == BLINKER_CMD_MIOT_TV)
{ // 电视模式
// Your mode function
COLORM = 0;
}
else if (mode == BLINKER_CMD_MIOT_READING)
{ // 阅读模式
// Your mode function
COLORM = 0;
colorR = 0;
colorG = 0;
colorB = 0;
}
else if (mode == BLINKER_CMD_MIOT_COMPUTER)
{ // 电脑模式
// Your mode function
COLORM = 0;
colorR = 0;
colorG = 0;
colorB = 0;
LedControl(colorW, colorT);
}
wsMode = mode;
BlinkerMIOT.mode(mode);
BlinkerMIOT.print();
}
void miotBright(const String &bright) // 用户自定义亮度控制的回调函数:
{
BLINKER_LOG("need set brightness: ", bright);
colorW = bright.toInt();
BLINKER_LOG("now set brightness: ", colorW);
pixelShow();
BlinkerMIOT.brightness(colorW);
BlinkerMIOT.print();
}
void miotColoTemp(int32_t colorTemp) // 用户自定义色温控制的回调函数:
{
BLINKER_LOG("need set colorTemperature: ", colorTemp);
;
BLINKER_LOG("需要设置色温: ", colorTemp);
colorT = map(colorTemp, 1000, 10000, 0, 100);
BlinkerMIOT.colorTemp(colorT);
BlinkerMIOT.print();
}
void miotQuery(int32_t queryCode) // 用户自定义设备查询的回调函数:
{
BLINKER_LOG("MIOT Query codes: ", queryCode);
switch (queryCode)
{
case BLINKER_CMD_QUERY_ALL_NUMBER:
BLINKER_LOG("MIOT Query All");
BlinkerMIOT.powerState(wsState ? "on" : "off");
BlinkerMIOT.color(0);
BlinkerMIOT.mode(0);
BlinkerMIOT.colorTemp(1000);
BlinkerMIOT.brightness(1);
BlinkerMIOT.print();
break;
case BLINKER_CMD_QUERY_POWERSTATE_NUMBER:
BLINKER_LOG("MIOT Query Power State");
BlinkerMIOT.powerState(wsState ? "on" : "off");
BlinkerMIOT.print();
break;
case BLINKER_CMD_QUERY_COLOR_NUMBER:
BLINKER_LOG("MIOT Query Color");
BlinkerMIOT.color(0);
BlinkerMIOT.print();
break;
case BLINKER_CMD_QUERY_MODE_NUMBER:
BLINKER_LOG("MIOT Query Mode");
BlinkerMIOT.mode(0);
BlinkerMIOT.print();
break;
case BLINKER_CMD_QUERY_COLORTEMP_NUMBER:
BLINKER_LOG("MIOT Query ColorTemperature");
BlinkerMIOT.colorTemp(1000);
BlinkerMIOT.print();
break;
case BLINKER_CMD_QUERY_BRIGHTNESS_NUMBER:
BLINKER_LOG("MIOT Query Brightness");
BlinkerMIOT.brightness(1);
BlinkerMIOT.print();
break;
default:
BlinkerMIOT.powerState(wsState ? "on" : "off");
BlinkerMIOT.color(0);
BlinkerMIOT.mode(0);
BlinkerMIOT.colorTemp(1000);
BlinkerMIOT.brightness(1);
BlinkerMIOT.print();
break;
}
}
void dataRead(const String &data)
{
BLINKER_LOG("Blinker readString: ", data);
Blinker.vibrate();
uint32_t BlinkerTime = millis();
Blinker.print("millis", BlinkerTime);
}
#if ESP_PANEL_LCD_BUS_TYPE == ESP_PANEL_BUS_TYPE_RGB
/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
panel->getLcd()->drawBitmap(area->x1, area->y1, area->x2 + 1, area->y2 + 1, color_p);
lv_disp_flush_ready(disp);
}
#endif
#if ESP_PANEL_USE_LCD_TOUCH
/* Read the touchpad */
void my_touchpad_read(lv_indev_drv_t *indev, lv_indev_data_t *data)
{
panel->getLcdTouch()->readData();
bool touched = panel->getLcdTouch()->getTouchState();
if (!touched)
{
data->state = LV_INDEV_STATE_REL;
}
else
{
TouchPoint point = panel->getLcdTouch()->getPoint();
data->state = LV_INDEV_STATE_PR;
/*Set the coordinates*/
data->point.x = point.x;
data->point.y = point.y;
// Serial.printf("Touch point: x %d, y %d\n", point.x, point.y);
}
}
#endif
bool lv_port_lock(uint32_t timeout_ms)
{
const TickType_t timeout_ticks = (timeout_ms == 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE;
}
void lv_port_unlock(void)
{
xSemaphoreGiveRecursive(lvgl_mux);
}
void event_handler(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED)
{
LV_LOG_USER("Clicked");
}
else if (code == LV_EVENT_VALUE_CHANGED)
{
LV_LOG_USER("Toggled");
}
}
void lv_example_btn_1(void)
{
Temper_test = "Temperature: ";
Temper_test += Temper;
Temper_test += "'C";
Humidity_text = "Humidity: ";
Humidity_text += Humidity;
Humidity_text += "%";
lv_obj_t* title = lv_obj_create(lv_scr_act());
lv_obj_set_size(title, 480, 120);
lv_obj_set_style_bg_color(title, lv_color_hex(0x00FF00), LV_STATE_DEFAULT);
title_label = lv_label_create(title);
static char title_str[] = "Indoor Lighting System";
lv_label_set_text(title_label, title_str);
lv_obj_align(title_label, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_text_font(title_label, &lv_font_montserrat_36, LV_STATE_DEFAULT);
//*温度 * /
lv_obj_t* temp = lv_obj_create(lv_scr_act());
lv_obj_set_style_bg_color(temp, lv_color_hex(0x00FF00), LV_STATE_DEFAULT);
lv_obj_set_size(temp, 480, 80);
lv_obj_set_pos(temp, 0, 110);
temp_label = lv_label_create(temp);
// lv_label_set_text(temp_label, Temper_test.c_str());
lv_label_set_text(temp_label, Temper_test.c_str());
lv_obj_align(temp_label, LV_ALIGN_OUT_LEFT_MID, 0, 0);
lv_obj_set_style_text_font(temp_label, &lv_font_montserrat_30, LV_STATE_DEFAULT);
/*湿度*/
lv_obj_t* humi = lv_obj_create(lv_scr_act());
lv_obj_set_style_bg_color(humi, lv_color_hex(0x0000FF), LV_STATE_DEFAULT);
lv_obj_set_size(humi, 480, 80);
lv_obj_set_pos(humi, 0, 180);
humi_label = lv_label_create(humi);
// lv_label_set_text(humi_label, Humidity_text.c_str());
lv_label_set_text(humi_label, Humidity_text.c_str());
lv_obj_align(humi_label, LV_ALIGN_OUT_LEFT_MID, 0, 0);
lv_obj_set_style_text_font(humi_label, &lv_font_montserrat_30, LV_STATE_DEFAULT);
}
///////////////////////////蓝牙相关//////////////////////////////////
// Callback function that gets called, when another device's advertisement has been received
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks
{
void onResult(BLEAdvertisedDevice advertisedDevice)
{
if (advertisedDevice.getName() == bleServerName)
{ // Check if the name of the advertiser matches
advertisedDevice.getScan()->stop(); // Scan can be stopped, we found what we are looking for
pServerAddress = new BLEAddress(advertisedDevice.getAddress()); // Address of advertiser is the one we need
doConnect = true; // Set indicator, stating that we are ready to connect
Serial.println("Device found. Connecting!");
}
}
};
// When the BLE Server sends a new temperature reading with the notify property
static void temperatureNotifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic,
uint8_t *pData, size_t length, bool isNotify)
{
// store temperature value
temperatureChar = (char *)pData;
newTemperature = true;
}
// When the BLE Server sends a new humidity reading with the notify property
static void humidityNotifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic,
uint8_t *pData, size_t length, bool isNotify)
{
// store humidity value
humidityChar = (char *)pData;
newHumidity = true;
// Serial.print(newHumidity);
}
// When the BLE Server sends a new humidity reading with the notify property
static void LEDStateNotifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic,
uint8_t *pData, size_t length, bool isNotify)
{
// store humidity value
LEDStateChar = (char *)pData;
newLEDState = true;
// Serial.print(newLEDState);
}
// Connect to the BLE Server that has the name, Service, and Characteristics
bool connectToServer(BLEAddress pAddress)
{
BLEClient *pClient = BLEDevice::createClient();
// Connect to the remove BLE Server.
pClient->connect(pAddress);
Serial.println(" - Connected to server");
// Obtain a reference to the service we are after in the remote BLE server.
BLERemoteService *pRemoteService = pClient->getService(bmeServiceUUID);
if (pRemoteService == nullptr)
{
Serial.print("Failed to find our service UUID: ");
Serial.println(bmeServiceUUID.toString().c_str());
return (false);
}
// Obtain a reference to the characteristics in the service of the remote BLE server.
temperatureCharacteristic = pRemoteService->getCharacteristic(temperatureCharacteristicUUID);
humidityCharacteristic = pRemoteService->getCharacteristic(humidityCharacteristicUUID);
LEDStateCharacteristic = pRemoteService->getCharacteristic(LEDCharacteristicUUID);
if (temperatureCharacteristic == nullptr || humidityCharacteristic == nullptr || LEDStateCharacteristic == nullptr)
{
Serial.print("Failed to find our characteristic UUID");
return false;
}
Serial.println(" - Found our characteristics");
// Assign callback functions for the Characteristics
temperatureCharacteristic->registerForNotify(temperatureNotifyCallback);
humidityCharacteristic->registerForNotify(humidityNotifyCallback);
LEDStateCharacteristic->registerForNotify(LEDStateNotifyCallback);
return true;
}
void Display_init()
{
String LVGL_Arduino = "Hello LVGL! ";
LVGL_Arduino += String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();
Serial.println(LVGL_Arduino);
Serial.println("I am ESP32_Display_Panel");
panel = new ESP_Panel();
/* Initialize LVGL core */
lv_init();
/* Initialize LVGL buffers */
static lv_disp_draw_buf_t draw_buf;
/* Using double buffers is more faster than single buffer */
/* Using internal SRAM is more fast than PSRAM (Note: Memory allocated using `malloc` may be located in PSRAM.) */
uint8_t *buf = (uint8_t *)heap_caps_calloc(1, LV_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_INTERNAL);
assert(buf);
lv_disp_draw_buf_init(&draw_buf, buf, NULL, LV_BUF_SIZE);
/* Initialize the display device */
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
/* Change the following line to your display resolution */
disp_drv.hor_res = ESP_PANEL_LCD_H_RES;
disp_drv.ver_res = ESP_PANEL_LCD_V_RES;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
#if ESP_PANEL_USE_LCD_TOUCH
/* Initialize the input device */
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = my_touchpad_read;
lv_indev_drv_register(&indev_drv);
#endif
/* There are some extral initialization for ESP32-S3-LCD-EV-Board */
#ifdef ESP_PANEL_BOARD_ESP32_S3_LCD_EV_BOARD
/* Initialize IO expander */
ESP_IOExpander *expander = new ESP_IOExpander_TCA95xx_8bit(ESP_PANEL_LCD_TOUCH_BUS_HOST_ID, ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000, ESP_PANEL_LCD_TOUCH_I2C_IO_SCL, ESP_PANEL_LCD_TOUCH_I2C_IO_SDA);
expander->init();
expander->begin();
/* Add into panel for 3-wire SPI */
panel->addIOExpander(expander);
/* For the newest version sub board, need to set `ESP_PANEL_LCD_RGB_IO_VSYNC` to high before initialize LCD */
pinMode(ESP_PANEL_LCD_RGB_IO_VSYNC, OUTPUT);
digitalWrite(ESP_PANEL_LCD_RGB_IO_VSYNC, HIGH);
#endif
/* Initialize bus and device of panel */
panel->init();
#if ESP_PANEL_LCD_BUS_TYPE != ESP_PANEL_BUS_TYPE_RGB
/* Register a function to notify LVGL when the panel is ready to flush */
/* This is useful for refreshing the screen using DMA transfers */
panel->getLcd()->setCallback(notify_lvgl_flush_ready, &disp_drv);
#endif
/* Start panel */
panel->begin();
}
void Bluetooth_Init()
{
Serial.println("Starting Arduino BLE Client application...");
// Init BLE device
BLEDevice::init("");
delay(1000);
// Retrieve a Scanner and set the callback we want to use to be informed when we
// have detected a new device. Specify that we want active scanning and start the
// scan to run for 30 seconds.
BLEScan *pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true);
pBLEScan->start(2);
delay(1000);
}
void Blinker_Init()
{
/*Bllier 初始化*/
BLINKER_DEBUG.stream(Serial);
BLINKER_DEBUG.debugAll();
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
Blinker.begin(auth, ssid, pswd);
Blinker.attachData(dataRead);
BlinkerMIOT.attachPowerState(miotPowerState);
BlinkerMIOT.attachColor(miotColor);
BlinkerMIOT.attachMode(miotMode);
BlinkerMIOT.attachBrightness(miotBright);
BlinkerMIOT.attachColorTemperature(miotColoTemp);
BlinkerMIOT.attachQuery(miotQuery);
colorR = 255;
colorG = 255;
colorB = 255;
colorW = 0;
colorT = 0;
wsState = true;
pixels.begin();
pixels.setBrightness(colorW);
WS2812.attach(ws2812_callback);
pixelShow();
}
/*======================================= TaskAPPStart ====================================*/
/*
* @brief: 启动创建任务
* @param:none
* @retval:none
*/
void TaskAPPStart()
{
/*创建Blinker线程*/
BaseType_t status;
status = xTaskCreatePinnedToCore(
(TaskFunction_t)ThreadBlinkerEntry, // Weather线程入口
(const char *const)"Thread_Blinker", // 线程名称
(const uint32_t)4096 * 2, // 线程栈
(void *const)NULL, // Weather线程入口参数
(UBaseType_t)6, // 线程优先级 0-24 数值越大优先级越高
(TaskHandle_t *)&ThreadBlinker, // 线程句柄
(const BaseType_t)APP_CPU_NUM); // 指定内核1
if (status == pdPASS)
{
Serial.println("Blinker线程创建成功...");
}
else
{
Serial.println("Blinker线程创建失败...");
}
/*创建Sensor线程*/
status = xTaskCreatePinnedToCore(
(TaskFunction_t)ThreadSensorEntry, // Sensor线程入口
(const char *const)"Thread_Sensor", // 线程名称
(const uint32_t)4096 * 1, // 线程栈
(void *const)NULL, // Sensor线程入口参数
(UBaseType_t)6, // 线程优先级 0-24 数值越大优先级越高
(TaskHandle_t *)&ThreadSensor, // 线程句柄
(const BaseType_t)PRO_CPU_NUM); // 指定内核0
if (status == pdPASS)
{
Serial.println("Sensor线程创建成功...");
}
else
{
Serial.println("Sensor线程创建失败...");
}
/* Create a task to run the LVGL task periodically */
lvgl_mux = xSemaphoreCreateRecursiveMutex();
// xTaskCreate(lvgl_task, "lvgl", 8192, NULL, 1, NULL);
/**
* To avoid errors caused by multiple tasks simultaneously accessing LVGL,
* should acquire a lock before operating on LVGL.
*/
lv_port_lock(0);
lv_example_btn_1();
// lv_demo_music(); // NOK
/*创建LVGL线程*/
status = xTaskCreatePinnedToCore(
(TaskFunction_t)ThreadLVGLEntry, // OLED线程
(const char *const)"Thread_LVGL", // 线程名称
(const uint32_t)4096 * 2, // 线程栈
(void *const)NULL, // OLED线程入口参数
(UBaseType_t)8, // 线程优先级 0-24 数值越大优先级越高
(TaskHandle_t *)&ThreadLVGL, // 线程句柄
(const BaseType_t)PRO_CPU_NUM); // 指定内核1
if (status == pdPASS)
{
Serial.println("LVGL线程创建成功...");
}
else
{
Serial.println("LVGL线程创建失败...");
}
/**
* Try an example. Don't forget to uncomment header.
* See all the examples online: https://docs.lvgl.io/master/examples.html
* source codes: https://github.com/lvgl/lvgl/tree/e7f88efa5853128bf871dde335c0ca8da9eb7731/examples
*/
lv_port_unlock();
delay(1000);
}
/*
* @brief:Blinker线程入口
* @param:none
* @retval:none
*/
void ThreadBlinkerEntry(void *pvParameters)
{
// xSemaphoreTake(sem_Blinker, portMAX_DELAY);
while (1)
{
if (LEDStateChar[0] == 'O' && LEDStateChar[1] == 'N' && flag == 2)
{
Serial.println("OK"); //起床打开灯
colorR = 255;
colorG = 255;
colorB = 255;
colorW = 100;
pixelShow();
delay(100);
wsState = true;
flag = 1;
/* code */
}
else if(LEDStateChar[0] == 'O' && LEDStateChar[1] == 'F' && LEDStateChar[2] == 'F' && flag == 1)
{
Serial.println("NO!"); //躺下关闭灯
colorW = 0;
pixelShow();
delay(100);
wsState = false;
flag = 2;
/* code */
}
else
{
// flag = 2;
}
Blinker.run();
vTaskDelay(1 / portTICK_PERIOD_MS);
}
}
/*
* @brief:LVGL线程入口
* @param:none
* @retval:none
*/
void ThreadLVGLEntry(void *pvParameter)
{
// xSemaphoreTake(sem_LVGL, portMAX_DELAY);
while (1)
{
lv_port_lock(0);
Temper_test = "Temperature: ";
Temper_test += Temper;
Temper_test += "'C";
Humidity_text = "Humidity: ";
Humidity_text += Humidity;
Humidity_text += "%";
lv_label_set_text(temp_label, Temper_test.c_str());
lv_label_set_text(humi_label, Humidity_text.c_str());
lv_task_handler();
// lv_timer_handler(); /* let the GUI do its work */
lv_port_unlock();
// Serial.println("updata");
delay(1000);
}
}
/*
* @brief:Sensor线程入口
* @param:none
* @retval:none
*/
void ThreadSensorEntry(void *pvParameters)
{
while (1)
{
// Serial.print("Temperature:");
if (doConnect == true)
{
if (connectToServer(*pServerAddress))
{
Serial.println("We are now connected to the BLE Server.");
// Activate the Notify property of each Characteristic
temperatureCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t *)notificationOn, 2, true);
humidityCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t *)notificationOn, 2, true);
LEDStateCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t *)notificationOn, 2, true);
connected = true;
}
else
{
Serial.println("We have failed to connect to the server; Restart your device to scan for nearby BLE server again.");
}
doConnect = false;
}
// if new temperature readings are available, print in the OLED
if (newTemperature && newHumidity && newLEDState)
{
newTemperature = false;
newHumidity = false;
newLEDState = false;
Temper = temperatureChar.substring(0, 6);
// Temperature Celsius
// Serial.print("Temperature:");
// Serial.print(temperatureChar);
// Serial.println("C");
// Serial.print("Temperature:");
// Serial.print(Temper);
// Serial.println("C");
// // display humidity
// Serial.print("Humidity:");
// Serial.print(humidityChar);
// Serial.println("%");
Humidity = humidityChar.substring(0, 6);
// Temperature Celsius
// Serial.print("Humidity:");
// Serial.print(Humidity);
// Serial.println("%");
// // display LEDState
// Serial.print("LEDState:");
// Serial.println(LEDStateChar);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
void setup()
{
Serial.begin(115200); /* prepare for possible serial debug */
Blinker_Init();
Display_init();
Bluetooth_Init();
TaskAPPStart();
Serial.println("Setup done");
}
void loop()
{
// Serial.println("Loop");
// sleep(1);
}
图2.4 服务端代码流程
图2.5 传感器端流程图
作品资料
作品源码:
https://bbs.eeworld.com.cn/my/home.php?cur=myhome&act=download
作品演示视频:
4、项目总结
https://bbs.eeworld.com.cn/thread-1290998-1-1.html
https://bbs.eeworld.com.cn/thread-1291483-1-1.html
https://bbs.eeworld.com.cn/thread-1292193-1-1.html
https://bbs.eeworld.com.cn/thread-1295979-1-1.html
感谢EEworld提供的平台,有机会向各位朋友分享自己的DIY成果,谢谢!!!
-
上传了资料:
【2024 DigiKey 创意大赛】室内智能照明灯设计代码与PCB文件
- 2024-10-13
-
发表了主题帖:
【2024 DigiKey 创意大赛】室内智能照明灯设计---进度篇3
10.1假期后继续对室内照明灯的程序继续开发,完整了主题程序架构的设计,目前只剩下温度传感器与MPU6050程序未开发。整理架构采用FreeRtos系统,将整体功能分为不同的线程处理,保证整体功能的实时性。
。。。。。。本来也想将温度传感器加入其中,但是发现D6T-1A-2C的IO电平为5V,与ESP32开发板电平不兼容,故后期考虑采用HT-30温湿度传感器开发。
当前,系统已经集成LCD屏幕与小爱同学功能,具体程序如下:
void UI_Init()
{
lv_obj_t *scr = lv_scr_act(); //创建scr
lv_obj_set_pos(scr,0,0);
lv_scr_load(scr);
label_1 =lv_label_create(scr);//创建label
lv_label_set_recolor(label_1,1);//颜色可变换
lv_label_set_long_mode(label_1,LV_LABEL_LONG_SCROLL_CIRCULAR);//设置滚动模式
lv_obj_set_pos(label_1,100,100);//设置位置
lv_obj_set_size(label_1,160,30);//设定大小
lv_label_set_text(label_1, "This is a GUI thread yes");//设定文本内容
label_2 =lv_label_create(scr);//创建labe2
lv_label_set_recolor(label_2,1);//颜色可变换
lv_label_set_long_mode(label_2,LV_LABEL_LONG_SCROLL_CIRCULAR);//设置滚动模式
lv_obj_set_pos(label_2,100,400);//设置位置
lv_obj_set_size(label_2,160,40);//设定大小
lv_label_set_text(label_2, "#ff0000 red#");//设定文本内容
}
/**
* [url=home.php?mod=space&uid=159083]@brief[/url] 初始化
* @param 无
* @retval 无
*/
void My_Init()
{
Serial.begin(115200);
Serial.println(title + " start");
/*Bllier 初始化*/
Serial.begin(115200);
BLINKER_DEBUG.stream(Serial);
BLINKER_DEBUG.debugAll();
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
Blinker.begin(auth, ssid, pswd);
Blinker.attachData(dataRead);
BlinkerMIOT.attachPowerState(miotPowerState);
BlinkerMIOT.attachColor(miotColor);
BlinkerMIOT.attachMode(miotMode);
BlinkerMIOT.attachBrightness(miotBright);
BlinkerMIOT.attachColorTemperature(miotColoTemp);
BlinkerMIOT.attachQuery(miotQuery);
colorR = 255;
colorG = 255;
colorB = 255;
colorW = 0;
colorT = 0;
wsState = true;
pixels.begin();
pixels.setBrightness(colorW);
WS2812.attach(ws2812_callback);
pixelShow();
/* 屏幕初始化 */
Serial.println("Initialize panel device");
ESP_Panel *panel = new ESP_Panel();
panel->init();
#if LVGL_PORT_AVOID_TEAR
// When avoid tearing function is enabled, configure the RGB bus according to the LVGL configuration
ESP_PanelBus_RGB *rgb_bus = static_cast<ESP_PanelBus_RGB *>(panel->getLcd()->getBus());
rgb_bus->configRgbFrameBufferNumber(LVGL_PORT_DISP_BUFFER_NUM);
rgb_bus->configRgbBounceBufferSize(LVGL_PORT_RGB_BOUNCE_BUFFER_SIZE);
#endif
panel->begin();
/* lvgl初始化 */
Serial.println("Initialize LVGL");
lvgl_port_init(panel->getLcd(), panel->getTouch());
UI_Init();
}
/*======================================= TaskAPPStart ====================================*/
/*
* @brief: 启动创建任务
* @param:none
* @retval:none
*/
void TaskAPPStart()
{
// sem_Blinker = xSemaphoreCreateCounting(1, 0);
// if (sem_Blinker == NULL)
// {
// Serial.println("Blinker信号量创建失败......");
// }
// else
// {
// Serial.println("Blinker信号量创建成功......");
// }
// sem_LVGL = xSemaphoreCreateCounting(1, 0);
// if (sem_LVGL == NULL)
// {
// Serial.println("LVGL信号量创建失败......");
// }
// else
// {
// Serial.println("LVGL信号量创建成功......");
// }
/*创建Blinker线程*/
BaseType_t status;
status = xTaskCreatePinnedToCore(
(TaskFunction_t)ThreadBlinkerEntry, //Weather线程入口
(const char *const) "Thread_Blinker", //线程名称
(const uint32_t)4096 * 2, //线程栈
(void *const)NULL, //Weather线程入口参数
(UBaseType_t)6, //线程优先级 0-24 数值越大优先级越高
(TaskHandle_t *)&ThreadBlinker, //线程句柄
(const BaseType_t)APP_CPU_NUM); //指定内核1
if (status == pdPASS)
{
Serial.println("Blinker线程创建成功...");
}
else
{
Serial.println("Blinker线程创建失败...");
}
/*创建LVGL线程*/
status = xTaskCreatePinnedToCore(
(TaskFunction_t)ThreadLVGLEntry, //OLED线程
(const char *const) "Thread_LVGL", //线程名称
(const uint32_t)4096 * 8, //线程栈
(void *const)NULL, //OLED线程入口参数
(UBaseType_t)2, //线程优先级 0-24 数值越大优先级越高
(TaskHandle_t *)&ThreadLVGL, //线程句柄
(const BaseType_t)APP_CPU_NUM); //指定内核1
if (status == pdPASS)
{
Serial.println("LVGL线程创建成功...");
}
else
{
Serial.println("LVGL线程创建失败...");
}
/*连接网络提示定时器*/
TimeAlarmLED = xTimerCreate(
"TIM_Alarm_LED", //定时器名称
pdMS_TO_TICKS(100), //定时器定时时间
pdTRUE, //pdTRUE = 周期性 , pdFALSE = 一次性
0, //定时器ID
TIMAlarmLEDEntry); //定时器回调函数
xTimerStart(TimeAlarmLED, 0);
delay(1000);
}
/*
* @brief:Blinker线程入口
* @param:none
* @retval:none
*/
void ThreadBlinkerEntry(void *pvParameters)
{
// xSemaphoreTake(sem_Blinker, portMAX_DELAY);
while (1)
{
Blinker.run();
for(int i = 0; i < NUMPIXELS; i++){
pixels.setPixelColor(i, colorR, colorG, colorB);
}
pixels.show();
LedControl(colorW,colorT);
// Serial.println("===================== POST =====================");
vTaskDelay(pdMS_TO_TICKS(10));
}
}
/*
* @brief:LVGL线程入口
* @param:none
* @retval:none
*/
void ThreadLVGLEntry(void *pvParameters)
{
// xSemaphoreTake(sem_LVGL, portMAX_DELAY);
ESP_LOGD(TAG, "Starting LVGL task");
uint32_t task_delay_ms = LVGL_PORT_TASK_MAX_DELAY_MS;
while (1) {
if (lvgl_port_lock(-1)) {
task_delay_ms = lv_timer_handler();
lvgl_port_unlock();
}
if (task_delay_ms > LVGL_PORT_TASK_MAX_DELAY_MS) {
task_delay_ms = LVGL_PORT_TASK_MAX_DELAY_MS;
} else if (task_delay_ms < LVGL_PORT_TASK_MIN_DELAY_MS) {
task_delay_ms = LVGL_PORT_TASK_MIN_DELAY_MS;
}
vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
}
}
/*
* @brief:空闲线程入口
* @param:none
* @retval:none
*/
void TIMAlarmLEDEntry(TimerHandle_t xTimer)
{
// digitalWrite(LED, !digitalRead(LED));
}
void loop()
{
}
-
发表了日志:
【2024 DigiKey 创意大赛】室内智能照明灯设计---进度篇3
- 2024-09-01
-
发表了日志:
【2024 DigiKey 创意大赛】室内智能照明灯设计---进度篇2
-
发表了主题帖:
【2024 DigiKey 创意大赛】室内智能照明灯设计---进度篇2
本帖最后由 mingzhe123 于 2024-9-1 16:24 编辑
有幸进阶得捷创意大赛,趁着周末,对当时的项目进行了一部分开发。目前进度已经完成RGB灯光控制,以及小爱音箱的控制。
下面是详细的代码:
#define BLINKER_WIFI
#define BLINKER_MIOT_LIGHT
#include <Blinker.h>
#include <Arduino.h>
char auth[] = "044be84a71f9";
char ssid[] = "C_WIFI";
char pswd[] = "C88888888";
// Download Adafruit_NeoPixel library here:
// https://github.com/adafruit/Adafruit_NeoPixel
#include <Adafruit_NeoPixel.h>
#define PIN 4
#define NUMPIXELS 1
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_RGB + NEO_KHZ800);
#define RGB_1 "RGBKey"
BlinkerRGB WS2812(RGB_1);
// BlinkerSlider Slider1(Slider_1);
// BlinkerSlider Slider2(Slider_2);
uint8_t colorR, colorG, colorB;
uint8_t colorW; //获取亮度
uint8_t colorT; //获取色温
uint8_t cold;//白光输出值
uint8_t warm;//黄光输出值
bool wsState;
bool COLORM; //彩灯模式
bool READINGM; //阅读模式
uint8_t wsMode = BLINKER_CMD_MIOT_DAY;
void pixelShow() //RGB控制函数
{
pixels.setBrightness(colorW);
for(int i = 0; i < NUMPIXELS; i++){
pixels.setPixelColor(i, colorR, colorG, colorB);
}
pixels.show();
}
void LedControl(float x,int y) //CW控制函数(X:亮度、Y:色温)
{
y = 100 - y ;
cold = x/100*y; //cold_tmp是PWM冷灯珠的占空比
warm = x/100*(100-y); //warm_tmp是PWM冷灯珠的占空比:
}
// void slider1_callback(int32_t value)
// {
// //设置冷光亮度
// ledcWrite(2, value);
// BLINKER_LOG("C value: ", value);
// }
// void slider2_callback(int32_t value)
// {
// //设置暖光亮度
// ledcWrite(1, value);
// BLINKER_LOG("W value: ", value);
// }
void ws2812_callback(uint8_t r_value, uint8_t g_value, uint8_t b_value, uint8_t bright_value) //RGB2812回调函数
{
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
BLINKER_LOG("R value: ", r_value);
BLINKER_LOG("G value: ", g_value);
BLINKER_LOG("B value: ", b_value);
BLINKER_LOG("Rrightness value: ", bright_value);
colorR = r_value;
colorG = g_value;
colorB = b_value;
colorW = bright_value;
pixelShow();
}
uint32_t getColor()
{
uint32_t color = colorR << 16 | colorG << 8 | colorB;
return color;
}
void miotPowerState(const String & state) //用户自定义电源类操作的回调函数:
{
BLINKER_LOG("need set power state: ", state);
if (state == BLINKER_CMD_ON) {
digitalWrite(LED_BUILTIN, HIGH);
BlinkerMIOT.powerState("on");
BlinkerMIOT.print();
wsState = true;
if (colorW == 0) colorW = 255;
}
else if (state == BLINKER_CMD_OFF) {
digitalWrite(LED_BUILTIN, LOW);
BlinkerMIOT.powerState("off");
BlinkerMIOT.print();
wsState = false;
colorW = 0;
}
pixelShow();
}
void miotColor(int32_t color) //用户自定义颜色设置的回调函数:
{
BLINKER_LOG("need set color: ", color);
colorR = color >> 16 & 0xFF;
colorG = color >> 8 & 0xFF;
colorB = color & 0xFF;
BLINKER_LOG("colorR: ", colorR, ", colorG: ", colorG, ", colorB: ", colorB);
pixelShow();
BlinkerMIOT.color(color);
BlinkerMIOT.print();
}
void miotMode(uint8_t mode) //用户自定义模式设置的回调函数:
{
BLINKER_LOG("need set mode: ", mode);
if (mode == BLINKER_CMD_MIOT_DAY) { //日光(10,25,41)
// Your mode function
COLORM = 0;
colorR = 10;
colorG = 255;
colorB = 41;
}
else if (mode == BLINKER_CMD_MIOT_NIGHT) { //月光 自定义
// Your mode function
COLORM = 0;
COLORM = 0;
colorR = 0;
colorG = 0;
colorB = 0;
}
else if (mode == BLINKER_CMD_MIOT_COLOR) { //彩光
// Your mode function
COLORM = 1;
}
else if (mode == BLINKER_CMD_MIOT_WARMTH) { //温馨
// Your mode function
COLORM = 0;
colorR = 10;
colorG = 255;
colorB = 41;
}
else if (mode == BLINKER_CMD_MIOT_TV) { //电视模式
// Your mode function
COLORM = 0;
}
else if (mode == BLINKER_CMD_MIOT_READING) { //阅读模式
// Your mode function
COLORM = 0;
colorR = 0;
colorG = 0;
colorB = 0;
}
else if (mode == BLINKER_CMD_MIOT_COMPUTER) { //电脑模式
// Your mode function
COLORM = 0;
colorR = 0;
colorG = 0;
colorB = 0;
LedControl(colorW,colorT);
}
wsMode = mode;
BlinkerMIOT.mode(mode);
BlinkerMIOT.print();
}
void miotBright(const String & bright) //用户自定义亮度控制的回调函数:
{
BLINKER_LOG("need set brightness: ", bright);
colorW = bright.toInt();
BLINKER_LOG("now set brightness: ", colorW);
pixelShow();
BlinkerMIOT.brightness(colorW);
BlinkerMIOT.print();
}
void miotColoTemp(int32_t colorTemp) //用户自定义色温控制的回调函数:
{
BLINKER_LOG("need set colorTemperature: ", colorTemp);;
BLINKER_LOG("需要设置色温: ", colorTemp);
colorT = map(colorTemp,1000,10000,0,100);
BlinkerMIOT.colorTemp(colorT);
BlinkerMIOT.print();
}
void miotQuery(int32_t queryCode) //用户自定义设备查询的回调函数:
{
BLINKER_LOG("MIOT Query codes: ", queryCode);
switch (queryCode)
{
case BLINKER_CMD_QUERY_ALL_NUMBER :
BLINKER_LOG("MIOT Query All");
BlinkerMIOT.powerState(wsState ? "on" : "off");
BlinkerMIOT.color(0);
BlinkerMIOT.mode(0);
BlinkerMIOT.colorTemp(1000);
BlinkerMIOT.brightness(1);
BlinkerMIOT.print();
break;
case BLINKER_CMD_QUERY_POWERSTATE_NUMBER :
BLINKER_LOG("MIOT Query Power State");
BlinkerMIOT.powerState(wsState ? "on" : "off");
BlinkerMIOT.print();
break;
case BLINKER_CMD_QUERY_COLOR_NUMBER :
BLINKER_LOG("MIOT Query Color");
BlinkerMIOT.color(0);
BlinkerMIOT.print();
break;
case BLINKER_CMD_QUERY_MODE_NUMBER :
BLINKER_LOG("MIOT Query Mode");
BlinkerMIOT.mode(0);
BlinkerMIOT.print();
break;
case BLINKER_CMD_QUERY_COLORTEMP_NUMBER :
BLINKER_LOG("MIOT Query ColorTemperature");
BlinkerMIOT.colorTemp(1000);
BlinkerMIOT.print();
break;
case BLINKER_CMD_QUERY_BRIGHTNESS_NUMBER :
BLINKER_LOG("MIOT Query Brightness");
BlinkerMIOT.brightness(1);
BlinkerMIOT.print();
break;
default :
BlinkerMIOT.powerState(wsState ? "on" : "off");
BlinkerMIOT.color(0);
BlinkerMIOT.mode(0);
BlinkerMIOT.colorTemp(1000);
BlinkerMIOT.brightness(1);
BlinkerMIOT.print();
break;
}
}
void dataRead(const String & data)
{
BLINKER_LOG("Blinker readString: ", data);
Blinker.vibrate();
uint32_t BlinkerTime = millis();
Blinker.print("millis", BlinkerTime);
}
void setup()
{
Serial.begin(115200);
BLINKER_DEBUG.stream(Serial);
BLINKER_DEBUG.debugAll();
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
Blinker.begin(auth, ssid, pswd);
Blinker.attachData(dataRead);
BlinkerMIOT.attachPowerState(miotPowerState);
BlinkerMIOT.attachColor(miotColor);
BlinkerMIOT.attachMode(miotMode);
BlinkerMIOT.attachBrightness(miotBright);
BlinkerMIOT.attachColorTemperature(miotColoTemp);
BlinkerMIOT.attachQuery(miotQuery);
// pinMode(14, OUTPUT);
// digitalWrite(14, HIGH);
// pinMode(15, OUTPUT);
// digitalWrite(15, HIGH);
colorR = 255;
colorG = 255;
colorB = 255;
colorW = 0;
colorT = 0;
wsState = true;
pixels.begin();
pixels.setBrightness(colorW);
WS2812.attach(ws2812_callback);
pixelShow();
// Slider1.attach(slider1_callback);
// Slider2.attach(slider2_callback);
}
void loop()
{
Blinker.run();
for(int i = 0; i < NUMPIXELS; i++){
pixels.setPixelColor(i, colorR, colorG, colorB);
}
pixels.show();
LedControl(colorW,colorT);
}
通过该代码可以实现点灯上位机软件对RGB灯光的控制,并可以通过小爱音箱调整RGB颜色、色温、亮度以及开关等;还可以通过小爱音箱控制不同的工作模式。
- 2024-08-25
-
发表了主题帖:
【2024 DigiKey 创意大赛】室内智能照明灯设计---进度篇_1
收到室内智能照明灯设计的物料已经差不都熬了,趁着这个周末有时间赶紧跟各位大大汇报一下任务进度。
首先,整个智能灯的框架已经确认了。主要使用ESP32 Board同时控制温度传感器读取温度,并通过MPU6050与小爱同学控制RGB灯光的状态,其中小爱同学的控制主要使用点灯平台进行互联。具体架构如下:
对于小爱同学部分的代码直接参照点灯官网源码开发即可https://diandeng.tech/doc/getting-start-ble。
对于D6T-1A-02 的ARDUINO代码,则可直接参照GitHub的源码即可 https://github.com/omron-devhub/d6t-2jcieev01-arduino
好了,这周就进行这么多了,下周等D6T-1A-02的接插件到了开始代码调试。
- 2024-08-19
-
发表了主题帖:
【2024 DigiKey 创意大赛】室内智能照明灯设计---物料开箱
时隔几天,设计的资料终于收到了,首先感谢DigiKey提供的物料,也感谢电子工程世界论坛的鼎立支持。
接下来就到激动人心的开箱环节了,哈哈哈。
确认过,DigiKey的包装就是给力
继续开开箱,内部包装也是给力,防静电袋,泡沫带,给力。
本次活动一共申请了2颗物料分别为ESP32开发板,屏幕非常给力。另一颗为欧姆龙的温度传感器。另外需要的陀螺仪我这边本身就有,接下来就利用esp32开发板、温度传感器与陀螺仪进行项目开发了。