Code not working when changing potentiometer with mpu6050

With some help of this forum I managed to get the code working with a potentiometer. I am new to arduino and coding so I need some assistance again.

The goal is to start a timer when the angle is >-80 or < 80 degrees. When the condition is met, a timer start. When the angle is 5 seconds above 80 degree, it need to turn on a led. (not with delay, but >5 seconds) I get that working with a potentiometer. When I change the code to replace the potentiometer with the mpu6050, it is not working. With the potentiometer, when the value is above for 1 seconds and than goes back under threshold, it reset and goes back to measuring value of the potentiometer. When the value goes above threshold with the mpu6050, it won't reset when goes back under threshold and stays in the loop.

hope you can help me.

Code 1: potentiometer

#include <Ticker.h>
Ticker secondTick;
volatile int watchdogCount = 0;
const int analogPin = A0;              // A0 = pin ADC0 potentiometer
const int ledBoard =  13;              // D7 output led D7 = GPIO 13
const int ledPin =  D4;                 // GPIO 3 Onboard LED** 

const int threshold = 500;             //Minium value
const unsigned long onTime    = 5000;  //time for the value to be above threshold
unsigned long       startTime = 0;

    
void setup() {
  // put your setup code here, to run once:
    pinMode(ledPin, OUTPUT);
    pinMode(ledBoard, OUTPUT);
    Serial.begin(115200);
    secondTick.attach(1,ISRwatchdog);
 }

void loop() {
   int analogValue = analogRead(analogPin);
    Serial.println (analogValue);
    Serial.println (watchdogCount);
    
  if(analogRead(analogPin) > threshold){
    delay(50);
    startTime = millis();
    while(analogRead(analogPin) > threshold){
      yield();
      if(millis() - startTime > onTime){
        digitalWrite(ledPin, HIGH);
        digitalWrite(ledBoard, HIGH);
        Serial.println("Above threshold");
        delay(500);
        Serial.println (analogValue);       
        while(analogRead(analogPin) > threshold){
          // whait 2 seconds to measure again
          delay(2000);
          Serial.println ("Above threshold");
        }
          digitalWrite(ledPin, LOW);
          digitalWrite(ledBoard, LOW);
        }
      }
    }
  }
    void ISRwatchdog() {
  watchdogCount = 0;
  if (watchdogCount == 600){
    Serial.println();
    Serial.println("The Watchdog bites!!");
    ESP.reset();
  } 
 }

Code 2: MPU6050


#include <Ticker.h>
#include "Wire.h"
#include <MPU6050_light.h>
MPU6050 mpu(Wire);
Ticker secondTick;

volatile int watchdogCount = 0;
const int analogPin = A0;              // A0 = pin ADC0 potentiometer
const int ledBoard =  13;              // D7 output led D7 = GPIO 13
const int ledPin =  D4;                 // GPIO 3 Onboard LED** 

unsigned long timer = 0;
const int threshold = 80;             //Minium value
const unsigned long onTime    = 5000;  //time for the value to be above threshold
unsigned long       startTime = 0;


void setup() {
  Serial.begin(115200);
  Wire.begin();
  pinMode(ledPin, OUTPUT);
  pinMode(ledBoard, OUTPUT);
  secondTick.attach(1,ISRwatchdog);
  
  byte status = mpu.begin();
  Serial.print(F("MPU6050 status: "));
  Serial.println(status);
  while (status != 0) { } // stop everything if could not connect to MPU6050

  Serial.println(F("Calculating offsets, do not move MPU6050"));
  delay(1000);
  mpu.calcOffsets(); // gyro and accelero
  Serial.println("Done!\n");
}

void loop() {
  mpu.update();

  if ((millis() - timer) > 10) { // print data every 10ms
    Serial.println("X : ");
    Serial.print(mpu.getAngleX());
    timer = millis();

    if (mpu.getAngleX() > threshold) {
      delay(50);
      startTime = millis();
      while (mpu.getAngleX() > threshold) {
        yield();
        if (millis() - startTime > onTime) {
       
          Serial.println("above threshold");
          delay(500);
          Serial.println (mpu.getAngleX());
          
          while (mpu.getAngleX() > threshold) {
           // whait 2 seconds to measure again
            delay(2000);
            Serial.println ("above threshold");
          }
          digitalWrite(ledPin, LOW);
          digitalWrite(ledBoard, LOW);
        }
      }
    }
  }
}
    void ISRwatchdog() {
  watchdogCount = 0;
  if (watchdogCount == 600){
    Serial.println();
    Serial.println("The Watchdog bites!!");
    ESP.reset();
  } 
 }

The people that did "help" you need some assistance themselve.

This code has a lot of blocking loops and then the code tries to "repair" the blocking with yield() and an extra watchdog-timer

