stm32使用MQTT发布传感器,图片数据至ONENET,并实现可视化管理

项目介绍

项目为课程设计,实现厨房着火,煤气泄漏的实时检测,STM32可以采集温度传感器DS18b20,烟雾MQ-7,CO MQ-2传感器的数据,同时采集OV7670摄像头的图片数据,通过MQTT协议发送到ONENET平台。在ONENET平台中可以通过可视化界面实时显示相应数据,同时可以调节相应传感器的报警阈值,出现报警时发送报警信息至用户邮箱。

硬件相关

STM32F103zet6

板子采用正点原子的精英板,引脚资源比较充裕,当然主要是因为没有其他板子了 嘿嘿。

温度传感器

DB18B20大概所有人都听过此传感器吧,好用就完事了。

MQ系列气体检测传感器

MQ系列真是个大家族,啥都有,一个传感器还能测好多数据,不过唯一麻烦的是我很难找到将电压值对应到具体PPM单位的数值,本项目直接简化处理了。可以参考这个视频中的相关计算。

lcd显示屏

采用了一个1.3的TFTlcd屏幕,spi驱动,为什么选这个呢,其实是做HOLOCUBIC剩下的屏幕,所以拿来用了。

OV7670(有FIFO)摄像头

朋友的摄像头,直接拿来用了,前期不太会玩,花了好长时间才明白。

ESP8266

用的正点原子的wifi模块,用了好长时间了,这个项目用完下个项目用。采用AT指令集的方式控制,STM32通过串口2发送AT信息给ESP8266。

接线

表格是本项目的接线

程序相关

传感器数据获取

DS18B20

直接copy的正点原子的例程,换了一下引脚,里面可能会有一些寄存器的知识,简单说一下吧。

//IO方向设置
#define DS18B20_IO_IN()  {GPIOA->CRH&=0XFFFF0FFF;GPIOA->CRH|=8<<12;}
#define DS18B20_IO_OUT() {GPIOA->CRH&=0XFFFF0FFF;GPIOA->CRH|=3<<12;}

由于连接的是PA11口,所以GPIOA->CRH&=0XFFFF0FFF先把对应位置清0,GPIOA->CRH|=8<<12把对应位置设为输入模式。大家可以参考这篇博客:https://blog.csdn.net/zhuguanlin121/article/details/118659348
在这里插入图片描述

ADC采集

用来获取MQ-2和MQ-7的数据,采用了ADC1的通道1,和ADC2的通道0,相应的配置基本网上都有,可以参考附件代码,下面贴一下部分代码。

void  Adc_Init(void)
{ 	
	ADC_InitTypeDef ADC_InitStructure; 
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1|RCC_APB2Periph_ADC2, ENABLE );	  //使能ADC1通道时钟

	RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M

	//PA1 作为模拟通道输入引脚                         
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;		//模拟输入引脚PA1//MQ-2
	GPIO_Init(GPIOA, &GPIO_InitStructure);	
	
	ADC_DeInit(ADC1);  //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
    ADC_DeInit(ADC2);
	
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;	//ADC工作模式:ADC1和ADC2工作在独立模式
	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数据右对齐
	ADC_InitStructure.ADC_NbrOfChannel = 1;	//顺序进行规则转换的ADC通道的数目
	ADC_Init(ADC1, &ADC_InitStructure);	//根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器   

	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;	//ADC工作模式:ADC1和ADC2工作在独立模式
	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数据右对齐
	ADC_InitStructure.ADC_NbrOfChannel = 1;	//顺序进行规则转换的ADC通道的数目
	ADC_Init(ADC2, &ADC_InitStructure);
  
	ADC_Cmd(ADC1, ENABLE);	//使能指定的ADC1
	ADC_Cmd(ADC2, ENABLE);
	
	ADC_ResetCalibration(ADC1);	//使能复位校准  
	ADC_ResetCalibration(ADC2);	
	
	while(ADC_GetResetCalibrationStatus(ADC1));	//等待复位校准结束
	while(ADC_GetResetCalibrationStatus(ADC2));
	
	ADC_StartCalibration(ADC1);	 //开启AD校准
  ADC_StartCalibration(ADC2);	
	
	while(ADC_GetCalibrationStatus(ADC1));	 //等待校准结束
  while(ADC_GetCalibrationStatus(ADC2));
	float Smog_Get_Vol(void) //MQ-2烟雾电压值
{
	u16 adc_value1 = 0;
	float voltage1 = 0;
	
	adc_value1 = Get_Adc1_Average(ADC_Channel_0,5);
	delay_ms(5);
	
  voltage1 = (3.3/4096.0)*(adc_value1);

	return voltage1;
}

