10

小熊派开源社区 · 2022年02月07日

基于RT-Thread Studio 和小熊派实现智慧农业

摘要:如何从零开发一个“智慧农业”案例?小伙伴们有福了,孙教授手把手的带大家基于小熊派+RT-Thread开发一个智慧农业案例。

一、硬件分析

1.1 背景平台任务介绍

  • 基于小熊派 BearPi-IoT Std 硬件平台
  • 采用 RT-Thread Studio 快速搭建软件工程
  • 实现智慧农业:基于 E53_IA1 扩展板
  • 输入:温度、湿度、光照度
  • 输出: FAN、LED
  • 前期准备工作:
  • 硬件:BearPi-IoT Std开发板
  • 硬件:WIFI8266通信板
  • 硬件:E53_IA1案例板
  • 软件:STM32CubeMX软件
  • 软件:RT-Thread Studio IDE

1.2 硬件电路连接

LCD-SPI2:

LCD_PWR_PIN           B15
LCD_DC_PIN            C6
LCD_RES_PIN           C7

RW007:

WAN_RI              B14
SPI3_NSS            A15
SPI3_MISO           C11
SPI3_SCK            C10
SPI3_MOSI           B5

ESP_UART_TX         C0
ESP_UART_RX         C1

SPI_FLASH:

QSPI_NCS            B11
QSPI_CLK            B10
QSPI_SI             B1
QSPI_SO             B0

TF_CARD:

SD_CMD              D2
SD_CLK              C12
SD_DO               C8

KEY & LED:

KEY1                B2
KEY2                B3
LED                 C13 

E53_IA1 :

LED_SW              A0
INT                 B9
Motor_SW            B8
I2C_SCL             B6
I2C_SDA             B7

调试串口:

UART1_TX             A9
UART1_RX             A10

二、新建工程

2.1 新建RTT工程

基于 STM32L431RCT6 新建RTT工程, 基于芯片,使用UART1
image.png

2.2 添加cube对应的驱动

打开CubeMx Settings,如图进行相关配置,包括:时钟,时钟频率 ,代码规则 ,生成工程时的版本MDK5.27
image.png
配置使用外部晶振
image.png
配置主频
image.png
配置代码生成规则
image.png
生成工程IDE及版本号
image.png
下面配置 串口,GPIO
image.png
配置KEY!
image.png
配置完成,生成代码后,保存关闭
image.png
由CubeMx自动生成的初始化代码
image.png
下面编译、下载程序
image.png
下载程序流程
image.png
打开终端,配置终端串口
image.png

三、添加相关组件和软件包

添加相关驱动配置宏,在 board.h 中添加

#define BSP_USING_I2C1
#define BSP_USING_LPUART1
#define BSP_LPUART1_TX_PIN       "PC0"
#define BSP_LPUART1_RX_PIN       "PC1"
#define RT_USING_SPI
#define BSP_USING_SPI2

进入RTT配置界面
image.png
已经添加的组件
image.png
添加组件 SPI ,软件模拟 I2C , 传感器,软件包 cjson , webclient, pahomqtt, at_device, bh1750, onenet, sht3x
image.png
添加的过程
image.png
配置完成后,保存关闭。可进入详细配置
image.png

四、添加 esp8266 驱动

配置 wifi 用户名和密码
image.png
在 CubeMX 中添加 lpuart1
image.png

五、添加 LCD 相关驱动

添加LCD使用的spi2
image.png
添加LCD的驱动代码
image.png

六、传感器I2C 配置

以下均使用i2c1

E53_IA1_bh1750_demo
E53_IA1_sht30_demo

在 RTT Settings 中添加 I2C1
image.png
添加LCD的头文件路径
image.png

七、GPIO 口的使用

添加 ES53 的引脚

LED_SW              A0
Motor_SW            B8
LED                 C13
KEY1                B2   
KEY2                B3

修改B2 B3为中断输入 A0 B8 C13 推挽输出

board.h中添加

