【STM32】 使用ESP8266与服务器进行通信

看了很久的江科大课程了,陆陆续续也学了不少东西,就想着做个类似课设的小项目,尽量多的用上学到的东西。因为是类似课设的小项目,所以做的东西比较简单;没有商业项目经验,所以代码写的很烂,漏洞和Bug想必只会多不会少。新手小白,如果被大佬看到了不顺眼的地方请多多海涵,也请多给我提建议,谢谢大家。

内容

定时器定时,每秒读取一次MPU6050,并通过ESP8266将数据上传到服务器,使用DMA转运串口数据。服务器接受到数据后,查看Y轴加速度数据,当大于2000时为安全状态,返回"Safe",否则返回"Warn"。当接收到串口数据后,若接收到"Warn",则打开蜂鸣器和LED小灯,否则关闭。
在OLED的第一行和第二行显示系统所在的工作状态和标志位,第3、4行将显示串口发送和接受的数据

总体设计

懒得画电路图了,请欣赏灵魂画手带来的作品

原理图

在这里插入图片描述

数据收发(ESP8266)部分

ESP8266使用串口通讯。
首先初始化转运数据所用的DMA1_Channel5,将串口接收到的数据转运到自定义的数组中。

接着初始化所使用的串口USART1,写好底层的串口收发代码,开启串口空闲中断,便于接受和判断ESP8266或服务器发来的数据。定义好相关标志位,在收到特定信息后,改变一些标志位便于主程序使用。
在ESP8266的代码文件include定义好的DMA和USART文件,并且基于此写ESP的初始化、连接WIFI、连接服务器、收发数据以及进行相关配置的代码。

传感器(MPU6050)部分

MPU6050使用I2C通讯。直接在初始化函数中初始化所使用的I2C2,并写好MPU6050的初始化、读写寄存器函数。

反馈部分

使用PB8 PB9 软件模拟I2C控制OLED
使用TIM2输出的PWM波控制蜂鸣器发声。在初始化函数中初始化所使用的TIM2,并写好MPU6050的初始化、读写寄存器函数。
使用空闲的PB12引脚控制LED小灯

主函数

在主函数中依次初始化上述所用的外设以及内部寄存器,使用TIM3定时一秒,每经过1秒就获取MPU6050数据并发送到服务器。

硬件设计

数据收发(ESP8266)部分

在这里插入图片描述
在这里插入图片描述
将USART1所在的PA9(Tx)、PA10(Rx)连接到ESP8266的Rx、Tx引脚,并使用PA8引脚连接到8266的RST便于进行硬件重置。

传感器(MPU6050)部分

在这里插入图片描述


将I2C2所在的PB10(SCL)、PB11(SDA)连接到MPU6050的SCL、SDA引脚

反馈部分

在这里插入图片描述
在这里插入图片描述
将蜂鸣器的I/O连接到TIM2的PWMI输出的PA0引脚。

在这里插入图片描述
在这里插入图片描述
OLED使用的软件模拟I2C,和LED一样寻找空闲引脚即可。

软件设计

数据收发(ESP8266)部分

DMA_for_USART.h
#ifndef __DMA_FOR_USART_H
#define	__DMA_FOR_USART_H

#include "stm32f10x.h"                  // Device header

//保存搬运的数据
extern char RxData[];

void DMA_for_USART_Init(void);

#endif

DMA_for_USART.c
/*
使用DMA1将串口接收的数据转运到RxData数组中暂存
*/

#include "DMA_for_USART.h"

char RxData[2048];

void DMA_for_USART_Init(void)
{
	//打开DMA1时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	//DMA初始化结构体
	DMA_InitTypeDef DMA_InitStructure;
	
	//数据源站点
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);//源地址为USART1的数据寄存器
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//数据宽度为一字节
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//不自增
	
	//数据目的站点
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)RxData;//目的地址
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//数据宽度为一字节
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//自增
	
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//转运方向 源地址->目的地址
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//循环装填
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//硬件触发
	DMA_InitStructure.DMA_BufferSize = 2048;//转运最大值为2048位
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	
	//初始化DMA1的通道5
	DMA_Init(DMA1_Channel5, &DMA_InitStructure);
	
	//使能DMA1的通道5
	DMA_Cmd(DMA1_Channel5, ENABLE);

}

Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H

