0
\$\begingroup\$

I'm building a MIDI merger, based on a Attiny (either 84 or 85).

How to read multiple serial ports at the same time (for example 8 RX inputs)?

I can't find the blog article anymore, but I think I did read that it's not as easy as instancing multiple SoftwareSerial objects like this:

#include <SoftwareSerial.h>

SoftwareSerial Serial1(3, 4);
SoftwareSerial Serial2(5, 6);

void setup()
{
  Serial1.begin(31250);
  Serial2.begin(31250);
}

void loop()
{
  if (Serial1.available() > 0)
  {
     a = Serial1.read();
  }
  if (Serial2.available() > 0)
  {
     b = Serial2.read();
  }
}

What is the recommended way to do this?

\$\endgroup\$
  • 4
    \$\begingroup\$ The ATtiny85 only has 6 usable GPIO pins, how would you propose to connect 8 serial devices to it? \$\endgroup\$ – Ron Beyer Sep 16 at 21:54
  • 3
    \$\begingroup\$ Realistically you cannot do this with an ATtiny. With a far faster processor you might be able to handle multiple serial ports in software, or you can find one with multiple hardware UARTs. Don't forget that the problem is not necessarily solvable at all until you come up with rules for what you will discard if the input data won't all fit in the output. You may also have some synchronization issues since the frame times will be slightly mismatched and shift relative to each other... \$\endgroup\$ – Chris Stratton Sep 16 at 21:56
  • 1
    \$\begingroup\$ @basj what speed do you want these serial ports to run at? 9600baud? \$\endgroup\$ – Voltage Spike Sep 16 at 22:13
  • \$\begingroup\$ @VoltageSpike It's MIDI, 31250 bits per second. \$\endgroup\$ – Justme Sep 16 at 22:23
  • 1
    \$\begingroup\$ "and maybe only 6 ins/6 outs or 5/5, to start with" - That would need 12 or 10 available GPIO pins, where the tiny85 has only 6. And even that is if 1) you use the internal clock instead of an external crystal and accept the resulting inaccurate clock (problematic for serial ports), and 2) use the reset pin as an input, making reprogramming impossible without an HV-capable programmer. Then there's the challenge of doing all those serial ports in software, in addition to any actual processing. Honestly, all this doesn't sound like a recipe for success. \$\endgroup\$ – marcelm Sep 17 at 19:13
3
\$\begingroup\$

This is possible... but hard.

The idea is to sample all of the GPIO pins at least 2x the rate of the serial bitstream and use software to decode any data that comes in.

You probably want to do the sampling in a timer-driver ISR and have it store the sampled level bytes into a circular buffer. Then the foreground process can pull sample bytes out of the buffer and decode the levels into data bits and bytes.

If you can assign all the serial pins to the same port (like PORTA on the ATTINY84), then you can very efficiently grab all the levels at once with a single IN instruction and efficiently store them in the target buffer as a single byte.

If you can sample at 16x the data rate then you can even do edge sync like a good hardware USART does....

enter image description here

Of course even at 20Mhz, this is a lot of work for a little ATTINY so you will be limited to the max baud, max duty cycle, and max processing you will be able to do before you run out of steam.

\$\endgroup\$
  • 2
    \$\begingroup\$ "so you will be limited to the max baud" you need to achieve 31250 baud. And you need to not only interpret it, but re-pack the MIDI frame, figuring out how to combine asynchronous sources with slowly rolling phase, and figuring out what to throw away when it doesn't all fit. IIRC some of this is stateful which may make the throwing away quite risky, ie, if a note off event doesn't fit. Working out the priorities could be a fun problem on a $3-4 ARM chip with some elbow room, less so on an ATtiny. \$\endgroup\$ – Chris Stratton Sep 16 at 23:52
  • 1
    \$\begingroup\$ I don't know much about MIDI, but this sounds like it could be a good task for either a small FPGA or a CPLD. \$\endgroup\$ – Caleb Reister Sep 17 at 7:01
  • \$\begingroup\$ Thanks for your answer. I'll study this. Let's say I start with only a 4 channel merger. Then an ATtiny84 will have enough pins. What will happen if I just try to instanciate multiple SoftwareSerial Serial(..., ...) objects? Will this work? \$\endgroup\$ – Basj Sep 17 at 7:04
  • \$\begingroup\$ @ChrisStratton if they told me they were making 1MM of these and I could keep the 1/2 difference between the $3-4 ARM and the $0.50 ATTINY if I could get it to fit, then I'd think this was a VERY fun project! :) Seriously tho, I'm already dreaming about the parallel processing state machine for the incoming bit decode, and the attomically interleaved output buffers... I think it could be a very fun challenge. \$\endgroup\$ – bigjosh Sep 17 at 17:41
  • 1
    \$\begingroup\$ The problem with BOM reduction bounties is how to make them account for changes in risk: what happens if on the even of manufacturing they discover they need a little more code to handle an unexpected case, that won't fit in the packed-to-the-last cycle ATtiny? \$\endgroup\$ – Chris Stratton Sep 17 at 19:43
