- 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 处理器简介
- 2023-12-18
-
发表了主题帖:
【得捷电子Follow me第3期】使用MicroPython完成任务
## 第一部分 短视频
https://training.eeworld.com.cn/video/38755
## 第二部分 总结报告
### 任务1:使用MicroPython系统
#### 展示
#### 硬件
Seeed Studio XIAO ESP32C3
#### 软件
* [Esptool](https://github.com/espressif/esptool/releases/download/v4.6.2/esptool-v4.6.2-win64.zip)(点击链接下载)
* [micorpython固件](https://micropython.org/resources/firmware/ESP32_GENERIC_C3-20231005-v1.21.0.bin)(点击链接下载)
* [Thonny](https://github.com/thonny/thonny/releases/download/v4.1.4/thonny-py38-4.1.4-windows-portable.zip)(点击链接下载)
#### 操作
1. Esptool配置为环境变量
2. 使用Esptool下载固件
打开下载好的micropython固件目录,在地址栏中输入cmd,打开命令行窗口,输入命令:
```bash
esptool --chip esp32c3 --port COM13 --baud 921600 --before default_reset --after hard_reset --no-stub write_flash --flash_mode dio --flash_freq 80m 0x0 ESP32_GENERIC_C3-20231005-V1.21.0.bin
```
其中 `--port`是 ESP32C3 的在设备管理器中的串口号,根据自己的情况修改。
等待下载完成。
3. 与micropython的 REPL交互
打开 Thonny,选择`运行` -> `配置解释器`,解释器选择`MicroPython(ESP32)`,选择正确的端口,点击确定。
### 任务2:驱动扩展板上的OLED屏幕
#### 展示
#### 硬件
* Seeed Studio XIAO ESP32C3
* Expansion Board Base for XIAO
#### 软件
* Thonny
* [ampy](https://learn.adafruit.com/micropython-basics-load-files-and-run-code/install-ampy)(Adafruit MicroPython Tool)
(安装需要 Python 环境)
命令行输入:
```bash
pip install adafruit-ampy
```
等待安装完成即可。
#### 代码
```python
"""
任务2:驱动扩展板上的OLED屏幕
使用扩展板上的OLED屏幕显示文字和图形
显示图形参考代码 https://docs.micropython.org/en/v1.21.0/esp8266/tutorial/ssd1306.html
"""
import time
from machine import Pin, SoftI2C
import ssd1306 # 第三方库
import math
def show_text():
"""
显示文本
"""
oled.fill(0)
oled.text("DigiKey", 0, 7)
oled.text("X", 60, 7)
oled.text("EEworld", 72, 7)
oled.text("Follow me", 24, 21)
oled.text("Episode 3", 24, 35)
oled.text("XIAO ESP32C3", 12, 49)
oled.show()
def show_picture():
"""
显示图片
"""
oled.fill(0)
oled.fill_rect(0, 0, 32, 32, 1)
oled.fill_rect(2, 2, 28, 28, 0)
oled.vline(9, 8, 22, 1)
oled.vline(16, 2, 22, 1)
oled.vline(23, 8, 22, 1)
oled.fill_rect(26, 24, 2, 4, 1)
oled.text('MicroPython', 40, 0, 1)
oled.text('SSD1306', 40, 12, 1)
oled.text('OLED 128x64', 40, 24, 1)
oled.show()
i2c = SoftI2C(scl=Pin(7), sda=Pin(6)) # ESP32C3 引脚分配
oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
button = Pin(3, Pin.IN, Pin.PULL_UP) # 配置 3 脚为输入(扩展板上的按键),默认上拉
index = 0 # 页面编号
flash_flag = True # 刷新标志位
while True:
if (button.value() == False): # 当按键被按下,9 脚被拉低
print("button pressed")
index = (index + 1)%2 # 页面编号更新
flash_flag = True # 刷新标志位置位
if (flash_flag == True): # 判断是否需要刷新
if (index == 0):
show_text()
else:
show_picture()
flash_flag = False # 刷新标志位清0
time.sleep_ms(100) # 简单按键防抖
```
```python
import ssd1306 # 第三方库
```
[ssd1306](https://github.com/micropython/micropython-lib/blob/master/micropython/drivers/display/ssd1306/ssd1306.py)(点击链接打开) 是驱动 I2C 的库函数。
****
```python
oled.text("DigiKey", 0, 7)
```
表示在OLED屏幕上的`(0,7)`坐标位置显示`DigiKey`。OLED 屏幕的每个字符是 8 * 8 大小。
#### 操作
1. 上传ssd1306库文件
`ssd1306.py`保存在 `lib`文件夹下
在`lib`所在目录打开命令行,输入
```bash
ampy -p COM13 lib
```
表示将`lib`目录及其目录下的文件和文件夹上传到 ESP32C3 中。
2. 编辑 `boot.py` 文件
### 任务3:控制蜂鸣器播放音乐
#### 展示
(可以看上面视频)
#### 硬件
* Seeed Studio XIAO ESP32C3
* Expansion Board Base for XIAO
#### 软件
* Esptool
* Thonny
* ampy
#### 代码
```python
"""
任务3:控制蜂鸣器播放音乐
播放音乐参考代码 https://wiki.seeedstudio.com/cn/XIAO_ESP32C3_MicroPython/
显示图片参考代码 https://blog.martinfitzpatrick.com/displaying-images-oled-displays/
Bad Apple 简谱 http://www.sooopu.com/html/399/399104.html
"""
import time
from machine import SoftI2C, Pin, PWM
import ssd1306
import framebuf
i2c = SoftI2C(scl=Pin(7), sda=Pin(6))
oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
button = Pin(3, Pin.IN, Pin.PULL_UP) # 配置 3 脚为输入(扩展板上的按键),默认上拉
# 蜂鸣器初始化
buzzer_Pin = Pin(5, Pin.OUT)
buzzer = PWM(buzzer_Pin)
buzzer.duty(0)
def load_image():
"""
读取图片
"""
with open('pic/BadApple.pbm', 'rb') as f:
f.readline() # Magic number
f.readline() # Creator comment
f.readline() # Dimensions
data = bytearray(f.read())
return framebuf.FrameBuffer(data, 128, 64, framebuf.MONO_HLSB)
# 定义每个音符的频率
C4 = 262
D4 = 294
E4 = 330
F4 = 349
G4 = 392
A4 = 440
B4 = 494
C5 = 523
C5_= 554
D5 = 587
E5 = 659
F5 = 698
G5 = 784
A5 = 880
B5 = 988
C6 = 1047
D6 = 1175
E6 = 1319
F6 = 1397
# 乐谱
notes = [
D5, D5, D5, D5, D5, D5, D5,
D5, D5, D5, D5, D5,
D5, D5, D5, D5, D5, D5, D5,
D5, D5, D5, D5, D5,
D4, 0 , D4, C4, D4, D4, 0 , D4, C4, D4,
D4, 0 , D4, C4, D4, D4, D4, F4, G4, F4, G4,
D4, 0 , D4, C4, D4, D4, 0 , D4, C4, D4,
D4, 0 , D4, C4, D4, G4, F4, G4, F4, D4, F4,
D5, E5, F5, G5, A5, D6, C6,
A5, D5, A5, G5, F5, E5,
D5, E5, F5, G5, A5, G5, F5,
E5, D5, E5, F5, E5, D5, C5_,E5,
D5, E5, F5, G5, A5, D6, C6,
A5, D5, A5, G5, F5, E5,
D5, E5, F5, G5, A5, G5, F5,
E5, F5, G5, A5,
C6, D6, A5, G5, A5, G5, A5,
C6, D6, A5, G5, A5, G5, A5,
G5, F5, E5, C5, D5, C5, D5,
E5, F5, G5, A5, D5, A5, C6,
C6, D6, A5, G5, A5, G5, A5,
C6, D6, A5, G5, A5, G5, A5,
G5, F5, E5, C5, D5, C5, D5,
E5, F5, G5, A5, D5, A5, C6,
C6, D6, A5, G5, A5, G5, A5,
C6, D6, A5, G5, A5, G5, A5,
G5, F5, E5, C5, D5, C5, D5,
E5, F5, G5, A5, D5, A5, C6,
C6, D6, A5, G5, A5, G5, A5,
C6, D6, A5, G5, A5, D6, E6,
F6, E6, D6, C6, A5, G5, A5,
#G5, F5, E5, C5, D5, A5, C6 # 版本1
G5, F5, E5, C5, D5 # 版本2
]
# 歌曲每个音符的持续时间(以毫秒为单位)
# 当 SongSpeed = 1.0 时,四分音符为 200ms
durations = [
400, 400, 400, 100, 100, 100, 100,
400, 400, 400, 200, 200,
400, 400, 400, 100, 100, 100, 100,
400, 400, 400, 200, 200,
400, 100, 100, 100, 100, 400, 100, 100, 100, 100,
400, 100, 100, 100, 100, 200, 100, 100, 200, 100, 100,
400, 100, 100, 100, 100, 400, 100, 100, 100, 100,
400, 100, 100, 100, 100, 200, 100, 100, 200, 100, 100,
200, 200, 200, 200, 400, 200, 200,
400, 400, 200, 200, 200, 200,
200, 200, 200, 200, 400, 200, 200,
200, 200, 200, 200, 200, 200, 200, 200,
200, 200, 200, 200, 400, 200, 200,
400, 400, 200, 200, 200, 200,
200, 200, 200, 200, 400, 200, 200,
400, 400, 400, 400,
200, 200, 200, 200, 400, 200, 200,
200, 200, 200, 200, 400, 200, 200,
200, 200, 200, 200, 400, 200, 200,
200, 200, 200, 200, 400, 200, 200,
200, 200, 200, 200, 400, 200, 200,
200, 200, 200, 200, 400, 200, 200,
200, 200, 200, 200, 400, 200, 200,
200, 200, 200, 200, 400, 200, 200,
200, 200, 200, 200, 400, 200, 200,
200, 200, 200, 200, 400, 200, 200,
200, 200, 200, 200, 400, 200, 200,
200, 200, 200, 200, 400, 200, 200,
200, 200, 200, 200, 400, 200, 200,
200, 200, 200, 200, 400, 200, 200,
200, 200, 200, 200, 400, 200, 200,
#200, 200, 200, 200, 400, 200, 200 # 版本1
200, 200, 200, 200, 400 # 版本2
]
def play_song():
"""
播放音乐
"""
total_notes = len(notes)
for i in range(total_notes):
current_note = notes
wait = durations
if current_note != 0:
buzzer.duty(512) # Set duty cycle for sound
buzzer.freq(current_note) # Set frequency for sound
else:
buzzer.duty(0) # Turn off the sound
time.sleep_ms(wait)
buzzer.duty(0) # Turn off the sound
time.sleep_ms(wait/10)
def show_image():
"""
显示图片
"""
oled.invert(1)
oled.blit(load_image(), 0, 0)
oled.show()
show_image()
while True:
if button.value() == False:
play_song() # Play the song
time.sleep_ms(100)
```
*****
```python
# 蜂鸣器初始化
buzzer_Pin = Pin(5, Pin.OUT)
buzzer = PWM(buzzer_Pin)
buzzer.duty(0)
```
`buzzer_Pin = Pin(5, Pin.OUT)` 表示将Pin5设置为输出模式,`buzzer = PWM(buzzer_Pin)`表示 buzzer_Pin 作为PWM输出,`buzzer.duty(0)` 表示设置PWM的占空比为 0 。
****
```python
def show_image():
"""
显示图片
"""
oled.invert(1)
oled.blit(load_image(), 0, 0)
oled.show()
```
`oled.invert(1)`表示颜色反转,`oled.blit(load_image(), 0, 0)`用来显示图片,`load_image()` 是点阵图片数据,`0,0`分别表示绘制图片起始点的x,y坐标。
*****
notes数组是音乐的音符,durations数组是每个音符的持续时间时间。
#### 操作
上传库文件和代码与前面任务相同,不赘述。
### 任务4:连接WiFi网络
#### 展示
#### 硬件
* Seeed Studio XIAO ESP32C3
* Expansion Board Base for XIAO
#### 软件
* Esptool
* Thonny
* ampy
#### 代码
```python
"""
任务4:连接WiFi网络
将Seeed Studio XIAO ESP32C3连接到WiFi网络,并访问互联网信息
参考 https://stackoverflow.com/questions/59296623/micropython-get-correct-current-time
"""
from machine import Pin, SoftI2C, RTC
import ssd1306
from time import sleep
import time
import network
import ntptime
# ESP32 Pin assignment
i2c = SoftI2C(scl=Pin(7), sda=Pin(6))
oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
station = network.WLAN(network.STA_IF) # 创建 WLAN 网络接口对象,客户端模式
station.active(True) # 激活网络接口
# 网络配置
wifi_ssid = "1708" # SSID
wifi_password = "tsl199725?" # 密码
ntp_server = "ntp.aliyun.com" # NTP 服务器地址
time_zone = 8 # 时区,东八区
def connect_to_wifi():
"""
连接到wifi网络
"""
print("Scanning for WiFi networks, please wait...")
authmodes = ['Open', 'WEP', 'WPA-PSK' 'WPA2-PSK4', 'WPA/WPA2-PSK']
# 显示扫描到的网络
for ssid, bssid, channel, RSSI, authmode, hidden in station.scan():
print("* {:s}".format(ssid))
print(" - Channel: {}".format(channel))
print(" - RSSI: {}".format(RSSI))
print(" - BSSID: {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}".format(*bssid))
print()
# 连接网络
while not station.isconnected():
print("Connecting...")
station.connect(wifi_ssid, wifi_password)
time.sleep(1)
# 显示连接网络信息
print("Connected!")
print("My IP Address:", station.ifconfig()[0])
def sync_ntp():
"""
NTP 同步时间
"""
ntptime.host = ntp_server
retry_times = 10
try:
while retry_times > 0:
retry_times = retry_times - 1
if ntptime.settime():
break
except:
print("Error syncing time")
connect_to_wifi() # 连接到网络
sync_ntp() # 同步NTP 服务器时间
rtc = RTC()
last_second = 61 # 上一次屏幕显示的秒数
while True:
year, month, day, wday ,hour,minute,second,mocrosecond = rtc.datetime()
if second != last_second: # 秒变化时,才刷新屏幕
print(rtc.datetime()) # 串口打印
last_second = second
oled.fill(0)
oled.text(str(year), 24, 24) # 年
oled.text(".", 56, 24)
if month < 10: # 月
oled.text("0" + str(month), 64, 24)
else:
oled.text(str(month), 64, 24)
oled.text(".", 80, 24)
if day < 10: # 日
oled.text("0" + str(day), 88, 24)
else:
oled.text(str(day), 88, 24)
hour = (hour + time_zone)%24 # 转为东八区时间
if hour < 10: # 时
oled.text("0" + str(hour), 32, 32)
else:
oled.text(str(hour), 32, 32)
oled.text(":", 48, 32)
if minute < 10: # 分
oled.text("0" + str(minute), 56, 32)
else:
oled.text(str(minute), 56, 32)
oled.text(":", 72, 32)
if second < 10: #秒
oled.text("0" + str(second), 80, 32)
else:
oled.text(str(second), 80, 32)
oled.show()
time.sleep_ms(50)
```
*****
`ntptime.host = ntp_server`设置 NTP 服务器地址
`ntptime.settime()`从NTP 服务器获取时间
需要注意,NTP 获取的时间是**UTC +0** 的时间,北京时间是**UCT+8**,所以代码中有`hour = (hour + time_zone)%24`
#### 操作
上传库文件和代码与前面任务相同,不赘述。
### 任务5:使用外部传感器
#### 展示
使用传感器感应环境的温湿度和环境光强度。
小米温湿度传感器
温湿度差别有点大呀 :sweat_smile:
#### 硬件
* Seeed Studio XIAO ESP32C3
* Expansion Board Base for XIAO
* [Grove - AHT20](https://www.digikey.cn/zh/products/detail/seeed-technology-co-ltd/101990644/11681294) (温湿度传感器)
* [Grove - Light Sensor](https://www.digikey.cn/zh/products/detail/seeed-technology-co-ltd/101020132/6558656) (环境光传感器)
Grove-AHT20 使用了一颗来自奥松电子(ASAIR)的数字式温度传感器AHT20,湿度测量范围0\~100%RH,分辨率0.024%RH;温度测量范围-40\~125℃,分辨率0.01℃。使用I2C协议传输数据。
Grove-Light Sensor 使用了一颗光敏电阻和来自Ti的运放LM358来构建电路。输出信号为模拟值,光线越亮,数值越大。
#### 软件
* [Esptool](https://github.com/espressif/esptool/releases/download/v4.6.2/esptool-v4.6.2-win64.zip)(点击链接下载)
* [Thonny](https://github.com/thonny/thonny/releases/download/v4.1.4/thonny-py38-4.1.4-windows-portable.zip)(点击链接下载)
#### 代码
```python
"""
任务5:使用外部传感器
连接环境光传感器或温湿度传感器,获取传感器的数值,并转换成真实的物理量
ssd1306 驱动库: https://github.com/micropython/micropython-lib/blob/master/micropython/drivers/display/ssd1306/ssd1306.py
AHT20 驱动库: https://github.com/joepweijers/DFRobot_AHT20_MicroPython
"""
import sys
import time
from machine import SoftI2C, Pin, ADC
import ssd1306
import DFRobot_AHT20 # AHT20
# SSD1306 屏幕驱动
i2c = SoftI2C(scl=Pin(7), sda=Pin(6)) # oled屏幕连接I2C引脚
oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
# 环境光传感器
light_pin = Pin(2) # 环境光传感器输出脚连接 GPIO2
# AHT20 驱动
light_sensor = DFRobot_AHT20.DFRobot_AHT20(i2c)
def read_AHT20_sensor(sensor):
"""
读取 AHT20 传感器数据
"""
try:
if sensor.begin() != True:
print("Failed to start AHT20 sensor")
sys.exit(1)
sensor.start_measurement_ready()
temperature = sensor.get_temperature_C()
humidity = sensor.get_humidity_RH()
print(f"Temperature: {temperature:3.1f} ℃")
print(f"Humidity: {humidity:3.1f} %RH")
return temperature, humidity
except:
print("Failed to read AHT20 sensor.")
def read_light_sensor(pin: Pin) -> int:
"""
读取环境光传感器数据
"""
adc = ADC(Pin(2)).read_u16() # 创建 ADC 对象,通过ADC传感器输出的电压值转为数值
print(f"Light: {adc}")
return adc # 返回 ADC 值
temperature = 0
humidity = 0
light = 0
oled.text("Temp :", 12, 10)
oled.text("Humid:", 12, 28)
oled.text("Light:", 12, 48)
oled.show()
start_time = time.ticks_us()
while True:
now_time = time.ticks_us()
if time.ticks_diff(now_time, start_time) > 200:
temperature, humidity =read_AHT20_sensor(light_sensor)
light = read_light_sensor(light_pin)
light = light / 65535 *100 # 转化为百分比值
# 清空上次显示的值
oled.fill_rect(60, 10, 91, 55, 0)
# 显示温度
oled.text(str(temperature)[:4], 60, 10)
oled.text("C", 92, 10)
# 显示湿度
oled.text(str(humidity)[:4], 60, 28)
oled.text("%RH", 92, 28)
# 显示环境光强度,
if (light != 100): # 当强度等于 100% 时,显示3个字符
oled.text(str(light)[:4], 60, 48)
else: # 当强度不等于 100% 时,显示4个字符
oled.text(str(light)[:3], 60, 48)
oled.text("%", 92, 48)
oled.show()
start = now_time
else:
time.sleep_ms(20)
```
*****
```python
# 环境光传感器
light_pin = Pin(2) # 环境光传感器输出脚连接 GPIO2
```
环境光传感器的输出脚连接到扩展版的J5上,对应ESP32C3的 `GPIO2`
*****
```python
# AHT20 驱动
light_sensor = DFRobot_AHT20.DFRobot_AHT20(i2c)
```
AHT20 使用I2C协议传输数据,D4(GPIO6)脚作为SDA,D5(GPIO7)脚作为SCL,与OLED屏幕使用相同的引脚。
*****
```python
light = read_light_sensor(light_pin)
light = light / 65535 *100 # 转化为百分比值
```
ESP32-C3 中的ADC是12位的,最大值2^12^ - 1,即 65535。光强最强时,传感器输出到ADC的转换值为 65535,`light / 65535 *100`把0 \~ 65535的范围转变为0 \~ 100%范围。
*****
```python
now_time = time.ticks_us()
if time.ticks_diff(now_time, start_time) > 200:
else:
time.sleep_ms(20)
```
`time.ticks_us()` 可以理解为获取当前的系统时间(毫秒单位),`time.ticks_diff(now_time, start_time)`用来计算 now_time 和 start_time 直接是时间差。
这段代码表示每隔200ms读取并显示温湿度和环境光数据。
#### 操作
上传库文件和代码与前面任务相同,不赘述。
### 任务6:寻找WiFi发射源的位置
#### 展示
#### 硬件
* Seeed Studio XIAO ESP32C3
* Expansion Board Base for XIAO
#### 软件
* Esptool
* Thonny
* ampy
#### 代码
```python
"""
任务6:寻找WiFi发射源的位置
实时测量WiFi信号强度,同时移动开发板位置,找到WiFi发射源(例如路由器)的位置
参考 https://wiki.seeedstudio.com/XIAO_ESP32C3_MicroPython/#final-wi-fi-signal-strength-tracker
"""
import network
import time
from time import sleep
import machine
from machine import Pin, SoftI2C
import ssd1306
import math
# SSD1306 屏幕驱动
i2c = SoftI2C(scl=Pin(7), sda=Pin(6)) # oled屏幕连接I2C引脚
oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
# 网络配置
wifi_ssid = "1708"
wifi_password = "tsl199725?"
machine.freq(160000000) # 设置 CPU频率为 160 MHz
oled.text("Satrting up...", 0, 0)
oled.show()
station = network.WLAN(network.STA_IF) # 创建 WLAN
station.active(True) # 激活网络接口
station.connect(wifi_ssid, wifi_password) # 连接到wifi网络
time.sleep(1)
while not station.isconnected():
time.sleep(1)
oled.fill(0)
oled.text("Connecting to", 0, 0)
oled.text(wifi_ssid, 0, 20)
oled.show()
time.sleep(2)
oled.fill(0)
ip_address = station.ifconfig()[0] # 获取 IP 地址
oled.text("Connected! ", 0, 0)
oled.text("IP Address:", 0, 20)
oled.text(ip_address, 0, 40)
oled.show()
time.sleep(1)
x_pos = [12, 38, 64, 90] # 对应信号强度方格的x坐标位置
statuses = ["poor", "normal", "good", "excellent"]
def calculate_block_count(rssi):
"""
根据RSSI值确定块数
"""
if -80
- 2023-12-17
-
加入了学习《 【得捷电子Follow me第3期】使用MicroPython完成任务》,观看 【得捷电子Follow me第3期】使用MicroPython完成任务
-
加入了学习《【得捷电子Follow me第2期】使用CircuitPython完成任务》,观看 【得捷电子Follow me第2期】使用CircuitPython完成任务
-
上传了资料:
【得捷Follow me第3期】代码合集