Voice Recognition with hardware serial

Hi,
I'm using an ATTiny 804 board with the ultimate goal of driving an led strip or two after a voice command. My voice module linked the Elechouse git repo, but the driver uses software serial, which basically doesn't fares well with led strips and effects.
So I programmed the VR module via an arduino uno R3 (on which, using the original driver, everything works fine), and I now am trying to make the tiny recognize the voice, but something goes wrong: this is my driver implementation (pardon the subs VR->DR, but I tried everything)

#include "VRClient.h"
#include <string.h>
#include <avr/delay.h>


DR* DR::instance;

/** temp data buffer */
uint8_t dr_buf[32];
uint8_t hextab[17]="0123456789ABCDEF";

/**
	@brief DR class constructor.
	@param receivePin --> software serial RX
		   transmitPin --> software serial TX
*/
DR::DR(uint8_t receivePin, uint8_t transmitPin) 
{
	instance = this;
	//Serial.begin(38400);
	//Serial.begin(9600);
}
int DR::begin(){
	Serial.begin(9600);
	while(!Serial)
		_delay_ms(100);
	send_pkt(FRAME_CMD_SET_BR,0,0,0);
	return 0;
}
/**
	@brief DR class constructor.
	@param buf --> return data .
			 buf[0]  -->  Group mode(FF: None Group, 0x8n: User, 0x0n:System
             buf[1]  -->  number of record which is recognized. 
             buf[2]  -->  Recognizer index(position) value of the recognized record.
             buf[3]  -->  Signature length
             buf[4]~buf[n] --> Signature
		   timeout --> wait time for receiving packet.
	@retval length of valid data in buf. 0 means no data received.
*/
int DR :: recognize(uint8_t *buf, int timeout)
{
	int ret, i;
	ret = receive_pkt(dr_buf, timeout);
	if(dr_buf[2] != FRAME_CMD_DR){
		return -1;
	}
	if(ret > 0){
		for(i = 0; i < (dr_buf[1] - 3); i++){
			buf[i] = dr_buf[4+i];
		} 
		return i;
	}
	
	return 0;
}

/**
    @brief Load records to recognizer.
    @param records --> record data buffer pointer.
           len --> number of records.
           buf --> pointer of return value buffer, optional.
             buf[0]    -->  number of records which are load successfully.
             buf[2i+1]  -->  record number
             buf[2i+2]  -->  record load status.
                00 --> Loaded 
                FC --> Record already in recognizer
                FD --> Recognizer full
                FE --> Record untrained
                FF --> Value out of range"
             (i = 0 ~ '(retval-1)/2' )
	@retval '>0' --> length of valid data in buf. 
            0 --> success, buf=0, and no data returned.
            '<0' --> failed.
*/
int DR :: load(uint8_t *records, uint8_t len, uint8_t *buf)
{
	uint8_t ret;
	send_pkt(FRAME_CMD_LOAD, records, len);
	ret = receive_pkt(dr_buf);
	if(ret<=0){
		return -1;
	}
	if(dr_buf[2] != FRAME_CMD_LOAD){
		return -1;
	}
	if(buf != 0){
		memcpy(buf, dr_buf+3, dr_buf[1]-2);
		return dr_buf[1]-2;
	}
	return 0;
}

/**
    @brief Load one record to recognizer.
    @param record --> record value.
           buf --> pointer of return value buffer, optional.
             buf[0]    -->  number of records which are load successfully.
             buf[2i+1]  -->  record number
             buf[2i+2]  -->  record load status.
                00 --> Loaded 
                FC --> Record already in recognizer
                FD --> Recognizer full
                FE --> Record untrained
                FF --> Value out of range"
             (i = 0 ~ '(retval-1)/2' )
	@retval '>0' --> length of valid data in buf. 
            0 --> success, buf=0, and no data returned.
            '<0' --> failed.
*/
int DR :: load(uint8_t record, uint8_t *buf)
{
	uint8_t ret;
	send_pkt(FRAME_CMD_LOAD, &record, 1);
	ret = receive_pkt(dr_buf);
	if(ret<=0){
		return -1;
	}
	if(dr_buf[2] != FRAME_CMD_LOAD){
		return -1;
	}
	if(buf != 0){
		memcpy(buf, dr_buf+3, dr_buf[1]-2);
		return dr_buf[1]-2;
	}
	return 0;
}

