Having trouble with a FOR loop.

In the code below I am receiving a height value via the HC12 serial TRX. This all works very well and I have coded a FOR loop to get a piezo buzzer to buzz the number of (100) as the height is reached. When the height changes and the threshold for N which is the height / 100 then this is not recognized because the loop keeps going. I want the number of buzzes to correspond with the height in 100's of feet. i.e. 100 feet one buzz then 200 feet two buzzes etc. until 800 feet. I also have a problem with the blanked out code, which should show that there is no received data from the HC12. I am not sure where to place this code so it recognizes the lack of data reception. I could do withou this but it would be the icing on the cake. :slight_smile: Please help me... thank you.

//HC12_BME280_paul_altitude_RX_V1_23_03_2018_works

const byte numChars = 32;
char receivedChars[numChars];
boolean newData = false;
#include <SoftwareSerial.h>;
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
int piezoPin = 5;
float Height = 0;
#define OLED_RESET 4
int n = 0;
Adafruit_SSD1306 display(OLED_RESET);
SoftwareSerial mySerial(2, 3); // RX, TX
void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
  pinMode(7, OUTPUT);
  digitalWrite(7, LOW);
  mySerial.print(F("AT+C001\r\n"));
  delay(100);
  digitalWrite(7, HIGH);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3c);  // initialize with the I2C addr 0x3D (for the 128x64)
  noTone(piezoPin);
}
void loop() {
  recvWithStartEndMarkers();
  showNewData();

  Height = atof(receivedChars);
  noTone(piezoPin);
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(20, 1);
  display.println("Altitude");
  display.setCursor(20, 20);
  display.println("in Feet");
  display.setTextSize(2);
  display.setCursor(55, 50);
  display.println(Height);
  display.display();

  noTone(piezoPin);

  if (Height >= 100 && Height <= 800) {
    int n = (Height / 100);

    for (int i = 0; i <= n; i++) {
      tone(piezoPin, 3000, 200);
      delay(1000);
     
      Serial.print("n in Feet =  ");
      Serial.println(n);
    }
  }


  /*  else {
    (tone(piezoPin, 3000)); // tone erzeugung
    delay(2000);
    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(20, 1);
    display.println("Switch");
    display.setCursor(40, 25);
    display.println("TX");
    display.setCursor(30, 50);
    display.println("ON!!!");
    display.display();

    {
  */
}
void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (mySerial.available() > 0 && newData == false) {
    rc = mySerial.read();
    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }
      else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;

      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}

void showNewData() {
  if (newData == true) {
    Serial.print("Height in Feet =  ");
    Serial.println(receivedChars);
    newData = false;
  }

}

the way your recvWithStartEndMarkers(); function works is non blocking - so you should not try to read or do anything with the receivedChars buffer until newData == true (which you clear when you call showNewData())

void loop() {
  recvWithStartEndMarkers();
  if (newData) {
     // here you have received your full message
    Serial.print("Height in Feet =  ");
    Serial.println(receivedChars);

     // handle what you need to do
     // ......

     newData = false; // get ready for the next message
  }
}

Thank you for the prompt reply. I am not sure what you mean, are you referring to the for loop or my problem where to put the blanked part of my code. If you could explain in more detail it would help me.

I mean (assuming your setup and code to display on the OLED was correct) your code should look more like this

const byte numChars = 32;
char receivedChars[numChars];
boolean newData = false;

#include <SoftwareSerial.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

const byte piezoPin = 5;
float Height = 0;

#define OLED_RESET 4

Adafruit_SSD1306 display(OLED_RESET);
SoftwareSerial mySerial(2, 3); // RX, TX

void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (mySerial.available() > 0 && newData == false) {
    rc = mySerial.read();
    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      } else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;

      }
    } else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}


void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
  pinMode(7, OUTPUT);
  digitalWrite(7, LOW);

  mySerial.print(F("AT+C001\r\n"));
  delay(100);

  digitalWrite(7, HIGH);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3c);  // initialize with the I2C addr 0x3D (for the 128x64)
  noTone(piezoPin);
}


void loop() {
  recvWithStartEndMarkers();
  if (newData == true) {
    Height = atof(receivedChars);
    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(20, 1);
    display.println("Altitude");
    display.setCursor(20, 20);
    display.println("in Feet");
    display.setTextSize(2);
    display.setCursor(55, 50);
    display.println(Height);
    display.display();

    Height = atof(receivedChars);
    for (int h=0; h<Height; h+=100) {
         tone(piezoPin, 3000, 200);
         delay(300);  // 200 for the tone duration and 100 for the silence
   }
    newData = false;
  }
}

untested, uncompiled, just typed here based on your code above