What I have understood so far
the mpu6050 measures an angle-value
if (- 80 > angle) and (angle < 80) which means angle between -79 and +79
then you say
When the angle is 5 seconds above 80 degree
above 80 degree means angle is 81,82,83,84...
which is the opposite to (angle < 80)

what do you really want?

Did you measure the angles with a small demo-code what you get if you turn the mpu6050-module? I doubt that the angle-value goes from +90 and if you turn a little bit more it jumps to -90

Post a hand-drawn picture of the physical positions of the sensor
example:

let's assume your sensor is mounted on a PCB. If the PCB's surface has a vertical direction
should the timing start if the direction of the surface deviate -10 degree + 10 degree from exact vertical?

Did you really measure what angle you read if you tip the sensor for -10° / +10° ?

You have nested three if-conditions and two while-loops.
This is really bad programming-style
additionally

startTime = millis();

is only updated if the upper if-condition

if (mpu.getAngleX() > threshold) 

stays true
If getAngleX() is below theshold StartTime never gets updated.
Sorry to say that. This code seems to be malfunctional

Please explain step by step what functionalitity you want to have.
Your code does more than just switching on a LED. So you should describe the full functionality
in normal words. I beg you in this decription pleeeeaaaase avoid programming terms just normal words.

best regards Stefan

Hi Stefan, thank you for your reply.

I see there are some contradictions in what I explained and what you see in the code.
The goals is to turn on a led when the angle is greater than 80 degree deviating from a vertical position, to the left or right. like a glass on a table. see picture:


When the object goes from vertical to horizontal in the blue zone between 80-90 degree and -80 and -90 degree.

if (mpu.getAngleX()> 80 || mpu.getAngleX()<-80)" {
do something
}

I can measure the angle with the code, it gives on the X, Y or Z axis the angle in a range of -90 up to 90 degree. I only need 1 axis (X).

When the angle is in the blue zone between 80 and 90 or -80 and -90, than a timer should start. After 5 seconds, the led goes on. But if during the timer the angle goes outside the threshold, the timer has to go back to 0. This worked with a potentiometer, but with the MPU6050 the angle is no longer measured and the timer works as a delay. I see on the serial monitor that the code get stuck in the last while loop because it print the second message.

What I said, I am new to coding so I am open foor feedback and improvements of the code. Obviously my code needs improvement. I find it strange that it works when I use a potentiometer or push button but not with the mpu6050.

Thanks.
Danny

Well in the MPCU-Code you have posted

