这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 有奖活动 » 人体红外PIR感应传感器使用(ESP32S3)

共1条 1/1 1 跳转至页

人体红外PIR感应传感器使用(ESP32S3)

助工
2026-11-03 21:57:55     打赏

摘要:主要使用ESP32S3开发板外接人体红外PIR感应传感器模块,当传感器检测到有人经过时,在网页UI界面上显示当前经过的时间以及历史经过的时间记录等,实现人体感应监测效果;


一、硬件介绍

1、主控开发板

使用的是XIAO ESP32S3开发板,集成ESP32S3 32位双核处理器芯片,最高运行频率240MHz,板载8MB PSRAM和8MB FLASH;

6.png


功能引脚示意图

6.png


2、人体感应传感器模块(SY-DP-36BM)

该模组属于被动红外人体运动检测模块,整套系统由热释电传感探头、菲涅尔透镜、专用信号处理芯片、稳压外围电路四部分协同完成人体感应检测;

458.png


通过跳线帽可配置两种触发模式 H:重复触发 / L:不重复触发;

> 不重复触发:感应输出高电平后,延时时间一结束,输出将自动从高电平变为低电平

> 可重复触发: 感应输出高电平后,在延时时间段内,如果有人体在其感应范围内活动,其输出将一直保持高电平,直到人离开后才延时将高电平变为低电平

97.png


可以通过上方的距离 / 时间调节旋钮,来改变对应的感应距离范围和感应延时时间;

> 延时时间(默认3秒): 3 ~ 300s

> 在延时时间段内,不再接收任何感应信号

> 感应距离(默认4m):4 ~ 8m


主要特性

工作电压范围:DC 5-20V(5V时,高电平 3V / 低 0V)

触发方式:重复触发 / 不可重复触发

延时时间3-300S(可调) 

感应角度100度

感应距离4-8米(可调) 




二、功能实现

1、硬件搭建

将传感器模块的OUT引脚连接至开发板的D7引脚上;


实物搭建效果:

25.png

2、功能效果

功能:当传感器模块监测到有人经过时,在WIFI网页的UI界面上记录并显示此时经过的时间,以及历史经过的时间记录;


UI界面效果

> 上方显示:当前有人 / 无人状态以及最新触发时间

> 下方显示:历史触发时间记录(10条触发记录)

1.png


模块主要工作原理

当前模块传感器主要通过热释电红外来检测人体的移动,模块通电后需约10s进行初始化,然后进入检测状态:当有人进入模块感应范围时,OUT引脚输出高电平,人离开感应范围则自动延时关闭高电平,输出低电平;


热释电晶体元件会随温度变化产生电荷:人体会向外辐射 10μm 左右的红外热辐射,只有红外辐射强度发生变化时,晶体才会输出微弱电信号,人静止不动、红外辐射恒定不变时,传感器无输出;


> 当前模块的模式为可重复触发模式

> 延时时间: 3s

> 感应距离:4m




三、代码编写

主要相关Arduino代码

#include <WiFi.h>
#include <WebServer.h>

#define PIR_PIN D7

const char* ssid     = "WIFI_SSID";
const char* password = "WIFI_PASS";

WebServer server(80);
const char* ntpServer = "ntp.aliyun.com"; 
const long gmtOffset_sec = 8 * 3600;         
const long daylightOffset_sec = 0;         

unsigned long lastTime = 0;                   // 上一次读取传感器的时间标记
const unsigned long interval = 500;           // 传感器读取间隔
bool pirState = false;                        // PIR传感器状态标记,false无人,true有人

#define MAX_HISTORY 10                         // 最大保存历史触发记录条数
String triggerHistory[MAX_HISTORY];           // 存储触发时间的字符串数组
int historyCount = 0;                         // 当前已经存储的记录数量


// 获取格式化后的北京时间
String getBeijingTime()
{
  struct tm timeinfo;
  // 读取失败返回--
  if (!getLocalTime(&timeinfo)) return "--";
  char buf[32];
  // 将时间结构体格式化为 年-月-日 时:分:秒 的字符串
  strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &timeinfo);
  return String(buf);
}

// 添加一条新的触发记录
void addHistory(String time)
{
  for (int i = MAX_HISTORY - 1; i > 0; i--) {
    triggerHistory[i] = triggerHistory[i - 1];
  }
  triggerHistory[0] = time;  // 新记录存入数组头部
  if (historyCount < MAX_HISTORY) historyCount++;
}

// 返回JSON数据给网页显示
void handleApi()
{
  String json = "{";
  json += "\"pirState\":" + String(pirState ? "true" : "false") + ","; // 传感器有人 / 无人状态
  json += "\"historyCount\":" + String(historyCount) + ",";             // 当前记录总数
  json += "\"history\":[";                                             // 历史记录数组
  // 循环拼接所有历史时间
  for(int i=0;i<historyCount;i++){
    json += "\"" + triggerHistory[i] + "\"";
    if(i != historyCount-1) json += ",";
  }
  json += "]}";
  server.send(200, "application/json", json);
}


