How to send serial commands over different software serial outputs on Arduino?  - Page 1
Forum section: electronics / beginners

author: abdullahseba
author_rank: Frequent Contributor
timestamp: March 18, 2016, 08:07:11 pm
content: HiHow can I send strings over the USB interface then route them to different software serial outputs according to whats inside the string?For example:Arduino receives: "O2-dothis" Arduino sends "dothis" out of output pin 2.Arduino receives: "O3-dothis2" Arduino sends "dothis2" out of output pin 3.Thanks in advance.

author: ade
author_rank: Unknown
timestamp: March 18, 2016, 08:52:36 pm
content: Using if statements, or switch statements, or more complex selection logic based on your requirements. But the exact pinouts will depend on the board that you have.Note that generally serial ports require two pins: one for transmit (TX) and another for receive (RX). The limitation with software ports is that you can only receive on one port at a time... not sure if you're expecting replies from the ports or not.Aside from the software ports you can also use regular (hardware) serial port. So your first serial port might be on pins 0 and 1. Your second serial port (depending on your board) might be on pins 18 and 19, etc. Using hardware ports allows you to receive from multiple serial ports in addition to your usb port.

author: michaeliv
author_rank: Frequent Contributor
timestamp: March 18, 2016, 10:25:30 pm
content: There is no magical way to automatically do this, you have to write the code to do it.Algo would look like :string = USB_Serial.Read_Stringif(string starts with "O2")Serial2.Write(string - first 2 chars)else if(string starts with "O3")Serial3.Write(string - first 2 chars)If you want more suggestions, it would be helpful if you post your code.If you care about performance you can process the individual chars instead of reading strings.

author: abdullahseba
author_rank: Frequent Contributor
timestamp: March 19, 2016, 03:09:31 pm
content: Quote from: michaeliv on March 18, 2016, 10:25:30 pmAfter hours of faffing aboutI finally worked something out.Simply send the pin number followed by the string eg: (7 CAT) will send CAT over pin 7 or you can say: (31CAT) which will send CAT over pin 31.There's one big downfall for my application, its SLOWunless it can be optimized I will have to use a much faster microcontroller.Code:#include <SoftwareSerial.h>String incomingByte = "";void setup() {Serial.begin(250000);}void loop() {// send data only when you receive data:if (Serial.available() > 0) {// read the incoming byte:incomingByte = Serial.readString();String Data = incomingByte;Data.remove(0, 2);String TX = incomingByte;TX.remove(2);SoftwareSerial mySerial(10, TX.toInt()); // RX, TXmySerial.begin(250000);mySerial.println(Data);}}

author: michaeliv
author_rank: Frequent Contributor
timestamp: March 19, 2016, 03:53:42 pm
content: Quote from: abdullahseba on March 19, 2016, 03:09:31 pmHow slow is it now and how fast does it need to be ?

author: ade
author_rank: Unknown
timestamp: March 19, 2016, 04:01:35 pm
content: Without profiling the program, it's probably slow because you are initializing the software serial port, setting its speed, etc., each time for every command inside the loop. If you can pre-setup the ports beforehand it will likely run much faster.

author: abdullahseba
author_rank: Frequent Contributor
timestamp: March 19, 2016, 09:43:36 pm
content: Quote from: michaeliv on March 19, 2016, 03:53:42 pm1 second delay and it needs to be instantaneous as normal serial communications are.

author: abdullahseba
author_rank: Frequent Contributor
timestamp: March 19, 2016, 09:49:19 pm
content: Quote from: ade on March 19, 2016, 04:01:35 pmYou cant pre initializing the software serial as they need to be reinitialized every time the first number changes.I don't think that's the the problem anyhow, as I tried what you said with the switch case but it still has the same problem with speed as well as been less flexible.Code:#include <SoftwareSerial.h>SoftwareSerial one(13, 4);SoftwareSerial two(13, 5);SoftwareSerial three(13, 6);SoftwareSerial four(13, 7);SoftwareSerial five(13, 8);SoftwareSerial six(13, 9);void setup() {Serial.begin(9600);one.begin(9600);two.begin(9600);three.begin(9600);four.begin(9600);five.begin(9600);six.begin(9600);}void loop() {if (Serial.available() > 0) {String incomingByte = Serial.readString();String Data = incomingByte;Data.remove(0, 2);String TX = incomingByte;TX.remove(2);switch (TX.toInt()) {case 4:one.println(Data);break;case 5:two.println(Data);break;case 6:three.println(Data);break;case 7:four.println(Data);break;case 8:five.println(Data);break;case 9:six.println(Data);break;}}}

author: electr_peter
author_rank: Unknown
timestamp: March 19, 2016, 10:04:38 pm
content: Consider port switching via hardware means instead. If only output (TX) is required, that would relatively easy with transistors or switch IC's. Software would only control which output control line is active, but would use single serial port on MCU.