Thank you I will give this a try. I had to go out thus the late answer. Your last message appeared but the code was not there just your written message. Now i can see it all. Will get bak. In the eman time thank you very much.

When I start the TX the first data that comes to the rx is around 5000 feet. I then push a button on the TX arduino board and the data is set to the height that I am at as 0 feet. I have loaded your code and the Oled shows 5000 feet and the buzzer just buzzes as set in the code. When I push the button on the TX the height on the tx goes to 0 but the rx is still stuck in a loop and shows 5000 feet on the Oled.The buzzer should not be on but it does buzz. If I may repeat perhaps more clearly what I am trying to achieve.
When the Oled shows 100 feet the buzzer should only buzz once every say 2 seconds, if the height is 200 feet then twice very 2 seconds and so non until it buzzes 8 times every 2 seconds at 800 feet. After every buzzer sequence of 2 seconds the code should check whether 200 feet has been reached and not stick in the loop. There should be no buzzer until 100 feet is reached. I hope this is clearer now, did you misunderstand me?

Don’t worry about the buzzing yet - try to print what you get receivedChars and see if that makes any sense

The data reived only comes when I restart the receiver board. The data is correct. but the buzzing keeps on and the data receive is not possible until a reset is made.

Modify the code to print receivedChars In the If section
Post your code
Post what is in the console

To be safe if altitude can be huge, change for (int h=0; h<Height; h+=100) {intofor (float h=0; h<Height; h+=100.0) {

The buzzer starts as soon as the receivedChars is over 0. Otherwise it stops. After I push the button to zero the height being sent by the TX there is a little fluctuation but this is not a problem. The flutuation varies between - over 0 to plus a few decimal places as you can see on the printout.

const byte numChars = 32;
char receivedChars[numChars];
boolean newData = false;

#include <SoftwareSerial.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

const byte piezoPin = 5;
float Height = 0;

#define OLED_RESET 4

Adafruit_SSD1306 display(OLED_RESET);
SoftwareSerial mySerial(2, 3); // RX, TX

void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (mySerial.available() > 0 && newData == false) {
    rc = mySerial.read();
    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      } else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;

      }
    } else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}


void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
  pinMode(7, OUTPUT);
  digitalWrite(7, LOW);

  mySerial.print(F("AT+C001\r\n"));
  delay(100);

  digitalWrite(7, HIGH);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3c);  // initialize with the I2C addr 0x3D (for the 128x64)
  noTone(piezoPin);
}


void loop() {
  recvWithStartEndMarkers();
  if (newData == true) {
    Height = atof(receivedChars);
    Serial.println(receivedChars);
    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(20, 1);
    display.println("Altitude");
    display.setCursor(20, 20);
    display.println("in Feet");
    display.setTextSize(2);
    display.setCursor(55, 50);
    display.println(Height);
    display.display();

    Height = atof(receivedChars);
    for (float h=0; h<Height; h+=100.0) {
         tone(piezoPin, 3000, 200);
         delay(300);  // 200 for the tone duration and 100 for the silence
   }
    newData = false;
  }
}

-0.04
0.13
0.03
0.13
0.19
0.13
0.19
0.09
-0.06
-0.16
-0.21
-0.10
-0.15
-0.15
-0.21
-0.20
-0.30
-0.24
-0.24
-0.23
-0.38
-0.43
-0.33
-0.27
-0.27
-0.22
-0.27
-0.37
-0.25
-0.30
-0.30
-0.35
-0.35
-0.30
-0.24
-0.04

Why is your data negative ?

You need to fix the code on the sender side to receive something meaningful

You have twice Height = atof(receivedChars);

I am receiving something meaningful, the height is correct. The minus comes from the small changes in air pressure. The atof only changes the char to a float.The height value is a float value of the receivedChars. The height is not doubled. The values I am seeing are really OK, but I said at the moment when I set the height with the button it can vary a small amount. The buzzing should only start when I reach the first 100 feet.

I meant you have twice the line Height = atof(receivedChars); in the code - one is enough :slight_smile:

Ok i see - you are not getting the absolute height but relative to the previous reading - is that what you are saying ?

Sorry I did not understand about the height. I have removed one line now. Yes the height is sent relative to 0 feet which I set with a button when the TX is on the ground. Then the TX is in a model glider and as it asends the data is sent to the RX and displayed on the Oled but I have to watch the plane so i need an indication with the buzzer when I rech 100 meters and so on up to 800 meters which is the max I can fly according to law. The 800 meter buzzer then has to be constant and not intermittent. I hope this shows what i am trying to achieve.

I have got to leave the PC at the moment. I will be back tomorrow. Thank you very much in the mean time.

Ok so you get absolute height then and the data you showed above was just your glider being on the ground and small variations due to accuracy

