Code zoeken

ESP32-S3 RGB LED-matrixproject 5 - Pijl altijd omhoog

ESP32-S3 RGB LED-matrixproject 5 - Pijl altijd omhoog

Project 5 – Pijl Altijd Omhoog (Oriëntatie-indicator met QMI8658C)

Project 5 gebruikt de QMI8658C-bewegingssensor om de oriëntatie van de ESP32-S3 RGB LED-matrix te detecteren en altijd een pijl weer te geven die omhoog wijst ten opzichte van de zwaartekracht. Hoe je het bord ook draait—USB-zijde boven, OUSB-zijde boven, zijde “15”, of zijde “34”—de pijl draait automatisch en wijst naar de fysiek naar boven gerichte richting.

Dit is een krachtige demonstratie van realtime oriëntatiedetectie met behulp van de ingebouwde versnellingsmeter. Alle zes projecten voor deze module worden gedemonstreerd in één YouTube-video, die ook op deze pagina is ingesloten. De volledige code voor Project 5 wordt automatisch onder het artikel geladen, en affiliatelinks verschijnen onder het codegedeelte.

Overzicht van de ESP32-S3 RGB LED-matrixmodule

De ESP32-S3 RGB LED-matrixmodule bevat verschillende componenten die dit project mogelijk maken:

  • ESP32-S3-microcontroller — biedt Wi-Fi, BLE en draait de LED/IMU-logica.
  • 8×8 RGB-LED-matrix — geeft de pijl in een van vier oriëntaties weer.
  • QMI8658C-versnellingsmeter — detecteert kanteling, beweging en oriëntatie.:contentReference[oaicite:0]{index=0}
  • USB-C-poort voor voeding en programmeren in de Arduino IDE.
  • Reset- en Boot-knoppen voor het uploaden van sketches.
  • GPIO-pinnen beschikbaar rond het bord voor extra projecten.

De pijlrichting wordt volledig bepaald door de versnellingsmeterwaarden. Wanneer het bord wordt gedraaid, detecteert de QMI8658C de nieuwe X/Y/Z-waarden, en de sketch kiest welk pijlpatroon (↑, ↓, ←, →) moet worden getekend.

Projecten in de video (tijdstempels)

  • 00:00 – Introductie
  • 02:01 – ESP32-borden installeren
  • 03:32 – Bibliotheken installeren
  • 05:32 – Project 1: Bewegende stip
  • 11:11 – Project 2: Tekst scrollen
  • 12:59 – Project 3: HTTP-tekst
  • 16:41 – Project 4: Kantelstip
  • 18:55Project 5: Pijl altijd omhoog (dit project)
  • 20:02 – Project 6: Doelspel

De video laat duidelijk zien hoe de pijl onmiddellijk van richting verandert op basis van hoe de module wordt gedraaid. Het bekijken van dit segment wordt sterk aanbevolen.:contentReference[oaicite:1]{index=1}

ESP32-borden installeren in Arduino IDE

Als je een eerder project hebt voltooid, is de bordconfiguratie al gedaan. Anders:

  1. Bestand > Voorkeuren → ESP32-bord-URL toevoegen
  2. Extra > Bord > Boards Manager… → “ESP32” installeren
  3. Selecteer het ESP32-S3-bord onder Extra > Bord
  4. Selecteer de juiste USB-COM-poort onder Extra > Poort

Vereiste bibliotheken installeren

Project 5 gebruikt:

  • Adafruit NeoMatrix
  • Adafruit NeoPixel
  • Adafruit GFX
  • QMI8658 (bewegingssensor)
  1. Sketch > Bibliotheek gebruiken > Bibliotheken beheren…
  2. Zoek: NeoMatrix → Installeren
  3. Installeer afhankelijkheden: NeoPixel + GFX
  4. Zoek en installeer QMI8658 door de auteur

Hoe Project 5 werkt

De QMI8658C meet de zwaartekracht op de X-, Y- en Z-assen. Door deze waarden te vergelijken, bepaalt de sketch welke fysieke zijde van het bord naar boven is gericht:

  • USB-zijde boven
  • OUSB-zijde boven (tegenover USB)
  • Zijde “15” boven
  • Zijde “34” boven

Elke oriëntatie komt overeen met een ander pijlpatroon op de 8×8-matrix. De toewijzing volgt je bevestigde oriëntatielogica van eerdere debug-sessies. De rotatie van het bord wordt continu uitgelezen en de pijl wordt bijgewerkt zodra de bovenste zijde verandert.

Project 5 – Code-instellingen (Pijl altijd omhoog)

Hieronder staan de door de gebruiker aanpasbare waarden uit het configuratiegebied. De volledige projectcode verschijnt automatisch onder het artikel.

Matrixconfiguratie


// Matrixconfiguratie
const int MATRIX_PIN    = 14;   // vast voor deze module
const int MATRIX_WIDTH  = 8;
const int MATRIX_HEIGHT = 8;

// Aanbevolen oriëntatie: Top-Left-oorsprong, progressieve modus
// (de daadwerkelijke constructor staat in de hieronder geladen code)

Dit project gebruikt de NEO_MATRIX_PROGRESSIVE-lay-out om ervoor te zorgen dat de pijl correct wijst op basis van daadwerkelijke beweging.

Helderheid


uint8_t matrixBrightness = 40;   // 0–255

Je kunt deze waarde verhogen voor helderdere omgevingen. Voor binnengebruik is 30–60 comfortabel.

Pijlkleur


// Pijlkleur
uint8_t arrowRed   = 255;
uint8_t arrowGreen = 0;
uint8_t arrowBlue  = 0;

Wijzig deze waarden om de kleur van de pijl aan te passen. Bijvoorbeeld:

  • Groene pijl: (0, 255, 0)
  • Blauwe pijl: (0, 0, 255)
  • Witte pijl: (255, 255, 255)

Gevoeligheid en afvlakking

Om trillingen te voorkomen, bevat de code afvlakkings- en drempellogica. In de instellingen vind je mogelijk iets als:


// Gevoeligheids-/afvlakkingsaanpassing
float tiltThreshold = 0.30f;   // aanpassen als de pijl te gemakkelijk verandert
  • Als je pijl te gemakkelijk omdraait → verhoog de drempelwaarde.
  • Als de pijl te langzaam verandert → verlaag de drempelwaarde.

Pijlpatronen

De sketch bevat bitmap-patronen voor pijlen voor:

  • ↑ omhoog
  • ↓ omlaag
  • ← links
  • → rechts

Je hoeft deze niet aan te passen, maar je kunt de vormen in de code wijzigen als je een andere stijl wilt.

Samenvatting

Project 5 laat zien hoe de ESP32-S3 RGB LED-matrix en de QMI8658C-versnellingsmeter samenwerken om de oriëntatie te detecteren en een pijl weer te geven die altijd naar boven wijst. Dit project bouwt voort op de Tilt Dot (Project 4) en bereidt je voor op het laatste interactieve spel in Project 6.

De volledige “Arrow Always Up”-sketch is beschikbaar onder dit artikel (automatisch geladen). Het wordt sterk aanbevolen om het bijbehorende deel van de video te bekijken om te zien hoe onmiddellijk de pijl reageert op rotatie van het bord. Als je dit project thuis wilt bouwen, vind je onder de codesectie affiliatelinks voor de ESP32-S3 RGB LED-matrixmodule.

Afbeeldingen

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
Taal: 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);
}

Dingen die je misschien nodig hebt

Hulpmiddelen en referenties

Bestanden📁

Fritzing-bestand