Help to Improve Code Efficiency

Hello, this is my first post here on the forum so if I make any mistakes please let me know and I'll do my best to fix them.

I am still relatively new to programming so please forgive me if there is something obvious that I am missing but I am looking for help to simplifying my currently working code to make more efficient use of storage space.

My current setup is an Arduino Nano with the "standard" cheap enc28j60 ethernet shield designed to directly plug into the Arduino Nano.
As is the code uses 23996 bytes or 78% of program storage and 1715 bytes or 83% of the dynamic memory for the Arduino Nano. I know that I can always move to a bigger mC like the Mega for more memory space, but would rather use the Nano with its smaller form factor.
The code below is a base template for a few projects I'm working on that interface with a network based audio DSP platform by QSC called Q-SYS. This allows me to easily find and manage the Arduino on the network through their configurator and to monitor the device connectivity through their software.
Here are a few links explaining Q-SYS Configurator and the Q-SYS Discovery Protocol (QDP) used by the configurator to find the devices on the network.

And here is the code:

/*
  This sketch is my attempt at a bodged implementation of the Q-SYS Discovery Protocol (QDP).
  The QDP protocol enables Q-SYS network peripherals to populate in the Q-SYS Peripheral Manager allowing for easy discovery and device configuration, such as setting the device hostname and network credentials.
  The current sketch enables an Arduino (with an ethernet shield) to send the necessary UDP packets to act as a Q-SYS network peripheral allowing it to show up in Q-SYS Peripheral Manager.
  Once in the Q-SYS Peripheral Manager a link can be clicked leading to the device IP where a crude webpage is hosted allowing for the editing of the previously mentioned device configuration.
  For simplicity I am just broadcasting the QDP response packet across the network as to my knowledge the EthernetENC library does not support multicast UDP traffic. I know this isn't a great idea and hope to eventually fix it.
  Link to more info on the QDP protocol can be found here- https://q-syshelp.qsc.com/Index.htm#Security/Scope_Protocols.htm

  created Dec. 1, 2021
  by Victor Ellis

*/


#include <SPI.h>         // needed for Arduino versions later than 0018
//#include <Ethernet.h>        // Ethernet library for official w5500 shields
#include <EthernetENC.h>        // Ethernet library for cheap enc28j60 shields
#include <EthernetUdp.h>        // UDP library from: bjoern@cs.stanford.edu 12/30/2008
#include <EEPROM.h>





// Enter a MAC address for your arduino.
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xDF
};


// The IP address will be dependent on your local network:  This is used for debugging
//IPAddress ip(10, 1, 30, 23);      // static IP address for the arduino.
//unsigned int localPort = 6504;      // local port for the arduno to listen on



// An EthernetUDP instance to let us send and receive packets over UDP
EthernetUDP Udp;
#define W5200_CS  10


// Initialize the Ethernet server library
// (port 80 is default for HTTP):
EthernetServer server(80);

// The broadcast IP address:
IPAddress Bip(255, 255, 255, 255);     // IP used to broadcast the QDP (Q-SYS Discovery Protocol) packets

int period = 9975;      // Period between sending of QDP packets - Should be about once a second
unsigned long time_now = 0;

int A, B, C, D; // Vars for storing IP address
int Ag, Bg, Cg, Dg; // Vars for retrieving IP address from eeprom
int P;    // Var for storing port address

bool sel = false;     // Var for setting Auto/ DHCP options on the web UI


String N;

char S[7];      // Var for storing the Auto/ DHCP setting
char H[21];     // Var for storing the device Hostname

char numBox[26] = " <input type=number name=";      // Repeating chars - used to save dynamic memory space
char brk[9] = "<br><br>";                           // Repeating chars - used to save dynamic memory space
char ipRange[22] = " min=0 max=255 value=";         // Repeating chars - used to save dynamic memory space









void setup() {

  EEPROM.get(10, Ag);   // Get the saved IP[0] value from the eeprom
  EEPROM.get(20, Bg);   // Get the saved IP[1] value from the eeprom
  EEPROM.get(30, Cg);   // Get the saved IP[2] value from the eeprom
  EEPROM.get(40, Dg);   // Get the saved IP[3] value from the eeprom
  EEPROM.get(50, S);   // Get the saved Static or DHCP state from the eeprom
  EEPROM.get(60, P);   // Get the saved port value from the eeprom
  EEPROM.get(100, H);   // Get the saved Hostname from the eeprom


  IPAddress ip(Ag, Bg, Cg, Dg);     // IP address for the Arduino
  unsigned int localPort = P;       // local port for the Arduino to listen on

  // start the Ethernet, UDP, and web Server:
  if (strncmp(S, "Static", 6) == 0) {   // Boot with static IP
    Ethernet.begin(mac, ip);
    sel = false;
  }
  else {                      // Boot with dynamic IP
    Ethernet.begin(mac);
    sel = true;
  }

  Udp.begin(localPort);

  server.begin();

  //  Serial.begin(9600);
}




