Author Topic: How to send serial commands over different software serial outputs on Arduino?  (Read 6276 times)

0 Members and 1 Guest are viewing this topic.

Offline abdullahsebaTopic starter

  • Frequent Contributor
  • **
  • Posts: 335
  • Country: gb
Hi
How 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.
This is my right hand this is my wrong hand
 

Offline ade

  • Supporter
  • ****
  • Posts: 231
  • Country: ca
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.
« Last Edit: March 18, 2016, 08:55:30 pm by ade »
 

Offline michaeliv

  • Frequent Contributor
  • **
  • Posts: 260
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_String
if(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.
 

Offline abdullahsebaTopic starter

  • Frequent Contributor
  • **
  • Posts: 335
  • Country: gb
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_String
if(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.
After hours of faffing about :scared: I 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 SLOW |O unless it can be optimized I will have to use a much faster microcontroller.
Code: [Select]
#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, TX
    mySerial.begin(250000);
    mySerial.println(Data);
  }
}
This is my right hand this is my wrong hand
 

Offline michaeliv

  • Frequent Contributor
  • **
  • Posts: 260
There's one big downfall for my application, its SLOW |O unless it can be optimized I will have to use a much faster microcontroller.
How slow is it now and how fast does it need to be ?
 

Offline ade

  • Supporter
  • ****
  • Posts: 231
  • Country: ca
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.
 

Offline abdullahsebaTopic starter

  • Frequent Contributor
  • **
  • Posts: 335
  • Country: gb
There's one big downfall for my application, its SLOW |O unless it can be optimized I will have to use a much faster microcontroller.
How slow is it now and how fast does it need to be ?
1 second delay and it needs to be instantaneous as normal serial communications are.
This is my right hand this is my wrong hand
 

Offline abdullahsebaTopic starter

  • Frequent Contributor
  • **
  • Posts: 335
  • Country: gb
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.
You 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: [Select]
#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;
  }
  }
  }
This is my right hand this is my wrong hand
 

Offline electr_peter

  • Supporter
  • ****
  • Posts: 1302
  • Country: lt
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.
 

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4078
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
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!
 

Offline Psi

  • Super Contributor
  • ***
  • Posts: 9946
  • Country: nz
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!

+1 for arduino library and blocking funtions being the issue

Simply 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.
« Last Edit: March 20, 2016, 12:49:50 am by Psi »
Greek letter 'Psi' (not Pounds per Square Inch)
 

Offline botcrusher

  • Regular Contributor
  • *
  • Posts: 192
  • Country: ca
Quote from: abdullahseba

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.


Hold up, if you have that many pins, you must be using the arduino mega, which is run on an ATmega2560. Which, last i checked, has 4 USARTs

If 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.
 

Offline ade

  • Supporter
  • ****
  • Posts: 231
  • Country: ca
Quote
+1 for arduino library and blocking funtions being the issue
Even 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.
 

Offline abdullahsebaTopic starter

  • Frequent Contributor
  • **
  • Posts: 335
  • Country: gb
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.
That's an idea, I ill have to do that or use a powerful microcontroller.
This is my right hand this is my wrong hand
 

Offline abdullahsebaTopic starter

  • Frequent Contributor
  • **
  • Posts: 335
  • Country: gb
Quote from: abdullahseba

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.


Hold up, if you have that many pins, you must be using the arduino mega, which is run on an ATmega2560. Which, last i checked, has 4 USARTs

If 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.
It has 4 hardware USART, that's the idea of software serial which allows you to use most digital outputs as serial ports.
This is my right hand this is my wrong hand
 

Offline sleemanj

  • Super Contributor
  • ***
  • Posts: 3024
  • Country: nz
  • Professional tightwad.
    • The electronics hobby components I sell.
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: [Select]
#include <SoftwareSerial.h>

const byte numChannels = 3;
SoftwareSerial channels[numChannels] = { {2,3}, {4,5}, {6,7} };

void setup() {
  // put your setup code here, to run once:
  for(byte x = 0; x < numChannels; x++)
  {
    channels[x].begin(9600);
  }
}

void loop() {
  // put your main code here, to run repeatedly:
  char buffer[255];
  byte channelNumber = 0, i = 0;
 
  if(Serial.available())
  {
    Serial.readBytesUntil('\n', buffer, sizeof(buffer));
    if(buffer[0] >= '0' && buffer[0] <= '9')
    {
      // Convert the number at the start of the buffer to an integer
      channelNumber = atoi(buffer);

      // Scoot forward in buffer until we find the space
      for(byte i = 0; i < sizeof(buffer); i++)
      {
        if(buffer[i] == ' ')
        {
          i++;
          break;
        }
      }

      // i is now the index of the space (or we ran off the end
      if(i < sizeof(buffer))
      {
        channels[channelNumber].print(&buffer[i]);
      }
    }
  }
}
~~~
EEVBlog Members - get yourself 10% discount off all my electronic components for sale just use the Buy Direct links and use Coupon Code "eevblog" during checkout.  Shipping from New Zealand, international orders welcome :-)
 

Offline abdullahsebaTopic starter

  • Frequent Contributor
  • **
  • Posts: 335
  • Country: gb
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: [Select]
#include <SoftwareSerial.h>

const byte numChannels = 3;
SoftwareSerial channels[numChannels] = { {2,3}, {4,5}, {6,7} };

void setup() {
  // put your setup code here, to run once:
  for(byte x = 0; x < numChannels; x++)
  {
    channels[x].begin(9600);
  }
}

void loop() {
  // put your main code here, to run repeatedly:
  char buffer[255];
  byte channelNumber = 0, i = 0;
 
  if(Serial.available())
  {
    Serial.readBytesUntil('\n', buffer, sizeof(buffer));
    if(buffer[0] >= '0' && buffer[0] <= '9')
    {
      // Convert the number at the start of the buffer to an integer
      channelNumber = atoi(buffer);

      // Scoot forward in buffer until we find the space
      for(byte i = 0; i < sizeof(buffer); i++)
      {
        if(buffer[i] == ' ')
        {
          i++;
          break;
        }
      }

      // i is now the index of the space (or we ran off the end
      if(i < sizeof(buffer))
      {
        channels[channelNumber].print(&buffer[i]);
      }
    }
  }
}
Thanks but it didn't work I'm not much good at programming to understand it properly.
what does this line do exactly? SoftwareSerial channels[numChannels]= { {2,3}, {4,5}, {6,7} };
This is my right hand this is my wrong hand
 

Offline michaeliv

  • Frequent Contributor
  • **
  • Posts: 260
what does this line do exactly? SoftwareSerial channels[numChannels]= { {2,3}, {4,5}, {6,7} };
It 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
 

Offline boffin

  • Supporter
  • ****
  • Posts: 1027
  • Country: ca
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
 

Offline rstofer

  • Super Contributor
  • ***
  • Posts: 9890
  • Country: us
Skip atoi() and just mask off everything except bits 0..3

  channelNumber = buffer[0] & 0x0f;

you already know that the byte is a valid digit

The situation that hasn't been handled is when channelNumber > numChannels
Instead of testing <= '9', test against the character representing numChannels - '3' in this case
Maybe set up chMax = numChannels + '0';
Then the test would look like

  if(buffer[0] >= '0' && buffer[0] <= chMax)

As an aside, I might try something like
 
  ch = buffer[0]
  if(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.

 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf