Repetitive code

Hello! Been lurking for about a month. Really enjoying the Arduino and learning to code. I am coding the analog sensors to read four analog voltage sensors. That information is then used in an if statement and a specific relay can be activated by each individual sensor.

So their are four identical sensors and relays paired to each other. The code I wrote to control these is the same for each sensor, except I added a number (2-5). Is their any way to do this without having to write the code over and over again? I will need to tweak the code in the future and I don't want to have to debug each sensor every time I make a change. I tried messing with arrays but I could not use it to accomplish what I wanted.

Thanks!

mattpre:
I will need to tweak the code in the future and I don't want to have to debug each sensor every time I make a change. I tried messing with arrays but I could not use it to accomplish what I wanted.

you are on the right path

post what you tried, you will get plenty of assistance.

I concur. I expect arrays are what you are looking for.

I also expect that if you post what you tried, you will get plenty of assistance.

The array code is long gone. What I found with arrays was I could add a number to the value of the integer, as opposed to defining a separate integer. I will post below an array from a similar code. He appears to be using three sensors. He is writing the int VWC value to two separate pins. Its not clear to me if that is the same value or if it is from the two separate sensors. Also not sure why it would be written on two pins if their are three sensors. I am posting the parts of his code I believe are relevant.

from: SendUptime does not work (iOS 9.0.1) - #10 by kjeske - Blynk Community

const int sensorsConnected = 2;  //number of sensors
  // read three sensors and append to the string:
  for (int analogPin = 0; analogPin < (sensorsConnected); analogPin++) {
    float sensorValue = analogRead(analogPin);
    float volt = (sensorValue / 1024.0) * 5.00 * 1.01 ; //Ref 5V
    int  VWC = 0;
    //Volumetric Water Content is a piecewise function of the voltage from the sensor
    if (volt < 0.00001 ) {
      VWC = 0;
    }
    else if (volt < 1.1) {
      VWC = (10 * volt) - 1;
    }
    else if (volt < 1.3) {
      VWC = (25 * volt) - 17.5;
    }
    else if (volt < 1.82) {
      VWC = (48.08 * volt) - 47.5;
    }
    else if (volt < 2.2) {
      VWC = (26.32 * volt) - 7.89;
    }
    else {
      VWC = (62.5 * volt) - 87.5;
    }

    dataString += String(VWC);
    if (analogPin < (sensorsConnected - 1)) {
      dataString += "%,";
    }
    else if (analogPin < (sensorsConnected)) {
      dataString += "%";
    }
    Blynk.virtualWrite(1, VWC);
    Blynk.virtualWrite(2, VWC);
    Blynk.virtualWrite(3, volt);
    Blynk.virtualWrite(7, millis() / 60000);
  }

The code I am currently working on is below. As you can see I am also learning to use Blynk, but I am still setting up the core Arduino functionality for this project. Again, no arrays currently, but I wanted to post because I'll take any input I can get. I'm sure this isn't the most efficient code.

#include <Blynk.h>

#define BLYNK_PRINT Serial
#include <SPI.h>
#include <Ethernet.h>
#include <BlynkSimpleEthernet.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "xxx";

#define W5100_CS  10
#define SDCARD_CS 4
int sensorData2(2);
int sensorData3(2);
int sensorData4(2);
int sensorData5(2);
int relay2 = 2;
int relay3 = 7;
int relay4 = 8;
int relay5 = 12;
int thres = 16;
int VWC2(2);
int VWC3(2);
int VWC4(2);
int VWC5(2);


BlynkTimer timer; // Announcing the timer



