u/AdjectiveNoun11

▲ 4 r/ArduinoHelp+1 crossposts

Howdy- working on a project, designing a children's robotic toy using an Arduino Mega. Currently, the project has seven integrated components:
- a set of LED buttons
- Two RGB LED outputs
- A PIR sensor, to detect when paper is "fed" to the toy
- An MS18 servo motor to drive the mouth component
- A potentiometer used as a dial between two response lists (to be expanded in the future)
- A DY-SV5W Voice Playback Module MP3 with speaker for song output
- A SunFounder color sensor

The project as is (see below) works as intended:

#include <Servo.h>


/* 
 * Integrated Emotion Machine
 * Board: Arduino Mega 2560
 * MP3 Module on Serial3 (TX3=14, RX3=15)
 * Potentiometer on A0
 * RGB LEDs on D7, D6, D2
 * PIR on D8
 * Servo on D35
 * Color sensor S0-S3/OUT on D42-D46
 */


// ===================== MP3 CONTROL =====================
byte commandLength;
byte command[6];
int checkSum = 0;


void sendCommand() {
  for (int q = 0; q < commandLength; q++) {
    Serial3.write(command[q]);
    Serial.print(command[q], HEX);
    Serial.print(" ");
  }
  Serial.println("End");
}


void playTrack(uint16_t trackNumber) {
  command[0] = 0xAA;
  command[1] = 0x07;
  command[2] = 0x02;
  command[3] = highByte(trackNumber);
  command[4] = lowByte(trackNumber);


  checkSum = 0;
  for (int q = 0; q < 5; q++) {
    checkSum += command[q];
  }


  command[5] = lowByte(checkSum);
  commandLength = 6;
  sendCommand();
}


void setVolume(byte vol) {
  command[0] = 0xAA;
  command[1] = 0x13;
  command[2] = 0x01;
  command[3] = vol;


  checkSum = 0;
  for (int q = 0; q < 4; q++) {
    checkSum += command[q];
  }


  command[4] = lowByte(checkSum);
  commandLength = 5;
  sendCommand();
}


// ===================== RGB LED CONTROL =====================
const int ledRed   = 7;
const int ledGreen = 6;
const int ledBlue  = 2;


const unsigned long songDuration = 15000;
bool songPlaying = false;
unsigned long songStartTime = 0;


void setLEDColor(byte r, byte g, byte b) {
  analogWrite(ledRed,   255 - r);
  analogWrite(ledGreen, 255 - g);
  analogWrite(ledBlue,  255 - b);
}


void setLEDWhite() {
  setLEDColor(255, 255, 255);
}


void setEmotionLED(String emotion) {
  if (emotion == "angry") {
    setLEDColor(255, 0, 0);
  }
  else if (emotion == "anxious") {
    setLEDColor(255, 60, 0);
  }
  else if (emotion == "happy") {
    setLEDColor(255, 180, 0);
  }
  else if (emotion == "fear") {
    setLEDColor(0, 255, 0);
  }
  else if (emotion == "sad") {
    setLEDColor(0, 0, 255);
  }
}


// ===================== EMOTION STRENGTH =====================
const int strengthPin = A0;
int emotionStrength = 1;


void updateEmotionStrength() {
  int value = analogRead(strengthPin);


  if (value < 400) {
    emotionStrength = 1;
  } 
  else if (value > 600) {
    emotionStrength = 2;
  }
}


// ===================== EMOTION PLAYBACK =====================
void playEmotion(String emotion) {
  updateEmotionStrength();


  Serial.print(emotion);
  Serial.print(" | Strength: ");
  Serial.println(emotionStrength);


  setEmotionLED(emotion);


  if (emotion == "angry") {
    playTrack(emotionStrength == 1 ? 1 : 2);
  }
  else if (emotion == "anxious") {
    playTrack(emotionStrength == 1 ? 3 : 4);
  }
  else if (emotion == "fear") {
    playTrack(emotionStrength == 1 ? 5 : 6);
  }
  else if (emotion == "happy") {
    playTrack(emotionStrength == 1 ? 7 : 8);
  }
  else if (emotion == "sad") {
    playTrack(emotionStrength == 1 ? 9 : 10);
  }


  songPlaying = true;
  songStartTime = millis();
}


