基于物联网设计的水稻田智能灌溉系统(STM32+华为云IOT)

news2024/11/15 20:31:58

一、项目介绍

随着科技的不断发展和人们生活水平的提高,农业生产也逐渐向智能化、高效化的方向发展。水稻作为我国主要的粮食作物之一,其生长过程中的灌溉管理尤为重要。传统的灌溉方式往往依赖于人工观察和控制,不仅效率低下,而且容易出现误差,无法满足现代农业生产的需求。

当前设计了一款基于STM32的水稻田智能灌溉系统。该系统能够通过水位传感器和温度传感器实时监测稻田的水位和水温,并根据设定的阈值自动控制水泵的开关,实现自动灌溉。同时,通过NBIOT模块将实时数据上传到华为云物联网云平台,用户可以通过手机APP远程监控和控制设备的运行,实现远程管理。相当于直接将水稻田搬到了云端,后期还会加入摄像头监控,加入图像处理,在家就可以了解到每一块稻田的生长情况。

整个系统的电源采用太阳能板供电。

整个系统的应用可以大大提高水稻灌溉的效率和准确性,减少人力资源的浪费,降低生产成本,并为农业生产提供更有力的技术保障。此外,系统还可以推广到其他领域的智能灌溉和控制中,具有广阔的市场应用前景。

image-20231012162206265

image-20231012162104696

二、设计需求

该系统通过水位传感器检测稻田的水位,根据预先设置的水位阈值,自动控制继电器开启水泵进行抽水灌溉,从而实现自动补充灌溉水。同时,温度传感器可以监测水温的变化,确保水温适宜。利用NBIOT模块实现与华为云物联网云平台的连接,将实时的水位和水温数据上传到云平台。

为了方便用户远程操作和监控,开发了手机APP。用户可以通过APP远程手动控制水泵的开关,实现远程操作。同时,手机APP可以实时显示设备上传的水温和水位数据,提供可视化的界面,方便用户监控农田状况。

主控芯片采用STM32F103C8T6,具有丰富的外设资源和良好的性能,能够满足系统的需求。水温检测方面采用DS18B20防水温度传感器,具有高精度和稳定性,可以准确地监测水温。NBIOT联网模块采用BC26,支持NB-IoT通信技术,能够实现与云平台的连接。云服务器采用华为云物联网服务器,提供稳定可靠的云端服务。手机APP采用Qt进行开发,可以在不同的平台上运行,并提供友好的用户界面和交互体验。

系统的功能主要包括以下几个方面:

(1)水位检测:系统需要通过水位传感器实时监测稻田的水位,并能够根据设定的阈值自动控制水泵的开关,实现自动灌溉。

(2)水温检测:系统需要通过温度传感器实时监测稻田的水温,并将数据传输到云平台进行远程监控。

(3)数据传输:系统需要通过NBIOT模块将实时水温和水位数据上传到华为云物联网云平台,实现远程监控和控制。

(4)远程控制:用户可以通过手机APP远程监控和控制设备的运行,包括水泵的开关和阈值的设置等。

(5)人机界面:手机APP需要提供友好的人机界面,能够实时显示设备上传的水温和水位数据,并提供相应的操作按钮。

在硬件选型方面,主要考虑了以下几个方面:

(1)主控芯片:选择了STM32F103C8T6作为主控芯片。STM32F103C8T6是一款基于ARM Cortex-M3内核的32位微控制器,具有高性能、低功耗、易于开发等优点。该芯片具有丰富的外设接口,能够满足本系统的需求。

(2)水位传感器:选择了防水型的水位传感器,以确保在潮湿环境下能够正常工作。该传感器具有高精度、高可靠性、低功耗等优点,能够满足本系统的需求。

(3)温度传感器:选择了DS18B20防水温度传感器,该传感器具有高精度、高可靠性、低功耗等优点,能够实时检测稻田的水温,并将数据传输到主控芯片。

(4)NBIOT模块:选择了BC26模块作为NBIOT通信模块。该模块具有低功耗、高稳定性、易于开发等优点,能够将实时数据上传到华为云物联网云平台。

(5)水泵:选择具有自动控制功能的水泵,能够根据主控芯片的控制信号自动开关。

