[文章]基于OpenHarmony的智慧牧场

阅读量0
3
5

【项目名称】:基于OpenHarmony的智慧牧场

【负责人 】 :韩帅杰

解决方案介绍

【描述】

①我国对畜牧业的支持逐年增加,尤其是在农村地区,对于一定规模的畜牧业养殖户,政府会给予技术和资金支持,从而促进畜牧业的发展。畜牧业与科技的结合,使得畜牧业发展更加规范、更加专业。但是由于==资金和人力的投入不足,一般畜牧业养殖户的规模较小,投入与产出相对不合理==。
②我国畜牧业在==农业总产值中所占比例较小==,根据2016年国家统计局公布的数据来看,仅占总产值的23%,而在畜牧业较发达的爱尔兰、荷兰等地,畜牧业占农业总产值的一半以上。我国畜牧业结构仍旧比较单一,主要是猪、牛、羊、兔、鸡、鸭、鹅等。对于一些市场需求数量比较少,但质量要求较高的产品,往往很少养殖,造成畜牧业整体发展不均衡,导致==畜牧业的市场风险大、价格浮动幅度大==。

本项目提出了一种智慧农业解决方案

利用OpenHarmony技术解决联合国17项可持续发展目标中的多项挑战:==2、没有饥饿;7、经济适用的清洁能源;8、体面工作和经济增长;9、产业、创新和基础设施;11、可持续城市和社区;12、负责任消费和生产;14、气候行动;15、陆地生物==。

前言

本项目包含技术有OpenHarmony设备端开发的HarmonyOs应用开发
设备端实现功能
==生物姿态检测、生物户外精准定位、生物心率检测、设备连接华为云上传数据、室内环境调节(温湿度、光强、水量、食量、图像传输)==
应用端实现功能
==接收云端数据,APP界面显示==

开发工程时间表

axisFormat  %m/%d
title 项目开发流程
section 项目确定
    需求分析       :a1, 2022-07-10, 4d
    可行性报告     :after a1, 5d
    概念验证       : 4d
section 项目实施
    概要设计      :2022-07-20 , 4d
    详细设计      :2022-07-24, 9d
    编程          :2022-08-01, 5d
    测试          :2022-08-06, 5d
section 发布验收
    发布: 2d
    验收: 5d

效果展示

应用场景

我国对畜牧业的支持逐年增加,尤其是在农村地区,对于一定规模的畜牧业养殖户,政府会给予技术和资金支持,从而促进畜牧业的发展。畜牧业与科技的结合,使得畜牧业发展更加规范、更加专业。但是由于资金和人力的投入不足,一般畜牧业养殖户的规模较小,投入与产出相对不合理。
我国畜牧业在农业总产值中所占比例较小,根据2016年国家统计局公布的数据来看,仅占总产值的23%,而在畜牧业较发达的爱尔兰、荷兰等地,畜牧业占农业总产值的一半以上。我国畜牧业结构仍旧比较单一,主要是猪、牛、羊、兔、鸡、鸭、鹅等。对于一些市场需求数量比较少,但质量要求较高的产品,往往很少养殖,造成畜牧业整体发展不均衡,导致畜牧业的市场风险大、价格浮动幅度大。

一、实现步骤

1.1 设备端开发

Schematic_智慧牧场.png

1.1.1 准备工作

==开发设计所涉及知识==:包括Hi3861的WiFi操作、AP模式、STA模式、按键功能、网络编程、JSON数据格式、I2C通信、UART通信、ADC读取。
==开发设计所涉及模块==:包含六轴陀螺仪、AD8232模块、SIM808模块、42步进电机、AHT20模块、BH1750模块、ESP32—CAM、SG90舵机、0.96寸OLED、水泵、水位传感器、直流电机。

1.1.2 环境搭建

参考之前发文:环境搭建

1.1.3 代码设计

1.1.3.1 生物姿态检测
1.1.3.1.1 查找论文

==Keywords: Dip angle; MPU6050;==
image.png

1.1.3.1.2 理论知识
(1) 摘要

使⽤==MPU6050检测倾⻆==的⽅法,并设计了硬件电路。为了使检测⻆度更加准确,软件编程中采⽤了==卡尔曼滤波算法==,可以有效去除⼲扰,使测量精度更⾼。在测试中搭建了实验平台,可以准确测量⽔平⻆和垂直⻆,该设计可⽤于物体的倾⻆检测。它具有实⽤价值。

(2) 介绍

