Sending Serial Commands On Multiple Software Serial Ports

Hi Guys,

Hopefully looking for some general advice to keep me in the right direction. Fairly new to arduino, but have done MatLab programming before and understand basic logic and am slowly learning the limits of functions as I read more and more.

I am trying to build a basic speaker controller. I have a pair of Meridian DSP5200 speakers, which can be RS232 controlled and am hoping to eventually add a centre channel and surrounds and need a way of controlling all 3 [L/R, C, Sur] together [whilst keeping the respective volumes separate.]

I am using Arduino Uno and utilising 3 software serials at once. As I don't want to flood the system or require myself to add extraneous code, I am utilising serial from only front L speaker, which I am reading and the hoping to adjust the others to suit. Using 3 of the MAX232 TTL to Serial converters.

I have copied my code below so you can take a look.

//Meridian Speaker Controller

#include <SoftwareSerial.h>

SoftwareSerial LRSerial(8, 9);    // RX, TX for Front L/R
SoftwareSerial CSerial(10, 11);   // RX, TX for C Channel and Sub
SoftwareSerial SUSerial(12, 13);  // RX, TX for Surround L/R

int AmpSource = 0;         //need to read the amp current source
int AmpVol = 0;            //need to read the current volume of the amp
const int COffsetVol = 3;  //This value is the volume offset for the centre channel
int LRVolNew;
const int SUOffsetVol = 5;  //This value is the volume offset for the surround channels
int SUVol;
int SUVolNew;
int CVol;
int CVolNew;
int LRVol;
int VolumeCommand = "VN";
int CVolSend = VolumeCommand + CVolNew;
int SUVolSend = VolumeCommand + SUVolNew;

void setup() {

  Serial.begin(9600);
  delay(10);

  //This is for starting the front speakers(s)
  LRSerial.begin(9600);
  delay(10);

  //This is for starting the C speaker(s) serial
  CSerial.begin(9600);
  delay(10);

  //This is for starting the Surround speaker(s) serial
  SUSerial.begin(9600);
  delay(10);
}

void loop() {


  LRSerial.listen();
  delay(10);

  // while there is data coming in, read it
  // and send to the hardware serial port:
  while (LRSerial.available() > 0) {
    LRSerial.read();
    LRVolNew = LRSerial.parseInt(SKIP_ALL);  //Processes the integer out
    Serial.println("LR Vol: ");
    Serial.println(LRVolNew);  //Prints it out

    delay(50);

    //This sets the new C Channel Volume, based on L/R Input
    int CVolNew = (LRVolNew - COffsetVol);
    Serial.println("C Vol: ");
    delay(25);
    Serial.println(CVolNew);

    //This sets the new Su Channel Volume, also based on L/R Input
    int SUVolNew = (LRVolNew - SUOffsetVol);
    Serial.println("SU Vol: ");
    delay(25);
    Serial.println(SUVolNew);

    while (LRSerial.available())
      LRSerial.read();
  }
  //This adjust the centre channel volume if it needs to be
  if (CVol != CVolNew) {
    delay(10);
    CSerial.print(CVolSend);
    int CVol = CVolNew;


    //Similarly, adjusting the surround channel(s) volume
    delay(10);
    SUSerial.print(SUVolSend);
    int SUVol = SUVolNew;
  }
}

Not looking for someone to rewrite this perfectly for me [as is too often the case here]. Just have a few questions regarding.

-I can get the program compiled with no errors and can now stop it looping unnecessarily using the while on LR serial, which is great.
-The next step is believe, is to send commands out on the other serial lines so that they may keep in step with the main [L/R] as required.'
-I was using CSerial.write and SUSerial.write to send these values to the speakers, but for some reason I cant seem to get them to respond. Am I missing something blaringly obvious?
-Format for sending volume commands to the speakers is VNXX, with XX being input parameters for volume (1-99).

As mentioned, any general advice here would be swell. Have also tried using CSerial.write and the same for the surrounds to no avail. I used the if statement down the bottom to avoid unnecessary looping and constant spamming on the serial ports, it only sends the command to change vol if it is out of step.

Again, as above, fairly new to arduino, happy to also cop a bit of flac for code layout/formatting :slight_smile:

Many thanks!

Welcome to the forum

Oh dear !

Have you got an RS232 interface between the Uno and the speakers ?

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

You need to use the listen() method to activate each serial line

From the documentation.

listen()
Enables the selected SoftwareSerial object to listen. Only one SoftwareSerial object can listen at a time; data that arrives for other ports will be discarded. Any data already received is discarded during the call to listen() function (unless the given instance is already listening).

I do indeed.

Using a TTL to Serial Converter. Actually have 3, and just swapping the RHS speaker to the other two atm to see if can actually get control that way.

I used the listen function for the first portion, so I can receive serial data IN on the main serial line.