float CO_Get_Vol(void)
{
	u16 adc_value2 = 0;
	float voltage2 = 0;
	
	adc_value2 = Get_Adc2_Average(ADC_Channel_1,5);
	delay_ms(5);
	
  voltage2  = (3.3/4096.0)*(adc_value2);
	
	return voltage2;
}
float Smog_GetPPM()
{
	float ppm=80*Smog_Get_Vol();
	return  ppm;
}

}				  

ONENET代码相关

发送传感器数据

这里很奇怪的是当数据量超过4个后,有时会自动离线,所以改为了三个,分不同批次的发送。

//以TYPE 1形式发送传感器数据
unsigned char OneNet_FillBuf(char *buf)
{

 char text[128];

 memset(text, 0, sizeof(text));
 strcpy(buf, "{\"datastreams\":[");
			 memset(text, 0, sizeof(text));
			 sprintf(text, "{\"id\":\"smog\",\"datapoints\":[{\"value\":%0.2f}]},", MQ_2);
			 strcat(buf, text);
			 
			 memset(text, 0, sizeof(text));
			 sprintf(text, "{\"id\":\"CO\",\"datapoints\":[{\"value\":%0.2f}]},", MQ_7);
			 strcat(buf, text);
			 
			 memset(text, 0, sizeof(text));
			 sprintf(text, "{\"id\":\"temp\",\"datapoints\":[{\"value\":%0.2f}]}", temp);
			 strcat(buf, text);
	   

 strcat(buf, "]}");
 
 return strlen(buf);

}
unsigned char OneNet_FillBuf_alarm(char *buf)
{
	u8 temp;
 char text[128];
	temp=alarm^stop;
 memset(text, 0, sizeof(text));
 strcpy(buf, "{\"datastreams\":[");
	
	     memset(text, 0, sizeof(text));
			 sprintf(text, "{\"id\":\"alarm\",\"datapoints\":[{\"value\":%d}]}", temp);
			 strcat(buf, text);

	
 strcat(buf, "]}");
 return strlen(buf);
}

void OneNet_SendData(void)
{

	MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};												//协议包
	
	char buf[256];
	
	short body_len = 0, i = 0;
	printf("Send--------1\r\n");
	//UsartPrintf(USART_DEBUG, "Tips:	OneNet_SendData-MQTT\r\n");
	
	memset(buf, 0, sizeof(buf));
	
	body_len = OneNet_FillBuf(buf);																	//获取当前需要发送的数据流的总长度
	
	if(body_len)
	{
		if(MQTT_PacketSaveData(DEVID, body_len, NULL, 1, &mqttPacket) == 0)							//封包
		{
			for(; i < body_len; i++)
				mqttPacket._data[mqttPacket._len++] = buf[i];
			
			ESP8266_SendData(mqttPacket._data, mqttPacket._len);									//上传数据到平台
			//UsartPrintf(USART_DEBUG, "Send %d Bytes\r\n", mqttPacket._len);
			
			MQTT_DeleteBuffer(&mqttPacket);															//删包
		}
		else
			UsartPrintf(USART_DEBUG, "WARN:	EDP_NewBuffer Failed\r\n");
	}
	
}

void OneNet_SendData_alarm(void)
{

	MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};												//协议包
	
	char buf[256];
	
	short body_len = 0, i = 0;
	printf("Send--------3\r\n");
	//UsartPrintf(USART_DEBUG, "Tips:	OneNet_SendData-MQTT\r\n");
	
	memset(buf, 0, sizeof(buf));
	
	body_len = OneNet_FillBuf_alarm(buf);																	//获取当前需要发送的数据流的总长度
	
	if(body_len)
	{
		if(MQTT_PacketSaveData(DEVID, body_len, NULL, 1, &mqttPacket) == 0)							//封包
		{
			for(; i < body_len; i++)
				mqttPacket._data[mqttPacket._len++] = buf[i];
			
			ESP8266_SendData(mqttPacket._data, mqttPacket._len);									//上传数据到平台
			//UsartPrintf(USART_DEBUG, "Send %d Bytes\r\n", mqttPacket._len);
			
			MQTT_DeleteBuffer(&mqttPacket);															//删包
		}
		else
			UsartPrintf(USART_DEBUG, "WARN:	EDP_NewBuffer Failed\r\n");
	}
	
}

发送图片数据

TPYE 2类型MQTT发送OV7670截取的图片数据
RGB565格式需要转成JPEG的格式,需要有一个转换的步骤。


#define PKT_SIZE 1024
void OneNet_SendData_Picture(char *devid, unsigned char* picture, unsigned int pic_len)
{
		MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};												//协议包
		unsigned char *pImage = (unsigned char *)picture;
		
    if(MQTT_PacketSaveBinData("pic",pic_len, &mqttPacket)==0)
		{
				//发送头
			ESP8266_Clear();
			
			UsartPrintf(USART_DEBUG, "Send %d Bytes\r\n", mqttPacket._len);
			ESP8266_SendData(mqttPacket._data, mqttPacket._len);				//上传数据到平台
			MQTT_DeleteBuffer(&mqttPacket);															//删包
			UsartPrintf(USART_DEBUG, "image len = %d\r\n", pic_len);
				//发送数据
			while(pic_len > 0)
			{
				delay_ms(300);												//传图时,时间间隔会大一点,这里额外增加一个延时
				
				if(pic_len >= PKT_SIZE)
				{
					ESP8266_SendData(pImage, PKT_SIZE);						//串口发送分片
					pImage += PKT_SIZE;
					pic_len -= PKT_SIZE;
				}
				else
				{
					ESP8266_SendData(pImage, (unsigned short)pic_len);		//串口发送最后一个分片
					pic_len = 0;
				}
			}
			UsartPrintf(USART_DEBUG, "image send ok\r\n");
		}
		else
		{
			UsartPrintf(USART_DEBUG, "WARN:	EDP_NewBuffer Failed\r\n");
		}
	
}

void OneNet_SendData_pic(void)
{
	camera_refresh();
	if(get_shoot==1){
			OneNet_SendData_Picture(NULL, JPG_enc_buf, pt_buf);
			ESP8266_Clear();
			yfy=1;
	}
}
void camera_refresh(void)
{
	int j,i,t;
 	u16 color;
	int width, height;
	jpeg_compress_info *cinfo;
	pt_buf = 0;
	printf("																							prepare shoot\r\n");
	if(ov_sta==2)
	{
		width = 320;//图像的宽度 ------
		height = 240;//图像的高度 4------
		cinfo = jpeg_create_compress();//------
		if (!cinfo) //------
		 {
			  printf("error in create cinfo, malloc faild!\n");
		 }
			cinfo->image_width = width;
		  cinfo->image_height= height;
		  cinfo->output =(char *)JPG_enc_buf;//fopen("test.jpg", "wb");
		  jpeg_set_default(cinfo);  
		  jpeg_start_compress(cinfo); 
			 
			OV7670_CS=0;	 
			OV7670_RRST=0;				//开始复位读指针 
			OV7670_RCK=0;
			OV7670_RCK=1;
			OV7670_RCK=0;
			OV7670_RRST=1;				//复位读指针结束 
			OV7670_RCK=1;  
			
		for(j=0;j<240;j++)
		{
			for(i=319;i>=0;i--)
			{
				GPIOB->CRL=0X88888888;	
				OV7670_RCK=0; 
				color=OV7670_DATA;		//读数据
				OV7670_RCK=1; 	
				color<<=8;					  
				OV7670_RCK=0;
				color|=OV7670_DATA;		//读数据		  
				OV7670_RCK=1; 
				GPIOB->CRL=0X33333333;
				
				//LCD_WR_DATA(color);	 
				
				//转换
				R = (color>>8)&0xF8;
				G = (color>>3)&0xFC;
				B = (color<<3)&0xF8;
				
				buffer[i*3+0] = R;//jpeg格式顺序RGB 如果是bmp格式写成BGR
				buffer[i*3+1] = G;
				buffer[i*3+2] = B;
			}
			jpeg_write_scanline(cinfo, buffer);//一行一行的压缩
		}  
		jpeg_finish_compress(cinfo);  
		jpeg_destory_compress(cinfo);
		

 		OV7670_CS=1; 							 
		OV7670_RCK=0; 
		OV7670_RCK=1; 
		EXTI->PR=1<<15;     		//清除LINE8上的中断标志位
		get_shoot=1;
	} 
	else 		get_shoot=0;
}	  