// ===================== BUTTON INPUT =====================
const int pins[5] = {23, 25, 27, 29, 31};
String emotions[5] = {"angry", "anxious", "happy", "fear", "sad"};


const unsigned long debounceDelay = 25;
int lastReading[5];
int stableState[5];
unsigned long lastDebounceTime[5];


// ===================== PIR + SERVO =====================
const int pirPin = 8;
const int servoPin = 35;


Servo paperServo;


const int servoMinAngle = 0;
const int servoMaxAngle = 180;
const int servoStep = 2;
const int servoDelay = 10;
const int sweepCount = 5;


bool paperHasTriggered = false;


void runServoSweeps() {
  for (int sweep = 0; sweep < sweepCount; sweep++) {
    for (int angle = servoMinAngle; angle <= servoMaxAngle; angle += servoStep) {
      paperServo.write(angle);
      delay(servoDelay);
    }


    for (int angle = servoMaxAngle; angle >= servoMinAngle; angle -= servoStep) {
      paperServo.write(angle);
      delay(servoDelay);
    }
  }


  paperServo.write(servoMinAngle);
}


// ===================== COLOR SENSOR =====================
const int S0 = 42;
const int S1 = 43;
const int S2 = 44;
const int S3 = 45;
const int sensorOut = 46;


const int numSamples = 100;
const int tolerance = 8;


int readColor(bool s2, bool s3) {
  digitalWrite(S2, s2);
  digitalWrite(S3, s3);
  return pulseIn(sensorOut, LOW, 20000);
}


bool inRange(int value, int target, int range) {
  return value >= target - range && value <= target + range;
}


String detectColor(int r, int g, int b) {
  if (inRange(r, 25, tolerance) &&
      inRange(g, 42, tolerance) &&
      inRange(b, 58, tolerance)) {
    return "yellow";
  }


  if (inRange(r, 13, tolerance) &&
      inRange(g, 55, tolerance) &&
      inRange(b, 24, tolerance)) {
    return "red";
  }


  if (inRange(r, 12, tolerance) &&
      inRange(g, 26, tolerance) &&
      inRange(b, 33, tolerance)) {
    return "orange";
  }


  if (inRange(r, 23, tolerance) &&
      inRange(g, 25, tolerance) &&
      inRange(b, 38, tolerance)) {
    return "green";
  }


  if (inRange(r, 57, tolerance) &&
      inRange(g, 33, tolerance) &&
      inRange(b, 19, tolerance)) {
    return "blue";
  }


  return "unknown";
}


String colorToEmotion(String color) {
  if (color == "red") {
    return "angry";
  }
  else if (color == "orange") {
    return "anxious";
  }
  else if (color == "yellow") {
    return "happy";
  }
  else if (color == "green") {
    return "fear";
  }
  else if (color == "blue") {
    return "sad";
  }


  return "none";
}


String readDetectedColor() {
  long redTotal = 0;
  long greenTotal = 0;
  long blueTotal = 0;


  Serial.println("Reading paper color...");


  for (int i = 0; i < numSamples; i++) {
    redTotal   += readColor(LOW, LOW);
    greenTotal += readColor(HIGH, HIGH);
    blueTotal  += readColor(LOW, HIGH);
    delay(2);
  }


  int redAvg = redTotal / numSamples;
  int greenAvg = greenTotal / numSamples;
  int blueAvg = blueTotal / numSamples;


  String detectedColor = detectColor(redAvg, greenAvg, blueAvg);


  Serial.print("R: ");
  Serial.print(redAvg);
  Serial.print(" | G: ");
  Serial.print(greenAvg);
  Serial.print(" | B: ");
  Serial.print(blueAvg);
  Serial.print("  -->  ");
  Serial.println(detectedColor);


  return detectedColor;
}


