- 2024-10-08
-
加入了学习《FollowMe 第二季:2 - Arduino UNO R4 Wi-Fi 及任务讲解》,观看 Arduino UNO R4 Wi-Fi 及任务讲解
-
发表了主题帖:
【Follow me第二季第2期】任务汇总贴
本帖最后由 tangye 于 2024-11-2 23:02 编辑
非常荣幸能够参加电子工程世界和得捷电子举办得Follow me 第二季第2期活动。
我以前没有接触过嵌入式系统的开发,对电子技术略有了解。本次活动让我有机会接触到一些新的领域,学习了不少新知识。实验过程中遇到不少问题,有些通过参考各位大佬的帖子解决了,有些还需要将来进一步探索。
我的代码:https://download.eeworld.com.cn/detail/tangye/634556
我的视频:https://training.eeworld.com.cn/video/41255
活动任务分4个阶段
入门任务:搭建环境并开启第一步 Blink / 串口打印 Hello EEWorld!
基础任务:驱动 12x8 点阵 LED;用 DAC 生成正弦波;用 OPAMP 放大 DAC 信号;用 ADC 采集并且打印数据到串口等其他接口可上传到上位机显示曲线
进阶任务:通过Wi-Fi,利用MQTT协议接入到开源的智能家居平台 HA(HomeAssistant)
扩展任务:
1. 通过 LTR-329 环境光传感器,上传光照度到 HA,通过 HA 面板显示数据;
2. 通过 SHT40温湿度传感器,上传光照度到 HA,通过 HA 面板显示数据;
我拿到的物料包括 Arduino uno R4 wifi 以及 LTR-329 光照传感器,SHT40 湿温度传感器。这是全家福照片
这是 Arduino uno R4 wifi 引脚图。后续实验的接线依赖这张图片
任务完成思路:
1. 入门任务:搭建环境并开启第一步Blink / 串口打印Hello EEWorld!
这部分的主要内容是安装Arduino-IDE环境,连接到开发板,并且根据IDE提供的范例,完成相应的代码。
Arduino IDE的官方下载位于
https://www.arduino.cc/en/software
我下载了 arduino-ide_2.3.2,在Windows 10环境中双击安装即可。
安装完成后,在“文件”--“首选项” 中,可以修改项目文件夹地址等内容,便于后续备份和维护。
打开示例文件Blink,可以看到相应的代码。
在这个文件的基础上,我加上串口输出指令,完成入门任务。
流程图
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(9600);
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
Serial.println("Hello World!");
delay(500); // wait for 0.5 second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
Serial.println("This is Arduino UNO R4!");
delay(500); // wait for 0.5 second
}
串口监视器输出如图
开发板闪灯视频,可以看到受控的LED灯以1Hz的频率闪烁,同时TX指示灯每秒闪2下
[localvideo]a245e3b3be64bf0461c2552c13c0d95d[/localvideo]
2. 基础任务:驱动 12x8 点阵 LED;用 DAC 生成正弦波;用 OPAMP 放大 DAC 信号;用 ADC 采集并且打印数据到串口等其他接口可上传到上位机显示曲线
基础任务在任务讲解视频 https://training.eeworld.com.cn/video/40793 中有详细介绍。
根据视频和论坛的帮助,我设计了LED矩阵的驱动代码,在程序初始化阶段,显示红心持续2秒,然后转为字幕并滚动
流程图
#include "ArduinoGraphics.h"
#include "Arduino_LED_Matrix.h"
ArduinoLEDMatrix matrix;
byte frame[8][12] = {
{ 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0 },
{ 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 },
{ 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
void setup() {
Serial.begin(115200);
matrix.begin();
matrix.renderBitmap(frame, 8, 12);
delay(2000);
matrix.beginDraw();
matrix.stroke(0xFFFFFFFF);
matrix.textFont(Font_4x6);
matrix.beginText(0, 1, 0xFFFFFF);
matrix.println("Hey");
matrix.endText();
matrix.endDraw();
delay(2000);
}
void loop() {
matrix.beginDraw();
matrix.stroke(0xFFFFFFFF);
matrix.textScrollSpeed(200);
const char text[] = "Hello Digikey!";
matrix.textFont(Font_5x7);
matrix.beginText(0, 1, 0xFFFFFF);
matrix.println(text);
matrix.endText(SCROLL_LEFT);
matrix.endDraw();
delay(500);
}
[localvideo]89876f0a54b42e0f3882f4bc5a69e9d7[/localvideo]
DAC生成正弦波,通过内部OPAMP放大,并输出到ADC最终显示在“串口绘图仪”,需要在模拟引脚上接线。
根据原理图,A0输出DAC波形,用导线连接到A1。在A2和A3之间接一个10K电阻,在A2和地之间接一个10K电阻。此时A3(即运放输出端)的电压信号为A0的2倍。
为了用内置的ADC测量并显示电压波形,需要分别连接A0和A4,A3和A5。我完成的接线如图
流程图
DAC/OPAMP/ADC代码
#include "analogWave.h"
#include <OPAMP.h>
// Create an instance of the analogWave class, using the DAC pin
analogWave wave(DAC);
int freq = 100;
void setup() {
wave.sine(freq); // Generate a sine wave 100Hz
wave.amplitude(0.4); // 0.0 ~ 1.0
OPAMP.begin(OPAMP_SPEED_HIGHSPEED);
analogWriteResolution(14);
Serial.begin(230400); // baud rate 230.4k
}
void loop() {
Serial.print(analogRead(A4));
Serial.print(" ");
Serial.println(analogRead(A5));
}
示波器观察A0
示波器观察A3
“串口绘图仪” 的显示
[localvideo]be9b39364da2e81c3416ad28d1cf9f56[/localvideo]
3. 进阶任务:通过Wi-Fi,利用MQTT协议接入到开源的智能家居平台 HA(HomeAssistant)
这个任务,需要准备HomeAssistant和EMQX。我在ubuntu系统里,用Docker方式搭建了HA环境,主要遇到的问题是docker镜像拉取困难。
经过一番折腾,发现home-assistant官方镜像托管在github上,可以直接pull(就是有些慢)
$ docker pull ghcr.io/home-assistant/home-assistant:stable
EMQX镜像是单独找的,实际上直接安装也很方便。
分别启动两个镜像
$ docker run -d --name="home-assistant" -v ~/HA_config:/config -p 8123:8123 ghcr.io/home-assistant:stable
$ docker run -d --name="EMQX" --network host emqx/emqx:5.7.2
这里需要说明一下,EMQX启动的时候,我没有指定端口映射,而是直接让容器使用了host的IP。
原因是,如果让EMQX默认连接内部网络docker0,每次启动的时候容器会拿到dhcp地址。启动顺序会影响地址,配置home-assistant指定的地址有可能失效。
接下来,按照任务讲解视频,分别访问 http://ip:18083 和 http://ip:8123 完成 EMQX 和 home-assistant 的配置
后续内容参考了各位大佬的帖子,在此一并致谢。
通过“示例”--“WiFiS3”--“ConnectWithWPA” 这个例程,修改 arduino_secrets.h 文件,写入 ssid(设备只支持2.4GHz)和访问密码,编译运行后可以在串口监视器上确认无线网络是否正确连接。
然后在库管理中,安装ArduinoMqttClient,运行例程连接MQTT服务
最后,用开发板送出数据,在HomeAssistant面板上显示。
流程图
代码,通过DAC在A0引脚输出正弦波,开发板测量电压值,显示在HA面板上
#include <ArduinoMqttClient.h>
#include "analogWave.h"
#include <Wire.h>
#include <WiFiS3.h>
#include "arduino_secrets.h"
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID; // your network SSID (name)
char pass[] = SECRET_PASS; // your network password (use for WPA, or use as key for WEP)
int status = WL_IDLE_STATUS;
WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);
analogWave wave(DAC);
const char broker[] = "192.168.90.79"; //MQTT Server IP
int port = 1883; //MQTT Server port
const char topic[] = "UNO/R4/ADC/volt_d"; //Topic
const char mqttuser[] = "admin";
const char mqttpassword[] = "admin";
//set interval for sending messages (milliseconds)
const long interval = 5000;
unsigned long previousMillis = 0;
int count = 0;
void setup() {
// Generate Wave
wave.sine(2); // Generate a sine wave 2Hz
wave.amplitude(0.8); // 0.0 ~ 1.0
analogWriteResolution(10);
Serial.begin(115200); // baud rate 115.2k
// Try to connect WLAN:
Serial.print("Attempting to connect to WPA SSID: ");
Serial.println(ssid);
while (WiFi.begin(ssid, pass) != WL_CONNECTED) {
Serial.print(".");
delay(5000);
}
Serial.println("You're connected to the network");
Serial.println();
Serial.print("Attempting to connect to the MQTT broker: ");
Serial.println(broker);
mqttClient.setUsernamePassword(mqttuser, mqttpassword);
while (!mqttClient.connect(broker, port)) {
Serial.print("MQTT connection failed! Error code = ");
Serial.println(mqttClient.connectError());
delay(1000);
}
Serial.println("You're connected to the MQTT broker!");
Serial.println();
}
void loop() {
mqttClient.poll();
//通过ADC采集 A0上输出的波形
float A0_voltage = analogRead(A0) * 4.7/1023; //A0, 10bit ADC采样,修正为十进制数
mqttClient.beginMessage(topic);
mqttClient.print(A0_voltage); //把计算出的电压值上传到MQTT服务器
mqttClient.endMessage();
delay(500);
}
在HA面板显示数据,需要修改 configuration.yaml 增加以下部分
mqtt:
- sensor:
- unique_id: arduino uno Voltage
name: "arduino Volt"
state_topic: "UNO/R4/ADC/volt_d"
suggested_display_precision: 3
unit_of_measurement: "V"
value_template: "{{ value }}"
重新加载YAML配置后,HA面板显示变化的电压值
扩展任务:
1. 通过 LTR-329 环境光传感器,上传光照度到 HA,通过 HA 面板显示数据;
2. 通过 SHT40温湿度传感器,上传光照度到 HA,通过 HA 面板显示数据;
连接传感器,我直接用的I2C接口,在传感器上焊了插针,用跳线连接VCC/GND/SCL/SDA信号。
使用LTR-329的例程,直接可以在串口监视器上看到CH0和CH1的光照度
在例程的基础上,结合上一阶段的代码,就能把数据上传到MQTT服务器。
流程图
传感器有两个数据,上传到一个订阅里用了json格式。
#include <ArduinoMqttClient.h>
#include <Wire.h>
#include <WiFiS3.h>
#include <Arduino_JSON.h>
#include "arduino_secrets.h"
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID; // your network SSID (name)
char pass[] = SECRET_PASS; // your network password (use for WPA, or use as key for WEP)
int status = WL_IDLE_STATUS;
#include "Adafruit_LTR329_LTR303.h"
Adafruit_LTR329 ltr = Adafruit_LTR329();
WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);
const char broker[] = "192.168.90.79"; //MQTT Server IP
int port = 1883; //MQTT Server port
const char topic[] = "UNO/R4/LTR329"; //Topic
const char mqttuser[] = "admin";
const char mqttpassword[] = "admin";
//set interval for sending messages (milliseconds)
const long interval = 5000;
unsigned long previousMillis = 0;
int count = 0;
JSONVar dataObj;
void setup() {
Serial.begin(115200);
Serial.println("Adafruit LTR-329 advanced test");
if ( ! ltr.begin() ) {
Serial.println("Couldn't find LTR sensor!");
while (1) delay(10);
}
Serial.println("Found LTR sensor!");
ltr.setGain(LTR3XX_GAIN_2);
Serial.print("Gain : ");
switch (ltr.getGain()) {
case LTR3XX_GAIN_1: Serial.println(1); break;
case LTR3XX_GAIN_2: Serial.println(2); break;
case LTR3XX_GAIN_4: Serial.println(4); break;
case LTR3XX_GAIN_8: Serial.println(8); break;
case LTR3XX_GAIN_48: Serial.println(48); break;
case LTR3XX_GAIN_96: Serial.println(96); break;
}
ltr.setIntegrationTime(LTR3XX_INTEGTIME_100);
Serial.print("Integration Time (ms): ");
switch (ltr.getIntegrationTime()) {
case LTR3XX_INTEGTIME_50: Serial.println(50); break;
case LTR3XX_INTEGTIME_100: Serial.println(100); break;
case LTR3XX_INTEGTIME_150: Serial.println(150); break;
case LTR3XX_INTEGTIME_200: Serial.println(200); break;
case LTR3XX_INTEGTIME_250: Serial.println(250); break;
case LTR3XX_INTEGTIME_300: Serial.println(300); break;
case LTR3XX_INTEGTIME_350: Serial.println(350); break;
case LTR3XX_INTEGTIME_400: Serial.println(400); break;
}
ltr.setMeasurementRate(LTR3XX_MEASRATE_200);
Serial.print("Measurement Rate (ms): ");
switch (ltr.getMeasurementRate()) {
case LTR3XX_MEASRATE_50: Serial.println(50); break;
case LTR3XX_MEASRATE_100: Serial.println(100); break;
case LTR3XX_MEASRATE_200: Serial.println(200); break;
case LTR3XX_MEASRATE_500: Serial.println(500); break;
case LTR3XX_MEASRATE_1000: Serial.println(1000); break;
case LTR3XX_MEASRATE_2000: Serial.println(2000); break;
}
// Try to connect WLAN:
Serial.print("Attempting to connect to WPA SSID: ");
Serial.println(ssid);
while (WiFi.begin(ssid, pass) != WL_CONNECTED) {
Serial.print(".");
delay(5000);
}
Serial.println("You're connected to the network");
Serial.println();
Serial.print("Attempting to connect to the MQTT broker: ");
Serial.println(broker);
mqttClient.setUsernamePassword(mqttuser, mqttpassword);
while (!mqttClient.connect(broker, port)) {
Serial.print("MQTT connection failed! Error code = ");
Serial.println(mqttClient.connectError());
delay(1000);
}
Serial.println("You're connected to the MQTT broker!");
Serial.println();
}
void loop() {
bool valid;
uint16_t visible_plus_ir, infrared;
if (ltr.newDataAvailable()) {
valid = ltr.readBothChannels(visible_plus_ir, infrared);
if (valid) {
Serial.print("CH0 Visible + IR: ");
Serial.print(visible_plus_ir);
Serial.print("\t\tCH1 Infrared: ");
Serial.println(infrared);
}
}
if (ltr.newDataAvailable()) {
valid = ltr.readBothChannels(visible_plus_ir, infrared);
if (valid) {
dataObj["dataCH0"] = visible_plus_ir;
dataObj["dataCH1"] = infrared;
String jsonString = JSON.stringify(dataObj);
mqttClient.beginMessage(topic);
mqttClient.print(jsonString);
mqttClient.endMessage();
}
}
delay(500);
}
同样要修改 HomeAssistant的configuration.yaml
mqtt:
- sensor:
- unique_id: CH0_Visible_IR
name: "CH0_Visible_IR"
state_topic: "UNO/R4/LTR329"
suggested_display_precision: 1
unit_of_measurement: "lux"
value_template: "{{ value_json.dataCH0 }}"
- unique_id: CH1_IR
name: "CH1_IR"
state_topic: "UNO/R4/LTR329"
suggested_display_precision: 1
unit_of_measurement: "lux"
value_template: "{{ value_json.dataCH1 }}"
重新加载 YAML 配置,HA面板显示两个测量通道的数据
这是台灯的照度信息。关闭台灯后,照度下降,HA面板可以显示历史曲线
换上SHT40,可以测试温湿度数据。示例中的 SGP40_SHT40_Test 不能直接运行,需要删除SGP40的部分。
流程图
有了LTR329的使用经验,SHT40的代码如下:
#include <ArduinoMqttClient.h>
#include <Wire.h>
#include <WiFiS3.h>
#include <Arduino_JSON.h>
#include "arduino_secrets.h"
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID; // your network SSID (name)
char pass[] = SECRET_PASS; // your network password (use for WPA, or use as key for WEP)
int status = WL_IDLE_STATUS;
#include "Adafruit_SHT4x.h"
Adafruit_SHT4x sht4;
WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);
const char broker[] = "192.168.90.79"; //MQTT Server IP
int port = 1883; //MQTT Server port
const char topic[] = "UNO/R4/SHT40"; //Topic
const char mqttuser[] = "admin";
const char mqttpassword[] = "admin";
//set interval for sending messages (milliseconds)
const long interval = 5000;
unsigned long previousMillis = 0;
int count = 0;
JSONVar dataObj;
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println();
Serial.println(F("##################################"));
Serial.println(F("SHT40 test with compensation"));
//*********************************************************************
//*************ADVANCED SETUP - SAFE TO IGNORE!************************
//Here we can configure the SHT40 Temperature and Humidity Sensor
//First we set the measurement precision
//There are three precision levels: High, Medium and Low
//The precision levels direclty affect the measurement duration, noise level and energy consumption
//On doubt, just leave it on default (High precision)
sht4.setPrecision(SHT4X_HIGH_PRECISION);
switch (sht4.getPrecision()) {
case SHT4X_HIGH_PRECISION:
Serial.println(F("SHT40 set to High precision"));
break;
case SHT4X_MED_PRECISION:
Serial.println(F("SHT40 set to Medium precision"));
break;
case SHT4X_LOW_PRECISION:
Serial.println(F("SHT40 set to Low precision"));
break;
}
//*********************************************************************
//*************ADVANCED SETUP - SAFE TO IGNORE!************************
// The SHT40 has a built-in heater, which can be used for self-decontamination.
// The heater can be used for periodic creep compensation in prolongued high humidity exposure.
// For normal operation, leave the heater turned off.
sht4.setHeater(SHT4X_NO_HEATER);
switch (sht4.getHeater()) {
case SHT4X_NO_HEATER:
Serial.println(F("SHT40 Heater turned OFF"));
break;
case SHT4X_HIGH_HEATER_1S:
Serial.println(F("SHT40 Heater: High heat for 1 second"));
break;
case SHT4X_HIGH_HEATER_100MS:
Serial.println(F("SHT40 Heater: High heat for 0.1 second"));
break;
case SHT4X_MED_HEATER_1S:
Serial.println(F("SHT40 Heater: Medium heat for 1 second"));
break;
case SHT4X_MED_HEATER_100MS:
Serial.println(F("SHT40 Heater: Medium heat for 0.1 second"));
break;
case SHT4X_LOW_HEATER_1S:
Serial.println(F("SHT40 Heater: Low heat for 1 second"));
break;
case SHT4X_LOW_HEATER_100MS:
Serial.println(F("SHT40 Heater: Low heat for 0.1 second"));
break;
}
//*********************************************************************
//*************ADVANCED SETUP IS OVER - LET'S CHECK THE CHIP ID!*******
if (! sht4.begin()) {
Serial.println(F("SHT40 sensor not found!"));
while (1) ;
}
else
{
Serial.print(F("SHT40 detected!\t"));
Serial.print(F("Serial number:\t"));
Serial.println(sht4.readSerial(), HEX);
}
Serial.println(F("----------------------------------"));
// Try to connect WLAN:
Serial.print("Attempting to connect to WPA SSID: ");
Serial.println(ssid);
while (WiFi.begin(ssid, pass) != WL_CONNECTED) {
Serial.print(".");
delay(5000);
}
Serial.println("You're connected to the network");
Serial.println();
Serial.print("Attempting to connect to the MQTT broker: ");
Serial.println(broker);
mqttClient.setUsernamePassword(mqttuser, mqttpassword);
while (!mqttClient.connect(broker, port)) {
Serial.print("MQTT connection failed! Error code = ");
Serial.println(mqttClient.connectError());
delay(1000);
}
Serial.println("You're connected to the MQTT broker!");
Serial.println();
}
void loop() {
sensors_event_t humidity, temp;
sht4.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data
float t = temp.temperature;
Serial.print("Temp *C = "); Serial.println(t);
float h = humidity.relative_humidity;
Serial.print("Hum. % = "); Serial.println(h);
dataObj["SHT40_temp"] = t;
dataObj["SHT40_humi"] = h;
String jsonString = JSON.stringify(dataObj);
mqttClient.beginMessage(topic);
mqttClient.print(jsonString);
mqttClient.endMessage();
delay(1000);
}
修改 HomeAssistant的configuration.yaml
mqtt:
- sensor:
- unique_id: temperature
name: "Temperature"
state_topic: "UNO/R4/SHT40"
suggested_display_precision: 1
unit_of_measurement: "掳C"
value_template: "{{ value_json.SHT40_temp }}"
- unique_id: humidity
name: "Humidity"
state_topic: "UNO/R4/SHT40"
suggested_display_precision: 1
unit_of_measurement: "%"
value_template: "{{ value_json.SHT40_humi }}"
HA面板显示当前的温湿度
遗留问题
HA面板的 YAML配置重新加载以后,之前环节出现的传感器依然显示(呈现灰色)。后续看看如何清理这些内容。
对本活动的心得体会
Fellow me 活动对于我这类没有经验的新手非常友好,教学视频、论坛帖子帮助我完成了从入门到进阶的每项实验。
通过这次活动,我对Arduino 开发环境,各种传感器和相应的库,MQTT都有了初步认识。Arduino UNO R4 WiFi 功能丰富,以后可以继续深入学习。
最后,再次感谢电子工程世界,感谢得捷电子,感谢论坛各位大佬,感谢活动管理员lightxixi
- 2024-10-07
-
上传了资料:
R4_wifi_实验代码
-
加入了学习《Follow me第二季第2期视频演示》,观看 演示视频
- 2024-10-06
-
加入了学习《【Follow me第二季第2期】任务提交》,观看 【Follow me第二季第2期】
-
加入了学习《Follow Me 第二季第二期总结视频》,观看 follow me 集合
- 2024-10-01
-
加入了学习《【Follow me 第二季第2期任务】 各个任务实现的展示效果》,观看 【Follow me 第二季第2期任务】MQTT接入到HomeAssistant
- 2024-09-29
-
回复了主题帖:
【Follow me第二季第2期】DAC 生成正弦波
这个DIY的示波器挺别致的
-
加入了学习《Follow me 第二季第2期dvacos视频》,观看 Follow me 第二季第2期视频
- 2024-09-26
-
加入了学习《【Follow me第二季第2期】+开发板硬件介绍和实现任务一 LED灯闪烁和串口打印》,观看 【Follow me第二季第2期】+通过外部SHT40温湿度传感器,上传温湿度到HA
- 2024-09-06
-
回复了主题帖:
【Follow me第二季第2期】扩展任务二:通过外部SHT40温湿度传感器,上传温湿度到HA...
效率很高啊,恭喜