MPU6050 是⼀款带有 ==3 轴陀螺仪==和 ==3 轴加速度计==的九轴运动传感器。并且⼀个可扩展的数字运动处理器DMP、==IIC==或SPI接⼝可以连接其他传感器,输出为9轴信号。
==⻆度检测==在⼯业⽣产和实践中有很多应⽤,如检测物体的⽔平⻆或倾斜⻆,可以使平衡⻋或四旋翼⻜行器,来检测物体的姿态,即空间⻆。
==⻆度检测==也可以⽤来检测==生物跌倒==,今年以来,随着社会的发展,我国逐渐进⼊老龄化,老年⼈⼝不断增加。人力成本逐渐增加,所以电⼦产品的检测产⽣了跌倒,减少人工监督成本。==传感器前端为MPU6050==。

(3) MPU6050简介

MPU6050 引脚图如图1 所⽰。图VDD 供电引脚由3.3V 供电,CS 为⽚选信号。使⽤ SPI 或 ==IIC 接⼝==进行数据传输。图2是MPU6050⻆度
检测的==三维图==。
image.png
image.png

MPU6050有==3个16位ADC==,分别采集3轴的加速度值或陀螺值,转换成数字输出。陀螺仪测量范围为==正负250度、正负500度、正负
1000度、正负2000度==,加速度计测量范围为==+2G、+4G、+8g、+16g==。⽚上1MB FIFO,可⽤于数据缓存。串行通讯接⼝,IIC速率可达400K,
SPI速率可达1M。DMP陀螺SPI接⼝内部的数字运动可降低数据融合的复杂度,⻆度输出值准确。

(4)软件程序设计

根据数据手册中的时序图
image.png
在VS CODE 下使⽤C 编程语⾔。完成MPU6050的初始化,然后调⽤MPU6050ReadAcc;函数输出对应的⻆度。以下部分代码:

/***************************************************************
  * 函数功能: 读取MPU6050的加速度数据
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明: 无
***************************************************************/ 
void MPU6050ReadAcc(short *accData)
{
    uint8_t buf[6];
    MPU6050_ReadData(MPU6050_ACC_OUT, buf, 6);
    accData[0] = (buf[0] << 8) | buf[1];
    accData[1] = (buf[2] << 8) | buf[3];
    accData[2] = (buf[4] << 8) | buf[5];
}

22C11C214DF6AB45FA5ABD6F7DD34787.jpg
image.png

1.1.3.2 生物心率检测

既然人类有可穿戴设备,那么生物也可以有!我们暂时采用AD8232模块进行采集,后续有更合适的话我们将进行跟进。

1.1.3.2.1 查找论文

==Keywords: electrocardiograph; AD8232;==
image.png

(1) 摘要

一种以AD8232单片机为模拟前端的便携式心电图仪的设计。从制造商用于测试电路配置的AFE芯片评估板开始,将开源硬件和软件组件集成到试验板原型中。最终,定制印刷电路板(PCB)被生产出来。该原型需要将微芯片安装在SMD-to-DIP适配器上,以便使用HI3861微控制器以及数据记录器和串口进行测试。

(2) 介绍

医疗设备的发展是一个具有重大相关性的研究领域,因为不断需要创新和改善医疗保健。生物使用的医疗设备的增长趋势有助于通过使用智能手机、监控传感器和注册、传输和存储用户数据的软件应用程序等技术解决这一问题,生物数据可随时访问。移动健康可穿戴设备市场发展非常迅速,消费者需要更精确的电池驱动移动设备。

(3) 硬件部分

使用HI3861微控制器将AD8232芯片提供的模拟心电信号数字化,ADC转换器配置为10位分辨率,即5V/1024=0.00488V的LSB。
3.1 引脚介绍
image.png

image.png

(4)程序设计
/***** 获取电压值函数 *****/
static float GetVoltage(void)
{
    unsigned int ret;
    unsigned short data;

    ret = AdcRead(WIFI_IOT_ADC_CHANNEL_5, &data, WIFI_IOT_ADC_EQU_MODEL_8, WIFI_IOT_ADC_CUR_BAIS_DEFAULT, 0xff);
    if (ret != WIFI_IOT_SUCCESS)
    {
        printf("ADC Read Fail\n");
    }

    return (float)data * 1.8 * 4 / 4096.0;
}

42ABA6902D2D9BD3449360185A1BBA2E.jpg

1.1.3.3 GPS和GSM的跟踪系统设计

本系统设计用到了SIM808模块中的==GSM==、==GPS==功能

1.1.3.3.1 查找论文

==Keywords : Global Positioning System, Global System for Mobile communication , Tracking , microcontroller==
image.png