// ===================== SETUP =====================
void setup() {
  Serial.begin(115200);
  Serial3.begin(9600);


  Serial.println("Integrated Emotion Machine Start");


  for (int i = 0; i < 5; i++) {
    pinMode(pins[i], INPUT_PULLUP);
    lastReading[i] = HIGH;
    stableState[i] = HIGH;
    lastDebounceTime[i] = 0;
  }


  pinMode(strengthPin, INPUT);


  pinMode(ledRed, OUTPUT);
  pinMode(ledGreen, OUTPUT);
  pinMode(ledBlue, OUTPUT);
  setLEDWhite();


  pinMode(pirPin, INPUT);


  paperServo.attach(servoPin);
  paperServo.write(servoMinAngle);


  pinMode(S0, OUTPUT);
  pinMode(S1, OUTPUT);
  pinMode(S2, OUTPUT);
  pinMode(S3, OUTPUT);
  pinMode(sensorOut, INPUT);


  digitalWrite(S0, HIGH);
  digitalWrite(S1, LOW);


  setVolume(20);
}


// ===================== LOOP =====================
void loop() {
  if (songPlaying && millis() - songStartTime >= songDuration) {
    setLEDWhite();
    songPlaying = false;
  }


  // Manual button input
  for (int i = 0; i < 5; i++) {
    int reading = digitalRead(pins[i]);


    if (reading != lastReading[i]) {
      lastDebounceTime[i] = millis();
    }


    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      if (reading != stableState[i]) {
        stableState[i] = reading;


        if (stableState[i] == LOW) {
          playEmotion(emotions[i]);
        }
      }
    }


    lastReading[i] = reading;
  }


  // Paper detection flow
  int pirState = digitalRead(pirPin);


  if (pirState == HIGH && !paperHasTriggered) {
    Serial.println("Paper detected.");


    paperHasTriggered = true;


    String paperColor = readDetectedColor();
    String emotion = colorToEmotion(paperColor);


    Serial.print("Mapped emotion: ");
    Serial.println(emotion);


    if (emotion != "none") {
      playEmotion(emotion);
    }
    else {
      Serial.println("Unknown color. No audio played.");
    }


    Serial.println("Moving paper.");
    runServoSweeps();
    Serial.println("Paper movement complete.");
  }


  if (pirState == LOW && paperHasTriggered) {
    Serial.println("PIR reset. Ready for next paper.");
    paperHasTriggered = false;
  }
}

However, I'm also trying to incorporate a DF2301Q Offline Voice Recognition Sensor, via the code below:

#include "DFRobot_DF2301Q.h"


#if (defined(ARDUINO_AVR_UNO) || defined(ESP8266))
  SoftwareSerial softSerial(/*rx =*/4, /*tx =*/5);
  DFRobot_DF2301Q_UART DF2301Q(/*softSerial =*/&softSerial);
#elif defined(ESP32)
  DFRobot_DF2301Q_UART DF2301Q(/*hardSerial =*/&Serial1, /*rx =*/D3, /*tx =*/D2);
#else
  // Arduino Mega (pins 18 TX1, 19 RX1)
  DFRobot_DF2301Q_UART DF2301Q(&Serial1);
#endif


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


  while (!(DF2301Q.begin())) {
    Serial.println("Communication with device failed, please check connection");
    delay(3000);
  }


  Serial.println("Begin ok!");
  Serial.println("Listening for voice commands...");


  // Optional confirmation sound
  DF2301Q.playByCMDID(23);
}