#include "stm32f10x.h"                  // Device header
#include "string.h"
#include "OLED.h"
#include "DMA_for_USART.h"
#include "LED.h"
#include "Buzzer.h"


//接受buffer
extern uint8_t ESP_Ready_flag;
extern uint8_t ESP_OK_flag;
extern uint8_t Rx_length;
extern uint8_t Serial_RxFlag;
extern uint8_t Safe_flag;

void Serial_Init(void);
void Serial_SendByte(unsigned char Byte);
void Serial_SendArray(uint8_t *Array, uint16_t length);
void Serial_SendString(char *String);

#endif

Serial.c
#include "Serial.h"

/*

串口收发数据控制ESP8266

利用串口空闲中断 和 String中的 strstr确认命令和返回的关键值

*/

//接收数据的长度
uint8_t Rx_length = 0;

//接收完成标志位
uint8_t Serial_RxFlag = 0;
//是否安全
uint8_t Safe_flag = 0;


//ESP8266处理完成标志
uint8_t ESP_Ready_flag = 0;
uint8_t ESP_OK_flag = 1;


void Serial_Init(void)
{
	//初始化转运数据用DMA
	DMA_for_USART_Init();
	
	
	//开启串口时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	//开启GPIOA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	//GPIOA初始化
	GPIO_InitTypeDef GPIO_InitStructure;
	//复用推挽输出为串口发送
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	//PA9引脚
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//上拉输入为串口接收
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	//PA10引脚
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//串口初始化
	USART_InitTypeDef USART_InitStructure;
	//波特率
	USART_InitStructure.USART_BaudRate = 115200;
	//硬件流控 设置为不控制
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	//串口模式设置为输出
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	//校验 设置为无校验
	USART_InitStructure.USART_Parity = USART_Parity_No; 
	//停止位长度设置 设置为1位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	//字长设置  无校验 设置为8位
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1, &USART_InitStructure);
	
	//设置中断优先组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	//初始化NVIC
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	//开启USART串口空闲中断IDLE到NVIC的输出
	USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
	
	//开启USART串口到DMA触发的硬件连接
	USART_DMACmd(USART1, USART_DMAReq_Rx | USART_DMAReq_Tx, ENABLE);
	
	//使能串口USART1
	USART_Cmd(USART1, ENABLE);

}

void Serial_SendByte(uint8_t Byte)
{
	//发送数据
	USART_SendData(USART1, Byte);
	//判断是否发送完成
	while(
		//获取USART1的发送数据寄存器空标志位USART_FLAG_TXE
		USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET
	);
	
	/*
	由手册得知 在写入发送数据寄存器后 
	TXE标志位会自动清0 所以不需要手动清0
	*/
}

//发送队列
void Serial_SendArray(uint8_t *Array, uint16_t length)
{
	for(uint16_t i = 0; i < length; i++)
	{
		
		Serial_SendByte(Array[i]);
		OLED_ShowString(2,1,"send");
		OLED_ShowNum(3,1,Array[i],1);
	}
}

//发送字符串
void Serial_SendString(char *String)
{
	OLED_ShowString(2,1,"send");
	OLED_ShowString(3,1,String);
	for(uint8_t i = 0; String[i] != '\0' ; i++)
		Serial_SendByte(String[i]);
}

