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直接用的透传模块,不需要二次开发