void loop() {
  uint8_t CMDID = DF2301Q.getCMDID();


  if (CMDID != 0) {
    Serial.print("CMDID = ");
    Serial.println(CMDID);


    // ===================== YOUR TRAINED WORDS =====================


    if (CMDID == 5)  Serial.println("Angry");
    if (CMDID == 6)  Serial.println("Mad");
    if (CMDID == 7)  Serial.println("Enraged");


    if (CMDID == 8)  Serial.println("Anxious");
    if (CMDID == 9)  Serial.println("Worried");
    if (CMDID == 10) Serial.println("Uncertain");


    if (CMDID == 11) Serial.println("Joy");
    if (CMDID == 12) Serial.println("Cheerful");
    if (CMDID == 13) Serial.println("Happy");


    if (CMDID == 14) Serial.println("Scared");
    if (CMDID == 15) Serial.println("Fear");
    if (CMDID == 16) Serial.println("Terror");


    if (CMDID == 17) Serial.println("Sad");
    if (CMDID == 18) Serial.println("Unhappy");
    if (CMDID == 19) Serial.println("Gloomy");
  }


  delay(200);  // faster response than 2000ms
}

Mapping the Command Word emotions to their respective outputs. I'll share my current integration in the comments; despite the Voice Recognition module being trained and recognizing command words, it's not creating an LED or Speaker output when the code is integrated together. I'm a little new to UART Serialization so I'm guessing this is where I'm making a mistake?

To pre-empt some questions: the wiring for the VR and main system are all correct, the code all works fine individually.

Edit: Didn't realize you can only post code blocks in main posts. Here's my current attempt at integration:

#include <Servo.h>
#include "DFRobot_DF2301Q.h"

// ===================== MP3 CONTROL =====================
byte commandLength;
byte command[6];
int checkSum = 0;

void sendCommand() {
  for (int q = 0; q < commandLength; q++) {
    Serial3.write(command[q]);
    Serial.print(command[q], HEX);
    Serial.print(" ");
  }
  Serial.println("End");
}

void playTrack(uint16_t trackNumber) {
  command[0] = 0xAA;
  command[1] = 0x07;
  command[2] = 0x02;
  command[3] = highByte(trackNumber);
  command[4] = lowByte(trackNumber);
  checkSum = 0;
  for (int q = 0; q < 5; q++) checkSum += command[q];
  command[5] = lowByte(checkSum);
  commandLength = 6;
  sendCommand();
}

void setVolume(byte vol) {
  command[0] = 0xAA;
  command[1] = 0x13;
  command[2] = 0x01;
  command[3] = vol;
  checkSum = 0;
  for (int q = 0; q < 4; q++) checkSum += command[q];
  command[4] = lowByte(checkSum);
  commandLength = 5;
  sendCommand();
}

// ===================== RGB LED CONTROL =====================
const int ledRed   = 7;
const int ledGreen = 6;
const int ledBlue  = 2;

const unsigned long songDuration = 15000;
bool songPlaying = false;
unsigned long songStartTime = 0;

void setLEDColor(byte r, byte g, byte b) {
  analogWrite(ledRed,   255 - r);
  analogWrite(ledGreen, 255 - g);
  analogWrite(ledBlue,  255 - b);
}

void setLEDWhite() { setLEDColor(255, 255, 255); }

void setEmotionLED(String emotion) {
  if      (emotion == "angry")   setLEDColor(255, 0,   0);
  else if (emotion == "anxious") setLEDColor(255, 60,  0);
  else if (emotion == "happy")   setLEDColor(255, 180, 0);
  else if (emotion == "fear")    setLEDColor(0,   255, 0);
  else if (emotion == "sad")     setLEDColor(0,   0,   255);
}

// ===================== EMOTION STRENGTH =====================
const int strengthPin = A0;
int emotionStrength = 1;

void updateEmotionStrength() {
  int value = analogRead(strengthPin);
  if      (value < 400) emotionStrength = 1;
  else if (value > 600) emotionStrength = 2;
}

// ===================== EMOTION PLAYBACK =====================
void playEmotion(String emotion) {
  updateEmotionStrength();

  Serial.print(emotion);
  Serial.print(" | Strength: ");
  Serial.println(emotionStrength);

  setEmotionLED(emotion);

  if      (emotion == "angry")   playTrack(emotionStrength == 1 ? 1 : 2);
  else if (emotion == "anxious") playTrack(emotionStrength == 1 ? 3 : 4);
  else if (emotion == "fear")    playTrack(emotionStrength == 1 ? 5 : 6);
  else if (emotion == "happy")   playTrack(emotionStrength == 1 ? 7 : 8);
  else if (emotion == "sad")     playTrack(emotionStrength == 1 ? 9 : 10);

  songPlaying = true;
  songStartTime = millis();
}