//串口空闲中断函数
void USART1_IRQHandler(void)
{
	if(USART_GetITStatus(USART1, USART_IT_IDLE) == SET)
	{
		//串口接收完成标志位置1
		Serial_RxFlag = 1;
		
		//读接收数据长度
		Rx_length = 2048 - DMA1_Channel5->CNDTR;
		//使能DMA1的通道5
		DMA_Cmd(DMA1_Channel5, DISABLE);
		//重置DMA1C5计数器
		DMA_SetCurrDataCounter(DMA1_Channel5, 2048);
		//使能DMA1的通道5
		DMA_Cmd(DMA1_Channel5, ENABLE);
		
		//清除标志位
		USART1->SR;  // 读取状态寄存器
    USART1->DR;  // 读取数据寄存器
		
		//清除IDLE中断标志位
		USART_ClearITPendingBit(USART1, USART_IT_IDLE);
		
		Serial_RxFlag = 1;
			
		RxData[Rx_length]='\0';
		
		if(ESP_Ready_flag == 1)
			OLED_ShowString(4,1,RxData);
		
		//ESP8266未返回ready
		if(ESP_Ready_flag == 0)
		{
			if(strstr(RxData,"ready") != NULL)
				{
					ESP_Ready_flag = 1;
					OLED_ShowString(3,9,"RD:1");
				}
		}
		
		//ESP8266未返回OK
		if(ESP_OK_flag == 0)
		{
			if(strstr(RxData,"OK") != NULL)
			{
				ESP_OK_flag = 1;
			}
		}
		
		
		//处于危险状态 判断服务器是否返回Safe
		if(Safe_flag == 0)
		{
			if(strstr(RxData,"Safe") != NULL)
			{
				OLED_ShowString(1,8,"ITSAFE");
				Safe_flag = 1;
				Buzzer_Turn_Off();
				LED_Turn_Off();
			}
		}
		
		//处于安全状态 判断服务器是否返回Warn
		if(strstr(RxData,"Warn") != NULL)
		{
			OLED_ShowString(1,8,"ITWARN");
			Safe_flag = 0;
			Buzzer_Turn_On();
			LED_Turn_On();
		}
	}
}

ESP8266.h
#ifndef __ESP8266_H
#define __ESP8266_H

#include "Serial.h"
#include "delay.h"

void ESP8266_Init(void);
void ESP8266_Connect_Wifi(char * wifi_name, char * wifi_pswd);
void ESP8266_Config(void);
void ESP8266_Connect_Server(char * ip_mode, char * ip_address, char * post);
void ESP8266_OpenSocket(void);
void ESP8266_CloseSocket(void);
void ESP8266_SendData(char *data);

#endif

ESP8266.c
#include "ESP8266.h"

//初始化
void ESP8266_Init(void)
{
	//串口初始化
	Serial_Init();
	
	//打开时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	//PA8引脚用于重置ESP8266
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	//重置ESP8266
	GPIO_SetBits(GPIOA, GPIO_Pin_8);
	Delay_ms(20);
	GPIO_ResetBits(GPIOA, GPIO_Pin_8);
	Delay_ms(20);
	GPIO_SetBits(GPIOA, GPIO_Pin_8);
	while(ESP_Ready_flag == 0);
	OLED_ShowString(2,8,"Rest");

	//设置ESP8266为STA模式
	ESP_OK_flag = 0;
	Serial_SendString("AT+CWMODE=1\r\n");
	while(ESP_OK_flag == 0);
	OLED_ShowString(2,8,"Init");
}

//连接wifi
void ESP8266_Connect_Wifi(char * wifi_name, char * wifi_pswd)
{
	ESP_OK_flag = 0;
	Serial_SendString("AT+CWJAP=\"");
	Serial_SendString(wifi_name);
	Serial_SendString("\",\"");
	Serial_SendString(wifi_pswd);
	Serial_SendString("\"\r\n");
	while(ESP_OK_flag == 0);
	OLED_ShowString(2,8,"Wifi");
}

//设置为单连接和透传模式
void ESP8266_Config(void)
{
	ESP_OK_flag = 0;
	Serial_SendString("AT+CIPMUX=0\r\n");
	while(ESP_OK_flag == 0);
	
	ESP_OK_flag = 0;
	Serial_SendString("AT+CIPMODE=1\r\n");
	while(ESP_OK_flag == 0);
	OLED_ShowString(2,8,"Conf");
}