void myTimerEvent()
{
  sensorData2 = analogRead(A2);
  Blynk.virtualWrite(V2, sensorData2);

  sensorData3 = analogRead(A3);
  Blynk.virtualWrite(V3, sensorData3);

  sensorData4 = analogRead(A4);
  Blynk.virtualWrite(V4, sensorData4);

  sensorData5 = analogRead(A5);
  Blynk.virtualWrite(V5, sensorData5);

}
void vh400loop() {
  float volt2 = (sensorData2 / 1024.0) * 5.00 * 1.01 ; //Ref 5V
   //VWC Curve
  if (volt2 < 0.00001 ) {
    VWC2 = 0;
  }
  else if (volt2 < 1.1) {
    VWC2 = (10 * volt2) - 1;
  }
  else if (volt2 < 1.3) {
    VWC2 = (25 * volt2) - 17.5;
  }
  else if (volt2 < 1.82) {
    VWC2 = (48.08 * volt2) - 47.5;
  }
  else if (volt2 < 2.2) {
    VWC2 = (26.32 * volt2) - 7.89;
  }
  else {
    VWC2 = (62.5 * volt2) - 87.5;
  }

  float volt3 = (sensorData3 / 1024.0) * 5.00 * 1.01 ; //Ref 5V
  if (volt3 < 0.00001 ) {
    VWC3 = 0;
  }
  else if (volt3 < 1.1) {
    VWC3 = (10 * volt3) - 1;
  }
  else if (volt3 < 1.3) {
    VWC3 = (25 * volt3) - 17.5;
  }
  else if (volt3 < 1.82) {
    VWC3 = (48.08 * volt3) - 47.5;
  }
  else if (volt3 < 2.2) {
    VWC3 = (26.32 * volt3) - 7.89;
  }
  else {
    VWC3 = (62.5 * volt3) - 87.5;
  }


  float volt4 = (sensorData4 / 1024.0) * 5.00 * 1.01 ; //Ref 5V

  if (volt4 < 0.00001 ) {
    VWC4 = 0;
  }
  else if (volt4 < 1.1) {
    VWC4 = (10 * volt4) - 1;
  }
  else if (volt4 < 1.3) {
    VWC4 = (25 * volt4) - 17.5;
  }
  else if (volt4 < 1.82) {
    VWC4 = (48.08 * volt4) - 47.5;
  }
  else if (volt4 < 2.2) {
    VWC4 = (26.32 * volt4) - 7.89;
  }
  else {
    VWC4 = (62.5 * volt4) - 87.5;
  }

  float volt5 = (sensorData3 / 1024.0) * 5.00 * 1.01 ; //Ref 5V
  if (volt5 < 0.00001 ) {
    VWC5 = 0;
  }
  else if (volt3 < 1.1) {
    VWC5 = (10 * volt5) - 1;
  }
  else if (volt5 < 1.3) {
    VWC5 = (25 * volt5) - 17.5;
  }
  else if (volt5 < 1.82) {
    VWC5 = (48.08 * volt5) - 47.5;
  }
  else if (volt5 < 2.2) {
    VWC5 = (26.32 * volt5) - 7.89;
  }
  else {
    VWC5 = (62.5 * volt5) - 87.5;
  }
  Blynk.virtualWrite(V12, VWC2);
  Blynk.virtualWrite(V13, VWC3);
  Blynk.virtualWrite(V14, VWC4);
  Blynk.virtualWrite(V15, VWC5);


}



void WaterRelayLoop()
{

  if ( VWC2 < thres) {
    digitalWrite(relay2, HIGH);
    Blynk.virtualWrite(V22, 1);

  } else if (VWC3 > thres) {
    digitalWrite(relay2, LOW);
    Blynk.virtualWrite(V22, 0);
  } else {
    ;
  }

  if (VWC3 < thres) {
    digitalWrite(relay3, HIGH);
    Blynk.virtualWrite(V23, 1);

  } else if (VWC3 > thres)
  { digitalWrite(relay3, LOW);
    Blynk.virtualWrite(V23, 0);
  }
  else {
    ;
  }

  if (VWC4 < thres) {
    digitalWrite(relay4, HIGH);
    Blynk.virtualWrite(V24, 1);

  } else if (VWC4 > thres) {
    digitalWrite(relay4, LOW);
    Blynk.virtualWrite(V24, 0);
  }  else {
    ;
  }

  if (VWC5 < thres) {
    digitalWrite(relay5, HIGH);
    Blynk.virtualWrite(V25, 1);
  } else if (VWC5 > thres) {
    digitalWrite(relay5, LOW);
    Blynk.virtualWrite(V25, 0);
  }
  else {
    ;
  }
}