三、华为云产品设备创建

这一章节主要是介绍华为云物联网云端产品与设备的创建流程。

3.1 开通物联网服务

地址: https://www.huaweicloud.com/product/iothub.html

image-20230801151136153

点击总览,查看接入信息。 我们当前设备准备采用MQTT协议接入华为云平台,这里可以看到MQTT协议的地址和端口号等信息。

image-20230925155200396

image-20230925155233810

总结:

端口号:   MQTT (1883)  
接入地址: e244e6efb9.st1.iotda-device.cn-north-4.myhuaweicloud.com

根据域名地址得到IP地址信息:

Microsoft Windows [版本 10.0.19045.3448]
(c) Microsoft Corporation。保留所有权利。

C:\Users\11266>ping e244e6efb9.st1.iotda-device.cn-north-4.myhuaweicloud.com

正在 Ping e244e6efb9.st1.iotda-device.cn-north-4.myhuaweicloud.com [117.78.5.125] 具有 32 字节的数据:
来自 117.78.5.125 的回复: 字节=32 时间=41ms TTL=94
来自 117.78.5.125 的回复: 字节=32 时间=44ms TTL=94
来自 117.78.5.125 的回复: 字节=32 时间=43ms TTL=94
来自 117.78.5.125 的回复: 字节=32 时间=42ms TTL=94

117.78.5.125 的 Ping 统计信息:
    数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
    最短 = 41ms,最长 = 44ms,平均 = 42ms

C:\Users\11266>

image-20230925155314730

MQTT协议接入端口号有两个,1883是非加密端口,8883是证书加密端口,单片机无法加载证书,所以使用1883端口比较合适。 接下来的ESP8266就采用1883端口连接华为云物联网平台。

3.2 创建产品

(1)创建产品

点击产品页,再点击创建产品。

image-20230925155450476

(2)填写产品信息

根据自己产品名字填写。

(3)品创建成功

(4)添加自定义模型

产品创建完成之后,点击进入产品详情页面,翻到最下面可以看到模型定义。

这个模型就是定义自己设备接下来需要向服务器上传那些数据类型。根据自己的数据类型进行编写。

3.3 添加设备

产品是属于上层的抽象模型,接下来在产品模型下添加实际的设备。添加的设备最终需要与真实的设备关联在一起,完成数据交互。

(1)注册设备

image-20230925160050686

(2)根据自己的设备填写

设备标识码、密码这些根据自己情况认真填写。

(3)保存设备信息

创建完毕之后,点击保存并关闭,得到创建的设备密匙信息。该信息在后续生成MQTT三元组的时候需要使用。

创建之后,得到的设备信息如下:

{
    "device_id": "65113d05a559fd7cd41435f8_lock1",
    "secret": "12345678"
}

(4)设备创建完成

可以点击设备进入到设备详情页面。

3.4 MQTT协议主题订阅与发布

(1)主题订阅格式

帮助文档地址:https://support.huaweicloud.com/devg-iothub/iot_02_2200.html

image-20230801151350067

image-20230801151355027

对于设备而言,一般会订阅平台下发消息给设备 这个主题。

设备想接收平台下发的消息,就需要订阅平台下发消息给设备 的主题,订阅后,平台下发消息给设备,设备就会收到消息。

如果设备想要知道平台下发的消息,需要订阅上面图片里标注的主题。

以当前设备为例,最终订阅主题的格式如下:
$oc/devices/{device_id}/sys/messages/down

最终的格式:
$oc/devices/65113d05a559fd7cd41435f8_lock1/sys/messages/down

(2)主题发布格式

对于设备来说,主题发布表示向云平台上传数据,将最新的传感器数据,设备状态上传到云平台。

这个操作称为:属性上报。

帮助文档地址:https://support.huaweicloud.com/api-iothub/iot_06_v5_3010.html

image-20230925160939978

根据帮助文档的介绍, 当前设备发布主题,上报属性的格式总结如下:

发布的主题格式:
$oc/devices/{device_id}/sys/properties/report
 