//使用TCP/UDP模式连接到服务器的指定端口
void ESP8266_Connect_Server(char * ip_mode, char * ip_address, char * post)
{
	ESP_OK_flag = 0;
	Serial_SendString("AT+CIPSTART=\"");
	Serial_SendString(ip_mode);
	Serial_SendString("\",\"");
	Serial_SendString(ip_address);
	Serial_SendString("\",");
	Serial_SendString(post);
	Serial_SendString("\r\n");
	
	while(ESP_OK_flag == 0);
	OLED_ShowString(2,8,"Serv");
}

//打开透传发送模式
void ESP8266_OpenSocket(void)
{
	Serial_SendString("AT+CIPSEND\r\n");
	OLED_ShowString(2,8,"Open");
}

//关闭透传发送模式
void ESP8266_CloseSocket(void)
{
	Serial_SendString("+++");
	OLED_ShowString(2,8,"Clos");
}

//发送字符串数据
void ESP8266_SendData(char *data)
{
	Serial_SendString(data);
	OLED_ShowString(2,8,"Send");
}

传感器(MPU6050)部分

MPU6050_Reg.h
#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H

/*

预定义 MPU6050的相关命令

*/

#define MPU6050_SMPLRT_DIV       0x19
#define MPU6050_CONFIG           0x1A
#define MPU6050_GYRO_CONFIG      0x1B
#define MPU6050_ACCEL_CONFIG     0x1C
			                           
#define MPU6050_ACCEL_XOUT_H     0x3B
#define MPU6050_ACCEL_XOUT_L     0x3C
#define MPU6050_ACCEL_YOUT_H     0x3D
#define MPU6050_ACCEL_YOUT_L     0x3E
#define MPU6050_ACCEL_ZOUT_H     0x3F
#define MPU6050_ACCEL_ZOUT_L     0x40
#define MPU6050_TEMP_OUT_H       0x41
#define MPU6050_TEMP_OUT_L       0x42
#define MPU6050_GYRO_XOUT_H      0x43
#define MPU6050_GYRO_XOUT_L      0x44
#define MPU6050_GYRO_YOUT_H      0x45
#define MPU6050_GYRO_YOUT_L      0x46
#define MPU6050_GYRO_ZOUT_H      0x47
#define MPU6050_GYRO_ZOUT_L      0x48
			                           
#define MPU6050_PWR_MGMT_1       0x6B
#define MPU6050_PWR_MGMT_2       0x6C
#define MPU6050_WHO_AM_I         0x75

#endif

MPU6050.h
#ifndef __MPU6050_H
#define __MPU6050_H

#include "stm32f10x.h"                  // Device header
#include "MPU6050_Reg.h"

//定义结构体用于传输6轴数据
typedef struct
{
  int16_t AccX;
	int16_t AccY;
	int16_t AccZ;
	int16_t GyroX;
	int16_t GyroY;
	int16_t GyroZ;
} MPU6050_Data;

//初始化
void MPU6050_Init(void);
//写寄存器
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
//读寄存器
uint8_t MPU6050_ReadReg(uint8_t RegAddress);

uint8_t MPU6050_GetID(void);
//读6轴数据
MPU6050_Data MPU6050_GetDATA(void);

#endif

MPU6050.c
#include "MPU6050.h"

/*

使用硬件I2C2对MPU6050进行读写控制
I2C2 中 SCL->PB10 SDA->PB11

*/

#define MPU6050_ADDRESS 0xD0

void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
	uint32_t Timeout;
	Timeout = 10000;									//给定超时计数时间
	while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS)	//循环等待指定事件
	{
		Timeout --;										//等待时,计数值自减
		if (Timeout == 0)								//自减到0后,等待超时
		{
			/*超时的错误处理代码,可以添加到此处*/
			break;										//跳出等待,不等了
		}
	}
}

