esp32c3 配网 mqtt esp32智能配网_物联网

接线图

esp32c3 配网 mqtt esp32智能配网_物联网_02

Demo流程

超声波模块每隔200ms发出一次信号进行测距,如果测量到的物体距离在范围内,则信号为 open_semaphore。

舵机旋转打开盖子,板载灯变亮,串口打印相关信息。

当打开盖子时,记录打开时间,并启动计时器进行定时检测,即每隔500ms进行检测。

如果检测到盖子关闭时间超过了阈值,则重置打开时间,并设置二值信号量状态为关闭。

得到关闭 close_semaphore 信号后,舵机转动进行关盖。

esp32c3 配网 mqtt esp32智能配网_嵌入式硬件_03

代码部分

代码需要用到的库: ESP32Servo

主体部分
#include "sonar.h"
#include "cover.h"
#include "servo.h"
int ledPin = 2;//板载灯
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; // 自旋锁

// 打开盖子
void open_cover()
{
  bool shouldAct = false; // 开盖行为
  
  /*
     临界区是一段代码片段,用于在多任务环境下保护共享资源,以确保对资源的访问不会被并发任务中断或干扰。
     临界区的作用是提供一种互斥机制,使得同一时间只有一个任务可以访问共享资源,避免并发访问导致的数据竞争和不一致性。
  */
  portENTER_CRITICAL(&mux); // 进入临界区
  if (openTime == 0) // 打开时间为0时
    shouldAct = true;
  openTime = micros(); // 记录当前打开盖子的时间
  portEXIT_CRITICAL(&mux);  // 离开临界区

  if (shouldAct)
    servo.write(145); // 舵机旋转145,即开盖°
}

void close_cover()
{
  servo.write(0); // 舵机为0°,即关盖
}

void setup() {
  pinMode(ledPin,OUTPUT); //输出模式
  Serial.begin(115200);
  sonar_init(&mux); // 超声波模块初始化
  cover_detect_init(&mux); // 初始化盖子检测相关的设置
  servo_init(); // 舵机初始化
  close_cover(); // 刚开始为盖上盖子状态
}

void loop() {
  // 超声波有信号回来
  if (xSemaphoreTake(open_semaphore, 0) == pdTRUE) // 打开信号为true
  {
    Serial.println("open"); // 串口打印信息
    open_cover();
    digitalWrite(ledPin, HIGH); // 灯亮
  }

  if (xSemaphoreTake(close_semaphore, 0) == pdTRUE) // 关闭信号为true
  {
    Serial.println("close");
    close_cover();
    digitalWrite(ledPin, LOW); // 灯灭
  }
}
关盖处理

cover.h

#pragma once //预处理指令,用于确保头文件只被编译一次

#include <freertos/FreeRTOS.h>
#include <esp32-hal-timer.h>
#include <freertos/semphr.h>

extern volatile unsigned long openTime;
extern volatile SemaphoreHandle_t close_semaphore;

void cover_detect_init(portMUX_TYPE *mux);

cover.cpp

#include "cover.h"

hw_timer_t *cover_timer = NULL; // 定时器
static portMUX_TYPE *_mux = NULL;
volatile SemaphoreHandle_t close_semaphore; // 关盖信号量
volatile unsigned long openTime = 0; // 打开盖子的时间

// 中断服务程序ISR:检测盖子是否关闭
void IRAM_ATTR close_detect()
{
  portENTER_CRITICAL_ISR(_mux);
  auto now = micros();
  if (openTime != 0 && (now - openTime) >= 4000000) // 打开盖子时间大于等于4s则关闭
  { 
    openTime = 0;
    xSemaphoreGiveFromISR(close_semaphore, NULL);
  }
  portEXIT_CRITICAL_ISR(_mux);
}

void cover_detect_init(portMUX_TYPE *mux)
{
  _mux = mux;
  close_semaphore = xSemaphoreCreateBinary();

  // 检测到关闭部分,0.5秒检测一次
  cover_timer = timerBegin(2, 80, true);  // 初始化计时器2,分频系数80,使能中断
  timerAttachInterrupt(cover_timer, close_detect, true);  // 附加中断处理函数 close_detect 到计时器
  timerAlarmWrite(cover_timer, 500000, true);  // 设置计时器的定时时间为500000微秒(0.5秒),并使能重复触发
  timerAlarmEnable(cover_timer);  // 启动计时器
}
舵机模块

servo.h

#pragma
#include <ESP32Servo.h>

extern Servo servo;

void servo_init();

servo.cpp

#include "myservo.h"

// 舵机部分
Servo servo;
int minUs = 500;
int maxUs = 2500;
int servoPin = 13;

void servo_init()
{
  // 舵机
  ESP32PWM::allocateTimer(1);
  servo.setPeriodHertz(50);
  servo.attach(servoPin, minUs, maxUs);
}
超声波模块

sonar.h

#pragma once
#include <freertos/FreeRTOS.h>
#include <esp32-hal-timer.h>
#include <freertos/semphr.h>

extern volatile SemaphoreHandle_t open_semaphore; // 信号量
void sonar_init(portMUX_TYPE *mux);

sonar.cpp

#include "sonar.h"

volatile SemaphoreHandle_t open_semaphore; // 信号量

// 超声波测距部分
const int trigPin = 17; 
const int echoPin = 18;
int distance = 0;

static portMUX_TYPE *_mux = NULL;
hw_timer_t *sonar_timer = NULL; // 定时器
volatile unsigned long startTime = 0; // 发出超声波时间
volatile unsigned long endTime = 0; // 收到超声波时间

// 硬件定时器ISR
void IRAM_ATTR ping()
{
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(15);
  digitalWrite(trigPin, LOW);
}

// ECHO 引脚ISR
void IRAM_ATTR changeISR() 
{
  auto now = micros(); // 当前时间
  auto state = digitalRead(echoPin);

  portENTER_CRITICAL_ISR(_mux);
  if (state) // 高电平,即刚发出超声波
    startTime = now;
  else
    endTime = now;
// 变成低电平时表示已经收到回声
// 如果 < 10cm 就发信号开盖
  if (!state) {
    auto t = endTime - startTime;
    auto dis = t * 0.01715;
    if (dis <= 10)
    {
      xSemaphoreGiveFromISR(open_semaphore, NULL); // 给一个开盖信号量发送信号
    }
  }
  portEXIT_CRITICAL_ISR(_mux);
}

void sonar_init(portMUX_TYPE* mux)
{
  _mux = mux;
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  open_semaphore = xSemaphoreCreateBinary();

  //测距定时器部分
  sonar_timer = timerBegin(0, 80, true);
  timerAttachInterrupt(sonar_timer, ping, true);
  timerAlarmWrite(sonar_timer, 200000, true); // 定时时间为 0.2s

  // echo引脚的中断
  attachInterrupt(digitalPinToInterrupt(echoPin), changeISR, CHANGE);

  // 开始周期测量
  timerAlarmEnable(sonar_timer);
}