Code efficiency help

Hi Guys,

New to arduino, and my ocd is running overtime. Please can someone help or guide me in the right direction.

I will attach full code below, but I want to highlight one section of code that consumes almost 20% of storage space on my Arduino Uno. I am sending readings from a dc voltage sensor and ac voltage sensor to my mqtt server using pubsubclient library. I have tried a few things with my limited knowledge to no avail.

Problematic code:

void sendStats(){
  float dcv = getDCVoltage();
  float acv = getACVoltage();
  String msg = "{DCOffice;" + String(dcv) + ";" + String(acv) + ";}";
  client.publish("stat", (char*) msg.c_str());
}

Storage jumps from 74% to 93%. Is there anyway I can limit the damage here?

Full project:

#include <SPI.h>
#include <EthernetENC.h>
#include <PubSubClient.h>
#include <EmonLib.h>

#define VOLT_CAL 620
#define Client_ID "DCOffice"

EnergyMonitor emon1; 

// Update these with values suitable for your network.
const byte mac[] PROGMEM = {  0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
IPAddress server(***, ***, ***, ***); //Constant, does not need to change during runtime

int interval=30000; //Is adjustable by sending topic: device/looptime msg: millisecs
unsigned long previousMillis=0;

void callback(char* topic, byte* payload, unsigned int length) {
  //Check id, then command, then pin number
  if (strcmp(topic,"DCOffice/cyclerelay")==0) {
      cycleRelay(atoi(payload));
  } else if (strcmp(topic,"DCOffice/switchrelayon")==0){
      switchOnRelay(atoi(payload));      
  } else if (strcmp(topic,"DCOffice/switchrelayoff")==0){
      switchOffRelay(atoi(payload));
  } else if (strcmp(topic,"DCOffice/relaystatus")==0){
      getRelayStatus(atoi(payload));
  } else if (strcmp(topic,"DCOffice/looptime")==0){
      setLoopTime(atoi(payload));
  } else if (strcmp(topic,"DCOffice/updatestats")==0){
      sendStats();
  }
}

EthernetClient ethClient;
PubSubClient client(ethClient);

void reconnect() {
  while (!client.connected()) {
    Serial.print(F("Connecting..."));
    if (client.connect("DCOffice")) {
      Serial.println(F("Success"));
      client.publish("NewDeviceConnected", "DCOffice");
      client.subscribe("DCOffice/cyclerelay");
      client.subscribe("DCOffice/switchrelayon");
      client.subscribe("DCOffice/switchrelayoff");
      client.subscribe("DCOffice/relaystatus");
      client.subscribe("DCOffice/looptime");
      client.subscribe("DCOffice/updatestats");
    } else {
      Serial.println(F("Retrying"));
      delay(5000);
    }
  }
}

void setup()
{
  pinMode(2, OUTPUT); //relay 1 AC/DC
  pinMode(3, OUTPUT); //relay 2 AC/DC
  pinMode(4, OUTPUT); //relay 3 AC/DC
  pinMode(5, OUTPUT); //relay 4 AC/DC
  pinMode(6, OUTPUT); //relay 5 AC/DC
  pinMode(7, OUTPUT); //relay 6 AC/DC
  pinMode(8, OUTPUT); //relay 7 AC/DC
  pinMode(9, OUTPUT); //relay 8 AC/DC
  //pinMode(A0, INPUT); //Sensor 1 N/A 14
  pinMode(A1, INPUT); //Sensor 2 AC Volts 15
  pinMode(A2, INPUT); //Sensor 3 DC Volts 16 
  //pinMode(A3, INPUT); //Sensor 4 N/A 17 
  //pinMode(A4, INPUT); //Sensor 5 N/A 18
  //pinMode(A5, INPUT); //Sensor 6 N/A 19

  //analogReference(EXTERNAL);

  Serial.begin(9600);

  emon1.voltage(A1, VOLT_CAL, 1.7);

  client.setServer(server, 1883);
  client.setCallback(callback);

  //Ethernet.begin(mac, ip); //
  if (Ethernet.begin(mac) == 0) {
    while (true) {
      delay(1);
    }
  }
}

void loop()
{
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  //Send Stats every 30 seconds to server
  unsigned long currentMillis = millis();
  if ((unsigned long)(currentMillis - previousMillis) >= interval) {
      sendStats();
      previousMillis = currentMillis;
   }
}

void sendStats(){
  float dcv = getDCVoltage();
  float acv = getACVoltage();
  String msg = "{DCOffice;" + String(dcv) + ";" + String(acv) + ";}";
  client.publish("stat", (char*) msg.c_str());
}

void cycleRelay(int pin){
  digitalWrite(pin, HIGH);
  delay(5000);
  digitalWrite(pin, LOW);
  //client.publish("NewDeviceConnected", "Relay Cycled");
}

void switchOffRelay(int pin){
  digitalWrite(pin, LOW);
  //client.publish("NewDeviceConnected", "Relay Switched Off");
}

void switchOnRelay(int pin){
  digitalWrite(pin, HIGH);
  //client.publish("NewDeviceConnected", "Relay Switched On");
}

void getRelayStatus(int pin){
  char buffer [33];
  itoa(digitalRead(pin), buffer, 10);
  //client.publish("NewDeviceConnected", buffer);
}

float getACVoltage(){
  emon1.calcVI(25, 1000);
  return emon1.Vrms;
}

float getDCVoltage(){
  float adc_voltage = 0.0;
  float in_voltage = 0.0;
  float R1 = 30000.0;
  float R2 = 7500.0; 
  float ref_voltage = 5;
  int adc_value = 0;
  adc_value = analogRead(A2);
  adc_voltage  = (adc_value * ref_voltage) / 1024.0;
  return in_voltage = adc_voltage / (R2/(R1+R2));
}

void setLoopTime(int time){
  interval = time;
}

Any help or guidance would be greatly appreciated. If you also maybe can give my full code a once over and make suggestions of any pitfalls or things I am doing wrong in terms of memory/storage usage that would also be appreciated.

Thanks in advance

I'd first comment out the String and fix some dummy values to to attempt to localize the problem:

  // String msg = "{DCOffice;" + String(dcv) + ";" + String(acv) + ";}";
  // client.publish("stat", (char*) msg.c_str());
  client.publish("stat", "DCOffice;26.99;19.77;"));

