Shawn SonySreya. V
Published © GPL3+

MoodBoard: The Emotion-Aware Sign That Lifts Your Day

An AI signboard that detects emotions and displays emojis to brighten your wait.

IntermediateFull instructions providedOver 1 day143
MoodBoard: The Emotion-Aware Sign That Lifts Your Day

Things used in this project

Hardware components

Wio Terminal
Seeed Studio Wio Terminal
Used as the main user interface — displays the bus stop sign by default, shows the detected emotion in real time, and plays matching sounds or animations.
×1
Seeed Studio XIAO ESP32S3 Plus
Seeed Studio XIAO ESP32S3 Plus
Handles AI inference and processing — receives image data from Grove Vision AI V2, runs recognition models if needed, and sends processed results to the Wio Terminal.
×1
Grove Shield for Seeeduino XIAO - with embedded battery management chip
Seeed Studio Grove Shield for Seeeduino XIAO - with embedded battery management chip
Provides easy modular connections for Grove Vision AI V2, sensors, and other peripherals without soldering or complex wiring.
×1
Grove Vision AI Module V2
Seeed Studio Grove Vision AI Module V2
Acts as the emotion detection engine — captures facial expressions, runs AI models locally to determine the emotion, and sends results to the XIAO ESP32-S3 Plus.
×1
Camera Module
Raspberry Pi Camera Module
Captures live images of people looking at the sign, enabling the AI system to determine their emotional state.
×1
18650 Li-On Battery Pack 4 cell 14.8v 3c 2000mAh
Provides high-capacity, portable power suitable for long runtime in mobile or outdoor applications.
×1
LM2596S DC-DC Buck Converte
Efficiently steps down the battery voltage to 3.7 V to safely power the XIAO ESP32-S3 Plus without energy waste or overheating.
×1
Seeed Studio Grove - Universal 4 Pin Buckled 5cm Cable (5 PCs Pack)
Seeed Studio Grove - Universal 4 Pin Buckled 5cm Cable (5 PCs Pack)
Used to simplify sensor and module connections with standardized plug-and-play interface, reducing wiring errors and ensuring quick, secure hardware integration.
×2

Software apps and online services

Arduino IDE
Arduino IDE
Provided an accessible and versatile platform for writing, compiling, and uploading code to both the Wio Terminal and Xiao ESP32-S3 Plus, enabling seamless I²C communication for the Xiao ESP32-S3 Plus with the Grove Vision AI V2 module and the Wio terminal.
SenseCraft AI
Seeed Studio SenseCraft AI
Offered an efficient deployment pipeline for flashing and managing AI models on the Grove Vision AI V2, simplifying integration of computer vision capabilities into embedded systems.
Roboflow
Streamlined the dataset preparation process, offering tools for annotation, augmentation, and export, ensuring a clean and well-structured dataset for model training.
Google Colab
Provided a cloud-based training environment with pre-configured scripts and resources from Seeed Studio, allowing for quick model prototyping and fine-tuning without the need for local GPU resources.

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
A soldering iron is a tool used for melting solder to join electronic components, wires, or metal pieces in various applications.
Solder Wire, Lead Free
Solder Wire, Lead Free
Soldering lead is a metal alloy wire used to join electronic components by melting the solder, creating a conductive bond in electronic circuits.

Story

Read more

Custom parts and enclosures

Enclosure

Created a enclosure from a cardboard box.

Schematics

Circuit Diagram

The Grove vision AI v2 and the wio terminal is connected to the XIAO ESP32-S3 Plus via I2C communication. The system is powered by a 18650 Li-On battery which is 14.8v. A buck convertor is used to down it to 3.7v to power the XIAO ESP32-S3 Plus.

Code

XIAO ESP32-S3 Plus inference.ino

Arduino
Receives inference results from the Grove Vision AI v2 via I2c and sends the required info to the Wio Terminal
#include <Arduino.h>
#include <Wire.h>
#include <Seeed_Arduino_SSCMA.h>

SSCMA AI;

#define SSCMA_CONNECT_TO_XIAO_S3         1
#define SSCMA_CONNECT_TO_GORVE_VISION_AI 0
#define LED_PIN                          21
#define WIO_TERMINAL_I2C_ADDRESS         0x08


#define CMD_ANGRY       0x00
#define CMD_CONFUSED    0x01
#define CMD_HAPPY       0x02
#define CMD_NEUTRAL     0x03
#define CMD_SAD         0x04
#define CMD_NULL        0x05  


unsigned long lastInferenceTime = 0;
unsigned long inferenceInterval = 200;
uint8_t lastEmotionCommand = CMD_NULL;

void setup()
{
   
    Serial.begin(115200);
    while (!Serial) delay(100); 

   
    Wire.begin();
    Wire.setClock(400000);
    
    
#if SSCMA_CONNECT_TO_XIAO_S3
    AI.begin(&Wire);
#endif

#if SSCMA_CONNECT_TO_GORVE_VISION_AI
    Wire.begin();
    AI.begin(&Wire, D3);
#endif

   
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, HIGH);
    
    
    sendCommandToWio(CMD_NULL);
    
    Serial.println("Enhanced Emotion Detection System Ready!");
    Serial.println("Emotion Classes:");
    Serial.println("0: Angry, 1: Confused, 2: Happy, 3: Neutral, 4: Sad, 5: Null");
    Serial.println("I2C Communication optimized for reduced latency");
}

void loop()
{
    unsigned long currentTime = millis();
    
    
    if (currentTime - lastInferenceTime >= inferenceInterval)
    {
        lastInferenceTime = currentTime;
        
        uint8_t detectedEmotion = CMD_NULL; 
        float highestScore = 0.0;

        if (!AI.invoke(1, false, false))
        {
            
            for (int i = 0; i < AI.classes().size(); i++)
            {
               
                if (AI.classes()[i].score > highestScore && AI.classes()[i].score > 0.5)
                {
                    highestScore = AI.classes()[i].score;
                    
                    
                    switch (AI.classes()[i].target)
                    {
                        case 0: detectedEmotion = CMD_ANGRY; break;
                        case 1: detectedEmotion = CMD_CONFUSED; break;
                        case 2: detectedEmotion = CMD_HAPPY; break;
                        case 3: detectedEmotion = CMD_NEUTRAL; break;
                        case 4: detectedEmotion = CMD_SAD; break;
                        case 5: detectedEmotion = CMD_NULL; break;
                        default: detectedEmotion = CMD_NULL; break;
                    }
                    
                    
                    Serial.print("Emotion detected - Class: ");
                    Serial.print(AI.classes()[i].target);
                    Serial.print(" (");
                    switch (AI.classes()[i].target)
                    {
                        case 0: Serial.print("Angry"); break;
                        case 1: Serial.print("Confused"); break;
                        case 2: Serial.print("Happy"); break;
                        case 3: Serial.print("Neutral"); break;
                        case 4: Serial.print("Sad"); break;
                        case 5: Serial.print("Null"); break;
                        default: Serial.print("Unknown"); break;
                    }
                    Serial.print(") Score: ");
                    Serial.println(AI.classes()[i].score);
                }
            }
        }

        
        if (detectedEmotion != lastEmotionCommand)
        {
            lastEmotionCommand = detectedEmotion;
            
            if (detectedEmotion == CMD_NULL)
            {
                digitalWrite(LED_PIN, HIGH);
                sendCommandToWio(CMD_NULL);
                Serial.println("No emotion detected. Showing bus stop");
            }
            else
            {
                digitalWrite(LED_PIN, LOW);
                sendCommandToWio(detectedEmotion);
                Serial.print("Emotion changed to: ");
                switch (detectedEmotion)
                {
                    case CMD_ANGRY: Serial.println("Angry"); break;
                    case CMD_CONFUSED: Serial.println("Confused"); break;
                    case CMD_HAPPY: Serial.println("Happy"); break;
                    case CMD_NEUTRAL: Serial.println("Neutral"); break;
                    case CMD_SAD: Serial.println("Sad"); break;
                }
            }
        }
    }
    
   
    delay(10);
}

void sendCommandToWio(uint8_t command)
{
    Wire.beginTransmission(WIO_TERMINAL_I2C_ADDRESS);
    Wire.write(command);
    
    uint8_t error = Wire.endTransmission();
    
    if (error == 0)
    {
        Serial.print("I2C Command sent successfully: 0x");
        Serial.println(command, HEX);
    }
    else
    {
        Serial.print("I2C Error: ");
        Serial.println(error);
        
       
        delay(10);
        Wire.beginTransmission(WIO_TERMINAL_I2C_ADDRESS);
        Wire.write(command);
        Wire.endTransmission();
    }
}