void(* resetFunc) (void) = 0; //declare reset function @ address 0




void loop() {

  //========== Sending of QDP packet ===============================
  if (millis() >= time_now + period) {
    time_now += period;
    Udp.beginPacket(Bip, 2467);     // Broadcast the QDP packet on UDP port 2467
    Udp.write("<QDP><device>\r  <name>");
    Udp.write(H);
    Udp.write("</name>\r  <type>ioframe8s</type>\r  <lan_a_ip>");
    Udp.print(Ethernet.localIP());
    Udp.write("</lan_a_ip>\r <periph_cfg_url>/#network</periph_cfg_url>\r</device>\r</QDP>");
    Udp.endPacket();
  }

  //========== Listen for incoming clients ===============================
  EthernetClient client = server.available();
  if (client) {
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    boolean currentLineIsGet = true;
    int tCount = 0;
    char tBuf[64];
    char *pch;

    while (client.connected()) {
      while (client.available()) {
        char c = client.read();

        if (currentLineIsGet && tCount < 63) {
          tBuf[tCount] = c;
          tCount++;
          tBuf[tCount] = 0;
        }

        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response
          while (client.available()) client.read();
          //Serial.println(tBuf);
          pch = strtok(tBuf, "?");

          //========== Pull in data/ settings from the webpage ===============================
          while (pch != NULL)  {
            if (strncmp(pch, "H=", 2) == 0)  {
              N = pch + 2;
              N.toCharArray(H, 15);
              EEPROM.put(100, H);   // save the Hostname to the eeprom
            }

            if (strncmp(pch, "Mode=", 5) == 0) {
              N = pch + 5;
              N.toCharArray(S, 7);
              EEPROM.put(50, S);   // save the Static/ DHCP state to the eeprom
            }

            if (strncmp(pch, "A=", 2) == 0)  {
              A = atoi(pch + 2);
              EEPROM.put(10, A);   // save the IP[0] value to the eeprom
            }

            if (strncmp(pch, "B=", 2) == 0)  {
              B = atoi(pch + 2);
              EEPROM.put(20, B);   // save the IP[1] value to the eeprom
            }

            if (strncmp(pch, "C=", 2) == 0)  {
              C = atoi(pch + 2);
              EEPROM.put(30, C);   // save the IP[2] value to the eeprom
            }

            if (strncmp(pch, "D=", 2) == 0)  {
              D = atoi(pch + 2);
              EEPROM.put(40, D);   // save the IP[3] value to the eeprom
            }

            if (strncmp(pch, "P=", 2) == 0)  {
              P = atoi(pch + 2);
              EEPROM.put(60, P);   // save value to the eeprom
              delay(10);
              resetFunc();  //call reset to activate new network settings
            }

            pch = strtok(NULL, "& ");
          }

          //========== Update the webpage UI ===============================
          client.write("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<html><body><H1>REMOTE GPIO NANO</H1>");      // Device header name

          client.write("This is a GPIO network peripheral designed for the Q-SYS architecture.");     // Device description
          client.write(brk);
          client.write("<form method=GET>Hostname: <input type=text name=H maxlength=20 value=");
          client.print(H);
          client.write(">");
          client.write(brk);
          client.write("<select name=Mode >");
          client.write("<option value=Auto");
          if (sel == true) {
            client.write(" selected");
          }
          client.write(">Auto</option>");
          client.write("<option value=Static");
          if (sel == false) {
            client.write(" selected");
          }
          client.write(">Static</option>");
          client.write("</select> ");

          client.write("IP Address:");
          client.write(numBox);
          client.write("A");
          client.write(ipRange);
          client.print(Ag);
          client.write(">.");
          client.write(numBox);
          client.write("B");
          client.write(ipRange);
          client.print(Bg);
          client.write(">.");
          client.write(numBox);
          client.write("C");
          client.write(ipRange);
          client.print(Cg);
          client.write(">.");
          client.write(numBox);
          client.write("D");
          client.write(ipRange);
          client.print(Dg);
          client.write(">");
          client.print(brk);

          client.write("UDP port:");
          client.write(numBox);
          client.write("P min=1 max=65535 value=");
          client.print(P);
          client.write(">");
          client.write(brk);
          client.write("<input type=submit></form>");

          client.write("Designed by: Victor R Ellis");

          client.write("</body></html>\r\n\r\n");
          client.stop();
        }
        else if (c == '\n') {
          currentLineIsBlank = true;
          currentLineIsGet = false;
        }
        else if (c != '\r') {
          currentLineIsBlank = false;
        }
      }
    }
  }

  // Put looping code here
  delay(1);
}