// ===================== BUTTON INPUT =====================
const int pins[5]      = {23, 25, 27, 29, 31};
String emotions[5]     = {"angry", "anxious", "happy", "fear", "sad"};
const unsigned long debounceDelay = 25;
int lastReading[5];
int stableState[5];
unsigned long lastDebounceTime[5];

// ===================== PIR + SERVO =====================
const int pirPin   = 8;
const int servoPin = 35;
Servo paperServo;

const int servoMinAngle = 0;
const int servoMaxAngle = 180;
const int servoStep     = 2;
const int servoDelay    = 10;
const int sweepCount    = 5;
bool paperHasTriggered  = false;

void runServoSweeps() {
  for (int sweep = 0; sweep < sweepCount; sweep++) {
    for (int angle = servoMinAngle; angle <= servoMaxAngle; angle += servoStep) {
      paperServo.write(angle);
      delay(servoDelay);
    }
    for (int angle = servoMaxAngle; angle >= servoMinAngle; angle -= servoStep) {
      paperServo.write(angle);
      delay(servoDelay);
    }
  }
  paperServo.write(servoMinAngle);
}

// ===================== COLOR SENSOR =====================
const int S0 = 42, S1 = 43, S2 = 44, S3 = 45, sensorOut = 46;
const int numSamples = 100;
const int tolerance  = 8;

int readColor(bool s2, bool s3) {
  digitalWrite(S2, s2);
  digitalWrite(S3, s3);
  return pulseIn(sensorOut, LOW, 20000);
}

bool inRange(int value, int target, int range) {
  return value >= target - range && value <= target + range;
}

String detectColor(int r, int g, int b) {
  if (inRange(r,25,tolerance) && inRange(g,42,tolerance) && inRange(b,58,tolerance)) return "yellow";
  if (inRange(r,13,tolerance) && inRange(g,55,tolerance) && inRange(b,24,tolerance)) return "red";
  if (inRange(r,12,tolerance) && inRange(g,26,tolerance) && inRange(b,33,tolerance)) return "orange";
  if (inRange(r,23,tolerance) && inRange(g,25,tolerance) && inRange(b,38,tolerance)) return "green";
  if (inRange(r,57,tolerance) && inRange(g,33,tolerance) && inRange(b,19,tolerance)) return "blue";
  return "unknown";
}

String colorToEmotion(String color) {
  if (color == "red")    return "angry";
  if (color == "orange") return "anxious";
  if (color == "yellow") return "happy";
  if (color == "green")  return "fear";
  if (color == "blue")   return "sad";
  return "none";
}

String readDetectedColor() {
  long redTotal = 0, greenTotal = 0, blueTotal = 0;
  Serial.println("Reading paper color...");
  for (int i = 0; i < numSamples; i++) {
    redTotal   += readColor(LOW,  LOW);
    greenTotal += readColor(HIGH, HIGH);
    blueTotal  += readColor(LOW,  HIGH);
    delay(2);
  }
  int redAvg = redTotal / numSamples;
  int greenAvg = greenTotal / numSamples;
  int blueAvg  = blueTotal / numSamples;
  String detectedColor = detectColor(redAvg, greenAvg, blueAvg);
  Serial.print("R: "); Serial.print(redAvg);
  Serial.print(" | G: "); Serial.print(greenAvg);
  Serial.print(" | B: "); Serial.print(blueAvg);
  Serial.print("  -->  "); Serial.println(detectedColor);
  return detectedColor;
}

// ===================== VOICE RECOGNITION =====================
// DF2301Q uses Serial1 on Mega (TX1=18, RX1=19)
// MP3 player uses Serial3 (TX3=14, RX3=15) — no conflict
DFRobot_DF2301Q_UART DF2301Q(&Serial1);