void MPU6050_Init(void)
{
	//手册内容
	//MyI2C_Init();
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	//复用开漏输出
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	I2C_InitTypeDef I2C_InitStructure;
	//I2C模式
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
	//I2C时钟速度 50kHZ
	I2C_InitStructure.I2C_ClockSpeed = 50000;
	//I2C时钟占空比2:1 ,I2C时钟速度>100kHz时有效
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
	//应答位使能
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
	//寄存器地址宽度
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	//从机地址 这里作为主机
	I2C_InitStructure.I2C_OwnAddress1 = 0x00;
	I2C_Init(I2C2, &I2C_InitStructure);
	//使能I2C
	I2C_Cmd(I2C2, ENABLE);
	
	//电源管理
	//解除睡眠 选择陀螺仪时钟
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);
	//6轴均不待机
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);
	//MPU6050配置
	//采样分频为10
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV  , 0x09);
	//配置外部同步 滤波最大
	MPU6050_WriteReg(MPU6050_CONFIG      , 0x06);
	//陀螺仪配置为最大量程
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG , 0x18);
	//加速度仪配置为最大量程
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);
}


//指定地址写
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
	//通讯开始
  //MyI2C_Start();
	I2C_GenerateSTART(I2C2, ENABLE);
	
	//等待事件EV5发生
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
	
	//指定设备地址
  //MyI2C_SendByte(MPU6050_ADDRESS);
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);
	
	//接受应答位
  //MyI2C_ReceiveAck();
	//等待事件EV6发生->设置为发送模式
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);

	//指定寄存器地址
  //MyI2C_SendByte(RegAddress);
	I2C_SendData(I2C2, RegAddress);
	//接受应答位
  //MyI2C_ReceiveAck();
	//等待事件EV8发生->发送中
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);
	
	//写数据
	//MyI2C_SendByte(Data);
	I2C_SendData(I2C2, Data);
	//不接受应答位
  //MyI2C_ReceiveAck();
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
	
	//通讯结束
  //MyI2C_End();
	I2C_GenerateSTOP(I2C2, ENABLE);
	
}

//指定地址读
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
	//通讯开始
  //MyI2C_Start();
	I2C_GenerateSTART(I2C2, ENABLE);
	
	//等待事件EV5发生
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
	
	//指定设备地址
  //MyI2C_SendByte(MPU6050_ADDRESS);
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);
	
	//接受应答位
  //MyI2C_ReceiveAck();
	//等待事件EV6发生->设置为发送模式
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);

	//指定寄存器地址
  //MyI2C_SendByte(RegAddress);
	I2C_SendData(I2C2, RegAddress);
	//接受应答位
  //MyI2C_ReceiveAck();
	//等待事件EV8发生->发送中
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);

	//开始时序
	//MyI2C_Start();
	I2C_GenerateSTART(I2C2, ENABLE);
	//等待事件EV5发生
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
	
	//指定设备地址 改成读地址
	//MyI2C_SendByte(MPU6050_ADDRESS | 0x01);
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);
	
	//接受应答位
	//MyI2C_ReceiveAck();
	//等待事件EV6发生->设置为接受模式
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);
	
	//提前给Ack置0 STOP置1
	I2C_AcknowledgeConfig(I2C2, DISABLE);
	I2C_GenerateSTOP(I2C2, ENABLE);
	
	//等待事件EV7发生->接受一个字节
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);
	
	//缓存数据
	//uint8_t Data = MyI2C_ReceiveByte();
	uint8_t Data = I2C_ReceiveData(I2C2);
	
	//ACK置1
	I2C_AcknowledgeConfig(I2C2, ENABLE);
	
	
//	//不需要其他数据 不返回应答
//	MyI2C_SendAck(1);
//	//通讯结束
//	MyI2C_End();
	
	return Data;
}

uint8_t MPU6050_GetID(void)
{
	return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}