Wio Terminal I2c.ino

Arduino
Receives the information from the XIAO board to display which emoji
#include <TFT_eSPI.h>
#include <SPI.h>
#include <Wire.h>

TFT_eSPI tft = TFT_eSPI();


#define BUZZER_PIN    WIO_BUZZER


#define WIO_TERMINAL_I2C_ADDRESS 0x08


#define CMD_ANGRY       0x00
#define CMD_CONFUSED    0x01
#define CMD_HAPPY       0x02
#define CMD_NEUTRAL     0x03
#define CMD_SAD         0x04
#define CMD_NULL        0x05 


#define COLOR_BG          0x0000
#define COLOR_FACE        0xFFE0 
#define COLOR_EYE         0x0000  
#define COLOR_MOUTH       0x0000  
#define COLOR_BLUSH       0xF81F 
#define COLOR_ANGRY_RED   0xF800  
#define COLOR_CONFUSED    0x8410  
#define COLOR_WHITE       0xFFFF  


enum Emotion {
  NEUTRAL,   
  HAPPY,     
  SAD,        
  ANGRY,     
  CONFUSED,  
  BUS_STOP,  
  EMOTION_COUNT
};


Emotion emotionCycle[] = {HAPPY, SAD, ANGRY, CONFUSED, NEUTRAL};
const int cycleLength = 5;


Emotion currentEmotion = BUS_STOP;
bool faceDetectionMode = false;


unsigned long lastEmotionChange = 0;
unsigned long emotionInterval = 7000; 
int currentCycleIndex = 0;


volatile uint8_t receivedCommand = CMD_NULL;
volatile bool newCommandReceived = false;

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

  tft.init();
  tft.setRotation(3);
  

  pinMode(BUZZER_PIN, OUTPUT);
  

  Wire.begin(WIO_TERMINAL_I2C_ADDRESS);
  Wire.onReceive(receiveI2CCommand);
  

  drawCurrentEmotion();
  
  Serial.println("Automatic Emotion Display Ready!");
  Serial.println("Waiting for emotion detection commands...");
  Serial.println("Emotion Classes: 0=Angry, 1=Confused, 2=Happy, 3=Neutral, 4=Sad, 5=Null(Bus Stop)");
}

void loop() {
  
  if (newCommandReceived) {
    handleI2CCommand();
    newCommandReceived = false;
  }
  
  delay(10);
}

void receiveI2CCommand(int numBytes) {
  if (numBytes > 0) {
    receivedCommand = Wire.read();
    newCommandReceived = true;
  }
  

  while (Wire.available()) {
    Wire.read();
  }
}

void handleI2CCommand() {
  switch (receivedCommand) {
    case CMD_ANGRY:
      Serial.println("Emotion: Angry ");
      currentEmotion = ANGRY;
      drawCurrentEmotion();
      playAmbientSound(currentEmotion);
      break;
      
    case CMD_CONFUSED:
      Serial.println("Emotion: Confused ");
      currentEmotion = CONFUSED;
      drawCurrentEmotion();
      playAmbientSound(currentEmotion);
      break;
      
    case CMD_HAPPY:
      Serial.println("Emotion: Happy ");
      currentEmotion = HAPPY;
      drawCurrentEmotion();
      playAmbientSound(currentEmotion);
      break;
      
    case CMD_NEUTRAL:
      Serial.println("Emotion: Neutral ");
      currentEmotion = NEUTRAL;
      drawCurrentEmotion();
      playAmbientSound(currentEmotion);
      break;
      
    case CMD_SAD:
      Serial.println("Emotion: Sad ");
      currentEmotion = SAD;
      drawCurrentEmotion();
      playAmbientSound(currentEmotion);
      break;
      
    case CMD_NULL:
      Serial.println("No emotion detected. Showing bus stop sign");
      currentEmotion = BUS_STOP;
      drawCurrentEmotion();
      break;
      
    default:
      Serial.print("Unknown I2C command received: 0x");
      Serial.println(receivedCommand, HEX);
      break;
  }
}