(1) 摘要

该系统采用基于全球移动通信系统(GSM)技术和GPS技术的嵌入式系统。该系统安装在生物中。接口GSM模块连接到HI3861。生物跟踪系统在牧场大环境中变得越来越重要,并且比其他系统更安全。现在,牧场中动物丢失正在迅速增加,有了这一点,我们可以很好地控制它。该系统旨在设计一种跟踪装置,该装置使用全球定位系统(GPS)确定生物的精确位置,并使用GSM调制解调器将该信息传输给用户。该系统提供以下功能:a)位置信息,b)使用短信进行实时跟踪

(2) 介绍

基于GPS和GSM的系统是最重要的系统之一,它集成了GSM和GPS技术。由于GSM和GPS系统的许多应用以及全世界数百万人对其的广泛使用,这是必要的。基于GPS和GSM的跟踪系统是一种利用全球定位系统(GPS)来确定其所连接生物的精确位置的系统。所提出的系统成本低、可靠,并具有精确跟踪功能。当大量物体或生物散布在地面上时,牧场管理员往往发现很难跟踪正在发生的事情。需要一个系统来确定物体在任何给定时间的位置和行驶的距离。此外,在用户的生物中需要跟踪系统是为了防止任何类型的盗窃,因为牧场管理员可以使用跟踪报告来定位被盗生物。 全球移动通信系统(GSM)和基于GPS的跟踪系统将提供有效、实时的车辆位置报告。基于GPS和GSM的跟踪系统提供了有关生物位置的所有规范。该系统使用来自GPS卫星的地理位置和时间信息。

(3)硬件设计

==3.1 GPS调制解调器==
GPS模块用于导航、定位、计时和其他目的。它由美国政府维护,任何人都可以通过GPS接收器自由访问。全球定位系统项目始于1973年,旨在克服以往导航系统的局限性,整合了20世纪60年代的大量分类工程设计研究。GPS由美国国防部(USDOD)创建和实施,最初由24颗卫星运行。它于1994年全面投入使用。
3.1.1 GPS提供以下信息:
1)==信息传输时间==
2) ==当时的位置==
==3.2 GSM调制解调器==
GSM调制解调器用于向移动用户发送数据和从移动用户接收数据。GSM调制解调器是与GSM无线网络一起工作的无线调制解调器。GSM的行为类似于拨号调制解调器。它们之间的主要区别在于拨号调制解调器通过固定电话线发送和接收数据,而GSM通过无线电波发送和接收。GSM调制解调器的工作基于命令,命令始终以==AT开头==(表示注意),以==<CR>字符结束==,例如拨号命令是ATD<number>;ATD7814629081;这里,拨号命令以分号(;)结束。在HI3861的帮助下,该AT命令被提供给GSM调制解调器。GSM调制解调器在MAX 232 IC的帮助下与微控制器串行连接。GSM指定的频率范围为1850到1990 MHz(移动台到基站)
==3.3 HI3861==
该系统使用AT巨型微控制器。它是一个8位RISC单片机。它由32 KB的闪存程序存储器、2 KB的内部SRAM和1024字节的EEPROM组成。在该系统中,它用于同步GSM和GPS的操作。GPS连续向微控制器发送位置数据,即车辆位置的纬度和经度,而GSM从微控制器发送和接收数据。GPS调制解调器连续提供许多参数作为输出,但只有NMEA(国家海洋电子协会)数据被读取并==显示在OLED上==。将相同的数据发送给移动用户,以便可以知道车辆的确切位置。用户的移动号码存储在EEPROM中。

(4) 程序设计

软件编程是用C语言完成的。GPS从卫星接收的数据(坐标)在软件中定义。解码NMEA(国家海洋电子协会)协议是开发该软件的主要目的。软件程序中应包含用户的手机号码,以便从我们在GSM调制解调器中使用的SIM卡接收位置值。NMEA协议由一组ASCII字符集的消息组成。GPS接收数据并以ASCII逗号分隔的消息字符串的形式显示。$'在每条消息的开头使用符号。位置(纬度和经度)的格式为ddmm。mmmm(度数分钟和十进制分钟)。软件协议由GGA(全球定位系统固定数据)和GLL(地理位置-纬度/经度)组成。但在这个系统中,我们只使用GGA。系统流程图如下所示:
image.png
==具体实现==

