HC-SR04 ultrasonic sensor on a servo sweeping next to a small OLED radar display

ระดับกลาง

สร้างจอแสดงผลเรดาร์อัลตราโซนิก

เซ็นเซอร์จอแสดงผลหุ่นยนต์

กวาดเซนเซอร์อัลตราโซนิกไปตามส่วนโค้งกว้างๆ ด้วยเซอร์โว แล้วดูจอแสดงผลแบบเรดาร์กวาดแบบเรียลไทม์ที่สร้างขึ้นบนหน้าจอ ซึ่งเป็นเส้นกวาดสีเขียวแบบคลาสสิก ทำจากชิ้นส่วนที่ทำงานนี้ได้อย่างแม่เจาะจงอยู่แล้ว

รวมชิ้นส่วนจริง 4 รายการ

ราคารวมชุดนี้: 968.00 ฿

ยังไม่รวมส่วนลดคำสั่งซื้อที่คุณอาจได้รับตอนชำระเงิน

สิ่งที่คุณต้องใช้

เป้าหมายของโครงการ

โครงการนี้เปลี่ยนเซ็นเซอร์ระยะทางตัวเดียวให้เป็นจอแสดงผลเรดาร์กวาดจริง: เซอร์โวจะหมุนเซ็นเซอร์ช้าๆ เป็นวงกว้าง ในขณะที่หน้าจอจะวาดเส้นสำหรับสิ่งที่ตรวจจับได้ในแต่ละมุม ซึ่งเป็นเอฟเฟกต์เส้นกวาดแบบเดียวกับหน้าจอเรดาร์เก่า สร้างขึ้นจากสามส่วนที่ทำงานนี้แยกกันอยู่แล้ว

ตัวควบคุมทำอะไร

ในการวนซ้ำ ตัวควบคุมจะสั่งให้เซอร์โวไปยังมุมใหม่ รอสักครู่เพื่อให้มันเคลื่อนที่ไปถึง สั่งงานเซ็นเซอร์อัลตราโซนิก อ่านค่าระยะทางกลับมา และวาดเส้นใหม่หนึ่งเส้นบนจอแสดงผลก่อนที่จะย้ายไปยังมุมถัดไป กวาด วัด วาด ซ้ำ

เซอร์โวทำอะไร

เซอร์โวจะหมุนเซ็นเซอร์อัลตราโซนิกไปตามแนวโค้งประมาณ 180 องศา ทีละขั้นตอน ทำให้การอ่านค่าระยะทางแต่ละครั้งมีทิศทางทางกายภาพที่แท้จริงที่จะวาด

เซ็นเซอร์อัลตราโซนิกทำอะไร

มันส่งพัลส์อัลตราโซนิกสั้นๆ และจับเวลาว่าเสียงสะท้อนใช้เวลานานแค่ไหนในการกลับมา แปลงเป็นค่าการวัดระยะทาง ซึ่งเป็นหลักการทำงานเดียวกับระบบโซนาร์จริง

จอแสดงผลทำอะไร

OLED จะวาดการอ่านค่าระยะทางใหม่แต่ละครั้งเป็นเส้นที่มุมที่วัดได้ สร้างภาพการกวาดแบบเรดาร์ที่แท้จริงทีละค่า

แนวคิดเกี่ยวกับซอฟต์แวร์และไลบรารี

ให้แต่ละส่วนทำงานแยกกันก่อน: กวาดเซอร์โวตลอดช่วงการเคลื่อนที่ทั้งหมดและยืนยันว่ามันเคลื่อนที่อย่างถูกต้อง, อ่านค่าระยะทางเพียงครั้งเดียวและพิมพ์ออกมา, จากนั้นวาดเส้นทดสอบแบบคงที่หนึ่งเส้นบนจอแสดงผลก่อนที่จะรวมทั้งสามส่วนเข้าด้วยกันในวงวนการกวาด

การทดสอบและการแก้ไขปัญหา

  • เซอร์โวกระตุกหรือไม่สามารถรักษามุมที่เสถียรได้ ตรวจสอบแหล่งจ่ายไฟของมัน เซอร์โวใช้กระแสไฟฟ้ามากกว่าที่ขา GPIO เพียงอย่างเดียวจะพยายามจ่ายให้
  • ค่าที่อ่านได้จากเซ็นเซอร์ระยะทางไม่สอดคล้องกันอย่างมาก เซ็นเซอร์อัลตราโซนิกไวต่อพื้นผิวที่อ่อนนุ่มหรือเป็นมุมที่ทำให้พัลส์กระจัดกระจายแทนที่จะสะท้อนกลับมาตรงๆ ให้ทดสอบกับผนังแข็งเรียบก่อน
  • หน้าจอแสดงผลแสดงเพียงส่วนหนึ่งของการกวาดเท่านั้น ยืนยันว่าเซอร์โวกำลังเคลื่อนที่ครบช่วงการเคลื่อนที่ทั้งหมด และการคำนวณมุมต่อพิกเซลตรงกับช่วงจริงของเซอร์โว

การอัปเกรดเสริม

เพิ่มโหมดการแสดงผลที่สองซึ่งบันทึกเฉพาะวัตถุที่ตรวจพบใกล้ที่สุดต่อการกวาด แทนที่จะวาดส่วนโค้งทั้งหมด ซึ่งเป็นการอ่านค่าการแจ้งเตือนระยะใกล้แบบง่ายๆ ที่สร้างขึ้นจากเซ็นเซอร์สองตัวเดียวกัน