void printEmotionName(Emotion emotion) {
  switch (emotion) {
    case NEUTRAL: Serial.println("Neutral "); break;
    case HAPPY: Serial.println("Happy "); break;
    case SAD: Serial.println("Sad "); break;
    case ANGRY: Serial.println("Angry "); break;
    case CONFUSED: Serial.println("Confused "); break;
    case BUS_STOP: Serial.println("Bus Stop"); break;
  }
}

void playAmbientSound(Emotion emotion) {
  switch (emotion) {
    case NEUTRAL:
      
      tone(BUZZER_PIN, 440, 200);
      break;
      
    case HAPPY:
      
      tone(BUZZER_PIN, 523, 150);
      delay(180);
      tone(BUZZER_PIN, 659, 150); 
      delay(180);
      tone(BUZZER_PIN, 784, 200);
      break;
      
    case SAD:
      
      tone(BUZZER_PIN, 440, 300); 
      delay(350);
      tone(BUZZER_PIN, 392, 300); 
      delay(350);
      tone(BUZZER_PIN, 349, 400); 
      break;
      
    case ANGRY:
      
      for (int i = 0; i < 3; i++) {
        tone(BUZZER_PIN, 150, 100); 
        delay(120);
        tone(BUZZER_PIN, 200, 100);
        delay(120);
      }
      break;
      
    case CONFUSED:
      {
      
        int confusedNotes[] = {330, 440, 370, 494, 415, 523};
        for (int i = 0; i < 6; i++) {
          tone(BUZZER_PIN, confusedNotes[i], 120);
          delay(150);
        }
      }
      break;
      
    case BUS_STOP:
     
      break;
  }
}

void drawCurrentEmotion() {

  tft.fillScreen(COLOR_BG);
  

  int centerX = 160; 
  int centerY = 120;
  int faceRadius = 80;
  
  switch (currentEmotion) {
    case NEUTRAL:
      drawNeutralEmoji(centerX, centerY, faceRadius);
      break;
    case HAPPY:
      drawHappyEmoji(centerX, centerY, faceRadius);
      break;
    case SAD:
      drawSadEmoji(centerX, centerY, faceRadius);
      break;
    case ANGRY:
      drawAngryEmoji(centerX, centerY, faceRadius);
      break;
    case CONFUSED:
      drawConfusedEmoji(centerX, centerY, faceRadius);
      break;
    case BUS_STOP:
      drawBusStopSign(centerX, centerY, faceRadius);
      break;
  }
}

void drawNeutralEmoji(int x, int y, int radius) {

  tft.fillCircle(x, y, radius, COLOR_FACE);
  tft.drawCircle(x, y, radius, COLOR_EYE);
  

  int eyeSize = radius / 10;
  int eyeOffset = radius / 3;
  tft.fillCircle(x - eyeOffset, y - radius/4, eyeSize, COLOR_EYE);
  tft.fillCircle(x + eyeOffset, y - radius/4, eyeSize, COLOR_EYE);
  
 
  int mouthWidth = radius / 2;
  tft.fillRect(x - mouthWidth/2, y + radius/4, mouthWidth, 4, COLOR_EYE);
}

void drawHappyEmoji(int x, int y, int radius) {

  tft.fillCircle(x, y, radius, COLOR_FACE);
  tft.drawCircle(x, y, radius, COLOR_EYE);
  

  int eyeOffset = radius / 3;
  int eyeWidth = radius / 6;
  
  
  for (int i = 0; i < eyeWidth; i++) {
    int eyeY = y - radius/4 + (i - eyeWidth/2) * (i - eyeWidth/2) / (eyeWidth/4);
    tft.drawPixel(x - eyeOffset - eyeWidth/2 + i, eyeY, COLOR_EYE);
    tft.drawPixel(x - eyeOffset - eyeWidth/2 + i, eyeY + 1, COLOR_EYE);
  }
  
 
  for (int i = 0; i < eyeWidth; i++) {
    int eyeY = y - radius/4 + (i - eyeWidth/2) * (i - eyeWidth/2) / (eyeWidth/4);
    tft.drawPixel(x + eyeOffset - eyeWidth/2 + i, eyeY, COLOR_EYE);
    tft.drawPixel(x + eyeOffset - eyeWidth/2 + i, eyeY + 1, COLOR_EYE);
  }
  

  int mouthRadius = radius / 2;
  for (int angle = 0; angle <= 180; angle += 2) {
    int mouthX = x + cos(radians(angle)) * mouthRadius;
    int mouthY = y + radius/6 + sin(radians(angle)) * mouthRadius/2;
    tft.fillCircle(mouthX, mouthY, 2, COLOR_EYE);
  }
  

  tft.fillCircle(x - radius * 0.7, y + radius/8, radius/8, COLOR_BLUSH);
  tft.fillCircle(x + radius * 0.7, y + radius/8, radius/8, COLOR_BLUSH);
}