/* input:AT+CGNSINF Command Response
* output:struct GGPS_DATA
*/
static void GPS_CGNSINF_Analyze(char *origin, GGPS_DATA *gpsdata)
{
    int counter = 0;
    char tmp[150] = {0};
    char *lptr = NULL;
    char *localptr = NULL;

    lptr = (char *)strstr(origin, "+CGNSINF");
    if (lptr == NULL)
    {
        return;
    } else {
        lptr += 10;
    }
        

    while (*lptr != '\0')
    {
        if (*lptr == ',' && *(lptr + 1) == ',')
        {
            tmp[counter] = *lptr;
            counter++;
            tmp[counter] = '0';
        } else if (*lptr == '\r' && *(lptr + 1) == '\n' && counter < 148)
        {
            tmp[counter] = '0';
            tmp[counter + 1] = ',';
            tmp[counter + 2] = 0;
            break;
        } else {
            tmp[counter] = *lptr;
        }
        lptr++;
        counter++;

        /* avoid array out of range */
        if (counter >= GNSINF_MSG_MAX_LEN){
            return;
        }
            
    }
    /* Clear struct data */
    memset(gpsdata, 0, sizeof( GGPS_DATA));

    localptr = (char *)strtok(tmp, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->GNSSrunstatus, localptr, sizeof(gpsdata->GNSSrunstatus));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->Fixstatus, localptr, sizeof(gpsdata->Fixstatus));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->UTCdatetime, localptr, sizeof(gpsdata->UTCdatetime));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->latitude, localptr, sizeof(gpsdata->latitude));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->logitude, localptr, sizeof(gpsdata->logitude));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->altitude, localptr, sizeof(gpsdata->altitude));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->speedOTG, localptr, sizeof(gpsdata->speedOTG));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->course, localptr, sizeof(gpsdata->course));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->fixmode, localptr, sizeof(gpsdata->fixmode));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->Reserved1, localptr, sizeof(gpsdata->Reserved1));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->HDOP, localptr, sizeof(gpsdata->HDOP));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->PDOP, localptr, sizeof(gpsdata->PDOP));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->VDOP, localptr, sizeof(gpsdata->VDOP));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->Reserved2, localptr, sizeof(gpsdata->Reserved2));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->satellitesinview, localptr, sizeof(gpsdata->satellitesinview));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->GNSSsatellitesused, localptr, sizeof(gpsdata->GNSSrunstatus));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->GLONASSsatellitesused, localptr, sizeof(gpsdata->GLONASSsatellitesused));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->Reserved3, localptr, sizeof(gpsdata->Reserved3));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->CN0max, localptr, sizeof(gpsdata->CN0max));

    localptr = (char *)strtok(NULL, ",");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->HPA, localptr, sizeof(gpsdata->HPA));

    localptr = (char *)strtok(NULL, "\r\n");
    if (localptr == NULL)
    {
        return;
    }
    strncpy(gpsdata->VPA, localptr, sizeof(gpsdata->VPA));
}

static void GsmCheckRingAndHanupMessage(void)
{
    if (strstr(g_uart_buff, "RING") != NULL)
    {
        printf("ring.\n");
        if (GsmGetConnectSts() == false)
        {
            GsmSetRingSts(true);
        }
    }
    if (strstr(g_uart_buff, "NO CARRIER") != NULL)
    {
        printf("hang up.\n");

        GsmSetHungUpSts(true);

        if (GsmGetConnectSts() == true)
        {
            GsmSetConnectSts(false);
        }
    }
}

static uint32_t GsmSendCmd(char *cmd, int len)
{
    if (cmd == NULL || len <= 0)
    {
        return HI_ERR_FAILURE;
    }

    uint32_t ret = HI_ERR_FAILURE;
    static uint32_t count = 0;
    uint8_t *uart_buff_ptr = g_uart_buff;

    ret = hi_uart_write(DEMO_UART_NUM, (hi_u8 *)cmd, len);
    if (ret == HI_ERR_FAILURE)
    {
        return HI_ERR_FAILURE;
    }
    printf(" SendData:len:%d,cmd:%s.\n", len, cmd);

    while (g_uartController.isReadBusy)
    {
        count++;
        if (count > UART_WAIT_COUNT_MAX)
        {
            break;
        }
        
    }

    if (g_uartController.isReadBusy)
    {
        printf("GsmSendCmd hi_uart_read busy return");
        return HI_ERR_FAILURE;
    }
    if (!g_uartController.isReadBusy){
        usleep(100000); /* sleep 100ms */
    }
    
    g_uartController.isReadBusy = true;
    g_ReceivedDatalen = hi_uart_read(DEMO_UART_NUM, uart_buff_ptr, UART_BUFF_SIZE);

    if (g_ReceivedDatalen > 0)
    {
        printf(" rcvData len:%d,msg:%s.\n", g_ReceivedDatalen, g_uart_buff);
        if (strstr(g_uart_buff, "OK") != NULL)
        {
            GsmCheckRingAndHanupMessage();
            memset(g_uart_buff, 0, sizeof(g_uart_buff));
            g_ReceivedDatalen = 0;
            g_uartController.isReadBusy = false;
            return HI_ERR_SUCCESS;
        }
        else
        {
            printf(" received error cmd\r\n");
            GsmCheckRingAndHanupMessage();
            memset(g_uart_buff, 0, sizeof(g_uart_buff));
            g_ReceivedDatalen = 0;
            g_uartController.isReadBusy = false;
            return HI_ERR_FAILURE;
        }
    }
    else
    {
        g_uartController.isReadBusy = false;
        printf(" SendCmd no cmd return!\r\n");
        return HI_ERR_FAILURE;
    }

    return HI_ERR_SUCCESS;
}