void setup()
{
  // Debug console
  Serial.begin(9600);

  pinMode(SDCARD_CS, OUTPUT);
  digitalWrite(SDCARD_CS, HIGH); // Deselect the SD card

  pinMode(relay2, OUTPUT);
  pinMode(relay3, OUTPUT);
  pinMode(relay4, OUTPUT);
  pinMode(relay5, OUTPUT);

  Blynk.begin(auth, IPAddress(192, 168, 1, 77), 8442);


  // Setup a function to be called every second
  timer.setInterval(1000L, myTimerEvent);
  timer.setInterval(2000L, WaterRelayLoop);
  timer.setInterval(2000L, vh400loop);

}

void loop()
{
  Blynk.run();
  timer.run(); // Initiates BlynkTimer
}

Thanks everyone!

I recommend a tutorial on the adafruit site. I think it is called, "a classy solution." I followed it and have been able to generalize it to solve a lot of problems. Using classes is a great way to avoid code duplication. In fact, I used functions in one version of something im working on and had so much duplicate code that it became a nightmare to manage. When I implemented the class solution, pages of code were reduced to single lines and, you don't have to worry about adjusting array bounds every time you add or remove an object.

Not sure what all those V2, V3... V22 things are. You'll probably have to build an array for them. But here's a first cut:

#include <Blynk.h>

#define BLYNK_PRINT Serial
#include <SPI.h>
#include <Ethernet.h>
#include <BlynkSimpleEthernet.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "91f56d8100684176a4044e42e0935cb0";

#define W5100_CS  10
#define SDCARD_CS 4

const byte sensorPins[5] = {0, 0, A2, A3, A4, A5};
int sensorData[5];
const byte relayPins[5] = {0, 0, 2, 7, 8, 12};
int thres = 16;
int VWC[5];


BlynkTimer timer; // Announcing the timer

void myTimerEvent()
{
  for (byte index = 2; index <= 5; ++index)
  {
    sensorData[index] = analogRead(sensorPins[index]);
    Blynk.virtualWrite(V2, sensorData[index]);        // Not sure what V2 is
  }
}

void vh400loop() {
  for (byte index = 2; index <= 5; ++index)
  {
    float volt = (sensorData[index] / 1024.0) * 5.00 * 1.01 ; //Ref 5V
     //VWC Curve
    if (volt < 0.00001 ) {
      VWC[index] = 0;
    }
    else if (volt2 < 1.1) {
      VWC[index] = (10 * volt) - 1;
    }
    else if (volt2 < 1.3) {
      VWC[index] = (25 * volt) - 17.5;
    }
    else if (volt2 < 1.82) {
      VWC[index] = (48.08 * volt) - 47.5;
    }
    else if (volt2 < 2.2) {
      VWC[index] = (26.32 * volt) - 7.89;
    }
    else {
      VWC[index] = (62.5 * volt) - 87.5;
    }
  }
  Blynk.virtualWrite(V12, VWC2);      // Not sure what V12-V15 are.
  Blynk.virtualWrite(V13, VWC3);
  Blynk.virtualWrite(V14, VWC4);
  Blynk.virtualWrite(V15, VWC5);
}



void WaterRelayLoop()
{
  for (byte index = 2; index <= 5; ++index)
  {
    if ( VWC[index] < thres) {
      digitalWrite(relayPins[index], HIGH);
      Blynk.virtualWrite(V22, 1);   // Not sure what V22 is
    } else if (VWC[index] > thres) {
      digitalWrite(relayPins[index], LOW);
      Blynk.virtualWrite(V22, 0);
    } else {
      ;
    }
  }
}

void setup()
{
  // Debug console
  Serial.begin(9600);

  pinMode(SDCARD_CS, OUTPUT);
  digitalWrite(SDCARD_CS, HIGH); // Deselect the SD card

  for (byte index = 2; index <= 5; ++index)
    pinMode(relayPins[index], OUTPUT);

  Blynk.begin(auth, IPAddress(192, 168, 1, 77), 8442);


  // Setup a function to be called every second
  timer.setInterval(1000L, myTimerEvent);
  timer.setInterval(2000L, WaterRelayLoop);
  timer.setInterval(2000L, vh400loop);

}

void loop()
{
  Blynk.run();
  timer.run(); // Initiates BlynkTimer
}