void drawSadEmoji(int x, int y, int radius) {

  tft.fillCircle(x, y, radius, COLOR_FACE);
  tft.drawCircle(x, y, radius, COLOR_EYE);
  
 
  int eyeSize = radius / 8;
  int eyeOffset = radius / 3;
  tft.fillCircle(x - eyeOffset, y - radius/4, eyeSize, COLOR_EYE);
  tft.fillCircle(x + eyeOffset, y - radius/4, eyeSize, COLOR_EYE);
  
 
  for (int i = 0; i < 3; i++) {
    tft.drawLine(x - eyeOffset - eyeSize, y - radius/3 + i, x - eyeOffset + eyeSize, y - radius/2.5 + i, COLOR_EYE);
    tft.drawLine(x + eyeOffset - eyeSize, y - radius/2.5 + i, x + eyeOffset + eyeSize, y - radius/3 + i, COLOR_EYE);
  }
  
 
  int mouthRadius = radius / 2.5;
  int mouthY = y + radius/3;
  
 
  for (int angle = 30; angle <= 150; angle += 1) {
    int mouthX = x + cos(radians(angle)) * mouthRadius;
    int mouthYPos = mouthY + sin(radians(angle)) * (mouthRadius/3);
    tft.fillCircle(mouthX, mouthYPos, 2, COLOR_EYE);
  }
  
  
  tft.fillCircle(x - eyeOffset + eyeSize/2, y - radius/8, 4, 0x87FF);
  tft.fillCircle(x - eyeOffset + eyeSize/2, y - radius/12, 3, 0x87FF);
  tft.fillCircle(x - eyeOffset + eyeSize/2, y, 2, 0x87FF);
}

void drawAngryEmoji(int x, int y, int radius) {
 
  tft.fillCircle(x, y, radius, COLOR_ANGRY_RED);
  tft.drawCircle(x, y, radius, COLOR_EYE);
  
 
  int eyeOffset = radius / 3;
  int eyeSize = radius / 12;
  
  
  tft.fillRect(x - eyeOffset - eyeSize, y - radius/4, eyeSize*2, eyeSize, COLOR_EYE);
  tft.fillRect(x + eyeOffset - eyeSize, y - radius/4, eyeSize*2, eyeSize, COLOR_EYE);
  
 
  tft.fillTriangle(
    x - eyeOffset - eyeSize*2, y - radius/2,
    x - eyeOffset + eyeSize, y - radius/3,
    x - eyeOffset - eyeSize, y - radius/3,
    COLOR_EYE
  );
  
  tft.fillTriangle(
    x + eyeOffset + eyeSize*2, y - radius/2,
    x + eyeOffset - eyeSize, y - radius/3,
    x + eyeOffset + eyeSize, y - radius/3,
    COLOR_EYE
  );
  

  int mouthWidth = radius / 3;
  tft.fillRect(x - mouthWidth/2, y + radius/3, mouthWidth, 6, COLOR_EYE);
  

  for (int i = 0; i < 6; i++) {
    float angle = i * 60 * PI / 180;
    int markX = x + cos(angle) * (radius + 15);
    int markY = y + sin(angle) * (radius + 15);
    tft.fillCircle(markX, markY, 3, COLOR_ANGRY_RED);
  }
}