#define LED0_PIN GET_PIN(C,13)
#define LED1_PINK_PIN GET_PIN(A,0)
#define MOTOR_PIN GET_PIN(B,8)
#define KEY1_PIN GET_PIN(B,2)
#define KEY2_PIN GET_PIN(B,3)

八、修改代码

源码:sht3x.c

sht3x_device_t dev_sht30 = RT_NULL;
void E53_IA1_sht30_task_entry(void* par)
{

        rt_uint8_t sht_addr = SHT3X_ADDR_PD ;
        dev_sht30 = sht3x_init("i2c1", sht_addr);
        if(!dev_sht30)
         {
             rt_kprintf("sht3x probe failed, check input args\n");
         }else
         {
             rt_kprintf("sht3x probed, addr:0x%x\n", sht_addr) ;
         }
        while(1)
        {

          if (dev_sht30)
         {
             /* read the sensor data */
             sht3x_read_singleshot(dev_sht30);
//           rt_kprintf("sht3x humidity   : %d.%d \n", (int)dev_sht30->humidity, (int)(dev_sht30->humidity * 10) % 10);
//           rt_kprintf("sht3x temperature: %d.%d \n", (int)dev_sht30->temperature, (int)(dev_sht30->temperature * 10) % 10);
         }
         else
         {
             rt_kprintf("Please using 'sht3x probe <i2c dev name> <pu/pd>' first\n");
         }
          rt_thread_mdelay(1000);
        }
}
int E53_IA1_sht30_demo(void)
{
    rt_thread_t tid;
    tid=rt_thread_create("E53_IA1_sht30_demo", E53_IA1_sht30_task_entry, RT_NULL,2*1024, 20, 7);
    if(tid!=RT_NULL)
    {
        rt_thread_startup(tid);
    }
    return 0;
}
/* 导出到 msh 命令列表中 */

MSH_CMD_EXPORT(E53_IA1_sht30_demo,E53_IA1_sth30 demo);

源码:bh17650.c

#include "sensor.h"

struct rt_sensor_data bh1750_data;
void E53_IA1_bh1750_task_entry(void *par)
{
    rt_device_t dev_bh1750=RT_NULL;
    dev_bh1750 = rt_device_find("li_bh1750");
    if (dev_bh1750 == RT_NULL)
    {
        rt_kprintf("Can't find li_bh1750 device\n");
        return;
    }

    /* 以只读模式打开bh1750 */
    if (rt_device_open(dev_bh1750, RT_DEVICE_FLAG_RDONLY) != RT_EOK)
    {
        rt_kprintf("open device failed!");
        return;
    }
   while(1)
   {

      rt_device_read(dev_bh1750, 0, &bh1750_data, 1);
      rt_kprintf("BH1750:%4d.%d lux\n", bh1750_data.data.light / 10, bh1750_data.data.light % 10);
      rt_thread_mdelay(1000);
   }
}

int E53_IA1_bh1750_demo(void)
{
    rt_thread_t tid;
    tid=rt_thread_create("E53_IA1_bh1750_demo", E53_IA1_bh1750_task_entry, RT_NULL, 2*1024, 20, 6);
    if(tid!=RT_NULL)
    {
        rt_thread_startup(tid);
    }
    return 0;
}
/* 导出到 msh 命令列表中 */
//MSH_CMD_EXPORT(E53_IA1_bh1750_demo, E53_IA1_bh1750 demo);

修改 onenet_sample.c, 全部复制

修改main.c,全部复制。 改rtconfig.h中,onenet的数据配置,这里的数据要根据自己的设备情况进行修改

/* IoT Cloud */
#define PKG_USING_ONENET
#define PKG_USING_ONENET_SAMPLE
#define ONENET_USING_MQTT
#define ONENET_INFO_DEVID "2************4"
#define ONENET_INFO_AUTH "w************e"
#define ONENET_INFO_APIKEY "T************************************="
#define ONENET_INFO_PROID "1************************************9"
#define ONENET_MASTER_APIKEY "X************************************="
#define PKG_USING_ONENET_LATEST_VERSION