//从MPU6050读取6轴数据
MPU6050_Data MPU6050_GetDATA(void)
{
	MPU6050_Data Data;
	
	Data.AccX = 
	(MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H)<<8) 
	| MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
	
	Data.AccY = 
	(MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H)<<8) 
	| MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
	
	Data.AccZ = 
	(MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H)<<8) 
	| MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
	
	Data.GyroX =
	(MPU6050_ReadReg(MPU6050_GYRO_XOUT_H)<<8) 
	| MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
	
	Data.GyroY =
	(MPU6050_ReadReg(MPU6050_GYRO_YOUT_H)<<8) 
	| MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
	
	Data.GyroZ =
	(MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H)<<8) 
	| MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
	
	return Data;
}

反馈部分

OLED

这部分不是本人原创,B站关注江协科技

LED.h
#ifndef __LED_H
#define __LED_H

#include "stm32f10x.h"                  // Device header

void LED_Init(void);
void LED_Turn_On(void);
void LED_Turn_Off(void);

#endif

LED.c
#include "LED.h"                  // Device header

/*

使用PB12控制LED亮灭

*/

void LED_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOB, GPIO_Pin_12);
}

//低电平开灯
void LED_Turn_On(void)
{
	GPIO_ResetBits(GPIOB, GPIO_Pin_12);
}

//高电平熄灯
void LED_Turn_Off(void)
{
	GPIO_SetBits(GPIOB, GPIO_Pin_12);
}

Buzzer.h
#ifndef __BUZZER_H
#define __BUZZER_H

#include "stm32f10x.h"                  // Device header

void Buzzer_Init(void);
void Buzzer_Turn_On(void);
void Buzzer_Turn_Off(void);

#endif

Buzzer.c
#include "Buzzer.h"

/*

使用TIM2即PA0引脚输出PWM波 使蜂鸣器发出响声

*/


#define Buzzer_Prot GPIOA
#define Buzzer_Pin GPIO_Pin_0

void Buzzer_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = Buzzer_Pin;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(Buzzer_Prot, &GPIO_InitStructure);
	
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseStructInit(&TIM_TimeBaseInitStructure);
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 100-1;//计数  ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1;//分频  PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出比较模式设置为模式PWM1
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出比较级性设置为不反相
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能
	TIM_OCInitStructure.TIM_Pulse = 50;//设置CCR
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);
	
	TIM_Cmd(TIM2, DISABLE);
}

void Buzzer_Turn_On(void)
{
	
	TIM_Cmd(TIM2, ENABLE);
	
}

void Buzzer_Turn_Off(void)
{
	TIM_Cmd(TIM2, DISABLE);
	TIM_SetCounter(TIM2, 0);
}

定时器

Timer.h
#ifndef __TIMER_H
#define __TIMER_H

#include "stm32f10x.h"                  // Device header

extern uint8_t Get_Data_Timer_Flag;

void Timer_Init(void);

void Timer_Turn_On(void);
	
void Timer_Turn_Off(void);

#endif

Timer.c
#include "Timer.h"

/*

Timer3产生1秒信号触发中断向服务器发信息

*/

uint8_t Get_Data_Timer_Flag = 0;

void Timer_Init(void)
{
	//配置定时器
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//使能APB1总线上的TIM3定时器
	TIM_InternalClockConfig(TIM3);//输入使用内部时钟
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//时基单元初始化结构体
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1 ;//输入信号滤波采样设置为1分频
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//计数模式设置为向上计数
	//定时频率为CK_CNT_OV=CK_PSC/(PSC+1)/(ARR+1);
	TIM_TimeBaseInitStructure.TIM_Period = 10000 -1;//10kHz进行10000次计数得到1Hz
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;//将72MHz分频7200为10kHz
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器 只有高级定时器拥有
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);//时基单元初始化
	
	TIM_ClearFlag(TIM3, TIM_FLAG_Update);//解决复位时会立即进入中断函数的问题
	TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);//中断设置为更新中断
	
	//配置NVIC中断控制器
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;//中断通道为TIM3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;//响应优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;//抢占优先级
	
	NVIC_Init(&NVIC_InitStructure);
	
	//关闭定时器
	TIM_Cmd(TIM3, DISABLE);
	
}

