Sending simple "console" commands through serial Tx

Status
Not open for further replies.

grayfx

Active member
Stupid question I know, but here goes:

Using a TEENSY 3.2 serial out (TX) to command another controller to play a track. Their documentation is minimal, so this should be easy, right??

From the other controller's manual:

The serial port operates at 19.2kbps, 8 data bits, one stop bit, and no parity.
The port provides a console-like interface that is designed for human or automated operation.
A typical terminal interaction might resemble the following:

>ls
000: my_routine1
001: my_routine2
002: my_routine3
>play 0
>status
p 0 239
>
The console is prepared to receive input when the “>” prompt is displayed.

play <track number>
This commands starts playback and requires an
argument that indicates the number of the
track to play (e.g., the first track is track zero).


SO: how do I implement a "console" command into a sketch correctly?

TIA!
 
use a state machine and a serial sniffer

I'm working on a similar project. We were given a spec for the communication protocol which turned out to be wrong. We learned this by sniffing the serial traffic both directions. I did this using the free RealTerm program and two USB to serial adapters (I have some from Belkin (RS232 level) and FTDI (TTL level). So I broke the serial connection from the existing host conroller (you don't have one in this case I guess) and the target. Plug one adapter into each. In RealTerm I listen on the host port and echo on the target port. I can see color-coded data including unprintable characters (CR and LF at a minimum) going both ways. It has been a huge time saver.

In your case you can just use one serial adapter and manually simulate the host using RealTerm, PuTTY or your favorite. Once you get manual commands working you know your program should work if it outputs the identical data. Then you can capture the actual response from the target. You can use the dual-adapter version once you get your host running.

We use simple state machines for such things. State diagrams are super useful and have a specific syntax. A great book, now old but still great, is William Fetcher's An Engineering Approach to Digital Design. I accidentally bought an extra copy of the original hardbound edition and would sell it if you are interested.

A rough idea, I can't draw a state diagram here:

State 0 - wait for ">" and probably a terminating CR or CR/LF pair
State 1 - is there a command to send? Check a a command ready flag
Yes: Serial.println("A command") or one of an array of commands println sends a CR-LF (I think, not just a LF but its been a while since I checked that), print does not
No: wait for a command ready flag to be set somewhere else in your code: a keypad or button maybe
Fall out of state 1 when command sent, clear the flag, go back to state 0.

A lot of details are missing here, of course, but that serial sniffer will help you debug.

Here is my serial event code which receives keypad events from the remote UI mentioned above. This is just a start, needs some error handling, etc. I just buffer one string input. At most they can arrive every 200 msec typically, 40 msec absolute fastest.

Code:
// input from UI
char inputString1[32];	
char bufferString1[32];
uint8_t chars1_read = 0;
boolean string1Complete = false;
uint8_t index1 = 0;

void serialEvent1 ()
{
	char inChar1 = 0;		// byte of data from UI keypad
	
	while (Serial1.available() && !string1Complete)
	{
	inChar1 = Serial1.read();
	
	chars1_read++;	// count chars actually read in
		
		if (inChar1 > 0x1F)	// is it printable?
		{
			inputString1[index1++] = inChar1;
		}
		else
		{
			if (0x0D == inChar1) 
			{
			}
			if (0x0A == inChar1) 
			{
				// message is complete
				inputString1[index1] = 0x0;	// null terminate it
				string1Complete = true;
			}
		}
	}
}

In loop() there is code like this:

Code:
	if (string1Complete)
	{
		// we got a message
		// clear flags for new message
		// but we should copy it first or it could be overwritten while we are reading it.
		for (int i =0; i<32; i++)
		{
			bufferString1[i] = inputString1[i];
			if (0 == bufferString1[i]) break; 	// nul term, stop copying
		}
		index1 = 0;
		string1Complete = false;	// clear flag
                // use the string copy to make a UI menu decision
        }
 
Very helpful! I have never used RealTerm. Worked great, and all the suppliers specs were correct re. baud rate, parity, commands:

7-13-2016 10-37-31 AM.jpg

Now, how do I push that into an arduino sketch?

STUCK!

TIA!
 
