一、项目描述
本项目使用Arduino UNO R4 WiFi和激光测距传感器VL53L3CX实现了无线测距,能够将测距数据上传到智能云端---腾讯云,远程获取传感器数据。Funpack第三季第5期活动描述如下:
任务2:搭配传感器,并通过网络连接到智能云端,可以从远程获取传感器的信息
Arduino UNO R4 WiFi 将瑞萨电子的 RA4M1 微处理器与乐鑫的 ESP32-S3 相结合,为创客打造了一款一体化工具,具有增强的处理能力和多样化的全新外设。凭借其内置的 Wi-Fi® 和蓝牙®功能,UNO R4 WiFi 使制造商能够探索无限的创意可能性。此外,这款多功能板拥有方便的板载 12x8 LED 矩阵和 Qwiic 连接器,为各个级别的创客提供了无与伦比的灵活性和可能性。官网:https://docs.arduino.cc/hardware/uno-r4-wifi/#features
VL53L3CX是意法半导体新推出的一款激光测距传感器,搭配ST的第三代FlightSense算法,在提高了检测精度的同时,测距范围覆盖了25mm到3000mm,还具有自动污迹校正,抗玻璃盖板串扰等特性。VL53L3CX借助算法和巧妙的模块构造,VL53L3CX还能够通过深度了解来检测视场内的不同对象。官网:https://wiki.dfrobot.com.cn/_SKU_SEN0378_DFRobot_VL53L3CX_Tof_Distance_Sensor_Breakout_Board
特性
- 高精度距离测量
- 最远距离可达300cm
- 典型全视角25°
- 多物体测距
- 玻璃盖板串扰的抗扰性
- 污痕补偿功能
应用场景
- 精准距离测量
- 机器人障碍物检测
- 无人机避障/停靠
- 距离触发开关
技术规格
- 供电电压:3.3V~5V
- 工作电流:<20mA
- 测量距离:25-3000mm
- 发射器:940 nm不可见激光(VCSEL)
- 视角范围:25°
- I2C地址:0x29
- 工作温度范围:-20℃~85℃
- 产品尺寸:18*15mm
二、软件流程图及各功能对应的主要代码片段及说明
首先是连线,注意Arduino UNO R4 WiFi的IO电平是5V,它有两个I2C接口,其中Qwiic connector是IIC0,电平是3.3V,软件上使用Wire1区分标准的Wire。 本文使用标准的I2C接口,如下图:
软件流程图如下所示:
上电后,初始化WIFI,连接WIFI,初始化传感器,初始化MQTT客户端,订阅主题,然后定时读取传感器数据通过MQTT发布到智能云端-----腾讯云。这里使用腾讯云平台,在腾讯云平台创建1个物联网公共实例。
填写域名和端口的格式如下:
${productid}.iotcloud.tencentdevices.com:1883
生成username和password方法:
要注意的是username和password是在连接到云端mqtt broker的时候需要填入的,可以通过这个在线工具https://55jy79.csb.app/生成。Hmac签名算法选择HMAC-SHA1。
其中产品ID、设备名、设备秘钥都是腾讯云平台中创建的设备详情页得到的。
(3)填写订阅和发布的消息,可从云平台中获得。
发送的消息指令,具体数据格式可通过云端的调试功能发送一条消息进行测试。
VL53L3CX驱动使用了Arduino的库PWFusion_VL53L3C:
MQTT库使用了PubSubClient:
驱动测试代码如下:
#include <Wire.h>
#include "PWFusion_VL53L3C.h"
VL53L3C TOF;
void setup() {
// Initialize I2C and serial port
Wire.begin();
Wire.setClock(400000);
Serial.begin(9600);
Serial.println(F("Initilizing VL53L3"));
TOF.begin();
Serial.println(F("VL53L3 Boot Complete"));
// Set the timing budget (sample period) to 100ms
TOF.setTimingBudget(100000);
// Start sampling data
TOF.startMeasurement();
Serial.println(F("Starting TOF measurements"));
}
void loop() {
MeasurmentResult measResult;
// Has the VL53L3 finished a new distance mesaurement
if (TOF.dataIsReady())
{
// Get the last measurement result and trigger a new measurement
TOF.getMeasurmentData(&measResult);
TOF.startNextMeasurement();
// The VL53L3 can track up to four onject simultaneously. Prind the
// measured distance to each detected onject.
Serial.print(F("Range Measurement: "));
for (int i=0; i<measResult.numObjs; i++)
{
Serial.print(measResult.rangeData[i].Range);
Serial.print(F("mm "));
}
Serial.println();
}
}
WIFI连接代码如下:
//连接网络
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();
MQTT客户端初始化代码如下:
//连接MQTT
mqttclient.setServer(mqtt_server, 1883);// connect mqtt server
mqttclient.setCallback(callback);
mqttclient.setKeepAlive(65535);
while (!mqttclient.connect(mqtt_clientid, mqtt_username, mqtt_userpwd))
{
Serial.println("mqtt connect fail, reconnect");
delay(2000);
}
Serial.println("mqtt connected!");
根据腾讯云的规范,账号密码如下:
const char *mqtt_server = "F0KI93P7B6.iotcloud.tencentdevices.com";
const char *mqtt_username = "F0KI93P7B6dev1;12010126;5GQQT;1735825267";
const char *mqtt_userpwd = "8b3f78b4f413e3569c21ef5a6b6899ce062705d5;hmacsha1";
const char *mqtt_clientid = "F0KI93P7B601";
MQTT客户端订阅主题代码如下:
// sub topic
boolean ret = mqttclient.subscribe(mqtt_sub_topic);
if (ret != true) {
Serial.println("mqtt subscribe topicfail\n");
}
Serial.println("mqtt subscribe topic ok\n");
主题根据腾讯云的规范设计,定义如下:
const char *mqtt_pub_topic = "$thing/up/property/F0KI93P7B6/dev1";
const char *mqtt_sub_topic = "$thing/down/property/F0KI93P7B6/dev1";
发送数据到云端的代码如下:
if (mqttclient.publish(mqtt_pub_topic, report_buf)) {
Serial.println("mqtt publish topic ok\n");
} else {
Serial.println("mqtt publish topic fail\n");
}
项目完整代码如下:
#include "WiFiS3.h"
#include "PubSubClient.h"
#include <ArduinoJson.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <Wire.h>
#include "PWFusion_VL53L3C.h"
VL53L3C TOF;
WiFiClient wifiClient;
PubSubClient mqttclient(wifiClient);
// 设置WIFI
char ssid[] = "xxx";
char pass[] = "xxx";
const char *mqtt_server = "F0KI93P7B6.iotcloud.tencentdevices.com";
const char *mqtt_username = "F0KI93P7B6dev1;12010126;5GQQT;1735825267";
const char *mqtt_userpwd = "8b3f78b4f413e3569c21ef5a6b6899ce062705d5;hmacsha1";
const char *mqtt_clientid = "F0KI93P7B601";
const char *mqtt_pub_topic = "$thing/up/property/F0KI93P7B6/dev1";
const char *mqtt_sub_topic = "$thing/down/property/F0KI93P7B6/dev1";
bool power_switch;
int distance;
int power_percent = 95;
DynamicJsonDocument doc(1024);
#define REPORT_DATA_TEMPLATE "{\"method\":\"report\",\"clientToken\":\"0001\",\"params\":{\"distance\":%d,\"power_switch\":%d,\"_mesh_generic_power_percent\":%d}}"
long lastMsg = 0;
char report_buf[1024];
void callback(char* topic, byte* payload, unsigned int length)
{
Serial.print("--->Message arrived [");
Serial.print(topic);
Serial.print("] ");
Serial.println();
Serial.print("payload [");
for (int i=0;i<length;i++) {
Serial.print((char)payload[i]);
}
Serial.println();
//处理上位机的控制指令
DeserializationError error = deserializeJson(doc, payload);
// Test if parsing succeeds.
if (error) {
Serial.print("deserializeJson() failed: ");
Serial.println(error.f_str());
return;
}else{
if (doc["clientToken"]!="00000001") {
if (doc["params"]["power_switch"] == 1) {
Serial.print("Power On ");
power_switch=1;
} else {
Serial.print("Power Off ");
power_switch=0;
}
}
}
}
void setup()
{
Serial.begin(9600);
while (!Serial);
//初始化I2C
Wire.begin();
Wire.setClock(400000);
//初始化激光传感器
Serial.println(F("Initilizing VL53L3"));
TOF.begin();
Serial.println(F("VL53L3 Boot Complete"));
//Set the timing budget (sample period) to 100ms
TOF.setTimingBudget(100000);
//Start sampling data
TOF.startMeasurement();
Serial.println(F("Starting TOF measurements"));
//连接网络
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();
//连接MQTT
mqttclient.setServer(mqtt_server, 1883);// connect mqtt server
mqttclient.setCallback(callback);
mqttclient.setKeepAlive(65535);
while (!mqttclient.connect(mqtt_clientid, mqtt_username, mqtt_userpwd))
{
Serial.println("mqtt connect fail, reconnect");
delay(2000);
}
Serial.println("mqtt connected!");
// sub topic
boolean ret = mqttclient.subscribe(mqtt_sub_topic);
if (ret != true) {
Serial.println("mqtt subscribe topicfail\n");
}
Serial.println("mqtt subscribe topic ok\n");
}
void loop()
{
//读取激光传感器数据
MeasurmentResult measResult;
//Has the VL53L3 finished a new distance mesaurement
if (TOF.dataIsReady())
{
//Get the last measurement result and trigger a new measurement
TOF.getMeasurmentData(&measResult);
TOF.startNextMeasurement();
//The VL53L3 can track up to four onject simultaneously. Prind the
//measured distance to each detected onject.
Serial.print(F("Range Measurement: "));
for (int i=0; i<measResult.numObjs; i++)
{
Serial.print(measResult.rangeData[i].Range);
Serial.print(F("mm "));
distance = measResult.rangeData[i].Range;
}
Serial.println();
}
// client loop
mqttclient.loop();
// pub topic
long now = millis();
if (now - lastMsg > 10000)
{
lastMsg = now;
memset(report_buf, 0, 1024);
sprintf(report_buf, REPORT_DATA_TEMPLATE, distance,power_switch,power_percent);
Serial.println(report_buf);
if (mqttclient.publish(mqtt_pub_topic, report_buf))
{
Serial.println("mqtt publish topic ok\n");
}
else
{
Serial.println("mqtt publish topic fail\n");
}
}
}
三、功能展示及说明
串口终端显示结果:
腾讯云远程查看传感器的数据:
四、对本活动的心得体会(包括意见或建议)
活动很nice,希望有更多的活动。