Introduction: Light Up Your Valentine With Sparkling Matrix

About: Howdy, we are application engineers in Seeed. Sharing projects with the maker community is awesome. Hope you like it XD Seeed is the IoT hardware enabler providing services that empower IoT developers to swift…

Valentine’s Day is a chance for you to send love messages. Why not make a fun LED face with inexpensive components to express your feeling!

Step 1: Things Used in This Project

Step 2: Story

Step 3: Hardware Connection

In this project, we use the gesture sensor as an input and give the feedback to the LED matrix driver to feed the red LED matrix.

You need to connect the Grove - Gesture and the Grove - Red LED Matrix w/Driver to the I^2^C port of the base shield which is plugged into the Seeeduino. Then connect Seeeduino to PC via a USB cable.

Step 4: Software Programming

The plan for this project is to display the static emoji (crying face and waiting face) when there is no left or right gesture been read.

Display dynamic eye-position-change emoji with controlled by left or right gesture when there is left or right gesture been read.

At the specific eye position, a beating heart will be displayed on the LED matrix.

The LED display patterns are designed by LED Matrix Editor, you can change or add your LED patterns in program shows above.

To get ready for this project, it is required to install Grove - Gesture, Grove - Red LED Matrix w/Driver and MsTimer2 libraries into the Arduino IDE.

   #include "Grove_LED_Matrix_Driver_HT16K33.h"
   #include "MsTimer2.h"
   #include "paj7620.h"
   #include "Wire.h"

During the setup procedure, we initialized the Serial, matrix and MsTimer2 functions. Initialized Serial is used to debugging the program, so it's unnecessary to connect to PC or use Serial Monitor after debugging. We use MsTimer2 in this project to control the display of the static emoji.

   // Initialized the serial to debug.
   Serial.begin(9600); 
   while(!Serial); 
   
   paj7620Init();
   
   Wire.begin();
   matrix.init();
   matrix.setBrightness(15);
   matrix.setBlinkRate(BLINK_OFF);
   
   MsTimer2::set(1000, displayStatic);
   MsTimer2::start();

In the main loop, we only take two outputs from the gesture sensor, GES_RIGHT_FLAG, GES_LEFT_FLAG and these are used to guide the displayDynamic() function.

The displayDynamic() function will calculate the interval from last time dynamic emoji was displayed to current time, if the interval exceed than TIMEOUT, it will stop playing static emoji, and set displayed dynamic emoji index to start. When left-to-right gestures are read, the start index is 0, and the index will be increased after left-to-right gestures read again. Otherwise, the start index is the last index of DYNAMIC array, and the index will be decreased.

   void displayDynamic(bool leftToRight) {
     unsigned long currentTime = millis();
     
     if (currentTime - prevTime > TIMEOUT) {
       dIndex = leftToRight ? 0 : ARRAY_LENGTH(DYNAMIC) - 1;
       showStatic = false;
     }
     
     // This is used to avoid exceed the boundary.
     if (dIndex >= ARRAY_LENGTH(DYNAMIC) || dIndex <= -1) {
       heartBeat();
       dIndex = leftToRight ? ARRAY_LENGTH(DYNAMIC) - 1 : 0;
     }
     
     prevTime = currentTime;
     matrixDisplay = DYNAMIC[leftToRight ? dIndex++ : dIndex--];
   }

No gesture is read if the TIMEOUT period is exceeded, static emoji playing will be restored.

Upload the program to your Seeeduino, all done. Play and have a fun!

Step 5: Code

#include "Grove_LED_Matrix_Driver_HT16K33.h"
#include "MsTimer2.h"
#include "paj7620.h"
#include "Wire.h"

#define TIMEOUT             5000
#define GES_REG_ADDR        0x43
#define ARRAY_LENGTH(array) \
  (sizeof(array) / sizeof(array[0]))

Matrix_8x8 matrix;
uint64_t matrixDisplay = 0;
unsigned long prevTime = millis();

int8_t sIndex = 0;
bool showStatic = true;
const uint64_t STATIC[] = {
  0x00003c0000a54200,
  0x00003c000000e700,
  0x00003c004242e700
};

int8_t dIndex = 0;
const uint64_t DYNAMIC[] = {
  0x00003c000021e700,
  0x00003c000042e700,
  0x00003c000084e700
};
const uint64_t HEART[] = {
  0x00183c7e7e240000,
  0x00183c7effff6600,
  0x183c7effffffff66,
  0x00183c7effff6600 
};

void displayStatic(void) {
  if (showStatic) {
    matrixDisplay = STATIC[sIndex];
    sIndex = (sIndex + 1) % ARRAY_LENGTH(STATIC);
  }
}

void heartBeat() {
  for (uint8_t i = 0; i < ARRAY_LENGTH(HEART); ++i) {
    matrix.writeOnePicture(HEART[i]);
    matrix.display();
    delay(500);
  }
}

void displayDynamic(bool leftToRight) {
  unsigned long currentTime = millis();
  if (currentTime - prevTime > TIMEOUT) {
    showStatic = false;
    prevTime = currentTime;
    dIndex = leftToRight ? 0 : ARRAY_LENGTH(DYNAMIC) - 1;
  }

  // This is used to avoid exceed the boundary.
  if (dIndex >= ARRAY_LENGTH(DYNAMIC) || dIndex <= -1) {
    heartBeat();
    dIndex = leftToRight ? ARRAY_LENGTH(DYNAMIC) - 1 : 0;
  }
  
  matrixDisplay = DYNAMIC[leftToRight ? dIndex++ : dIndex--];
}

void setup() {
  // Initialized the serial to debug.
  Serial.begin(9600);
  while(!Serial);
  
  uint8_t errorCode = paj7620Init();
  
  Wire.begin();
  matrix.init();
  matrix.setBrightness(15);
  matrix.setBlinkRate(BLINK_OFF);
  
  MsTimer2::set(1000, displayStatic);
  MsTimer2::start();
}

void loop() {
  uint8_t data, errorCode = paj7620ReadReg(GES_REG_ADDR, 1, &data);
  
  if (!errorCode) {
    switch (data) {
      case GES_RIGHT_FLAG:
        displayDynamic(true);
        break;

      case GES_LEFT_FLAG: 
        displayDynamic(false);
        break;
    }
  }

  if (millis() - prevTime > TIMEOUT) {
    showStatic = true;
  }

  matrix.writeOnePicture(matrixDisplay);
  matrix.display();
  
  delay(100);
}