Do I still need to use the listen functions when sending on those lines?

Every document I have read thus far seems to be based around/modelled on reading multiple serials at once. To simplify this controller, I am only interested in what comes IN on LRSerial [the main speakers] and then only send to the remaining two serials.

All speakers have IR control with my remote and I receive serial data each time I send the IR command. Hence, I want to take this and then send to the other speakers [at a lowered/increased volume offset]. If I had a code to make adjustments each time I received serial from each line I fear it would over complicate things. 1 master line and 2 slave lines if that makes sense?

If I were you I would give up on using the Uno and multiple instances of SoftwareSerial. Buy a Mega instead and you have 4 independent hardware UARTs which will make things so much simpler

Not when sending... but you do to receive.

If you are only listening on 1 port then this should still work (provided you "listen" on that port). The other option as pointed out above is to use an Arduino the has multiple hardware serial ports... then you can send/receive simultaneously on all ports.

Whilst I do agree here somewhat, with you both, I am not sending a huge /amount/ of serial data on these ports [the other two].

As you know when watching TV or surround content, it will be only minor volume changes, that I simply need reflected across the board. Because I have to feed the speakers with coaxial digital [S/PDIF], the volume control has to be done on the speakers themselves, rather than in any sort of amplifier or pre-amp stage as it were.

Hence, I have to calibrate manually, and offset each speaker(s) volume relative to the front L/R. I Would be interested to have a play around with the mega to see how much of a difference it makes.

If I can ask part of my original question again though, does that code seem ok in terms of sending commands /to/ the speakers? I have tried both the Serial.read and Serial.write functions and cant seem to get any response.

I currently have one hooked up as "LR" and one as "C" for testing purposes, and run the code and can input IR vol. commands with the remote and I am getting correct values being printed on the serial monitor. I have then swapped the connections and repeated with the other speaker and had the same result.

The commands list as per meridians reference is fairly basic, VN45 [Volume to 45] etc. Usually two Char sometimes followed by 1 or 2 values. If I can assume correctly, the fact that I can receive would mean that I should be able to send? They recommend using a null modem or crossover cable, so my answer was to invert the physical cabling into the Uno, which then allows me to read.

Thanks to both of you for the help thus far.

For me using separate hardware UARTs instead of SoftwareSerial is not a matter the volume and direction of data being sent/received but more a matter of ease of programming, reliability and ease of testing/debugging

Update, I have tried tinkering with this code for a few hours last night to strange results.

I can seem to read/receive serial data ok on all of the software.serial ports. However, my challenge as I said before is not being able to send/write serial back to the units successfully.

I scrutinized Meridians user guide for another of their speaker models, which also allows the RS232 port to be used for communications to it.

Their requirements are this:

Port settings

9600 Baud, 8 data bits, 1 stop bit, no parity, no flow control

Command format

RS232 commands consist of ASCII characters which are case
sensitive. Some commands require parameters, so these are
followed by additional characters. All commands are terminated
with a carriage return.

For example, to change the volume level to 45, the command
is:

VN45

This will cause the loudspeaker to change its volume and
respond with:

Volume 45

Is there a way to check that there are in fact the setting that I am using? Is there a default for software.serial ports? Would assume that it would be 8-N-1?

I also tried stripping back the code and sending "VN45"as a pertinent example, and then reading the return. Each time I run it, I seem to receive -1, which to me indicates that there is nothing, meaning it wont accept what i'm sending [makes perfect sense seeing as nothing happens haha].

It may be easier to use a terminal program like PuTTY or Coolterm, and talk to the speakers directly, no Arduino involved.

You can set all the serial parameters, type what you want and see the response.

You could open multiple serial lines in Coolterm.

That eliminates any issues you may have inadvertently created with code or software serial or whatever, and increase your confidence for switching back to your code version of talking and listening.

a7

Can I just say a7, you are an absolute legend.

Have been busy with christmas and holidays etc, but downloaded coolterm and connected directly with my trusty old USB-232 cable and away we went!

Turns out I was receiving serial but not able to send, then I checked and apparently the code sets are different than I had originally anticipated. Meridian manufacture various speakers lines and I couldn'd find the command list for mine so I used what I thought was closest. Turns out there is another model which ive subsequently discovered is closer in operation, meaning I can successfully both SEND and RECEIVE on my serial now.

Hopefully soon ill jump back into IDE and re-tweak these and with any luck get this off the ground!

Many thanks again, huuuuuge tip!!

It is a nice trick. Obvsly it works well with traffic that is all printable ASCII and is not time sensitive - something I wish were always the case.

If you design a serial communication protocol one day, make it easy to work with!

The person I learned the trick from is still alive, and I am in touch with her. It is she who is the absolute legend and I will pass along your remarks to her.

always HTH

a7

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.