技能梳理17@Rasphberry Pi 3B+stm32+dht11+lora+onenet

150 阅读2分钟

dht11检测出到温湿度,通过lora传输到树莓派,树莓派传输到onenet,收发处理用了两块stm32开发板

1、项目简介

在这里插入图片描述

2、实现逻辑

#从机stm32检测到温湿度通过lora传给主机stm32
#主机stm32将数据通过串口发送给树莓派
#树莓派将数据发送给onenet

3、应用场景

#远程线上检测温湿度
#近程(几公里)检测温湿度

4、核心代码梳理

//stm32主从机程序(比较简答)
/* USER CODE BEGIN 0 */
//temp hump
void DHT11_IO_IN(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.Pin = DHT11_Pin;
    GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
    HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
}

void DHT11_IO_OUT(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.Pin = DHT11_Pin;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
}

void DHT11_Rst(void) {
    DHT11_IO_OUT(); 	//
    DHT11_DQ_OUT_LOW; 	//
    HAL_Delay(20);    	//
    DHT11_DQ_OUT_HIGH; 	//
    delay_us(30);     	//
}

uint8_t DHT11_Check(void) {
    uint8_t retry=0;
    DHT11_IO_IN();
    while (DHT11_DQ_IN && retry<100) {
        retry++;
        delay_us(1);
    };
    if(retry>=100)return 1;
    else retry=0;
    while (!DHT11_DQ_IN&&retry<100) {
        retry++;
        delay_us(1);
    };
    if(retry>=100)return 1;
    return 0;	//???DHT11??0
}

uint8_t DHT11_Read_Bit(void) {
    uint8_t retry=0;
    while(DHT11_DQ_IN&&retry<100) {
        retry++;
        delay_us(1);
    }
    retry=0;
    while(!DHT11_DQ_IN&&retry<100) {
        retry++;
        delay_us(1);
    }
    delay_us(40);
    if(DHT11_DQ_IN)return 1;
    else return 0;
}

uint8_t DHT11_Read_Byte(void) {
    uint8_t i,dat;
    dat=0;
    for (i=0; i<8; i++) {
        dat<<=1;
        dat|=DHT11_Read_Bit();
    }
    return dat;
}

uint8_t DHT11_Read_Data(uint16_t *temp,uint16_t *humi) {
    uint8_t buf[5];
    uint8_t i;
    DHT11_Rst();
    if(DHT11_Check()==0) {
        for(i=0; i<5; i++) {
            buf[i]=DHT11_Read_Byte();
        }
        if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4]) {
            *humi=(buf[0]<<8) + buf[1];
            *temp=(buf[2]<<8) + buf[3];
        }
    } else return 1;
    return 0;
}

uint8_t DHT11_Init(void) {
    DHT11_Rst();
    return DHT11_Check();
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
    /* USER CODE BEGIN 1 */

    /* USER CODE END 1 */

    /* MCU Configuration--------------------------------------------------------*/

    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();

    /* USER CODE BEGIN Init */

    /* USER CODE END Init */

    /* Configure the system clock */
    SystemClock_Config();

    /* USER CODE BEGIN SysInit */

    /* USER CODE END SysInit */

    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    MX_USART2_UART_Init();
    /* USER CODE BEGIN 2 */
#ifdef TX
    while(DHT11_Init()) {
        HAL_Delay(500);
    }
#endif
    __HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);//open uart1 RXNE

    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
#ifdef TX
//check  temp hump
        DHT11_Read_Data(&temperature,&humidity);
        tx_data[6] = temperature>>8;
        tx_data[7] = temperature&0xff;
        tx_data[8] = humidity>>8;
        tx_data[9] = humidity&0xff;
        HAL_UART_Transmit(&huart1, (uint8_t *)&tx_data, 10, 0xFFFF);
        HAL_Delay(2000);
#endif

#ifdef RX
        if(rx_flag)
        {
            rx_flag = 0;
            HAL_UART_Transmit(&huart2, (uint8_t *)&tx_data, 10, 0xFFFF);
            __HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);//open uart1 RXNE
        }

#endif
        HAL_Delay(100);
        HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    }
    /* USER CODE END 3 */
}


//树莓派端程序
def build_payload(type, payload):
    datatype = type
    packet = bytearray()
    packet.extend(struct.pack("!B", datatype))
    if isinstance(payload, str):
        udata = payload.encode('utf-8')
        length = len(udata)
        packet.extend(struct.pack("!H" + str(length) + "s", length, udata))
    return packet