As suggested you can use arrays to solve the issue. Another (compatible) approach it is to wrap repetative code in a function. Take a look at your method vh400: Your calculations only differ in 1 single integer input and you only return one float value.
Try to write a function float doCalculation(int inputPin) which already cuts down your code to1/4th

Thanks for all the help. Got arrays working. Plan to read more on classes, right now gonna get the project up and running. Its so easy to keep coding :D.

For the record, the V1, V2 etc. are virtual pins from the Blynk library. In the future I will use the Blynk app to manipulate the relay threshold for each individual relay in the relay loop, but not worrying about that yet. I am posting the code I rewrote and compiled tonight. Seems to be working great.

#include <Blynk.h>

#define BLYNK_PRINT Serial
#include <SPI.h>
#include <Ethernet.h>
#include <BlynkSimpleEthernet.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "xxxxxxxxxxxxxx";

#define W5100_CS  10
#define SDCARD_CS 4

const byte sensorPins[5] = {A2, A3, A4, A5};
int sensorData[5];

int relayPins[5] = {2, 7, 8, 12};
int relayNumber = 0;

const byte analogVir[5] = {V0, V1, V2, V3};

int threshold = 16;
int VWC[5];
const byte vwcVir[5] = {V10, V11, V12, V13};

int relayVirt[5] = {V20, V21, V22, V23};



BlynkTimer timer; // Announcing the timer

void adcReading()
{
  for (byte index = 0; index <= 3; ++index)
  {
    sensorData[index] = analogRead(sensorPins[index]);
    Blynk.virtualWrite(analogVir[index], sensorData[index]);
  }
}

void vh400loop() {
  for (byte index = 0; index <= 3; ++index)
  {
    float volt = (sensorData[index] / 1024.0) * 5.00 * 1.01 ; //Ref 5V
    //VWC Curve
    if (volt < 0.00001 ) {
      VWC[index] = 0;
    }
    else if (volt < 1.1) {
      VWC[index] = (10 * volt) - 1;
    }
    else if (volt < 1.3) {
      VWC[index] = (25 * volt) - 17.5;
    }
    else if (volt < 1.82) {
      VWC[index] = (48.08 * volt) - 47.5;
    }
    else if (volt < 2.2) {
      VWC[index] = (26.32 * volt) - 7.89;
    }
    else {
      VWC[index] = (62.5 * volt) - 87.5;
    }
    Blynk.virtualWrite(vwcVir[index], VWC[index]);
  }

}
void WaterRelayLoop()
{
  for (byte index = 0; index <= 3; ++index)
  {
    if ( VWC[index] < threshold) {
      digitalWrite(relayPins[index], HIGH);
      Blynk.virtualWrite(relayVirt[index], 1);
    } else if (VWC[index] > threshold) {
      digitalWrite(relayPins[index], LOW);
      Blynk.virtualWrite(relayVirt[index], 0);
    } else {
      ;
    }

  }
}
void setup()
{
  // Debug console
  Serial.begin(9600);

  pinMode(SDCARD_CS, OUTPUT);
  digitalWrite(SDCARD_CS, HIGH); // Deselect the SD card

  for (relayNumber = 0; relayNumber < 4; ++relayNumber)
    pinMode(relayPins[relayNumber], OUTPUT);

  Blynk.begin(auth, IPAddress(192, 168, 1, 77), 8442);


  // Setup a function to be called every second
  timer.setInterval(1000L, adcReading);
  timer.setInterval(1000L, WaterRelayLoop);
  timer.setInterval(1000L, vh400loop);

}

void loop()
{
  Blynk.run();
  timer.run(); // Initiates BlynkTimer
}

TKall:
I recommend a tutorial on the adafruit site. I think it is called, "a classy solution."

Yes that's its name, and this is the link.

I just worked through it and it's great. BUT.... to me it's ugly to have the class Flasher{}; right at the top before setup() and loop() and more to the point before the line Flasher led1(....);

So I copied edit:I mean moved the class right to the bottom of the sketch (which had worked as advertised) but now get this error, presumably because it doesn't know the class is actually lower down:

'Flasher' does not name a type