如下图所示,根据自己的OneNet产品、设备等填写
image.png
编译后下载,运行。上位机连接网络页面
image.png

九、OneNet云平台的配置

登录 https://open.iot.10086.cn/注册账号 从“控制台”进入 “控制台首页” 进入“全部产品服务”再进入“多协议接入” 创建 MQTT 协议的产品“BearPi-IoT”。产品ID PROID: _4******7_ Master-APIKey:_V************************************************=_

image.png
进入产品,创建设备“BearPi_RT-Thread_Agriculture”
image.png

#define ONENET_INFO_DEVID "7************4"
#define ONENET_INFO_AUTH "w************e"
#define ONENET_INFO_APIKEY "s************************************="
#define ONENET_INFO_PROID "4************7"
#define ONENET_MASTER_APIKEY "V************************************="

最后进行应用管理设置
image.png

十、结论

本次测试过程从工程创建到系统完成,一共花费约2小时,RTT Studio属于高效、质量高的物联网构建方案

代码部分,自行添加的大型文件有: main.c 和 onenet_sample.c. 部分修改的代码有: bh17650.c 和 sht3x.c

其余均为参量等少量修改等,如wifi密码等

main.c源码:

/*
 * Copyright (c) 2019-2021, BearPi IoT Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2021-04-17    BearPi IoT    first version
 */

#include <rtthread.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include <board.h>
#include <rtdevice.h>
#include "sensor.h"
#include "sht3x.h"
#include "dev_lcd.h"
#include "onenet.h"

extern int E53_IA1_sht30_demo(void);
extern int E53_IA1_bh1750_demo(void);
extern int onenet_mqtt_init(void);                    //初始化onenet
extern int onenet_upload_cycle(void);                 //循环上报温湿度数据
extern void onenet_upload_entry(void *parameter);     //循环上报
extern int onenet_upload_cycle(void);                 //循环上报任务
extern int onenet_set_cmd_rsp(int argc, char **argv); //接收云端指令信息
extern int cloud_connect_flag;
extern sht3x_device_t dev_sht30;
extern struct rt_sensor_data bh1750_data;
int motor_status;
int led_pink_status;
int motor_flag = 0;
int led_flag = 0;
int send_flag1 = 0, send_flag2 = 0;
void key1_recall(void *args)
{
    if (motor_status)
    {
        rt_pin_write(MOTOR_PIN, PIN_LOW);
        motor_flag = 0;
    }
    else
    {
        rt_pin_write(MOTOR_PIN, PIN_HIGH);
        motor_flag = 1;
    }
    rt_kprintf("KEY1 test!---\n");
    motor_status = ~motor_status;
    send_flag1 = 1;
}
void key2_recall(void *args)
{

    if (led_pink_status)
    {
        rt_pin_write(LED1_PINK_PIN, PIN_LOW);
        led_flag = 0;
    }
    else
    {
        rt_pin_write(LED1_PINK_PIN, PIN_HIGH);
        led_flag = 1;
    }
    rt_kprintf("KEY2 test!+++\n");
    led_pink_status = ~led_pink_status;
    send_flag2 = 1;
}