最终的格式:
$oc/devices/65113d05a559fd7cd41435f8_lock1/sys/properties/report
发布主题时,需要上传数据,这个数据格式是JSON格式。

上传的JSON数据格式如下:

{
  "services": [
    {
      "service_id": <填服务ID>,
      "properties": {
        "<填属性名称1>": <填属性值>,
        "<填属性名称2>": <填属性值>,
        ..........
      }
    }
  ]
}
根据JSON格式,一次可以上传多个属性字段。 这个JSON格式里的,服务ID,属性字段名称,属性值类型,在前面创建产品的时候就已经介绍了,不记得可以翻到前面去查看。

根据这个格式,组合一次上传的属性数据:
{"services": [{"service_id": "lock","properties":{"lock":1}}]}

3.5 MQTT三元组

MQTT协议登录需要填用户ID,设备ID,设备密码等信息,就像我们平时登录QQ,微信一样要输入账号密码才能登录。MQTT协议登录的这3个参数,一般称为MQTT三元组。

接下来介绍,华为云平台的MQTT三元组参数如何得到。

(1)MQTT服务器地址

要登录MQTT服务器,首先记得先知道服务器的地址是多少,端口是多少。

帮助文档地址:https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-portal/home

MQTT协议的端口支持1883和8883,它们的区别是:8883 是加密端口更加安全。但是单片机上使用比较困难,所以当前的设备是采用1883端口进连接的。

根据上面的域名和端口号,得到下面的IP地址和端口号信息: 如果设备支持填写域名可以直接填域名,不支持就直接填写IP地址。 (IP地址就是域名解析得到的)

华为云的MQTT服务器地址:117.78.5.125
域名:e244e6efb9.st1.iotda-device.cn-north-4.myhuaweicloud.com
华为云的MQTT端口号:1883

注意! 具体要看这里:

image-20230925161111346

(2)生成MQTT三元组

华为云提供了一个在线工具,用来生成MQTT鉴权三元组: https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/

打开这个工具,填入设备的信息(也就是刚才创建完设备之后保存的信息),点击生成,就可以得到MQTT的登录信息了。

下面是打开的页面:

image-20230801151418248

填入设备的信息: (上面两行就是设备创建完成之后保存得到的)

image-20230925161154059

得到三元组之后,设备端通过MQTT协议登录鉴权的时候,填入参数即可。

ClientId 65113d05a559fd7cd41435f8_lock1_0_0_2023092508
Username 65113d05a559fd7cd41435f8_lock1
Password 1a3e7f486aa551bca7b6ff5c19c29d2006e940ec1f98ab416e10be1288106953

3.6 模拟设备登录测试

经过上面的步骤介绍,已经创建了产品,设备,数据模型,得到MQTT登录信息。 接下来就用MQTT客户端软件模拟真实的设备来登录平台。测试与服务器通信是否正常。

(1)填入登录信息

打开MQTT客户端软件,对号填入相关信息(就是上面的文本介绍)。然后,点击登录,订阅主题,发布主题。

image-20230925161345360

(2)打开网页查看

完成上面的操作之后,打开华为云网页后台,可以看到设备已经在线了。

点击详情页面,可以看到上传的数据。

到此,云平台的部署已经完成,设备已经可以正常上传数据了。

四、硬件选型

下面是为了验证设计而选型的相关的硬件模型,能够实现最终的设计效果。 模型设计最终验证成功之后,重新画板选型硬件一体化设计产品就方便多了。

【1】STM32开发板

image-20231012155204914

【2】水泵电机

image-20231012155515636

【3】BC26-NBIOT

image-20231012155624706

【4】水温检测

image-20231012155721909

【5】水位检测

链接:https://item.taobao.com/item.htm?spm=a21n57.1.0.0.636b523cIyIKxC&id=735112974467&ns=1&abbucket=19#detail

image-20231012155816680

【6】太阳能板

image-20231012161934961

五、代码设计

5.1 主函数代码框架

#include "stm32f10x.h"
#include "ds18b20.h"
#include "nbiot.h"

// 定义水位传感器引脚和继电器引脚
#define WATER_LEVEL_PIN   GPIO_Pin_0
#define WATER_LEVEL_PORT  GPIOA
#define RELAY_PIN         GPIO_Pin_1
#define RELAY_PORT        GPIOA

