一、任务视频
【得捷电子Follow me】提交视频-Follow me 第二季第2期任务提交-EEWORLD大学堂
二、任务实现详情
一、任务介绍
很高兴能参加此次Follow me第二季第二期活动,通过这次活动学到了Arduino的一些知识,以及Home assistant相关的知识,对智能家居有了更进一步的理解。本次完成了活动所要求的任务,包括LED闪烁、串口输出、LED矩阵、DAC、ADC、WIFI连接HA通过MQTT传输数据、LTR329获取光强数据、SHT40传感器数据获取等。所用到的器件有Arduino UNO R4 WIFI主板一块,LTR329和SHT40传感器小板各一块和4P连接线2根。
这款开发板主控芯片升级为了瑞萨的32位Arm Cortex-M4内核的RA4M1微控制器,拥有256KB Flash和32KB SRAM,时钟频率来到了48MHz,接口可以与之前的R3进行兼容,但是这次与电脑的接口换上了更为先进的TYPE-C接口。这块板子还有一个12*8的LED矩阵,可以用来显示数字、字母、简单的图形甚至汉字,有点像microbit,可玩性更强了。
软件上主要使用的是Arduino IDE进行开发的。但为了完成进阶任务,还需要安装MQTT服务器和Home assistant平台。其中HA平台在windows上安装比较麻烦,花费的时间比较长。
二、任务实现
2.1 入门任务
作为第一个任务,在使用之前需要先安装该开发板的库。在开发板管理器中搜索然后安装即可。
2.1.1 Blink功能
开发板上包含多个LED,分别是串口收发的2个LED、电源LED,以及1个用户可用的LED。为了实现Blink 功能,点击菜单栏的“文件 -> 示例 -> 01.Basics -> Blink”,可以通过这个示例实现了对LED的闪烁控制。
可以修改loop函数中delay函数的参数,改变闪烁时间,例如改成500ms,然后上传代码,可以看到LED闪烁速度的变化。
流程图为:
2.1.2 串口输出Hello EEWorld!
串口输出需要对serial进行初始化,然后调用Serial.println函数进行输出。代码如下:
输出效果如下图:
流程图为:
2.2 基础任务
2.2.1.驱动12x8点阵LED
上图是LED点阵的原理图,总共96个LED。在程序中对应的是3个32位的整型数,LED点阵的顺序是从左至右,从上至下,对应的位为1时点亮LED。
Arduino在程序上有很好的易用性,不用过分的去纠结底层的原理,直接拿来用就行。在该任务中通过其示例代码,可以看到其使用了Arduino_LED_Matrix库,通过matrix.loadFrame() 函数调用对应的数组就可以实现相应的显示。
显示效果是4个LED灯循环转动。
流程图为:
2.2.2 用DAC生成正弦波
通过主控芯片内置的DAC输出正弦信号,在该任务中使用A0通道进行输出。其输出函数也很简单,使用函数wave.sine(freq)通过设置不同参数,改变正弦波的频率,输出波形如下图,可以看到黄色波形为正弦波,输出频率为10Hz。
流程图为;
代码如下:
#include "analogWave.h" analogWave wave(DAC);
int freq = 10; // in hertz, change accordingly
void setup() {
Serial.begin(115200);
wave.sine(freq); // Generate a sine wave with the initial frequency
}
void loop() {
}
2.2.3 用OPAMP放大DAC信号
该开发板的主控芯片内置了运算放大器,在这里搭建了一个同相比例放大电路,放大倍数为2倍。下面的程序使用A0输出正弦信号然后输入到A1,A1作为运放的正相输入端,A2是反相输入端,A3为输出端。接线如下图所示
流程图为:
代码如下
#include <OPAMP.h>
int wave_sin[256] = {
0x80,0x83,0x85,0x88,0x8A,0x8D,0x8F,0x92,
0x94,0x97,0x99,0x9B,0x9E,0xA0,0xA3,0xA5,
0xA7,0xAA,0xAC,0xAE,0xB1,0xB3,0xB5,0xB7,
0xB9,0xBB,0xBD,0xBF,0xC1,0xC3,0xC5,0xC7,
0xC9,0xCB,0xCC,0xCE,0xD0,0xD1,0xD3,0xD4,
0xD6,0xD7,0xD8,0xDA,0xDB,0xDC,0xDD,0xDE,
0xDF,0xE0,0xE1,0xE2,0xE3,0xE3,0xE4,0xE4,
0xE5,0xE5,0xE6,0xE6,0xE7,0xE7,0xE7,0xE7,
0xE7,0xE7,0xE7,0xE7,0xE6,0xE6,0xE5,0xE5,
0xE4,0xE4,0xE3,0xE3,0xE2,0xE1,0xE0,0xDF,
0xDE,0xDD,0xDC,0xDB,0xDA,0xD8,0xD7,0xD6,
0xD4,0xD3,0xD1,0xD0,0xCE,0xCC,0xCB,0xC9,
0xC7,0xC5,0xC3,0xC1,0xBF,0xBD,0xBB,0xB9,
0xB7,0xB5,0xB3,0xB1,0xAE,0xAC,0xAA,0xA7,
0xA5,0xA3,0xA0,0x9E,0x9B,0x99,0x97,0x94,
0x92,0x8F,0x8D,0x8A,0x88,0x85,0x83,0x80,
0x7D,0x7B,0x78,0x76,0x73,0x71,0x6E,0x6C,
0x69,0x67,0x65,0x62,0x60,0x5D,0x5B,0x59,
0x56,0x54,0x52,0x4F,0x4D,0x4B,0x49,0x47,
0x45,0x43,0x41,0x3F,0x3D,0x3B,0x39,0x37,
0x35,0x34,0x32,0x30,0x2E,0x2D,0x2C,0x2A,
0x29,0x28,0x26,0x25,0x24,0x23,0x22,0x21,
0x20,0x1F,0x1E,0x1D,0x1D,0x1C,0x1C,0x1B,
0x1B,0x1A,0x1A,0x1A,0x19,0x19,0x19,0x19,
0x19,0x19,0x19,0x19,0x1A,0x1A,0x1A,0x1B,
0x1B,0x1C,0x1C,0x1D,0x1D,0x1E,0x1F,0x20,
0x21,0x22,0x23,0x24,0x25,0x26,0x28,0x29,
0x2A,0x2C,0x2D,0x2F,0x30,0x32,0x34,0x35,
0x37,0x39,0x3B,0x3D,0x3F,0x41,0x43,0x45,
0x47,0x49,0x4B,0x4D,0x4F,0x52,0x54,0x56,
0x59,0x5B,0x5D,0x60,0x62,0x65,0x67,0x69,
0x6C,0x6E,0x71,0x73,0x76,0x78,0x7B,0x7D
};
int i = 0;
void setup()
{
OPAMP.begin(OPAMP_SPEED_HIGHSPEED);
analogWriteResolution(8); // set the analog output resolution to 12 bit (4096 levels)
}
void loop()
{
analogWrite(DAC, wave_sin[i]/2); // write the selected waveform on DAC0
i++;
if (i == 256) // Reset the counter to repeat the wave
i = 0;
delayMicroseconds(10); // Hold the sample value for the sample time
}
输出信号如下图所示,绿色为输入信号,黄色为输出信号,输出信号是输入信号的2倍:
2.2.4 用ADC采集并且打印数据到串口等其他接口可上传到上位机显示曲线
在这个示例里,使用ADC的A0端口,对输入的10Hz的正弦波进行采集。接线图如下所示:
流程图为:
代码如下:
const int analogInPin = A0; // Analog input pin that the potentiometer is attached to
int sensorValue = 0; // value read from the pot
int outputValue = 0; // value output to the PWM (analog out)
void setup() {
// initialize serial communications at 9600 bps:
Serial.begin(9600);
}
void loop() {
// read the analog in value:
sensorValue = analogRead(analogInPin);
// map it to the range of the analog out:
outputValue = map(sensorValue, 0, 1023, 0, 255);
// change the analog out value:
Serial.println(outputValue);
delay(2);
}
2.3 进阶任务
通过Wi-Fi,利用MQTT协议接入到开源的智能家居平台HA(HomeAssistant)
这个任务的难度最大了,需要安装多个软件,包括MQTT服务器和HA平台。因为没有使用其它的平台,因此全部安装到windows电脑上了。
首先搭建MQTT的服务器,参考了下面的帖子才找到了能够在windows下使用的MQTT服务器,这里使用的是emqx搭建mqtt服务器。
Windows系统下本地MQTT服务器搭建(保姆级教程)_mqtt windows-CSDN博客
接着安装HA平台,我这里使用的是虚拟机Virtualbox,然后在home assistant的官网下载对应的镜像。启动服务之后,可以在网页上看到对HA的控制面板。
当HA和MQTT的服务器都创建好之后,就可以向HA中添加设备了,这里是参考了 【Follow me第二季第2期】+通过Wi-Fi,利用MQTT协议接入到开源的智能家居平台HA - DigiKey得捷技术专区 - 电子工程世界-论坛 (eeworld.com.cn) 这篇帖子,通过向topic“homeassistant/sensor/sensorBedroomH/config”发送信息添加设备:
{
"device_class":"humidity",
"name":"Humidity",
"state_topic":"homeassistant/sensor/sensorBedroom/state",
"unit_of_measurement":"%",
"value_template":"{{ value_json.humidity}}",
"unique_id":"hum01ae",
"device":{
"identifiers":[
"bedroom01ae"
],
"name":"Bedroom"
}
}
这时就可以将新建的设备添加到仪表盘了。从上面的信息可以看出其创建了一个"Humidity"设备,可以通过向"homeassistant/sensor/sensorBedroom/state"这个topic发送json为humidity的数据,这样在仪表盘上就能显示数据了发送的数据示例如下:
显示效果如下:
因此在arduino上需要实现mqtt消息发送的功能。
流程图:
代码如下
#include <ArduinoMqttClient.h>
#include <WiFiS3.h>
#include <WiFiClient.h>
#include <Arduino_JSON.h>
char ssid[] = "ChinaNet-JvAb";
char pass[] = "s3fackxx";
const char broker[] = "192.168.1.8";
int port = 1883;
const char state_topic[] = "homeassistant/sensor/sensorBedroom/state";
WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);
JSONVar dataObj;
const long interval = 1000;
unsigned long previousMillis = 0;
int count = 0;
void setup() {
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.println("HA com test");
Serial.println("connect to WPA SSID: ");
Serial.println(ssid);
// attempt to connect to WiFi network:
while (WiFi.begin(ssid, pass) != WL_CONNECTED) {
Serial.print("......");
delay(5000);
}
Serial.print("connect wifi succeed!");
Serial.println();
Serial.println(WiFi.localIP());
// You can provide a unique client ID, if not set the library uses Arduino-millis()
// Each client must have a unique client ID
mqttClient.setId("clientId");
// You can provide a username and password for authentication
mqttClient.setUsernamePassword("li", "l1111111");
Serial.print("connect to the MQTT broker: ");
Serial.println(broker);
if (!mqttClient.connect(broker, port)) {
Serial.print("MQTT connection failed! Error code = ");
Serial.println(mqttClient.connectError());
while (1);
}
Serial.println("connected to the MQTT succeed!");
Serial.println();
}
void loop() {
// put your main code here, to run repeatedly:
mqttClient.poll();
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
// save the last time a message was sent
previousMillis = currentMillis;
dataObj["humidity"] = count;
String jsonString = JSON.stringify(dataObj);
// send message, the Print interface can be used to set the message contents
mqttClient.beginMessage(state_topic);
mqttClient.print(jsonString);
mqttClient.endMessage();
count++;
if(count>=90)
count = 0;
delay(1000);
}
}
2.4 扩展任务
2.4.1 通过外部LTR-329 环境光传感器,上传光照度到HA,通过HA面板显示数据
使用开发板上的I2C接口连接传感器小板。
首先获取LTR-329的光传感器数据,流程图
代码如下
#include "Adafruit_LTR329_LTR303.h"
Adafruit_LTR329 ltr = Adafruit_LTR329();
void setup() {
Serial.begin(115200);
Serial.println("Adafruit LTR-329 advanced test");
if ( ! ltr.begin(&Wire1) ) {
Serial.println("Couldn't find LTR sensor!");
while (1) delay(10);
}
Serial.println("Found LTR sensor!");
ltr.setGain(LTR3XX_GAIN_2);
ltr.setIntegrationTime(LTR3XX_INTEGTIME_100);
ltr.setMeasurementRate(LTR3XX_MEASRATE_200);
}
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);
}
}
delay(100);
}
然后在HA平台上新增一个LTR329的传感器。同样通过向topic:homeassistant/sensor/sensorLTR329/config发送如下消息,新建一个LTR329传感器。
{
"name":"LTR329",
"state_topic":"homeassistant/sensor/sensorLTR329/state",
"unit_of_measurement":"Lux",
"value_template":"{{ value_json.value}}",
"unique_id":"ltr01ae",
"device":{
"identifiers":[
"bedroom01ae"
],
"name":"LTR329-1"
}
}
然后arduino通过mqtt协议向相应的topic发送消息即可,
流程图为:
如下代码实现数据的上传:
#include <ArduinoMqttClient.h>
#include <WiFiS3.h>
#include <WiFiClient.h>
#include <Arduino_JSON.h>
#include "Adafruit_LTR329_LTR303.h"
Adafruit_LTR329 ltr = Adafruit_LTR329();
char ssid[] = "ChinaNet-JvAb";
char pass[] = "s3fackxx";
const char broker[] = "192.168.1.8";
int port = 1883;
const char state_topic[] = "homeassistant/sensor/sensorLTR329/state";
WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);
JSONVar dataObj;
const long interval = 1000;
unsigned long previousMillis = 0;
int count = 0;
void setup() {
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.println("HA com test");
Serial.println("connect to WPA SSID: ");
Serial.println(ssid);
// attempt to connect to WiFi network:
while (WiFi.begin(ssid, pass) != WL_CONNECTED) {
Serial.print("......");
delay(5000);
}
Serial.print("connect wifi succeed!");
Serial.println();
Serial.println(WiFi.localIP());
// You can provide a unique client ID, if not set the library uses Arduino-millis()
// Each client must have a unique client ID
mqttClient.setId("clientId");
// You can provide a username and password for authentication
mqttClient.setUsernamePassword("li", "l1111111");
Serial.print("connect to the MQTT broker: ");
Serial.println(broker);
if (!mqttClient.connect(broker, port)) {
Serial.print("MQTT connection failed! Error code = ");
Serial.println(mqttClient.connectError());
while (1);
}
Serial.println("connected to the MQTT succeed!");
Serial.println();
if ( ! ltr.begin(&Wire1) ) {
Serial.println("Couldn't find LTR sensor!");
while (1) delay(10);
}
Serial.println("Found LTR sensor!");
ltr.setGain(LTR3XX_GAIN_2);
ltr.setIntegrationTime(LTR3XX_INTEGTIME_100);
ltr.setMeasurementRate(LTR3XX_MEASRATE_200);
}
void loop() {
// put your main code here, to run repeatedly:
bool valid;
uint16_t visible_plus_ir, infrared;
if (ltr.newDataAvailable()) {
valid = ltr.readBothChannels(visible_plus_ir, infrared);
}
mqttClient.poll();
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
// save the last time a message was sent
previousMillis = currentMillis;
dataObj["value"] = visible_plus_ir;
String jsonString = JSON.stringify(dataObj);
// send message, the Print interface can be used to set the message contents
mqttClient.beginMessage(state_topic);
mqttClient.print(jsonString);
mqttClient.endMessage();
delay(200);
}
}
2.4.2 通过外部SHT40温湿度传感器,上传温湿度到HA,通过HA面板显示数据
因为2个传感器小板都是使用的I2C接口,因此可以进行串行的连接,接线图如下:
温湿度数据包含了2项内容,因此在HA平台上新增了2个设备,一个用于显示温度,一个用于显示湿度。
topic:homeassistant/sensor/sensorSHT40-TEMP/config
{
"name":"sht40",
"state_topic":"homeassistant/sensor/sensorSHT40-TEMP/state",
"unit_of_measurement":"C",
"value_template":"{{ value_json.value}}",
"unique_id":"sht40ae",
"device":{
"identifiers":[
"sht40ae-temp"
],
"name":"sht40-t"
}
}
topic:homeassistant/sensor/sensorSHT40-HUM/config
{
"name":"sht40",
"state_topic":"homeassistant/sensor/sensorSHT40-HUM/state",
"unit_of_measurement":"%",
"value_template":"{{ value_json.value}}",
"unique_id":"sht40ae",
"device":{
"identifiers":[
"sht40ae-hum"
],
"name":"sht40-h"
}
}
流程图为:
代码实现如下:
#include <ArduinoMqttClient.h>
#include <WiFiS3.h>
#include <WiFiClient.h>
#include <Arduino_JSON.h>
#include "Adafruit_LTR329_LTR303.h"
#include "Adafruit_SHT4x.h"
Adafruit_LTR329 ltr = Adafruit_LTR329();
Adafruit_SHT4x sht4 = Adafruit_SHT4x();
char ssid[] = "ChinaNet-JvAb";
char pass[] = "s3fackxx";
const char broker[] = "192.168.1.8";
int port = 1883;
const char state_topic[] = "homeassistant/sensor/sensorLTR329/state";
const char state_topic_temp[] = "homeassistant/sensor/sensorSHT40-TEMP/state";
const char state_topic_hum[] = "homeassistant/sensor/sensorSHT40-HUM/state";
WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);
JSONVar dataObj;
const long interval = 200;
unsigned long previousMillis = 0;
int count = 0;
void setup() {
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.println("HA com test");
Serial.println("connect to WPA SSID: ");
Serial.println(ssid);
// attempt to connect to WiFi network:
while (WiFi.begin(ssid, pass) != WL_CONNECTED) {
Serial.print("......");
delay(5000);
}
Serial.print("connect wifi succeed!");
Serial.println();
Serial.println(WiFi.localIP());
// You can provide a unique client ID, if not set the library uses Arduino-millis()
// Each client must have a unique client ID
mqttClient.setId("clientId");
// You can provide a username and password for authentication
mqttClient.setUsernamePassword("li", "l1111111");
Serial.print("connect to the MQTT broker: ");
Serial.println(broker);
if (!mqttClient.connect(broker, port)) {
Serial.print("MQTT connection failed! Error code = ");
Serial.println(mqttClient.connectError());
while (1);
}
Serial.println("connected to the MQTT succeed!");
Serial.println();
if ( ! ltr.begin(&Wire1) ) {
Serial.println("Couldn't find LTR sensor!");
while (1) delay(10);
}
Serial.println("Found LTR sensor!");
ltr.setGain(LTR3XX_GAIN_2);
ltr.setIntegrationTime(LTR3XX_INTEGTIME_100);
ltr.setMeasurementRate(LTR3XX_MEASRATE_200);
if(sht4.begin(&Wire1) == false)
{
Serial.println("SHT40 not detected. Please check wiring. Freezing.");
while (1)
;
}
sht4.setPrecision(SHT4X_HIGH_PRECISION);
sht4.setHeater(SHT4X_NO_HEATER);
Serial.println("SHT40 acknowledged.");
}
void loop() {
// put your main code here, to run repeatedly:
bool valid;
uint16_t visible_plus_ir, infrared;
sensors_event_t humidity, temp;
sht4.getEvent(&humidity, &temp);
if (ltr.newDataAvailable()) {
valid = ltr.readBothChannels(visible_plus_ir, infrared);
}
mqttClient.poll();
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
// save the last time a message was sent
previousMillis = currentMillis;
dataObj["value"] = visible_plus_ir;
String jsonString = JSON.stringify(dataObj);
// send message, the Print interface can be used to set the message contents
mqttClient.beginMessage(state_topic);
mqttClient.print(jsonString);
mqttClient.endMessage();
dataObj["value"] = temp.temperature;
Serial.println("Temp *C = " + String( temp.temperature));
jsonString = JSON.stringify(dataObj);
mqttClient.beginMessage(state_topic_temp);
mqttClient.print(jsonString);
mqttClient.endMessage();
dataObj["value"] = humidity.relative_humidity;
Serial.println("Hum. % = " + String(humidity.relative_humidity));
jsonString = JSON.stringify(dataObj);
mqttClient.beginMessage(state_topic_hum);
mqttClient.print(jsonString);
mqttClient.endMessage();
delay(200);
}
}
上传的效果如下图:
三、总结
个人感觉这次的任务比以往更有难度,尤其是开发环境的搭建上,比如HA平台的安装,MQTT服务器的安装等。HA平台的使用上也比较有难度。还有就是这次任务可能需要一些额外的设备,比如示波器、额外的排线。不过有难度的任务才能学到更多的知识,通过这次任务认识了HA平台,对智能家居的开发有了新的任务。最后感谢EEWORLD和得捷对这次活动的支持。
三、可编译下载的代码
Follow me 第二季第2期任务代码-嵌入式开发相关资料下载-EEWORLD下载中心
本帖最后由 manhuami2007 于 2024-10-31 11:53 编辑