int main(void)
{
    int count = 1;
    rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);      //输出模式
    rt_pin_mode(LED1_PINK_PIN, PIN_MODE_OUTPUT); //输出模式
    rt_pin_mode(MOTOR_PIN, PIN_MODE_OUTPUT);     //输出模式
    /*KEY1中断初始化*/
    rt_pin_mode(KEY1_PIN, PIN_MODE_INPUT);                                   //输入模式
    rt_pin_attach_irq(KEY1_PIN, PIN_IRQ_MODE_FALLING, key1_recall, RT_NULL); //下降沿检测并调用回调函数
    rt_pin_irq_enable(KEY1_PIN, PIN_IRQ_ENABLE);                             //使能中断
    /*KEY2中断初始化*/
    rt_pin_mode(KEY2_PIN, PIN_MODE_INPUT);                                   //输入模式
    rt_pin_attach_irq(KEY2_PIN, PIN_IRQ_MODE_FALLING, key2_recall, RT_NULL); //下降沿检测并调用回调函数
    rt_pin_irq_enable(KEY2_PIN, PIN_IRQ_ENABLE);                             //使能中断
    lcd_set_color(WHITE, BLUE);                                              //背景白色,字体蓝色
    onenet_mqtt_init();                                                      //初始化onenet云平台
    E53_IA1_sht30_demo();                                                    //E53_IA1智慧农业扩展板温湿度传感器监测任务
    E53_IA1_bh1750_demo();                                                   //E53_IA1智慧农业扩展板光照强度传感器监测任务
    onenet_set_cmd_rsp(RT_NULL, RT_NULL);                                    //启动接收接收云平台指令并解析进行控制
    onenet_upload_cycle();                                                   //循环上报数据到云平台任务

    while (count++)
    {
        lcd_show_string(180, 60, 32, "%2d%  ", (int)dev_sht30->humidity);                                           //湿度数据LCD显示
        lcd_show_string(60, 60, 32, "%d.%d", (int)dev_sht30->temperature, (int)(dev_sht30->temperature * 10) % 10); //温度数据LCD显示
        lcd_show_string(60, 125, 32, "%4d", bh1750_data.data.light / 10);                                           //光照强度数据LCD显示
        /*通过上传数据到云平台成功来判断是否上云成功*/
        if (cloud_connect_flag == 1)
        {
            lcd_set_color(GREEN, BLUE);
            lcd_show_string(140, 125, 32, "OneNET");
            lcd_set_color(WHITE, BLUE);
        }
        else
        {
            lcd_set_color(RED, YELLOW);
            lcd_show_string(140, 125, 32, "OneNET");
            lcd_set_color(WHITE, BLUE);
        }
        /*E53_IA1 电机运行开关状态显示*/
        if (motor_flag == 1)
        {
            lcd_set_color(BLUE, GREEN);
            lcd_show_string(10, 210, 24, "FAN:ON ");
            lcd_set_color(WHITE, BLUE);
        }
        else if (motor_flag == 0)
        {
            lcd_set_color(BLUE, RED);
            lcd_show_string(10, 210, 24, "FAN:OFF ");
            lcd_set_color(WHITE, BLUE);
        }
        /*E53_IA1 LED紫色灯开关状态显示*/
        if (led_flag == 1)
        {
            lcd_set_color(BLUE, GREEN);
            lcd_show_string(140, 210, 24, "LED:ON ");
            lcd_set_color(WHITE, BLUE);
        }
        else if (led_flag == 0)
        {
            lcd_set_color(BLUE, RED);
            lcd_show_string(140, 210, 24, "LED:OFF");
            lcd_set_color(WHITE, BLUE);
        }
        if (send_flag1 == 1) //将本地按键控制状态上传到云平台来判定控制的设备:E53_IA1智慧农业扩展板电机
        {
            send_flag1 = 0;
            if (onenet_mqtt_upload_digit("Motor_status", motor_flag))
            {
                LOG_E("upload has an error, stop uploading");
            }
            else
            {
                LOG_D("buffer : {\"Motor_status\":%d}", motor_flag);
            }
        }
        if (send_flag2 == 1) //将本地按键控制状态上传到云平台来判定控制的设备:E53_IA1智慧农业扩展板LED紫色灯
        {
            send_flag2 = 0;
            if (onenet_mqtt_upload_digit("LED_status", led_flag))
            {
                LOG_E("upload has an error, stop uploading");
            }
            else
            {
                LOG_D("buffer : {\"LED_status\":%d}", led_flag);
            }
        }

        /*板载蓝灯闪烁*/
        rt_pin_write(LED0_PIN, PIN_HIGH);
        rt_thread_mdelay(500);
        rt_pin_write(LED0_PIN, PIN_LOW);
        rt_thread_mdelay(500);
    }

    return RT_EOK;
}

