Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 17 of 17

Thread: Sending simple "console" commands through serial Tx

  1. #1

    Sending simple "console" commands through serial Tx

    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!

  2. #2
    Senior Member
    Join Date
    Nov 2012
    Location
    Salt Lake City, UT, USA
    Posts
    269

    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
            }

  3. #3
    Very helpful! I have never used RealTerm. Worked great, and all the suppliers specs were correct re. baud rate, parity, commands:

    Click image for larger version. 

Name:	7-13-2016 10-37-31 AM.jpg 
Views:	90 
Size:	129.5 KB 
ID:	7615

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

    STUCK!

    TIA!

  4. #4
    Senior Member
    Join Date
    Nov 2012
    Location
    Salt Lake City, UT, USA
    Posts
    269
    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.

  5. #5
    I really appreciate the help! Still a bit stuck, though. I have a feeling I'm screwing up syntax...dunno.

    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)");
    }

  6. #6
    before anybody asks, YES the TEENSY's wire for gnd and TX, the receiving TTL is gnd and RX.

    Using a small TTL to RS232 DB9 converter, this one: https://www.amazon.com/gp/product/B0...?ie=UTF8&psc=1

    TIA
    Last edited by grayfx; 07-16-2016 at 03:00 AM.

  7. #7
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    8,827
    Should the last line in loop() be:


    Code:
    HWSERIAL.println("play(001)");
    To directly include code just wrap it like this:
    Name:  code.PNG
Views: 346
Size:  1.7 KB

  8. #8
    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

    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!

  9. #9
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    8,827
    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.

  10. #10
    Senior Member
    Join Date
    Nov 2012
    Location
    Salt Lake City, UT, USA
    Posts
    269

    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

  11. #11
    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!
    Attached Files Attached Files

  12. #12
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    8,827
    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.

  13. #13
    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!

  14. #14
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    8,827
    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 by defragster; 07-17-2016 at 12:24 AM. Reason: pinmode moved

  15. #15
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,173
    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?

  16. #16
    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:
    Click image for larger version. 

Name:	7-23-2016 2-56-14 PM.jpg 
Views:	50 
Size:	41.0 KB 
ID:	7716


    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.sketch_jul23a.ino

  17. #17
    Anybody ever mess around with Console.h?

    https://www.arduino.cc/en/Tutorial/ConsoleRead

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •