Supachai Vorapojpisut
Published © CC BY

Trying Thai voice commands on Arduino Nano RP2040 board

Arduino Speech Recognition Engine is quite interesting with its offline, MCU-oriented, and support more than 40 languages.

BeginnerProtip1 hour160
Trying Thai voice commands on Arduino Nano RP2040 board

Things used in this project

Hardware components

Arduino Nano RP2040 Connect
Arduino Nano RP2040 Connect
×1

Software apps and online services

Arduino Speech Recognition Engine
PlatformIO IDE
PlatformIO IDE
DSpotterSDK Maker

Story

Read more

Code

main.cpp

Arduino
VR_LEDControl example with MQTT features
#include <Arduino.h>
#include <SPI.h>
#include <WiFiNINA.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <DSpotterSDK_MakerHL.h>
#include <LED_Control.h>

// WiFi and MQTT settings
// constants
#define WIFI_SSID         "WIFI_SSID"
#define WIFI_PASSWD       "WIFI_PASSWD"
#define MQTT_BROKER       "MQTT_BROKER"
#define MQTT_PORT         1883
#define MQTT_STATUS_TOPIC "STATIC_TOPIC"
#define MQTT_CMD_TOPIC    "CMD_TOPIC"

// persistent variables
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
JsonDocument jsonDoc;
int lastEvent = 0;

// private functions
void MQTTReconnect(void);
void MQTTCallback(char* topic, byte* payload, unsigned int length);

// The DSpotter License Data.
#include "CybLicense.h"
#define DSPOTTER_LICENSE g_lpdwLicense

// The DSpotter Keyword Model Data.
#if defined(TARGET_ARDUINO_NANO33BLE) || defined(TARGET_PORTENTA_H7) || defined(TARGET_NICLA_VISION)
// For ARDUINO_NANO33BLE and PORTENTA_H7
#include "Model_L1.h"             // The packed level one model file.
// For NANO_RP2040_CONNECT
#elif defined(TARGET_NANO_RP2040_CONNECT)
#include "Model_L0.h"             // The packed level zero model file.
#endif
#define DSPOTTER_MODEL g_lpdwModel

// Define for led return value, please refer to the info.txt
#define COMMAND_LED_GREEN  10000
#define COMMAND_LED_RED    10001
#define COMMAND_LED_BLUE   10002
#define COMMAND_LED_OFF    10003

// The VR engine object. Only can exist one, otherwise not worked.
static DSpotterSDKHL g_oDSpotterSDKHL;

void VRCallback(int nFlag, int nID, int nScore, int nSG, int nEnergy);

// initialize hardware and software components
void setup() {
  // Init LED control
  LED_Init_All();

  // Init Serial output for show debug info
  Serial.begin(9600);
  while(!Serial);
  DSpotterSDKHL::ShowDebugInfo(true);

  // Init VR engine & Audio
  if (g_oDSpotterSDKHL.Init(DSPOTTER_LICENSE, sizeof(DSPOTTER_LICENSE), DSPOTTER_MODEL, VRCallback) != DSpotterSDKHL::Success)
    return;

  // Check NINA module
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true);
  }

  // Check firmware version
  String fv = WiFi.firmwareVersion();
  if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
    Serial.print("Firmware version: ");
    Serial.println(fv);
    Serial.println("Please upgrade the firmware");
  }

  // Scan available WiFi networks
  int numSsid = WiFi.scanNetworks();
  for (int net_id = 0; net_id < numSsid; net_id++) {
    Serial.print(net_id);
    Serial.print(") ");
    Serial.print(WiFi.SSID(net_id));
    Serial.print("\tSignal: ");
    Serial.print(WiFi.RSSI(net_id));
    Serial.println(" dBm");
  }

  // connect WiFi
  do {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(WIFI_SSID);
    int status = WiFi.begin(WIFI_SSID, WIFI_PASSWD);
    Serial.print("WiFi status: ");
    Serial.println(status);
    delay(5000);
  } while (WiFi.status() != WL_CONNECTED);

  // connect MQTT
  mqttClient.setServer(MQTT_BROKER, MQTT_PORT);
  mqttClient.setCallback(MQTTCallback);
  mqttClient.connect("RP2040-Nano-Voice");
  mqttClient.subscribe(MQTT_CMD_TOPIC);
}

void loop() {
  static uint32_t prevMs = 0;
  // Reconnect to MQTT
  //MQTTReconnect(); // skip this function, because it causes a lot of lost recording frames
  // Do VR
  g_oDSpotterSDKHL.DoVR();

  // report status change via MQTT
  if (lastEvent != 0) {
    char buf[256];
    jsonDoc.clear();
    switch(lastEvent) {
      case COMMAND_LED_GREEN:
        jsonDoc["event"] = "LED_GREEN";
        break;
      case COMMAND_LED_RED:
        jsonDoc["event"] = "LED_RED";
        break;
      case COMMAND_LED_BLUE:
        jsonDoc["event"] = "LED_BLUE";
        break;
      case COMMAND_LED_OFF:
        jsonDoc["event"] = "LED_OFF";
        break;
      default:
        jsonDoc["event"] = "UNKNOWN";
        break;
    }
    serializeJson(jsonDoc, buf);
    mqttClient.publish(MQTT_STATUS_TOPIC, buf);
    lastEvent = 0;
  }

  // MQTT loop
  if ((millis() - prevMs) > 1000) {
    prevMs = millis();
    mqttClient.loop();
  }
}

// Callback function for VR engine
void VRCallback(int nFlag, int nID, int nScore, int nSG, int nEnergy) {
  if (nFlag==DSpotterSDKHL::InitSuccess) {
      //ToDo
  }
  else if (nFlag==DSpotterSDKHL::GetResult) {
      /*
      When getting an recognition result,
      the following index and scores are also return to the VRCallback function:
          nID        The result command id
          nScore     nScore is used to evaluate how good or bad the result is.
                     The higher the score, the more similar the voice and the result command are.
          nSG        nSG is the gap between the voice and non-command (Silence/Garbage) models.
                     The higher the score, the less similar the voice and non-command (Silence/Garbage) models are.
          nEnergy    nEnergy is the voice energy level.
                     The higher the score, the louder the voice.
      */
      switch(nID) {
        case COMMAND_LED_GREEN:
          LED_RGB_Green();
          break;
        case COMMAND_LED_RED:
          LED_RGB_Red();
          break;
        case COMMAND_LED_BLUE:
          LED_RGB_Blue();
          break;
        case COMMAND_LED_OFF:
          LED_RGB_Off();
          break;
        default:
          break;
      }
      lastEvent = nID;
  } else if (nFlag==DSpotterSDKHL::ChangeStage) {
      switch(nID) {
          case DSpotterSDKHL::TriggerStage:
            LED_RGB_Off();
            LED_BUILTIN_Off();
            break;
          case DSpotterSDKHL::CommandStage:
            LED_BUILTIN_On();
            break;
          default:
            break;
      }
  } else if (nFlag==DSpotterSDKHL::GetError) {
      if (nID == DSpotterSDKHL::LicenseFailed)
      {
          //Serial.print("DSpotter license failed! The serial number of your device is ");
          //Serial.println(DSpotterSDKHL::GetSerialNumber());
      }
      g_oDSpotterSDKHL.Release();
      while(1);//hang loop
  } else if (nFlag == DSpotterSDKHL::LostRecordFrame) {
      //ToDo
  }
}

// Reconnect to MQTT
void MQTTReconnect(void) {
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(WIFI_SSID);
    int status = WiFi.begin(WIFI_SSID, WIFI_PASSWD);
    Serial.print("WiFi status: ");
    Serial.println(status);
    delay(5000);
  }
  while (!mqttClient.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (mqttClient.connect("RP2040-Nano-Voice")) {
      Serial.println("connected");
      mqttClient.subscribe(MQTT_CMD_TOPIC);
    } else {
      Serial.print("failed, rc=");
      Serial.print(mqttClient.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

// Callback function for MQTT
void MQTTCallback(char* topic, byte* payload, unsigned int length) {
  char buf[256];
  memset(buf, 0, sizeof(buf));
  memcpy(buf, payload, length);
  Serial.print("Got message: ");
  Serial.println(buf);
  deserializeJson(jsonDoc, buf);
  if (jsonDoc.containsKey("command")) {
    String command = jsonDoc["command"];
    if (command == "LED_GREEN") {
      LED_RGB_Green();
    } else if (command == "LED_RED") {
      LED_RGB_Red();
    } else if (command == "LED_BLUE") {
      LED_RGB_Blue();
    } else if (command == "LED_OFF") {
      LED_RGB_Off();
    }
  }
}

Credits

Supachai Vorapojpisut
4 projects • 2 followers
Background in Electrical Engineering with past experience in firmware development for several processor platforms.

Comments