Do something like the code I posted yesterday: wait for the ">" character, send a command (I don't know how you will decide to do that: an array of strings, input from a keypad, ?). I use SerialEvent but you can also just spin in loop() and check for Serial.available(). Try googling for other examples, this is a common kind of application: machine to machine.

Basically your code duplicates what you did by hand in RealTerm. Serial.println("play(001)") will do just what you did. Start with that.

BTW I'm using RT 3.0.0.29 and just grabbed the latest 3.0.0.30, I had some issues with earlier versions, but can't remember what they were.
 
I really appreciate the help! Still a bit stuck, though. I have a feeling I'm screwing up syntax...dunno.

View attachment conductor_teensy.ino

This is the code I have so far, very simple:

//SERIAL_8N1 protocol spec'd by external controller, 8 data bits,
//one stop bit, and no parity
// set this to the hardware serial port you wish to use
#define HWSERIAL Serial1
int led = 13;
int t1;
int play = t1;


void setup() {
Serial.begin(19200);
HWSERIAL.begin(19200, SERIAL_8N1);
pinMode(led, OUTPUT);
}

void loop() {

//this is for me to see if the teensy has taken in the new program, LED timing changed per rev manually
int incomingByte;
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(200); // wait for a second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second

//communication to external controller
Serial.println("play(001)");
}
 
Should the last line in loop() be:


Code:
HWSERIAL.println("play(001)");

To directly include code just wrap it like this:
code.PNG
 
I appreciate the help but that didn't do the trick. I'm not sure if we're on the same page, you did see the initial program did have have a very similar last line in the loop, Serial.println("play(001)");

I swapped it with your suggestion HWSERIAL.println("play(001)");
No changes in response.

But my blinking LED sure looks cool, grrrrrr :mad:

Does the hardware interface (the TTL to RS232 converter) seem like a logical area to focus on? For example, finding a USB 2.0 to RS232 converter back in the day was not fun. It took about 8 different models to find one working device. Are TTL to RS232 converters that touchy?

Or is it just a code issue?

Again, any help is appreciated!
 
I understood you wanted to send out Serial1 for the UART to the RS232 converter. That last line as Serial will print out the USB port, sending out that port will never get to Serial1==HWSERIAL was what I saw.

It you get a decent FTDI adapter - I used one from SparkFun at ~$14 and had good results - which ends up in USB it works well for what it does. A similar converter to RS232 I would hope would work as well if you get from a trusted source. Indeed I may on the wrong page - so disclaimer - YMMV.
 
some more possible, untested sample code

So your music controller wants RS232 levels?

Put your USB to serial adapter on a PC with RealTerm and see if the output from Teensy is correct.

Here's how I start a serial port, in setup()
Code:
        Serial1.begin(9600);			// start Serial1
	while((!Serial1) && (millis()<10000));		// wait until serial1 port is open or timeout
	Serial.print("Serial1 ready at ");    // send debug message out usb serial port
	Serial.println(millis());

I don't have any of the specs for your music controller, but in loop if you have something like

Code:
char inChar1;
char inputString1[32];	
uint8_t index1 = 0;

// I have not tried to compile or run this since I don't have the music hardware
// This waits for '>', asks to play 001, wait 5 sec, repeat forever
// repeats response from music player

void loop(void) 
{
	while (Serial1.available())
		{
		inChar1 = Serial1.read();
			
			if ('>' == inChar1)	// prompt char
			{
				inputString1[index1++] = inChar1;   // save in a string for later use, maybe no need for this
				Serial.println("got >");
			}
			else if (0x0D == inChar1)  // CR
			{
				Serial.println("got carriage return 0x0D")
				// discard it
			}
			else if (0x0A == inChar1)  // LF
			{
				Serial.println("got line feed (newline) 0x0A")
				// discard it
				inputString1[index1] = 0x0;	// null terminate string after ending CR-LF removed
			}
			else
			{
				// got some other chars than ><cr><lf> from music
				if (inChar1 > 0x1F)	// is it printable? if so add it to string
				{
					[index1++] = inChar1;
				}
				else
				{
					// non printable, show hex value, don't add to string
					Serial.print("got 0x");
					Serial.println(inChar1, HEX);    // print hex value, works even if unprintable char
				}
			}
		}

		// print the string to debug usb
		Serial.println(inputString1);
		Serial.println("asking to play 001");
		Serial1.println("play(001)");

		delay(5000);	// 5 seconds to start playing and respond to serial

}	// end loop

I like to output LOTs of debug info so if something unexpected happens I have a clue about it
 
defragster I appreciate the help. If anybody's confused it's me ;)

So: I'm confused. You mention getting a FTDI adapter to go to USB. I need to pull TTL serial commands from TEESNY to a (really annoying) external controller that is RS232 and female DB9 (pin order straight-through) port. The only time I intend to use USB is to program the TEENSY.

Here's where the code is now (also rev2 .ino, attached):

#include <SoftwareSerial.h>

//SERIAL_8N1 protocol spec'd by external controller, 8 data bits,
//one stop bit, and no parity
// set this to the hardware serial port you wish to use
#define HWSERIAL Serial1
int led = 13;
//int t1;
//int play = t1;


void setup() {
Serial.begin(19200);
HWSERIAL.begin(19200);
HWSERIAL.begin(SERIAL_8N1);
pinMode(led, OUTPUT);
}

