Multiple SoftwareSerial ports alternative

I have two arduino pro micro (atmega32u4), and i want to send a char over Serial from the first arduino and receive it from the other. If this char doesnt come (plug is disconnected) after an interval i want to know it.

With one SoftwareSerial it works perfect…with two i cannot make it work…(ultimatelely i would be happy with 3-4 serials). I tried “listen”, but it confuses the program execution:
so my code is:

#include "MIDIUSB.h"
#include <SoftwareSerial.h>
#define myRxPin 14
SoftwareSerial myCable1(myRxPin, 99); //rx,tx
#define myRxPin2 15
SoftwareSerial myCable2(myRxPin2, 98); //rx,tx

bool newDataRecorded[3] = {false};
unsigned long prevMillis[3] = {0};
const long interval = 50;

void controlChange(byte channel, byte control, byte value) {
  //send some midi
}

void pressTheButtons(byte control1, byte control2) {
  controlChange(0, control1, 127);
  delay(10);
  controlChange(0, control2, 127);
  delay(10);
}

void checkCable(SoftwareSerial& thisCable, int whichJack, byte control1, byte control2) {
  thisCable.listen();
  if (thisCable.available() > 0) {
    char myID = thisCable.read();
    if (myID == '!' ) {
      prevMillis[whichJack] = millis();
      if (newDataRecorded[whichJack] == false) {
        Serial.println("!, on");
        pressTheButtons(120, control2);
        newDataRecorded[whichJack] = true;
      }
    }
  }
  if ((millis() - prevMillis[whichJack]) > interval) {
    if (newDataRecorded[whichJack] == true) {
      Serial.println("off");
      pressTheButtons(control1, control2);
      newDataRecorded[whichJack] = false;
    }
  }
}


void setup() {
  Serial.begin(115200);
  myCable1.begin(4800);
  myCable2.begin(4800);
}

void loop(){
 checkCable(myCable1, 1, 100, 101);

//this works fine, until i try to add the second SoftwareSerial with the following line
//checkCable(myCable2, 2, 110, 150);
}

Any suggestion?

uzer123:
I have two arduino pro micro (atmega32u4), and i want to send a char over Serial from the first arduino and receive it from the other. If this char doesnt come (plug is disconnected) after an interval i want to know it.

With one SoftwareSerial it works perfect…with two i cannot make it work…(ultimatelely i would be happy with 3-4 serials). I tried “listen”, but it confuses the program execution:
so my code is:

#include "MIDIUSB.h"

#include <SoftwareSerial.h>
#define myRxPin 14
SoftwareSerial myCable1(myRxPin, 99); //rx,tx
#define myRxPin2 15
SoftwareSerial myCable2(myRxPin2, 98); //rx,tx

bool newDataRecorded[3] = {false};
unsigned long prevMillis[3] = {0};
const long interval = 50;

void controlChange(byte channel, byte control, byte value) {
  //send some midi
}

void pressTheButtons(byte control1, byte control2) {
  controlChange(0, control1, 127);
  delay(10);
  controlChange(0, control2, 127);
  delay(10);
}

void checkCable(SoftwareSerial& thisCable, int whichJack, byte control1, byte control2) {
  thisCable.listen();
  if (thisCable.available() > 0) {
    char myID = thisCable.read();
    if (myID == ‘!’ ) {
      prevMillis[whichJack] = millis();
      if (newDataRecorded[whichJack] == false) {
        Serial.println("!, on");
        pressTheButtons(120, control2);
        newDataRecorded[whichJack] = true;
      }
    }
  }
  if ((millis() - prevMillis[whichJack]) > interval) {
    if (newDataRecorded[whichJack] == true) {
      Serial.println(“off”);
      pressTheButtons(control1, control2);
      newDataRecorded[whichJack] = false;
    }
  }
}

void setup() {
  Serial.begin(115200);
  myCable1.begin(4800);
  myCable2.begin(4800);
}

void loop(){
checkCable(myCable1, 1, 100, 101);

//this works fine, until i try to add the second SoftwareSerial with the following line
//checkCable(myCable2, 2, 110, 150);
}




Any suggestion?

unlike with multiple hardwareSerial like on a MEGA, you should ‘switch’ between the softwareSerial Channels. ie

myCable1.begin(4800);

//do whatever you need to do with 1

myCable1.end();

myCable2.begin(4800);

//do whatever you need to do with 2

myCable2.end();

and so on…

On a Pro Micro, Serial1 (pins 0 and 1) is a hardware serial port (and not the same as the Serial that you use for communication with the PC). So you only need one software serial.

Are you aware that the ProMicro has a spare HardwareSerial port (Serial1 on pins 0 and 1) which will work much better than any SoftwareSerial ?