void drawConfusedEmoji(int x, int y, int radius) {
 
  tft.fillCircle(x, y, radius, COLOR_FACE);
  tft.drawCircle(x, y, radius, COLOR_EYE);
  
 
  int eyeOffset = radius / 3;
  tft.fillCircle(x - eyeOffset, y - radius/4, radius/10, COLOR_EYE); 
  

  int rightEyeSize = radius/10;
  tft.drawLine(x + eyeOffset - rightEyeSize, y - radius/4 - rightEyeSize, 
               x + eyeOffset + rightEyeSize, y - radius/4 + rightEyeSize, COLOR_EYE);
  tft.drawLine(x + eyeOffset - rightEyeSize, y - radius/4 + rightEyeSize, 
               x + eyeOffset + rightEyeSize, y - radius/4 - rightEyeSize, COLOR_EYE);
  

  for (int i = -radius/3; i < radius/3; i += 2) {
    int waveY = y + radius/4 + sin(i * 0.3) * 3;
    tft.fillCircle(x + i, waveY, 1, COLOR_EYE);
  }
  

  drawSpiral(x - 20, y - radius - 20, 8, COLOR_CONFUSED);
  drawSpiral(x + 20, y - radius - 20, 8, COLOR_CONFUSED);
  
  
  tft.setTextColor(COLOR_CONFUSED);
  tft.setTextSize(3);
  tft.drawString("?", x + radius/2, y - radius/2);
}

void drawSpiral(int centerX, int centerY, int size, uint16_t color) {
  for (float angle = 0; angle < 720; angle += 10) {
    float radius = angle / 100.0;
    int x = centerX + cos(radians(angle)) * radius;
    int y = centerY + sin(radians(angle)) * radius;
    tft.fillCircle(x, y, 1, color);
  }
}

void drawBusStopSign(int x, int y, int radius) {
 
  uint16_t orangeColor = 0xFD20;
  
  
  float octagonRadius = radius * 0.9;
  int points[16]; 
  
  for (int i = 0; i < 8; i++) {
    float angle = i * 45 * PI / 180; 
    points[i*2] = x + cos(angle) * octagonRadius;     
    points[i*2+1] = y + sin(angle) * octagonRadius; 
  }
  
  
  for (int i = 0; i < 8; i++) {
    int next = (i + 1) % 8;
    tft.fillTriangle(
      x, y,
      points[i*2], points[i*2+1],
      points[next*2], points[next*2+1],
      orangeColor
    );
  }
  

  for (int i = 0; i < 8; i++) {
    int next = (i + 1) % 8;
    tft.drawLine(points[i*2], points[i*2+1], points[next*2], points[next*2+1], COLOR_EYE);
  }
  
 
  int busWidth = radius / 2;
  int busHeight = radius / 3;
  int busX = x - busWidth/2;
  int busY = y - busHeight/2 - radius/6;
  

  tft.fillRoundRect(busX, busY, busWidth, busHeight, 4, COLOR_EYE);
  

  int windowWidth = busWidth / 4;
  int windowHeight = busHeight / 3;
  tft.fillRect(busX + 4, busY + 2, windowWidth, windowHeight, orangeColor);
  tft.fillRect(busX + busWidth - windowWidth - 4, busY + 2, windowWidth, windowHeight, orangeColor);
  

  int wheelSize = 4;
  tft.fillCircle(busX + wheelSize + 2, busY + busHeight + 2, wheelSize, COLOR_EYE);
  tft.fillCircle(busX + busWidth - wheelSize - 2, busY + busHeight + 2, wheelSize, COLOR_EYE);
  
 
  tft.fillRect(busX + busWidth/3, busY + busHeight/2, 3, busHeight/2, orangeColor);
  

  tft.setTextColor(COLOR_EYE);
  tft.setTextSize(2);
  tft.drawString("BUS", x - 18, y + radius/3);
  tft.drawString("STOP", x - 24, y + radius/3 + 16);
}

i2c inference.ino

Arduino
Used to receive inference results from the Grove vison AI V2 via I2C.
#include <Arduino.h>
#include <Wire.h>
#include <Seeed_Arduino_SSCMA.h>

SSCMA AI;

#define SSCMA_CONNECT_TO_XIAO_S3         1
#define SSCMA_CONNECT_TO_GORVE_VISION_AI 0
#define LED_PIN                          21 // LED pin (confirmed as 21)