uint32_t GpsGetLocation(GGPS_INFO *gpsInfo)
{
    uint32_t ret = HI_ERR_FAILURE;
    static uint32_t count = 0;
    uint8_t *uart_buff_ptr = g_uart_buff;

    ret = hi_uart_write(DEMO_UART_NUM, (hi_u8 *)"AT+CGNSINF\r\n", strlen("AT+CGNSINF\r\n"));
    if (ret == HI_ERR_FAILURE)
    {
        return NULL;
    }

    while (g_uartController.isReadBusy)
    {
        count++;
        if (count > UART_WAIT_COUNT_MAX)
        {
            break;
        }
        usleep(100000); /* sleep 100ms */
    }

    if (g_uartController.isReadBusy)
    {
        printf("GpsGetLocation hi_uart_read busy return");
        return HI_ERR_FAILURE;
    }else{
        usleep(100000); /* sleep 100ms */
    }

    g_uartController.isReadBusy = true;
    g_ReceivedDatalen = hi_uart_read(DEMO_UART_NUM, uart_buff_ptr, UART_BUFF_SIZE);

    if (g_ReceivedDatalen > 0)
    {
        printf(" rcvData len:%d,msg:%s.\n", g_ReceivedDatalen, g_uart_buff);

        uint8_t *strLocation = (uint8_t *)strstr(g_uart_buff, "+CGNSINF: 1,1");

        if (strLocation != NULL)
        {
            GGPS_DATA gpsData;
            GPS_CGNSINF_Analyze((char *)g_uart_buff, &gpsData);

            printf("latitude:%s.\n", gpsData.latitude);
            printf("logitude:%s.\n", gpsData.logitude);

            memcpy_s(gpsInfo->UTCdatetime, sizeof(gpsInfo->UTCdatetime), gpsData.UTCdatetime, sizeof(gpsData.UTCdatetime));
            memcpy_s(gpsInfo->logitude, sizeof(gpsInfo->logitude), gpsData.logitude, sizeof(gpsData.logitude));
            memcpy_s(gpsInfo->latitude, sizeof(gpsInfo->latitude), gpsData.latitude, sizeof(gpsData.latitude));
            memcpy_s(gpsInfo->satellitesinview, sizeof(gpsInfo->satellitesinview), gpsData.satellitesinview, sizeof(gpsData.satellitesinview));

            GsmCheckRingAndHanupMessage();

            memset(g_uart_buff, 0, sizeof(g_uart_buff));
            g_ReceivedDatalen = 0;
            g_uartController.isReadBusy = false;

            return HI_ERR_SUCCESS;
        } else {
            GsmCheckRingAndHanupMessage();
            memset(g_uart_buff, 0, sizeof(g_uart_buff));
            g_ReceivedDatalen = 0;
            g_uartController.isReadBusy = false;
            return HI_ERR_FAILURE;
        }
    } else {
        printf(" SendCmd no cmd return!\r\n");
        g_uartController.isReadBusy = false;

        return HI_ERR_FAILURE;
    }
}

