Need some help reducing memory

I created a program (based on Telnet client software that has been posted) for interfacing a Rane Halogen audio system with a Monoprice 8x8 video matrix. My test board that I was working on is an Arduino Mega, but I just compiled it for an Arduino Uno (as that is what I built a case for) and I am getting a compiler warning:

Sketch uses 18370 bytes (56%) of program storage space. Maximum is 32256 bytes.
Global variables use 1565 bytes (76%) of dynamic memory, leaving 483 bytes for local variables. Maximum is 2048 bytes.
Low memory available, stability problems may occur.

The purpose of this program is to interface between a RANE Halogen audio system and allow control from its beautiful control interface to other appliances. The device I am controlling (at the moment) is a Monoprice Blackbird 8x8 HDMI Matrix (product #27842). And how it does it, it listens in via telnet to TCP port 4996 on the RANE Halogen sound system, and when it matches commands that are sent, it will then send a command to the HDMI Matrix over TCP port 4001.

I am still quite new to C++ programming but I have been programming for a while now in VB and Javascript, so I was able to grasp a lot of Arduino specific concepts quickly. But the limited memory is proving to be difficult.

I have attached the source code. If you could help that would be wonderful.

The commands that the RANE sends are in this format:
<C&12&1>
The first/last characters is the opening/ending to the command
The & is a dividing character
The second character represents the type of control
The 12 in the above example is control item number
The 1 is the value of the control number.

// This sketch is based on many examples, but is used to access the string from a RANE Halogen system and then feed particular outputs to RS232 commands to other components

#include <SPI.h>
#include <Ethernet.h>
#include <SoftwareSerial.h>


// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
  0x00, 0xDE, 0xAD, 0x12, 0x34, 0x56
};
IPAddress ip(172, 16, 50, 244);

// Enter the IP address of the server you're connecting to:
IPAddress server(172, 16, 50, 254);
//IPAddress server(192, 168, 2, 174); test environment
IPAddress HDMIMatrix(172, 16, 50, 249);
//IPAddress HDMIMatrix(192, 168, 2, 185); test environment

// Initialize the Ethernet client library with the IP address from above
// since we are connecting to RANE Halogen, the port for that is 4996:
EthernetClient client1;
EthernetClient client2;

// Store string and then process when completed
String StringReceived = "";
char character;
int HALloop = 1;
String HALType = "";
String HALControl = "";
String HALValue = "";

void setup() {

  // You can use Ethernet.init(pin) to configure the CS pin
  //Ethernet.init(10);  // Most Arduino shields
  //Ethernet.init(5);   // MKR ETH shield
  //Ethernet.init(0);   // Teensy 2.0
  //Ethernet.init(20);  // Teensy++ 2.0
  //Ethernet.init(15);  // ESP8266 with Adafruit Featherwing Ethernet
  //Ethernet.init(33);  // ESP32 with Adafruit Featherwing Ethernet

  // Open serial communications and wait for port to open:
  Serial.begin(57600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  // start the Ethernet connection:
  Serial.println("Attempting to obtain IP from DHCP, stand by...");
  Ethernet.begin(mac);
  delay(3500);
  if (Ethernet.begin(mac) == 0) {
    // Failed to configure ethernet using DHCP, reverting to static IP
    Serial.println("Failed to obtain DHCP address, reverting to ");
    Serial.println(ip);
    Ethernet.begin(mac, ip);
  } else {
    // Obtained DHCP address
    Serial.println("Received IP: ");
    Serial.println(Ethernet.localIP());
  }

  // Check for Ethernet hardware present
  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
    while (true) {
      delay(1); // do nothing, no point running without Ethernet hardware
    }
  }
  while (Ethernet.linkStatus() == LinkOFF) {
    Serial.println("Ethernet cable is not connected.");
    delay(500);
  }

  // give the Ethernet shield a second to initialize:
  delay(1000);
  Serial.println("connecting to Halogen Server...");

RestartHalogen:
  // if you get a connection to the server, report back via serial:
  if (client1.connect(server, 4996)) {
    Serial.println("connected to Halogen Server.");
  } else {
    // if you didn't get a connection to the server:
    Serial.println("connection failed to Halogen Server");
    delay(500);
    goto RestartHalogen;
  }

  // give the Ethernet shield a second to initialize:
  delay(1000);
  Serial.println("connecting to HDMI Matrix...");

RestartHDMIMatrix:
  //if you get a connection to the server, report back via serial:
  if (client2.connect(HDMIMatrix, 4001)) {
    Serial.println("connected to HDMI Matrix.");
  } else {
    // if you didn't get a connection to the HDMI Matrix:
    Serial.println("connection failed to HDMI Matrix");
    delay(500);
    goto RestartHDMIMatrix;
  }
}