void loop() {

//this is for me to see if the teensy has taken in the new program, LED timing changed per rev manually
int incomingByte;
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(200); // wait for a second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(200); // wait for a second

//communication to external controller
//Serial.println("play(001)");
HWSERIAL.println("play(001)");
}



I'm not sure I'm using" HWSERIAL.begin(SERIAL_8N1);" correctly in the setup...

TIA!
 

Attachments

  • conductor_teensy_rev2.ino
    936 bytes · Views: 113
The ref to FTDI was to ENSURE confusion. Actually as noted it works well - I was hoping to say if it can convert to USB then there is hope for an RS232 adapter that is more 1::1 in function, but you need to find a reputable source - I didn't check - but Adafruit or SparkFun should offer quality if they carry one. When you buy ebay or other even an advertised device matching your needs may not function - in my case I got a cheap FTDI that used an unsupported chipset for my OS USB.

Do you have a second Teensy? If you plugged a second Teensy - with no adapter needed - you could confirm and USB print what it receives from a connected Serial1 port to your development device. Then you could confirm and taylor exactly what you are sending as something that should work when a suitable RS232 converter is properly attached. This would also allow the second Teensy to emulate the end device by sending out the '>' prompt character (per bboyes post #4)

NOTE: wiring is important the TX from each end hits the RX on the other and a GND must be safely common between them - this applies to the converter too - since I don't see a connection pic I had to add this.

Final tip - when you have multiple Teensy's to view USB debug - or even program :: USE TYQT.
 
Ah, thanks!

WRT the last rev of the code do you see any issues? Just gotta get that play command out.

I have ordered 2 different TTL to RS232 converters just in case.

From what I have put TEENSYs through in the past I am confident that if it is a hardware issue our little buddy is not the problem. $10 converters seem a more likely issue (if the code I have is correct).

Troubleshooting:
The external controller does also have pushbuttons to execute playback. Those work. The rest of that controller checks out.

Thanks for the help!
 
Sorry - I can't look at code that isn't formatted like indicated in post #7 ... :)

Why is softserial there? See this I think the problem was your double .begin on Serial. Compiled but not tested.

See this https://www.arduino.cc/en/Serial/Begin - SERIAL_8N1 is the default

Code:
//SERIAL_8N1 protocol spec'd by external controller, 8 data bits,
//one stop bit, and no parity
// set this to the hardware serial port you wish to use
//int t1;
//int play = t1;

#define qBlink() (digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN) ))


void setup() {
  Serial.begin(38400);
  pinMode(LED_BUILTIN, OUTPUT);
  while (!Serial && (millis ()  <= 8000)) {
    delay(120); qBlink(); // Attention - waiting for USB to connect
    delay(40);  qBlink();
  }

  Serial.println("Hello World");
  Serial1.begin(19200, SERIAL_8N1);
}

void loop() {

  //this is for me to see if the teensy has taken in the new program, LED timing changed per rev manually
  int incomingByte;
  qBlink();
  delay(400); // wait for a second

  //communication to external controller
  Serial.println("play(001)");
  Serial1.println("play(001)");
}
 
Last edited:
This code looks like it will repeatedly tell the player to start playing every 400 ms.

Code:
void loop() {
//this is for me to see if the teensy has taken in the new program, LED timing changed per rev manually
int incomingByte;
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(200); // wait for a second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(200); // wait for a second

//communication to external controller
//Serial.println("play(001)");
HWSERIAL.println("play(001)");
}

In your first message, you said "The console is prepared to receive input when the “>” prompt is displayed". So you probably need to use HWSERIAL.available() to check if the player has sent any data, and HWSERIAL.read() to get the bytes it sends, and even more code to check if any of those bytes are the ">" character indicating it's ready to receive your command. Like bboyes tried to help you in #10.

Does the documentation say what the player will do if you send it commands when it's not ready? What if you resend the same command every 0.4 seconds?
 
Everybody, thank you for your help and patience.

...still ill communicado though...

Here's where we are:
- external controller checks out on receiving commands through Realterm and it turns out the command syntax is pretty flexible (for example play, play0, play 0, play<0>, and play"0" all do the same thing
- I have 3 different TTL to RS232 converters, rotating them through each program iteration to see if that is the real problem. I'm pretty sure they are OK, I'm the real issue here.


I can send out TTL command strings no problem:
7-23-2016 2-56-14 PM.jpg


The real problem - and I know you have tried to help solve this with pulling serial info back from controller - is I need a simple command prompt.

Basically, I need a command that initiates the external controller's command prompt to let the DA** command in!

I know I'm slow here, but can somebody shed the most simplest light on this?

Here's the idiot code I have so far, revised.View attachment sketch_jul23a.ino
 
Status
Not open for further replies.
Back
Top