uint32_t GsmCallCellPhone(char *cellPhoneNumeber)
{
    uint32_t ret = HI_ERR_FAILURE;
    char sendCmd[32] = "";
    uint8_t cPhoneNumLength = strlen(cellPhoneNumeber);

    if (cPhoneNumLength < PHONE_NUMB_LEN)
    {
        return HI_ERR_FAILURE;
    }

    /* Send AT+CSQ. */
    strncpy(sendCmd, "AT+CSQ\r\n", strlen("AT+CSQ\r\n"));
    printf(" sendCmd=%s\n", sendCmd);
    ret = GsmSendCmd(sendCmd, strlen(sendCmd));
    if (ret == HI_ERR_FAILURE)
    {
        return HI_ERR_FAILURE;
    }
    memset(sendCmd, 0, strlen(sendCmd));

    /* Call cellPhone Number:ATD+cellPhoneNumber. */
    snprintf(sendCmd, sizeof(sendCmd), "ATD%s;\r\n", cellPhoneNumeber);
    printf(" sendCmd=%s\n", sendCmd);
    ret = GsmSendCmd(sendCmd, strlen(sendCmd));
    if (ret == HI_ERR_FAILURE)
    {
        return HI_ERR_FAILURE;
    }

    return HI_ERR_SUCCESS;
}

==效果图==
42B306CDA17CBC02B8AA59184B47DA31.jpg

1.1.3.4 室内管理系统

包含主要有温湿度、光照、水槽、投食控制、光伏发电、图传
关键词:
==THIC(温度和湿度独立控制)、空调系统、能源绩效、建筑节能==

1.1.3.4.1查找论文

image.png

(1) 摘要

==温度和湿度独立控制==(THIC)系统是一种通过不同方法分别调节==室内温度和湿度==的方法。由于这一区别,THIC空调系统比传统系统更能满足室内温度和湿度的调节要求,并显示出巨大的节能潜力。基于对已建立应用的分析,检查了THIC系统的能量性能。关键部件的性能和整个系统的能耗都表明,与传统系统相比,THIC系统在能源性能方面有显著改善。

(2) 介绍

==建筑能耗==约占中国总能耗的23%,随着经济和社会的持续快速发展,这一比例预计将增加。推进建筑节能减排具有重要的战略意义。与此同时,非住宅建筑在中国发展非常迅速。2008年,中国非住宅建筑总面积约为78亿平方米,每年新建约400-500万平方米。空调系统的能耗占非住宅建筑总能耗的很大比例,通常为20–60%。因此,降低空调系统的能耗是非住宅建筑节能的主要途径之一。
==太阳能光伏发电==是利用半导体界面的光生伏特效应,将光能直接转化而为电能的一种技术。太阳能电池的芯片是具有光电效应的半导体器件,以硅半导体材料为主(即单晶硅和多晶硅)。半导体中的 PN 结被光照射后,被吸收的光将高能级状态下的电子激发为自由电子,使其在晶体内沿各个方向移动,余下空穴(电子移动前的位置)。空穴也围绕晶体移动,带负电荷的自由电子在 N 结聚集,带正电荷的空穴在 P 结聚集,当外部产生闭合回路时,电流产生。
==源热泵系统==( Geothermal Heat Pump Sys—tem)在学术上的定义是指通过输入少量的高位能源(通常为电能),实现由低位能源向高位能源转移的热泵空气调节系统。其通俗的定义是指一种通过浅层土壤或地下水进行热交换的高效节能、零污染、供暖与制冷兼备且可提供生活用水的空气调节系统[12]。
土壤或地下水具有良好的储热性质,所以,在冬季低位热能可通过地源热泵对建筑物进行供暖,同时储存冷量,以备夏季制冷使用;而在夏季,建筑物中的热量通过地源热泵转移到地下,在对建筑物降温制冷的同时,也将冬季进行供暖的能量进行了储备。

(3)硬件设计

image.png
image.png

(4)程序设计

==光照强度部分==

/***************************************************************
* 函数名称: Start_BH1750
* 说    明: 启动BH1750
* 参    数: 无
* 返 回 值: 无
***************************************************************/
void Start_BH1750(void)
{
    WifiIotI2cData bh1750_i2c_data = {0};
    uint8_t send_data[1] = {0x10};
    bh1750_i2c_data.sendBuf = send_data;
    bh1750_i2c_data.sendLen = 1;
    I2cWrite(WIFI_IOT_I2C_IDX_1, (BH1750_Addr << 1) | 0x00, &bh1750_i2c_data);
}

==温湿度部分==

/***************************************************************
* 函数名称: Init_SHT30
* 说    明: 初始化SHT30,设置测量周期
* 参    数: 无
* 返 回 值: 无
***************************************************************/
void Init_SHT30(void)
{
    WifiIotI2cData sht30_i2c_data = {0};
    uint8_t send_data[2] = {0x22, 0x36};
    sht30_i2c_data.sendBuf = send_data;
    sht30_i2c_data.sendLen = 2;
    I2cWrite(WIFI_IOT_I2C_IDX_1, (SHT30_Addr << 1) | 0x00, &sht30_i2c_data);
}