/**
    @brief clear recognizer.
    @retval  0 --> success
            -1 --> failed
*/
int DR :: clear()
{	
	int len;
	send_pkt(FRAME_CMD_CLEAR, 0, 0);
	len = receive_pkt(dr_buf);
	if(len<=0){
		return -1;
	}

	if(dr_buf[2] != FRAME_CMD_CLEAR){
		return -1;
	}
	//DBGLN("DR Module Cleared");
	return 0;
}

/**
    @brief send data packet in Voice Recognition module protocol format.
    @param cmd --> command 
           subcmd --> subcommand
           buf --> data area
           len --> length of buf
*/
void DR :: send_pkt(uint8_t cmd, uint8_t subcmd, uint8_t *buf, uint8_t len)
{
	while(Serial.available()){
		Serial.read();// replace flush();
	}
	Serial.write(FRAME_HEAD);
	Serial.write(len+3);
	Serial.write(cmd);
	Serial.write(subcmd);
	Serial.write(buf, len);
	Serial.write(FRAME_END);
	}

	/**
	    @brief send data packet in Voice Recognition module protocol format.
	    @param cmd --> command 
		   buf --> data area
		   len --> length of buf
	*/
	void DR :: send_pkt(uint8_t cmd, uint8_t *buf, uint8_t len)
	{
		while(Serial.available()){
			Serial.read();// replace flush();
		}
		Serial.write(FRAME_HEAD);
		Serial.write(len+2);
		Serial.write(cmd);
		Serial.write(buf, len);
		Serial.write(FRAME_END);
	}

	/**
	    @brief send data packet in Voice Recognition module protocol format.
	    @param buf --> data area
		   len --> length of buf
	*/
	void DR :: send_pkt(uint8_t *buf, uint8_t len)
	{
		while(Serial.available()){
		Serial.read();// replace flush();
	}
	Serial.write(FRAME_HEAD);
	Serial.write(len+1);
	Serial.write(buf, len);
	Serial.write(FRAME_END);
}

/**
    @brief receive a valid data packet in Voice Recognition module protocol format.
    @param buf --> return value buffer.
           timeout --> time of reveiving
    @retval '>0' --> success, packet lenght(length of all data in buf)
            '<0' --> failed
*/
int DR :: receive_pkt(uint8_t *buf, uint16_t timeout)
{
	int ret;
	ret = receive(buf,2, timeout);
	if(ret != 2){
		
		return -1;
	}
	if(buf[0] != FRAME_HEAD){
		return -2;
	}
	if(buf[1] < 2){
		return -3;
	}
	//ret = rece2(buf+2, buf[1], timeout);
	if(buf[buf[1]+1] != FRAME_END){
		return -4;
	}
	
//	DBGBUF(buf, buf[1]+2);
	
	return buf[1]+2;
}

/**
    @brief receive data .
    @param buf --> return value buffer.
           len --> length expect to receive.
           timeout --> time of reveiving
    @retval number of received bytes, 0 means no data received.
*/
int DR::rece2(uint8_t* buf, int len, uint16_t to){
return Serial.readBytes(buf,len);
}
int DR::receive(uint8_t *buf, int len, uint16_t timeout)
{
  int read_bytes = 0;
  int ret;
  unsigned long start_millis;
  
  while (read_bytes < len) {
    start_millis = millis();
    do {
      ret = Serial.read();
      if (ret >= 0) {
        break;
     }
    } while( (millis()- start_millis ) < timeout);
    
    if (ret < 0) {
      return read_bytes;
    }
    buf[read_bytes] = (char)ret;
    read_bytes++;
  }
  
  return read_bytes;
}

and here is my sketch:

#include <VRClient.h>
#include <Adafruit_NeoPixel.h>
#include <avr/delay.h>

// NOT NEEDED BUT EASY SWITCH
#define PIN_TX PIN_PB1
#define PIN_RX PIN_PB0

// test LED
#define T PIN_PA4

// commands
#define stopC (0)
#define bluC (1)
#define rossoC (2)
#define arancioC (4)


// Region for future LED strip
struct Colore {
  uint8_t r;
  uint8_t g;
  uint8_t b;
  Colore(uint8_t rosso, uint8_t verde, uint8_t blu) {
    r = rosso;
    g = verde;
    b = blu;
  }
  Colore() {}
};

