- 2024-09-23
-
加入了学习《【Follow me第二季第2期】+开发板硬件介绍和实现任务一 LED灯闪烁和串口打印》,观看 【Follow me第二季第2期】+开发板硬件介绍和实现任务一 LED灯闪烁和串口打印
-
回复了主题帖:
【Follow me第二季第2期】Arduino Uno R4 WiFi 通过MQTT连入Home Assistant
Jacktang 发表于 2024-9-23 07:28
容器技术来安装和运行Home Assistant这个看来是有技巧的
主要是安装后配置文件太麻烦了 还是自发现比较适合实际应用
- 2024-09-22
-
加入了学习《【Follow me第二季第2期】+开发板硬件介绍和实现任务一 LED灯闪烁和串口打印》,观看 【Follow me第二季第2期】+通过外部SHT40温湿度传感器,上传温湿度到HA
-
发表了主题帖:
【Follow me第二季第2期】 所有帖子汇总及其思路分析
本帖最后由 一只小跳帽 于 2024-9-23 21:17 编辑
任务一
大家好!很高兴能与大家分享关于我们申请到的Arduino UNO R4 WiFi开发板的硬件资源。这款开发板在保留了Arduino UNO R3大部分接口的基础上,进行了一些升级并增加了新的功能。
与R3的兼容性:它保留了与R3相同的大部分接口,这意味着我们可以很方便地在R3和R4之间进行切换,同时兼容大部分为R3设计的扩展板和项目。
新增CAN通信接口:最显著的是增加了CAN(Controller Area Network)通信接口。CAN是一种常用的工业通信协议,广泛应用于汽车、工业控制等领域。通过增加CAN接口,我们可以模拟更多的功能,实现与各种CAN设备的通信和控制。
实现模拟输出:这款开发板还增加了DAC(Digital-to-Analog Converter,数字到模拟转换器)接口,实现了模拟输出功能。这使得Arduino UNO R4 WiFi不仅能够处理数字信号,还能处理模拟信号,进一步扩展了其应用范围。
总的来说,Arduino UNO R4 WiFi开发板在保留了原有功能的基础上,通过增加CAN通信接口和DAC接口,提供了更丰富的硬件资源和更广泛的应用场景。无论是对于初学者还是对于需要进行复杂项目开发的用户来说,都是一个非常不错的选择。
下面我们通过原理图来详细查看。本次Arduino UNO R4 WiFi开发板增加了USB Type-C接口,这是一个重要的升级。通过USB Type-C接口,开发板可以实现直接模拟串口进行通信,这与以往将P1和P2引出到USB A型接口的设计有所不同。因此,在设计时我们需要格外注意这一点。
另外,新增加的LED矩阵也是本次升级的一个亮点。这个LED矩阵使得开发板可以显示更多的信息,为用户提供了更加丰富的视觉反馈和交互体验。
总的来说,通过原理图我们可以清晰地看到Arduino UNO R4 WiFi开发板在硬件资源上的升级和增加的新功能。这些改进不仅提升了开发板的性能,还扩展了其应用场景,为用户带来了更多的便利和可能性。
在关注Arduino UNO R4 WiFi开发板时,我们特别注意到其上的瑞萨MCU是5V芯片,而ESP32则是3.3V芯片。由于这两种芯片的电压要求不同,因此需要使用电平转换芯片来进行通信,以确保信号的稳定传输。此外,这两个芯片之间的通信是通过串口来实现的,这使得它们能够方便地交换数据和信息。在设计时,我们需要特别注意电平转换芯片的选择和使用,以确保开发板的正常工作和稳定性。
下面我们来完成本次任务1:blink。通过原理图,我们可以看到使用13端口来控制黄色的LED灯进行闪烁。
首先,我们需要配置13端口为输出模式,这样我们才能通过它来控制LED灯的亮灭。在Arduino编程中,我们可以使用
pinMode()
函数来设置端口模式,使用
digitalWrite()
函数来控制端口的电平。
接下来,在
loop()
循环中,我们使用
delay()
函数进行延迟,先延迟1000毫秒(即1秒),然后点亮LED灯;再延迟1000毫秒,熄灭LED灯。这样,LED灯就会以1秒的间隔进行闪烁。
以下是实现该功能的Arduino代码示例:
复制
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
效果如下
接下来我们来完成第二个任务:打印“Hello EEWorld!”。这个任务相对简单,我们只需要在
loop()
循环中每隔一秒通过串口打印一次即可。
首先,我们需要确保已经通过USB Type-C接口将开发板连接到电脑,并在Arduino IDE中选择正确的串口。
然后,在
setup()
函数中,我们使用
Serial.begin()
函数来初始化串口通信,设置波特率为9600(或其他你想要的波特率)。
在
loop()
函数中,我们使用
Serial.println()
函数来打印“Hello EEWorld!”,并使用
delay()
函数来每隔一秒打印一次。
以下是实现该功能的Arduino代码示例:
复制
void setup() {
// 初始化串口通信,设置波特率为9600
Serial.begin(9600);
}
void loop() {
// 每隔一秒打印一次“Hello EEWorld!”
Serial.println("Hello EEWorld!");
delay(1000);
}
将上述代码上传到Arduino UNO R4 WiFi开发板后,打开Arduino IDE的串口监视器,设置正确的波特率,你就可以看到每隔一秒就打印一次“Hello EEWorld!”了。这样,我们就完成了第二个任务。
下面我们把两个都结合起来代码如下
复制
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println("Hello EEWorld!");
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
初次接触Arduino R4开发板,我深感其魅力无穷。这款开发板不仅继承了Arduino系列一贯的易用性和灵活性,还在硬件资源上进行了显著的升级和扩展。
在使用过程中,我深刻体会到了Arduino R4的强大功能。新增的USB Type-C接口让我能够更方便地与电脑进行通信和数据传输,而无需担心接口不兼容的问题。同时,开发板上的LED矩阵也为我提供了丰富的视觉反馈,使得调试和交互变得更加直观和便捷。
在编程方面,Arduino R4同样表现出色。我使用了Arduino IDE进行代码编写和上传,整个过程非常顺畅。通过简单的代码,我就能够控制开发板上的各种硬件资源,实现各种有趣的功能和效果。
当然,初次使用Arduino R4也遇到了一些挑战。比如,我需要适应新的硬件资源和接口,了解它们的工作原理和使用方法。但是,通过查阅资料和不断实践,我逐渐掌握了这些新知识,并成功完成了多个有趣的项目。
总的来说,初次使用Arduino R4开发板是一次非常愉快和充实的经历。我不仅学到了很多新知识,还感受到了Arduino系列开发板的无限魅力和潜力。我相信,在未来的学习和实践中,我会更加深入地了解和掌握这款开发板,创造出更多有趣和实用的项目。
下面的视频为这个项目的整个过程
任务二
今天我们将着手完成任务二的第一个关键任务——驱动一个由12x8 LED灯组成的点阵屏。
点阵屏的巧妙之处:
高效控制:该点阵屏仅通过10个管脚就能实现全面控制,这得益于其独特的点阵布局和复用技术。
硬件设计:从硬件原理图上,我们可以清晰看到,通过特定的电路逻辑,如当管脚7为高电平而管脚3为低电平时,能够点亮LED1;反之,则能点亮LED2。
刷新策略:
逐列刷新:为了有效减少管脚需求并实现流畅的视觉效果,我们将采用逐列刷新的策略。这意味着我们将以极快的速度依次激活每一列(在此场景下,我们将其视为“行”以符合点阵屏的常见操作方式),并在激活的同时,通过其他管脚设置相应的列(行)数据。
视觉暂留:由于人眼的视觉暂留效应,这种高速但有序的刷新过程将使得LED的点亮看起来是连续的,而非闪烁的。
动画效果实现:
帧数据设计:为了创造动画效果,我们将设计一系列帧数据,每帧数据代表点阵屏上LED的一种特定点亮状态。
显示控制:通过依次显示这些帧,并精确控制每帧之间的显示时间间隔,我们就能够呈现出动态变化的图像,即动画效果。
总结:
通过巧妙利用点阵屏的复用技术和高速刷新策略,我们成功地实现了仅用10个管脚对12x8 LED点阵屏的全面控制
Arduino官方特别为点阵屏开发了一套高效的API,通过引入
Arduino_LED_Matrix.h
库,开发者可以便捷地控制点阵屏。首先,你需要创建一个点阵屏对象,例如命名为
ledMatrix
,以
ArduinoLEDMatrix
类实例化:
cpp复制代码
ArduinoLEDMatrix ledMatrix;
接下来,进行初始化设置,准备控制一个12x8的点阵屏。这个库允许你使用一个12*8的二维数组来直接控制屏幕上的每个LED,其中数组元素为1时表示LED点亮,为0时表示熄灭。
通过这种方式,在程序中动态地修改这个二维数组中的0和1,即可轻松实现不同画面的展示。这种API的设计极大地简化了点阵屏的控制逻辑,使得开发者能够更专注于创意和内容的实现,而非底层的硬件操作。
总结来说,Arduino官方提供的
Arduino_LED_Matrix
库为点阵屏开发带来了前所未有的便利,通过简单的API调用和直观的二维数组控制,即可实现复杂多变的显示效果。
官方还开发了一个小的插件可以进行创作动画大家可以通过下方的视频进行查看使用方法
下面是我的这个 任务的代码大致内容过程为通过工具设置一个动画并且加载进去
复制
#include "Arduino_LED_Matrix.h"
#include <stdint.h>
#include "animation.h"
ArduinoLEDMatrix matrix;
void setup() {
// put your setup code here, to run once:
matrix.loadSequence(animation);
matrix.begin();
// turn on autoscroll to avoid calling next() to show the next frame; the parameter is in milliseconds
// matrix.autoscroll(300);
matrix.play(true);
}
void loop() {
// put your main code here, to run repeatedly:
}
const uint32_t animation[][4] = {
{
0xfff00000,
0x0,
0x0,
66
},
{
0xfff00,
0x0,
0x0,
66
},
{
0xff,
0xf0000000,
0x0,
66
},
{
0x0,
0xfff0000,
0x0,
66
},
{
0x0,
0xfff0,
0x0,
66
},
{
0x0,
0xf,
0xff000000,
66
},
{
0x0,
0x0,
0xfff000,
66
},
{
0x0,
0x0,
0xfff,
66
}
};
下面完成第二个任务首先使用DAC产生一个正弦波
DAC,全称Digital-to-Analog Converter,即数字模拟转换器,是一种电子设备或电路,其核心功能是将数字信号转换为相应的模拟信号。在现代电子系统中,DAC扮演了至关重要的角色,它是连接数字电路与模拟电路的桥梁,为数字系统与模拟系统之间的数据交互提供了基础。
DAC(Digital-to-Analog Converter,数模转换器)和PWM(Pulse Width Modulation,脉冲宽度调制)在电子系统中扮演着不同的角色,它们之间存在显著的区别。以下是DAC和PWM之间的主要区别:
1. 功能与原理
DAC:DAC是一种电子设备或电路,用于将数字信号转换为模拟信号。它接收离散的数字信号(如二进制代码),并将其转换为连续可变的模拟信号(如电压或电流)。DAC广泛应用于音频设备、控制系统、仪器仪表等领域,作为数字电路与模拟电路之间的桥梁。
PWM:PWM是一种通过调节信号的占空比来模拟连续模拟信号的技术。PWM信号是方波,其频率固定,但占空比(即高电平持续的时间与周期的比值)可变。通过调整占空比,PWM可以模拟出不同幅度的模拟信号。PWM广泛应用于高功率转换效率的电源、马达控制、音频放大等领域。
2. 输出特性
DAC:DAC的输出是连续的模拟信号,其幅度可以在一定范围内连续变化。DAC的输出信号质量高,能够还原音频的细节和动态特性,适用于需要高保真度音频输出的场合。
PWM:PWM的输出是方波信号,其幅度本身并不连续变化,但通过滤波等处理可以转换为近似的模拟信号。PWM的输出效率高,功耗低,适用于需要高效率低功耗的音频驱动等场合。
3. 应用领域
DAC:DAC广泛应用于音频设备(如CD播放器、功放)、控制系统(如工业自动化控制)、仪器仪表(如示波器、频谱分析仪)等领域。在这些领域中,DAC负责将数字信号转换为模拟信号以供后续处理或输出。
PWM:PWM广泛应用于高功率转换效率的电源(如开关电源)、马达控制(如电机驱动器)、音频放大(如数字音频放大器)等领域。在这些领域中,PWM通过调节占空比来控制功率元件的通断,从而实现高效能的功率转换或音频放大。
4. 精度与分辨率
DAC:DAC的精度和分辨率取决于其位数(如8位、12位、16位等)。位数越高,DAC能够输出的模拟信号值数量越多,精度和分辨率也就越高。
PWM:PWM的精度和分辨率受到其频率和占空比调节精度的限制。虽然通过提高PWM的频率和占空比调节精度可以提高其模拟精度,但通常难以达到DAC那样的高精度和高分辨率。
综上所述,DAC和PWM在功能、原理、输出特性、应用领域以及精度与分辨率等方面都存在显著的区别。
在R3版本中,所有输出均基于PWM技术实现。然而,在R4版本中,我们引入了DAC(数字模拟转换器)于A引脚,尽管其输出调用的函数仍为
analogWrite()
,但实质上提供了与PWM不同的模拟信号生成机制。
下面我们来分析一下这个函数
复制
void analogWrite(pin_size_t pinNumber, int value)
{
#if (DAC12_HOWMANY > 0) || (DAC8_HOWMANY > 0)
if (IS_DAC(pinNumber)) {
auto cfg_dac = getPinCfgs(pinNumber, PIN_CFG_REQ_DAC);
if(IS_DAC_8BIT(cfg_dac[0])) {
#if DAC8_HOWMANY > 0
if(GET_CHANNEL(cfg_dac[0]) < DAC8_HOWMANY) {
_dac8[GET_CHANNEL(cfg_dac[0])].analogWrite(value);
}
#endif
}
else {
if(GET_CHANNEL(cfg_dac[0]) < DAC12_HOWMANY) {
_dac12[GET_CHANNEL(cfg_dac[0])].analogWrite(value);
}
}
return;
}
这里我们可以看出使用IS_DAC()来进行判断是否为DAC引脚如果是的话使用DAC输出方式如果不是的话使用普通的PWM方式
因此,接下来我们可以利用 `sin()` 函数来生成一个正弦波,并使用变量 `a` 来作为一个因子。根据我们在高中所学的知识,正弦波的函数形式为 `f(x) = a * sin(kx + b)` 。其中,`a` 表示振幅,在此我们暂且固定不变。那么,我们可以通过改变 `k` 和 `b` 的值,分别来调整正弦波的周期和相位。接下来,我们将通过实时改变这两个参数的值,来实现对所打印函数图形的实时更新。现在,让我们着手实现这一部分。
生成DAC正弦波的代码
复制
analogWriteResolution(12);
// put your main code here, to run repeatedly:
a+=0.01;
if(a>2*PI)
{
a=0;
}
float out=map_float(sin(K*a+B),-1,1,0,4096);
analogWrite(DAC, out);
在这里需要特别注意的是,`analogWriteResolution()` 函数会改变 DAC(数模转换器)的精度。关于这部分的详细说明,我会在下面的视频中进行阐述。此外,`map_float` 函数与 `map` 函数的作用是相似的。由于 `sin` 函数的输出范围是 -1 到 1,因此我们需要将其映射到 0 到 4096 的区间内。
下面我们进行配置运算放大器 opamp 有三个输入口一个+输入一个-输入一个输出 + 为A1 -为A2 输出为A3 官方给了一个典型电路和计算公式
这里我们进行使用一种典型电路及输入就是输出连接方法为输出连接-输入
这里配置为高速模式
OPAMP.begin(OPAMP_SPEED_HIGHSPEED);
还有最后就是读取ADC并且打印这里我使用的A5 这个就很简单了接收并且打印
复制
int reading = analogRead(A5);
//a=map(reading,0,4096,0,2*3.1415926);
Serial.println(reading);
接下来,我将为大家介绍如何改变 `k` 和 `b` 的值。我所采用的方法是通过串口发送数据,数据格式为“A:15”。在接收到字符串后,我会对其进行解析,以提取冒号前后的数据,从而得到 `A` 和 `15`,进而相应地更改对应的值。
代码如下
复制
String str="";
if (Serial.available() > 0) {
// 读取一个字节的数据,并将其转换为字符后添加到receivedString中
// 注意:这里假设我们不知道字符串何时结束,因此持续读取直到超时或检测到特定的结束字符
char incomingByte = Serial.read();
// 检查是否是字符串的结束符(例如换行符'\n')
// 注意:根据你的发送方,结束符可能不同
if (incomingByte == '\n') {
// 如果是结束符,则不将其添加到receivedString中,而是直接处理字符串
// 在这里,我们只是简单地打印它
parseString(receivedString);
// 清空receivedString,为接收下一个字符串做准备
receivedString = "";
} else {
// 如果不是结束符,则将其添加到receivedString中
receivedString += incomingByte;
}
}
void parseString(String input) {
// 查找冒号在字符串中的位置
int colonIndex = input.indexOf(':');
if (colonIndex == -1) {
Serial.println("Input format is incorrect. Missing colon.");
return;
}
// 提取标识符(冒号之前的部分)
String identifier = input.substring(0, colonIndex);
// 提取数字(冒号之后的部分),注意substring的第二个参数是结束索引(不包含),所以我们需要+1
String numberStr = input.substring(colonIndex + 1);
int number = numberStr.toInt(); // 将字符串转换为整数
if(identifier=="K")
{
K=number;
}
if(identifier=="B")
{
B=number;
}
}
完整的代码
复制
#include <OPAMP.h>
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
OPAMP.begin(OPAMP_SPEED_HIGHSPEED);
analogReadResolution(14);
analogWriteResolution(12);
}
double a=0;
String receivedString = "";
int K=1;
int B=0;
void loop() {
// put your main code here, to run repeatedly:
a+=0.01;
if(a>2*PI)
{
a=0;
}
float out=map_float(sin(K*a+B),-1,1,0,4096);
analogWrite(DAC, out);
// Serial.println(out);
int reading = analogRead(A5);
//a=map(reading,0,4096,0,2*3.1415926);
Serial.println(reading);
//Serial.print(",");
//Serial.println(out);
String str="";
if (Serial.available() > 0) {
// 读取一个字节的数据,并将其转换为字符后添加到receivedString中
// 注意:这里假设我们不知道字符串何时结束,因此持续读取直到超时或检测到特定的结束字符
char incomingByte = Serial.read();
// 检查是否是字符串的结束符(例如换行符'\n')
// 注意:根据你的发送方,结束符可能不同
if (incomingByte == '\n') {
// 如果是结束符,则不将其添加到receivedString中,而是直接处理字符串
// 在这里,我们只是简单地打印它
parseString(receivedString);
// 清空receivedString,为接收下一个字符串做准备
receivedString = "";
} else {
// 如果不是结束符,则将其添加到receivedString中
receivedString += incomingByte;
}
}
}
//AB
void parseString(String input) {
// 查找冒号在字符串中的位置
int colonIndex = input.indexOf(':');
if (colonIndex == -1) {
Serial.println("Input format is incorrect. Missing colon.");
return;
}
// 提取标识符(冒号之前的部分)
String identifier = input.substring(0, colonIndex);
// 提取数字(冒号之后的部分),注意substring的第二个参数是结束索引(不包含),所以我们需要+1
String numberStr = input.substring(colonIndex + 1);
int number = numberStr.toInt(); // 将字符串转换为整数
if(identifier=="K")
{
K=number;
}
if(identifier=="B")
{
B=number;
}
}
float map_float(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
最后的结果图片我使用的是VOFA串口助手打印的因为arduino的串口示波器更改有点麻烦
中间是更改K和B值的改变下面的视频为详细教程和结果和一些其他的不太好纸面介绍的知识
任务三
通过Wi-Fi,利用MQTT协议接入到开源的智能家居平台HA(HomeAssistant)
这个任务在于是否能够理解mqtt协议和部署HomeAssistant
大家可以先安下面的流程图来完成如果不可以的话可以看我的任务提交贴
今天,我们将细致而有序地完成一个项目——搭建Home Assistant平台,并通过MQTT协议实现设备接入。整个过程将分为四个清晰、连贯的步骤,确保每一步都精准无误,助力您成功构建智能家居生态系统。
第一步:安装Home Assistant
首先,我们将着手安装Home Assistant。这一步骤是构建智能家居中枢的关键,Home Assistant以其强大的集成能力和灵活的自定义选项而闻名,能够轻松管理家中的各种智能设备。我们将按照官方指南或选择适合您操作系统的安装方法,确保Home Assistant能够稳定运行在您的设备上。
第二步:部署MQTT服务器
紧接着,我们将安装并配置MQTT服务器。MQTT,即消息队列遥测传输,是一种轻量级的、基于发布/订阅模式的消息协议,非常适合物联网(IoT)设备的通信。我们将选择一个稳定可靠的MQTT服务器软件(如Mosquitto),并根据实际需求进行配置,确保它能够高效、安全地处理来自智能家居设备的消息。
第三步:将Home Assistant连接到MQTT服务器
在成功部署MQTT服务器后,下一步是将Home Assistant与之连接起来。这一步骤至关重要,因为它将使得Home Assistant能够接收并处理来自MQTT服务器的消息,进而实现对智能家居设备的控制。我们将通过Home Assistant的配置界面,添加MQTT集成,并填写MQTT服务器的相关信息,完成连接设置。
第四步:使用MQTT客户端进行连接测试
为了确保MQTT服务器的正常运行以及Home Assistant与MQTT服务器的成功连接,我们将使用MQTT客户端工具进行连接测试。这一步骤不仅验证了通信链路的通畅性,还让我们有机会熟悉MQTT的基本操作,为后续的设备接入打下坚实基础。
最后一步:使用Arduino R4 WiFi开发板接入系统
作为整个项目的亮点,我们将使用Arduino R4 WiFi开发板,通过编程使其能够通过MQTT协议与Home Assistant进行通信。通过编写相应的代码,Arduino R4 WiFi将能够发送状态信息至MQTT服务器,并接收来自Home Assistant的控制指令,实现智能家居设备的智能化控制。这一步骤将充分展示MQTT在物联网应用中的强大潜力。
首先,我们将通过安装Linux虚拟机来启动我们的Home Assistant部署之旅,这里特别选择Ubuntu系统作为我们的基础平台。Ubuntu以其完善的生态系统、广泛的用户群体以及丰富的解决方案而著称,这使得它成为部署智能家居控制中心——Home Assistant的理想选择。
在准备阶段,我们将确保Ubuntu虚拟机被正确安装并配置,以便为后续步骤提供一个稳定、可靠的环境。通过选择Ubuntu,我们不仅能够享受到其强大的功能和灵活性,还能轻松获取到来自全球开发者和社区的支持与帮助,这对于解决在部署过程中可能遇到的各种问题至关重要。
接下来,我们将利用容器技术来安装和运行Home Assistant。容器化部署以其轻量级、可移植性和易于管理的特点,在现代软件开发和运维中得到了广泛应用。通过容器,我们可以将Home Assistant及其依赖项封装在一个独立的虚拟环境中,从而避免与系统其他部分的潜在冲突,并确保其稳定运行。
综上所述,选择Ubuntu系统作为Linux虚拟机的基础,并结合容器技术来安装Home Assistant,将为我们提供一个强大、灵活且易于维护的智能家居控制中心解决方案。
下面进行安装docker
#安装前先卸载操作系统默认安装的docker,
sudo apt-get remove docker docker-engine docker.io containerd runc
#安装必要支持
sudo apt install apt-transport-https ca-certificates curl software-properties-common gnupg lsb-release
#添加 Docker 官方 GPG key (可能国内现在访问会存在问题)
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# 阿里源(推荐使用阿里的gpg KEY)
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
#添加 apt 源:
#Docker官方源
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
#阿里apt源
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
#更新源
sudo apt update
sudo apt-get update
#安装最新版本的Docker
sudo apt install docker-ce docker-ce-cli containerd.io
#等待安装完成
#查看Docker版本
sudo docker version
#查看Docker运行状态
sudo systemctl status docker
现在安装好docker 下面安装docker版的Home Assistant
docker search homeassistant
2、拉取docker镜像
复制
docker pull homeassistant/home-assistant:latest
3、创建容器并运行
复制
docker run -d --name="hass" -v /path/to/config:/config -v /etc/localtime:/etc/localtime:ro -p 8123:8123 --net
-d: 表示在后台运行容器。
--name my_home_assistant_instance: 指定容器的名称为 "my_home_assistant_instance",你可以根据需要修改。
-v /path/to/config:/config: 将本地配置目录映射到容器内的 /config 目录。确保将 /path/to/config 替换为你的 Home Assistant 配置目录的实际路径。
-v /etc/localtime:/etc/localtime:ro: 将主机的时间配置映射到容器内,保持容器和主机时间同步。
--net=host: 使用主机网络模式,使得容器可以直接使用主机的网络配
-p:映射端口(容器内的端口直接映射到本地主机端口最后便是刚才下载的镜像了,运行该容器。
注:这里启动docker容器之后每次启动docker容器都会数据初始化建议-v进行数据挂载
docker 查看并启动容器
//查看容器信息
docker ps -a
//docker 使用<container_id>启动容器
docker start <container_id>
//使用容器名称启动--name 参数指定容器的名称为 "my_container_instance"。my_image:tag 是你要启动的 Docker 镜像的名称和标签。
docker run --name my_container_instance my_image:tag
docker stop <container_id>
通过上面看到我们已经创建 了Home Assistant 的镜像下面进行启动
这里我们已经启动了Home Assistant 下面我们使用浏览器进行打开首先使用ifconfig 查看ip再使用端口8123 进行访问
现在安装好了 但是还没有mqtt服务因此不能使用mqtt连接下面安装mqtt服务器
1 认识EMQX
1.1 EMQX 简介
EMQX 是一款开源的大规模分布式 MQTT 消息服务器,功能丰富,专为物联网和实时通信应用而设计。EMQX 5.0 单集群支持 MQTT 并发连接数高达 1 亿条,单服务器的传输与处理吞吐量可达每秒百万级 MQTT 消息,同时保证毫秒级的低时延。
EMQX 支持多种协议,包括 MQTT (3.1、3.1.1 和 5.0)、HTTP、QUIC 和 WebSocket 等,保证各种网络环境和硬件设备的可访问性。EMQX 还提供了全面的 SSL/TLS 功能支持,比如双向认证以及多种身份验证机制,为物联网设备和应用程序提供可靠和高效的通信基础设施。
1.2 EMQX 版本类型
EMQX 有 4 种部署模式,包括两种云服务模式(EMQX Cloud Serverless 和 EMQX Cloud 专有版)和两种自托管模式(EMQX 开源版 和 EMQX 企业版)。以下表格列出了这些部署模式的对比,以帮助您根据业务需求进行选择。想进一步了解具体的功能对比,参考功能对比。
对于非企业级应用,使用EMQX开源版即可,本文也是以该版本作为Demo,介绍其搭建和使用方法。
2 Ubuntu搭建EMQX 平台
2.1 下载和安装
2.1.1 下载
下载地址:
https://www.emqx.io/zh/downloads
打开网站,选择安装环境:
2.1.2 安装
使用如下三个步骤在Ubuntu上安装EMQX:
Step 1: 从软件链接源下载软件包,并装载安装环境
curl -s https://assets.emqx.com/scripts/install-emqx-deb.sh | sudo bash
下载完成后,可以看见如下log:
Step-2: 安装软件
sudo apt-get install emqx
Step-3: 运行软件
执行如下代码,如果没有任何信息打印出来,说明EMAX已经正常启动了
sudo systemctl start emqx
2.2 查看运行端口
和EMQX相关的端口有如下这些:
端口号 介绍
1883 MQTT 协议端口
8883 MQTT/SSL 端口
8083 MQTT/WebSocket 端口
8080 HTTP API 端口
18083 Dashboard 管理控制台端口
查看EMQX的运行端口:
netstat -ap | grep 18083
查看MQTT 协议端口:
netstat -ap | grep 1883
3 运行Dashboard 管理控制台
要在第三方终端上运行Dashboard 管理控制台,必须保证18083端口运行被访问,那么怎么做呢?检查该端口是否被允许外网访问。
3.1 查看Ubuntu上的防火墙
首先确保ufw 已经安装在当前的Ubuntu系统中,如果没有安装,使用如下命令:
sudo apt update
sudo apt install ufw
一种最简单的方式就是关闭防火墙,如果允许外网访问,不建议这样做:
sudo ufw disable
3.2 运行Dashboard 管理控制台
step-1: 查看当前Ubuntu主机的ip,使用命令:
ifconfig
执行命令后,终端会打印当前主机所有网卡相关的IP信息,找到主机的实际ip,然后在第三方电脑主机上登录
step-2: 登录服务器
打开浏览器,输入相应IP和端口号,就能打开网页了。举个例子,以笔者本人测试主机为例
192.168.1.11:18083
如果安装成功,可以看见如下页面,说明EMQX可以正常工作了。
第三步使用 Home Assistant 连接到mqtt 服务器
在设置的设备与服务中搜索mqtt 进行配置 ip地址就是自己本地的ip 端口使用的是mqtt统一端口1883 密码和用户名使用自己设置的就可以设置成功后就可以看到集群中连接的设备和订阅的主题
这个就是Home Assistant 的设备
下面我们先使用mqtt的客户端连接到mqtt服务器 这里我使用的是mqtt客户端工具
使用这个工具我们可以很简单的模拟我们的开发板如何配置mqtt和发送的主题
首先我们先配置端口ip地址然后进行连接
然后点击连接在浏览器上查看节点是否连接成功
这里看到已经有两个节点了有一个就是我们刚刚连接的客户端
下面我们要把这个设备连接到我们的 Home Assistant 中这里很多人选择直接配置设备文件但是我发现这种方法十分的不麻烦而且也不利于我们后续的设备自己连接函数需要我们一个一个的配置下面我使用的方法是使用设置自动的添加到我们这个系统中 只需要向特定的主题发送对应的内容就可以下面我来试一下
这里我们选择配置成我们下一个任务的 温湿度传感器类型进行发现
这里看到我们已经成功的添加了一个设备并且配置他的两个状态为 一个是温度一个是湿度下面我们对其进行发送状态
这个已经有数值了 下面我们先通过开发板连接上mqtt 下一节我们再上传报文
首先我们使用PubSubClient 库进行连接下面简单介绍一下连接的流程首先创建一个实体类
复制
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
后续的所有操作都是使用的这个实体
下一步进行连接到mqtt服务器
复制
void connectMQTTServer(){
String clientId = "UNOR4";
// 连接MQTT服务器
if (mqttClient.connect(clientId.c_str())) {
Serial.println("MQTT Server Connected.");
Serial.println("Server Address: ");
Serial.println(mqttServer);
Serial.println("ClientId:");
Serial.println(clientId);
} else {
Serial.print("MQTT Server Connect Failed. Client State:");
Serial.println(mqttClient.state());
delay(3000);
}
}
后面再进行学习上面的mqtt客户端发送给服务器的主题和内容进行发送让Home Assistant 自发现的方式
注意我后面因为网关的原因自己进行配置了一次网络
复制
void Register_temp()
{
// Stream& output;
const char * topics="homeassistant/sensor/sensorBedroomT/config";
String output;
JsonDocument doc;
doc["device_class"] = "temperature";
doc["name"] = "Temperature";
doc["state_topic"] = "homeassistant/sensor/sensorroom/state";
doc["value_template"] = "{{ value_json.temperature}}";
doc["unique_id"] = "temp01ae";
JsonObject device = doc["device"].to<JsonObject>();
device["identifiers"][0] = "room01";
device["name"] = "room";
doc.shrinkToFit(); // optional
serializeJson(doc, output);
Serial.println(output.length());
if(mqttClient.publish(topics, output.c_str())){
Serial.println("Publish topic:");Serial.println(topics);
Serial.println("Publish message:");Serial.println(output);
} else {
Serial.println("Message Publish Failed.");
}
}
void Register_hum()
{
// Stream& output;
const char * topics="homeassistant/sensor/sensorBedroomH/config";
String output;
JsonDocument doc;
doc["device_class"] = "humidity";
doc["name"] = "Humidity";
doc["state_topic"] = "homeassistant/sensor/sensorroom/state";
doc["unit_of_measurement"] = "%";
doc["value_template"] = "{{ value_json.humidity}}";
doc["unique_id"] = "hum01ae";
JsonObject device = doc["device"].to<JsonObject>();
device["identifiers"][0] = "room01";
device["name"] = "room";
doc.shrinkToFit(); // optional
serializeJson(doc, output);
Serial.println(output);
Serial.println(output.length());
if(mqttClient.publish(topics, output.c_str())){
Serial.println("Publish topic:");Serial.println(topics);
Serial.println("Publish message:");Serial.println(output);
} else {
Serial.println("Message Publish Failed.");
}
}
运行后已经连接上了
到这里已经完全了下面是整个的代码
复制
//#include <WiFiS3.h>
#include <PubSubClient.h>
#include <WiFi.h>
#define ARDUINOJSON_SLOT_ID_SIZE 1
#define ARDUINOJSON_STRING_LENGTH_SIZE 1
#define ARDUINOJSON_USE_DOUBLE 0
#define ARDUINOJSON_USE_LONG_LONG 0
#include <ArduinoJson.h>
// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "aaaa";
const char* password = "aaaaaaaa";
const char* mqttServer = "broker.emqx.io";
IPAddress server(192,168,31 ,200);
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
void setup() {
Serial.begin(9600);
// 连接WiFi
connectWifi();
// 设置MQTT服务器和端口号
mqttClient.setServer(server, 1883);
// 连接MQTT服务器
connectMQTTServer();
Register_temp();
Register_hum();
}
void loop() {
if (mqttClient.connected()) { // 如果开发板成功连接服务器
mqttClient.loop(); // 保持客户端心跳
} else { // 如果开发板未能成功连接服务器
connectMQTTServer(); // 则尝试连接服务器
}
}
void connectMQTTServer(){
String clientId = "UNOR4";
// 连接MQTT服务器
if (mqttClient.connect(clientId.c_str())) {
Serial.println("MQTT Server Connected.");
Serial.println("Server Address: ");
Serial.println(mqttServer);
Serial.println("ClientId:");
Serial.println(clientId);
} else {
Serial.print("MQTT Server Connect Failed. Client State:");
Serial.println(mqttClient.state());
delay(3000);
}
}
void connectWifi(){
WiFi.begin(ssid, password);
//等待WiFi连接,成功连接后输出成功信息
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.println("");
Serial.println(WiFi.localIP());
}
void Register_temp()
{
// Stream& output;
const char * topics="homeassistant/sensor/sensorBedroomT/config";
String output;
JsonDocument doc;
doc["device_class"] = "temperature";
doc["name"] = "Temperature";
doc["state_topic"] = "homeassistant/sensor/sensorroom/state";
doc["value_template"] = "{{ value_json.temperature}}";
doc["unique_id"] = "temp01ae";
JsonObject device = doc["device"].to<JsonObject>();
device["identifiers"][0] = "room01";
device["name"] = "room";
doc.shrinkToFit(); // optional
serializeJson(doc, output);
Serial.println(output.length());
if(mqttClient.publish(topics, output.c_str())){
Serial.println("Publish topic:");Serial.println(topics);
Serial.println("Publish message:");Serial.println(output);
} else {
Serial.println("Message Publish Failed.");
}
}
void Register_hum()
{
// Stream& output;
const char * topics="homeassistant/sensor/sensorBedroomH/config";
String output;
JsonDocument doc;
doc["device_class"] = "humidity";
doc["name"] = "Humidity";
doc["state_topic"] = "homeassistant/sensor/sensorroom/state";
doc["unit_of_measurement"] = "%";
doc["value_template"] = "{{ value_json.humidity}}";
doc["unique_id"] = "hum01ae";
JsonObject device = doc["device"].to<JsonObject>();
device["identifiers"][0] = "room01";
device["name"] = "room";
doc.shrinkToFit(); // optional
serializeJson(doc, output);
Serial.println(output);
Serial.println(output.length());
if(mqttClient.publish(topics, output.c_str())){
Serial.println("Publish topic:");Serial.println(topics);
Serial.println("Publish message:");Serial.println(output);
} else {
Serial.println("Message Publish Failed.");
}
}
任务四
通过外部SHT40温湿度传感器,上传温湿度到HA,通过HA面板显示数据
这个任务难点是如何获取SHT40 并且理解如何上传mqtt数据
流程图如下
今天来完成第四个任务通过外部SHT40温湿度传感器,上传温湿度到HA,通过HA面板显示数据
首先完成对SHT40 的采集
SHT4X是业内知名的Sensirion公司推出的第四代数字温湿度传感器系列。传承了盛思锐湿度和温度传感器在业界有口皆碑的质量和可靠性,SHT40 可在广阔的测量范围内提供始终如一的高精确度,为用户带来最佳的性价比。 SHT40凭借宽电源电压范围 (3.3 – 5 V) 和顶尖精度(±1.8% 相对湿度,±0.2℃),它拥有更出色的灵活性。 SHT40可以测量 0 至 100% 的相对湿度以及 -40℃ 至 125℃ 的温度,可以达到 ±1.8%的湿度精度和 ±0.2℃温度精度。 SHT40集成可变功率加热器,在冷凝环境也可以正常工作。 凭借 3.3 V 至 5 V 的宽电源电压以及低于0.15mA的低功耗模式,SHTC40非常适合移动和电池驱动型应用,可以非常容易的集成到智能楼宇、天气站、仓库存储、养殖、孵化等应用场景中
首先获取温湿度数据这里要注意的是我们使用的 是Qwiic Connector 所以使用的是IIC2端口因此很多例子我们不能使用比如Adafruit_SHT4x 库中例子他就是使用的是IIC1 的接口
下面我们首先使用官方的历程进行读取数据
复制
/***************************************************
This is an example for the SHT4x Humidity & Temp Sensor
Designed specifically to work with the SHT4x sensor from Adafruit
----> https://www.adafruit.com/products/4885
These sensors use I2C to communicate, 2 pins are required to
interface
****************************************************/
#include <Wire.h>
#include "Adafruit_SHT4x.h"
Adafruit_SHT4x sht4 = Adafruit_SHT4x();
void setup() {
Serial.begin(115200);
while (!Serial)
delay(10); // will pause Zero, Leonardo, etc until serial console opens
Serial.println("Adafruit SHT4x test");
if (! sht4.begin(&Wire1)) {
Serial.println("Couldn't find SHT4x");
while (1) delay(1);
}
Serial.println("Found SHT4x sensor");
Serial.print("Serial number 0x");
Serial.println(sht4.readSerial(), HEX);
// You can have 3 different precisions, higher precision takes longer
sht4.setPrecision(SHT4X_HIGH_PRECISION);
switch (sht4.getPrecision()) {
case SHT4X_HIGH_PRECISION:
Serial.println("High precision");
break;
case SHT4X_MED_PRECISION:
Serial.println("Med precision");
break;
case SHT4X_LOW_PRECISION:
Serial.println("Low precision");
break;
}
// You can have 6 different heater settings
// higher heat and longer times uses more power
// and reads will take longer too!
sht4.setHeater(SHT4X_NO_HEATER);
switch (sht4.getHeater()) {
case SHT4X_NO_HEATER:
Serial.println("No heater");
break;
case SHT4X_HIGH_HEATER_1S:
Serial.println("High heat for 1 second");
break;
case SHT4X_HIGH_HEATER_100MS:
Serial.println("High heat for 0.1 second");
break;
case SHT4X_MED_HEATER_1S:
Serial.println("Medium heat for 1 second");
break;
case SHT4X_MED_HEATER_100MS:
Serial.println("Medium heat for 0.1 second");
break;
case SHT4X_LOW_HEATER_1S:
Serial.println("Low heat for 1 second");
break;
case SHT4X_LOW_HEATER_100MS:
Serial.println("Low heat for 0.1 second");
break;
}
}
void loop() {
sensors_event_t humidity, temp;
uint32_t timestamp = millis();
sht4.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data
timestamp = millis() - timestamp;
Serial.print("Temperature: "); Serial.print(temp.temperature); Serial.println(" degrees C");
Serial.print("Humidity: "); Serial.print(humidity.relative_humidity); Serial.println("% rH");
Serial.print("Read duration (ms): ");
Serial.println(timestamp);
delay(1000);
}
我们查看串口打印的数据
复制
Read duration (ms): 10
Temperature: 24.83 degrees C
Humidity: 54.25% rH
Read duration (ms): 10
串口正确的读到了温湿度
下面我们再结合上一个连接mqtt的例子进行上传数据这里我们上次设置的上传数据 的主题是homeassistant/sensor/sensorroom/state
那这里我们就再加一个函数用于上传数据
复制
void Update_Value(float temp,float hum)
{
String topics="homeassistant/sensor/sensorroom/state";
JsonDocument doc;
doc["temperature"] = temp;
doc["humidity"] = hum;
String output;
doc.shrinkToFit(); // optional
serializeJson(doc, output);
if(mqttClient.publish(topics, output.c_str())){
Serial.println("Publish topic:");Serial.println(topics);
Serial.println("Publish message:");Serial.println(output);
} else {
Serial.println("Message Publish Failed.");
}
}
这样我们就可以完整的上传我们需要的温湿度了下面我们来看一下结果
我们看到已经上传上去了 下面是完整的代码
复制
//#include <WiFiS3.h>
#include <PubSubClient.h>
#include "Adafruit_SHT4x.h"
#include <Wire.h>
#include <WiFi.h>
#include "aaa.h"
#define ARDUINOJSON_SLOT_ID_SIZE 1
#define ARDUINOJSON_STRING_LENGTH_SIZE 1
#define ARDUINOJSON_USE_DOUBLE 0
#define ARDUINOJSON_USE_LONG_LONG 0
#include <ArduinoJson.h>
// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* mqttServer = "broker.emqx.io";
IPAddress server(192,168,31 ,200);
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
Adafruit_SHT4x sht4 = Adafruit_SHT4x();
void setup() {
Serial.begin(9600);
// 连接WiFi
connectWifi();
if (! sht4.begin(&Wire1 )) {
Serial.println("Couldn't find SHT4x");
while (1) delay(1);
}
// 设置MQTT服务器和端口号
mqttClient.setServer(server, 1883);
// 连接MQTT服务器
connectMQTTServer();
Register_temp();
Register_hum();
}
void loop() {
sensors_event_t humidity, temp;
sht4.getEvent(&humidity, &temp);
Serial.print("Temperature: "); Serial.print(temp.temperature); Serial.println(" degrees C");
Serial.print("Humidity: "); Serial.print(humidity.relative_humidity); Serial.println("% rH");
if (mqttClient.connected()) { // 如果开发板成功连接服务器
Update_Value(temp.temperature,humidity.relative_humidity);
} else { // 如果开发板未能成功连接服务器
connectMQTTServer(); // 则尝试连接服务器
}
}
void connectMQTTServer(){
String clientId = "UNOR4";
// 连接MQTT服务器
if (mqttClient.connect(clientId.c_str())) {
Serial.println("MQTT Server Connected.");
Serial.println("Server Address: ");
Serial.println(mqttServer);
Serial.println("ClientId:");
Serial.println(clientId);
} else {
Serial.print("MQTT Server Connect Failed. Client State:");
Serial.println(mqttClient.state());
delay(3000);
}
}
void connectWifi(){
WiFi.begin(ssid, password);
//等待WiFi连接,成功连接后输出成功信息
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.println("");
Serial.println(WiFi.localIP());
}
void Register_temp()
{
// Stream& output;
const char * topics="homeassistant/sensor/sensorBedroomT/config";
String output;
JsonDocument doc;
doc["device_class"] = "temperature";
doc["name"] = "Temperature";
doc["state_topic"] = "homeassistant/sensor/sensorroom/state";
doc["value_template"] = "{{ value_json.temperature}}";
doc["unique_id"] = "temp01ae";
JsonObject device = doc["device"].to<JsonObject>();
device["identifiers"][0] = "room01";
device["name"] = "room";
doc.shrinkToFit(); // optional
serializeJson(doc, output);
Serial.println(output.length());
if(mqttClient.publish(topics, output.c_str())){
Serial.println("Publish topic:");Serial.println(topics);
Serial.println("Publish message:");Serial.println(output);
} else {
Serial.println("Message Publish Failed.");
}
}
void Register_hum()
{
// Stream& output;
const char * topics="homeassistant/sensor/sensorBedroomH/config";
String output;
JsonDocument doc;
doc["device_class"] = "humidity";
doc["name"] = "Humidity";
doc["state_topic"] = "homeassistant/sensor/sensorroom/state";
doc["unit_of_measurement"] = "%";
doc["value_template"] = "{{ value_json.humidity}}";
doc["unique_id"] = "hum01ae";
JsonObject device = doc["device"].to<JsonObject>();
device["identifiers"][0] = "room01";
device["name"] = "room";
doc.shrinkToFit(); // optional
serializeJson(doc, output);
Serial.println(output);
Serial.println(output.length());
if(mqttClient.publish(topics, output.c_str())){
Serial.println("Publish topic:");Serial.println(topics);
Serial.println("Publish message:");Serial.println(output);
} else {
Serial.println("Message Publish Failed.");
}
}
void Update_Value(float temp,float hum)
{
String topics="homeassistant/sensor/sensorroom/state";
JsonDocument doc;
doc["temperature"] = temp;
doc["humidity"] = hum;
String output;
doc.shrinkToFit(); // optional
serializeJson(doc, output);
if(mqttClient.publish(topics.c_str(), output.c_str())){
Serial.println("Publish topic:");Serial.println(topics);
Serial.println("Publish message:");Serial.println(output);
} else {
Serial.println("Message Publish Failed.");
}
}
下方的视频为详细讲解
感想
试用Arduino的经历是一次宝贵的学习之旅。它不仅让我掌握了电子技术和编程知识,更重要的是激发了我的创造力和想象力。我相信,在未来的日子里,Arduino将继续陪伴我探索未知、实现梦想。对于所有对电子技术和创新感兴趣的人来说,Arduino无疑是一个值得尝试和深入学习的平台。
代码链接
【Follow me第二季第2期】 所有任务代码-嵌入式开发相关资料下载-EEWORLD下载中心
-
发表了主题帖:
【Follow me第二季第2期】+通过外部SHT40温湿度传感器,上传温湿度到HA
本帖最后由 一只小跳帽 于 2024-9-22 22:31 编辑
今天来完成第四个任务通过外部SHT40温湿度传感器,上传温湿度到HA,通过HA面板显示数据
首先完成对SHT40 的采集
SHT4X是业内知名的Sensirion公司推出的第四代数字温湿度传感器系列。传承了盛思锐湿度和温度传感器在业界有口皆碑的质量和可靠性,SHT40 可在广阔的测量范围内提供始终如一的高精确度,为用户带来最佳的性价比。 SHT40凭借宽电源电压范围 (3.3 – 5 V) 和顶尖精度(±1.8% 相对湿度,±0.2℃),它拥有更出色的灵活性。 SHT40可以测量 0 至 100% 的相对湿度以及 -40℃ 至 125℃ 的温度,可以达到 ±1.8%的湿度精度和 ±0.2℃温度精度。 SHT40集成可变功率加热器,在冷凝环境也可以正常工作。 凭借 3.3 V 至 5 V 的宽电源电压以及低于0.15mA的低功耗模式,SHTC40非常适合移动和电池驱动型应用,可以非常容易的集成到智能楼宇、天气站、仓库存储、养殖、孵化等应用场景中
首先获取温湿度数据这里要注意的是我们使用的 是Qwiic Connector 所以使用的是IIC2端口因此很多例子我们不能使用比如Adafruit_SHT4x 库中例子他就是使用的是IIC1 的接口
下面我们首先使用官方的历程进行读取数据
/***************************************************
This is an example for the SHT4x Humidity & Temp Sensor
Designed specifically to work with the SHT4x sensor from Adafruit
----> https://www.adafruit.com/products/4885
These sensors use I2C to communicate, 2 pins are required to
interface
****************************************************/
#include <Wire.h>
#include "Adafruit_SHT4x.h"
Adafruit_SHT4x sht4 = Adafruit_SHT4x();
void setup() {
Serial.begin(115200);
while (!Serial)
delay(10); // will pause Zero, Leonardo, etc until serial console opens
Serial.println("Adafruit SHT4x test");
if (! sht4.begin(&Wire1)) {
Serial.println("Couldn't find SHT4x");
while (1) delay(1);
}
Serial.println("Found SHT4x sensor");
Serial.print("Serial number 0x");
Serial.println(sht4.readSerial(), HEX);
// You can have 3 different precisions, higher precision takes longer
sht4.setPrecision(SHT4X_HIGH_PRECISION);
switch (sht4.getPrecision()) {
case SHT4X_HIGH_PRECISION:
Serial.println("High precision");
break;
case SHT4X_MED_PRECISION:
Serial.println("Med precision");
break;
case SHT4X_LOW_PRECISION:
Serial.println("Low precision");
break;
}
// You can have 6 different heater settings
// higher heat and longer times uses more power
// and reads will take longer too!
sht4.setHeater(SHT4X_NO_HEATER);
switch (sht4.getHeater()) {
case SHT4X_NO_HEATER:
Serial.println("No heater");
break;
case SHT4X_HIGH_HEATER_1S:
Serial.println("High heat for 1 second");
break;
case SHT4X_HIGH_HEATER_100MS:
Serial.println("High heat for 0.1 second");
break;
case SHT4X_MED_HEATER_1S:
Serial.println("Medium heat for 1 second");
break;
case SHT4X_MED_HEATER_100MS:
Serial.println("Medium heat for 0.1 second");
break;
case SHT4X_LOW_HEATER_1S:
Serial.println("Low heat for 1 second");
break;
case SHT4X_LOW_HEATER_100MS:
Serial.println("Low heat for 0.1 second");
break;
}
}
void loop() {
sensors_event_t humidity, temp;
uint32_t timestamp = millis();
sht4.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data
timestamp = millis() - timestamp;
Serial.print("Temperature: "); Serial.print(temp.temperature); Serial.println(" degrees C");
Serial.print("Humidity: "); Serial.print(humidity.relative_humidity); Serial.println("% rH");
Serial.print("Read duration (ms): ");
Serial.println(timestamp);
delay(1000);
}
我们查看串口打印的数据
Read duration (ms): 10
Temperature: 24.83 degrees C
Humidity: 54.25% rH
Read duration (ms): 10
串口正确的读到了温湿度
下面我们再结合上一个连接mqtt的例子进行上传数据这里我们上次设置的上传数据 的主题是homeassistant/sensor/sensorroom/state
那这里我们就再加一个函数用于上传数据
void Update_Value(float temp,float hum)
{
String topics="homeassistant/sensor/sensorroom/state";
JsonDocument doc;
doc["temperature"] = temp;
doc["humidity"] = hum;
String output;
doc.shrinkToFit(); // optional
serializeJson(doc, output);
if(mqttClient.publish(topics, output.c_str())){
Serial.println("Publish topic:");Serial.println(topics);
Serial.println("Publish message:");Serial.println(output);
} else {
Serial.println("Message Publish Failed.");
}
}
这样我们就可以完整的上传我们需要的温湿度了下面我们来看一下结果
我们看到已经上传上去了 下面是完整的代码
//#include <WiFiS3.h>
#include <PubSubClient.h>
#include "Adafruit_SHT4x.h"
#include <Wire.h>
#include <WiFi.h>
#include "aaa.h"
#define ARDUINOJSON_SLOT_ID_SIZE 1
#define ARDUINOJSON_STRING_LENGTH_SIZE 1
#define ARDUINOJSON_USE_DOUBLE 0
#define ARDUINOJSON_USE_LONG_LONG 0
#include <ArduinoJson.h>
// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* mqttServer = "broker.emqx.io";
IPAddress server(192,168,31 ,200);
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
Adafruit_SHT4x sht4 = Adafruit_SHT4x();
void setup() {
Serial.begin(9600);
// 连接WiFi
connectWifi();
if (! sht4.begin(&Wire1 )) {
Serial.println("Couldn't find SHT4x");
while (1) delay(1);
}
// 设置MQTT服务器和端口号
mqttClient.setServer(server, 1883);
// 连接MQTT服务器
connectMQTTServer();
Register_temp();
Register_hum();
}
void loop() {
sensors_event_t humidity, temp;
sht4.getEvent(&humidity, &temp);
Serial.print("Temperature: "); Serial.print(temp.temperature); Serial.println(" degrees C");
Serial.print("Humidity: "); Serial.print(humidity.relative_humidity); Serial.println("% rH");
if (mqttClient.connected()) { // 如果开发板成功连接服务器
Update_Value(temp.temperature,humidity.relative_humidity);
} else { // 如果开发板未能成功连接服务器
connectMQTTServer(); // 则尝试连接服务器
}
}
void connectMQTTServer(){
String clientId = "UNOR4";
// 连接MQTT服务器
if (mqttClient.connect(clientId.c_str())) {
Serial.println("MQTT Server Connected.");
Serial.println("Server Address: ");
Serial.println(mqttServer);
Serial.println("ClientId:");
Serial.println(clientId);
} else {
Serial.print("MQTT Server Connect Failed. Client State:");
Serial.println(mqttClient.state());
delay(3000);
}
}
void connectWifi(){
WiFi.begin(ssid, password);
//等待WiFi连接,成功连接后输出成功信息
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.println("");
Serial.println(WiFi.localIP());
}
void Register_temp()
{
// Stream& output;
const char * topics="homeassistant/sensor/sensorBedroomT/config";
String output;
JsonDocument doc;
doc["device_class"] = "temperature";
doc["name"] = "Temperature";
doc["state_topic"] = "homeassistant/sensor/sensorroom/state";
doc["value_template"] = "{{ value_json.temperature}}";
doc["unique_id"] = "temp01ae";
JsonObject device = doc["device"].to<JsonObject>();
device["identifiers"][0] = "room01";
device["name"] = "room";
doc.shrinkToFit(); // optional
serializeJson(doc, output);
Serial.println(output.length());
if(mqttClient.publish(topics, output.c_str())){
Serial.println("Publish topic:");Serial.println(topics);
Serial.println("Publish message:");Serial.println(output);
} else {
Serial.println("Message Publish Failed.");
}
}
void Register_hum()
{
// Stream& output;
const char * topics="homeassistant/sensor/sensorBedroomH/config";
String output;
JsonDocument doc;
doc["device_class"] = "humidity";
doc["name"] = "Humidity";
doc["state_topic"] = "homeassistant/sensor/sensorroom/state";
doc["unit_of_measurement"] = "%";
doc["value_template"] = "{{ value_json.humidity}}";
doc["unique_id"] = "hum01ae";
JsonObject device = doc["device"].to<JsonObject>();
device["identifiers"][0] = "room01";
device["name"] = "room";
doc.shrinkToFit(); // optional
serializeJson(doc, output);
Serial.println(output);
Serial.println(output.length());
if(mqttClient.publish(topics, output.c_str())){
Serial.println("Publish topic:");Serial.println(topics);
Serial.println("Publish message:");Serial.println(output);
} else {
Serial.println("Message Publish Failed.");
}
}
void Update_Value(float temp,float hum)
{
String topics="homeassistant/sensor/sensorroom/state";
JsonDocument doc;
doc["temperature"] = temp;
doc["humidity"] = hum;
String output;
doc.shrinkToFit(); // optional
serializeJson(doc, output);
if(mqttClient.publish(topics.c_str(), output.c_str())){
Serial.println("Publish topic:");Serial.println(topics);
Serial.println("Publish message:");Serial.println(output);
} else {
Serial.println("Message Publish Failed.");
}
}
下方的视频为详细讲解
-
上传了资料:
【Follow me第二季第2期】 所有任务代码
- 2024-09-18
-
发表了主题帖:
【Follow me第二季第2期】Arduino Uno R4 WiFi 通过MQTT连入Home Assistant
本帖最后由 一只小跳帽 于 2024-9-22 14:52 编辑
今天,我们将细致而有序地完成一个项目——搭建Home Assistant平台,并通过MQTT协议实现设备接入。整个过程将分为四个清晰、连贯的步骤,确保每一步都精准无误,助力您成功构建智能家居生态系统。
第一步:安装Home Assistant
首先,我们将着手安装Home Assistant。这一步骤是构建智能家居中枢的关键,Home Assistant以其强大的集成能力和灵活的自定义选项而闻名,能够轻松管理家中的各种智能设备。我们将按照官方指南或选择适合您操作系统的安装方法,确保Home Assistant能够稳定运行在您的设备上。
第二步:部署MQTT服务器
紧接着,我们将安装并配置MQTT服务器。MQTT,即消息队列遥测传输,是一种轻量级的、基于发布/订阅模式的消息协议,非常适合物联网(IoT)设备的通信。我们将选择一个稳定可靠的MQTT服务器软件(如Mosquitto),并根据实际需求进行配置,确保它能够高效、安全地处理来自智能家居设备的消息。
第三步:将Home Assistant连接到MQTT服务器
在成功部署MQTT服务器后,下一步是将Home Assistant与之连接起来。这一步骤至关重要,因为它将使得Home Assistant能够接收并处理来自MQTT服务器的消息,进而实现对智能家居设备的控制。我们将通过Home Assistant的配置界面,添加MQTT集成,并填写MQTT服务器的相关信息,完成连接设置。
第四步:使用MQTT客户端进行连接测试
为了确保MQTT服务器的正常运行以及Home Assistant与MQTT服务器的成功连接,我们将使用MQTT客户端工具进行连接测试。这一步骤不仅验证了通信链路的通畅性,还让我们有机会熟悉MQTT的基本操作,为后续的设备接入打下坚实基础。
最后一步:使用Arduino R4 WiFi开发板接入系统
作为整个项目的亮点,我们将使用Arduino R4 WiFi开发板,通过编程使其能够通过MQTT协议与Home Assistant进行通信。通过编写相应的代码,Arduino R4 WiFi将能够发送状态信息至MQTT服务器,并接收来自Home Assistant的控制指令,实现智能家居设备的智能化控制。这一步骤将充分展示MQTT在物联网应用中的强大潜力。
首先,我们将通过安装Linux虚拟机来启动我们的Home Assistant部署之旅,这里特别选择Ubuntu系统作为我们的基础平台。Ubuntu以其完善的生态系统、广泛的用户群体以及丰富的解决方案而著称,这使得它成为部署智能家居控制中心——Home Assistant的理想选择。
在准备阶段,我们将确保Ubuntu虚拟机被正确安装并配置,以便为后续步骤提供一个稳定、可靠的环境。通过选择Ubuntu,我们不仅能够享受到其强大的功能和灵活性,还能轻松获取到来自全球开发者和社区的支持与帮助,这对于解决在部署过程中可能遇到的各种问题至关重要。
接下来,我们将利用容器技术来安装和运行Home Assistant。容器化部署以其轻量级、可移植性和易于管理的特点,在现代软件开发和运维中得到了广泛应用。通过容器,我们可以将Home Assistant及其依赖项封装在一个独立的虚拟环境中,从而避免与系统其他部分的潜在冲突,并确保其稳定运行。
综上所述,选择Ubuntu系统作为Linux虚拟机的基础,并结合容器技术来安装Home Assistant,将为我们提供一个强大、灵活且易于维护的智能家居控制中心解决方案。
下面进行安装docker
#安装前先卸载操作系统默认安装的docker,
sudo apt-get remove docker docker-engine docker.io containerd runc
#安装必要支持
sudo apt install apt-transport-https ca-certificates curl software-properties-common gnupg lsb-release
#添加 Docker 官方 GPG key (可能国内现在访问会存在问题)
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# 阿里源(推荐使用阿里的gpg KEY)
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
#添加 apt 源:
#Docker官方源
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
#阿里apt源
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
#更新源
sudo apt update
sudo apt-get update
#安装最新版本的Docker
sudo apt install docker-ce docker-ce-cli containerd.io
#等待安装完成
#查看Docker版本
sudo docker version
#查看Docker运行状态
sudo systemctl status docker
现在安装好docker 下面安装docker版的Home Assistant
docker search homeassistant
2、拉取docker镜像
docker pull homeassistant/home-assistant:latest
3、创建容器并运行
docker run -d --name="hass" -v /path/to/config:/config -v /etc/localtime:/etc/localtime:ro -p 8123:8123 --net
-d: 表示在后台运行容器。
--name my_home_assistant_instance: 指定容器的名称为 "my_home_assistant_instance",你可以根据需要修改。
-v /path/to/config:/config: 将本地配置目录映射到容器内的 /config 目录。确保将 /path/to/config 替换为你的 Home Assistant 配置目录的实际路径。
-v /etc/localtime:/etc/localtime:ro: 将主机的时间配置映射到容器内,保持容器和主机时间同步。
--net=host: 使用主机网络模式,使得容器可以直接使用主机的网络配
-p:映射端口(容器内的端口直接映射到本地主机端口最后便是刚才下载的镜像了,运行该容器。
注:这里启动docker容器之后每次启动docker容器都会数据初始化建议-v进行数据挂载
docker 查看并启动容器
//查看容器信息
docker ps -a
//docker 使用<container_id>启动容器
docker start <container_id>
//使用容器名称启动--name 参数指定容器的名称为 "my_container_instance"。my_image:tag 是你要启动的 Docker 镜像的名称和标签。
docker run --name my_container_instance my_image:tag
docker stop <container_id>
通过上面看到我们已经创建 了Home Assistant 的镜像下面进行启动
这里我们已经启动了Home Assistant 下面我们使用浏览器进行打开首先使用ifconfig 查看ip再使用端口8123 进行访问
现在安装好了 但是还没有mqtt服务因此不能使用mqtt连接下面安装mqtt服务器
1 认识EMQX
1.1 EMQX 简介
EMQX 是一款开源的大规模分布式 MQTT 消息服务器,功能丰富,专为物联网和实时通信应用而设计。EMQX 5.0 单集群支持 MQTT 并发连接数高达 1 亿条,单服务器的传输与处理吞吐量可达每秒百万级 MQTT 消息,同时保证毫秒级的低时延。
EMQX 支持多种协议,包括 MQTT (3.1、3.1.1 和 5.0)、HTTP、QUIC 和 WebSocket 等,保证各种网络环境和硬件设备的可访问性。EMQX 还提供了全面的 SSL/TLS 功能支持,比如双向认证以及多种身份验证机制,为物联网设备和应用程序提供可靠和高效的通信基础设施。
1.2 EMQX 版本类型
EMQX 有 4 种部署模式,包括两种云服务模式(EMQX Cloud Serverless 和 EMQX Cloud 专有版)和两种自托管模式(EMQX 开源版 和 EMQX 企业版)。以下表格列出了这些部署模式的对比,以帮助您根据业务需求进行选择。想进一步了解具体的功能对比,参考功能对比。
对于非企业级应用,使用EMQX开源版即可,本文也是以该版本作为Demo,介绍其搭建和使用方法。
2 Ubuntu搭建EMQX 平台
2.1 下载和安装
2.1.1 下载
下载地址:
https://www.emqx.io/zh/downloads
打开网站,选择安装环境:
2.1.2 安装
使用如下三个步骤在Ubuntu上安装EMQX:
Step 1: 从软件链接源下载软件包,并装载安装环境
curl -s https://assets.emqx.com/scripts/install-emqx-deb.sh | sudo bash
下载完成后,可以看见如下log:
Step-2: 安装软件
sudo apt-get install emqx
Step-3: 运行软件
执行如下代码,如果没有任何信息打印出来,说明EMAX已经正常启动了
sudo systemctl start emqx
2.2 查看运行端口
和EMQX相关的端口有如下这些:
端口号 介绍
1883 MQTT 协议端口
8883 MQTT/SSL 端口
8083 MQTT/WebSocket 端口
8080 HTTP API 端口
18083 Dashboard 管理控制台端口
查看EMQX的运行端口:
netstat -ap | grep 18083
查看MQTT 协议端口:
netstat -ap | grep 1883
3 运行Dashboard 管理控制台
要在第三方终端上运行Dashboard 管理控制台,必须保证18083端口运行被访问,那么怎么做呢?检查该端口是否被允许外网访问。
3.1 查看Ubuntu上的防火墙
首先确保ufw 已经安装在当前的Ubuntu系统中,如果没有安装,使用如下命令:
sudo apt update
sudo apt install ufw
一种最简单的方式就是关闭防火墙,如果允许外网访问,不建议这样做:
sudo ufw disable
3.2 运行Dashboard 管理控制台
step-1: 查看当前Ubuntu主机的ip,使用命令:
ifconfig
执行命令后,终端会打印当前主机所有网卡相关的IP信息,找到主机的实际ip,然后在第三方电脑主机上登录
step-2: 登录服务器
打开浏览器,输入相应IP和端口号,就能打开网页了。举个例子,以笔者本人测试主机为例
192.168.1.11:18083
如果安装成功,可以看见如下页面,说明EMQX可以正常工作了。
第三步使用 Home Assistant 连接到mqtt 服务器
在设置的设备与服务中搜索mqtt 进行配置 ip地址就是自己本地的ip 端口使用的是mqtt统一端口1883 密码和用户名使用自己设置的就可以设置成功后就可以看到集群中连接的设备和订阅的主题
这个就是Home Assistant 的设备
下面我们先使用mqtt的客户端连接到mqtt服务器 这里我使用的是mqtt客户端工具
使用这个工具我们可以很简单的模拟我们的开发板如何配置mqtt和发送的主题
首先我们先配置端口ip地址然后进行连接
然后点击连接在浏览器上查看节点是否连接成功
这里看到已经有两个节点了有一个就是我们刚刚连接的客户端
下面我们要把这个设备连接到我们的 Home Assistant 中这里很多人选择直接配置设备文件但是我发现这种方法十分的不麻烦而且也不利于我们后续的设备自己连接函数需要我们一个一个的配置下面我使用的方法是使用设置自动的添加到我们这个系统中 只需要向特定的主题发送对应的内容就可以下面我来试一下
这里我们选择配置成我们下一个任务的 温湿度传感器类型进行发现
这里看到我们已经成功的添加了一个设备并且配置他的两个状态为 一个是温度一个是湿度下面我们对其进行发送状态
这个已经有数值了 下面我们先通过开发板连接上mqtt 下一节我们再上传报文
首先我们使用PubSubClient 库进行连接下面简单介绍一下连接的流程首先创建一个实体类
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
后续的所有操作都是使用的这个实体
下一步进行连接到mqtt服务器
void connectMQTTServer(){
String clientId = "UNOR4";
// 连接MQTT服务器
if (mqttClient.connect(clientId.c_str())) {
Serial.println("MQTT Server Connected.");
Serial.println("Server Address: ");
Serial.println(mqttServer);
Serial.println("ClientId:");
Serial.println(clientId);
} else {
Serial.print("MQTT Server Connect Failed. Client State:");
Serial.println(mqttClient.state());
delay(3000);
}
}
后面再进行学习上面的mqtt客户端发送给服务器的主题和内容进行发送让Home Assistant 自发现的方式
注意我后面因为网关的原因自己进行配置了一次网络
void Register_temp()
{
// Stream& output;
const char * topics="homeassistant/sensor/sensorBedroomT/config";
String output;
JsonDocument doc;
doc["device_class"] = "temperature";
doc["name"] = "Temperature";
doc["state_topic"] = "homeassistant/sensor/sensorroom/state";
doc["value_template"] = "{{ value_json.temperature}}";
doc["unique_id"] = "temp01ae";
JsonObject device = doc["device"].to<JsonObject>();
device["identifiers"][0] = "room01";
device["name"] = "room";
doc.shrinkToFit(); // optional
serializeJson(doc, output);
Serial.println(output.length());
if(mqttClient.publish(topics, output.c_str())){
Serial.println("Publish topic:");Serial.println(topics);
Serial.println("Publish message:");Serial.println(output);
} else {
Serial.println("Message Publish Failed.");
}
}
void Register_hum()
{
// Stream& output;
const char * topics="homeassistant/sensor/sensorBedroomH/config";
String output;
JsonDocument doc;
doc["device_class"] = "humidity";
doc["name"] = "Humidity";
doc["state_topic"] = "homeassistant/sensor/sensorroom/state";
doc["unit_of_measurement"] = "%";
doc["value_template"] = "{{ value_json.humidity}}";
doc["unique_id"] = "hum01ae";
JsonObject device = doc["device"].to<JsonObject>();
device["identifiers"][0] = "room01";
device["name"] = "room";
doc.shrinkToFit(); // optional
serializeJson(doc, output);
Serial.println(output);
Serial.println(output.length());
if(mqttClient.publish(topics, output.c_str())){
Serial.println("Publish topic:");Serial.println(topics);
Serial.println("Publish message:");Serial.println(output);
} else {
Serial.println("Message Publish Failed.");
}
}
运行后已经连接上了
到这里已经完全了下面是整个的代码
//#include <WiFiS3.h>
#include <PubSubClient.h>
#include <WiFi.h>
#define ARDUINOJSON_SLOT_ID_SIZE 1
#define ARDUINOJSON_STRING_LENGTH_SIZE 1
#define ARDUINOJSON_USE_DOUBLE 0
#define ARDUINOJSON_USE_LONG_LONG 0
#include <ArduinoJson.h>
// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "aaaa";
const char* password = "aaaaaaaa";
const char* mqttServer = "broker.emqx.io";
IPAddress server(192,168,31 ,200);
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
void setup() {
Serial.begin(9600);
// 连接WiFi
connectWifi();
// 设置MQTT服务器和端口号
mqttClient.setServer(server, 1883);
// 连接MQTT服务器
connectMQTTServer();
Register_temp();
Register_hum();
}
void loop() {
if (mqttClient.connected()) { // 如果开发板成功连接服务器
mqttClient.loop(); // 保持客户端心跳
} else { // 如果开发板未能成功连接服务器
connectMQTTServer(); // 则尝试连接服务器
}
}
void connectMQTTServer(){
String clientId = "UNOR4";
// 连接MQTT服务器
if (mqttClient.connect(clientId.c_str())) {
Serial.println("MQTT Server Connected.");
Serial.println("Server Address: ");
Serial.println(mqttServer);
Serial.println("ClientId:");
Serial.println(clientId);
} else {
Serial.print("MQTT Server Connect Failed. Client State:");
Serial.println(mqttClient.state());
delay(3000);
}
}
void connectWifi(){
WiFi.begin(ssid, password);
//等待WiFi连接,成功连接后输出成功信息
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.println("");
Serial.println(WiFi.localIP());
}
void Register_temp()
{
// Stream& output;
const char * topics="homeassistant/sensor/sensorBedroomT/config";
String output;
JsonDocument doc;
doc["device_class"] = "temperature";
doc["name"] = "Temperature";
doc["state_topic"] = "homeassistant/sensor/sensorroom/state";
doc["value_template"] = "{{ value_json.temperature}}";
doc["unique_id"] = "temp01ae";
JsonObject device = doc["device"].to<JsonObject>();
device["identifiers"][0] = "room01";
device["name"] = "room";
doc.shrinkToFit(); // optional
serializeJson(doc, output);
Serial.println(output.length());
if(mqttClient.publish(topics, output.c_str())){
Serial.println("Publish topic:");Serial.println(topics);
Serial.println("Publish message:");Serial.println(output);
} else {
Serial.println("Message Publish Failed.");
}
}
void Register_hum()
{
// Stream& output;
const char * topics="homeassistant/sensor/sensorBedroomH/config";
String output;
JsonDocument doc;
doc["device_class"] = "humidity";
doc["name"] = "Humidity";
doc["state_topic"] = "homeassistant/sensor/sensorroom/state";
doc["unit_of_measurement"] = "%";
doc["value_template"] = "{{ value_json.humidity}}";
doc["unique_id"] = "hum01ae";
JsonObject device = doc["device"].to<JsonObject>();
device["identifiers"][0] = "room01";
device["name"] = "room";
doc.shrinkToFit(); // optional
serializeJson(doc, output);
Serial.println(output);
Serial.println(output.length());
if(mqttClient.publish(topics, output.c_str())){
Serial.println("Publish topic:");Serial.println(topics);
Serial.println("Publish message:");Serial.println(output);
} else {
Serial.println("Message Publish Failed.");
}
}
- 2024-09-07
-
发表了主题帖:
【Follow me第二季第2期】实现任务二 驱动12x8点阵LED;用DAC生成正弦波并放大采集
本帖最后由 eew_ljd6R2 于 2024-9-7 23:16 编辑
大家好,
今天我们将着手完成任务二的第一个关键任务——驱动一个由12x8 LED灯组成的点阵屏。
点阵屏的巧妙之处:
高效控制:该点阵屏仅通过10个管脚就能实现全面控制,这得益于其独特的点阵布局和复用技术。
硬件设计:从硬件原理图上,我们可以清晰看到,通过特定的电路逻辑,如当管脚7为高电平而管脚3为低电平时,能够点亮LED1;反之,则能点亮LED2。
刷新策略:
逐列刷新:为了有效减少管脚需求并实现流畅的视觉效果,我们将采用逐列刷新的策略。这意味着我们将以极快的速度依次激活每一列(在此场景下,我们将其视为“行”以符合点阵屏的常见操作方式),并在激活的同时,通过其他管脚设置相应的列(行)数据。
视觉暂留:由于人眼的视觉暂留效应,这种高速但有序的刷新过程将使得LED的点亮看起来是连续的,而非闪烁的。
动画效果实现:
帧数据设计:为了创造动画效果,我们将设计一系列帧数据,每帧数据代表点阵屏上LED的一种特定点亮状态。
显示控制:通过依次显示这些帧,并精确控制每帧之间的显示时间间隔,我们就能够呈现出动态变化的图像,即动画效果。
总结:
通过巧妙利用点阵屏的复用技术和高速刷新策略,我们成功地实现了仅用10个管脚对12x8 LED点阵屏的全面控制
Arduino官方特别为点阵屏开发了一套高效的API,通过引入Arduino_LED_Matrix.h库,开发者可以便捷地控制点阵屏。首先,你需要创建一个点阵屏对象,例如命名为ledMatrix,以ArduinoLEDMatrix类实例化:
cpp复制代码
ArduinoLEDMatrix ledMatrix;
接下来,进行初始化设置,准备控制一个12x8的点阵屏。这个库允许你使用一个12*8的二维数组来直接控制屏幕上的每个LED,其中数组元素为1时表示LED点亮,为0时表示熄灭。
通过这种方式,在程序中动态地修改这个二维数组中的0和1,即可轻松实现不同画面的展示。这种API的设计极大地简化了点阵屏的控制逻辑,使得开发者能够更专注于创意和内容的实现,而非底层的硬件操作。
总结来说,Arduino官方提供的Arduino_LED_Matrix库为点阵屏开发带来了前所未有的便利,通过简单的API调用和直观的二维数组控制,即可实现复杂多变的显示效果。
官方还开发了一个小的插件可以进行创作动画大家可以通过下方的视频进行查看使用方法
下面是我的这个 任务的代码大致内容过程为通过工具设置一个动画并且加载进去
#include "Arduino_LED_Matrix.h"
#include <stdint.h>
#include "animation.h"
ArduinoLEDMatrix matrix;
void setup() {
// put your setup code here, to run once:
matrix.loadSequence(animation);
matrix.begin();
// turn on autoscroll to avoid calling next() to show the next frame; the parameter is in milliseconds
// matrix.autoscroll(300);
matrix.play(true);
}
void loop() {
// put your main code here, to run repeatedly:
}
const uint32_t animation[][4] = {
{
0xfff00000,
0x0,
0x0,
66
},
{
0xfff00,
0x0,
0x0,
66
},
{
0xff,
0xf0000000,
0x0,
66
},
{
0x0,
0xfff0000,
0x0,
66
},
{
0x0,
0xfff0,
0x0,
66
},
{
0x0,
0xf,
0xff000000,
66
},
{
0x0,
0x0,
0xfff000,
66
},
{
0x0,
0x0,
0xfff,
66
}
};
下面完成第二个任务首先使用DAC产生一个正弦波
DAC,全称Digital-to-Analog Converter,即数字模拟转换器,是一种电子设备或电路,其核心功能是将数字信号转换为相应的模拟信号。在现代电子系统中,DAC扮演了至关重要的角色,它是连接数字电路与模拟电路的桥梁,为数字系统与模拟系统之间的数据交互提供了基础。
DAC(Digital-to-Analog Converter,数模转换器)和PWM(Pulse Width Modulation,脉冲宽度调制)在电子系统中扮演着不同的角色,它们之间存在显著的区别。以下是DAC和PWM之间的主要区别:
1. 功能与原理
DAC:DAC是一种电子设备或电路,用于将数字信号转换为模拟信号。它接收离散的数字信号(如二进制代码),并将其转换为连续可变的模拟信号(如电压或电流)。DAC广泛应用于音频设备、控制系统、仪器仪表等领域,作为数字电路与模拟电路之间的桥梁。
PWM:PWM是一种通过调节信号的占空比来模拟连续模拟信号的技术。PWM信号是方波,其频率固定,但占空比(即高电平持续的时间与周期的比值)可变。通过调整占空比,PWM可以模拟出不同幅度的模拟信号。PWM广泛应用于高功率转换效率的电源、马达控制、音频放大等领域。
2. 输出特性
DAC:DAC的输出是连续的模拟信号,其幅度可以在一定范围内连续变化。DAC的输出信号质量高,能够还原音频的细节和动态特性,适用于需要高保真度音频输出的场合。
PWM:PWM的输出是方波信号,其幅度本身并不连续变化,但通过滤波等处理可以转换为近似的模拟信号。PWM的输出效率高,功耗低,适用于需要高效率低功耗的音频驱动等场合。
3. 应用领域
DAC:DAC广泛应用于音频设备(如CD播放器、功放)、控制系统(如工业自动化控制)、仪器仪表(如示波器、频谱分析仪)等领域。在这些领域中,DAC负责将数字信号转换为模拟信号以供后续处理或输出。
PWM:PWM广泛应用于高功率转换效率的电源(如开关电源)、马达控制(如电机驱动器)、音频放大(如数字音频放大器)等领域。在这些领域中,PWM通过调节占空比来控制功率元件的通断,从而实现高效能的功率转换或音频放大。
4. 精度与分辨率
DAC:DAC的精度和分辨率取决于其位数(如8位、12位、16位等)。位数越高,DAC能够输出的模拟信号值数量越多,精度和分辨率也就越高。
PWM:PWM的精度和分辨率受到其频率和占空比调节精度的限制。虽然通过提高PWM的频率和占空比调节精度可以提高其模拟精度,但通常难以达到DAC那样的高精度和高分辨率。
综上所述,DAC和PWM在功能、原理、输出特性、应用领域以及精度与分辨率等方面都存在显著的区别。
在R3版本中,所有输出均基于PWM技术实现。然而,在R4版本中,我们引入了DAC(数字模拟转换器)于A引脚,尽管其输出调用的函数仍为analogWrite(),但实质上提供了与PWM不同的模拟信号生成机制。
下面我们来分析一下这个函数
void analogWrite(pin_size_t pinNumber, int value)
{
#if (DAC12_HOWMANY > 0) || (DAC8_HOWMANY > 0)
if (IS_DAC(pinNumber)) {
auto cfg_dac = getPinCfgs(pinNumber, PIN_CFG_REQ_DAC);
if(IS_DAC_8BIT(cfg_dac[0])) {
#if DAC8_HOWMANY > 0
if(GET_CHANNEL(cfg_dac[0]) < DAC8_HOWMANY) {
_dac8[GET_CHANNEL(cfg_dac[0])].analogWrite(value);
}
#endif
}
else {
if(GET_CHANNEL(cfg_dac[0]) < DAC12_HOWMANY) {
_dac12[GET_CHANNEL(cfg_dac[0])].analogWrite(value);
}
}
return;
}
这里我们可以看出使用IS_DAC()来进行判断是否为DAC引脚如果是的话使用DAC输出方式如果不是的话使用普通的PWM方式
因此,接下来我们可以利用 `sin()` 函数来生成一个正弦波,并使用变量 `a` 来作为一个因子。根据我们在高中所学的知识,正弦波的函数形式为 `f(x) = a * sin(kx + b)` 。其中,`a` 表示振幅,在此我们暂且固定不变。那么,我们可以通过改变 `k` 和 `b` 的值,分别来调整正弦波的周期和相位。接下来,我们将通过实时改变这两个参数的值,来实现对所打印函数图形的实时更新。现在,让我们着手实现这一部分。
生成DAC正弦波的代码
analogWriteResolution(12);
// put your main code here, to run repeatedly:
a+=0.01;
if(a>2*PI)
{
a=0;
}
float out=map_float(sin(K*a+B),-1,1,0,4096);
analogWrite(DAC, out);
在这里需要特别注意的是,`analogWriteResolution()` 函数会改变 DAC(数模转换器)的精度。关于这部分的详细说明,我会在下面的视频中进行阐述。此外,`map_float` 函数与 `map` 函数的作用是相似的。由于 `sin` 函数的输出范围是 -1 到 1,因此我们需要将其映射到 0 到 4096 的区间内。
下面我们进行配置运算放大器 opamp 有三个输入口一个+输入一个-输入一个输出 + 为A1 -为A2 输出为A3 官方给了一个典型电路和计算公式
这里我们进行使用一种典型电路及输入就是输出连接方法为输出连接-输入
这里配置为高速模式
OPAMP.begin(OPAMP_SPEED_HIGHSPEED);
还有最后就是读取ADC并且打印这里我使用的A5 这个就很简单了接收并且打印
int reading = analogRead(A5);
//a=map(reading,0,4096,0,2*3.1415926);
Serial.println(reading);
接下来,我将为大家介绍如何改变 `k` 和 `b` 的值。我所采用的方法是通过串口发送数据,数据格式为“A:15”。在接收到字符串后,我会对其进行解析,以提取冒号前后的数据,从而得到 `A` 和 `15`,进而相应地更改对应的值。
代码如下
String str="";
if (Serial.available() > 0) {
// 读取一个字节的数据,并将其转换为字符后添加到receivedString中
// 注意:这里假设我们不知道字符串何时结束,因此持续读取直到超时或检测到特定的结束字符
char incomingByte = Serial.read();
// 检查是否是字符串的结束符(例如换行符'\n')
// 注意:根据你的发送方,结束符可能不同
if (incomingByte == '\n') {
// 如果是结束符,则不将其添加到receivedString中,而是直接处理字符串
// 在这里,我们只是简单地打印它
parseString(receivedString);
// 清空receivedString,为接收下一个字符串做准备
receivedString = "";
} else {
// 如果不是结束符,则将其添加到receivedString中
receivedString += incomingByte;
}
}
void parseString(String input) {
// 查找冒号在字符串中的位置
int colonIndex = input.indexOf(':');
if (colonIndex == -1) {
Serial.println("Input format is incorrect. Missing colon.");
return;
}
// 提取标识符(冒号之前的部分)
String identifier = input.substring(0, colonIndex);
// 提取数字(冒号之后的部分),注意substring的第二个参数是结束索引(不包含),所以我们需要+1
String numberStr = input.substring(colonIndex + 1);
int number = numberStr.toInt(); // 将字符串转换为整数
if(identifier=="K")
{
K=number;
}
if(identifier=="B")
{
B=number;
}
}
完整的代码
#include <OPAMP.h>
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
OPAMP.begin(OPAMP_SPEED_HIGHSPEED);
analogReadResolution(14);
analogWriteResolution(12);
}
double a=0;
String receivedString = "";
int K=1;
int B=0;
void loop() {
// put your main code here, to run repeatedly:
a+=0.01;
if(a>2*PI)
{
a=0;
}
float out=map_float(sin(K*a+B),-1,1,0,4096);
analogWrite(DAC, out);
// Serial.println(out);
int reading = analogRead(A5);
//a=map(reading,0,4096,0,2*3.1415926);
Serial.println(reading);
//Serial.print(",");
//Serial.println(out);
String str="";
if (Serial.available() > 0) {
// 读取一个字节的数据,并将其转换为字符后添加到receivedString中
// 注意:这里假设我们不知道字符串何时结束,因此持续读取直到超时或检测到特定的结束字符
char incomingByte = Serial.read();
// 检查是否是字符串的结束符(例如换行符'\n')
// 注意:根据你的发送方,结束符可能不同
if (incomingByte == '\n') {
// 如果是结束符,则不将其添加到receivedString中,而是直接处理字符串
// 在这里,我们只是简单地打印它
parseString(receivedString);
// 清空receivedString,为接收下一个字符串做准备
receivedString = "";
} else {
// 如果不是结束符,则将其添加到receivedString中
receivedString += incomingByte;
}
}
}
//AB
void parseString(String input) {
// 查找冒号在字符串中的位置
int colonIndex = input.indexOf(':');
if (colonIndex == -1) {
Serial.println("Input format is incorrect. Missing colon.");
return;
}
// 提取标识符(冒号之前的部分)
String identifier = input.substring(0, colonIndex);
// 提取数字(冒号之后的部分),注意substring的第二个参数是结束索引(不包含),所以我们需要+1
String numberStr = input.substring(colonIndex + 1);
int number = numberStr.toInt(); // 将字符串转换为整数
if(identifier=="K")
{
K=number;
}
if(identifier=="B")
{
B=number;
}
}
float map_float(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
最后的结果图片我使用的是VOFA串口助手打印的因为arduino的串口示波器更改有点麻烦
中间是更改K和B值的改变下面的视频为详细教程和结果和一些其他的不太好纸面介绍的知识
-
加入了学习《【Follow me第二季第2期】+开发板硬件介绍和实现任务一 LED灯闪烁和串口打印》,观看 【Follow me第二季第2期】实现任务二 驱动12x8点阵LED;用DAC生成正弦波并放大采集
- 2024-09-02
-
回复了主题帖:
【Follow me第二季第2期】+开发板硬件介绍和实现任务一 LED灯闪烁和串口打印
秦天qintian0303 发表于 2024-9-1 18:16
自带串口调试还是不错的,用外部串口调试工具应该也可以吧?
外部也可以但是要注意的是这个和以往的r3是不同的以往USBa和rt串口都是serial但是r4是typec是serial 端口可能是serial1这个还没确定需要试一下看但是我想的最好还是使用debug但是我还没有实现后续成功了在这里分享
- 2024-09-01
-
发表了主题帖:
【Follow me第二季第2期】+开发板硬件介绍和实现任务一 LED灯闪烁和串口打印
本帖最后由 eew_ljd6R2 于 2024-9-1 15:14 编辑
大家好!很高兴能与大家分享关于我们申请到的Arduino UNO R4 WiFi开发板的硬件资源。这款开发板在保留了Arduino UNO R3大部分接口的基础上,进行了一些升级并增加了新的功能。
与R3的兼容性:它保留了与R3相同的大部分接口,这意味着我们可以很方便地在R3和R4之间进行切换,同时兼容大部分为R3设计的扩展板和项目。
新增CAN通信接口:最显著的是增加了CAN(Controller Area Network)通信接口。CAN是一种常用的工业通信协议,广泛应用于汽车、工业控制等领域。通过增加CAN接口,我们可以模拟更多的功能,实现与各种CAN设备的通信和控制。
实现模拟输出:这款开发板还增加了DAC(Digital-to-Analog Converter,数字到模拟转换器)接口,实现了模拟输出功能。这使得Arduino UNO R4 WiFi不仅能够处理数字信号,还能处理模拟信号,进一步扩展了其应用范围。
总的来说,Arduino UNO R4 WiFi开发板在保留了原有功能的基础上,通过增加CAN通信接口和DAC接口,提供了更丰富的硬件资源和更广泛的应用场景。无论是对于初学者还是对于需要进行复杂项目开发的用户来说,都是一个非常不错的选择。
下面我们通过原理图来详细查看。本次Arduino UNO R4 WiFi开发板增加了USB Type-C接口,这是一个重要的升级。通过USB Type-C接口,开发板可以实现直接模拟串口进行通信,这与以往将P1和P2引出到USB A型接口的设计有所不同。因此,在设计时我们需要格外注意这一点。
另外,新增加的LED矩阵也是本次升级的一个亮点。这个LED矩阵使得开发板可以显示更多的信息,为用户提供了更加丰富的视觉反馈和交互体验。
总的来说,通过原理图我们可以清晰地看到Arduino UNO R4 WiFi开发板在硬件资源上的升级和增加的新功能。这些改进不仅提升了开发板的性能,还扩展了其应用场景,为用户带来了更多的便利和可能性。
在关注Arduino UNO R4 WiFi开发板时,我们特别注意到其上的瑞萨MCU是5V芯片,而ESP32则是3.3V芯片。由于这两种芯片的电压要求不同,因此需要使用电平转换芯片来进行通信,以确保信号的稳定传输。此外,这两个芯片之间的通信是通过串口来实现的,这使得它们能够方便地交换数据和信息。在设计时,我们需要特别注意电平转换芯片的选择和使用,以确保开发板的正常工作和稳定性。
下面我们来完成本次任务1:blink。通过原理图,我们可以看到使用13端口来控制黄色的LED灯进行闪烁。
首先,我们需要配置13端口为输出模式,这样我们才能通过它来控制LED灯的亮灭。在Arduino编程中,我们可以使用pinMode()函数来设置端口模式,使用digitalWrite()函数来控制端口的电平。
接下来,在loop()循环中,我们使用delay()函数进行延迟,先延迟1000毫秒(即1秒),然后点亮LED灯;再延迟1000毫秒,熄灭LED灯。这样,LED灯就会以1秒的间隔进行闪烁。
以下是实现该功能的Arduino代码示例:
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
效果如下
接下来我们来完成第二个任务:打印“Hello EEWorld!”。这个任务相对简单,我们只需要在loop()循环中每隔一秒通过串口打印一次即可。
首先,我们需要确保已经通过USB Type-C接口将开发板连接到电脑,并在Arduino IDE中选择正确的串口。
然后,在setup()函数中,我们使用Serial.begin()函数来初始化串口通信,设置波特率为9600(或其他你想要的波特率)。
在loop()函数中,我们使用Serial.println()函数来打印“Hello EEWorld!”,并使用delay()函数来每隔一秒打印一次。
以下是实现该功能的Arduino代码示例:
void setup() {
// 初始化串口通信,设置波特率为9600
Serial.begin(9600);
}
void loop() {
// 每隔一秒打印一次“Hello EEWorld!”
Serial.println("Hello EEWorld!");
delay(1000);
}
将上述代码上传到Arduino UNO R4 WiFi开发板后,打开Arduino IDE的串口监视器,设置正确的波特率,你就可以看到每隔一秒就打印一次“Hello EEWorld!”了。这样,我们就完成了第二个任务。
下面我们把两个都结合起来代码如下
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println("Hello EEWorld!");
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
初次接触Arduino R4开发板,我深感其魅力无穷。这款开发板不仅继承了Arduino系列一贯的易用性和灵活性,还在硬件资源上进行了显著的升级和扩展。
在使用过程中,我深刻体会到了Arduino R4的强大功能。新增的USB Type-C接口让我能够更方便地与电脑进行通信和数据传输,而无需担心接口不兼容的问题。同时,开发板上的LED矩阵也为我提供了丰富的视觉反馈,使得调试和交互变得更加直观和便捷。
在编程方面,Arduino R4同样表现出色。我使用了Arduino IDE进行代码编写和上传,整个过程非常顺畅。通过简单的代码,我就能够控制开发板上的各种硬件资源,实现各种有趣的功能和效果。
当然,初次使用Arduino R4也遇到了一些挑战。比如,我需要适应新的硬件资源和接口,了解它们的工作原理和使用方法。但是,通过查阅资料和不断实践,我逐渐掌握了这些新知识,并成功完成了多个有趣的项目。
总的来说,初次使用Arduino R4开发板是一次非常愉快和充实的经历。我不仅学到了很多新知识,还感受到了Arduino系列开发板的无限魅力和潜力。我相信,在未来的学习和实践中,我会更加深入地了解和掌握这款开发板,创造出更多有趣和实用的项目。
下面的视频为这个项目的整个过程