So your ask is 1 beep when you cross 100m and then no beep until you cross 200m when you do 2 beeps etc and above 800m it’s a constant beep

This is easily programmed with a small state machine. Steps could be
BELOW_100
BELOW_200
BELOW_300
...
BELOW_800
TOO_HIGH

You monitor height and depending on current state you check when cross to a new level

switch(state) {
   case BELOW_100: // we were below 100
       if (Height >= 100) { // we are crossing the limit
         state = BELOW_200; // set new state and beep once
         beep(UP, 1); // write this function to beep the right number of times with frequency indicating up or down
      }
   break;

   case BELOW_200: // we were below 200
       if (Height >= 200) { // we are crossing the limit
         state = BELOW_300; // set new state and beep twice 
         beep(UP, 2);
      } else // check in case we went back below 100
      if (Height <= 98) { // manage a bit of room to not beep all the time around 100m
         state = BELOW_100; // record new state and beep once indicating crossing down
         beep(DOWN, 1);
      } 
   break;

   case BELOW_300:
       if (Height >= 300) {
         state = BELOW_400;
         beep(UP, 3);
      } else
      if (Height <= 198) {
         state = BELOW_200;
         beep(DOWN, 2);
      } 
   break;

... you get the idea

}

This is by far not the best code and can heavily be simplified with just a variable holding previous height and checking if trasnsition was crossed but this has the advantage of being super easy to read and could get you started

Read about enum to represent states with keywords in your language

enum : byte {BELOW_100, BELOW_200, BELOW_300, ..., BELOW_800, TOO_HIGH} state; // of course don’t write ... I’m lazy you need all the keywords in there

I am still struggling but will sort it out. enum is difficult to understand. Just need some time. Thanks.

Hi J-M-L, sorry for the long delay in a answering but I have been trying your suggestion.
You are very kind to help me but my knowledge is just not enough to follow your suggestion.
I think I am going to eave the problem for a bit, it is far more complicated than I ever imagined.
When I think through the logic thought I see now that it is complex. many thanks again for your kind efforts.

It is not that complex.

Save the old height to a variable.
Save the current height to a variable.

Divide both these variables by 100. (This will tell us how many beeps to do)

Compare the variables, if they are different we need to do some beeps.

If the current height beeps is >= 8. Make the beep continuous.

If it is between 1 and 7. Beep that many times.

Is this what you meant? I cannot test it at the moment whether the height reaction causes the beeps but I would like your opinion whether this code is plausible. I still have one problem but the no signal window flashes on and off between the height and no Rx but I know why but would like it when there is really no RX after a certain time not just between the loop times, this would mean more delays.. Anyway your comments would be most welcome.

const byte numChars = 32;
char receivedChars[numChars];
boolean newData = false;

#include <SoftwareSerial.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

const byte piezoPin = 5;
float Height = 0;
float oldHeight = 0;
int beep = 0;
int n = 0;
#define OLED_RESET 4

Adafruit_SSD1306 display(OLED_RESET);
SoftwareSerial mySerial(2, 3); // RX, TX

void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (mySerial.available() > 0 && newData == false) {
    rc = mySerial.read();
    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      } else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;

      }
    } else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}


void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
  pinMode(7, OUTPUT);
  digitalWrite(7, LOW);

  mySerial.print(F("AT+C001\r\n"));
  delay(100);

  digitalWrite(7, HIGH);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3c);  // initialize with the I2C addr 0x3D (for the 128x64)
  noTone(piezoPin);
}


void loop() {
  recvWithStartEndMarkers();
  if (newData == false) {
    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(27, 5);
    display.println("NO RX ! ");
    display.setCursor(2, 40);
    display.println("TURN TX ON");
    display.display();
 //   tone(piezoPin, 3000, 200);
 //   delay(1000);
  }

  if (newData == true) {
    Serial.println(receivedChars);
    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(20, 1);
    display.println("Altitude");
    display.setCursor(20, 20);
    display.println("in Feet");
    display.setTextSize(2);
    display.setCursor(55, 50);
    display.println(Height);
    display.display();
    delay(50);
    Height = atof(receivedChars);
    beep = (oldHeight / 100 - Height / 100);
    if (beep < 1)  {
      noTone;
      oldHeight = Height;
    }
    if (beep >= 1) {
      n = beep;
      for (int i = 0; i <= n; i++)
        tone(piezoPin, 3000, 200);
      delay(1000);
      oldHeight = Height;
    }
    if (beep >= 8) {
      tone(piezoPin, 3000, 200);
      delay(3000);
      oldHeight = Height;
    }
    Serial.print ("oldHeight");
    Serial.println (oldHeight);
  }
  newData = false;
}