void loop() {
  // if there are incoming bytes available
  // from the server, read them and print them:
  if (client1.available()) {
    char b = client1.read();
    //Serial.print(b);

    if (b == '<') {
      // Start of new command from HAL, dump old string contents
      StringReceived = "";
      HALType = "";
      HALControl = "";
      HALValue = "";

    }
    if (b != '<' and b != '>') {
      // contents of HAL command stored
      if (b == '&' and HALloop == 1) {
        HALType = StringReceived;
        HALloop = 2;
        StringReceived = "";
      }
      if (b == '&' and HALloop == 2) {
        HALControl = StringReceived;
        StringReceived = "";
      } else {
        StringReceived.concat(b);
      }
    }
    if (b == '>') {
      HALValue = StringReceived;
      HALloop = 1;

      // End of HAL command reached, now time to process it

      switch (HALControl.toInt()) {
        case 62: // Window TV - Xfinity 1 Video selection
          client2.write("OUT01:01.");
          break;
        case 67: // Window TV - Xfinity 2 Video selection
          client2.write("OUT02:01.");
          break;
        case 68: // Window TV - Fire Stick Video selection
          client2.write("OUT03:01.");
          break;
        case 69: // Window TV - Chromecast Video selection
          client2.write("OUT04:01.");
          break;
        case 70: // Window TV - HDMI Desk Video selection
          client2.write("OUT05:01.");
          break;
        case 71: // Window TV - HDMI Counter Video selection
          client2.write("OUT06:01.");
          break;
        case 72: // Snack TV - Xfinity 1 Video selection
          client2.write("OUT01:02.");
          break;
        case 73: // Snack TV - Xfinity 2 Video selection
          client2.write("OUT02:02.");
          break;
        case 74: // Snack TV - Fire Stick Video selection
          client2.write("OUT03:02.");
          break;
        case 75: // Snack TV - Chromecast Video selection
          client2.write("OUT04:02.");
          break;
        case 76: // Snack TV - HDMI Desk Video selection
          client2.write("OUT05:02.");
          break;
        case 77: // Snack TV - HDMI Counter Video selection
          client2.write("OUT06:02.");
          break;
/// ... extra cases removed total of 112
        default:
          // default case is to do nothing.
      }

      //Send commands
      if (HALType == "C" and HALValue == "0") {
        // Do Nothing, commmand buttons trigger twice.
      } else if (HALType == "C" and HALValue == "1") {
        // Sending command button commands
      } else {
        // Noting all other type of commands
        Serial.println("HALType: " + HALType + " | HALControl: " + HALControl + " | HALValue: " + HALValue);
      }

      // End of processing

    }
  }

  // as long as there are bytes in the serial queue,
  // read them and send them out the socket if it's open:
  while (Serial.available() > 0) {
    char inChar = Serial.read();
    if (client1.connected()) {
      client1.print(inChar);
      client1.print("received");
    }
  }

  // if the server's disconnected, stop the client:
  if (!client1.connected()) {
    Serial.println();
    Serial.println("disconnecting.");
    client1.stop();
    // do nothing:
    while (true) {
      delay(1);
    }
  }
}

HAL_control code.txt (11.4 KB)

You can open up a bunch of RAM with the 'F' macro, a la:

void setup() {
  Serial.begin(115200);
  // Serial.println("Failed to obtain DHCP address, reverting to ");
  Serial.println(F("Failed to obtain DHCP address, reverting to "));
}

void loop() {
}

Swap the comment markers and compile both to see the difference

dougp:
You can open up a bunch of RAM with the 'F' macro, a la:

void setup() {

Serial.begin(115200);
  // Serial.println("Failed to obtain DHCP address, reverting to ");
  Serial.println(F("Failed to obtain DHCP address, reverting to "));
}

void loop() {
}




Swap the comment markers and compile both to see the difference

Okay, I will bite that did a LOT. Thank you

Sketch uses 18416 bytes (57%) of program storage space. Maximum is 32256 bytes.
Global variables use 1155 bytes (56%) of dynamic memory, leaving 877 bytes for local variables. Maximum is 2048 bytes.

