搜索代码

ESP32-S3 RGB LED矩阵项目5 - 箭头始终向上

ESP32-S3 RGB LED矩阵项目5 - 箭头始终向上

项目 5 - 箭头始终向上(使用 QMI8658C 的方向指示器)

项目5使用QMI8658C运动传感器检测ESP32-S3 RGB LED矩阵的朝向,始终显示一个指向重力方向上的箭头。无论你怎么旋转电路板——USB侧朝上、OUSB侧朝上、侧面“15”或侧面“34”——箭头都会自动旋转并指向物理上的上方。

这是使用机载加速度计进行实时定向传感的强大演示。该模块的所有六个项目都在一个YouTube视频中演示,视频也嵌入在此页面上。项目5的完整代码会在文章下方自动加载,并且附属链接在代码部分下方出现。

ESP32-S3 RGB LED矩阵模块概述

ESP32-S3 RGB LED矩阵模块包括几个使该项目成为可能的组件:

  • ESP32-S3 微控制器- 提供 Wi-Fi、BLE,并运行 LED/IMU 逻辑。
  • 8×8 RGB LED 矩阵- 在四个方向中显示箭头。
  • QMI8658C 加速度计- 检测倾斜、运动和方向。:contentReference[oaicite:0]{index=0}
  • USB-C端口用于Arduino IDE的电源和编程。
  • 重置和启动按钮用于上传草图。
  • GPIO引脚可用于其他项目的资源。

箭头方向完全由加速度计读数决定。当板子旋转时,QMI8658C 传感器感知新的 X/Y/Z 值,程序选择应该绘制哪个箭头图案 (↑, ↓, ←, →)。

视频中涵盖的项目(时间戳)

  • 00:00- 介绍
  • 02:01- 安装ESP32开发板
  • 03:32安装库
  • 05:32- 项目 1: 移动点
  • 11:11- 项目 2:文本滚动
  • 12:59- 项目 3:HTTP 文本
  • 16:41- 项目 4:倾斜点
  • 18:55-项目5:箭头始终向上(此项目)
  • 20:02- 项目6:目标游戏

视频清楚地显示了箭头如何根据模块的转动瞬间改变方向。强烈建议观看这一部分。:contentReference[oaicite:1]{index=1}

在Arduino IDE中安装ESP32开发板

如果您完成了任何早期项目,董事会的设置已经完成。否则:

  1. File > Preferences→ 添加ESP32板网址
  2. Tools > Board > Boards Manager…→ 安装 "ESP32"
  3. 在下方选择ESP32-S3开发板Tools > Board
  4. 在下方选择正确的USB COM端口Tools > Port

安装所需库

项目5使用:

  • Adafruit NeoMatrix
  • Adafruit NeoPixel
  • Adafruit GFX
  • QMI8658(运动传感器)
  1. Sketch > Include Library > Manage Libraries…
  2. 搜索:新矩阵→ 安装
  3. 安装依赖项:NeoPixel+GFX
  4. 搜索并安装QMI8658由其作者编写

项目5如何运作

QMI8658C 在 X、Y 和 Z 轴上测量重力。通过比较这些值,草图确定电路板的哪个物理面朝上:

  • USB面朝上
  • OUSB面朝上(相对 USB)
  • 侧面“15”朝上
  • 面朝“34”向上

每个方向对应于8×8矩阵上的不同箭头图案。映射遵循您之前调试会话中确认的方向逻辑。棋盘的旋转持续读取,箭头会在向上侧变化时立即更新。

项目 5 - 代码设置(箭头始终向上)

以下是来自配置区域的用户可调值。完整的项目代码会在文章下方自动显示。

矩阵配置


// Matrix configuration
const int MATRIX_PIN    = 14;   // fixed for this module
const int MATRIX_WIDTH  = 8;
const int MATRIX_HEIGHT = 8;

// Recommended orientation: Top-Left origin, progressive mode
// (actual constructor is inside the code loaded below)