==水位控制部分==

static float GetVoltage(void)
{
    unsigned int ret;
    unsigned short data;

    ret = AdcRead(WIFI_IOT_ADC_CHANNEL_2, &data, WIFI_IOT_ADC_EQU_MODEL_8, WIFI_IOT_ADC_CUR_BAIS_DEFAULT, 0xff);
    if (ret != WIFI_IOT_SUCCESS)
    {
        printf("ADC Read Fail\n");
    }

    return (float)data * 1.8 * 4 / 4096.0;
}

==图传部分==
**这部分用的ESP32官方提供的例程实现**

#include "esp_camera.h"
#include <WiFi.h>

#define CAMERA_MODEL_AI_THINKER

#include "camera_pins.h"

const char* ssid = "**";
const char* password = "123456789";

void startCameraServer();

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Serial.println();

  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  //init with high specs to pre-allocate larger buffers
  if(psramFound()){
    config.frame_size = FRAMESIZE_UXGA;
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

#if defined(CAMERA_MODEL_ESP_EYE)
  pinMode(13, INPUT_PULLUP);
  pinMode(14, INPUT_PULLUP);
#endif

  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }

  sensor_t * s = esp_camera_sensor_get();
  //initial sensors are flipped vertically and colors are a bit saturated
  if (s->id.PID == OV3660_PID) {
    s->set_vflip(s, 1);//flip it back
    s->set_brightness(s, 1);//up the blightness just a bit
    s->set_saturation(s, -2);//lower the saturation
  }
  //drop down frame size for higher initial frame rate
  s->set_framesize(s, FRAMESIZE_QVGA);

#if defined(CAMERA_MODEL_M5STACK_WIDE)
  s->set_vflip(s, 1);
  s->set_hmirror(s, 1);
#endif

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");

  startCameraServer();

  Serial.print("Camera Ready! Use 'http://");
  Serial.print(WiFi.localIP());
  Serial.println("' to connect");
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(10000);
}

==效果图==
B7F7F1D67522136BB556F8FA21891280.jpg
C6D641BE4886E5EF5D52E081397C7721.jpg

5E0817EA858A9654401978BC667AC48E.jpg
80037926073C84F38132A57DF2150DF9.jpg
63AFF4FB53107DA19A845A0065AF1846.jpg
BF8F7381726C7CF8D502FFD81629EEF9.jpg

1.1.3.5 手机APP配网
1.1.3.5.1 查找论文

Keywords
==access point selection, opportunistic connectivity,public networks, wireless networking==
image.png

(1) 摘要

AP选择是由物理层驱动的。所有通用操作系统当前使用的选择策略,用于简单地自动选择接入点

(2) 介绍

连接步骤:
1、Hi3861 上面有一个user按键,用户可以按下这个按钮,Hi386会进入 AP 模式
2、手机扫描 WIFI 列表:扫描到Hi3861的SSID(目前是“Hispark-WiFi-IoT”)连接该智能硬件设备,通过手机APP发送我们要连接的热点的ssid和密码
3、Hi3861通过 UDP 包获取配置信息,切换网络模式连接 WIFI 后配网完成

(3) 代码实现

ap_mode.c:主要实现AP模式,并实现一个简单的UDP服务器,获取手机APP传输过来的热点账号和密码。
sta_mode.c:实现连接配网的功能。
这里主要参考了连老师的文章:手机APP配网

1.1.3.6 MQTT连接华为云
1.1.3.6.1 程序设计

==(1)MQTT初始化==

void mqtt_init(mqtt_broker_handle_t* broker, const char* clientid);

初始化要连接到代理的信息
image.png

==(2) 写入username与password==

void mqtt_init_auth(mqtt_broker_handle_t* broker, const char* username, const char* password);

启用身份验证以连接到代理。
image.png

==(3) 建立TCP连接==
编写TCP连接函数