// 定义温度传感器引脚
#define TEMP_SENSOR_PIN   GPIO_Pin_2
#define TEMP_SENSOR_PORT  GPIOA

// 定义NBIOT模块和云服务器相关参数
#define NBIOT_RX_PIN      GPIO_Pin_3
#define NBIOT_RX_PORT     GPIOA
#define NBIOT_TX_PIN      GPIO_Pin_4
#define NBIOT_TX_PORT     GPIOA
#define SERVER_ADDRESS    "your_server_address"
#define DEVICE_ID         "your_device_id"
#define DEVICE_KEY        "your_device_key"

// 定义APP开关水泵控制命令
#define CMD_PUMP_ON       '1'
#define CMD_PUMP_OFF      '0'

// 初始化函数
void init();
// 获取水位值
uint8_t getWaterLevel();
// 控制水泵
void controlPump(uint8_t state);
// 获取温度值
float getTemperature();
// 更新数据到华为云服务器
void updateDataToCloud(float temperature, uint8_t waterLevel);
// 处理NBIOT模块接收到的命令
void handleNBIOTCommand(char cmd);

int main(void) {
  // 初始化
  init();

  while (1) {
    // 获取水位值
    uint8_t waterLevel = getWaterLevel();
    // 控制水泵
    controlPump(waterLevel);
    // 获取温度值
    float temperature = getTemperature();
    // 更新数据到华为云服务器
    updateDataToCloud(temperature, waterLevel);
    // 处理NBIOT模块接收到的命令
    char command = receiveNBIOTCommand();
    handleNBIOTCommand(command);
  }
}

void init() {
  // 初始化GPIO和相关外设
  // ...

  // 初始化DS18B20温度传感器
  DS18B20_Init(TEMP_SENSOR_PIN);

  // 初始化NBIOT模块
  NBIOT_Init(NBIOT_RX_PIN, NBIOT_TX_PIN, SERVER_ADDRESS, DEVICE_ID, DEVICE_KEY);
}

uint8_t getWaterLevel() {
  // 读取水位传感器的值
  // ...
}

void controlPump(uint8_t state) {
  if (state == 0) {
    // 关闭继电器,停止水泵
    GPIO_ResetBits(RELAY_PORT, RELAY_PIN);
  } else {
    // 打开继电器,启动水泵
    GPIO_SetBits(RELAY_PORT, RELAY_PIN);
  }
}

float getTemperature() {
  // 读取温度传感器的值
  // ...
}

void updateDataToCloud(float temperature, uint8_t waterLevel) {
  // 将温度和水位值上传到华为云服务器
  // ...
}

void handleNBIOTCommand(char cmd) {
  if (cmd == CMD_PUMP_ON) {
    // 手机APP发送打开水泵的命令
    controlPump(1);
  } else if (cmd == CMD_PUMP_OFF) {
    // 手机APP发送关闭水泵的命令
    controlPump(0);
  }
}

5.2 BC26模块代码

#include <stm32f10x.h>
#include <usart.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <delay.h>

// 定义BC26串口
#define BC26_USART USART1

// 定义MQTT服务器信息
#define MQTT_SERVER_IP "192.168.1.100"
#define MQTT_SERVER_PORT "1883"
#define MQTT_USERNAME "username"
#define MQTT_PASSWORD "password"
#define MQTT_CLIENT_ID "client_id"

// 串口发送字符串
void sendString(const char *str) {
    while (*str) {
        USART_SendData(BC26_USART, *str++);
        while (USART_GetFlagStatus(BC26_USART, USART_FLAG_TC) == RESET);
    }
}

// 延时等待响应
bool waitForResponse(const char *response, uint32_t timeout) {
    uint32_t startTime = millis();
    uint32_t elapsedTime = 0;
    uint8_t rxData = 0;
    uint8_t responseIndex = 0;

    while (elapsedTime < timeout) {
        if (USART_GetFlagStatus(BC26_USART, USART_FLAG_RXNE) != RESET) {
            rxData = USART_ReceiveData(BC26_USART);
            if (rxData == response[responseIndex]) {
                responseIndex++;
                if (response[responseIndex] == '\0') {
                    return true; // 收到完整响应
                }
            } else {
                responseIndex = 0; // 重置响应索引
            }
        }

        elapsedTime = millis() - startTime;
    }

    return false; // 超时未收到完整响应
}