const Colore blu_1 = Colore(0, 100, 255);
const Colore blu_2 = Colore(0, 0, 255);
const Colore blu_3 = Colore(0, 0, 200);
const Colore rosso_1 = Colore(255, 0, 0);
const Colore rosso_2 = Colore(200, 0, 0);
const Colore rosso_3 = Colore(200, 0, 100);
const Colore a_1 = Colore(255, 150, 0);
const Colore a_2 = Colore(255, 100, 0);
const Colore a_3 = Colore(255, 50, 0);
const Colore nero = Colore(0, 0, 0);

struct Effetto {
  Colore colori[2];
   void set_colori( Colore f, Colore o) {
    colori[0] = f;
    colori[1] = o;
  }
};

volatile Effetto spento;
volatile Effetto blu;
volatile Effetto rosso;
volatile Effetto giallo;
volatile Effetto* Effetti[] = { &blu, &rosso, &giallo,&spento };


volatile Effetto* effetto;
volatile Effetto* prev;
// END LED 

DR mic(PIN_TX, PIN_RX);
uint8_t buf[64];

void setup() {

pinMode(T,OUTPUT);
digitalWrite(T,LOW);

// set colours
  spento.set_colori(nero, nero);
  blu.set_colori(blu_1, blu_3);
  rosso.set_colori(rosso_1, rosso_2);
  giallo.set_colori(a_1, a_2);
  effetto = &spento;
  prev=&spento;

  mic.begin();
_delay_ms(500);

  mic.clear();
  mic.load((uint8_t) 0);
  mic.load((uint8_t)2);
  mic.load((uint8_t)1);
  mic.load((uint8_t)4);
}

void loop() {
  //Your code here:
  int ret;
  ret = 0;

  ret = mic.recognize(buf, 50);

  if (ret >= 0) {  
    switch (buf[1]) {
      case stopC:
        effetto = &spento;
        break;
      case rossoC:
        effetto = &rosso;
        break;
      case 0x01:
        effetto = &blu;
        break;
      case arancioC:
      default:
        effetto = &giallo;
        break;
    }
  }

if(prev!=effetto)
  {
 
  dbg();
  }
_delay_ms(500);


void dbg(){
  digitalWrite(T,HIGH);
  _delay_ms(345);
  digitalWrite(T,LOW);
  chg=false;
}

The fact is that I see serial activity and the voice command gets recognized, as you can see by the captured frame:

The frame is correct but somehow something is wrong and isn't read by the tiny (the T led doesn't blink). With the same commands everything seems ok with the software serial. I think there must be something I am missing, can you please help me? At this point I don't know what to do.

Thanks

Is the clock stable and at frequency in the ATTiny 804 board?

Hi I use the internal clock, I'm currently breadboarding with a soic-dip adapter to experiment with the tiny. 16MHz internal option selected

Check the timing, the RC oscillator may be off enough to cause these problems. I have never had good luck with RC oscillators for communications.

thanks for you reply. I'm sorry but I don't know how to check that… the only thing I can say is that my oscilloscope can decode at 9600 bauds both channels. What you see in the picture above is a particular instance of the VR module transmitting to the mcu, but the mcu never responds to that (it does some init stuff tho).
can you please explain how to check the frequencies? I fanything else is needed, eg the captures of the mcu transmitting, just ask please.
thanks

Write some test code that will generate a pulse, then measure how stable the pulse is. You can also repeatedly send the same value serially and measure your timing that way. I like sending 0xC3 that I get 11000011, easy to spot or the converse 00111100 will also work. But you need to do the measurements with the scope not the data analyzer section.

ok I measured and the single bits are fairly consistent at around 110us of width, both received and sent.

EDIT: what I'm also seeing is that all of the commands are sent and echoed back correctly. And the "voice recognized" command is also correct, but it is just not parsed by the mcu

That should eliminate all of the external hardware problems. This also indicates the problem is in the software serial portion of the code as you initially indicated. The next thing I would try is to echo each byte you receive from the voice module. If that is correct then try doing it with a keyboard entry. The code looks good but I did not go through it very well. Also write some code to test the LED.

Your reply made my day knowing it is working.

Ok so, down in the receive pkt method I had commented out the second part of the buffer. Also I had a bad track on my breadboard.
All in all it is solved now.
Thanks

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.