It is not practical to listen on more than one SoftwareSerial port at any one time. If you want to use two or more you MUST be able to let each sending device know when you are ready to listen to it so that it only transmits when you are ready.

The Arduino Mega has 3 spare HardwareSerial ports.

...R

MUST be able to let each sending device know when you are ready to listen to it so that it only transmits when you are ready.

Something like a 'request attention pin" per port, then respond wit a ready signal. And then do the exchange. swSerial doesn't store in the buffer automatically like hwSerial does so if in setup() there is

 sourceSerial.begin(BAUD);
  targetSerial.begin(BAUD);

then within a function there could be

  targetSerial.listen();
    targetSerial.print("READY");
    delay(100);

Afterwhich you can use targetSerial.available to receive the response in a blocking way.

Thanks for all the answers.. Some more info: 1) i dont want to use MEGA because i want to have USB midi, which mega doesnt have.

2) use of pins 0 and 1 is interesting, because i can have one soft and one hard serial working at the same time(i guess)

3) can i use both 0 and 1 pins as Rx?

4) the whole project is like this: I will have 2 identical (actually much more, but 2 is the beginning)Arduinos that both will send Serial in some ports (always), check some other ports for incoming Serial (but maybe there is no connection), and read some other inputs(pots etc). I want to use only a mono cable. So i have Gnd and Serial transmission on this cable. The example with one serial works fine, but i guess it will be hard to sync all the serials.. is there any other approach (although i invested a lot of time in this)

uzer123:

  1. i dont want to use MEGA because i want to have USB midi, which mega doesnt have.

Then use a Teensy 3.5 or Teensy 3.6

2) use of pins 0 and 1 is interesting, because i can have one soft and one hard serial working at the same time(i guess)

pins 0 & 1 belong to the hw Serial1 if you are using it, so once you initialize that port (.begin()) don't use them for anything else. You can have the midi-usb, the Serial1 working at the same time, and on top of that you can use 1 swSerial at one time.

the whole project is like this: I will have 2 identical (actually much more, but 2 is the beginning)Arduinos that both will send Serial in some ports (always),

If more than 2 is the plan, you may as well try 2x swSerial so you know what you will run into, but as has been explained, you can have only 1 port active at one time. What you could do though is send a high signal for as longer than the length of receiving 1 byte, preceding the transmission, on the incoming sw data line of the micro, and use that as the 'attention signal', poll the pin between every byte received, and start transmission there after.

I want to use only a mono cable. So i have Gnd and Serial transmission on this cable.

