Custom tone through telnet

Hello. It might be easier just to look at my code to understand my question. I have a telnet thing set up so if it receives L M H or O it will do certain acts. I want to set it up so that if you type C it will ask for a tone in Hz then play that tone. so far it doesn twork at all. code:

#include <Console.h>

int var;
char incomingByte;

void setup() {
  Bridge.begin();
  Console.begin();

  while (!Console);
  Console.println("type H, M, L, or O to make or change tones.");

  pinMode(9, OUTPUT);
}
void loop() {
  if (Console.available() > 0) {
    incomingByte = Console.read();
    if (incomingByte == 'H') {
      tone(9, 1000);
      Console.println("Playing High tone");
    }

    if (incomingByte == 'M') {
      tone(9, 500);
      Console.println("Playing Midium tone");
    }

    if (incomingByte == 'L') {
      tone(9, 250);
      Console.println("Playing Low tone");
    }
    if (incomingByte == 'O') {
      noTone(9);
      Console.println("Stoping tone");
   }
       if (incomingByte == 'C') {
      noTone(9);
      Console.println("Type tone in Hz");
      incomingByte = 0;
      while(incomingByte != '0') {
      incomingByte = Console.read();
      if (incomingByte != '0') {
      var = Console.read();
      tone(9, var);
    }
   }
  }
 } 
}

Thanks,
Ethan

Yún?

Yep

Thread moved to the Yún section.

What exactly are you expecting to happen here? (I'm changing the indenting some to make the structure more obvious, and I'm adding line numbers for the sake of discussion)

ComputerNerd:

01      if (incomingByte == 'C')

02      {
03         noTone(9);
04         Console.println("Type tone in Hz");
05         incomingByte = 0;
06         while(incomingByte != '0')
07         {
08            incomingByte = Console.read();
09            if (incomingByte != '0')
10            {
11               var = Console.read();
12               tone(9, var);
13            }
14         }

Let's look at what it actually does:

Line 01: You're checking for a 'C' command, so far, so good.

Line 03: Stop any pending tone, makes sense.

Line 04: Prompt for additional data

Line 05: Clear out incomingByte

Line 06: Perform the next code block (lines 7 through 13) as long as incomingByte is not the ASCII character zero.

Line 08: Read a byte from the input stream, or get a NULL if no data is available.

Line 09: Is the character that was just read something other than the ASCII character zero? (Note, this is NOT checking for the binary value zero (NULL) which would indicate no character read.)

Line 11: Read another byte from the input stream, or get a NULL if no data is available, assign that byte to var.

Line 12: Use the ASCII character value as a number, and generate a tone at that frequency.

There are a bunch of problems here. The value read in line 8 is used for the test in the next line, but is not otherwise used to build your frequency. So, the first character you type in response to the prompt is not actually used. If you type a zero character on the keyboard, you'll skip lines 11 and 12, and you'll loop back to line 6, and the loop will be broken because incoming byte is now '0'. If you type any other character, the next character will be read and used as a frequency. But it will use the binary equivalent of the ASCII character: if you type '5', for example, it has an ASCII value of 0x35 or 53 decimal, so you will end up generating a 53 Hz tone. Then, the next character will be used to check for the '0' value to break the loop, and if you type anything other than '0' you'll take the next character and again generate a new tone with the corresponding value.

I'm sure this is nothing close to what you want. I assume that you want to prompt for a value, enter a series of digits until some terminator, and then convert that string of digits to a frequency value and generate a tone based on that.

You will need to change your loop to keep reading characters and place them in a buffer, until you get a terminator like a carriage return. You will need to make sure you look for NULL character values that mean no character yet available (and don't use that as the end of value terminator!)

You may have some success using readStringUntil() to read characters up until the terminator character.

Robin2 also has a nice tutorial on reading serial data: Serial Input Basics. It deals with the serial port, but since the Serial class and the Console class both derive from the Stream class, and the functions he's using to read from the serial port are the same functions available with the Console class, his examples should be directly applicable. (However, serialEvent() is NOT applicable.)

Okay, so i rewrote my code and got this:

if (incomingByte == 'C') {
noTone(9);
Console.println("Type tone in Hz");
var1 = Console.readStringUntil("E");
if (var1 != 0) {
tone(9, incomingByte);
}
}

I got the error:

invalid conversion from 'const char*' to 'char' [-fpermissive]

ComputerNerd:
Okay, so i rewrote my code and got this:

if (incomingByte == 'C') {
noTone(9);
Console.println("Type tone in Hz");
var1 = Console.readStringUntil("E");
if (var1 != 0) {
tone(9, incomingByte);
}
}

Better. You're reading a string, with a trailing 'E' and everything up until that point is returned as a String and stored in var1.

But then it goes sour. You are then testing if the string is NULL, which isn't really valid. Then, you are generating a tone using incomingByte, which is still 'C' at this point, so will end up generating a 67 Hz tone, regardless of the input.

ComputerNerd:
I got the error:

invalid conversion from 'const char*' to 'char' [-fpermissive]

What is the type of var1? I suspect you have declared it as a char, but it needs to be a String.

The return value from readStringUntil() is a character string. It needs to be assigned to a String variable. Then you need to do some checks to make sure it is a valid number (contains only digits and no other characters) and finally you need to convert it to an integer, perhaps using the String class toInt() function. Finally, you need to make sure the converted value is in a valid range, and use that converted value in the tone() function call.

Arduino: 1.6.5 (Mac OS X), Board: "Arduino Yún"

Beep_Control.ino: In function 'void loop()':
Beep_Control:86: error: invalid conversion from 'const char*' to 'char' [-fpermissive]
In file included from /Applications/Arduino.app/Contents/Java/hardware/arduino/avr/cores/arduino/HardwareSerial.h:29:0,
from /Applications/Arduino.app/Contents/Java/hardware/arduino/avr/cores/arduino/Arduino.h:224,
from /Applications/Arduino.app/Contents/Java/libraries/Bridge/src/Bridge.h:26,
from /Applications/Arduino.app/Contents/Java/libraries/Bridge/src/Console.h:22,
from Beep_Control.ino:30:
/Applications/Arduino.app/Contents/Java/hardware/arduino/avr/cores/arduino/Stream.h:94:10: error: initializing argument 1 of 'String Stream::readStringUntil(char)' [-fpermissive]
String readStringUntil(char terminator);
^
Beep_Control:88: error: cannot convert 'String' to 'unsigned int' for argument '2' to 'void tone(uint8_t, unsigned int, long unsigned int)'
invalid conversion from 'const char*' to 'char' [-fpermissive]

is the error but I declared it a String variable. I feel like i am hovering around the answer.

ComputerNerd:
is the error but I declared it a String variable. I feel like i am hovering around the answer.

Please post your complete code, and use code tags (the </> button.)

It sounds like you have problems with the return value of readStringUntil() and will the frequency parameter to tone().

The variable to hold the return value of readStringUntil() must be declared as a String. Then that String must be converted to an int before sending it to tone().

ok, i will rewrite the code and post it all here

#include <Console.h>
String var1 = "0";
int var = '0';
char incomingByte;      // a variable to read incoming Console data into

void setup() {
 Bridge.begin();   // Initialize Bridge
 Console.begin();  // Initialize Console

 // Wait for the Console port to connect
 while (!Console);
 Console.println("type H, M, L, or O to make or change tones.");

 // initialize the LED pin as an output:
 pinMode(9, OUTPUT);
 pinMode(13, OUTPUT);
}
void loop() {
 // see if there's incoming Console data:
 if (Console.available() > 0) {
   // read the oldest byte in the Console buffer:
   incomingByte = Console.read();
   // if it's a capital H (ASCII 72), turn on the LED:
   if (incomingByte == 'H') {
     tone(9, 1000);
     digitalWrite(13, 255);
     Console.println("Playing High tone");
   }
   // if it's an M turn off the LED:
   if (incomingByte == 'M') {
     tone(9, 500);
     digitalWrite(13, 127.5);
     Console.println("Playing Midium tone");
   }
   // if it's an L (ASCII 76) turn off the LED:
   if (incomingByte == 'L') {
     tone(9, 250);
     digitalWrite(13, 63.75);
     Console.println("Playing Low tone");
   }
   // if it's an L (ASCII 76) turn off the LED:
   if (incomingByte == 'O') {
     noTone(9);
     digitalWrite(13, LOW);
     Console.println("Stoping tone");
   }
   if (incomingByte == 'P') {
     incomingByte = 'Z';
     while(incomingByte != 'O') {
     incomingByte = Console.read();
     var = analogRead(0);
     tone(9, (var + 200));
     }
   }
   if (incomingByte == 'C') {
     noTone(9);
     Console.println("Type tone in Hz");
     var1 = Console.read();
     int(var1);
     if (var1 != 0) {
     tone(9, var1);
   }
 }
} 
}

ComputerNerd:
OK, var1 is a String.

   if (incomingByte == 'C') {

noTone(9);
    Console.println("Type tone in Hz");
    var1 = Console.read();
    int(var1);
    if (var1 != 0) {
    tone(9, var1);
  }
}

You're calling read(), which returns a character, but trying to assign it to a String, and you're getting an error because that isn't allowed.

Then, you're calling tone, passing in a String for the second parameter, but it expects an int, which is causing your other set of errors.

You need to read a string from the console, then convert the string to an integer, and then you can use that as an argument to tone(). You're getting your variables and types mixed up.

I played with the code, and came up with something that I think will work, although it is hardly ideal and is rather ugly, but it's all the time I can spend on it right now. It would be a lot simpler to use readStringUntil('\n') but doesn't work well with live data: it keeps reading characters until the end of line delimiter (the argument to the function) or until a one second timeout with no data. I didn't want to mess around with changing the timeout, so I came up with this ugly hack:

  if (incomingByte == 'C') {
     noTone(9);
     Console.println("Type tone in Hz");
     
     // There is probably a newline after the C command.
     // If so, read it and throw it away.
     if (Console.peek() == '\n')
       (void)Console.read();
     
     // Clear out the string, ready to build a new one
     var1 = "";
     while (true)
     {
       // Read another input character
       incomingByte = Console.read();
       
       // Is it the end of the input (newline character?) If so, stop reading
       if (incomingByte == '\n')
         break;
       
       // Is it a valid character? If so, add it to the string.
       // (read() returns -1 if no character ready)
       if (incomingByte != -1)
         var1 = var1 + incomingByte;
     }

     // Convert the string to a binary number, returns zero if invalid format
     int freq = var1.toInt();
     
     // Check for a valid string conversion. If so, generate a tone.
     if (freq > 0) {
       tone(9, freq);
   }
 }

There is lots of room for improvement, both in optimizations, and in data validation and error checking. You'll note that the frequency test uses greater than zero, where you used equals. That's because entering a frequency of 32767 gives a value of 32767, but entering 32768 or above gives a negative number (due to out of range value wrapping.) So, 32 kHz being above the limit of normal human hearing, it seemed like a reasonable limit and not worth going into long integer conversions.