聚豐項目 > 遠程蜂箱檢測系統
蜂蜜具有促進消化,護膚美容,抗菌消炎,提高免疫力,入藥等作用,但是傳統蜂蜜的產量卻是有限的,因為野中蜂對生存環境也比較苛刻,野中蜂比較喜歡生存在干燥,通風,污染低,蜜源豐富的情況,溫度不宜過高,噪聲較小,沒有刺激性氣味的環境下,并且巢蟲對蜂蜜的危害比較大,一般會附在蜂箱底部,如果生長過多,就會趴在巢房上,像這種情況下,蜜蜂就會飛逃,或者出現其他環境不適合的情況下也會出現蜂群飛逃的情況,蜂蜜的質量跟釀蜜的時間是成正比的,所以當一年中出現蜜蜂飛逃會嚴重影響蜂蜜的質量和產量。所以蜂農一般過一段時間就會對蜜蜂的情況進行檢查,但是由于時間或者距離等限制不能及時檢查,接下來就是我用所學知識對蜜蜂生存環境的檢測,基于AB32VG1與VWXR2并搭載RT-Thread操作系統的遠程蜂箱檢測系統,使用DHT11對蜂箱內的溫濕度進行檢測,判斷蜂箱是否淋雨,使用光敏電阻對蜂箱內的亮度進行檢測,是HC-SR04對蜂箱底部進行巢蟲檢測,并通VWXR2進行遠程的數據傳輸。然后在客戶端上查看蜂箱狀況。即使出差也不用擔心。
jf_15811252

jf_15811252
團隊成員
理想三旬 嵌入式軟件工程師
RT-Thread使用情況概述:
整個方案涉及的技術棧有:主控選型、傳感器選型、IO口的分配 、操作系統的選擇與運用、API 接口對接、wifi通信協議、整個系統的調試等等。通過這個作品,讓我對RT-Thread操作系統的框架,內核啟動流程等有了深刻的認識,對面對對象的編程手法有了更深的理解,實現對線程、信號量、互斥量靜態和動態的創建和刪除與運用,實現ADC、UARTD等設備的運用。
內核部分:使用了線程、信號量、定時器等
組件部分:Fish
軟件包:DHT11、HC-SR04
設備驅動:GPIO,ADC,UART等
硬件主要使用了AB32VG1開發板,DHT11溫濕度模塊,HC-SR04模塊,MH檢測裝置,以及VWXR2三明治開發板等(由于在實驗室有些模塊沒有找到,如4G,聲音等,所以對項目進行簡化)。
I/O分配情況:

整個系統接線圖:
各個模塊簡介:
AB32VG1:
CPU: AB5301A;( LQFP48 封裝,主頻 120M,片上集成 RAM 192K, flash 8 Mbit, ADCPWM, USB, UART, IIC 等資源).
搭載藍牙模塊,FM 模塊,一路 TF Card 接口,一路 USB 接口,一路 IIC 接口,一路音頻接口(美標 CTIA),六路 ADC 輸入引腳端子引出,六路 PWM 輸出引腳端子引出,一個全彩 LED 燈模塊, 一個電源指示燈, 三個燒錄指示燈,一個 IRDA(紅外接收端口),一個 Reset 按鍵, 三個功能按鍵(通用版為兩個功能按鍵)。主要功能是作為主控并且搭載RT-Thread操作系統實現實時性控制。
VWXR2:
內置低功耗32位cpu,可兼做處理器,主頻最高支持380MHz
工作電壓:2.7-5.5V
外設:9*GPIO,2*Uart,2*ADC
天線支持:板載+可選ipex
采樣率:16K/16bit
語音輸入:內置2路音頻ADC,可直接模擬mic
音頻輸出:1路
板載音頻功放:最大支持2.6W
推薦喚醒距離:<=3m
默認語音技能:天氣、百科、日歷、計算器、成語、翻譯、已支持音樂內容點播支持線性雙MIC,間距靈活可調(>40mm),ID和MD結構設計靈活,易集成.
主要功能是實現MCU與客戶端的連接,進行數據交互。
DHT11:
DHT11數字溫濕度傳感器是一款含有已校準數字信號輸出的溫濕度復合傳感器。單線通訊方式,測量精度不怎么樣。供電5v不然會出現意想不到的BUG.主要功能是實現蜂箱內部的溫濕度采集,讓蜂農看到蜂箱內部的情況。
HC-SR04:
采用IO口TRIG觸發測距,給至少10us的高電平信號;模塊自動發送8個40khz的方波,自動檢測是否有信號返回;有信號返回,通過IO口ECHO輸出一個高電平,高電平持續的時間就是超聲波從發射到返回的時間。測距范圍是2cm-400cm,值得注意的是,供電為5V,不然會出現采集數據不準或者不能采集的情況。實現巢蟲檢測。
MH采集:
即光照強度采集,模擬量輸出,能使數據的處理更靈活,實現蜂箱蓋的檢測,由于蜜蜂是喜歡黑暗的環境,通過采集光敏電阻的值,并且有用戶設置亮度為多少時才觸發。
摘要:RT-Thread 是一個集實時操作系統(RTOS)內核、中間件組件和開發者社區于一體的技術平臺,RT-Thread 也是一個組件完整豐富、高度可伸縮、簡易開發、超低功耗、高安全性的物聯網操作系統。RT-Thread 具備一個 IoT OS 平臺所需的所有關鍵組件,例如GUI、網絡協議棧、安全傳輸、低功耗組件等等。經過11年的累積發展,RT-Thread 已經擁有一個國內最大的嵌入式開源社區,同時被廣泛應用于能源、車載、醫療、消費電子等多個行業,累積裝機量超過 8億 臺,成為國人自主開發、國內最成熟穩定和裝機量最大的開源 RTOS。
RT-Thread 主要采用 C 語言編寫,淺顯易懂,方便移植。它把面向對象的設計方法應用到實時系統設計中,使得代碼風格優雅、架構清晰、系統模塊化并且可裁剪性非常好。針對資源受限的微控制器(MCU)系統,可通過方便易用的工具,裁剪出僅需要 3KB Flash、1.2KB RAM 內存資源的 NANO 版本(NANO 是 RT-Thread 官方于 2017 年 7 月份發布的一個極簡版內核);而對于資源豐富的物聯網設備,RT-Thread 又能使用在線的軟件包管理工具,配合系統配置工具實現直觀快速的模塊化裁剪,無縫地導入豐富的軟件功能包,實現類似 Android 的圖形界面及觸摸滑動效果、智能語音交互效果等復雜功能。其他的不說,就非常符合國人習慣,RT-Thread Studio 也好用,完善化的論壇,里面有個非常好用的功能就是對一個函數按CTRL鍵就會跳到相應說明的位置,還有中文的API參考手冊等諸多功能,對新手入門特友好。
VWXR2與MCU通信協議
1 串口通信約定
波特率:9600/115200
數據位:8
奇偶校驗:無
停止位:1
數據流控:無
MCU:用戶控制板控制芯片,與涂鴉模組通過串口進行通信
2 幀格式說明

? 所有大于 1 個字節的數據均采用大端模式傳輸。
? 一般情況下,采用同命令字一發一收同步機制。

? 模組控制命令下發及 MCU 狀態上報則采用異步模式,假設模組控制命令下發的命令字為
x,MCU 狀態上報的命令字為 y,如下所示:
?模組控制命令下發

? MCU 狀態上報

?數據類型

