Multiply Software Serial

Hey guys,

I was looking to read from Multiply software serials at a time. I have gotten it to work with one software serial. I can read all the info coming from the software serial. But once i add a second software serial it fails to read any of the ports. i need to read from 6 different software serials. Could anyone point me in the right direction?

Reading from software serial 1 means you cannot read from the other five as the Arduino has no background threads to do so.

You could consider a MEGA which has 4 hardware serial (which work automatically) and do 2 software serials but then still the 2 would interfere with each other (depending on their load of course).

The way to solve this is to rewrite software serial based upon pin interrupts, which would allow you to read in parallel. Not trivial but doable. The good news is there exist already (at least one) good pin change interrupt libraries.

Would you be able to point me towards this library

yes but google is faster

You can have multiple software serial instances, you just can only communicate with one at a time. Any data that arrives on port 2 while port 1 is being used will be lost. See https://www.arduino.cc/en/Reference/SoftwareSerialListen. If you need to be able to have all ports open at once, SoftwareSerial isn't your answer, but a Mega or something else with more hardware ports is.

What baud rates? What else is the processor going to be doing at the same time? What processor?

It's possible to receive six simultaneous serial inputs using software methods but there are limitations on the baud rates and on what else the processor can be doing at the same time. And you'd probably have to write that code yourself.

The AltSoftSerial documentation describes how it can be used in conjunction with SoftwareSerial and HardwareSerial to receive on three channels simultaneously, with caveats.

I've used AltSoftSerial and gSoftSerial (my own library) to receive two 9600 baud channels simultaneously. Perhaps that would work on a Mega.

Another approach would be to use a Mega and two SPI UARTs.

The buad Rate on all my boards is 9600. Unfortunately for this project i have to use an UNO I'm unusable to use the mega. I was looking at AltSoftSerial But the pins that it send out on are predetermines and i have to read from a max of 6 other arduinos at a time. Im currently looking into Pin interrupts and that might be my best way around my problem.

What are you going to do with the data as it is streaming in?

Im going to be saving to an array so that I'm able to pass it along the system

How will you pass it to the system?

Im going to set up a method that write to the next Arduino in the system.

A method? Ha ha. Okay. Good luck.

You must check both falling and rising signals.
In the ISR you collect timestamps until you have received one byte for that pin.

TO send 1 byte you have a start bit and 8 bits and a stop bit = 10 bits in total.
The baud rate = 9600 bits per second.
This means that 10 bits takes approx 1/960 second ~~ 1040 micros.

The start bit gives the first IRQ => log the start time in micros
read timestamps into an array until timestamp is approx 1040 usec after starttime.

volatile uint16_t timesA[10];
volatile uint8_t idxA = 0;
volatile uint32_t startTimeA;
volatile bool newByte = true;

void pin_1_isr()
{
  uint32_t time = micros();
  if (newbyte)
  {
    newByte = false;
    startTimeA = time;
    return;
  }
  // found the stop bit?
  if (time - startTimeA > 1040) // 1040 might need a tweak
  {
    newByte = true;
    byte b = convert(timesA);   // to be written by you
    // put byte b in a ringbuffer
    memset(timesA, 10, 0); // clear the array
    idxA = 0;
    return;
  }
  timesA[idxA++] = time - startTimeA;
}

The convert function gets an array of timestamps,
the length can be different as the serial protocol does not change between 2 same bits

example you want to send the byte pattern 0x3B = 0011 1011

that becomes with start and stop bit

1 0011 1011 1

as 1 bit takes about 100 usec the timestamps will be

100 300 600 700 1000 0 0 0 0 0

then you need to take the deltas (adding the startTImeA)
and divide by 100 to get bits

100-0 => 1
300 - 100 => 2
600 - 300 => 3
700 - 600 => 1
1000 - 700 => 3

that is a runlength representation of the byte incl stop and start
(note that 1+2+3+1+3 == 10)

as you know these bit runlengths are alternating you can state
1x High = 1
2 x LOW = 00
3 x HIGH = 111
1x LOW = 0
3x HIGH = 111

gives the pattern 1 00 111 0 111

remove the start and stop bit gives 00 111 0 11
make 2 groups of 4 bytes and we have our byte back 0011 1011 = 0x38 (what was sent)

in short not complex

your turn to start coding..

This is great going to start cracking away at it now thanks a lot for the help my friend

Rob, the problem with only using pin change interrupts is that you might not get a stop bit if the last data bit is logic 1. The transition your code will try to interpret as a stop bit will actually be the start bit of the next character... if there is one. So you need to have some sort of watchdog timer (not literally the WDT) that checks for that case.

If the incoming data is ASCII then the last data bit is always logic 0 and a stop bit transition is guaranteed. That's the case I wrote my code for (intended use was with a GPS). Here's one version of the ISR from that code. It's specific to an Uno.

void rxISR()
{
  uint8_t t0, d, rxBits, index;

  t0 = TCNT0;			// time of data transition (plus ISR latency)
  d = *rxPort & rxBitMask;	// read RX data level
  
  if (rxState == 0xFF) {	// if a start bit hasn't been detected yet

    // If it looks like a start bit then initialize; otherwise ignore the rising edge and exit.

    if (d != 0) return;		// it's high so not a start bit, exit
    rxState = 0;		// got a start bit
    rxMask = 0x01;		// bit mask, lsb first
    rxValue = 0x00;		// RX character to be, a blank slate

  } else {	// data bit or stop bit (probably) received

    // Determine how many bit periods have elapsed since the last transition.
    // Multiply & shift is ~10x faster than rxBits /= TIMER_TICKS_BIT_WIDTH.

    rxBits = (t0 - prev_t0 + rxHalfBitWidth);  // add 1/2 bit to round result
    rxBits = uint16_t(rxBits * FAST_DIVIDE_MULTIPLIER) >> rxFastDivideShift;
    rxState = rxBits + rxState;    // total bit periods since start


    // If the data is 0 then back fill previous bits with 1's.
    // If the data is 1 then previous bits were 0's so only this bit is a 1.
 
    if (d == 0) {                              // if RX level is low
      for (uint8_t i=0; i<rxBits; i++) {       // for all previous bits
       rxValue |= rxMask;                      // set them to 1
       rxMask = rxMask << 1;                   // shift to next bit
      }
      rxMask = rxMask << 1;                    // shift to current bit
    } else { 	// d==1)                       // else: RX level is high
      rxMask = rxMask << rxBits-1;             // shift to current bit
      rxValue |= rxMask;                       // and set it to 1
    }

  // If 8th bit or stop bit then the character is complete.
  // If it's the 8th bit the stop bit transition interrupt will be ignored (above).

    if (rxState > 7) {	// if 8th bit or stop bit the character is complete
      rxState = 0xFF;		// begin looking for a start bit
      index = (rxHead+1) % RX_BUFFER_SIZE;
      if (index != rxTail) {
        rxBuffer[rxHead] = rxValue;
        rxHead = index;
      }
    }
  }	// endif start bit or data/stop bit
  
    prev_t0 = t0;               // remember time stamp
}

That are details the OP has to find out, but you have a point!