1. 蓝牙模块
1.1 简介
• HC-08 蓝牙模块是通过串口与单片机 进行通信,这个模块既可以作为主机也可以作为从机(通过 AT 指令配置),并且最远传输距离为80m,最大传输速度为1Mbps。如图:

1.2 引脚
• STATE:状态输出引脚。未连接时,则为低电平。连接成功时,则为高电平。可以在程序中作指示引脚使用。
• RXD:串口接收引脚。接单片机的 TX 引脚。
• TXD:串口发送引脚。接单片机的 RX 引脚。
• GND:接地电源。
• VCC:输入 3.3~6V 的电源。
• KEY:主机用于清除配对的从机地址记忆(需要拉高电平 200ms 以上)。
• 但是正常情况下只需要接 RXD,TXD,VCC,DND这四个引脚。
• 蓝牙模块上还有一个 LED灯和一个小按键 (按键控制着引脚 KEY )。默认情况下,当 LED灯闪烁时表示蓝牙模块当前为从机,正在等待连接。而长亮的时候就代表已经有主机连接上该模块,可以正常进行透传通讯了。
• 当按键按下后,主机将清除已被记录的从机地址。另外,也可使用 AT+CLEAR 指令,实现主机清除已记录的从机地址的功能。
1.3 常用AT指令
• 测试的,发AT,返回OK,代表模块正常,返回OK。
• AT+RESET是重启模块指令。
• AT+DEFAULT是恢复出场指令。
• AT+BAUD=xx,y是修改波特率指令,一般来说默认波特率是9600。
1.4 实操
• 在bluetooth.c中
cpp
#include "bluetooth.h"
#include "stdio.h"
#include "string.h"
#include "stdarg.h"
UART_HandleTypeDef uart2_handle = {0};//串口的句柄
uint8_t uart2_rx_buf[UART2_RX_BUF_SIZE];//定义接收的数据存放位置
uint16_t uart2_rx_len = 0;//计数器,表示接收了多少个数
//串口初始化
void bt_init(uint32_t baudrate){
uart2_handle.Instance = USART2;//选择串口2
uart2_handle.Init.BaudRate = baudrate;
uart2_handle.Init.Mode = UART_MODE_TX_RX;
uart2_handle.Init.Parity = UART_PARITY_NONE;//不打开检验位
uart2_handle.Init.StopBits = UART_STOPBITS_1;//1个停止位
uart2_handle.Init.WordLength = UART_WORDLENGTH_8B;//传输字长为8位
uart2_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;//硬件流不打开
HAL_UART_Init(&uart2_handle);
}
void uart2_clear(){
memset(uart2_rx_buf,0,sizeof(uart2_rx_buf));
uart2_rx_len = 0;
}
void USART2_IRQHandler(){
uint8_t receive_data = 0;
if(__HAL_UART_GET_FLAG(&uart2_handle,UART_FLAG_RXNE) != RESET){//看是不是被置1了
if(uart2_rx_len >= sizeof(uart2_rx_buf))
uart2_rx_len = 0;//接收前需要判断一下,,uart2_cnt是否超出总长度uart2_RX_BUF_SIZE
HAL_UART_Receive(&uart2_handle,&receive_data,1,1000);//接收数据
uart2_rx_buf[uart2_rx_len++] = receive_data;//把接收到的数据放进数组
//HAL_UART_Transmit(&uart2_handle,&receive_data,1,1000);
}
if(__HAL_UART_GET_FLAG(&uart2_handle,UART_FLAG_IDLE) != RESET){
__HAL_UART_CLEAR_IDLEFLAG(&uart2_handle);
printf("bt recv: %s\r\n",uart2_rx_buf);
uart2_clear();//在这里,已经把buf清空了
}
}
//void send_buf(char* buf,uint8_t size){
//
//
// HAL_UART_Transmit(&uart2_handle,(uint8_t *)buf,size,100);
//}
void bt_send(char * format, ...)
{
uint8_t send_buf[128] ={0};
va_list arg;//可变的参数列表
va_start(arg, format);//初始化arg,使其指向可变参数列表的起始位置,format是传递给函数的格式字符串参数,va_start告诉编译器从format之后的参数开始处理可变参数。
vsprintf((char *)send_buf, format, arg);//函数将可变参数格式化为字符串,并存储在send_buf中。
va_end(arg);//结束处理
HAL_UART_Transmit(&uart2_handle, send_buf, sizeof(send_buf), 100);//数据发给手机,
}
• uart1.c
cpp
#include "uart1.h"
#include "stdio.h"
#include "string.h"
UART_HandleTypeDef uart1_handle = {0};//串口的句柄
uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE];//定义接收的数据存放位置
uint16_t uart1_rx_len = 0;//计数器,表示接收了多少个数
//串口初始化
void uart1_init(uint32_t baudrate){
uart1_handle.Instance = USART1;//选择串口1
uart1_handle.Init.BaudRate = baudrate;
uart1_handle.Init.Mode = UART_MODE_TX_RX;
uart1_handle.Init.Parity = UART_PARITY_NONE;//不打开检验位
uart1_handle.Init.StopBits = UART_STOPBITS_1;//1个停止位
uart1_handle.Init.WordLength = UART_WORDLENGTH_8B;//传输字长为8位
uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;//硬件流不打开
HAL_UART_Init(&uart1_handle);
}
void HAL_UART_MspInit(UART_HandleTypeDef *huart){
if(huart->Instance == USART1){
GPIO_InitTypeDef gpio_init = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();//使能GPIO时钟
__HAL_RCC_USART1_CLK_ENABLE();
gpio_init.Mode = GPIO_MODE_AF_PP;//TX线是看GPIO外设配置为复用推挽
gpio_init.Pin = GPIO_PIN_9;
gpio_init.Pull = GPIO_PULLUP;//默认上拉
gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&gpio_init);//初始化gpio
gpio_init.Mode = GPIO_MODE_INPUT;//RX线是看GPIO外设配置为上拉输入
gpio_init.Pin = GPIO_PIN_10;
HAL_GPIO_Init(GPIOA,&gpio_init);//初始化gpio
HAL_NVIC_SetPriority(USART1_IRQn,3,3);
HAL_NVIC_EnableIRQ(USART1_IRQn);
__HAL_UART_ENABLE_IT(huart,UART_IT_RXNE);//使能RXNE中断
__HAL_UART_ENABLE_IT(huart,UART_IT_IDLE);//使能空闲中断
}else if(huart->Instance == USART2){//蓝牙的
GPIO_InitTypeDef gpio_init = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();//使能GPIO时钟
__HAL_RCC_USART2_CLK_ENABLE();
gpio_init.Mode = GPIO_MODE_AF_PP;//TX线是看GPIO外设配置为复用推挽
gpio_init.Pin = GPIO_PIN_2;
gpio_init.Pull = GPIO_PULLUP;//默认上拉
gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&gpio_init);//初始化gpio
gpio_init.Mode = GPIO_MODE_INPUT;//RX线是看GPIO外设配置为上拉输入
gpio_init.Pin = GPIO_PIN_3;
HAL_GPIO_Init(GPIOA,&gpio_init);//初始化gpio
HAL_NVIC_SetPriority(USART2_IRQn,3,3);
HAL_NVIC_EnableIRQ(USART2_IRQn);
__HAL_UART_ENABLE_IT(huart,UART_IT_RXNE);//使能RXNE中断
__HAL_UART_ENABLE_IT(huart,UART_IT_IDLE);//使能空闲中断
}
}
int fputc(int ch,FILE *f){//重定向printf函数
while((USART1->SR & 0x40) == 0);//一直等 发送数据寄存器不为空
USART1->DR = (uint8_t)ch;
return ch;
}
void uart1_clear(){
memset(uart1_rx_buf,0,sizeof(uart1_rx_buf));
uart1_rx_len = 0;
}
void USART1_IRQHandler(){
uint8_t receive_data = 0;
if(__HAL_UART_GET_FLAG(&uart1_handle,UART_FLAG_RXNE) != RESET){//看是不是被置1了
if(uart1_rx_len >= sizeof(uart1_rx_buf))
uart1_rx_len = 0;//接收前需要判断一下,,uart1_cnt是否超出总长度UART1_RX_BUF_SIZE
HAL_UART_Receive(&uart1_handle,&receive_data,1,1000);//接收数据
uart1_rx_buf[uart1_rx_len++] = receive_data;//把接收到的数据放进数组
//HAL_UART_Transmit(&uart1_handle,&receive_data,1,1000);
}
if(__HAL_UART_GET_FLAG(&uart1_handle,UART_FLAG_IDLE) != RESET){
__HAL_UART_CLEAR_IDLEFLAG(&uart1_handle);
printf("recv: %s\r\n",uart1_rx_buf);
uart1_clear();//在这里,已经把buf清空了
}
}
• main.c
cpp
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "bluetooth.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* LED初始化 */
uart1_init(115200);
bt_init(115200);
uint8_t i = 0;
while(1)//流水灯实验
{
bt_send("hello bt%d\r\n",i++);
delay_ms(500);
}
}
1.5 实战2,用蓝牙控制插座
• bluetooth.c
cpp
#include "bluetooth.h"
#include "stdio.h"
#include "string.h"
#include "stdarg.h"
#include "plugin.h"
UART_HandleTypeDef uart2_handle = {0};//串口的句柄
uint8_t uart2_rx_buf[UART2_RX_BUF_SIZE];//定义接收的数据存放位置
uint16_t uart2_rx_len = 0;//计数器,表示接收了多少个数
//串口初始化
void bt_init(uint32_t baudrate){
uart2_handle.Instance = USART2;//选择串口2
uart2_handle.Init.BaudRate = baudrate;
uart2_handle.Init.Mode = UART_MODE_TX_RX;
uart2_handle.Init.Parity = UART_PARITY_NONE;//不打开检验位
uart2_handle.Init.StopBits = UART_STOPBITS_1;//1个停止位
uart2_handle.Init.WordLength = UART_WORDLENGTH_8B;//传输字长为8位
uart2_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;//硬件流不打开
HAL_UART_Init(&uart2_handle);
}
void uart2_clear(){
memset(uart2_rx_buf,0,sizeof(uart2_rx_buf));
uart2_rx_len = 0;
}
void USART2_IRQHandler(){
uint8_t receive_data = 0;
if(__HAL_UART_GET_FLAG(&uart2_handle,UART_FLAG_RXNE) != RESET){//看是不是被置1了
if(uart2_rx_len >= sizeof(uart2_rx_buf))
uart2_rx_len = 0;//接收前需要判断一下,,uart2_cnt是否超出总长度uart2_RX_BUF_SIZE
HAL_UART_Receive(&uart2_handle,&receive_data,1,1000);//接收数据
uart2_rx_buf[uart2_rx_len++] = receive_data;//把接收到的数据放进数组
//HAL_UART_Transmit(&uart2_handle,&receive_data,1,1000);
}
if(__HAL_UART_GET_FLAG(&uart2_handle,UART_FLAG_IDLE) != RESET){
__HAL_UART_CLEAR_IDLEFLAG(&uart2_handle);
printf("bt recv: %s\r\n",uart2_rx_buf);
if(strstr((char *)uart2_rx_buf,"on") != NULL){
plugin_on();
}else if(strstr((char *)uart2_rx_buf,"off") != NULL){
plugin_off();
}
uart2_clear();//在这里,已经把buf清空了
}
}
//void send_buf(char* buf,uint8_t size){
//
//
// HAL_UART_Transmit(&uart2_handle,(uint8_t *)buf,size,100);
//}
void bt_send(char * format, ...)
{
uint8_t send_buf[128] ={0};
va_list arg;
va_start(arg, format);
vsprintf((char *)send_buf, format, arg);
va_end(arg);
HAL_UART_Transmit(&uart2_handle, send_buf, sizeof(send_buf), 100);
}
• 在uart.c同上面第一实操的是一样的。
• plugin.c
cpp
#include "plugin.h"
void plugin_init(){//继电器可以看作成一个开关,开关连接着负载,这里的负载是个喇叭
GPIO_InitTypeDef gpio_init = {0};
__HAL_RCC_GPIOB_CLK_ENABLE();//使能GPIO时钟
gpio_init.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init.Pin = GPIO_PIN_6;//推挽输出
gpio_init.Pull = GPIO_PULLUP;//默认上拉
gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&gpio_init);//初始化gpio
plugin_off();//关灯
}
void plugin_on(){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET);
}
void plugin_off(){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET);
}
uint8_t get_plugin_staut(){
return (uint8_t) HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6);
}
• main.c
cpp
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "bluetooth.h"
#include "plugin.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* LED初始化 */
uart1_init(115200);
bt_init(115200);
plugin_init();
uint8_t i = 0;
while(1)//流水灯实验
{
bt_send("hello bt%d\r\n",i++);
delay_ms(500);
}
}
2. 4G模块
2.1 内网穿透
• 内网穿透的原理就像在内网和外网之间搭建了一座桥梁,使得外部网络可以穿过内网的障碍,直接访问内部的设备。如图:

• 内网里面的pc可以通过用网络调试助手搭建一个服务器,然后用花生壳配置生成一个可以给外界设备使用的公网地址。
• 然后可以在EC05配置4g模块,将地址和端口传进去。
2.2 4g模块,如图:

2.3 4g模块指示灯作用,如图:

• 注意:DATA和LINK反了,绿色应该时连接服务器,橙色应该时 串口接收/发送数据。
2.4 实操,4g控制led
• led.c
cpp
#include "led.h"
void led_init(){
GPIO_InitTypeDef gpio_init = {0};
__HAL_RCC_GPIOB_CLK_ENABLE();//使能GPIO时钟
gpio_init.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init.Pin = GPIO_PIN_8 | GPIO_PIN_9;//推挽输出
gpio_init.Pull = GPIO_PULLUP;//默认上拉
gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&gpio_init);//初始化gpio
led1_off();//关灯
led2_off();
}
void led1_on(){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);
}
void led1_off(){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
}
void led1_toggle(){
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}
void led2_on(){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);
}
void led2_off(){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);
}
void led2_toggle(){
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
}
• e840.c
cpp
#include "e840.h"
#include "stdio.h"
#include "string.h"
#include "delay.h"
#include "stdarg.h"
UART_HandleTypeDef e840_handle = {0};//串口的句柄
uint8_t e840_rx_buf[E840_RX_BUF_SIZE];//定义接收的数据存放位置
uint16_t e840_cnt = 0;//计数器,表示接收了多少个数
uint16_t e840_cntPre = 0;//保存接收前一个数是第几个数
//串口初始化
void e840_uart_init(uint32_t baudrate){
e840_handle.Instance = USART2;//选择串口2
e840_handle.Init.BaudRate = baudrate;
e840_handle.Init.Mode = UART_MODE_TX_RX;
e840_handle.Init.Parity = UART_PARITY_NONE;//不打开检验位
e840_handle.Init.StopBits = UART_STOPBITS_1;//1个停止位
e840_handle.Init.WordLength = UART_WORDLENGTH_8B;//传输字长为8位
e840_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;//硬件流不打开
HAL_UART_Init(&e840_handle);
}
uint8_t e840_wait_receive(){
if(e840_cnt == 0)
return E840_ERROR;
if(e840_cnt == e840_cntPre){
e840_cnt = 0;
return E840_EOK;
}
e840_cntPre = e840_cnt;
return E840_ERROR;
}
void e840_clear(){
memset(e840_rx_buf,0,sizeof(e840_rx_buf));
e840_cnt = 0;
}
uint16_t e840_reveice_data(char * recv_data){
if(e840_wait_receive() == E840_EOK){
printf("e840 recv: %s\r\n",e840_rx_buf);
memcpy(recv_data,e840_rx_buf,strlen((char *)e840_rx_buf));//把接收区的数据拷贝出去
e840_clear();
return strlen(recv_data);
}
return 0;
}
void e840_init(uint32_t baudrate){
e840_uart_init(baudrate);
}
void USART2_IRQHandler(){
uint8_t receive_data = 0;
if(__HAL_UART_GET_FLAG(&e840_handle,UART_FLAG_RXNE) != RESET){//看是不是被置1了
if(e840_cnt >= sizeof(e840_rx_buf))
e840_cnt = 0;//接收前需要判断一下,,e840_cnt是否超出总长度e840_RX_BUF_SIZE
HAL_UART_Receive(&e840_handle,&receive_data,1,1000);//接收数据
e840_rx_buf[e840_cnt++] = receive_data;//把接收到的数据放进数组
//E840_clear();
//HAL_UART_Transmit(&E840_handle,&receive_data,1,1000);
}
}
• main.c
cpp
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "e840.h"
#include "string.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* LED初始化 */
uart1_init(115200);
e840_init(115200);
printf("hello world\r\n");
char recv_data[E840_RX_BUF_SIZE];
while(1)
{
e840_reveice_data(recv_data);
if(strstr(recv_data,"ON") != NULL)
led1_on();
else if(strstr(recv_data,"OFF") != NULL)
led1_off();
delay_ms(10);
}
}