该项目使用NEO_MATRIX_PROGRESSIVE布局以确保箭头根据实际移动正确指向。

亮度


uint8_t matrixBrightness = 40;   // 0–255

您可以在光线较强的环境中提高此数值。在室内使用时,30-60是舒适的。

箭头颜色


// Arrow color
uint8_t arrowRed   = 255;
uint8_t arrowGreen = 0;
uint8_t arrowBlue  = 0;

更改这些值以修改箭头的颜色。例如:

  • 绿色箭头:(0, 255, 0)
  • 蓝色箭头:(0, 0, 255)
  • 白色箭头:(255, 255, 255)

灵敏度和平滑处理

为了避免抖动,代码包含平滑和阈值逻辑。在设置中,您可能会找到类似:


// Sensitivity / smoothing adjustment
float tiltThreshold = 0.30f;   // adjust if arrow changes too easily
  • 如果你的箭头翻转得太容易→增加阈值。
  • 如果箭头变化太慢 →减少阈值。

箭头图案

草图包括箭头位图模式用于:

  • ↑ 向上
  • ↓ 下
  • ← 左
  • → 右

您无需修改这些,但如果您想要不同的风格,可以更改代码中的形状。

摘要

项目5展示了ESP32-S3 RGB LED矩阵和QMI8658C加速度计如何协同工作以检测方向,并显示一支始终向上的箭头。该项目基于倾斜点(项目4),为您在项目6中的最终互动游戏做好准备。

本文章下方提供完整的“箭头总是向上”草图(自动加载)。强烈建议观看视频中对应部分,以观察箭头如何瞬间响应电路板的旋转。如果您希望在家中构建此项目,ESP32-S3 RGB LED矩阵模块的关联链接在代码部分下方出现。

图像

ESP32 S3 Matrix
ESP32 S3 Matrix
ESP32 S3 Matrix  pin out
ESP32 S3 Matrix pin out
ESP32-S3_RGB_8x8_matrix-3
ESP32-S3_RGB_8x8_matrix-3
ESP32 S3 Matrix attached with buzzer to pin6 and GND
ESP32 S3 Matrix attached with buzzer to pin6 and GND
ESP32 S3 Matrix displaying red heart
ESP32 S3 Matrix displaying red heart
ESP32 S3 Matrix displaying green heart
ESP32 S3 Matrix displaying green heart
ESP32 S3 Matrix displaying rainbow heart 3
ESP32 S3 Matrix displaying rainbow heart 3
ESP32-S3_RGB_8x8_matrix1
ESP32-S3_RGB_8x8_matrix1
ESP32-S3_RGB_8x8_matrix-2
ESP32-S3_RGB_8x8_matrix-2
ESP32-S3-Mtrix - Alway Up
ESP32-S3-Mtrix - Alway Up
803-ESP32-S3 RGB LED Matrix Project 5 - Arrow always up
语言: C++
/* 
  Project 5: Arrow Always Up – ESP32-S3 RGB LED Matrix (Waveshare)
  This sketch reads tilt from the QMI8658C IMU and smoothly moves a dot 
  on the 8×8 RGB LED matrix based on board orientation.

  ▶️ Video Tutorial:
  https://youtu.be/JKLuYrRcLMI

  📚⬇️ Resources & Code Page:
  https://robojax.com/RJT833

  QMI8658_RGB_2
*/
#include <Arduino.h>
#include <math.h>

#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>

#include <QMI8658.h>   // by Lahav Gahali

// -------- LED MATRIX SETUP --------
#define MATRIX_PIN    14
#define MATRIX_WIDTH  8
#define MATRIX_HEIGHT 8

Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(
  MATRIX_WIDTH, MATRIX_HEIGHT, MATRIX_PIN,
  NEO_MATRIX_TOP + NEO_MATRIX_LEFT +
  NEO_MATRIX_ROWS + NEO_MATRIX_PROGRESSIVE,
  NEO_RGB + NEO_KHZ800
);