That means you can not send an 'acknowledge-ready' signal back, so you may lose data because there can be more transmissions happening at the same time. Receiving on more than one swSerial at a time can not be done you need to have control of the incoming data stream. As an extension of the earlier idea, you could use the data line like this. (for sake of explanation i will call all other arduino's Uno0, Uno1, Uno2 etc.) All databus pins on the Uno's are in input mode. the Micro goes logic HIGH on databus Uno0, for 1 or 2 ms and then waits for a response on the same pin now used as a swSerial-RX for some time (a few ms should do) If data is received or not, it then goes on to do the same on the databus of Uno1 and so on and so forth. That can work. You will need to put at least a resistor (1K) in line on the data bus, which is anyway a good plan, prevents burning out pins as a result of shorting wires. It will be a complex system to program, but can be done.

Power_Broker: Then use a Teensy 3.5 or Teensy 3.6

Yes, i know that, but its 10 times more expensive. And i want to make many of these systems (10-20 for sure) so its not a good option here.

Deva_Rishi: pins 0 & 1 belong to the hw Serial1 if you are using it, so once you initialize that port (.begin()) don't use them for anything else.

Yes, this is what i said..but it can work only with 2 input system..

That means you can not send an 'acknowledge-ready' signal back, so you may lose data because there can be more transmissions happening at the same time. Receiving on more than one swSerial at a time can not be done you need to have control of the incoming data stream. As an extension of the earlier idea, you could use the data line like this. (for sake of explanation i will call all other arduino's Uno0, Uno1, Uno2 etc.) All databus pins on the Uno's are in input mode. the Micro goes logic HIGH on databus Uno0, for 1 or 2 ms and then waits for a response on the same pin now used as a swSerial-RX for some time (a few ms should do) If data is received or not, it then goes on to do the same on the databus of Uno1 and so on and so forth. That can work. You will need to put at least a resistor (1K) in line on the data bus, which is anyway a good plan, prevents burning out pins as a result of shorting wires. It will be a complex system to program, but can be done.

This "acknowledge" thing looks very nice, but it might be very complicated after a bit. But i am afraid it will be very slow at the end.. and this is why:

Lets say i have Micro1 and Micro2. Micro1 has 2 outputs, and 3 inputs (these inputs send HIGH and wait for Serial). Micro2 has the same inputs/outputs.

So, Micro1 input 1, sends HIGH wait for Micro2 output (1 or 2) to receive this HIGH and send the Serial byte. Micro1 input 2, sends HIGH wait for Micro2 output (1 or 2) to receive this HIGH and send the Serial byte. Micro1 input 3, sends HIGH wait for Micro2 output (1 or 2) to receive this HIGH and send the Serial byte.

Micro1 output 1, waits for HIGH in order to send the Serial byte. Micro1, output 2, waits for HIGH in order to send the Serial byte.

The exact same steps must be done to Micro2...but in order to synchronize and give enough time to pool all these ports it will take a lot of time i guess...


I have started thinking about another approach that looks promising: every output sends PWM..as far as i know if you do something like analogWrite(myOutputPin, dutyCycle) it stays like this even if arduino do 1000 things in between...

On the input of the other arduino, i can use interrupts.. if i meet a HIGH on the input pin, i record the millis.. Another interrupt: if it goes LOW, i record another millis.. Now i compare the two records and have the time of one pulse, which can be identical for my pwm...(i guess it is known how many milliseconds is one pulse in every dutyCycle..

So, if i know the dutyCycle..i will know from which pin it comes from (and i can have 1- 255 different duty Cycles which is extremely good for my project)..

Do you think that can work?

Do you think that can work?

No, PWM frequency is generally to high to read accurately, and to read a duty cycle even more so. It's a thought but a viable option.

This "acknowledge" thing looks very nice, but it might be very complicated after a bit. But i am afraid it will be very slow at the end.. and this is why:

It may be slow if you send only one byte at a time, though keep in mind that sending a byte at 9600 bps takes about a millisecond. All i am saying is that using swSerial, you need to do some kind of 'handshake' which should be initiated by the master (or initiated by the master upon request of the slave) If you want to use only a single data-bus and common GND (eg 2 wires) then letting the master poll for data would be the easiest, though you could still let the slave make a request for attention first. Either way the waste of time is in the 'acknowledge' and how you set that up.

Lets say i have Micro1 and Micro2. Micro1 has 2 outputs, and 3 inputs (these inputs send HIGH and wait for Serial). Micro2 has the same inputs/outputs.

Hey if are you connecting micros over a full duplex connection ? you will need more than those 2 wires ! Anyway swSerial is anyway quite slow and slows down you program (a lot of processing time goes into both receiving and transmitting, much more than a hwSerial port) but checking if data is available should not take much more than 1ms, even more so if the polling is done and the sender responds to it straight away. then add a ms for every byte transferred (and if more than one byte is send, one for the terminator) If you want to create a 'mesh' then you should consider using ESP's and do a wifi-mesh instead, that way you can have more nodes without issue, and you can keep your micro as a midi-usb. btw i think you can do a midi usb on a Mega, but with a different bootloader, not an easy project, but neither is this. For the sake of overview i suggest you make a diagram of the connections that you want to have (full amount, i mean if you want 10 nodes on your 'Master-Micro' draw them and their connections, and post it here. Please use names like master and slave.

thanks for your time.. before i abandon pwm idea..and start giving you the diagrams..

No, PWM frequency is generally to high to read accurately, and to read a duty cycle even more so. It's a thought but a viable option.

hm...i found this really interesting.. https://github.com/bhagman/SoftPWM

with this i solve two problems 1) pwm in every pin..2) slow pwm..so maybe easily readable... and i guess it will run all the time using timers..(so no need to wait or handsake)

uzer123: 4) the whole project is like this: I will have 2 identical (actually much more, but 2 is the beginning)Arduinos that both will send Serial in some ports (always), check some other ports for incoming Serial (but maybe there is no connection), and read some other inputs(pots etc). I want to use only a mono cable. So i have Gnd and Serial transmission on this cable. The example with one serial works fine, but i guess it will be hard to sync all the serials.. is there any other approach (although i invested a lot of time in this)

Assuming you want to do your communication on a single three conductor cable using the serial tx/rx pins, then you will need communication command/control, and possible electrical modifications. Otherwise you will be trying to follow every conversation in a room of talking people. The first thing is you will need to have an understanding of is how the RS232 TTL type of communication electrically works. With nthis understood, you will need to develop an electrical setup for this type of communication to work when there are multipal boards connected together. Once this is finalized, then develop the command and control code to provide for the communication. Just saying.

Maybe use RS-485 instead of RS-232?

with this i solve two problems 1) pwm in every pin..2) slow pwm..so maybe easily readable... and i guess it will run all the time using timers..(so no need to wait or handsake)

Good luck but it will be even slower and less accurate.