How To: Prevent duplicate coding

I am rather new in programming and wonder how to compact the code I wrote for using a flexible number of CJMCU-8110 connected to my NodeMCU. In the attached test code below I have 2 connected, but in the near future I want to expand to 3 or 4 and would like to drive that by a variable (f.i. int SensorNumber = 2) instead of duplicating the code for each sensor like I did in the example.

Who can guide me to a solution?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include <Arduino.h>
#include <Wire.h>    // I2C library
#include "ccs811.h"  // CCS811 library
#include "ClosedCube_HDC1080.h"

CCS811 ccs811_1;
CCS811 ccs811_2;

ClosedCube_HDC1080 HDC1080_1;
ClosedCube_HDC1080 HDC1080_2;

int SDApin;
int SCLpin = D1;

HDC1080_SerialNumber sernum;
char format[12];
bool vBootphase = true;
uint16_t eco2, etvoc, errstat, raw;

void setup() {Serial.begin(115200); while(!Serial){;}}

void loop() 
{
  if (vBootphase == true)
  {
    // Channel 1: Setup
    SDApin = D2;
    Wire.begin(SDApin, SCLpin);
    delay(500);
    // ** CCS811 **
    Serial.println("");
    Serial.println("*** CCS1 ***");
    Serial.print("CCS :");
    ccs811_1.set_i2cdelay(50);
    ccs811_1.begin();
    delay(500);
    ccs811_1.start(CCS811_MODE_1SEC);
    delay(1000);
    Serial.print("setup: hardware    version: "); Serial.println(ccs811_1.hardware_version(),HEX);
    Serial.print("setup: bootloader  version: "); Serial.println(ccs811_1.bootloader_version(),HEX);
    Serial.print("setup: application version: "); Serial.println(ccs811_1.application_version(),HEX);
    // HDC1080
    HDC1080_1.begin(0x40);
    Serial.print("Manufacturer ID=0x");
    Serial.println(HDC1080_1.readManufacturerId(), HEX); // 0x5449 ID of Texas Instruments
    Serial.print("Device ID=0x");
    Serial.println(HDC1080_1.readDeviceId(), HEX); // 0x1050 ID of the device
    Serial.print("Device Serial Number=");
    sernum = HDC1080_1.readSerialNumber();
    sprintf(format, "%02X-%04X-%04X", sernum.serialFirst, sernum.serialMid, sernum.serialLast);
    Serial.println(format);

    // Channel 2: Setup
    SDApin = D6;
    Wire.begin(SDApin, SCLpin);
    delay(500);
    // ** CCS811 **
    Serial.println("*** CCS2 ***");
    // Print CCS811 versions
    Serial.print("CCS2 :");
    ccs811_2.set_i2cdelay(50);
    ccs811_2.begin();
    delay(500);
    ccs811_2.start(CCS811_MODE_1SEC);
    delay(1000);
    Serial.print("setup: hardware    version: "); Serial.println(ccs811_2.hardware_version(),HEX);
    Serial.print("setup: bootloader  version: "); Serial.println(ccs811_2.bootloader_version(),HEX);
    Serial.print("setup: application version: "); Serial.println(ccs811_2.application_version(),HEX);
    // HDC1080
    HDC1080_2.begin(0x40);
    Serial.print("Manufacturer ID=0x");
    Serial.println(HDC1080_2.readManufacturerId(), HEX); // 0x5449 ID of Texas Instruments
    Serial.print("Device ID=0x");
    Serial.println(HDC1080_2.readDeviceId(), HEX); // 0x1050 ID of the device
    Serial.print("Device Serial Number=");
    sernum = HDC1080_2.readSerialNumber();
    sprintf(format, "%02X-%04X-%04X", sernum.serialFirst, sernum.serialMid, sernum.serialLast);
    Serial.println(format);

    vBootphase = false;
  }
  else
  {
    // Channel 1
    SDApin = D2;
    Wire.begin(SDApin, SCLpin);
    delay(500);
    // ** CCS811 **
    Serial.print("\nSensor connected on: ");
    Serial.print("SDA - ");Serial.print(SDApin); Serial.print(" | SCL - ");Serial.print(SCLpin);
    Serial.print("\n");
    ccs811_1.read(&eco2,&etvoc,&errstat,&raw);
    delay(500);
    Serial.print("ErrStat: ");Serial.print(errstat);Serial.print(" | ");Serial.println(ccs811_1.errstat_str(errstat));
    if(!errstat==CCS811_ERRSTAT_OK) 
    {
      Serial.print("CCS811-1: ");
      Serial.println("Failed to read from sensor");
      ccs811_1.begin();
      delay(500);
      ccs811_1.start(CCS811_MODE_1SEC);
    }
    else
    { 
      Serial.print("CCS811-1: ");
      Serial.print("eco2=");    Serial.print(eco2);     Serial.print(" ppm | ");
      Serial.print("etvoc=");   Serial.print(etvoc);    Serial.print(" ppb");
      Serial.print("\n");
    }
    // ** HDC1080 **
    Serial.print("T="); Serial.print(HDC1080_1.readTemperature()); Serial.print("C");
    Serial.print(", RH="); Serial.print(HDC1080_1.readHumidity()); Serial.println("%");

    // Channel 2
    SDApin = D6;
    Wire.begin(SDApin, SCLpin);
    delay(500);
    // ** CCS811 **
    Serial.print("Sensor connected on: ");
    Serial.print("SDA - ");Serial.print(SDApin); Serial.print(" | SCL - ");Serial.print(SCLpin);
    Serial.print("\n");
    ccs811_2.read(&eco2,&etvoc,&errstat,&raw);
    delay(500);
    Serial.print("ErrStat: ");Serial.print(errstat);Serial.print(" | ");Serial.println(ccs811_2.errstat_str(errstat));
    if(errstat!=CCS811_ERRSTAT_OK) 
    {
      Serial.print("CCS811-2: ");
      Serial.println("Failed to read from sensor");
      ccs811_1.begin();
      delay(500);
      ccs811_2.start(CCS811_MODE_1SEC);
    }
    else
    { 
      Serial.print("CCS811-2: ");
      Serial.print("eco2=");    Serial.print(eco2);     Serial.print(" ppm | ");
      Serial.print("etvoc=");   Serial.print(etvoc);    Serial.print(" ppb");
      Serial.print("\n");
    }
    // ** HDC1080 **
    Serial.print("T="); Serial.print(HDC1080_1.readTemperature()); Serial.print("C");
    Serial.print(", RH="); Serial.print(HDC1080_1.readHumidity()); Serial.println("%");

    delay(10000);
  }
}
The way you avoid duplicate code is to place close attention to what you're doing when you use ctrl-c and ctrl-v.