author: Jeroen3
author_rank: Super Contributor
timestamp: March 19, 2016, 11:01:43 pm
content: Isn't everything in Arduino blocking? That would be the root cause of it being slow.Get some circular buffers and interrupts routines in the thing!

author: Psi
author_rank: Super Contributor
timestamp: March 20, 2016, 12:47:45 am
content: Quote from: Jeroen3 on March 19, 2016, 11:01:43 pm+1 for arduino library and blocking funtions being the issueSimply taking a string from UART RX int and pumping it out various software serial outputs based on contents should be trivial.You would need a timer running at your software baudrate so you can clock out the data bit by bit inside the timer interrupt.

author: botcrusher
author_rank: Regular Contributor
timestamp: March 20, 2016, 12:56:04 am
content: Quote from: abdullahsebaHold up, if you have that many pins, you must be using the arduino mega, which is run on an ATmega2560. Which, last i checked, has4USARTsIf you need more ports than that, and need better performance, i believe that there are a few assembly based libraries (that you can use via c) that can really squeeze all the performance out of this.Something similar to shiftOutFast (but for serial) might milk the performance you need.

author: ade
author_rank: Unknown
timestamp: March 20, 2016, 01:50:15 am
content: QuoteEven with blocking functions there should not be a 1 second latency between the serial read and the beginning of the write, when there's only a single input stream.I've never used software serial but it seems that the overhead is huge. Either that or the String functions are terribly inefficient -- maybe worth just using byte arrays. But without some profiling it's hard to say where the code is spending most of its time.

author: abdullahseba
author_rank: Frequent Contributor
timestamp: March 20, 2016, 10:19:47 am
content: Quote from: electr_peter on March 19, 2016, 10:04:38 pmThat's an idea, I ill have to do that or use a powerful microcontroller.

author: abdullahseba
author_rank: Frequent Contributor
timestamp: March 20, 2016, 10:24:10 am
content: Quote from: botcrusher on March 20, 2016, 12:56:04 amIt has 4 hardware USART, that's the idea of software serial which allows you to use most digital outputs as serial ports.

author: sleemanj
author_rank: Super Contributor
timestamp: March 20, 2016, 11:33:02 am
content: Lose the "String", use an array for your SoftwareSerial objects.Untested except it compiles.It would be better to use strtol than atoi and the manual seeking afterwards, but the double pointer use in strtol is confusing.Code:#include <SoftwareSerial.h>const byte numChannels = 3;SoftwareSerial channels = { {2,3}, {4,5}, {6,7} };void setup() {// put your setup code here, to run once:for(byte x = 0; x < numChannels; x++){channels.begin(9600);}}void loop() {// put your main code here, to run repeatedly:char buffer;byte channelNumber = 0, i = 0;if(Serial.available()){Serial.readBytesUntil('\n', buffer, sizeof(buffer));if(buffer >= '0' && buffer <= '9'){// Convert the number at the start of the buffer to an integerchannelNumber = atoi(buffer);// Scoot forward in buffer until we find the spacefor(byte i = 0; i < sizeof(buffer); i++){if(buffer == ' '){i++;break;}}// i is now the index of the space (or we ran off the endif(i < sizeof(buffer)){channels.print(&buffer);}}}}

author: abdullahseba
author_rank: Frequent Contributor
timestamp: March 20, 2016, 03:46:49 pm
content: Quote from: sleemanj on March 20, 2016, 11:33:02 amThanks but it didn't work I'm not much good at programming to understand it properly.what does this line do exactly? SoftwareSerial channels= { {2,3}, {4,5}, {6,7} };

author: michaeliv
author_rank: Frequent Contributor
timestamp: March 20, 2016, 05:40:28 pm
content: Quote from: abdullahseba on March 20, 2016, 03:46:49 pmIt creates an array of 3 SoftwareSerial's with the specified rx(2,4,6) and tx(3,5,7) pins.http://stackoverflow.com/questions/9656576/how-can-i-initialize-an-array-of-objects-whose-constructor-require-two-or-more-a

author: boffin
author_rank: Unknown
timestamp: April 16, 2016, 04:40:06 am
content: I have a patched software serial which allows TX only implementations (or RX only).https://github.com/snafu-ca/serial-gauge/tree/master/libraries/SoftwareSerial

author: rstofer
author_rank: Super Contributor
timestamp: April 16, 2016, 05:30:27 pm
content: Skip atoi() and just mask off everything except bits 0..3channelNumber = buffer & 0x0f;you already know that the byte is a valid digitThe situation that hasn't been handled is when channelNumber > numChannelsInstead of testing <= '9', test against the character representing numChannels - '3' in this caseMaybe set up chMax = numChannels + '0';Then the test would look likeif(buffer >= '0' && buffer <= chMax)As an aside, I might try something likech = bufferif(ch >= '0' && ch <= chMax)to avoid having the code do the indexing twice. You would expect GCC to remove the redundant calculation but, unless you want to look at the assembly listing, you don't know that! A minor issue... Especially when compared to software serial.