任务调度相关

由于代码中任务众多,因此使用了一种任务调度的方式,具体可参考以下文章
STM32 简单多任务调度的方法与程序例程
具体代码可看附件。

//结构体创建
typedef struct{
		void (*fTask)(void);  //fTask为任务指针,指向函数
		long uNextTick;       //该任务下一次执行的时间
		int uLenTick;         //调度周期
}Task;
// 任务列表
static Task myTaskTab[] =
{
{Task_SysTick, 0, 0}       //计算系统时间
,{KEY_pro,     0, 100}     //100ms执行一次
,{LCD_display, 150, 100}    //第150ms开始执行,每隔100ms执行一次
,{Sensor_get,  500, 2000}
,{Control_pro, 3000,  1000}
,{Control_shoot,750,100}
,{Senddata_pro,4000,2000}//4000
,{Receivedata_pro,50,30}
,{if_disconnect,2000,5000}
};
int main(void)
 {
	//主函数变量定义
	 int i = 0;
	//初始化相关
	delay_init();	    	  //延时函数初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	dev_SysTick_init();	// 系统时钟初始化
	Hardware_init();
	Onenet_init();
	Value_init();
	OneNet_SendData_pic();
	TIM_Cmd(TIM2, ENABLE);
	LCD_Fill(0,0,240,240,WHITE); //刷新页面
	while(1)
	{
		// 任务循环
		for (i = 0; i < ARRAYSIZE(myTaskTab); i++)
		{
			if (myTaskTab[i].uNextTick <= GetTimingTick())
			{
			myTaskTab[i].uNextTick += myTaskTab[i].uLenTick;
			myTaskTab[i].fTask();
			}
		}
		
	}	 
}

ONENET平台相关

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
各种控件使用的都是私有过滤器,模板基本一样,目的是显示最新得到的数据。
在这里插入图片描述

资源

stm32 ESP8266 Onenet 图片上传,数据上传
注:第一次写文章,错误可能会有,请多包涵。

  • 6
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
1,开发板提供强大的WEB访问和控制功能,通过浏览器可以实现开发板的控制和操作(包括视频监控,音乐播放和继电器控制等功能) 2,支持android手机app视频监控,提供手机APP源代码。支持VLC/1K播放器(IOS),OPlayer(Android)等软件直接播放开发板MJPG视频,只要在这些软件上输入开发板HTTP地址即可。 3,支持PC软件监控,实测视频参考(支持PC安装VLC播放器直接播放开发板MJPG视频,只要在VLC播放器中输入开发板HTTP地址即可): 4,支持OneNet云平台EDP协议,轻松实现视频监控,所有代码开源,通过网页或者APP可轻松访问公开的摄像头数据。 5,支持阿里云物联网平台,支持TLS方式接入(已经移植了MbedTLS协议栈),实现MQTT消息的订阅与发布数据更安全。 6,支持百度AI平台人脸识别技术,人脸识别应用实现为一个人脸识别控制继电器开关的功能。测试时,将开发板摄像头对准人脸(人脸相隔40~60cm),如果开发板检查到当前人脸和人脸库中的匹配,则会使继电器闭合,否则断开继电器。 7,支持百度AI平台语音识别技术语音合成技术,应用的功能为:当按下开发板上的按键后,开发板开始捕捉2秒长度的PCM音频数据,并发送给AI平台识别成文字;同时开发板将识别出来的文字发送给AI语音合成接口,获得一段MP3并播放;同时开发板会检测合成的文字中有没有“开灯”“关灯”字符串,有的话则控制继电器做出相应动作。另一个语音合成功能是,我们可以使用串口发送数据到开发版,开发板将收到的文字请求AI平台,并获得一段MP3并播放 8,支持百度AI平台物体识别技术,物体识别功能为:开发板周期性的利用摄像头捕捉的图像并请求AI平台进行物体识别,同时将识别出来的文字使用百度语音合成功能合成为MP3并播放出来

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值