0
\$\begingroup\$

This (community wiki) answer is for future reference, to give additional informations that were only present in comments here and there and links:

  • Since 2011, Arduino's SoftwareSerial is now using Mikal Hart's NewSoftSerial. It has to be noted that:

    The library has the following known limitations:

    • If using multiple software serial ports, only one can receive data at a time.
    • ....

    It has a 64 RX buffer, see the source here:

    #define _NewSS_MAX_RX_BUFF 64 // RX buffer size
    
  • About using multiple instances:

    There has been considerable support for an library that would allow multiple soft serial devices. However, handling asynchronously received data from two, three, or four or more serial devices turns out to be an extremely difficult, if not intractable problem. Imagine four serial devices connected to an Arduino, each transmitting at 38,400 baud. As bits arrive, Arduino’s poor little processor must sample and process each of 4 incoming bits within 26 microseconds or else lose them forever. Yikes!

    It occurred to me, though, that multiple instances could still be possible if the library user were willing to make a small concession. NewSoftSerial is written on the principle that you can have as many devices connected as resource constraints allow, as long as you only use one of them at a time. If you can organize your program code around this constraint, then NewSoftSerial may work for you.

  • Example:

    #include <NewSoftSerial.h>
    
    // Here's a GPS device connect to pins 3 and 4
    NewSoftSerial gps(4,3);
    
    // A serial thermometer connected to 5 and 6
    NewSoftSerial therm(6,5);
    
    // An LCD connected to 7 and 8
    NewSoftSerial LCD(8,7); // serial LCD
    
    void loop()
    {
      ...
      // collect data from the GPS unit for a few seconds
      gps.listen();
      read_gps_data();  // use gps as active device
      // collect temperature data from thermometer
      therm.listen();
      read_thermometer_data(); // now use therm
      // LCD becomes the active device here
      LCD.listen();
      LCD.print("Data gathered...");
      ...
    }
    

    You can’t write code like this:

    void loop()
    {
      device1.listen();
      if (device1.available() > 0)
      {
        int c = device1.read();
        ...
      }
      device2.listen();
      if (device2.available() > 0)
      {
        int c = device2.read();
        ...
      }
    }
    
  • Quoting this page:

    Serial Port Options:

    • HardwareSerial - Best performance. Always use this first, if available! Teensy and Teensy++ have one HardwareSerial port which is available (not used for uploading or the Arduino Serial Monitor). Arduino Mega has 3 extra HardwareSerial ports. Arduino Uno has none.
    • AltSoftSerial - Can simultaneously transmit and receive. Minimal interference with simultaneous use of HardwareSerial and other libraries. Consumes a 16 bit timer (and will not work with any libraries which need that timer) and disables some PWM pins. Can be sensitive to interrupt usage by other libraries.
    • SoftwareSerial(formerly "NewSoftSerial") - Can have multiple instances on almost any pins, but only 1 can be active at a time. Can not simultaneously transmit and receive. Can interfere with other libraries or HardwareSerial if used at slower baud rates. Can be sensitive to interrupt usage by other libraries.
    • Old SoftwareSerial (SoftwareSerial in Arduino 0023 & earlier) - Very poor performance.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.