// 初始化BC26模块
bool initBC26Module() {
    sendString("AT+CFUN=1\r\n"); // 设置BC26模块为全功能模式
    if (!waitForResponse("OK", 1000)) {
        return false;
    }

    sendString("AT+CIMI\r\n"); // 获取SIM卡的IMSI号码
    if (!waitForResponse("OK", 1000)) {
        return false;
    }

    sendString("AT+QCFG=\"nwscanmode\",0,1\r\n"); // 设置网络扫描模式为自动模式
    if (!waitForResponse("OK", 1000)) {
        return false;
    }

    sendString("AT+QCFG=\"iotopmode\",2,1\r\n"); // 设置连接模式为Cat M1/NB1优先模式
    if (!waitForResponse("OK", 1000)) {
        return false;
    }

    sendString("AT+QICSGP=1,1,\"your_apn\",\"\",\"\",1\r\n"); // 配置APN参数,根据具体网络运营商设置
    if (!waitForResponse("OK", 1000)) {
        return false;
    }

    sendString("AT+QIACT=1\r\n"); // 激活PDP上下文
    if (!waitForResponse("OK", 10000)) {
        return false;
    }

    sendString("AT+QMTOPEN=0,\"");
    sendString(MQTT_SERVER_IP); // 连接MQTT服务器
    sendString("\",");
    sendString(MQTT_SERVER_PORT);
    sendString("\r\n");
    if (!waitForResponse("+QMTOPEN: 0,0", 20000)) {
        return false;
    }

    sendString("AT+QMTCONN=0,\"");
    sendString(MQTT_CLIENT_ID); // 使用指定的Client ID连接MQTT服务器
    sendString("\",\"");
    sendString(MQTT_USERNAME);
    sendString("\",\"");
    sendString(MQTT_PASSWORD);
    sendString("\"\r\n");
    if (!waitForResponse("+QMTCONN: 0,0,0", 10000)) {
        return false;
    }

    return true; // 初始化成功
}

int main(void) {
    // 初始化系统时钟等

    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = 9600;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(BC26_USART, &USART_InitStructure);
    USART_Cmd(BC26_USART, ENABLE);

    initBC26Module();

    while (1) {
        // 执行其他操作或处理MQTT消息

    }
}

5.3 电机控制代码

#include <stm32f10x.h>
#include <stdbool.h>

// 定义电机引脚
#define MOTOR_PORT GPIOA
#define MOTOR_PIN GPIO_Pin_0

// 初始化电机引脚
void initMotor() {
    // 使能GPIO时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    // 初始化GPIO引脚
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = MOTOR_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(MOTOR_PORT, &GPIO_InitStructure);
}

// 控制电机状态
void setMotorState(bool enabled) {
    if (enabled) {
        GPIO_SetBits(MOTOR_PORT, MOTOR_PIN); // 电机打开
    } else {
        GPIO_ResetBits(MOTOR_PORT, MOTOR_PIN); // 电机关闭
    }
}

int main(void) {
    // 初始化系统时钟等

    initMotor();

    while (1) {
        // 控制电机状态
        setMotorState(true); // 打开电机

        // 延时一段时间

        setMotorState(false); // 关闭电机

        // 延时一段时间
    }
}

5.4 水位检测代码

#include <stm32f10x.h>
#include <stdio.h>
#include <delay.h>

// 定义ADC引脚和通道
#define ADC_PORT GPIOA
#define ADC_PIN GPIO_Pin_0
#define ADC_CHANNEL ADC_Channel_0

