Cannot read value from numeric input, interference from array

Dear Blynkers

I am busy adapting the “Water Tank Level Indicator with Low Level Warning Notifications” project by GG07 (thank you, GG07, your project proves incredibly helpful). I want to incorporate an adjustable flow-rate alarm, for notifying the user when the drain rate exceeds an adjustable limit. For this, I have tied a numeric input on V1, the variable named drainAlert, into the app.

I calculate the flowrate by reading the volume over time. To achieve a good average over a longer timeframe, I store both the volume and time in a two-dimensional array. Somehow, the second-last time value, stored as (levelChange[1][2]), overwrites what I’m trying to read from V1. I cannot make sense of it!

I’m using a cheap knock-off of the Node MCU V3 ver 0.1, Android 8.0.0, on the Blynk server with Blynk 0.4.10.

Below, the trimmed code:


//****Virtual Pin Assignments****

// V1 - Numeric Widget - Drain Rate, 0 to disable 
// V8 - Terminal

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

//#define BLYNK_PRINT Serial   // Comment this out to disable prints and save space
#define filterSamples 15     //odd number, not smaller than 3

char auth[] = "***";
char ssid[] = "***";
char pass[] = "***";

int maximumRange;
int flowStep = 0;
int volume;
int percent;
int drainAlert = 0;
int levelChange[9][2];
int flowSampling = 1.0;                   //interval in minutes over which flowrate is calculated. There are 9 datapoints spread over this interval 
int flowRate;
unsigned long prevTime = 0;
int smoothDistance;                      //variables for sensor1 data
const int waterTankDia = 2830;           //Water Tank Diameter (mm)
const int waterTankHt = 1890;            //ie. installed Sensor Height (mm)
const int minDistance = 300;             //At this distance, tank is full       
long duration, distance;                 //Duration used to calculate distance

BlynkTimer timer;
WidgetTerminal terminal(V8);

//**********************************************************************
BLYNK_CONNECTED() { // runs once at device startup, once connected to server.
  //Blynk.syncVirtual(V1);
  Serial.println("Connected to Blynk");
}
 
//**********************************************************************
//Switch drain rate alert off if V1 == 0
BLYNK_WRITE(V1) {
  drainAlert = param.asInt();
  }

//**********************************************************************
void MeasureCmForSmoothing() {
  //  Measures ultrasonic distance in *mm*!
  
  smoothDistance = 300+flowStep;  
  flowStep++;

 // if ((millis() - prevTime)>(flowSampling*4000)){
    prevTime = millis();
    for( int a = 8; a >= 1; a-- ) {
      levelChange[a+1][1] = levelChange[a][1];
      levelChange[a+1][2] = levelChange[a][2];
      }
    levelChange[1][2] = prevTime/1000; 
    levelChange[1][1] = ((waterTankHt*(PI*(sq(waterTankDia/2))))-(smoothDistance*(PI*(sq(waterTankDia/2)))))/1000000; 
      
    for (int a = 1; a <= 9; a++){
      Serial.print (a);
      Serial.print ("=");
      Serial.print (levelChange[a][1]);
      Serial.print ("/");
      Serial.print (levelChange[a][2]);
      Serial.print (":");
      }
    Serial.println ();
    if (levelChange[9][1] != 0){
      flowRate = ((levelChange[1][1] - levelChange[9][1])*3600)/(levelChange[1][2] - levelChange[9][2]);  //result in l/h
      }
  
    volume = levelChange[1][1];
    percent = ((float)volume/maximumRange)*100;
    Serial.print("distance=");
    Serial.print(smoothDistance);
    Serial.print("; volume=");
    Serial.print(volume);
    Serial.print("; percent=");
    Serial.print(percent);
    Serial.print("; time=");
    Serial.print(millis());
    Serial.print("; flowrate=");
    Serial.print(flowRate);  
    Serial.print("; DrainAlert=");
    Serial.println(drainAlert);     
    Serial.println(); 
   // }
 }
 
//**********************************************************************
void UploadMeasurements() {
  
 if (drainAlert != 0){ 
    terminal.print("FLOW RATE ALERT");
    terminal.println(flowRate);
    terminal.flush();
    }
}

//**********************************************************************
void setup() {
  
  Serial.begin(74880);
  for(int a = 1; a < 10; a++) {           //Initialize array with zeroes
    levelChange[a][1] = 0;
    }
    
  //determine the tank volume, which is waterTankHt minus minDistance, rounded to the nearest 10
  maximumRange = ((waterTankHt*(PI*(sq(waterTankDia/2))))-(minDistance*(PI*(sq(waterTankDia/2)))))/1000000;
  maximumRange = maximumRange/10;
  maximumRange = maximumRange*10;
  
  Blynk.begin(auth, ssid, pass);
    
  timer.setTimeout(400, [](){
  timer.setInterval(10000L, UploadMeasurements); 
  });
  timer.setTimeout(700, [](){
  timer.setInterval(5000L, MeasureCmForSmoothing);
  });
 }
 