Its not clear whether you are talking about RAM (dynamic) storage or flash (program).

Try this

void sendStats(){
  char msg[30] = "{DCOffice;";
  char val[8];
  dtostrf(getDCVoltage(), 1, 2, val);
  strcat(msg, val);
  strcat(msg, ";");
  dtostrf(getACVoltage(), 1, 2, val);
  strcat(msg, val);
  strcat(msg, ";}");
  client.publish("stat", msg);
}

or

void sendStats(){
  char msg[30], dc[8], ac[8];
  dtostrf(getDCVoltage(), 1, 2, dc);
  dtostrf(getACVoltage(), 1, 2, ac);
  sprintf(msg, "{DCOffice;%s;%s;}", dc, ac);
  client.publish("stat", msg);
}

From what I read, that's the only use of floats in your program, so it causes all sorts of floating point math, as well as formatting code, to get added to your sketch.

Thanks for the reply. When entering dummy data as suggested storage falls from 94% to 77%

void sendStats(){
  //float dcv = getDCVoltage();
  //float acv = getACVoltage();
  //String msg = "DCOffice;" + String(dcv) + ";" + String(acv) + ";";
  client.publish("stat", "DCOffice;0.00;234.45;");//(char*) msg.c_str());
}
Sketch uses 24950 bytes (77%) of program storage space. Maximum is 32256 bytes.
Global variables use 1442 bytes (70%) of dynamic memory, leaving 606 bytes for local variables. Maximum is 2048 bytes.

My method:

void sendStats(){
  float dcv = getDCVoltage();
  float acv = getACVoltage();
  String msg = "DCOffice;" + String(dcv) + ";" + String(acv) + ";";
  client.publish("stat", (char*) msg.c_str());
}

Produces the following:

Sketch uses 30272 bytes (93%) of program storage space. Maximum is 32256 bytes.
Global variables use 1430 bytes (69%) of dynamic memory, leaving 618 bytes for local variables. Maximum is 2048 bytes.

Is that an issue?

Thanks @PaulRB

Your first method knocks 3% of the storage. So from 94% to 91%. Its a start I guess

Hi @J-M-L

Yes sorry if I was unclear. the sendStats() function is consuming almost 20% of program storage space by itself. Doesnt seem right to me

You use strcmp() many times, so try changing them from this:

if (strcmp(topic,"DCOffice/cyclerelay")==0) {

to

if (strcmp_P(topic,(PGM_P) F("DCOffice/cyclerelay"))==0) {

If that’s the only place where you use String and float then it’s not surprising.

The question was more « does the fact that you consume some memory creates an issue for you in terms of being able to complete your project ? »

Memory is there to be used :slight_smile:

The compiler/linker will spot that if sendStats() is not called, then other functions like getDCVoltage() and getACVoltage() are not used anywhere else, so will ignore/exclude them. As pointed out, only those functions use float data types, so the libraries for those can also be excluded.

@J-M-L

I suppose you are right, but being new to arduino I was just very suprised that String() and float can consume so much storage. Coming from C# you never really worry about storage :slight_smile:

EDIT: After processing your reply a bit (I might be a bit slow :rofl: ) it finally clicked what you meant here. So excluding the calls to my other functions also excludes all the other libraries, variables etc of the underlying functions. Gotcha. Makes sense now. So its not just those three statements, its the sum of functions called in those 3. Makes a lot more sense now

@PaulRB

Thank you for your help and insight. I suppose I will need to get accustomed to working with very limited storage. C# did not do me any favors there :slight_smile:

This construct may also be risky, depending on whether or not Client.publish() makes its own copy of msg. The problem is that the storage assigned to the local variable message does not survive very long after the function containing it returns. If in doubt, msg should be global or static.

@6v6gt

Thanks, but the msg variable is only need exactly once by the publish method when sendStats() is called. Only need it for that one instance. Or am I misunderstanding you?

I don't know the library so I will simply make the general point by example.

If, during the execution of the method Client.publish(), the data is immediately copied/send somewhere, it will be OK.
If however, Client.publish() simply puts a pointer to the data somewhere and some asynchronous task (say a timer call back routine) comes later to process it, the data may have already gone missing by that time.

Fair point but here it’s not a problem, the pubsub library has its own buffer and makes a copy

1 Like

It's not the language. It's the hardware environment. You don't have to worry storage if you write C/C++ code on a PC.

That's how crappy software ends up being made and we need more RAM, larger hard drives etc...

worrying a bit about this can go a long way

2 Likes

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