在本項目中用到了raw(蜂箱蓋狀態),value(溫濕度,電源電壓值),bool(巢蟲檢測)以及defaut(開發板狀態)上報數據類型。
/**
* @brief raw型dp數據上傳
* @param[in] {dpid} dpid號
* @param[in] {value} 當前dp值指針
* @param[in] {len} 數據長度
* @return Null
* @note Null
*/
unsigned char mcu_dp_raw_update(unsigned char dpid,const unsigned char value[],unsigned short len)
{
unsigned short send_len = 0;
if(stop_update_flag == ENABLE)
return SUCCESS;
//
send_len = set_wifi_uart_byte(send_len,dpid);
send_len = set_wifi_uart_byte(send_len,DP_TYPE_RAW);
//
send_len = set_wifi_uart_byte(send_len,len / 0x100);
send_len = set_wifi_uart_byte(send_len,len % 0x100);
//
send_len = set_wifi_uart_buffer(send_len,(unsigned char *)value,len);
wifi_uart_write_frame(STATE_UPLOAD_CMD,MCU_TX_VER,send_len);
return SUCCESS;
}
/**
* @brief bool型dp數據上傳
* @param[in] {dpid} dpid號
* @param[in] {value} 當前dp值
* @return Null
* @note Null
*/
unsigned char mcu_dp_bool_update(unsigned char dpid,unsigned char value)
{
unsigned short send_len = 0;
if(stop_update_flag == ENABLE)
return SUCCESS;
send_len = set_wifi_uart_byte(send_len,dpid);
send_len = set_wifi_uart_byte(send_len,DP_TYPE_BOOL);
//
send_len = set_wifi_uart_byte(send_len,0);
send_len = set_wifi_uart_byte(send_len,1);
//
if(value == FALSE) {
send_len = set_wifi_uart_byte(send_len,FALSE);
}else {
send_len = set_wifi_uart_byte(send_len,1);
}
wifi_uart_write_frame(STATE_UPLOAD_CMD, MCU_TX_VER, send_len);
return SUCCESS;
}
/**
* @brief value型dp數據上傳
* @param[in] {dpid} dpid號
* @param[in] {value} 當前dp值
* @return Null
* @note Null
*/
unsigned char mcu_dp_value_update(unsigned char dpid,unsigned long value)
{
unsigned short send_len = 0;
if(stop_update_flag == ENABLE)
return SUCCESS;
send_len = set_wifi_uart_byte(send_len,dpid);
send_len = set_wifi_uart_byte(send_len,DP_TYPE_VALUE);
//
send_len = set_wifi_uart_byte(send_len,0);
send_len = set_wifi_uart_byte(send_len,4);
//
send_len = set_wifi_uart_byte(send_len,value >> 24);
send_len = set_wifi_uart_byte(send_len,value >> 16);
send_len = set_wifi_uart_byte(send_len,value >> 8);
send_len = set_wifi_uart_byte(send_len,value & 0xff);
wifi_uart_write_frame(STATE_UPLOAD_CMD,MCU_TX_VER,send_len);
return SUCCESS;
}
/**
* @brief fault型dp數據上傳
* @param[in] {dpid} dpid號
* @param[in] {value} 當前dp值
* @return Null
* @note Null
*/
unsigned char mcu_dp_fault_update(unsigned char dpid,unsigned long value)
{
unsigned short send_len = 0;
if(stop_update_flag == ENABLE)
return SUCCESS;
send_len = set_wifi_uart_byte(send_len,dpid);
send_len = set_wifi_uart_byte(send_len,DP_TYPE_BITMAP);
//
send_len = set_wifi_uart_byte(send_len,0);
if((value | 0xff) == 0xff) {
send_len = set_wifi_uart_byte(send_len,1);
send_len = set_wifi_uart_byte(send_len,value);
}else if((value | 0xffff) == 0xffff) {
send_len = set_wifi_uart_byte(send_len,2);
send_len = set_wifi_uart_byte(send_len,value >> 8);
send_len = set_wifi_uart_byte(send_len,value & 0xff);
}else {
send_len = set_wifi_uart_byte(send_len,4);
send_len = set_wifi_uart_byte(send_len,value >> 24);
send_len = set_wifi_uart_byte(send_len,value >> 16);
send_len = set_wifi_uart_byte(send_len,value >> 8);
send_len = set_wifi_uart_byte(send_len,value & 0xff);
}
wifi_uart_write_frame(STATE_UPLOAD_CMD, MCU_TX_VER, send_len);
return SUCCESS;
}
/**
* @brief 系統所有dp點信息上傳,實現APP和muc數據同步
* @param Null
* @return Null
* @note 此函數SDK內部需調用,MCU必須實現該函數內數據上報功能,包括只上報和可上報可下發型數據
*/
void all_data_update(void)
{
mcu_dp_raw_update(DPID_MEAL_PLAN,0,0); //當前蜂箱狀況檢測指針,當前蜂箱狀況檢測數據長度RAW型數據上報;
mcu_dp_value_update(DPID_BATTERY_PERCENTAGE,0); //當前電池電量VALUE型數據上報;
mcu_dp_bool_update(DPID_CHARGE_STATE,0); //當前充電狀態BOOL型數據上報;
mcu_dp_enum_update(DPID_COVER_STATE,0); //當前蜂箱蓋狀態枚舉型數據上報;
mcu_dp_fault_update(DPID_FAULT,0); //當前故障告警故障型數據上報;
mcu_dp_value_update(DPID_FEED_REPORT,0); //當前檢測結果上報VALUE型數據上報;
mcu_dp_value_update(DPID_VOICE_TIMES,0); //當前語音播放次數VALUE型數據上報;
mcu_dp_bool_update(DPID_LIGHT,0); //當前小夜燈BOOL型數據上報;
mcu_dp_value_update(DPID_NOW_TEMP,0); //當前溫度檢測VALUE型數據上報;
mcu_dp_value_update(DPID_HUM,90); //當前濕度檢測VALUE型數據上報;
mcu_dp_bool_update(DPID_CHECK,0); // 當前巢蟲檢測 BOOL型數據上報;
mcu_dp_value_update(DPID_LIGHT,0); //VALUE型數據上報;
}調試:調試過程中我采用的是一個傳感器一個傳感器的調,出錯容易找,然后在進行多個傳感器的融合
創建好工程后,編寫按鍵(實現按鍵VWXR2復位與配網模式切換),LED(實現AB32VG1開發板正常運行觀察,避免出現卡死情況然后并不知道哪里出了問題)功能。
//貼的主要代碼,全部代碼已經放入gitee
static void key_thread_entry(void* p)
{
uint8_t byn_value = 0;
while(1)
{
byn_value = btn_scan(0);
switch(byn_value)
{
case KEY0_PRES:
rt_kprintf("key0 pushed\n");
mcu_reset_wifi();
rt_kprintf("mcu_reset_wifi\n");//使VWXR2開發板復位,并進入配網模式
break;
case KEY1_PRES:
rt_kprintf("key1 pushed\n");
mcu_set_wifi_mode(0);
rt_kprintf("smart_wifi_mode");//實現智能配網
break;
default:
break;
}
rt_thread_mdelay(100);
}
}
int rt_thread_key(void)
{
rt_thread_t key_ret = RT_NULL;
key_init();
key_ret = rt_thread_create("key", key_thread_entry, RT_NULL, 512, 9, 10);
if(key_ret == RT_NULL)
{
rt_kprintf("key init ERROR");
return RT_ERROR;
}
else {
rt_kprintf("rt_thread_key succeed....\n");
}
rt_thread_startup(key_ret);
}
INIT_APP_EXPORT(rt_thread_key);打開調試助手觀察串口輸出情況