I'm guessing (hoping?) there's a way to "fool" it into looking ahead and would appreciate knowing how to do that?

For the record, here's the sketch direct from the above link, but with class moved to the end:

//https://learn.adafruit.com/multi-tasking-the-arduino-part-1/a-classy-solution

//class Flasher taken from here, right to the end
//it compiled when it was here
//     now gives error: exit status 1 'Flasher' does not name a type

Flasher led1(12, 100, 400);
Flasher led2(13, 350, 350);

void setup()
{
}

void loop()
{
  led1.Update();
  led2.Update();
}//loop

//class Flasher copied here from where it originally was at the top:

class Flasher
{
  // Class Member Variables
  // These are initialized at startup
  int ledPin;      // the number of the LED pin
  long OnTime;     // milliseconds of on-time
  long OffTime;    // milliseconds of off-time

  // These maintain the current state
  int ledState;                 // ledState used to set the LED
  unsigned long previousMillis;   // will store last time LED was updated

  // Constructor - creates a Flasher 
  // and initializes the member variables and state
  public:
  Flasher(int pin, long on, long off)
  {
  ledPin = pin;
  pinMode(ledPin, OUTPUT);     
    
  OnTime = on;
  OffTime = off;
  
  ledState = LOW; 
  previousMillis = 0;
  }

  void Update()
  {
    // check to see if it's time to change the state of the LED
    unsigned long currentMillis = millis();
     
    if((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
    {
      ledState = LOW;  // Turn it off
      previousMillis = currentMillis;  // Remember the time
      digitalWrite(ledPin, ledState);  // Update the actual LED
    }
    else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
    {
      ledState = HIGH;  // turn it on
      previousMillis = currentMillis;   // Remember the time
      digitalWrite(ledPin, ledState);   // Update the actual LED
    }
  }
}; //Flasher

Nice. Congrats. Looks like you found all the errors in my code and fixed them.

Just one thing -- the arrays really only need to be size 4. Sorry about that.

arduino_oldy:
I'm guessing (hoping?) there's a way to "fool" it into looking ahead and would appreciate knowing how to do that?

One way to achieve this is by placing the class in a separate file and importing it at the beginning.

KilianB:
One way to achieve this is by placing the class in a separate file and importing it at the beginning.

Indeed, and I have made it into a library (basically blink without delay in a box) by using this example here.

But nevertheless, I'd still like to see it done in the sketch.

The compiler needs to know about all code BEFORE it is used, just like a in physical project you must have all the components before they can be assembled.

There are a few ways you can do this:

1: put all functions/classes at the top and and your main functions at the bottom.

2: put classes/functions at the bottom and then declare them at the top so the compiler knows they are there.
The Arduino IDE does this for you automatically for functions but not for classes.

3: put your classes in separate files and include them at the top of your sketch. ( the way most programmers do this)

Here's a link: Class declaration

C and C++ distinguish between declaring and defining. A declaration can go anywhere and after declaring the declared variable, type, class or function can be referenced. Multiple identical declarations are allowed. A definition can go anywhere and automatically declares too. There needs to be exactly one definition(*), and every use must be textually after a declaration/definition.

The Arduino environment automatically declares all your functions for you, effectively at the top of the file, so
you don't have to worry about it. It doesn't do this for classes though.

So just declare (but don't define) the class at the top.

Its actually a bit more complicated that this for classes, its easiest to look at examples of classes in existing
libraries to see how it all works.

(*) The definition can be in a separate compilation unit, ie it is resolved at link-time, not compile time.
Arduino IDE simplifies things for you by compiling everything together at once, rather than linking pre-compiled
libraries. This also allows library code to be conditional on the board type.

MarkT:
Its actually a bit more complicated that this for classes, its easiest to look at examples of classes in existing
libraries to see how it all works.

Yes, thanks- that's what I did by turning the adafruit class example into a library. If I still feel the urge I might then mimic that back in the sketch to do the declaration of the class at the top while defining it below loop(). I now at least have blink without delay in a box to use whenever: I have a bit of a policy to use a delay()-less blink on pin 13 as a heartbeat and now that will be easier.

It just looks cr@p to me to have miles of class stuff at the top above setup().

Thanks to KilianB and Hutkikz too.