switching the LED high is missing
potentiometer-code

    while (analogRead(analogPin) > threshold) {
      yield();
      if (millis() - startTime > onTime) {
        digitalWrite(ledPin, HIGH);
        digitalWrite(ledBoard, HIGH);
        Serial.println("Above threshold");
        delay(500);
        Serial.println (analogValue);
        while (analogRead(analogPin) > threshold) {
          // whait 2 seconds to measure again
          delay(2000);
    if (mpu.getAngleX() > threshold) {
      delay(50);
      startTime = millis();
      while (mpu.getAngleX() > threshold) {
        yield();
        if (millis() - startTime > onTime) {

          Serial.println("above threshold");
          delay(500);
          Serial.println (mpu.getAngleX());

          while (mpu.getAngleX() > threshold) {
            // whait 2 seconds to measure again
            delay(2000);

I'm using a dark scheme for the Arduino-Editor

best regards Stefan

I changed the code multiple times to try thing, so I missed that. But that does not explain why the timer won't start and won't reset and stays in the second while loop.

I forgot to mention that I use a ESP8266 > Wemos Mini D1 Pro. that is why I have the yield() and watchdog. Because without it, it reset the board after 2seconds delay.

What is the best way to start a timer if a statement is true? I can also change the first If for a while. Like you said, I have an if > while > if while.

How do I fix the:
startTime = millis();

"is only updated if the upper if-condition"

Thanks

first of all: you have a limited knowledge about programming.
This completely OK I'm just stating it to explain something.
There are programming-techniques you don't know yet.

This is the reason why you should use normal words to describe the functionality that you want. If you try to describe the functionality with lines of code your limited knowledge about programming will block you to give a good description. You would ask for details that become obsolete with different programming-techniques. And you would try to solve problems that simply vanish completely when using different programming-techniques.

So pelase do me a favor and describe the wanted functionality in normal words and avoid all programming-terms.

me personal I use non-blocking coding all the time. Especially non-blocking timing.
Non-blocking means not a single while-loop inside

void loop() {

}

Only inside setup I use a while-loop for connecting to WiFi.

I want to describe your functionality in my own words to check if I understand it right.

if angle is between -90° and -80° or angle is between +80° and +90° start a timer.

when timer has started check if 5 seconds have passed by

If 5 seconds do have passed by switch on LED.

I guess some more things shall happen.
Please describe the functionality in normal words.

best regards Stefan

Yes, you are right. It needs to function as an alarm that calls me when a horse is laying down on its side. It needs to lay down a minimum of 5 seconds to confirm it. When during the 5 seconds the horse stand up or rolls, it doesn't should alarm me. I hope this explaination is clear.

For testing purpose, the calling function is now the led in the code. I get it working with wifi but I removed it for implementing the mpu6050 and get the sensor working first because It keeps calling me when it stays in a loop.

My knowledge is limited like you stated. It is hard to find good sources about how to code in C/C++. Not the definition of a something but how to combine everything in a good code. I download multiple books and watched youtube.

Nest an if function is not per definition a bad thing right? You first need to know if the value is above threshold before starting the timer for example.

Regards,
Danny

Hi Danny,

So this gives an overview about the project.
This means my description is sufficient?

if angle is between -90° and -80° or angle is between +80° and +90° start a timer.

when timer has started check if 5 seconds have passed by

If 5 seconds do have passed by switch on LED.

if angle becomes bigger than -80 or angle becomes smaller than +80 then stop timer and change to idle-mode. Which is just checking for

if angle is between -90° and -80° or angle is between +80° and +90° start a timer.

best regards Stefan

Here is a piece of code that shows how to use a programming-technique called state-machine.
You define certain steps where each step does one particular thing
The state-machine goes through each step in a sequence.
executing a certain step or checking conditions invoke the change to a new step

each step is called a "state" hence state-machine

a completely different remark:
Your project is monitoring a horse. A living creature. And as you want to be alarmed if the horse lays down for more than 5 seconds your device should have 99,999% reliability
As a hobby-project such a high reliability is hard to achieve.

You should add tripple redunancy (three devices doing the same job)
an external watchdog (if your monitoring device is not responding with a heartbeat every minute activate another alarming-device

or even better the device "mounted" on the horse must send a "everything-OK" signal once every minute permanently 24/7 If the signal is not received activate alarm.

This ensures whenever there is a malfunction in the horse-device an alarm is activated.

How about a much simpler device than a Wemos D1 Mini?
A simple tilt switch that is opened and stops powersupply of a very basic wireless device as soon as the horse lays down? If signal is not there for more than 5 seconds activate alarm

best regards Stefan

Do you have an example code that shows how a state-machine works in my application or similar?

The device must be reliable, but that is something to think about when I have a basic code working. But I agree, there must be a backup system. I use the Wemos because of its size and the Wifi. I also could use a Arduino mini with wifi. I tried other sensors like the sw520D and the SCA60C. I chose the MPU6050 because I can mount it in different orientation and use the X, Y or Z-axis. The sw520D is not accurate and had some issues with the SCA60C as well.

Kind regards,
Danny

Hi Danny,

I have the impression that you have not yet understood the difference what it means sending a signal if alarm-situation is there and sending a signal "everything is OK" all the time.

The approach send signal "alarm" and in all other situations do nothing offers less security than the opposite:

sending 24 hours / 7 days per week a signal "everything is OK" once every minute

The receiving device starts alarming if the signal "everything is OK" was not send for two minutes or ten seconds or whatever.

This approach creates an alarm if:

  • battery of horse-device is empty
  • device is mounted the wrong way
  • calibration is wrong
  • horse-device has no connection to the receiver
  • code has a bug and is malfunctioning
    etc. etc

A Wemos D1 mini is a quite complexe device.

A very simple 433 MHz transmitter that simply sends some kind of a heartbeat-signal
whenever it is connected to powersupply in combination with a receiver that expects this heartbeat-signal in short time-intervals and whenever the heartbeat-signal is not received for more than 5 seconds
start alarm might offer a higher reliablity due to the simple nature of the device used

If this 433MHz transmitter is combined with a very simple tilt-switch which means the switch opens as soon as the horse lays down and only closes if horse is standing.

Closed switch => transmitter sends signal "Everything OK" again

using two devices or even three offer more reliability as it is very unlikely that two devices fail at the same time. Same thing on the receiver-side.

ooops I fogot to paste the code into the posting

// start of macros dbg and dbgi
#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);
// usage: dbg("1:my fixed text",myVariable);
// myVariable can be any variable or expression that is defined in scope

#define dbgi(myFixedText, variableName,timeInterval) \
  do { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  } while (false);
// usage: dbgi("2:my fixed text",myVariable,1000);
// myVariable can be any variable or expression that is defined in scope
// third parameter is the time in milliseconds that must pass by until the next time a
// Serial.print is executed
// end of macros dbg and dbgi

void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__));
  Serial.print( F("  compiled ") );
  Serial.print(F(__DATE__));
  Serial.print( F(" ") );
  Serial.println(F(__TIME__));
}


boolean TimePeriodIsOver (unsigned long &periodStartTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - periodStartTime >= TimePeriod )
  {
    periodStartTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  }
  else return false;            // not expired
}

unsigned long MyTestTimer = 0;                   // variables MUST be of type unsigned long
const byte    OnBoard_LED = 2;
const byte ledPin = 3;// D4;                 // GPIO 3 Onboard LED** 
const byte ledBoard =  13;              // D7 output led D7 = GPIO 13


void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}


const byte CheckAngle        = 0;
const byte startTiming       = 1;
const byte check5SecondsOver = 2;
const byte activateAlarm     = 3;
const byte waitForAlarmDeactivating = 4;

const byte deactivate_Pin = 4;

byte myStateVar;

unsigned long waitingTime = 5000;
unsigned long StartWaiting;
unsigned long currentMillis;
unsigned long oneSecondStart;

int AngleX;


void setup() {
  Serial.begin(115200);
  Serial.print( F("\n Setup-Start  \n") );
  PrintFileNameDateTime();
  myStateVar = CheckAngle;
}


void myStateMachine() {

  //mpu.update();
  AngleX = 70;//mpu.getAngleX();

  switch (myStateVar) {

    case CheckAngle:
      digitalWrite(ledPin, LOW);
      digitalWrite(ledBoard, LOW);
      if ( (AngleX >= -90) &&  (AngleX <= -80) || ( (AngleX <= 90) && (AngleX >= 80) ) ) {
        myStateVar = startTiming;
      }
      break;

    case startTiming:
      StartWaiting = millis();
      myStateVar = check5SecondsOver;
      break;

    case check5SecondsOver:
      if ( (AngleX > -80) || (AngleX < 80) ) {
        myStateVar = CheckAngle;
        break;
      }

      if ( TimePeriodIsOver(StartWaiting, waitingTime) ) {
        myStateVar = activateAlarm;
      }
      break;

    case activateAlarm:
      digitalWrite(ledPin, HIGH);
      digitalWrite(ledBoard, HIGH);
      myStateVar = waitForAlarmDeactivating;


    case waitForAlarmDeactivating:
      if ( digitalRead(deactivate_Pin == HIGH) ) {
        myStateVar = CheckAngle ;
      }
  }
} //void myStateMachine()


void loop() {
  BlinkHeartBeatLED(OnBoard_LED,250);

  myStateMachine();
}

I don't know how dangerous it is if "the horse laying down" is not discovered for an hour or even longer.
I assume you love the horse. And this should make you think how much is it worth to me to make the monitoring as reliable as possible. Maybe even buying a professional device for a lot of money for this purpose.

best regards Stefan

Thank for the explaination, I understand it now. It is a good idea to build in such a security.

For the horse it is no problem when it is laying down. Some horses sleeps standing and some lay down are partly lay down. It is to give an alarm when the horse is about to give birth. such devices excist, I own one. But I don't like the way it works, it is outdated. It uses GSM and it is complicated to set it up and reset after an alarm. I want to use wifi. the whole wifi part and calling and alarming is working with other sensors, but the pmu6050 in combination with my bad coding skills results in a not working alarm.

Edit:
I tried to understand your code but I don't know what it means and how it works. The part above `byte myStateVar; like the #define with myFixedText and variableName.

`
What is the byte MyStateVar? It checks the angle, but CheckAngle is in a byte 0? What does it mean, is the just the name of the first case?

What is the priority of the switch and case? It starts with the CheckAngle and the if inside. When condition is met, the timer starts and it goes to case startTiming:, does it also goes to that case and the others, when the condition in the case CheckAngle is not met?

Kind regards,
Danny

I notice that the timer never hits the threshold. The start time is updated and the difference between startwaiting and millis() remains the same.
if ( TimePeriodIsOver(StartWaiting, waitTime) ) {

Is never executed. Why is that?

Kind regards,
Danny

very good observation. You are a analysing the code thoroughly.
It really works. This is one of the somehow advanced options in coding with parameters

The timervariable gets updated through the "&"-symbol in front of the parameter in the functions definition

boolean TimePeriodIsOver (unsigned long &periodStartTime,

boolean TimePeriodIsOver (unsigned long & periodStartTime,

This "&" tells the compiler to give back the value of variable "periodStartTime" to the variable the function was called with.

This means this line of code

if ( TimePeriodIsOver(StartWaiting, waitingTime) )

uses the variable named "StartWaiting" and the value of this variable "StartWaiting" gets updated with the value of variable "periodStartTime"

the variable "periodStartTime" is updated inside the function "TimePeriodIsOver" in this line

periodStartTime = currentMillis;

and the new value is handed back to variable "StartWaiting" after finishing the execution of function "TimePeriodIsOver"
This mechanism automatically updates the variable "StartWaiting" with just a single line of code

best regards Stefan

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