That reduces my Global variable usage by 20%!
Is there any other way I might be able to optimize the code?

ralbrightii:
Is there any other way I might be able to optimize the code?

We can't see your code.

Without the code, I use shots of Tequila to reduce my memory.

Avoid using Strings whenever possible. They eat RAM for breakfast.
Is this a complete sketch? Where do you ever use StringReceived?

StringReceived, HALType, HALControl and HALValue are all ""?

Serial.begin(57600); What is this, 1980's? If you are using the Serial Console, then it can handle 115200 easily. (But this has nothing to do with RAM).

goto ? Really?

Have you read this? Yes, it compiles. Yes it works. But we will make fun of you for using goto in a c++ program.

TheMemberFormerlyKnownAsAWOL:
We can't see your code.

Not sure why not... it is listed in full in the first post.

SteveMann:
Avoid using Strings whenever possible. They eat RAM for breakfast.
Is this a complete sketch? Where do you ever use StringReceived?

Yes this is a complete sketch (however I did remove some of the Case statements due to the 9000 character limit for the forum.

StringReceived is used as the staging/holding area in the beginning of the loop for the Serial output from the TCP commands from the Halogen device.

SteveMann:
StringReceived, HALType, HALControl and HALValue are all ""?

The program does utilize these as placeholders for the commands that come from the Serial output... however

  • HALType will always just be a single character.
  • HALControl will be a number that is 2 or 3 digits long
  • HALValue will usually be a number (at least the values that I care about will always be a number.) Is there a way to note that if a Serial output in that portion of a loop is alphabetically to ignore it or to only allow numeric values?

SteveMann:
Serial.begin(57600); What is this, 1980's? If you are using the Serial Console, then it can handle 115200 easily. (But this has nothing to do with RAM).

Yeah you are right, but for my purposes it wasn't needed to go any faster the commands that are sent are usually under 10 characters per command. Once it goes into production, these serial print commands will be commented out as not needed.

SteveMann:
goto ? Really?

Have you read this? Yes, it compiles. Yes it works. But we will make fun of you for using goto in a c++ program.

Believe it or not, yes I did. Do you have another suggestion to make sure that it does connect to the device in question? And make fun of it away. No hurt feelings here. :slight_smile: :slight_smile: :slight_smile: :slight_smile: However I do note that article does state "it can simplify certain programs" and "With that said, there are instances where a goto statement can come in handy, and simplify coding." LOL.
:sunglasses: :sunglasses: :sunglasses:

ralbrightii:
Not sure why not... it is listed in full in the first post.

But you've modified it since then ...

However I do note that article does state "it can simplify certain programs" and "With that said, there are instances where a goto statement can come in handy, and simplify coding."

...and this is neither

Hi,
If your complete code is over 9000, then attach the ino file to your next post.

Thanks.. Tom... :slight_smile:

TheMemberFormerlyKnownAsAWOL:
But you've modified it since then ...

guilty as charged :frowning: :frowning: :frowning: .... sorry. It is attached below

TheMemberFormerlyKnownAsAWOL:
...and this is neither

I would be more than happy to learn of an alternative method. Just off of the top of my head, I cannot seem to think of a good way to do it. :frowning:

TomGeorge:
Hi,
If your complete code is over 9000, then attach the ino file to your next post.

Thanks.. Tom... :slight_smile:

Sorry Tom.... :-[ :-[
I had posted it as a TXT file...
Here is the code as the INO file.

HAL_control.ino (10.8 KB)

ralbrightii:
I would be more than happy to learn of an alternative method. Just off of the top of my head, I cannot seem to think of a good way to do it. :frowning:

Hint (from your own code)

 while (!Serial) {
    ; 
  }

These client2.write("OUT01:01."); all look like strings that should have stayed in PROGMEM.

TheMemberFormerlyKnownAsAWOL:
These

    client2.write("OUT01:01.");

all look like strings that should have stayed in PROGMEM.

Those are commands being sent out via the Ethernet Shield interface to the HDMI matrix. No string (as far as I can tell at least.)

ralbrightii:
No string (as far as I can tell at least.)

Well, they are enclosed in double quotes.

Thank you everyone. The code was put inplace and tested with the equipment today and it worked flawlessly. I will be posting this in this forum and for RANE Halogen software forums as a way to control other devices through the RANE Halogen software.

Thank you.

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