void setup()
{
    // Initialize serial communication
    Serial.begin(115200);
    while (!Serial) delay(1000); // Wait for serial connection

    // Initialize I2C and SSCMA
#if SSCMA_CONNECT_TO_XIAO_S3
    Wire.begin();
    AI.begin(&Wire);
#endif

#if SSCMA_CONNECT_TO_GORVE_VISION_AI
    Wire.begin();
    AI.begin(&Wire, D3);
#endif

    // Initialize LED pin
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, HIGH); // Start with LED off
}

void loop()
{
    bool personDetected = false; // Flag to track person detection

    if (!AI.invoke(1, false, false))
    {
        Serial.println("invoke success");
        Serial.print("perf: prepocess=");
        Serial.print(AI.perf().prepocess);
        Serial.print(", inference=");
        Serial.print(AI.perf().inference);
        Serial.print(", postpocess=");
        Serial.println(AI.perf().postprocess);

        // Check for person detection in bounding boxes
        for (int i = 0; i < AI.boxes().size(); i++)
        {
            Serial.print("Box[");
            Serial.print(i);
            Serial.print("] target=");
            Serial.print(AI.boxes()[i].target);
            Serial.print(", score=");
            Serial.print(AI.boxes()[i].score);
            Serial.print(", x=");
            Serial.print(AI.boxes()[i].x);
            Serial.print(", y=");
            Serial.print(AI.boxes()[i].y);
            Serial.print(", w=");
            Serial.print(AI.boxes()[i].w);
            Serial.print(", h=");
            Serial.println(AI.boxes()[i].h);

            // Set flag if person (target == 0) is detected
            if (AI.boxes()[i].target == 0)
            {
                personDetected = true;
            }
            else
            {
                personDetected = false;
            }
        }

        // Control LED based on person detection
        if (personDetected)
        {
            digitalWrite(LED_PIN, LOW); // Turn LED on
            Serial.println("Person detected! LED ON");
        }
        else
        {
            digitalWrite(LED_PIN, HIGH); // Turn LED off
            Serial.println("No person detected. LED OFF");
        }

        // Optional: Uncomment for additional outputs (minimize to improve performance)
        /*
        for (int i = 0; i < AI.classes().size(); i++)
        {
            Serial.print("Class[");
            Serial.print(i);
            Serial.print("] target=");
            Serial.print(AI.classes()[i].target);
            Serial.print(", score=");
            Serial.println(AI.classes()[i].score);
        }
        for (int i = 0; i < AI.points().size(); i++)
        {
            Serial.print("Point[");
            Serial.print(i);
            Serial.print("]: target=");
            Serial.print(AI.points()[i].target);
            Serial.print(", score=");
            Serial.print(AI.points()[i].score);
            Serial.print(", x=");
            Serial.print(AI.points()[i].x);
            Serial.print(", y=");
            Serial.println(AI.points()[i].y);
        }
        for (int i = 0; i < AI.keypoints().size(); i++)
        {
            Serial.print("keypoint[");
            Serial.print(i);
            Serial.print("] target=");
            Serial.print(AI.keypoints()[i].box.target);
            Serial.print(", score=");
            Serial.print(AI.keypoints()[i].box.score);
            Serial.print(", box:[x=");
            Serial.print(AI.keypoints()[i].box.x);
            Serial.print(", y=");
            Serial.print(AI.keypoints()[i].box.y);
            Serial.print(", w=");
            Serial.print(AI.keypoints()[i].box.w);
            Serial.print(", h=");
            Serial.print(AI.keypoints()[i].box.h);
            Serial.print("], points:[");
            for (int j = 0; j < AI.keypoints()[i].points.size(); j++)
            {
                Serial.print("[");
                Serial.print(AI.keypoints()[i].points[j].x);
                Serial.print(",");
                Serial.print(AI.keypoints()[i].points[j].y);
                Serial.print("],");
            }
            Serial.println("]");
        }
        if (AI.last_image().length() > 0)
        {
            Serial.print("Last image:");
            Serial.println(AI.last_image().c_str());
        }
        */
    }

    // Optional: Add delay to control inference rate
    delay(100); // Adjust based on desired frame rate (e.g., 50-500ms)
}

Credits

Shawn Sony
1 project • 1 follower
Sreya. V
1 project • 1 follower

Comments