// -------- QMI8658 IMU SETUP --------
QMI8658 imu;
QMI8658_Data imuData;

// -------- USER SETTINGS --------

// true  -> arrow points to opposite side
//         USB↔OUSB, 34↔15
// false -> arrow points to the same side that is UP
bool useOppositeMapping = false;

// Arrow color (0–255 each)
uint8_t dotRed   = 0;
uint8_t dotGreen = 150;
uint8_t dotBlue  = 0;

// Board sides
enum Side {
  SIDE_CENTER = 0,
  SIDE_USB,
  SIDE_OUSB,
  SIDE_15,
  SIDE_34
};

// Direction for arrow drawing
enum ArrowDir {
  ARROW_CENTER,
  ARROW_UP,
  ARROW_DOWN,
  ARROW_LEFT,
  ARROW_RIGHT
};

bool isFlat = false;

const char* sideName(Side s) {
  switch (s) {
    case SIDE_CENTER: return "CENTER";
    case SIDE_USB:    return "USB";
    case SIDE_OUSB:   return "OUSB";
    case SIDE_15:     return "15";
    case SIDE_34:     return "34";
    default:          return "?";
  }
}

// -------- ARROW DRAWING (YOUR CODE, UNCHANGED) --------

// Draw a simple arrow on 8x8 matrix pointing in the given direction
void drawArrow(ArrowDir dir, uint16_t color) {
  matrix.fillScreen(0);

  switch (dir) {
    case ARROW_UP:
      // Tip
      matrix.drawPixel(3, 0, color);
      matrix.drawPixel(4, 0, color);
      // Second row
      matrix.drawPixel(2, 1, color);
      matrix.drawPixel(3, 1, color);
      matrix.drawPixel(4, 1, color);
      matrix.drawPixel(5, 1, color);
      // Shaft
      matrix.drawLine(3, 2, 3, 6, color);
      matrix.drawLine(4, 2, 4, 6, color);
      break;

    case ARROW_DOWN:
      // Tip
      matrix.drawPixel(3, 7, color);
      matrix.drawPixel(4, 7, color);
      // Row above tip
      matrix.drawPixel(2, 6, color);
      matrix.drawPixel(3, 6, color);
      matrix.drawPixel(4, 6, color);
      matrix.drawPixel(5, 6, color);
      // Shaft
      matrix.drawLine(3, 1, 3, 5, color);
      matrix.drawLine(4, 1, 4, 5, color);
      break;

    case ARROW_LEFT:
      // Tip
      matrix.drawPixel(0, 3, color);
      matrix.drawPixel(0, 4, color);
      // Column after tip
      matrix.drawPixel(1, 2, color);
      matrix.drawPixel(1, 3, color);
      matrix.drawPixel(1, 4, color);
      matrix.drawPixel(1, 5, color);
      // Shaft
      matrix.drawLine(2, 3, 6, 3, color);
      matrix.drawLine(2, 4, 6, 4, color);
      break;

    case ARROW_RIGHT:
      // Tip
      matrix.drawPixel(7, 3, color);
      matrix.drawPixel(7, 4, color);
      // Column before tip
      matrix.drawPixel(6, 2, color);
      matrix.drawPixel(6, 3, color);
      matrix.drawPixel(6, 4, color);
      matrix.drawPixel(6, 5, color);
      // Shaft
      matrix.drawLine(1, 3, 5, 3, color);
      matrix.drawLine(1, 4, 5, 4, color);
      break;

    case ARROW_CENTER:
    default:
      // Simple plus in the center
      matrix.drawLine(3, 3, 4, 3, color);
      matrix.drawLine(3, 4, 4, 4, color);
      matrix.drawLine(3, 3, 3, 4, color);
      matrix.drawLine(4, 3, 4, 4, color);
      break;
  }

  matrix.show();
}

// -------- IMU → SIDE DETECTION --------
// We calibrated earlier:
//  +X = USB,   -X = OUSB
//  +Y = 15,    -Y = 34    (after your correction)