String voiceCMDToEmotion(uint8_t id) {
  if (id >= 5  && id <= 7)  return "angry";    // Angry, Mad, Enraged
  if (id >= 8  && id <= 10) return "anxious";  // Anxious, Worried, Uncertain
  if (id >= 11 && id <= 13) return "happy";    // Joy, Cheerful, Happy
  if (id >= 14 && id <= 16) return "fear";     // Scared, Fear, Terror
  if (id >= 17 && id <= 19) return "sad";      // Sad, Unhappy, Gloomy
  return "none";
}

// ===================== SETUP =====================
void setup() {
  Serial.begin(115200);
  Serial3.begin(9600);

  Serial.println("Integrated Emotion Machine Start");

  // Buttons
  for (int i = 0; i < 5; i++) {
    pinMode(pins[i], INPUT_PULLUP);
    lastReading[i]     = HIGH;
    stableState[i]     = HIGH;
    lastDebounceTime[i] = 0;
  }

  pinMode(strengthPin, INPUT);

  // LEDs
  pinMode(ledRed,   OUTPUT);
  pinMode(ledGreen, OUTPUT);
  pinMode(ledBlue,  OUTPUT);
  setLEDWhite();

  // PIR + Servo
  pinMode(pirPin, INPUT);
  paperServo.attach(servoPin);
  paperServo.write(servoMinAngle);

  // Color sensor
  pinMode(S0, OUTPUT); pinMode(S1, OUTPUT);
  pinMode(S2, OUTPUT); pinMode(S3, OUTPUT);
  pinMode(sensorOut, INPUT);
  digitalWrite(S0, HIGH);
  digitalWrite(S1, LOW);

  // Voice recognition
  while (!DF2301Q.begin()) {
    Serial.println("DF2301Q not found, retrying...");
    delay(3000);
  }
  Serial.println("DF2301Q ready.");
  DF2301Q.playByCMDID(23);  // confirmation chime

  setVolume(20);
}

// ===================== LOOP =====================
void loop() {
  // Song timeout → reset LED
  if (songPlaying && millis() - songStartTime >= songDuration) {
    setLEDWhite();
    songPlaying = false;
  }

  // --- INPUT 1: Physical buttons ---
  for (int i = 0; i < 5; i++) {
    int reading = digitalRead(pins[i]);
    if (reading != lastReading[i]) lastDebounceTime[i] = millis();
    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      if (reading != stableState[i]) {
        stableState[i] = reading;
        if (stableState[i] == LOW) playEmotion(emotions[i]);
      }
    }
    lastReading[i] = reading;
  }

  // --- INPUT 2: Voice recognition ---
  uint8_t cmdID = DF2301Q.getCMDID();
  if (cmdID != 0) {
    Serial.print("Voice CMDID: ");
    Serial.println(cmdID);
    String voiceEmotion = voiceCMDToEmotion(cmdID);
    if (voiceEmotion != "none") {
      Serial.print("Voice emotion: ");
      Serial.println(voiceEmotion);
      playEmotion(voiceEmotion);
    }
  }

  // --- INPUT 3: Paper (PIR + color sensor) ---
  int pirState = digitalRead(pirPin);

  if (pirState == HIGH && !paperHasTriggered) {
    Serial.println("Paper detected.");
    paperHasTriggered = true;

    String paperColor  = readDetectedColor();
    String paperEmotion = colorToEmotion(paperColor);

    Serial.print("Mapped emotion: ");
    Serial.println(paperEmotion);

    if (paperEmotion != "none") {
      playEmotion(paperEmotion);
    } else {
      Serial.println("Unknown color. No audio played.");
    }

    Serial.println("Moving paper.");
    runServoSweeps();
    Serial.println("Paper movement complete.");
  }

  if (pirState == LOW && paperHasTriggered) {
    Serial.println("PIR reset. Ready for next paper.");
    paperHasTriggered = false;
  }
}
reddit.com
u/AdjectiveNoun11 — 15 days ago