# 当客户端收到来自服务器的CONNACK响应时的回调。也就是申请连接,服务器返回结果是否成功等
def on_connect(client, userdata, flags, rc):
    global tem
    global hum
    print("连接结果:" + mqtt.connack_string(rc))
    #上传数据
    # 定义上传数据的json格式  该格式是oneNET规定好的  按格式修改其中变量即可
    body = {
        "datastreams": [
            {
                "id": "temp",  # 对应OneNet的数据流名称
                "datapoints": [
                    {
                        "value": tem  # 数据值
                    }
                ]
            },
            {
                "id": "hump",  # 对应OneNet的数据流名称
                "datapoints": [
                    {
                        "value": hum  # 数据值
                    }
                ]
            }
        ]
    }
    json_body = json.dumps(body)
    packet = build_payload(TYPE_JSON, json_body)
    client.publish("$dp", packet, qos=1)  #qos代表服务质量



# 从服务器接收发布消息时的回调。
def on_message(client, userdata, msg):
    print("温度:"+str(msg.payload,'utf-8')+"°C")


#当消息已经被发送给中间人,on_publish()回调将会被触发
def on_publish(client, userdata, mid):
    print("mid:" + str(mid))


# 发送和保存传感器数据相关程序  #
def threadDATAPRO(): # 线程数据处理
    threaddatapro = threading.Thread(target=dataCheck)
    threaddatapro.start()
    while True:
        if not threaddatapro.is_alive():  # 如果发送和保存线程卡死 隔60s再次开启
            threaddatapro = threading.Thread(target=dataCheck)
            threaddatapro.start()
        time.sleep(60)


def dataCheck(): # 检测
    global tem
    global hum
    s0=serial.Serial("/dev/ttyAMA0",9600)  # 树莓派端
    # s0=serial.Serial("/dev/ttyUSB1",9600)  # 树莓派USB端
    # s0 = serial.Serial("COM9", 9600)  # PC端

    dataarr = []
    datagdellStage = 0
    charcount = 0
    temp = 0
    while True:
        while s0.inWaiting():
            try:
                temp = s0.read()
            except Exception as e:
                pass

            if datagdellStage == 2 and temp:  # 如果有数据 存到dataarr
                dataarr.append(temp)
                if len(dataarr) >= charcount:
                    print('main line 50 收到的数据:', dataarr)
                    if dataarr[1] == b'a':
                        tem = dataarr[6][0] + dataarr[7][0]*0.1
                        hum = dataarr[8][0] + dataarr[9][0]*0.1
            if datagdellStage == 1 and temp:  # 如果有数据
                if temp == b'a':   # data:
                    datagdellStage = 2
                    charcount = 10
                    dataarr.append(temp)
                    pass
                if datagdellStage != 2:
                    datagdellStage = 0
                    charcount = 0
                    dataarr = []
                    temp = '\x00'
            if temp == b'd':  # 收到帧头
                datagdellStage = 1
                charcount = 0
                dataarr = []
                dataarr.append(temp)
        time.sleep(0.01)



if __name__ == '__main__':
    global tem
    global hum

    tem = 0
    hum = 0

    threadDataPro = threading.Thread(target=threadDATAPRO)
    threadDataPro.setDaemon(True)  # 守护线程
    threadDataPro.start()

    client = mqtt.Client(client_id=DEV_ID, protocol=mqtt.MQTTv311)
    client.on_connect = on_connect
    client.on_publish = on_publish
    client.on_message = on_message
    client.username_pw_set(username=PRO_ID, password=AUTH_INFO)
    client.connect('183.230.40.39', port=6002, keepalive=120)

    # client.loop_forever()
    client.loop_start()

    # schedule.every(3).seconds.do(dataSend)  # 每几秒发送一次数据
    while True:
        #schedule.run_pending() #定时器,在while True死循环中,schedule.run_pending()是保持schedule一直运行,
        # 去查询上面那一堆的任务,在任务中,就可以设置不同的时间去运行
        # 定义上传数据的json格式  该格式是oneNET规定好的  按格式修改其中变量即可
        body = {
            "datastreams": [
                {
                    "id": "temp",  # 对应OneNet的数据流名称
                    "datapoints": [
                        {
                            "value": tem  # 数据值
                        }
                    ]
                },
                {
                    "id": "hump",  # 对应OneNet的数据流名称
                    "datapoints": [
                        {
                            "value": hum  # 数据值
                        }
                    ]
                }
            ]
        }
        json_body = json.dumps(body)
        packet = build_payload(TYPE_JSON, json_body)
        client.publish("$dp", packet, 1)
        time.sleep(3)

5、部分参考资料

#要熟悉树莓派的烧录、编译及其它环境配置相关基础知识
#用到的硬件是stm32最小开发板
在这里插入图片描述

6、注意事项

#主从机程序用的一套,通过宏定义区分//#define TX #define RX
#树莓派那端使用的是python编写
#树莓派和onenet端通信是mqtt方式
#lora直接用的透传模块,不需要二次开发

完整可运行项目地址