- 2024-11-08
-
回复了主题帖:
【Follow me第二季第2期】(Arduino Uno R4)作品提交汇总
ctrlshift12138 发表于 2024-11-8 23:54
在旧版种,代码缩进是正常的;新版界面,代码缩进有问题的。
...
好像是 Tab 缩进无法识别
-
回复了主题帖:
【Follow me第二季第2期】(Arduino Uno R4)作品提交汇总
nmg 发表于 2024-11-2 19:36
可以具体说说嘛,看我们这边能否改进
在旧版种,代码缩进是正常的;新版界面,代码缩进有问题的。
- 2024-10-11
-
回复了主题帖:
【Follow me第二季第2期】(Arduino Uno R4)作品提交汇总
文章中嵌入的代码缩进有些问题,大家可以下载源代码自行查看
-
回复了主题帖:
【Follow me第二季第2期】(Arduino Uno R4)作品提交汇总
文章最后2张图是显示BUG,我在编辑页面是没有这2张图的,浏览时请大家忽略
- 2024-10-08
-
发表了主题帖:
【Follow me第二季第2期】(Arduino Uno R4)作品提交汇总
# 【Follow me第二季第2期】任务提交
## 一、前置准备:
硬件:无
软件:VSCode、科学上网工具
### 1、设置代理
打开VSCode,进入设置
搜索“代理”,在1和2中填写代理服务器的地址和端口,并取消勾选3
### 2、安装PlatformIO IDE扩展
选择扩展,搜索“PlatformIO”,安装扩展插件
### 4、升级WiFi模块固件(非必须步骤,可跳过)
下载下面代码到arduino Uno R4 WiFi开发板,检查的WiFi模块(ESP32-S3)的固件。如果WiFi模块固件不是最新版本,建议升级固件。
```cpp
#include
#include
void setup() {
Serial.begin(115200);
BaseType_t xReturn;
// 检查WiFi模组
if (WiFi.status() == WL_NO_MODULE)
{
Serial.println("Communication with WiFi module failed!");
while (true);
}
String fv = WiFi.firmwareVersion();
Serial.print("Current WiFi firmware version:");
Serial.println(fv);
Serial.print("Laste WiFi firmware version:");
Serial.println(WIFI_FIRMWARE_LATEST_VERSION);
if (fv < WIFI_FIRMWARE_LATEST_VERSION)
{
Serial.println("Please upgrade the firmware");
}
}
void loop() {
}
```
[官方更新WiFi模块固件教程和资源链接](https://support.arduino.cc/hc/en-us/articles/9670986058780-Update-the-connectivity-module-firmware-on-UNO-R4-WiFi#espflash)
1、下载并解压官方资源
2、使用跳线帽(或金属镊子)短接图中突出显示的引脚:
![The GND and Download ESP32 pins.](/data/attachment/forum/202410/08/020439n8seso30j6f2sosn.png.thumb.jpg?rand=7115.516748703152)
3、使用 USB 线将 UNO R4 WiFi 板连接到电脑
4、运行`update.bat`脚本
5、选择正确的串口,并等待固件更新完成
## 二、入门任务
**Blink&串口打印Hello EEWorld!**
硬件:Arduino UNO R4 WiFi、USB-C to A线
软件:VSCode
### 1、操作步骤
打开PlatformIO的“home”主页,选择“New Project”
填写“Name”,“Board”选择“Arduino Uno R4 WiFi”,“Framework”选择“Arduino”,“Location”为项目存放位置,自行设置。
点击“FInish”,等待相关资源下载完成(如果卡在下载资源,请设置代理)。
### 2、代码
```cpp
#include
#include
void blink(void * para);
void printHelloWorld(void * para);
// 任务句柄
TaskHandle_t blink_task_handle = NULL;
TaskHandle_t print_hello_world_task_handle = NULL;
void setup() {
pinMode(LED_BUILTIN, OUTPUT); // LED管脚设置为输出模式
Serial.begin(115200); // 串口设置波特率115200
// 创建Blink任务
xTaskCreate((TaskFunction_t)blink,
(const char*)"blink",
100,
NULL,
1,
&blink_task_handle);
configASSERT( blink_task_handle );
// 创建串口打印任务
xTaskCreate((TaskFunction_t)printHelloWorld,
(const char *)"printHelloWord",
100,
NULL,
2,
&print_hello_world_task_handle);
configASSERT(print_hello_world_task_handle);
vTaskStartScheduler(); // 启动任务调度
}
void blink(void * para)
{
while (1)
{
digitalWrite(LED_BUILTIN, HIGH); // 点亮LED
vTaskDelay(500 * portTICK_PERIOD_MS); // 延时500ms
digitalWrite(LED_BUILTIN, LOW); // 关闭LED
vTaskDelay(500 * portTICK_PERIOD_MS); // 延时500ms
}
}
void printHelloWorld(void * para)
{
while (1)
{
Serial.println("Hello EEWorld !"); // 串口打印
vTaskDelay(1000 * portTICK_PERIOD_MS); // 延时1000ms
}
}
void loop() {
}
```
### 3、说明
软件流程图:
使用FreeRTOS创建了“blink”和“printHelloWord”两个任务,分别实现了LED指示灯每隔0.5秒闪烁一次,串口每隔1秒发送一次“Hello EEWorld!”。
```cpp
pinMode(LED_BUILTIN, OUTPUT); // LED管脚设置为输出模式
```
`LED_BUILTIN`为板上的黄色LED灯对应的管脚,见下图,`OUTPUT`表示将该管脚设置为输出模式。
### 4、效果展示
LED灯闪烁
串口打印
## 三、基础任务
**驱动12x8点阵LED;用DAC生成正弦波;用OPAMP放大DAC信号;用ADC采集并且打印数据到串口等其他接口可上传到上位机显示曲线**
硬件:Arduino UNO R4 WiFi、USB-C to A线、10K插件电阻、公对公杜邦线
软件:VSCode、Arduino IDE
### 1、代码:驱动12x8点阵LED
```cpp
#include
#include
#include
#include
void blink(void * para);
void ledMatrix(void * para);
// 任务句柄
TaskHandle_t blink_task_handle = NULL;
TaskHandle_t led_matrix_task_handle = NULL;
ArduinoLEDMatrix matrix;
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
matrix.begin();
// 创建Blink任务
xTaskCreate((TaskFunction_t)blink,
(const char*)"blink",
100,
NULL,
1,
&blink_task_handle);
configASSERT( blink_task_handle );
// 创建点阵LED任务
xTaskCreate((TaskFunction_t)ledMatrix,
(const char *)"ledMatrix",
100,
NULL,
2,
&led_matrix_task_handle);
configASSERT(led_matrix_task_handle);
vTaskStartScheduler(); // 启动任务调度
}
void loop() {
}
void blink(void * para)
{
while (1)
{
digitalWrite(LED_BUILTIN, HIGH); // 点亮LED
vTaskDelay(500 * portTICK_PERIOD_MS); // 延时500ms
digitalWrite(LED_BUILTIN, LOW); // 关闭LED
vTaskDelay(500 * portTICK_PERIOD_MS); // 延时500ms
}
}
void ledMatrix(void *para)
{
while (1)
{
uint16_t length = sizeof(frames)/sizeof(frames[0]);
for ( uint16_t i = 0; i < length; i++)
{
matrix.loadFrame(&frames[0]);
vTaskDelay(33 * portTICK_PERIOD_MS);
}
}
}
```
### 2、说明
软件流程图:
使用FreeRTOS创建了“blink”和“led_matrix”两个任务,分别实现了LED指示灯每隔0.5秒闪烁一次;使用LED矩阵播放动画。动画的数据在.h文件中,在代码合集中给出。
因为动画的30fps,每帧约33.3ms,所以使用了`vTaskDelay(33 * portTICK_PERIOD_MS);`来设置固定的延时。
关于如何生成动画的文件:
1、使用ffmpeg,将视频中的每一帧输出为12*8大小的bmp文件;
```ba
ffmpeg -i video.m4s -vf scale=12:8 output_frame_%04d.bmp
```
2、将bmp文件转换为hex格式的文件,以下是python转换脚本
```python
from PIL import Image
import os
def convert_image_to_led_matrix(input_image):
# 打开图片并将其转换为灰度模式
image = Image.open(input_image).convert('1') # '1'模式表示黑白模式
# 缩放图片到12x8分辨率
image = image.resize((12, 8))
# 创建一个96位的数组来存储整个LED矩阵
led_matrix_full = [0] * 96
# 遍历每个像素,将其转换为1或0并存储到led_matrix_full中
for y in range(8):
for x in range(12):
pixel = image.getpixel((x, y))
if pixel == 0: # 黑色像素表示LED点亮
led_matrix_full[y * 12 + x] = 1
# 将96位数据分割成3个32位的uint32数组
led_matrix_32 = [0, 0, 0]
for i in range(3):
for bit in range(32):
led_matrix_32 |= (led_matrix_full[i * 32 + bit] 1000)
{
if (digitalRead(LED_BUILTIN) == HIGH)
led_switch.setState(true);
else
led_switch.setState(false);
last_update_time = millis();
}
}
/**
* @brief MQTT接收到数据后的回调函数
*
* @param topic
* @param payload
* @param length
*/
void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length)
{
char message[length];
memcpy(message, payload, length);
message[length] = '\0';
// 打印接收的数据
Serial.print("{dbg}New message on topic: ");
Serial.println(topic);
Serial.print("Data: ");
Serial.println((const char *)message);
if (strstr(message, "ON") != NULL) // 在字符串中查找另一个子字符串
{
// uint8_t dutyCyclt = 0;
// if (sscanf(message, "on#%d", &dutyCyclt) == 1) // 从一个字符串中读取数据,并按照指定的格式存储到变量中
// {
// /* code */
// }
digitalWrite(LED_BUILTIN, HIGH);
Serial.println("LED ON");
}
else if (strstr(message, "OFF") != NULL)
{
/* code */
digitalWrite(LED_BUILTIN, LOW);
Serial.println("LED OFF");
}
else
{
Serial.println("Unrecongnized meaasge");
}
memset(message, 0, length);
}
/**
* @brief 连接MQTT服务器后的回调函数
*
*/
void onMqttConnected()
{
Serial.println("Connected to the broker!");
mqtt.subscribe(topic_subscribe);
Serial.println("Subscribe to topic: ");
Serial.print(topic_subscribe);
// mqtt.publish(topic_subscribe, "Hello, MQTT!");
}
/**
* @brief 断开 MQTT broker后的回调函数
*
*/
void onMqttDisconneted()
{
Serial.println("Disconnected from the broker!");
}
/**
* @brief MQTT连接状态改变后的回调函数
*
* @param state
*/
void onMqttStateChanged(HAMqtt::ConnectionState state)
{
Serial.print("MQTT state changed to: ");
Serial.println(static_cast(state));
}
/**
* @brief 连接WiFI和MQTT服务器
*
*/
void wifiAndMqttInit()
{
// 检查WiFi模组
if (WiFi.status() == WL_NO_MODULE)
{
Serial.println("Communication with WiFi module failed!");
while (true)
;
}
String fv = WiFi.firmwareVersion();
if (fv < WIFI_FIRMWARE_LATEST_VERSION)
{
Serial.println("Please upgrade the firmware");
}
// 连接WiFi
WiFi.begin(ssid, password); // Connect to WPA/WPA2 network:
Serial.print("Attempting to connect to WPA SSID: ");
Serial.println(ssid);
while (WiFi.status() != WL_CONNECTED)
{
WiFi.begin(ssid, password);
delay(1000);
}
printWifiStatus();
// 配置MQTT
Serial.println("Starting connect to MQTT server");
mqtt.onMessage(onMqttMessage); // 当设备接收到 MQTT 消息时调用
mqtt.onConnected(onMqttConnected); // 每次获取与 MQTT 代理的连接时调用
mqtt.onDisconnected(onMqttDisconneted); // 每次与 MQTT 代理的连接丢失时调用
mqtt.onStateChanged(onMqttStateChanged); // 每次连接状态改变时调用
mqtt.setDataPrefix("homeassistant/sensor"); // 设置数据主题的前缀
// 连接MQTT服务器
if (!mqtt.begin(mqtt_server, mqtt_port, mqtt_user, mqtt_password))
{
Serial.print("Failed, rc = ");
Serial.print(mqtt.getState());
Serial.println(", try again in 5 seconds");
delay(5000);
}
}
/**
* @brief 打印WiFi信息
*
*/
void printWifiStatus()
{
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your board's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
```
### 2、说明
软件流程图:
关于Homeassistant的搭建,9月的技术直播中已经详细说明了,可以观看我的[录播回顾](https://www.bilibili.com/video/BV1884seCEAA)。
我的Homeassistant安装在Linux的docker容器中,EMQX没有使用docker,直接安装在Linux上。
**注意:在MQTT集成中,需要勾选“启用自动发现”,发现前缀配置为“homeassistant”,否则无法自动发现arduno开发板。**
调用`dawidchyrzynski/home-assistant-integration`库,实现HA联动功能。
```cpp
HASwitch led_switch("ledSwitch"); // 开关实体
```
创建了1个HA开关实体,开发板连接到MQTT Broker后,HA自动发现,会在主页面上显示一个LED灯开关,开关可以控制开发板上的LED灯。
```cpp
if (digitalRead(LED_BUILTIN) == HIGH)
led_switch.setState(true); // 反馈开关状态到MQTT Broker
else
led_switch.setState(false); // 反馈开关状态到MQTT Broker
```
需要调用`led_switch.setState()`来将开关状态反馈到HA,如果HA获取不到开关状态,会默认开关状态是关。
### 3、效果展示
关灯状态
开灯状态
## 五、扩展任务
硬件:Arduino UNO R4 WiFi、USB-C to A线、SHT40温湿度传感器扩展板、Qwiic缆线
软件:VSCode
### 1、代码
```cpp
#include
#include
#include
#include
#include
#include
// macro definitions
// make sure that we use the proper definition of NO_ERROR
#ifdef NO_ERROR
#undef NO_ERROR
#endif
#define NO_ERROR 0
// WiFi 设置
const char *ssid = "1708"; // WiFi名称
const char *password = "tsl199725?"; // WiFi密码
// MQTT 设置
const char *mqtt_server = "192.168.31.46"; // MQTT服务器IP
uint16_t mqtt_port = 1883; // MQTT端口
const char *mqtt_user = "arduino_uno_r4"; // 用户名
const char *mqtt_password = "arduino_uno_r4"; // 密码
const char *mqtt_client_id = "arduino"; // 客户id
const char *topic_subscribe = "homeassistant/sensor/arduino"; // 订阅主题
WiFiClient client;
HADevice device(mqtt_client_id); // 创建一个HA设备
HAMqtt mqtt(client, device);
HASensor sht4x_temp_sensor("sht4x_temp"); // 温度传感器实体
HASensor sht4x_humi_sensor("sht4x_humi"); // 湿度传感器实体
HASwitch led_switch("ledSwitch"); // 开关实体
SensirionI2cSht4x sensor; // SHT40
static char errorMessage[64];
static int16_t error;
uint32_t last_update_time;
void wifiAndMqttInit();
void printWifiStatus();
void setup()
{
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
Wire1.begin();
sensor.begin(Wire1, SHT40_I2C_ADDR_44);
sensor.softReset();
delay(10);
// 读取SHT40序列号
uint32_t serial_number;
error = sensor.serialNumber(serial_number);
if (NO_ERROR != error)
{
Serial.print("Error trying to execute serialNumber(): ");
errorToString(error, errorMessage, sizeof(errorMessage));
Serial.println(errorMessage);
return;
}
Serial.print("serialNumber: ");
Serial.println(serial_number);
wifiAndMqttInit(); // 初始化
device.setName("Arduino"); // 设备名称
device.setSoftwareVersion("1.0.0"); // 设备软件版本
led_switch.setIcon("mdi:led-outline");
led_switch.setName("arduino LED");
// led_switch.onCommand(onSwitchCommand);
sht4x_temp_sensor.setIcon("mdi:coolant-temperature");
sht4x_temp_sensor.setName("温度传感器");
sht4x_humi_sensor.setIcon("mdi:water-percent");
sht4x_humi_sensor.setName("湿度传感器");
}
void loop()
{
mqtt.loop();
if ((millis() - last_update_time) > 1000)
{
if (digitalRead(LED_BUILTIN) == HIGH)
led_switch.setState(true); // 反馈开关状态到MQTT Broker
else
led_switch.setState(false); // 反馈开关状态到MQTT Broker
// 读取SHT40温湿度数据
float a_temperature;
float a_humidity;
error = sensor.measureHighPrecision(a_temperature, a_humidity);
if (NO_ERROR != error)
{
Serial.print("Error trying to execute measureHighPrecision(): ");
errorToString(error, errorMessage, sizeof(errorMessage));
Serial.println(errorMessage);
return;
}
Serial.print("aTemperature: ");
Serial.println(a_temperature);
Serial.print("aHumidity: ");
Serial.println(a_humidity);
// 发送数据到MQTT Broker
String str = String(a_temperature, 2);
sht4x_temp_sensor.setValue(str.c_str());
sht4x_temp_sensor.setUnitOfMeasurement("℃");
str = String(a_humidity, 2);
sht4x_humi_sensor.setValue(str.c_str());
sht4x_humi_sensor.setUnitOfMeasurement("%");
last_update_time = millis();
}
}
/**
* @brief MQTT接收到数据后的回调函数
*
* @param topic
* @param payload
* @param length
*/
void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length)
{
char message[length];
memcpy(message, payload, length);
message[length] = '\0';
// 打印接收的数据
Serial.print("{dbg}New message on topic: ");
Serial.println(topic);
Serial.print("Data: ");
Serial.println((const char *)message);
if (strstr(message, "ON") != NULL) // strstr()在字符串中查找另一个子字符串
{
// uint8_t dutyCyclt = 0;
// if (sscanf(message, "on#%d", &dutyCyclt) == 1) // 从一个字符串中读取数据,并按照指定的格式存储到变量中
// {
// /* code */
// }
digitalWrite(LED_BUILTIN, HIGH);
Serial.println("LED ON");
}
else if (strstr(message, "OFF") != NULL)
{
/* code */
digitalWrite(LED_BUILTIN, LOW);
Serial.println("LED OFF");
}
else
{
Serial.println("Unrecongnized meaasge");
}
memset(message, 0, length);
}
/**
* @brief 连接MQTT服务器后的回调函数
*
*/
void onMqttConnected()
{
Serial.println("Connected to the broker!");
mqtt.subscribe(topic_subscribe);
Serial.println("Subscribe to topic: ");
Serial.print(topic_subscribe);
// mqtt.publish(topic_subscribe, "Hello, MQTT!");
}
/**
* @brief 断开 MQTT broker后的回调函数
*
*/
void onMqttDisconneted()
{
Serial.println("Disconnected from the broker!");
}
/**
* @brief MQTT连接状态改变后的回调函数
*
* @param state
*/
void onMqttStateChanged(HAMqtt::ConnectionState state)
{
Serial.print("MQTT state changed to: ");
Serial.println(static_cast(state));
}
/**
* @brief 连接WiFI和MQTT服务器
*
*/
void wifiAndMqttInit()
{
// 检查WiFi模组
if (WiFi.status() == WL_NO_MODULE)
{
Serial.println("Communication with WiFi module failed!");
while (true)
;
}
String fv = WiFi.firmwareVersion();
if (fv < WIFI_FIRMWARE_LATEST_VERSION)
{
Serial.println("Please upgrade the firmware");
}
// 连接WiFi
WiFi.begin(ssid, password); // Connect to WPA/WPA2 network:
Serial.print("Attempting to connect to WPA SSID: ");
Serial.println(ssid);
while (WiFi.status() != WL_CONNECTED)
{
WiFi.begin(ssid, password);
delay(1000);
}
printWifiStatus();
// 配置MQTT
Serial.println("Starting connect to MQTT server");
mqtt.onMessage(onMqttMessage); // 当设备接收到 MQTT 消息时调用
mqtt.onConnected(onMqttConnected); // 每次获取与 MQTT 代理的连接时调用
mqtt.onDisconnected(onMqttDisconneted); // 每次与 MQTT 代理的连接丢失时调用
mqtt.onStateChanged(onMqttStateChanged); // 每次连接状态改变时调用
mqtt.setDataPrefix("homeassistant/sensor"); // 设置数据主题的前缀
// 连接MQTT服务器
if (!mqtt.begin(mqtt_server, mqtt_port, mqtt_user, mqtt_password))
{
Serial.print("Failed, rc = ");
Serial.print(mqtt.getState());
Serial.println(", try again in 5 seconds");
delay(5000);
}
}
/**
* @brief 打印WiFi信息
*
*/
void printWifiStatus()
{
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your board's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
```
### 2、说明
软件流程图:
调用`sensirion/Sensirion I2C SHT4x`的库,来读取STH40的温湿度数据。
**注意:开发板有2个IIC端口,QWIC接口使用的是WIRE1,所以使用`Wire1.begin();`来使能IIC端口。**
```cpp
HASensor sht4x_temp_sensor("sht4x_temp"); // 温度传感器实体
HASensor sht4x_humi_sensor("sht4x_humi"); // 湿度传感器实体
```
创建了2个HA传感器实体,分别显示温度和湿度。
```cpp
sensor.measureHighPrecision(a_temperature, a_humidity);
```
使用`measureHighPrecision()`函数,来获取温度和湿度数据。
```cpp
sht4x_temp_sensor.setValue();
sht4x_humi_sensor.setValue();
```
通过`setValue()`函数,将温度和湿度数据发送到MQTT Broker。
### 3、效果展示
HA中显示的温湿度数据:
家里的小米温湿度传感器
## 六、源代码
## 七、展示视频
[大学堂视频链接](https://training.eeworld.com.cn/video/41270)
- 2024-09-12
-
加入了学习《直播回放: TI 德州仪器基于 Arm 的 AM62 处理器简介》,观看 德州仪器基于 Arm 的 AM62 处理器简介