[作品提交] 【Follow me第二季第2期】+ 任务总结提交

manhuami2007   2024-10-6 22:07 楼主

一、任务视频

【得捷电子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根。

image.png  

这款开发板主控芯片升级为了瑞萨的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 入门任务

 

作为第一个任务,在使用之前需要先安装该开发板的库。在开发板管理器中搜索然后安装即可。

image-20241006215237-1.png  

2.1.1 Blink功能

开发板上包含多个LED,分别是串口收发的2个LED、电源LED,以及1个用户可用的LED。为了实现Blink 功能,点击菜单栏的“文件 -> 示例 -> 01.Basics -> Blink”,可以通过这个示例实现了对LED的闪烁控制。

image-20241006215237-2.png  

可以修改loop函数中delay函数的参数,改变闪烁时间,例如改成500ms,然后上传代码,可以看到LED闪烁速度的变化。

image-20241006215237-3.png  

流程图为:

image.png  

2.1.2 串口输出Hello EEWorld!

串口输出需要对serial进行初始化,然后调用Serial.println函数进行输出。代码如下:

image-20241006215237-4.png  

输出效果如下图:

image-20241006215237-5.png  

流程图为:

image.png  

2.2 基础任务

2.2.1.驱动12x8点阵LED

image-20241006215435-6.png  

上图是LED点阵的原理图,总共96个LED。在程序中对应的是3个32位的整型数,LED点阵的顺序是从左至右,从上至下,对应的位为1时点亮LED。

Arduino在程序上有很好的易用性,不用过分的去纠结底层的原理,直接拿来用就行。在该任务中通过其示例代码,可以看到其使用了Arduino_LED_Matrix库,通过matrix.loadFrame() 函数调用对应的数组就可以实现相应的显示。

image.png  

显示效果是4个LED灯循环转动。

   image.png  

流程图为:

image.png  

2.2.2 用DAC生成正弦波

通过主控芯片内置的DAC输出正弦信号,在该任务中使用A0通道进行输出。其输出函数也很简单,使用函数wave.sine(freq)通过设置不同参数,改变正弦波的频率,输出波形如下图,可以看到黄色波形为正弦波,输出频率为10Hz。

image.png  

流程图为;

image.png  

代码如下:

#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为输出端。接线如下图所示

image-20241006215721-8.jpeg  

流程图为:

image.png  

代码如下

#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倍:

image.png  

  

2.2.4 用ADC采集并且打印数据到串口等其他接口可上传到上位机显示曲线

在这个示例里,使用ADC的A0端口,对输入的10Hz的正弦波进行采集。接线图如下所示:

image-20241006215826-10.jpeg  可以看到其在串口绘图器中显示曲线如下图所示:

image-20241006215826-11.png  

流程图为:

image.png  

代码如下:

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的控制面板。

image-20241006215912-12.png  

 

当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的数据,这样在仪表盘上就能显示数据了发送的数据示例如下:

image-20241006215938-13.png  

显示效果如下:

image-20241006215938-14.png  

因此在arduino上需要实现mqtt消息发送的功能。

流程图:

   image.png  

代码如下

#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接口连接传感器小板。

image-20241006220018-15.jpeg  

首先获取LTR-329的光传感器数据,流程图

image.png  

代码如下

#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发送消息即可,

流程图为:

image.png  

如下代码实现数据的上传:

#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接口,因此可以进行串行的连接,接线图如下:

image-20241006220136-16.jpeg  

温湿度数据包含了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"
  }
}

流程图为:

image.png  

代码实现如下:

#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);
  }
}

上传的效果如下图:

image.png    

    三、总结

个人感觉这次的任务比以往更有难度,尤其是开发环境的搭建上,比如HA平台的安装,MQTT服务器的安装等。HA平台的使用上也比较有难度。还有就是这次任务可能需要一些额外的设备,比如示波器、额外的排线。不过有难度的任务才能学到更多的知识,通过这次任务认识了HA平台,对智能家居的开发有了新的任务。最后感谢EEWORLD和得捷对这次活动的支持。

 

三、可编译下载的代码

各个任务的源代码.zip (76.9 KB)
(下载次数: 2, 2024-10-6 22:06 上传)
 

Follow me 第二季第2期任务代码-嵌入式开发相关资料下载-EEWORLD下载中心

 

本帖最后由 manhuami2007 于 2024-10-31 11:53 编辑

回复评论

暂无评论,赶紧抢沙发吧
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复