onenet_sample.c 源码

/*
* File      : onenet_sample.c
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License along
*  with this program; if not, write to the Free Software Foundation, Inc.,
*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date           Author       Notes
* 2018-04-24     chenyong     first version
*/
#include <stdlib.h>
#include <rtdevice.h>
#include <board.h>
#include <onenet.h>
#include <sensor.h>
#include <CJSON.h>
#define DBG_ENABLE
#define DBG_COLOR
#define DBG_SECTION_NAME "onenet.sample"
#if ONENET_DEBUG
#define DBG_LEVEL DBG_LOG
#else
#define DBG_LEVEL DBG_INFO
#endif /* ONENET_DEBUG */

#include <rtdbg.h>
#include <sht3x.h>
#ifdef FINSH_USING_MSH
#include <finsh.h>
extern sht3x_device_t dev_sht30;
extern struct rt_sensor_data bh1750_data;
extern int motor_flag;
extern int led_flag;
int cloud_connect_flag = 0;

void onenet_upload_entry(void *parameter)
{
    int value1 = 0;
    int value2 = 0;
    int value3 = 0;
    uint32_t count1 = 0;
    uint32_t send_cnt = 0;

    while (1)
    {
        value1 = dev_sht30->temperature;
        value2 = dev_sht30->humidity;
        value3 = bh1750_data.data.light;

        switch (send_cnt++)
        {
        case 0:
            if (onenet_mqtt_upload_digit("temperature", value1) < 0)
            {
                LOG_E("upload has an error, stop uploading");
                cloud_connect_flag = 0;
            }
            else
            {
                LOG_D("buffer : {\"temperature\":%d}", value1);
                cloud_connect_flag = 1;
            }

            break;
        case 1:
            if (onenet_mqtt_upload_digit("humidity", value2))
            {
                LOG_E("upload has an error, stop uploading");
            }
            else
            {
                LOG_D("buffer : {\"humidity\":%d}", value2);
            }
            break;
        case 2:
            if (onenet_mqtt_upload_digit("luminance", value3))
            {
                LOG_E("upload has an error, stop uploading");
            }
            else
            {
                LOG_D("buffer : {\"luminance\":%d}", value3);
            }
            break;
        case 3:
            if (count1++ >= 999999)
            {
                count1 = 0;
            }
            if (onenet_mqtt_upload_digit("Count", count1))
            {
                LOG_E("upload has an error, stop uploading");
            }
            else
            {
                LOG_D("buffer : {\"Count\":%d}", count1);
            }
            break;
        }
        if (send_cnt >= 4)
            send_cnt = 0;

        rt_thread_mdelay(rt_tick_from_millisecond(500));
    }
}

int onenet_upload_cycle(void)
{
    rt_thread_t tid;
    tid = rt_thread_create("onenetupload", onenet_upload_entry, RT_NULL, 5 * 1024, 20, 6);
    if (tid != RT_NULL)
    {
        rt_thread_startup(tid);
    }
    return 0;
}
MSH_CMD_EXPORT(onenet_upload_cycle, send data to OneNET cloud cycle);

int onenet_publish_digit(int argc, char **argv)
{
    if (argc != 3)
    {
        LOG_E("onenet_publish [datastream_id]  [value]  - mqtt pulish digit data to OneNET.");
        return -1;
    }

    if (onenet_mqtt_upload_digit(argv[1], atoi(argv[2])) < 0)
    {
        LOG_E("upload digit data has an error!\n");
    }

    return 0;
}
MSH_CMD_EXPORT_ALIAS(onenet_publish_digit, onenet_mqtt_publish_digit, send digit data to onenet cloud);

int onenet_publish_string(int argc, char **argv)
{
    if (argc != 3)
    {
        LOG_E("onenet_publish [datastream_id]  [string]  - mqtt pulish string data to OneNET.");
        return -1;
    }

    if (onenet_mqtt_upload_string(argv[1], argv[2]) < 0)
    {
        LOG_E("upload string has an error!\n");
    }

    return 0;
}
MSH_CMD_EXPORT_ALIAS(onenet_publish_string, onenet_mqtt_publish_string, send string data to onenet cloud);

