技能梳理15@stm32+lora(sx1276)+oled+ds18b20+土壤湿度传感器+按键+蜂鸣器+电路板
从机采集温湿度及超标报警并显示在oled屏幕上,按下按键通过lora模块传输给主机,主机再也显示在其搭载的屏幕上
从机采集温湿度及超标报警并显示在oled屏幕上,按下按键通过lora模块传输给主机,主机再也显示在其搭载的屏幕上
1、项目简介
2、实现逻辑
#从机实现对温湿度数据的检测
#从机将数据显示到oled上
#从机检测到数据超标后进行蜂鸣器报警
#从机将检测到的数据进行编码并无线传输
#从机按键能控制数据发送和停止(按一下一直发,按一下停,发送快一些)
#从机上两个指示灯,绿色代表正在发送数据,红色代表停止发送
#主机通过规定好的协议进行数据解析及在oled上显示数据
3、应用场景
#远程(中距离)检测温湿度
#粮库使用
4、核心代码梳理
//从机程序
void RF_Initial(uint8_t mode) //0 rx 1 tx
{
SX1276_Init(MODE_LORA);
SX1276_LoRa_SetDataRate(2);
SX1276_SetFreq(23); //23,433Hz
SX1276_SetPower(15, 1); //20dBm
SX1276_SetRxMode(); //
}
uint8_t RF_SendPacket(uint8_t *Sendbuffer, uint8_t length)
{
uint8_t error = 0, i=0, ack_len=0, ack_buffer[65]= { 0 }, TxBuffer[100];
SX1276_SendPacket(Sendbuffer, length);
return (0);
}
void RF_RecvHandler(void)
{
uint8_t error=0, i=0, length=0, recv_buffer[20]= { 0 };
uint8_t temp;
uint8_t TL,TH;
uint16_t tem;
length = SX1276_ReceivePacket(recv_buffer); //
if(length==9)
{
if ((recv_buffer[0]==0x55) && (recv_buffer[1]==0xAA))
{
uint16_t sum = recv_buffer[3]+recv_buffer[4]+recv_buffer[5]+recv_buffer[6]+recv_buffer[7];
sum = sum & 0x00ff;
if(sum != recv_buffer[8])
return;
//HAL_GPIO_TogglePin(GPIOC, BOARD_LED_Pin);
TH = recv_buffer[4];
TL = recv_buffer[5];
if(TH>7)
{
TH=~TH;
TL=~TL;
temp=0;//温度为负
} else temp=1;//温度为正
tem=TH; //获得高八位
tem<<=8;
tem+=TL;//获得底八位
check_temp = (float)tem*0.625;//转换
if(!temp)
check_temp = -check_temp;
check_temp = check_temp/10;
printf("The temp is: %.2fC\r\n", check_temp);
check_hump = (float)recv_buffer[7];
check_hump = recv_buffer[6] + check_hump/100;
printf("The hump is: %.2f%c\r\n", check_hump, '%');
}
SX1276_SetRxMode();
}
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
uint32_t clk;
/* 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_ADC1_Init();
MX_I2C1_Init();
MX_SPI1_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
MX_USART3_UART_Init();
/* USER CODE BEGIN 2 */
OLED_Init();
OLED_ColorTurn(0);//0正常显示,1 反色显示
OLED_DisplayTurn(0);//0正常显示 1 屏幕翻转显示
OLED_Refresh();
DS18B20_Init();
RF_Initial(0);
OLED_Clear();
OLED_ShowString(0,0,"TEMP: . C",16);
OLED_ShowString(0,14,"HUMP: . %",16);
OLED_ShowString(0,28,"ALARM:",24);
OLED_ShowNum(53,0,12,2,16);
OLED_ShowNum(77,0,34,2,16);
OLED_ShowString(43,0,"-",16);
OLED_ShowString(80,28,"T",24);
OLED_ShowString(100,28,"H",24);
OLED_ShowString(100,28," ",24);
OLED_Refresh();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
#ifdef TX
//check temp
check_temp = DS18B20_Get_Temp()/10;
intT = (int)check_temp; /*合成实际温度整数部分****精度相对上面的更高*/
float tem_tmp = check_temp-intT; /*合成实际温度小数部分*/
decT = tem_tmp*100;
//check hump
check_hump = 4092-ADC_num;
check_hump = (float)(check_hump/3292*100);//ad->hump%
if((check_hump >= 0) && (check_hump <= 100))
{
intH = (int)check_hump;
float hum_tmp = check_hump - intH;
hum_tmp *= 100;
decH = hum_tmp;
lora_order[6] = intH;
lora_order[7] = decH;
}
HAL_ADC_Start_IT(&hadc1);
//check alarm
if((check_temp > TEMP_ALARM_UP) || (check_hump > HUMP_ALARM_UP) || (check_temp < TEMP_ALARM_DOWN)
|| (check_hump < HUMP_ALARM_DOWN)) //超标报警
{
HAL_GPIO_WritePin(GPIOC, BEEP_Pin, GPIO_PIN_SET);
if((check_temp > TEMP_ALARM_UP) || (check_temp < TEMP_ALARM_DOWN))
OLED_ShowString(80,28,"T",24);
else OLED_ShowString(80,28," ",24);
if((check_hump > HUMP_ALARM_UP) || ((check_hump < HUMP_ALARM_DOWN)))
OLED_ShowString(100,28,"H",24);
else OLED_ShowString(100,28," ",24);
OLED_Refresh();
}
else
{
HAL_GPIO_WritePin(GPIOC, BEEP_Pin, GPIO_PIN_RESET);
OLED_ShowString(80,28," ",24);
OLED_ShowString(100,28," ",24);
OLED_Refresh();
}
//send by lora / led display
if(clk % 10 == 0)
{
if(send_flag%2)//send
{
uint16_t sum = lora_order[3]+lora_order[4]+lora_order[5]+lora_order[6]+lora_order[7];
sum = sum & 0x00ff;
lora_order[8] = sum;
RF_SendPacket(lora_order, 9);
HAL_GPIO_WritePin(GPIOB, SEND_LED_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, STOP_LED_Pin, GPIO_PIN_SET);
}
else//stop
{
HAL_GPIO_WritePin(GPIOB, SEND_LED_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB, STOP_LED_Pin, GPIO_PIN_RESET);
}
if(check_temp < 0)
OLED_ShowString(43,0,"-",16);
else
OLED_ShowString(43,0," ",16);
OLED_ShowNum(53,0,intT,2,16);
OLED_ShowNum(77,0,decT,2,16);
OLED_ShowNum(53,14,intH,2,16);
OLED_ShowNum(77,14,decH,2,16);
OLED_Refresh();
}
HAL_Delay(100);
//HAL_GPIO_TogglePin(GPIOC, BOARD_LED_Pin);
clk++;
#endif
#ifdef RX
RF_RecvHandler();
#endif
}
/* USER CODE END 3 */
}
//复位DS18B20
void DS18B20_Rst(void)
{
HAL_GPIO_WritePin(DS18B20_GPIO_Port,DS18B20_Pin,GPIO_PIN_RESET); //拉低DQ
delay_us(750); //拉低750us
HAL_GPIO_WritePin(DS18B20_GPIO_Port,DS18B20_Pin,GPIO_PIN_SET); //DQ=1
delay_us(15); //15US
}
//等待DS18B20的回应
//返回1:未检测到DS18B20的存在
//返回0:存在
uint8_t DS18B20_Check(void)
{
uint8_t retry=0;
//SET PB1 INPUT
while (HAL_GPIO_ReadPin(DS18B20_GPIO_Port,DS18B20_Pin) && retry<200)
{
retry++;
delay_us(1);
};
if(retry>=200)return 1;
else retry=0;
while (!HAL_GPIO_ReadPin(DS18B20_GPIO_Port,DS18B20_Pin)&&retry<240)
{
retry++;
delay_us(1);
};
if(retry>=240)return 1;
return 0;
}
//从DS18B20读取一个位
//返回值:1/0
uint8_t DS18B20_Read_Bit(void) // read one bit
{
uint8_t data;
HAL_GPIO_WritePin(DS18B20_GPIO_Port,DS18B20_Pin,GPIO_PIN_RESET);
delay_us(2);
HAL_GPIO_WritePin(DS18B20_GPIO_Port,DS18B20_Pin,GPIO_PIN_SET);
delay_us(12);
if(HAL_GPIO_ReadPin(DS18B20_GPIO_Port,DS18B20_Pin))data=1;
else data=0;
delay_us(50);
return data;
}
//从DS18B20读取一个字节
//返回值:读到的数据
uint8_t DS18B20_Read_Byte(void) // read one byte
{
uint8_t i,j,dat;
dat=0;
for (i=1; i<=8; i++)
{
j=DS18B20_Read_Bit();
dat=(j<<7)|(dat>>1);
}
return dat;
}
//写一个字节到DS18B20
//dat:要写入的字节
void DS18B20_Write_Byte(uint8_t dat)
{
uint8_t j;
uint8_t testb;
for (j=1; j<=8; j++)
{
testb=dat&0x01;
dat=dat>>1;
if (testb)
{
HAL_GPIO_WritePin(DS18B20_GPIO_Port,DS18B20_Pin,GPIO_PIN_RESET);// Write 1
delay_us(2);
HAL_GPIO_WritePin(DS18B20_GPIO_Port,DS18B20_Pin,GPIO_PIN_SET);
delay_us(60);
}
else
{
HAL_GPIO_WritePin(DS18B20_GPIO_Port,DS18B20_Pin,GPIO_PIN_RESET);// Write 0
delay_us(60);
HAL_GPIO_WritePin(DS18B20_GPIO_Port,DS18B20_Pin,GPIO_PIN_SET);
delay_us(2);
}
}
}
//开始温度转换
void DS18B20_Start(void)// ds1820 start convert
{
DS18B20_Rst();
DS18B20_Check();
DS18B20_Write_Byte(0xcc);// skip rom
DS18B20_Write_Byte(0x44);// convert
}
//初始化DS18B20的IO口 DQ 同时检测DS的存在
//返回1:不存在
//返回0:存在
uint8_t DS18B20_Init(void)
{
DS18B20_Rst();
return DS18B20_Check();
}
//从ds18b20得到温度值
//精度:0.1C
//返回值:温度值 (-550~1250)
float DS18B20_Get_Temp(void)
{
uint8_t temp;
uint8_t TL,TH;
short tem;
float return_tem;
DS18B20_Start(); // ds1820 start convert
DS18B20_Rst();
DS18B20_Check();
DS18B20_Write_Byte(0xcc);// skip rom
DS18B20_Write_Byte(0xbe);// convert
TL=DS18B20_Read_Byte(); // LSB
TH=DS18B20_Read_Byte(); // MSB
lora_order[4] = TH;
lora_order[5] = TL;
if(TH>7)
{
TH=~TH;
TL=~TL;
temp=0;//温度为负
} else temp=1;//温度为正
tem=TH; //获得高八位
tem<<=8;
tem+=TL;//获得底八位
return_tem = (float)tem*0.625;//转换
if(temp)return return_tem; //返回温度值
else return -return_tem;
}
/**
* @brief EXIT中断回调函数
* @param GPIO_Pin —— 触发中断的引脚
* @retval none
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
/* 判断哪个引脚触发了中断 */
switch(GPIO_Pin)
{
case GPIO_PIN_8:
/* 处理GPIO8发生的中断 */
delay_us(10000);//消抖
if(HAL_GPIO_ReadPin(SEND_BUT_GPIO_Port, SEND_BUT_Pin))
{
send_flag++;
HAL_GPIO_TogglePin(GPIOC, BOARD_LED_Pin);
}
break;
default:
break;
}
}
//get hump by adc conv
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
HAL_ADC_Stop_IT(hadc);
ADC_num = HAL_ADC_GetValue(hadc);
}
//主机程序
//和从机差不太多,见资料包
5、部分参考资料
ds18b20参考:
https://mculover666.blog.csdn.net/article/details/108302704?utm_medium=distribute.pc_relevant.
none-task-blog-BlogCommendFromMachineLearnPai2-1.control&dist_request_id=&depth_1-utm_source=distribute.
pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
https://blog.csdn.net/qq153471503/article/details/102858978
yl69参考:
https://blog.csdn.net/qq_41960161/article/details/106118778?utm_medium=distribute.
pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242
LCD12864参考:
https://blog.csdn.net/as480133937/article/details/97765912?utm_medium=distribute.pc_relevant_download.
none-task-blog-baidujs-2.nonecase&depth_1-utm_source=distribute.pc_relevant_download.none-task-blog-baidujs-2.nonecase
通讯协议格式说明(注:为16进制):
帧头 数据帧长度 设备ID 温度(℃) 湿度(%) 和校验(低8位)
55 AA 1字节(0-255) 1字节(0-255) 2字节(TH TL) 2字节(HH HL) 1字节
温度转换:u8 temp;
u8 TL,TH;
short tem;
TL=DS18B20_Read_Byte(); // LSB
TH=DS18B20_Read_Byte(); // MSB
if(TH>7)
{
TH=~TH;
TL=~TL;
temp=0;//温度为负
}else temp=1;//温度为正
tem=TH; //获得高八位
tem<<=8;
tem+=TL;//获得底八位
tem=(float)tem*0.625;//转换
if(temp) return tem; //返回温度值
else return -tem; //tem是实际温度的10倍
例:01 17 = (1*256+23)*0.625=174.375 则实际温度是17.4375℃(举例用) 温度范围 -55℃-+125℃
湿度转换:float hum = HH+HL%100 例:24 57 = 36+87%100 = 36.87 ->湿度36.87%
和校验 = (设备ID+TH+TL+HH+HL)低8位
例:55 AA 06 01(默认ID为01) 01 17 24 57 94
主从机硬件pcb、原理图见资料包
6、注意事项
#sx1276是spi通信,也有那种直接封装好使用串口的lora模块,比如E19
#本来要使用lcd做显示,后来种种原因使用的oled 9.6
#注意主从机之间的协议
完整可运行项目地址
或 点击下方”大饼匠人“卡片,关注并回复"15"免费下载开发资料
更多推荐
所有评论(0)