按下key0后VWXR2開發板會播報“我已進入配網狀態,請下載APP為我完成配網”,演示如下:
2.DHT11調試與數據上報
在RT-Thread Setting中添加DHT11的sdk包,選擇第一個并保存

配置自己分配的引腳

刪掉SDK包中的void rt_hw_us_delay(rt_uint32_t us)函數
在board.c中添加自己的硬件定時器us函數,uint8_t等會報錯,在前面加上rt_就可以了
void rt_hw_us_delay(rt_uint32_t us)
{
rt_uint32_t ticks;
rt_uint32_t told, tnow, tcnt = 0;
rt_uint32_t reload = TMR0PR;
ticks = us * reload / (1000);
told = TMR0CNT;
while (1)
{
tnow = TMR0CNT;
if (tnow != told)
{
if (tnow > told)
{
tcnt += tnow - told;
}
else
{
tcnt += reload - told + tnow;
}
told = tnow;
if (tcnt >= ticks)
{
break;
}
}
}
}觀察串口輸出

測試成功,在線程中添加數據上報代碼
#define DHT11_DATA_PIN GET_PIN(A, 0)
static void read_temp_entry(void *parameter)
{
rt_device_t dev = RT_NULL;
struct rt_sensor_data sensor_data;
rt_size_t res;
rt_uint8_t get_data_freq = 1; /* 1Hz */
dev = rt_device_find("temp_dht11");
if (dev == RT_NULL)
{
return;
}
if (rt_device_open(dev, RT_DEVICE_FLAG_RDWR) != RT_EOK)
{
rt_kprintf("open device failed!\n");
return;
}
rt_device_control(dev, RT_SENSOR_CTRL_SET_ODR, (void *)(&get_data_freq));
while (1)
{
res = rt_device_read(dev, 0, &sensor_data, 1);
if (res != 1)
{
rt_kprintf("read data failed! result is %d\r\n", res);
rt_device_close(dev);
return;
}
else
{
if (sensor_data.data.temp >= 0)
{
uint8_t temp = (sensor_data.data.temp & 0xffff) >> 0; // get temp
uint8_t humi = (sensor_data.data.temp & 0xffff0000) >> 16; // get humi
rt_kprintf("temp:%d, humi:%d\n" ,temp, humi);
mcu_dp_value_update(DPID_NOW_TEMP,temp); //當前溫度檢測VALUE型數據上報;
mcu_dp_value_update(DPID_HUM,humi); //當前濕度檢測VALUE型數據上報;
}
}
rt_thread_delay(3000);
}
}
static int dht11_read_temp_sample(void)
{
rt_thread_t dht11_thread;
dht11_thread = rt_thread_create("dht_tem",read_temp_entry, RT_NULL,1024,13,20);
if(dht11_thread==RT_NULL)
{
rt_kprintf("dht11_thread fail....\n");
}
else {
rt_kprintf("dht11_thread succeed....\n");
}
rt_thread_startup(dht11_thread);
}
INIT_APP_EXPORT(dht11_read_temp_sample);
static int rt_hw_dht11_port(void)
{
struct rt_sensor_config cfg;
cfg.intf.user_data = (void *)DHT11_DATA_PIN;
rt_hw_dht11_init("dht11", &cfg);
return RT_EOK;
}
INIT_COMPONENT_EXPORT(rt_hw_dht11_port);打開手機APP,觀察數據