Side detectSideUp(float ax_g, float ay_g, float az_g) {
  // Flat detection
  const float flatThreshXY = 0.15f;
  const float flatThreshZ  = 0.15f;

  if (fabs(ax_g) < flatThreshXY &&
      fabs(ay_g) < flatThreshXY &&
      fabs(az_g - 1.0f) < flatThreshZ) {
    isFlat = true;
    return SIDE_CENTER;
  }
  isFlat = false;

  // Thresholds to say "this axis is really tilted"
  const float tiltThreshY = 0.5f;
  const float tiltThreshX = 0.5f;

  // Prefer Y axis for 15 / 34
  if (fabs(ay_g) >= tiltThreshY) {
    if (ay_g > 0) {
      return SIDE_34;   // +Y = 34 up
    } else {
      return SIDE_15;   // -Y = 15 up
    }
  }

  // Otherwise, use X axis for USB / OUSB
  if (fabs(ax_g) >= tiltThreshX) {
    if (ax_g > 0) {
      return SIDE_USB;   // +X = USB up
    } else {
      return SIDE_OUSB;  // -X = OUSB up
    }
  }

  // Not clearly tilted → treat as center
  return SIDE_CENTER;
}

// Map from UP side to where the arrow should point
Side arrowSideFromUpSide(Side upSide) {
  if (!useOppositeMapping) {
    // Arrow shows the side that is UP
    return upSide;
  }

  // Arrow shows the opposite side
  switch (upSide) {
    case SIDE_USB:   return SIDE_OUSB;
    case SIDE_OUSB:  return SIDE_USB;
    case SIDE_15:    return SIDE_34;
    case SIDE_34:    return SIDE_15;
    case SIDE_CENTER:
    default:         return SIDE_CENTER;
  }
}

// Convert SIDE to ArrowDir
ArrowDir arrowDirFromSide(Side s) {
  switch (s) {
    case SIDE_USB:    return ARROW_UP;
    case SIDE_OUSB:   return ARROW_DOWN;
    case SIDE_15:     return ARROW_LEFT;
    case SIDE_34:     return ARROW_RIGHT;
    case SIDE_CENTER:
    default:          return ARROW_CENTER;
  }
}

// ---------------- SETUP & LOOP ----------------

void setup() {
  Serial.begin(115200);
  delay(500);

  matrix.begin();
  matrix.setBrightness(20);
  matrix.fillScreen(0);
  matrix.show();

  // IMU: SDA=11, SCL=12 on ESP32-S3-Matrix
  if (!imu.begin(11, 12)) {
    Serial.println("Failed to initialize QMI8658!");
    while (1) { delay(1000); }
  }

  imu.setAccelUnit_mg(true);
  imu.setGyroUnit_dps(true);
  imu.setDisplayPrecision(4);

  Serial.print("QMI8658 initialized. useOppositeMapping = ");
  Serial.println(useOppositeMapping ? "TRUE" : "FALSE");
}

void loop() {
  if (!imu.readSensorData(imuData)) {
    return;
  }

  float ax_g = imuData.accelX / 1000.0f;
  float ay_g = imuData.accelY / 1000.0f;
  float az_g = imuData.accelZ / 1000.0f;

  Side upSide     = detectSideUp(ax_g, ay_g, az_g);
  Side arrowSide  = arrowSideFromUpSide(upSide);
  ArrowDir dir    = arrowDirFromSide(arrowSide);

  uint16_t color = matrix.Color(dotRed, dotGreen, dotBlue);
  drawArrow(dir, color);

  // Debug
  Serial.print("AX="); Serial.print(ax_g, 3);
  Serial.print(" AY="); Serial.print(ay_g, 3);
  Serial.print(" AZ="); Serial.print(az_g, 3);
  Serial.print(" | UP=");    Serial.print(sideName(upSide));
  Serial.print(" | ARROW="); Serial.println(sideName(arrowSide));

  delay(80);
}

|||您可能需要的东西

资源与参考

文件📁

Fritzing 文件