static int init_socket(mqtt_broker_handle_t *broker, const char *hostname, short port, int keepalive)
{
    int flag = 1;
    struct hostent *hp;

    // 创建套接字
    if((socket_id = socket(PF_INET, SOCK_STREAM, 0)) < 0)
        return -1;

    // 禁用Nagle算法
    if (setsockopt(socket_id, IPPROTO_TCP, 0x01, (char *)&flag, sizeof(flag)) < 0)
    {
        close_socket(&mqtt_broker);
        return -2;
    }

    // 查询主机IP启动
    hp = gethostbyname(hostname);
    if (hp == NULL )
    {
        close_socket(&mqtt_broker);
        return -2;
    }

    struct sockaddr_in socket_address;
    memset(&socket_address, 0, sizeof(struct sockaddr_in));
    socket_address.sin_family = AF_INET;
    socket_address.sin_port = htons(port);
    memcpy(&(socket_address.sin_addr), hp->h_addr, hp->h_length);

    // 连接套接字
    if((connect(socket_id, (struct sockaddr *)&socket_address, sizeof(socket_address))) < 0)
    {
        close_socket(&mqtt_broker);
        return -1;
    }

    // MQTT stuffs
    mqtt_set_alive(broker, mqtt_keepalive);
    broker->socketid = socket_id;
    broker->mqttsend = send_packet;
    return 0;
}

==(4) 建立MQTT连接==

int mqtt_connect(mqtt_broker_handle_t* broker);

image.png
==(5) 订阅MQTT==
编写订阅MQTT主题函数

static int subscribe_topic(char *topic)//订阅主题
{
    unsigned short msg_id = 0, msg_id_rcv = 0;
    int packet_lengthgth = 0;
	int ret = -1;
 
    if(topic == NULL) {
        return -1;
    }
    
    ret = mqtt_subscribe(&mqtt_broker, topic, &msg_id);
	if( ret == -1 ) {
		close_socket(&mqtt_broker);
		return -1;
	}
    packet_lengthgth = read_packet(MQTT_DEMO_READ_TIME_SEC, MQTT_DEMO_READ_TIME_US);
    if(packet_lengthgth < 0)
    {
        printf("Error(%d) on read packet!\n", packet_lengthgth);
        close_socket(&mqtt_broker);
        return -1;
    }
 
    if(MQTTParseMessageType(pcaket_buffer) != MQTT_MSG_SUBACK)
    {
        printf("SUBACK expected!\n");
        close_socket(&mqtt_broker);
        return -2;
    }
 
    msg_id_rcv = mqtt_parse_msg_id(pcaket_buffer);
	if(msg_id != msg_id_rcv)
	{
		printf("%d message id was expected, but %d message id was found!\n", msg_id, msg_id_rcv);
        close_socket(&mqtt_broker);
        return -3;
	}
 
    return 0;
}

==(6) 数据推送与解析==
采用cJSON封包与解包(使用W800 SDK功能包中cJSON实现),共有两个封包(一个设备属性上报,一个命令应答上报),一个解包解析IoT平台命令,其他不过多赘述具体详见华为IoTDA 设备接入文档: 设备Iot连接文档

/*************************打包发布请求*****************/
static int packPublishReq(char *jsonBuffer)
{
	cJSON *jsRet = NULL;
	cJSON *jsArray = NULL;
	int ackLen = 0;
	
	jsRet = cJSON_CreateObject();
	if(jsRet)
	{   
		jsArray = cJSON_CreateArray();
        cJSON_AddItemToObject(jsRet, "services", jsArray);
        {
            cJSON *arrayObj_1 = cJSON_CreateObject();
            cJSON_AddItemToArray(jsArray, arrayObj_1);
			cJSON_AddStringToObject(arrayObj_1, "service_id", "Temperature");

            cJSON *arrayObj_2 = cJSON_CreateObject();
            cJSON_AddItemToObject(arrayObj_1, "properties", arrayObj_2);
            cJSON_AddStringToObject(arrayObj_2, "temp", Temperature.temp);
            cJSON_AddStringToObject(arrayObj_2, "humi", Temperature.humi);
            cJSON_AddStringToObject(arrayObj_2, "led",  Temperature.ON_OFF);
            
            cJSON_AddStringToObject(arrayObj_1,"event_time", Temperature.timestamp);
        }
        char *databuf = cJSON_PrintUnformatted(jsRet);
		if(databuf) {
			if( jsonBuffer ) {
				ackLen = strlen(databuf);
				memcpy( jsonBuffer, databuf,ackLen);
			}
            tls_mem_free(databuf);
        }
        cJSON_Delete(jsRet); 
    }
    return ackLen;
}

==(7)数据应答==
按照华为云IoT设备平台命令下发文档需要将
下行中的request_id={request_id} 复制到上行中,只有这样下行与上行request_id相同才能保证平台命令数据下发成功任务

下行 $oc/devices/{device_id}/sys/commands/request_id={request_id}
上行:$oc/devices/{device_id}/sys/commands/response/request_id={request_id}

==(8) 华为IoT平台配置==
登录
设备接入华为云平台之前,需要在平台注册用户账号,华为云地址
参考文章


回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友