项目说明
本项目以 STM32 为核心控制器,构建智能路灯控制系统。系统利用 BH1750 采集环境光照强度,结合人体红外传感器(如 HC-SR501)判断人员活动情况,从而实现“按需照明”。
控制策略为:当环境光照强度低于设定阈值且检测到有人活动时,开启路灯;当光照强度高于阈值或长时间无人时,自动关闭路灯。
在通信方面,引入 ESP-01S 模块,通过 MQTT 协议将设备运行状态(包括光照值、人员检测状态及路灯开关状态)实时上报至服务器,实现远程监控与数据采集。
项目开源链接见文章结尾。
项目硬件连接
系统以 STM32 为核心控制单元,各外设模块连接关系如下:
-
人体红外传感器(HC-SR501) 信号输出端连接至 STM32 的 PA0 引脚,用于检测人体活动并产生中断或状态输入信号。
工作跳帽接到 L上,不重复触发。 -
光照传感器(BH1750) SCL → PB6 SDA → PB7 ADDR 引脚接地,器件地址配置为低地址模式。
-
Wi-Fi 通信模块(ESP-01S) 连接至 STM32 的 USART3 串口,用于 MQTT 数据通信; 同时使用 USART1 作为调试串口,便于系统调试与日志输出。
-
LED 指示灯(模拟路灯) 连接至 STM32 的 PC13 引脚,用于模拟路灯的开关状态输出。
ESP01S固件状态
ESP01S 需要使用带 MQTT 固件的版本。在安信可的官网可以找到,使用1471号固件,他也有对应的说明文档。如果嫌麻烦,可以直接看我文件中提供的文档和说明。 ESP01S官网:docs.ai-thinker.com/esp8266/
软件设计
软件设计的整体流程是: 1、初始化各个设备。 2、外部中断触发检测人体红外状态。每次状态保存一段时间。 3、每隔1S检测一次光照强度。并且使用MQTT上报数据。
void main_control_init(void)
{
int ret = 0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
// 常用外设初始化
bsp_usart1_init(115200);
bsp_usart3_init(115200);
delay_init();
led_init();
hc_sr50x_init();
soft_i2c_init(SOFT_I2C1);
bh1750_init(BH1750_ADDR_L,BH1750_CONT_H_RES);
bh1750_set_mtreg(BH1750_ADDR_L,69);
printf("SmartLed V1.2 Build Time: %s %s\r\n", __DATE__, __TIME__);
esp_init();
ret = esp_at_cmd(&esp01s_device, "AT+RST", "OK", 2000);
printf("AT+RESET resp: %s\n", esp01s_device.resp_buf);
if(ret != 0) while(1);
delay_ms(500);
ret = esp_at_cmd(&esp01s_device, "AT", "OK", 2000);
printf("AT resp: %s\n", esp01s_device.resp_buf);
if(ret != 0) while(1);
ret = esp_at_cmd(&esp01s_device, "AT+CWMODE=1", "OK", 2000);
printf("AT+CWMODE resp: %s\n", esp01s_device.resp_buf);
if(ret != 0) while(1);
ret = esp_at_cmd(&esp01s_device, "AT+CWJAP=\"CMCC-XJmL\",\"sR62HiPv\"", "OK", 5000);
printf("AT+CWJAP resp: %s\n", esp01s_device.resp_buf);
if(ret != 0) while(1);
ret = esp_at_cmd(&esp01s_device, "AT+MQTTUSERCFG=0,1,\"lqh\",\"lqh\",\"123\",0,0,\"\"", "OK", 2000);
printf("AT+MQTTUSERCFG resp: %s\n", esp01s_device.resp_buf);
if(ret != 0) while(1);
ret = esp_at_cmd(&esp01s_device, "AT+MQTTCONN=0,\"broker.emqx.io\",1883,0", "OK", 5000);
printf("AT+MQTTCONN resp: %s\n", esp01s_device.resp_buf);
if(ret != 0) while(1);
ret = esp_at_cmd(&esp01s_device, "AT+MQTTSUB=0,\"/lqh/mqtttest/led\",0", "OK", 2000);
printf("AT+MQTTSUB resp: %s\n", esp01s_device.resp_buf);
if(ret != 0) while(1);
}
void main_control_loop(void)
{
uint16_t count = 0;
float lux;
uint8_t hc_sr50x_delay_state = 0;
uint32_t hc_sr50x_state_delay_count = 0;
char mqtt_put_message[256]={0};
uint8_t led_state = 0;
while (1)
{
count++;
// 如果有人 hc_sr50_state 这个状态在中断中置 1
if(hc_sr50_state == 1)
{
// 有人就处于状态延迟状态
hc_sr50x_delay_state = 1;
hc_sr50x_state_delay_count = 0;
hc_sr50_state = 0;
printf("Someone has entered the area\r\n");
}
// 如果处于状态延迟状态
if(hc_sr50x_delay_state == 1)
{
hc_sr50x_state_delay_count++;
if(hc_sr50x_state_delay_count > 200) // 将每次的有人进入状态至少保持 2S
{
// 2S后清空这个 状态延迟状态
hc_sr50x_state_delay_count = 0;
hc_sr50x_delay_state = 0;
}
}
if(count % 100 == 0) // 1S 读取一次光照强度
{
if (bh1750_read_lux(BH1750_ADDR_L, BH1750_CONT_H_RES, &lux) == 0)
{
printf("continuous Light = %.2f lux hc_sr50x_delay_state:%d\r\n", lux, hc_sr50x_delay_state);
}
// 光照小于一定值,且有人处于状态延迟状态时,打开灯
if(lux < 500 && hc_sr50x_delay_state == 1){
led_on();
led_state = 1;
}else{
// 其他情况关灯
led_off();
led_state = 0;
}
// 上报状态信息
sprintf(mqtt_put_message,"AT+MQTTPUB=0,\"/lqh/mqtttest/led_state\",\"%d\",0,0",led_state);
esp_at_cmd(&esp01s_device, mqtt_put_message , "OK", 2000);
printf("AT+MQTTSUB resp: %s\n", esp01s_device.resp_buf);
sprintf(mqtt_put_message,"AT+MQTTPUB=0,\"/lqh/mqtttest/lux\",\"%.2f\",0,0",lux);
esp_at_cmd(&esp01s_device, mqtt_put_message , "OK", 2000);
printf("AT+MQTTSUB resp: %s\n", esp01s_device.resp_buf);
sprintf(mqtt_put_message,"AT+MQTTPUB=0,\"/lqh/mqtttest/hc_sr50x_delay_state\",\"%d\",0,0",hc_sr50x_delay_state);
esp_at_cmd(&esp01s_device, mqtt_put_message , "OK", 2000);
printf("AT+MQTTSUB resp: %s\n", esp01s_device.resp_buf);
}
// 读取 ESP 接收
int ret = esp_read_line(&esp01s_device);
if(ret == 0)
{
printf("Received line: %s", esp01s_device.resp_buf);
esp_dispatch_line(&esp01s_device, (char *)esp01s_device.resp_buf);
} else {
// 没有完整行数据,继续等待
}
delay_ms(10);
}
}
void USART3_IRQHandler(void)
{
if (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
{
uint8_t data = USART_ReceiveData(USART3);
ringbuffer_putchar(&esp01s_device.rx_rb, data);
USART_ClearITPendingBit(USART3, USART_IT_RXNE);
}
}
代码中使用需要修改的部分,一个是连接初始化,一个是MQTT部分。 首先连接WIFI需要是自己的WIFI。
ret = esp_at_cmd(&esp01s_device, "AT+CWJAP=\"CMCC-XJmL\",\"sR62HiPv\"", "OK", 1000);
printf("AT+CWJAP resp: %s\n", esp01s_device.resp_buf);
还有就是MQTT的连接订阅主题,尽量使用自己的,如果你使用我代码中的,如果你是用的自己服务器还好,但是如果使用的是我这个公用的,那大家可能会冲突。
项目开源链接
本项目资料包单片机软件总工程开源,设计文档,使用文档部分开源。开源部分有讲解视频方便大家跑起来理解。可先查看代码下在自己环境中是否能够正常使用,再决定后续操作。
软件开源链接:关注微信公众号小离的嵌入式笔记。回复关键字开源项目集,在文件中找到【001】基于STM32的智能路灯控制系统设计即可。