// ADC初始化
void initADC() {
    // 使能ADC时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

    // 初始化GPIO引脚
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = ADC_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(ADC_PORT, &GPIO_InitStructure);

    // 初始化ADC参数
    ADC_InitTypeDef ADC_InitStructure;
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // 独立模式
    ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 单通道扫描模式
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 连续转换模式
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 不用外部触发
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = 1; // 采样通道数量
    ADC_Init(ADC1, &ADC_InitStructure);

    // 配置ADC采样通道
    ADC_RegularChannelConfig(ADC1, ADC_CHANNEL, 1, ADC_SampleTime_55Cycles5);

    // 使能ADC
    ADC_Cmd(ADC1, ENABLE);

    // ADC校准
    ADC_ResetCalibration(ADC1);
    while (ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1));
}

// 读取ADC转换值
uint16_t readADC() {
    ADC_RegularChannelConfig(ADC1, ADC_CHANNEL, 1, ADC_SampleTime_55Cycles5);
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));

    return ADC_GetConversionValue(ADC1);
}

int main(void) {
    // 初始化系统时钟等

    initADC();

    while (1) {
        uint16_t adcValue = readADC();
        printf("ADC Value: %d\r\n", adcValue);

        // 进行水位传感器的处理和其他操作

        delay_ms(1000); // 延时1秒
    }
}

5.5 水温检测代码

#include <stm32f10x.h>
#include <delay.h>

// 定义温度传感器引脚
#define TEMPERATURE_PIN GPIO_Pin_1
#define TEMPERATURE_GPIO GPIOA

// 初始化温度传感器
void initTemperatureSensor() {
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitStructure.GPIO_Pin = TEMPERATURE_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 使用开漏输出模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    
    GPIO_Init(TEMPERATURE_GPIO, &GPIO_InitStructure);
}

// 发送复位信号
void resetSensor() {
    GPIO_SetBits(TEMPERATURE_GPIO, TEMPERATURE_PIN);
    delay_us(500);
    GPIO_ResetBits(TEMPERATURE_GPIO, TEMPERATURE_PIN);
    delay_us(500);
    GPIO_SetBits(TEMPERATURE_GPIO, TEMPERATURE_PIN);
    delay_us(500);
}

// 读取一位数据
uint8_t readBit() {
    uint8_t data = 0;
    GPIO_SetBits(TEMPERATURE_GPIO, TEMPERATURE_PIN);
    delay_us(2);
    GPIO_ResetBits(TEMPERATURE_GPIO, TEMPERATURE_PIN);
    delay_us(2);
    GPIO_SetBits(TEMPERATURE_GPIO, TEMPERATURE_PIN);
    delay_us(1);
    if (GPIO_ReadInputDataBit(TEMPERATURE_GPIO, TEMPERATURE_PIN)) {
        data = 1;
    }
    delay_us(60);
    return data;
}

// 读取一个字节数据
uint8_t readByte() {
    uint8_t byte = 0;
    for (int i = 0; i < 8; i++) {
        byte |= readBit() << i;
    }
    return byte;
}

// 写入一位数据
void writeBit(uint8_t bit) {
    GPIO_SetBits(TEMPERATURE_GPIO, TEMPERATURE_PIN);
    delay_us(2);
    GPIO_ResetBits(TEMPERATURE_GPIO, TEMPERATURE_PIN);
    delay_us(2);
    if (bit) {
        GPIO_SetBits(TEMPERATURE_GPIO, TEMPERATURE_PIN);
    }
    delay_us(60);
    GPIO_SetBits(TEMPERATURE_GPIO, TEMPERATURE_PIN);
}

// 写入一个字节数据
void writeByte(uint8_t byte) {
    for (int i = 0; i < 8; i++) {
        writeBit(byte & 0x01);
        byte >>= 1;
    }
}

// 获取温度值
float getTemperature() {
    uint8_t temperature[2];

    resetSensor();
    writeByte(0xCC); // 跳过ROM命令
    writeByte(0x44); // 启动温度转换命令
    delay_ms(800); // 等待转换完成

    resetSensor();
    writeByte(0xCC); // 跳过ROM命令
    writeByte(0xBE); // 读取温度值命令

    temperature[0] = readByte();
    temperature[1] = readByte();

    int16_t rawTemperature = (temperature[1] << 8) | temperature[0];
    float temp = rawTemperature * 0.0625;

    return temp;
}