In particular, anything you edit after pressing ctrl-v is a clue that the thing you copied should become a function, and the thing you edited should become a parameter.

https://imgur.com/p7xnsvq
Like your D2, ccs811_1 and HDC1080_1 would be parameters.
Followed by D6, ccs811_2 and HDC1080_2
Instead of creating and naming each duplicated item, like this:
1
2
CCS811 ccs811_1;
CCS811 ccs811_2;


create a vector of them:
vector<CCS811> vec_of_CCS811(size_of_vector);

Then, you loop over the vector, carrying out your operations on each duplicated object. Something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
for (int i = 0; i < vec_of_CCS811.size(); ++i)
{
    SDApin = D2;
    Wire.begin(SDApin, SCLpin);
    delay(500);
    // ** CCS811 **
    Serial.print("\nSensor connected on: ");
    Serial.print("SDA - ");Serial.print(SDApin); Serial.print(" | SCL - ");Serial.print(SCLpin);
    Serial.print("\n");
    vec_of_CCS811[i].read(&eco2,&etvoc,&errstat,&raw);  // VECTOR USED HERE
    delay(500);
    Serial.print("ErrStat: ");Serial.print(errstat);Serial.print(" | ");Serial.println(ccs811_1.errstat_str(errstat));
    if(!errstat==CCS811_ERRSTAT_OK) 
    {
      Serial.print("CCS811-1: ");
      Serial.println("Failed to read from sensor");
      vec_of_CCS811[i].begin();   // VECTOR USED HERE
      delay(500);
      vec_of_CCS811[i].start(CCS811_MODE_1SEC);   // VECTOR USED HERE
    }
    else
    { 
      Serial.print("CCS811-1: ");   // STRING NEEDS TO BE BUILT EACH TIME FOR EACH i VALUE
      Serial.print("eco2=");    Serial.print(eco2);     Serial.print(" ppm | ");
      Serial.print("etvoc=");   Serial.print(etvoc);    Serial.print(" ppb");
      Serial.print("\n");
    }
    // ** HDC1080 **
    Serial.print("T="); Serial.print(HDC1080_1.readTemperature()); Serial.print("C"); // SAME PRINCIPLE NEEDED FOR THESE DUPLICATED ITEMS
    Serial.print(", RH="); Serial.print(HDC1080_1.readHumidity()); Serial.println("%");


That code I posed above won't quite work because there are a couple of other things you change each time (e.g. SDApin) but that's the principle. You make a vector of them, and then you loop over the vector, applying the duplicated code to each item in the vector, with small alterations for anything super specific (like SDApin);

For example, you might have a vector
vector<SDApin> vecSDAPins;
that you previously populate so that vecSDAPins[0] is D2, and vecSDAPins[1] is D6.

Or you might have specialist functions that accept the CCS811 object and an int value, and apply different data in the loop according to the i value.
Last edited on
Thanks repeater. I will try to test this option and let you know as soon as possible.
create a vector of them:
vector<CCS811> vec_of_CCS811(size_of_vector);

I am not sure that aurdino supports vectors.
Before you recode all this, test that you have the tools.
Thank you jonnin. Out of the box my NodemCU does not support it, but I have seen some external library options for this. Those have different implementations, though way of thinking probably will be not that different.
So I am going to try those and let you know.
Have a look at this which throws a different light on the way you are approaching this. With a bit of luck it might be of help.

https://electronics.stackexchange.com/questions/76617/determining-i2c-address-without-datasheet

The relevance is it is a I2C scanner which manages the outputs from multiple sensors. You don't need vectors or arrays because the I2C system identifies the devices that are connected.

Without knowing much about your CC... devices it appears most of your output about manufacturers etc is over-embellishment and not needed unless you have lots of program memory.

Aside from the "without-datasheet" each device manufacturer will/should have a datasheet telling you what the devices real stream output is, and enable you to format it as required.
All works perfect. Only had to change
vector<CCS811> vec_of_CCS811(size_of_vector);
into
std::vector<CCS811> vec_of_CCS811(size_of_vector);

Now I indeed can use a dynamic list to drive the number of sensors.

Thanks, thanks, thanks!
Topic archived. No new replies allowed.