//**********************************************************************
void loop() {
   Blynk.run(); // Initiates Blynk
   timer.run(); // Initiates SimpleTimer
}
1 Like

Can you define this more precisely?

Does everything look ok in Serial Monitor?

Yes, the serial printout of the array looks good, but it shows the wrong value for V1, or drainAlert. Below two random lines from the serial monitor. As you can see, levelChange[8][2], 107 and 112 respectively, somehow sneak their values into variable drainAlert. I suspect that this is a programming issue (as is evident, I’m a novice), not a Blynk issue.

1=9856/142:2=9862/137:3=9869/132:4=9875/127:5=9881/122:6=9888/117:7=9894/112:8=9900/107:9=9907/102:
distance=323; volume=9856; percent=98; time=142104; flowrate=-4590; DrainAlert=107

1=9850/147:2=9856/142:3=9862/137:4=9869/132:5=9875/127:6=9881/122:7=9888/117:8=9894/112:9=9900/107:
distance=324; volume=9850; percent=98; time=147104; flowrate=-4500; DrainAlert=112

@tigger_na what step values do you have for V1?

Add a Serial Print in V1 to see if the value is OK.

I have 50 unit steps. Calling V1 to print in the BLYNK_WRITE(V1) function definition retrieved V1 perfectly. Trying to pass that value on globally resulted in the familiar failure.

Which led me to further tinkering, and simply changing the variable name from “drainAlert” to “drainLimit” and back to “drainAlert” (normal find and replace all) solved the problem perfectly. I can’t even pretend to understand why, but there you have it. Thank you for the help!

Arduino arrays are zero-based, meaning that the first element is 0 not 1.
That means an array of 8 elements should be referenced as 0 to 8, not 1 to 9.

I’m reading your code on a phone and not making that much sense of exactly what you’re trying to do, and how this relates to the results you’re seeing, but your comment about

may indicate that the array values aren’t being stored and referenced correctly because of the non zero-based array addressing.

Pete.

1 Like

But it’s a 9 element array so 1 to 8 (<9) is ok. Not sure why OP is not starting from 0 though.
int levelChange[9][2];

But this is wrong:

  for(int a = 1; a < 10; a++) {           //Initialize array with zeroes
    levelChange[a][1] = 0;
    }

Edit: <= 9 is NOT OK.

1 Like

Good morning

You are right. After I “solved” the problem with the original variable by renaming, it cropped up again with a different variable and virtual pin. I rewrote the code to correctly initialize and manipulate the two-dimensional array for a zero base, and after that it worked.

From the Arduino forum: “For this reason you should be careful in accessing arrays. Accessing past the end of an array (using an index number greater than your declared array size - 1) is reading from memory that is in use for other purposes. Reading from these locations is probably not going to do much except yield invalid data. Writing to random memory locations is definitely a bad idea and can often lead to unhappy results such as crashes or program malfunction. This can also be a difficult bug to track down.” A difficult bug indeed.

The highlight of the changes:


int levelChange[2][15] = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
                          {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};


   
//**********************************************************************
void MeasureCmForSmoothing() {

      prevTime = millis();
      for( int a = 14; a-- > 0; ) {
        levelChange[0][a+1] = levelChange[0][a];
        levelChange[1][a+1] = levelChange[1][a];
        }
      levelChange[1][0] = prevTime/1000; 
      levelChange[0][0] = ((waterTankHt*(PI*(sq(waterTankDia/2))))-(smoothDistance*(PI*(sq(waterTankDia/2)))))/1000000; 
      
      for (int a = 0; a < 15; a++){
        Serial.print (a);
        Serial.print ("=");
        Serial.print (levelChange[0][a]);
        Serial.print ("/");
        Serial.print (levelChange[1][a]);
        Serial.print (":");
        }
      Serial.println ();
      
      if (levelChange[0][14] != 0){
        flowRate = ((levelChange[0][0] - levelChange[0][14])*3600)/(levelChange[1][0] - levelChange[1][14]);  //result in l/h
        }
  
.
.
.
1 Like

@tigger_na FYI, backticks not commas or apostrophes for formatting code in the forum. I fixed your post.

Blynk%20-%20FTFC

Thank you, I know and watched the video and I tried, but my keyboard is different with a lot of weird European Samsung hang-ups which I’m unable to figure out. What is the ASCII code for the backticks?

// To adress The Problem once and for all
void UploadNewBrain() {
}

Never mind, I got it now, thank you.

2 Likes