การเดินสายไฟ

  • HC-SR04 VCC → ESP32-S3 5V
  • HC-SR04 GND → ESP32-S3 GND
  • HC-SR04 TRIG → ESP32-S3 GPIO4 (เอาต์พุต)
  • HC-SR04 ECHO → ตัวแบ่งแรงดัน 1kΩ/2kΩ → ESP32-S3 GPIO5 (อินพุต) — ECHO เป็นลอจิก 5V ตัวแบ่งจะป้องกันขาอินพุต 3.3V
  • SG90 สีแดง → ESP32-S3 5V
  • SG90 สีน้ำตาล/ดำ → ESP32-S3 GND
  • SG90 สีส้ม/เหลือง (สัญญาณ) → ESP32-S3 GPIO6 (PWM ผ่าน ESP32Servo)
  • OLED VCC → ESP32-S3 3.3V
  • OLED GND → ESP32-S3 GND
  • OLED SDA → ESP32-S3 GPIO8 (I2C เริ่มต้น)
  • OLED SCL → ESP32-S3 GPIO9 (I2C เริ่มต้น)

HC-SR04 จะไม่น่าเชื่อถือเมื่อระยะห่างน้อยกว่า ~2 ซม. และเซอร์โวต้องการการหน่วงเวลาสั้นๆ หลังจากการ `write()` แต่ละครั้งก่อนที่การอ่านค่าจะน่าเชื่อถือ ซึ่งเป็นเหตุผลว่าทำไมจึงมีการหยุดชั่วคราว 150 มิลลิวินาทีต่อขั้นตอน สิ่งนี้จะวาดเรดาร์ "กวาด" แบบเส้นเดียวที่เรียบง่าย (วาดใหม่ในแต่ละมุม) แทนที่จะเป็นร่องรอยแบบคงที่ — ใช้งานได้จริง แต่เรียบง่ายกว่าเอฟเฟกต์หน้าจอเรดาร์เต็มรูปแบบ; คุณสามารถเพิ่มจุดร่องรอยที่จางลงได้เมื่อเวอร์ชันพื้นฐานทำงานแล้ว

แผนภาพการต่อสาย

VCC → 5V GND TRIG → GPIO4 ECHO GPIO5 5V GND GPIO6 VCC → 3.3V GND SDA → GPIO8 SCL → GPIO9 ESP32-S3 HC-SR04 1kΩ/2kΩ voltage divider SG90 red SG90 brown/black SG90 orange/yellow OLED

โค้ดเริ่มต้น

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ESP32Servo.h>

// --- Pin definitions: adjust to match your ESP32-S3 board's silkscreen if different ---
#define TRIG_PIN   4   // HC-SR04 trigger (output)
#define ECHO_PIN   5   // HC-SR04 echo (input, through a 1k/2k voltage divider - ECHO is 5V logic)
#define SERVO_PIN  6   // SG90 signal (PWM-capable pin)
#define OLED_SDA   8   // ESP32-S3 default I2C SDA
#define OLED_SCL   9   // ESP32-S3 default I2C SCL

#define SCREEN_WIDTH  128
#define SCREEN_HEIGHT 64
#define OLED_ADDR     0x3C

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
Servo radarServo;

const int MAX_DISTANCE_CM = 40;   // radar range shown on screen
const int CENTER_X = 64;
const int CENTER_Y = 63;
const int RADIUS_PX = 60;

long readDistanceCM() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  long duration = pulseIn(ECHO_PIN, HIGH, 25000); // 25ms timeout (~4m max)
  if (duration == 0) return -1; // no echo / out of range
  return duration * 0.0343 / 2;
}

void setup() {
  Serial.begin(115200);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);

  radarServo.attach(SERVO_PIN);

  Wire.begin(OLED_SDA, OLED_SCL);
  if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
    Serial.println("OLED init failed");
    while (true) delay(1000);
  }
  display.clearDisplay();
  display.display();
}

void sweepAndDraw(int angle) {
  radarServo.write(angle);
  delay(150); // let the servo settle before reading

  long distance = readDistanceCM();

  display.clearDisplay();

  // Sweep line from center at current angle
  float rad = radians(angle);
  int lineLen = RADIUS_PX;
  if (distance > 0 && distance < MAX_DISTANCE_CM) {
    lineLen = map(distance, 0, MAX_DISTANCE_CM, 0, RADIUS_PX);
  }
  int x = CENTER_X - cos(rad) * lineLen;
  int y = CENTER_Y - sin(rad) * lineLen;
  display.drawLine(CENTER_X, CENTER_Y, x, y, SSD1306_WHITE);

  // Text readout
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.print("Angle: ");
  display.print(angle);
  display.print((char)247); // degree symbol
  display.setCursor(0, 10);
  display.print("Dist: ");
  if (distance > 0) {
    display.print(distance);
    display.print(" cm");
  } else {
    display.print("--");
  }
  display.display();
}

void loop() {
  for (int angle = 0; angle <= 180; angle += 3) {
    sweepAndDraw(angle);
  }
  for (int angle = 180; angle >= 0; angle -= 3) {
    sweepAndDraw(angle);
  }
}

ไม่แน่ใจขั้นตอนไหน? ถามเรา