// 网页显示界面UI
void handleRoot()
{
  String html = "<!DOCTYPE html>";
  html += "<html><head><meta charset='utf-8'>";
  html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
  html += "<title>人体感应监测界面</title>";
  html += "<style>";
  html += "*{margin:0;padding:0;box-sizing:border-box;}";
  html += "body{font-family:-apple-system,'Segoe UI','Microsoft YaHei',sans-serif;";
  html += "background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);min-height:100vh;";
  html += "display:flex;justify-content:center;align-items:flex-start;padding:20px;}";
  html += ".card{background:#fff;border-radius:20px;box-shadow:0 20px 60px rgba(0,0,0,.2);";
  html += "width:100%;max-width:420px;padding:32px 28px;margin-top:40px;}";
  html += "h2{text-align:center;color:#333;font-size:22px;margin-bottom:24px;font-weight:600;}";
  html += ".box{text-align:center;padding:20px;border-radius:14px;margin-bottom:20px;transition:0.3s;}";
  html += ".box-on{background:linear-gradient(135deg,#f093fb 0%,#f5576c 100%);color:#fff;}";
  html += ".box-off{background:#f5f6fa;color:#999;}";
  html += ".box-label{font-size:14px;opacity:.85;margin-bottom:6px;}";
  html += ".box-text{font-size:28px;font-weight:700;}";
  html += ".history-title{font-size:15px;color:#555;margin-bottom:12px;font-weight:600;";
  html += "border-left:4px solid #764ba2;padding-left:10px;}";
  html += ".history-list{list-style:none;}";
  html += ".history-list li{padding:12px 14px;margin-bottom:8px;background:#f8f9ff;";
  html += "border-radius:10px;font-size:14px;color:#555;display:flex;align-items:center;gap:10px;transition:0.2s;}";
  html += ".dot{width:8px;height:8px;border-radius:50%;background:#f5576c;flex-shrink:0;}";
  html += ".empty{text-align:center;color:#bbb;padding:20px;font-size:14px;}";
  html += "</style></head><body>";
  html += "<div>";
  html += "<h2>人体感应监测界面</h2>";

  // 当前状态
  html += "<div id='statusBox' class='box box-off'>";
  html += "<div>当前状态</div>";
  html += "<div id='statusText'>无人</div></div>";

  // 最新触发时间
  html += "<div id='latestBox' class='box box-off'>";
  html += "<div>最新触发时间</div>";
  html += "<div id='latestTime'>暂无记录</div></div>";

  // 历史记录
  html += "<div>历史触发记录(<span id='historyNum'>0</span> 条)</div>";
  html += "<ul id='historyList'></ul>";
  html += "</div>";

  html += "<script>";
  html += "let lastHash = '';";  // 保存上一次数据校验值,用于判断数据是否变化
  html += "function getHash(d){return d.pirState + '|' + d.historyCount + '|' + d.history.join(',');}";
  html += "async function updateData(){try{";
  html += "const res = await fetch('/api',{cache:'no-cache'});";
  html += "const d = await res.json();";  // 解析返回的json
  html += "const h = getHash(d);";
  html += "if(h === lastHash) return;";  
  html += "lastHash = h;";

  // 更新当前状态
  html += "const sBox=document.getElementById('statusBox');";
  html += "const sTxt=document.getElementById('statusText');";
  html += "if(d.pirState){sBox.className='box box-on';sTxt.innerText='有人';}";
  html += "else{sBox.className='box box-off';sTxt.innerText='无人';}";

  // 更新最新触发时间
  html += "const lBox=document.getElementById('latestBox');";
  html += "const lTxt=document.getElementById('latestTime');";
  html += "if(d.history.length>0){lBox.className='box box-on';lTxt.innerText=d.history[0];}";
  html += "else{lBox.className='box box-off';lTxt.innerText='暂无记录';}";

  // 更新历史记录
  html += "document.getElementById('historyNum').innerText=d.historyCount;";
  html += "const list=document.getElementById('historyList');list.innerHTML='';";
  html += "if(d.history.length===0){list.innerHTML='<div class=\"empty\">暂无触发记录,等待人体经过…</div>';}";
  html += "else{d.history.forEach(item=>{let li=document.createElement('li');li.innerHTML='<span class=\"dot\"></span>'+item;list.appendChild(li);});}";
  html += "}catch(e){console.log('获取失败',e);}}"; 
  html += "setInterval(updateData,2000);updateData();"; 
  html += "</script>";
  html += "</body></html>";
  server.send(200, "text/html", html);
}


void setup() {
  Serial.begin(115200);             
  pinMode(PIR_PIN, INPUT);              // PIR引脚
  pinMode(LED_BUILTIN, OUTPUT);        
  digitalWrite(LED_BUILTIN, HIGH); 

  WiFi.begin(ssid, password);           // WiFi连接
  Serial.print("正在连接WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi连接成功");
  Serial.print("网页地址:");
  Serial.println(WiFi.localIP());        // 网页显示IP地址

  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); // 配置NTP自动同步时间
  server.on("/", handleRoot);         // 注册路由
  server.on("/api", handleApi);      
  server.begin();                     // 启动网页服务器
}


void loop() {
  server.handleClient();  

  unsigned long now = millis();
  // 间隔读取传感器引脚状态
  if (now - lastTime >= interval) {
    lastTime = now;
    bool val = digitalRead(PIR_PIN);  // 读取OUT引脚电平
    // 状态发生变化
    if (val != pirState) {
      pirState = val;
      if (pirState) {
        // 检测到经过
        String t = getBeijingTime();
        addHistory(t);
        Serial.println("【触发】" + t);
        digitalWrite(LED_BUILTIN, LOW);
      } else {
        // 离开
        Serial.println("【离开】");
        digitalWrite(LED_BUILTIN, HIGH);
      }
    }
  }
}




四、效果演示

21.gif





  



共1条 1/1 1 跳转至页

回复

匿名不能发帖!请先 [ 登陆 注册 ]