ESP32Demo智能垃圾桶
本文最后更新于51 天前,其中的信息可能已经过时,如有错误请发送邮件到echobydq@gmail.com

前言

这篇文章是关于ESP32的一个小Demo,名称为 智能垃圾桶

涉及到的模块为 SG90舵机和HC-SR04超声波, 使用的编程烧录软件为 Arduino

一开始打算直接扔在ESP32基础教程 – Echo (liveout.cn)这篇文章里,不做过多介绍,因为这个小Demo只用到了舵机和超声波模块。

后来感觉模块虽少,但是涉及到的知识其实挺多的,有PWM、外部中断(硬件)、硬件定时器和二值信号量,所以就单独写了一篇文章。

如果想要再进化一下,可以尝试添加摄像头模块,进行深度学习,分别垃圾种类,这方面还没涉及到,就不多说了。

如果十分感兴趣,可以参考此篇文章:STEAM案例 | 《智能垃圾桶》项目的设计 – 知乎 (zhihu.com)

PS:这个Demo非本人原创,我加了些注释额外代码,并且整理优化成了此篇文章,相关链接会在文章结尾部分给出。

 

接线图

 

Demo流程

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

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

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

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

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

 

 

 

代码部分

代码需要用到的库: 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);
}

相关链接

Demo教程视频: ESP32之智能垃圾桶制作讲解—哔哩哔哩_bilibili

GitHub仓库:https://github.com/PGwind/esp32project

ESP32代码记录:ESP32基础教程 – Echo (liveout.cn)

PS:写程序难免会有问题,而且国内的ChatGPT访问受限

这里就给个朋友搭建的链接吧:chuanwen智能

 

觉得有帮助可以投喂下博主哦~感谢!
作者:Echo
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0协议
转载请注明文章地址及作者哦~

评论

  1. 2月前
    2023-7-23 18:13:45

    太棒啦,已经 star 啦~

    来自重庆
    • Avatar photo
      博主
      豆豆
      2月前
      2023-7-23 18:25:52

      哇,感谢豆豆!

      来自江苏
  2. 一岁
    2月前
    2023-7-16 15:11:54

    来自广西
    • Avatar photo
      博主
      一岁
      2月前
      2023-7-16 19:09:22

      来自江苏

发送评论(请正确填写邮箱地址,否则将会当成垃圾评论处理) 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