[SOLVED] EMIC 2 Butchers Text (One letter at a time!)

Hello! I have an Emic2 text to speech module. If you don't know, it's purpose is to take in data and convert it to, well, text! (It's pretty cool!)

I have used it happily with strings and chars and whatnot, converting it all to speech!

However, I'm trying to do some input from a char array Serial buffer thing. The idea is you enter "hello" on the Serial Monitor. It processes that into a char array (like a Serial buffer) and then speaks the end result.

But here's my problem. When I enter in data, instead of saying 'hello,' it speaks one letter at a time. ('h...e...l...l...o')

Why is it that the char array makes the text seperated???

Here's my code: (I'm terrible at Serial input buffers, but I'm learning! Slowly... :frowning: )

void program2() {
  Serial2.println(">Running Program 2");
  Serial2.println("All data lines closed during this operation");
  Serial2.println("===TEXT TO SPEECH===");
  Serial2.println("Enter '9' to Exit");

  while (1 < 2) {
    while (Serial2.available() > 0) {
      char inData[20]; // Allocate some space for the string
      char inChar = -1; // Where to store the character read
      byte index = 0; // Index into array; where to store the character
      String procData;

      if (index < 19) // One less than the size of the array
      {
        inChar = Serial2.read(); // Read a character
        inData[index] = inChar; // Store it
        index++; // Increment where to write next
        inData[index] = '\0'; // Null terminate the string
        procData = inData;
      }
     emic.speak(procData);
    }
  }
Serial2.println(">Program Complete");
}

Thanks!
SQ

Delta_G:
So the problem is that the serial data comes in slowly.

Then couldn't I just crank up the baud rate of Serial2?

Delta_G:
You need to keep collecting characters into your buffer until you get to the end of the string and then and only then call your text to speech engine.

But what if the user's input length varies? What if they enter 'hi' instead of 'hello?' I want ANYTHING they enter to be converted, within reason. With varying length how can I tell when I hit the 'end of the string'.

Oh wait... You mean use '\n' to detect when the string should end. Well, the thing that is connected to Serial2 is a bluetooth module, and my phone is sent to transmit 'No line ending.' Should I change that? (I'd have to modify some other code elsewhere in my sketch to compensate for the addition of a newline character)

Tadaa!

Some new code. Am I on the right track?

void program2() {
  Serial2.println(">Running Program 2");
  Serial2.println("All data lines closed during this operation");
  Serial2.println("===TEXT TO SPEECH===");
  Serial2.println("Enter '9' to Exit");

  while (1 < 2) {
    while (Serial2.available() > 0) {
      char inData[20]; // Allocate some space for the string
      char inChar = -1; // Where to store the character read
      byte index = 0; // Index into array; where to store the character
      String procData;
      char endMarker = '\n';
      char Exit = '9';
      if (inChar == Exit) {
        Serial2.println(">Program Complete");
        return;
      }

      else if (inChar != endMarker) // One less than the size of the array
      {
        inChar = Serial2.read(); // Read a character
        inData[index] = inChar; // Store it
        index++; // Increment where to write next
        inData[index] = '\0'; // Null terminate the string
        procData = inData;
      } else {
        inData[index] = '\0'; // terminate the string
        index = 0;
        emic.speak(procData);
      }
    }
  }
}

SQ

I moved the variables outside of the While loop so they won't get 'redeclared' (and thus, destroyed) each time through. Or at least, that's the idea, right?

void program2() {
  Serial2.println(">Running Program 2");
  Serial2.println("All data lines closed during this operation");
  Serial2.println("===TEXT TO SPEECH===");
  Serial2.println("Enter '9' to Exit");
  char inData[20]; // Allocate some space for the string
  char inChar = -1; // Where to store the character read
  byte index = 0; // Index into array; where to store the character
  String procData;
  char endMarker = '\n';
  char Exit = '9';

  while (1 < 2) {
    while (Serial2.available() > 0) {
      if (inChar == Exit) {
        Serial2.println(">Program Complete");
        return;
      }

      else if (inChar != endMarker) // One less than the size of the array
      {
        inChar = Serial2.read(); // Read a character
        inData[index] = inChar; // Store it
        index++; // Increment where to write next
        inData[index] = '\0'; // Null terminate the string
        procData = inData;
      } else {
        inData[index] = '\0'; // terminate the string
        index = 0;
        emic.speak(procData);
      }
    }
  }
}

SQ

Delta_G:
else if (inChar != endMarker) // One less than the size of the array

Replaced that line of code, with this one:

else if (inChar != endMarker && inChar < 20)

Wait a minute. I just looked at Robin's tutorial. In his tutorial, he used '<19' rather than '<20.' If the array is 20 characters long, wouldn't anything LESS than 20 work? Why did he use less than 19?

In fact, if the array is 20 characters long, it could even be "<= 20" (less-than or equal-to twenty)

Delta_G:
You can't use <= 20 because inData[20] doesn't exist. Remember, you're counting 20 elements but you're starting at 0, so the twentieth is 19.

The reason @Robin2 used <19 was to make sure to leave slot 19 in the array open. Remember, you gotta leave room to null terminate that string. A 5 character string like Hello takes an array 6 characters long to store.

That clears everything up! Thanks so much!
I'm going to test this code (using '<19') tomorrow, and I'll let you know of any issues! But I doubt there will be anything!

Thank you so much, I have struggled alot with Serial input. Thanks for clearing it up!

SQ

Hmmm... Didn't quite work! :o

Here is my current code. When I enter "hi!" the device just says "h" over and over again, looping.
Notice how "endMarker = '!'" For now I'm just manually putting '!' at the end of every transmission.

void program2() {
  Serial2.println(">Running Program 2");
  Serial2.println("All data lines closed during this operation");
  Serial2.println("===TEXT TO SPEECH===");
  Serial2.println("Enter '*' to Exit");
  char inData[20]; // Allocate some space for the string
  char inChar = -1; // Where to store the character read
  byte index = 0; // Index into array; where to store the character
  String procData;
  char endMarker = '!';
  char Exit = '*';

  while (1 < 2) {
    while (Serial2.available() > 0) {
      if (inChar == Exit) {
        Serial2.println(">Program Complete");
        return;
      }

      else if (inChar != endMarker && inChar < 19) // One less than the size of the array
      {
        inChar = Serial2.read(); // Read a character
        inData[index] = inChar; // Store it
        index++; // Increment where to write next
        inData[index] = '\0'; // Null terminate the string
        procData = inData;
      } else {
        inData[index] = '\0'; // terminate the string
        index = 0;
        emic.speak(procData);
      }
    }
  }
}

SQ

My new code. Should I comment out "inData[index] = '\0'; // Null terminate the string" the first time? (In the If statement, not the Else) In the If statement, doesn't it just terminate the string early???

This code just makes it say "h" once, when I enter "hi!"

void program2() {
  Serial2.println(">Running Program 2");
  Serial2.println("All data lines closed during this operation");
  Serial2.println("===TEXT TO SPEECH===");
  Serial2.println("Enter '*' to Exit");
  char inData[20]; // Allocate some space for the string
  char inChar = -1; // Where to store the character read
  byte index = 0; // Index into array; where to store the character
  char endMarker = '!';
  char Exit = '*';

  while (1 < 2) {
    while (Serial2.available() > 0) {
      if (inChar == Exit) {
        Serial2.println(">Program Complete");
        return;
      }

      else if (inChar != endMarker && inChar < 19) // One less than the size of the array
      {
        inChar = Serial2.read(); // Read a character
        inData[index] = inChar; // Store it
        index++; // Increment where to write next
        inData[index] = '\0'; // Null terminate the string
      } else {
        inData[index] = '\0'; // terminate the string
        index = 0;
        emic.speak(inData[index]);
      }
    }
  }
}

Delta_G:

 else {

inData[index] = '\0'; // terminate the string
        index = 0;
        emic.speak(inData[index]);
      }




And given the fact that you set index back to zero right before you called speak(), which letter should it be?

Will speak() take a pointer to a char array like print() will? Why not try just passing inData, without the array index, so it is just a pointer to the beginning of the char array.

Like this?

emic.speak(inData);

Delta_G:
Yup, just like you would do with print if you wanted to print the whole string.

If that doesn't work then we need to look at that library and figure out what it expects in that call. I'm just guessing that it will take a char array.

It didn't work. Still just "h"
Wait, so, what should I do?

Ok, I'm looking at it now.

The '.h' file.

/**
 * Name: EMIC2
 * Author: Nick Lamprianidis (lampnick67@yahoo.com)
 * Version: 1.0
 * Description: A library for interfacing the EMIC2 Text-to-Speech module
 * License: Copyright (c) 2013 Nick Lamprianidis 
 *          This library is licensed under the MIT license
 *          http://www.opensource.org/licenses/mit-license.php
 * Source: https://github.com/pAIgn10/EMIC2
 *
 * Filename: EMIC2.h
 * File description: Definitions and methods for the EMIC2 library
 */

#ifndef EMIC2_h
#define EMIC2_h

#include <Arduino.h>
#include <SoftwareSerial.h>
#include <SD.h>
#include <string.h>

// #define VERBOSE true // Use this if you want verbose output of the operations executed

#define SD_C 1

// A class for interfacing the Emic 2 module
class EMIC2
{
public:
	EMIC2();
	~EMIC2();
	void begin(uint8_t rx_pin, uint8_t tx_pin);
	void begin(uint8_t rx_pin, uint8_t tx_pin, uint8_t cs_pin);
	void speak(char *msg);
	void speak(String msg);
	void speak(char num);
	void speak(unsigned char num);
	void speak(int num);
	void speak(unsigned int num);
	void speak(long num);
	void speak(unsigned long num);
	void speak(double num);
	void speak(char *filename, uint8_t sd);
	void speak(String filename, uint8_t sd);
	void speakDemo(uint8_t num);
	void sendCmd(char *cmd);
	void ready();
	EMIC2& operator~();
	EMIC2& operator!();
	EMIC2& operator++();
	EMIC2& operator--();
	EMIC2& operator+=(uint8_t adjust_volume);
	EMIC2& operator-=(uint8_t adjust_volume);
	EMIC2& operator>>(uint16_t adjust_rate);
	EMIC2& operator<<(uint16_t adjust_rate);
	void setVoice(uint8_t voice);
	uint8_t getVoice();
	void setVolume(int8_t volume);
	void resetVolume();
	int8_t getVolume();
	void setRate(uint16_t rate);
	void resetRate();
	uint16_t getRate();
	void setLanguage(uint8_t language);
	uint8_t getLanguage();
	void setParser(uint8_t parser);
	uint8_t getParser();
	void setDefaultSettings();
	// With only 64Bytes available on the receive buffer of the various Arduino boards
	// UARTs and the SoftwareSerial library, current settings and version information
	// can't be obtainted in their entirety. So the following functions are being
	// implemented only for reference and they are not going to be utilized in any way
	void getCurrentSettings();
	void getVInfo();

private:
	SoftwareSerial *_emic2_io;
	uint8_t _paused;
	uint8_t _voice;
	int8_t _volume;
	uint16_t _rate;
	uint8_t _language;
	uint8_t _parser;
	uint8_t _sd;
};

#endif  // EMIC2

From looking at it, it seems as if it accepts single chars... but not char arrays.
If there is any way to convert the array to a string, however...

Delta_G:
You null terminated it, so the array is a string. Did you mean to convert it to a String?

Yes, sorry.

Delta_G:

void speak(char *msg);

There's the one that takes a char array. Go look for that one in the .cpp and see what it does with it.

So your saying that "char *msg" is the accept for a char array? I think I get it what your saying... I will take a look at the CPP file, and post it here as well.

Delta_G:
Do one more thing too. Put a Serial print right before the speak() command and see if it prints it out right. If it just prints one character then we're barking up the wrong tree.

Good idea.

As far as converting to a String, someone mention the "str()" function here:

Serial2.println(inData);
        emic.speak(inData);

I did that. The serial monitor just showed 'h'

We are barking up the wrong tree, it looks like... :frowning:

Delta_G:
Shouldn't you read the character BEFORE you start checking to see what it is?

Sorry, but I don't really get what your saying here

I put "Serial2.println(inData);" at the end of the last Else block. And at the end of the "else if" (the first one)

stupid-questions:
Sorry, but I don't really get what your saying here

I put "Serial2.println(inData);" at the end of the last Else block. And at the end of the "else if" (the first one)

It just returns 'h' twice. What were you saying about the Serial2.avalible line?

Delta_G:
Not available, but read. You've got the read() in the wrong place.

Sorry I meant to post #33 before you wrote #34. I get it now, give me a sec Im gonna test it right now... :slight_smile:

My new code. "inChar = Serial2.read(); // Read a character" is right under "while (Serial2.available() > 0)"
Is this what you meant?

while (Serial2.available() > 0) {
      inChar = Serial2.read(); // Read a character
      if (inChar == Exit) {
        Serial2.println(">Program Complete");
        return;
      }

      else if (inChar != endMarker && inChar < 19) // One less than the size of the array
      {
        inData[index] = inChar; // Store it
        index++; // Increment where to write next
        inData[index] = '\0'; // Null terminate the string
        Serial2.println(inData);
      } else {
        inData[index] = '\0'; // terminate the string
        index = 0;
        Serial2.println(inData);
        emic.speak(inData);
      }
    }