int main(void) {
    // 初始化系统时钟等
    
    initTemperatureSensor();
    
    while (1) {
        float temperature = getTemperature();
        
        // 处理温度数据
        
    }
}

六、上位机开发

这篇主要介绍硬件端的设计、华为云物联网服务器端的设计。下一篇详细介绍WEB网页端、手机APP和微信小程序的开发。

image.png

image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1411578.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【AD-阻塞卡顿感-捕捉功能】移动布线、移动元器件时有很强的阻塞卡顿感,移动不到想要的地方

现象如下&#xff1a; 解决办法&#xff1a; 出现这种问题是因为AD的捕捉功能设置不对&#xff0c;我这边设置的是全部选择&#xff0c;所以出现了这种阻塞卡顿感。把选项全部取消&#xff0c;阻塞感就消失了。 取消后的现象&#xff1a; 至于原理上为什么会这样&#xff0…

区块链技术在教育领域的应用:Web3教育变革

随着Web3时代的来临&#xff0c;区块链技术在各个领域都展现出了巨大的潜力&#xff0c;而在教育领域&#xff0c;区块链的应用正引领着一场教育变革。本文将深入探讨区块链技术在教育领域的创新应用&#xff0c;以及这一应用如何推动Web3时代的教育变革。 1. 学历和成绩的去中…

C语言实现归并排序算法(附带源代码)

归并排序 把数据分为两段&#xff0c;从两段中逐个选最小的元素移入新数据段的末尾。 可从上到下或从下到上进行。 动态效果过程演示&#xff1a; 归并排序&#xff08;Merge Sort&#xff09;是一种分治算法&#xff0c;它将一个数组分为两个子数组&#xff0c;分别对这两个…

学习gin框架知识的小注意点

Gin框架的初始化 有些项目中 初始化gin框架写的是&#xff1a; r : gin.New() r.Use(logger.GinLogger(), logger.GinRecovery(true)) 而不是r : gin.Default() 为什么呢&#xff1f; 点击进入Default源码发现其实他也是new两个中间件&#xff0c;&#xff08;Logger&…

单调性的应用

1单调性 应用场景&#xff1a;常应用于双指针的进一步优化问题中含义&#xff1a;针对指针 i 1 > i i1>i i1>i一定有 j 1 > j j1>j j1>j或者 j 1 < j j1<j j1<j这样我们就可以利用该性质对算法进行进一步优化&#xff0c;避免一些不必要的遍历…

基于卡尔曼滤波的平面轨迹优化

文章目录 概要卡尔曼滤波代码主函数代码CMakeLists.txt概要 在进行目标跟踪时,算法实时测量得到的目标平面位置,是具有误差的,连续观测,所形成的轨迹如下图所示,需要对其进行噪声滤除。这篇博客将使用卡尔曼滤波,对轨迹进行优化。 优化的结果为黄色线。 卡尔曼滤波代码…

微信小程序元素/文字在横向和纵向实现居中对齐、两端对齐、左右对齐、上下对齐

元素对齐往往是新学者的一大困惑点&#xff0c;在此总结常用的各种元素和文字对齐方式以供参考&#xff1a; 初始显示 .wxml <view style"width: 100%;height: 500rpx; background-color: lightgray;"><view style"width: 200rpx;height:100rpx;bac…

STM32CubeMX教程29 USB_HOST - 使用FatFs文件系统读写U盘

目录 1、准备材料 2、实验目标 3、USB概述 3.1、USB协议 3.2、USB设备 3.3、USB接口 3.4、硬件原理 4、实验流程 4.0、前提知识 4.1、CubeMX相关配置 4.1.0、工程基本配置 4.1.1、时钟树配置 4.1.2、外设参数配置 4.1.3、外设中断配置 4.2、生成代码 4.2.0、配…

C++ day2 类 访问权限

1> 思维导图 2> 自己封装一个矩形类(Rect)&#xff0c;拥有私有属性:宽度(width)、高度(height)&#xff0c; 定义公有成员函数: 初始化函数:void init(int w, int h) 更改宽度的函数:set_w(int w) 更改高度的函数:set_h(int h) 输出该矩形的周长和面积函数:void s…

数据权限方案设计(后端)