Thanks in advance for the advice and help!

I have been programming computers since 1965 and have never seen the words "memory" and "efficiency" used in the same sentence.
If you are thinking about using less memory, then don't use any code written by anyone else. You are using tons of it. So, don't worry about memory until you run out of it.
Paul

Thanks, I've changed the title as I meant more efficient code to use less memory as you said. As for only using my own code do you mean writing my own libraries instead of using existing ones?
The code I've posted is just for handling the basic communication between Q_SYS and the arduino so I haven't included any of the functionality code that would go in the main loop. Once the rest of that code was added I did run out of memory, but was able to strip out some of the functionality for the device to get the code down enough to fit.

All libraries and other similar devices all must be generalized code, so there is lots of extra code to make it fit all conditions. If the goal is to minimize program size, then the code must be specific to only your project.
So, you must balance the goal for minimum memory size with time to design, to write, and to debug your code.
Paul

Check if your client and udp libraries support progmem somehow (eg via the F() macro)

Really? "memory efficiency" is a pretty big deal WRT sorting and searching algorithms...

I think that most of your problem is using the ENC28J60 ethernet shield. Unlike the newer ethernet shields, the ENC28J60 does not include anything but the physical interface. All of the TCP/IP stuff has to be done in the Arduino and therefore the libraries are quite large.

If you switch to the Arduino Ethernet Shield (or equivalent) the WizNet chip does all of the TCP/IP work and the libraries are much smaller.

:rofl:

OK, how about coding efficiency? It has nothing to do with code size, but here's a suggestion to make your code more readable. Short variable names don't make smaller code- it makes difficult reading.

Seriously, I wanted to see how you were using the variable "N", but you can't efficiently use the search feature because the letter "N" shows up in over 300 places in your code.

Always use descriptive variable names.
For example:
int ipA, ipB, ipC, ipD; // Vars for storing IP address
makes more sense than
int A, B, C, D; // Vars for storing IP address

And it compiles exactly the same.

Cryptic code like this may make sense to you today, but in a month or a year, even you will struggle to remember what "N" does.

Thanks for the tip I'll keep it in mind, I realized that now as I was just rushing at the time trying to get it to work and now that I'm going back through it its a pain to read like you said.

I just tried compiling the code with the standard ethernet library and your right it's much smaller. (59%) of program storage space and (56%) of dynamic memory. I was originally planning on using a wiznet w5500 shield but it was tied up in another project so I ended up using the ENC28J60 shield.

Thanks for bringing this to my attention!

Search for "space N" - " N".

hi everyone i have a problem. I don't know where to post this topic. i am a novice at this. here is my question.

I want to print this randomly can you write me the serial.printl code for it

#include <SoftwareSerial.h>
#include <avr/pgmspace.h>
const char string_0 PROGMEM = "Bugun kendine guvenebilirsin fakat bazi puruzler gun icinde seni rahatsiz edebilir. Yenilgiyi kabul etmiyorsun.";
const char string_1 PROGMEM = "Yakininda bulunan birinden hoslaniyor duygularinin ileri gitmemesi icin kendine gereksiz yere baski uyguluyorsun.";
const char string_2 PROGMEM = "Ask hayatin disaridan sorunsuz gibi gorunse de problemlerin var Strese hazirlikli olmalisin. Sakin kontrolu kaybetme";
const char string_3 PROGMEM = "Acele ve israr aleyhine oluyor, Ask hayatinla ilgili rahatsiz oldugun durumlari degistirmek icin bir seyler yapman gerekiyor.";
const char string_4 PROGMEM = "Seni seven birinin sana cok sevindirici surprizi var. Ask hayatina guven ve canlilik gelecek Seni sasirtacak gelismeler var.";
const char string_5 PROGMEM = "gecici maddi sikintilarin var ask hayatin hakkinda guzel seyler soylemek isterdim ama o da pek iyi gorunmuyor. Kendin ol";

void setup()
{

}

void loop()
{

}

See here under "Array of strings" PROGMEM - Arduino Reference

and here: random() - Arduino Reference