/* onenet mqtt command response callback and analysis function */
static void onenet_cmd_rsp_cb(uint8_t *recv_data, size_t recv_size, uint8_t **resp_data, size_t *resp_size)
{
    char res_buf[] = {"cmd is received!\n"};
    cJSON *cjson_test = NULL;
    cJSON *cjson_ctl = NULL;
    LOG_D("recv data is %.*s\n", recv_size, recv_data);
    char temp[] = {25};
    /* user have to malloc memory for response data */
    *resp_data = (uint8_t *)ONENET_MALLOC(strlen(res_buf));

    strncpy((char *)*resp_data, res_buf, strlen(res_buf));

    *resp_size = strlen(res_buf);
    cjson_test = cJSON_Parse((char *)recv_data);
    cjson_ctl = cJSON_GetObjectItem(cjson_test, "LED");
    printf("OneNet Command  %d\r\n", cjson_ctl->valueint);
    if (cjson_ctl->valueint == 1)
    {
        rt_pin_write(LED1_PINK_PIN, PIN_HIGH);
        led_flag = 1;
        sprintf(temp, "LED?");
        printf("OneNet Command LED ON\r\n");
        if (onenet_mqtt_upload_digit("LED_status", led_flag))
        {
            LOG_E("upload has an error, stop uploading");
        }
        else
        {
            LOG_D("buffer : {\"LED_status\":ON}");
        }
    }
    if (cjson_ctl->valueint == 0)
    {
        rt_pin_write(LED1_PINK_PIN, PIN_LOW);
        led_flag = 0;
        printf("OneNet Command LED OFF\r\n");
        if (onenet_mqtt_upload_digit("LED_status", led_flag))
        {
            LOG_E("upload has an error, stop uploading");
        }
        else
        {
            LOG_D("buffer : {\"LED_status\":OFF}");
        }
    }
    cjson_ctl = cJSON_GetObjectItem(cjson_test, "Motor");
    printf("OneNet Command  %d\r\n", cjson_ctl->valueint);
    if (cjson_ctl->valueint == 1)
    {
        rt_pin_write(MOTOR_PIN, PIN_HIGH);
        motor_flag = 1;
        printf("OneNet Command Motor ON\r\n");
        if (onenet_mqtt_upload_digit("Motor_status", motor_flag))
        {
            LOG_E("upload has an error, stop uploading");
        }
        else
        {
            LOG_D("buffer : {\"Motor_status\":ON}");
        }
    }
    if (cjson_ctl->valueint == 0)
    {
        rt_pin_write(MOTOR_PIN, PIN_LOW);
        motor_flag = 0;
        printf("OneNet Command Motor OFF\r\n");
        if (onenet_mqtt_upload_digit("Motor_status", motor_flag))
        {
            LOG_E("upload has an error, stop uploading");
        }
        else
        {
            LOG_D("buffer : {\"Motor_status\":OFF}");
        }
    }
}

/* set the onenet mqtt command response callback function */
int onenet_set_cmd_rsp(int argc, char **argv)
{
    onenet_set_cmd_rsp_cb(onenet_cmd_rsp_cb);
    return 0;
}
MSH_CMD_EXPORT(onenet_set_cmd_rsp, set cmd response function);

#endif /* FINSH_USING_MSH */

作者介绍:

孙冬梅:女,博士/博士后,副教授,2004年获南京理工大学测试计量技术及仪器专业博士学位;2011.1~2015.12进入南京工业大学“动力工程及工程热物理”博士后流动站从事研究工作。现任职于南京工业大学电控学院,兼任江苏省仪器仪表学会理事,江苏省射频识别技术标准化技术委员会委员。

原文:小熊派开源社区
作者:孙冬梅

推荐阅读

更多相关内容请关注小熊开源社区专栏。
推荐阅读
关注数
1
内容数
1
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息