app對溫度進行了一個縮小10的處理
3.HC-SR04
添加SDK包,并且修改引腳,并進行數據上報
#define SR04_TRIG_PIN GET_PIN(E, 0)
#define SR04_ECHO_PIN GET_PIN(A, 6)
int sr04_read_distance_sample(void);
int rt_hw_sr04_port(void);
static void sr04_read_distance_entry(void *parameter)
{
rt_device_t dev = RT_NULL;
struct rt_sensor_data sensor_data;
rt_size_t res;
rt_uint16_t distance=0;
dev = rt_device_find(parameter);
if (dev == RT_NULL) {
rt_kprintf("Can't find device:%s\n", parameter);
return;
}
if (rt_device_open(dev, RT_DEVICE_FLAG_RDWR) != RT_EOK) {
rt_kprintf("open device failed!\n");
return;
}
rt_device_control(dev, RT_SENSOR_CTRL_SET_ODR, (void *)100);
while (1) {
res = rt_device_read(dev, 0, &sensor_data, 1);
if (res != 1) {
rt_kprintf("read data failed!size is %d\n", res);
rt_device_close(dev);
return;
}
else {
distance=sensor_data.data.proximity / 10+sensor_data.data.proximity % 10;
if(distance>=20&&distance<=400)
//rt_kprintf("distance:%3d.%dcm, timestamp:%5d\n", sensor_data.data.proximity / 10, sensor_data.data.proximity % 10, sensor_data.timestamp);
if(distance>30)
mcu_dp_bool_update(DPID_CHECK,0); // 當前巢蟲檢測 BOOL型數據上報;
else {
mcu_dp_bool_update(DPID_CHECK,1); // 當前巢蟲檢測 BOOL型數據上報;
}
}
rt_thread_mdelay(2000);
}
}
int sr04_read_distance_sample(void)
{
rt_thread_t sr04_thread;
sr04_thread = rt_thread_create("sr04",
sr04_read_distance_entry,
"pr_sr04",
1024,
11,
20);
if (sr04_thread != RT_NULL) {
rt_thread_startup(sr04_thread);
}
return RT_EOK;
}
INIT_APP_EXPORT(sr04_read_distance_sample);4.AB32VG1與VWXR2之間的通信
#include"uart1.h"
/* 用于接收消息的信號量 */
static struct rt_semaphore rx_sem;
rt_device_t serial;
/* 接收數據回調函數 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
/* 串口接收到數據后產生中斷,調用此回調函數,然后發送接收信號量 */
rt_sem_release(&rx_sem);
return RT_EOK;
}
static void serial_thread_entry(void *parameter)
{
char ch;
rt_kprintf("test UART1 \n");
while (1)
{
/* 從串口讀取一個字節的數據,沒有讀取到則等待接收信號量 */
while (rt_device_read(serial, -1, &ch, 1) != 1)
{
/* 阻塞等待接收信號量,等到信號量后再次讀取數據 */
rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
}
uart_receive_input(ch);
}
}
static int uart_sample(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
char uart_name[RT_NAME_MAX];
char str[] = "hello RT-Thread!\r\n";
if (argc == 2)
{
rt_strncpy(uart_name, argv[1], RT_NAME_MAX);
}
else
{
rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX);
}
/* 查找系統中的串口設備 */
serial = rt_device_find(uart_name);
if (!serial)
{
rt_kprintf("find %s failed!\n", uart_name);
return RT_ERROR;
}
/* 初始化信號量 */
rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
/* 以中斷接收及輪詢發送模式打開串口設備 */
rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
/* 設置接收回調函數 */
rt_device_set_rx_indicate(serial, uart_input);
/* 發送字符串 */
rt_device_write(serial, 0, str, (sizeof(str) - 1));
/* 創建 serial 線程 */
rt_thread_t thread = rt_thread_create("serial", serial_thread_entry,
RT_NULL, 1024, 7, 10);
/* 創建成功則啟動線程 */
if (thread != RT_NULL)
{
rt_kprintf("uart1_thread succeed....\n");
rt_thread_startup(thread);
}
else
{
ret = RT_ERROR;
}
return ret;
}
//在protel.c中添加單字節函數,進行數據的上報
/**
* @brief 串口發送數據
* @param[in] {value} 串口要發送的1字節數據
* @return Null
*/
void uart_transmit_output(unsigned char value)
{
rt_device_write(serial, 0,&value, 1);//單字節上報
}程序寫好后,打開涂鴉調試助手,選擇模擬模組,因為我要調試我寫的程序是否和模組通信

VWXR2與串口接線如圖所示
![]()

當調試助手出現下面這個界面時,證明程序沒問題,然后就可以和模組連接了

項目代碼地址:
https://gitee.com/AB32VG1/ab32.git