void Timer_Turn_On(void)
{
	//开启定时器
	TIM_Cmd(TIM3, ENABLE);
}
	
void Timer_Turn_Off(void)
{
	//关闭定时器
	TIM_Cmd(TIM3, DISABLE);
}



void TIM3_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)//中断是否来自TIM3的更新事件
	{
		TIM_Cmd(TIM3, DISABLE);
		Get_Data_Timer_Flag = 1;
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);//中断处理程序完成 清除中断标志位
		TIM_Cmd(TIM3, ENABLE);
	}
}

主程序

main.c
#include "stm32f10x.h"                  // Device header

#include <string.h>
#include <stdio.h>

#include "Delay.h"
#include "OLED.h"
#include "ESP8266.h"
#include "MPU6050.h"
#include "Timer.h"

static uint8_t send_count = 0;

MPU6050_Data data;

//wifi名
char wifi_name[] = "******";
//wifi密码
char wifi_pswd[] = "***********";

char ip_mode[] = "TCP";
//服务器ip
char server_addr[] = "*************";
//服务器开启的端口
char server_port[] = "26";


int main(void)
{
	Timer_Init();
	OLED_Init();
	MPU6050_Init();
	ESP8266_Init();
	
	Buzzer_Init();
	LED_Init();
	
	ESP8266_Connect_Wifi(wifi_name, wifi_pswd);
	ESP8266_Config();
	ESP8266_Connect_Server(ip_mode, server_addr, server_port);
	
	
	//暂存转为字符串后的数据
	char temp_char[20];
	
	Timer_Turn_On();
	
	//打开TCP连接
	ESP8266_OpenSocket();
	
	while(1)
	{
		if(Get_Data_Timer_Flag != 0)
		{
//			//打开TCP连接
//			ESP8266_OpenSocket();
			OLED_ShowString(2,1,"Send");
			//清除自定义标志位
			Get_Data_Timer_Flag =0;
			//获取数据
			data = MPU6050_GetDATA();
			
			//发送数据
			sprintf(temp_char,"%d",data.AccX);
			ESP8266_SendData(temp_char);
			ESP8266_SendData(",");
			
			sprintf(temp_char,"%d",data.AccY);
			ESP8266_SendData(temp_char);
			ESP8266_SendData(",");
			
			sprintf(temp_char,"%d",data.AccZ);
			ESP8266_SendData(temp_char);
			ESP8266_SendData(",");
			
			sprintf(temp_char,"%d",data.GyroX);
			ESP8266_SendData(temp_char);
			ESP8266_SendData(",");
			
			sprintf(temp_char,"%d",data.GyroY);
			ESP8266_SendData(temp_char);
			ESP8266_SendData(",");
			
			sprintf(temp_char,"%d",data.GyroZ);
			ESP8266_SendData(temp_char);
			
			send_count ++;
			OLED_ShowNum(2,4,send_count,2);
			
			
//			//关闭TCP连接
//			ESP8266_CloseSocket();
		}
		
	}
}

服务器代码

记得开启在防火墙开启26端口(当然可以自定义为想要的端口啦)

import socket
import re

server = socket.socket()
server.bind(('0.0.0.0',26))
server.listen(1)
data_send = ""
while True:
    client, addr = server.accept()
    data_recv = client.recv(1024)
    #接收数据
    data_recv = data_recv.decode()
    print(f"Received: ")
    print(data_recv)
    #从数据中提取数字信息
    numbels = re.findall(r'\d+',data_recv)
    print(numbels)
    #判断是否安全
    if int(numbels[2]) < 2000:
        data_send = "Warn"
    else :
        data_send = "Safe"
    client.sendall(data_send.encode())
    print(data_send)
    client.close()

结果图

水平放置时,Y_Acc大于2000,返回Safe,蜂鸣器不响灯不亮
在这里插入图片描述
在这里插入图片描述
倾斜放置时,Y_Acc小于2000,返回Warn,蜂鸣器响灯亮
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值