有些BO模型放在bo包下用来装一些SQL语句查询返回&#xff0c;用于在Service层中进行处理业务逻辑,不会出现在Controller接口层的包装中。在持久层的基础组件中&#xff0c;我司业务种用的是Mysql5.7版本。在前后端开发使用的是分离技术&#xff0c;即前端使用流行的VUEElement-…

RISC-V常用汇编指令

RISC-V寄存器表&#xff1a; RISC-V和常用的x86汇编语言存在许多的不同之处&#xff0c;下面将列出其中部分指令作用&#xff1a; 指令语法描述addiaddi rd,rs1,imm将寄存器rs1的值与立即数imm相加并存入寄存器rdldld t0, 0(t1)将t1的值加上0,将这个值作为地址&#xff0c;取…

6家券商综合评级上升,12月券商App终端业务体验评测报告发布

随着移动金融服务的盛行&#xff0c;手机 App 炒股成为广大股民普遍的选择。股市行情变幻莫测&#xff0c;行情推送速度会影响到投资者的交易决策&#xff0c;委托下单与撤单等关键操作环节的响应性能又会极大影响投资者的收益。由此&#xff0c;行情数据的推送实时性和交易的快…

小程序系列--14.小程序分包

一、基础概念 1. 什么是分包 分包指的是把一个完整的小程序项目&#xff0c;按照需求划分为不同的子包&#xff0c;在构建时打包成不同的分包&#xff0c;用户在使用时按需进行加载。 2. 分包的好处 3. 分包前项目的构成 4. 分包后项目的构成 5. 分包的加载规则 6. 分包的…

vue3预览pdf文件的几种方法

vue3预览pdf集中方法 方法一&#xff1a; iframe&#xff1a;这种方法显示有点丑 <iframesrc"E:\\1.pdf"frameborder"0"style"width: 80%; height: 100vh; margin: auto; display: block"></iframe>方法二&#xff1a; 展示效果&…

Objective-C方法的声明实现及调用

1.无参数的方法 1)声明 a.位置&#xff1a;在interface括弧的外面 b.语法&#xff1a; - (返回值类型)方法名称; interface Person : NSObject -(void) run; end 2)实现 a.位置&#xff1a;在implementation中实现 b.语法&#xff1a;加大括弧将方法实现的代码写在大括孤之中 …

浅出深入-机器学习

文章目录 一、K近邻算法1.1 先画一个散列图1.2 使用K最近算法建模拟合数据1.3 进行预测1.4 K最近邻算法处理多元分类问题1.5 K最近邻算法用于回归分析1.6 K最近邻算法项目实战-酒的分类1.6.1 对数据进行分析1.6.2 生成训练数据集和测试数据集1.6.3 使用K最近邻算法对数据进行建…

抽象工厂模式-C#实现

该实例基于WPF实现&#xff0c;直接上代码&#xff0c;下面为三层架构的代码。 一 Model using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace 设计模式练习.Model.抽象工厂模式 {public abstrac…

学习笔记之 机器学习之预测雾霾

文章目录 Encoder-DecoderSeq2Seq (序列到序列&#xff09; Encoder-Decoder 基础的Encoder-Decoder是存在很多弊端的&#xff0c;最大的问题就是信息丢失。Encoder将输入编码为固定大小的向量的过程实际上是一个“信息有损的压缩过程”&#xff0c;如果信息量越大&#xff0c;…

Rollup:打包 TypeScript - React 组件库

调用浏览器摄像头拍照组件 1、前提1、安装依赖2、添加 rollup.config.js 配置3、修改 package.json3.1 添加打包命令3.2 添加组件入口3.3 添加组件声明入口3.4 浏览器支持 1、前提 1.1 通过 create-react-app take-photo --template 创建前端应用 1.2 添加组件 TakePhoto (拍照…

华为三层交换机之基本操作

Telnet简介 Telnet是一个应用层协议,可以在Internet上或局域网上使用。它提供了基于文本的远程终端接口&#xff0c;允许用户在本地计算机上登录到远程计算机&#xff0c;然后像在本地计算机上一样使用远程计算机的资源。Telnet客户端和服务器之间的通信是通过Telnet协议进行的…