Detecting value changes

Status
Not open for further replies.

raflyer

Well-known member
I know this is a silly question but I have attempted 10 times writing code to make this work and nothing has so here goes.

I need to detect a change in data and what direction it changed and turn one output on or off depending whether it decrease or different output if it increased.

Exact application,
X-plane 10 using Teensy 3.1, I need to control 2 relay's via 2 outputs. Reading the elevator trim dataref which is a Float value, read it, If it increases in value. Turn on relay a. If it decreases in value, turn on relay b It may var by only .02 but I need to read that. Most of the time it is steady.

I'm doing this to turn on the motor that that drives my Trim wheels for UP or Down Nose trim in my Sim.

I have programmed much harder stuff in this sim so this has got me frustrated.

Thanks for any help
Rob
 
Last edited:
I know this is a silly question but I have attempted 10 times writing code to make this work and nothing has so here goes.

I need to detect a change in data and what direction it changed and turn an output on or off depending whether it decrease or increased.

Exact application,
X-plane 10 using Teensy 3.1, I need to control 2 relay's via 2 outputs. Reading the elevator trim dataref which is a Float value, read it, If it increases in value. Turn on relay a. If it decreases in value, turn on relay b It may var by only .02 but I need to read that. Most of the time it is steady.

I'm doing this to turn on the motor that that drives my Trim wheels for UP or Down Nose trim in my Sim.

I have programmed much harder stuff in this sim so this has got me frustrated.

Thanks for any help
Rob

Code:
/untested:
int val;
int oldval = -1;
....
//in your loop:
if (val != oldval ) // has changed
 {
  if (val<oldval) 
   { //new value < old value
     //turn relay on
   } else { //new value > old value
    //turn relay off
   }
  oldval = val;
 }
 
Unfortunately the value range is from -1 to 1 , down to the 3rd decimal place hence the Float value. I tried what you have above except it was
float val;
float tempval = -0.02;
the rest just like your example.

No joy on that sketch

Example, trim is reading 0.0964554 Hit trim button and value changed to 0.093453 etc It doesn't move much.
 
Last edited:
Unfortunately the value range is from -1 to 1 , down to the 3rd decimal place hence the Float value. I tried what you have above except it was
float val;
float tempval = -0.02;
the rest just like your example.

No joy on that sketch

Example, trim is reading 0.0964554 Hit trim button and value changed to 0.093453 etc It doesn't move much.

multiply by, say 1000 (if min step size is 1e-3) and work with integer as in Frank's example.
for step size of 2e-3 multiply by 500, and so on
logic may be little more complicated if you wanted to detect decrease and increase of number
 
Ok here is the code snippet. I am using 2 relays. One turns the motor left, the other turns it right. So i'm trying to get this, if elevTrim is increasing turn on relay 1, if it is decreasing turn on relay 2 If it is not moving both should be off The 2 relays can not be on at the same time. the canlight is there so the relays can only be turned on if the flightsim is running and we have system voltage present. The code below doesnt work. Both relay's are on all the time.
Also, the data coming in elevTrim is a 0.123456 length input so to change it to an integer we need to * 1000000 correct.


Code:
val = elevTrim * 1000;
   if (val != oldval) {
    if (val > oldval && canLight) {
      digitalWrite(7, LOW);
    } else if (val == oldval) {
      digitalWrite(7, HIGH);
    }
    if (val != oldval){
    if (val < oldval && canLight) {
      digitalWrite(8, LOW);
    } else if (val == oldval) {
     digitalWrite(8 , HIGH);
    }
   }
  }
 
Last edited:
What if there is a very slow change ?

When does the relay/motor turn off?

The change is always at least .02 difference if there is trim movement. I want the motor to do nothing if the value isn't moving.
 
Something like this ? But the motors will turn off almost immediately, there is nothing to control how much the motors will run.

Code:
val = elevTrim * 1000;
  if ((val != oldval) && canLight {
    if ( val > oldval ) {
      digitalWrite(7, LOW);
    } 
    if ( val < oldval) {
      digitalWrite(8, LOW);
    }
  }
  else {
      digitalWrite(7, HIGH);
      digitalWrite(8, HIGH);
  }
  oldval = val;
 
maybe (val > oldval+19) (since you multiplied by 1000 and you said you want 0.02 delta)
and (val < oldval -19)
and put the oldval=val inside both inner if.
so the value can fluctuate between +/-19 and the motor will not move.
 
Finally got it to work. Problem is that with the delay, my other parts of the code are being missed. The key part was the delay. It needs it or it misses the change. The Teensy is so stinking fast that the values were always the same before the relay and motor had time to react. Here is the code.
Code:
val = elevTrim * 100;
  
  if ((val != oldval && canLight) && (digitalRead(0) == HIGH)) {
    if (val > oldval) {
      digitalWrite(7, LOW);
    }  if (val < oldval) {
      digitalWrite(8, LOW);
    }
  }

  else {
    digitalWrite(7, HIGH);
    digitalWrite(8, HIGH);
  }
  delay(200);

  oldval = val;
 
Last edited:
Finally got it to work. Problem is that with the delay, my other parts of the code are being missed. The key part was the delay. It needs it or it misses the change. The Teensy is so stinking fast that the values were always the same before the relay and motor had time to react. Here is the code.
Code:
val = elevTrim * 100;
  
  if ((val != oldval && canLight) && (digitalRead(0) == HIGH)) {
    if (val > oldval) {
      digitalWrite(7, LOW);
    }  if (val < oldval) {
      digitalWrite(8, LOW);
    }
  }

  else {
    digitalWrite(7, HIGH);
    digitalWrite(8, HIGH);
  }
  delay(200);

  oldval = val;

If your value was increasing, then changed direction and decreased, without ever having been steady in between, wouldn't this set both pin 7 and pin 8 LOW?

Also, this would detect any change in val of more than .01, no? (because it's muliplying by 100 then truncating to an integer. aren't you looking for changes of more than .02? or does that not matter?
 
If your value was increasing, then changed direction and decreased, without ever having been steady in between, wouldn't this set both pin 7 and pin 8 LOW?

Also, this would detect any change in val of more than .01, no? (because it's muliplying by 100 then truncating to an integer. aren't you looking for changes of more than .02? or does that not matter?

Yes, I want it to change when the difference has been .02 or 2 after integer and trunc. You are correct that it moves for every 1 now. Not sure how to get to 2

For the other issue you mentioned. Haven't run into that yet but it shouldn't be an issue as normally there should not be an immediate reversal.

So now i'm at this point. Only want the relay's to activate on a difference of 2 after the conversion or .02 before. Some how make it work w/o the need for the delay as it has added some delays in other part of my code. Full code below just for reference.

Code:
#include <Bounce.h>

float map1 (float x, const float& x0, const float& x1, const float& y0, const float& y1)
{
  if (x <= x0) return y0;
  if (x >= x1) return y1;

  float gradient = (y1 - y0) / (x1 - x0);
  return y0 + (x - x0) * gradient;
}

const int potPin1 = A0; // Throttle 1 Lever
const int potPin2 = A1; // Throttle 2 Lever
const int potPinS = A2; // Spolier Lever
const int potPinF = A3; // Flaps Lever
const int potPinT = A4; // Trim Pot
const int parkSW  = 4;


int revsolenoid;
int val;
int oldval = -1;
FlightSimFloat throttle1;
FlightSimFloat throttle2;
FlightSimFloat spoiler;
FlightSimFloat flapLever;
FlightSimFloat fuelCut1;
FlightSimFloat fuelCut2;
FlightSimFloat simpark;
FlightSimFloat asp;
FlightSimFloat elevTrim;
FlightSimFloat parkBrake;  //Handle position
FlightSimFloat leftBrake;
FlightSimInteger reverse1;
FlightSimInteger reverse2;
FlightSimInteger wow;
FlightSimFloat supplyVolts;

Bounce spoilerArmSW = Bounce (5, 5);

void setup() {
  Serial.begin(9600);
  pinMode (12, OUTPUT); //Speed Brake Armed Light Relay 10
  pinMode (6, OUTPUT); //Park Release Solenoid
  pinMode (11, OUTPUT); //Park Brake Lamp
  pinMode (10, OUTPUT); //Spoiler Lever motor
  pinMode (9, OUTPUT); // Reverse Solenoid Lock out
  pinMode (8, OUTPUT); //Trim Whl Mtr UP
  pinMode (7, OUTPUT); // Trom Whl Mtr Down
  pinMode (parkSW, INPUT_PULLUP);
  pinMode (5, INPUT_PULLUP); //spoiler arm SW
  pinMode (2, INPUT_PULLUP); //FuelCut1
  pinMode (1, INPUT_PULLUP); //FuelCut2
  pinMode (0, INPUT_PULLUP); //Stab Trim motor cutout

  throttle1  = XPlaneRef("sim/cockpit2/engine/actuators/throttle_ratio[0]");
  throttle2  = XPlaneRef("sim/cockpit2/engine/actuators/throttle_ratio[1]");
  spoiler    = XPlaneRef("sim/cockpit2/controls/speedbrake_ratio");
  flapLever  = XPlaneRef("sim/cockpit2/controls/flap_ratio");
  reverse1   = XPlaneRef("sim/cockpit2/engine/actuators/prop_mode[0]");
  reverse2   = XPlaneRef("sim/cockpit2/engine/actuators/prop_mode[1]");
  wow        = XPlaneRef("sim/flightmodel/failures/onground_any"); //weight on wheels sw
  asp        = XPlaneRef("sim/cockpit2/gauges/indicators/airspeed_kts_pilot");
  elevTrim   = XPlaneRef("sim/cockpit2/controls/elevator_trim");
  parkBrake  = XPlaneRef("FJS/732/FltControls/ParkBrakeHandle");
  simpark    = XPlaneRef("sim/cockpit2/controls/parking_brake_ratio");
  fuelCut1   = XPlaneRef("sim/cockpit2/engine/actuators/mixture_ratio[0]");
  fuelCut2   = XPlaneRef("sim/cockpit2/engine/actuators/mixture_ratio[1]");
  leftBrake  = XPlaneRef("sim/cockpit2/controls/left_brake_ratio");
  supplyVolts = XPlaneRef("sim/cockpit2/electrical/bus_volts[0]");
}

void loop() {
  FlightSim.update();
  analogReadResolution(12);
  bool canLight = (supplyVolts > 10.0) && FlightSim.isEnabled();

  //  float analog1 = analogRead(potPin1);
  //  throttle1 = map1(analog1, 440, 2050, 1.0, 0.0);

  //  float analog2 = analogRead(potPin2);
  //  throttle2 = map1(analog2, 480, 2098, 1.0, 0.0);

  float analogrev = analogRead(potPin1);
  if (analogrev > 2080) {
    reverse1 = 3;
    throttle1 = map1(analogrev, 2080, 3200, 0.0, 1.0);
  }
  else if (analogrev < 2080) {
    reverse1 = 1;
    throttle1 = map1(analogrev, 440, 2050, 1.0, 0.0);
  }

  float analogrev2 = analogRead(potPin2);
  if (analogrev2 > 2110) {
    reverse2 = 3;
    throttle2 = map1(analogrev2, 2110, 3200, 0.0, 1.0);
  }
  else if (analogrev2 < 2110) {
    reverse2 = 1;
    throttle2 = map1(analogrev2, 480, 2098, 1.0, 0.0);
  }

  //Read Spoiler Lever Position  Armed position = -0.500
  float analogS = analogRead(potPinS);
  spoiler = map1(analogS, 850, 3120, 0.0, 1.0);

  //Read Trim Potsition
  // float analogT = analogRead(potPinT);
  // elevTrim = map1(analogT, 300, 1353, -0.2, 0.32);
  //    Serial.println(analogT);
  //    Serial.println(elevTrim);

  //Trim Wheel Motor commands

 val = elevTrim * 100;
  
  if ((val != oldval && canLight) && (digitalRead(0) == HIGH)) {
    if (val > oldval) {
      digitalWrite(7, LOW);
    }  if (val < oldval) {
      digitalWrite(8, LOW);
    }
  }

  else {
    digitalWrite(7, HIGH);
    digitalWrite(8, HIGH);
  }
  delay(300);

  oldval = val;



  //Read Flap Lever position
  float analogF = analogRead(potPinF);
  if (analogF < 100) {
    flapLever = 0.0;
  }
  if (analogF >= 150 && analogF <= 300) {
    flapLever = 0.125;
  }
  if (analogF >= 630 && analogF <= 700) {
    flapLever = 0.250;
  }

  if (analogF >= 780 && analogF <= 920) {
    flapLever = 0.375;
  }
  if (analogF >= 1140 && analogF <= 1200) {
    flapLever = 0.500;
  }

  if (analogF >= 1300 && analogF <= 1450) {
    flapLever = 0.625;
  }

  if (analogF >= 1550 && analogF <= 1680) {
    flapLever = 0.750;
  }

  if (analogF >= 1880 && analogF <= 2180) {
    flapLever = 0.875;
  }

  if (analogF >= 2290 && analogF <= 2700) {
    flapLever = 1.0;
  }

  if (asp > 40 && wow == 1 && flapLever > .700)
  {
    digitalWrite(9, LOW);
  }
  else
  {
    digitalWrite(9, HIGH);
  }

  if  (digitalRead(4) == LOW) {
    parkBrake = 1.0;
    simpark = 1.0;
    digitalWrite(11, LOW);
  }
  else {
    parkBrake = 0.0;
    simpark = 0.0;
    digitalWrite(11, HIGH);
  }

  // Spoiler Lever auto deploy Logic
  if (digitalRead(5) == LOW && spoiler < 0.8 && wow == 1 && flapLever > .700) {
    digitalWrite(10, LOW);
  }
  else {
    if (spoiler > 0.9);
    digitalWrite(10, HIGH);
  }

  if (digitalRead(5) == LOW && spoiler < 0.8 && wow == 0 && flapLever > 0.100) {
    digitalWrite(12, LOW);
  }
  else {
    digitalWrite(12, HIGH);
  }

  if (digitalRead(1) == LOW) {
    fuelCut2 = 0.0;
  }
  else {
    fuelCut2 = 1.0;
  }
  if (digitalRead(2) == LOW) {
    fuelCut1 = 0.0;
  }
  else {
    fuelCut1 = 1.0;
  }

  // activate park brake release solenoid
  if (leftBrake > 0.7 && parkBrake > 0.7) {
    digitalWrite(6, LOW);
  }
  else {
    digitalWrite(6, HIGH);
  }
}
 
Another thought, would this be easier to accomplish using an H-Bridge? Reason I ask is that have one available and eventually I need to change the speed of the motor depending some other parameters. Maybe it can react quicker than the relays?
 
Any delay() in code means the Teensy is offline for that duration, there are ways to avoid them. Using interrupt driven detection and timers and other library elements you can find on PJRC.com - the forum - or with a web search.

> http://www.pjrc.com/teensy/td_timing.html
> https://www.pjrc.com/teensy/td_timing_elaspedMillis.html

Likely need to debounce the input data you see or events will go undetected.

Unless relays are solid state they could soon reach their lifetime if they are fired rapidly.
 
Deciding when to start motors, without making a clear decision how long to run them, depending on the sensor in put will never work.

Its not a coding question really, its about deciding clear and useful application logic. Once that is done,code can be written to implement the logic, but at the present the OP seems to have no logic decided for how long to run the motors depending upon the size of the change in input signal.

Until that us done the coding will stay a mess.
 
Deciding when to start motors, without making a clear decision how long to run them, depending on the sensor in put will never work.

Its not a coding question really, its about deciding clear and useful application logic. Once that is done,code can be written to implement the logic, but at the present the OP seems to have no logic decided for how long to run the motors depending upon the size of the change in input signal.

Until that us done the coding will stay a mess.

I beg to differ, It's hard to explain aviation tech and arduino code all in the same conversation. Think of it this way. Tap a push button one time fast. (same as hitting my Elevator Trim button one time) the value will change on the data from the sim usually by .02 So say from 0.0912345 to .1132456 I want to catch the momentary change because 'most' of the time thats all there will be. It's called trimming the airplane So if i give one 'click' as us pilots call it of up trim, i want the motorized trim wheel to move as well and only for split second. Now if I hold the trim button for a more serious adjustment of trim then the motor will run the trim wheels a longer period. It is never a set time period as sometimes we 'bump or click' the trim and sometimes we have to hold it for a sec or 2 or 3. I hope this makes sense as I said it is hard to explain to non flying people. Sort of like tappign your brake pedal sometimes and sometimes stepping on it longer. So, here I go again,
Step 1, Elevator Trim Data from x-plane has a range of -1 to +1
Step 2, Normal short Trim adjustments usually only change that value by .02, More extreme case would be a 1 to 3sec hold of trim button which changes value more considerably.
Step 3, Goal is to catch these changes and activate appropriate relay to drive a Trim wheel that mimics a real planes action.
Step 4, Verify code does not stall out rest of sketch.
 
mlu may be more right than you see ... Without a controlled piece of software well coded only by accident/luck will you get the right results - and then adding to that something else will never work - or prior work will regress.

Teensy is awesome and powerful - the library tools I hinted at and linked in p#18 will open up a controlled foray into the power you have in your hands. There are IDE :: Files / Examples you can work with that will give constantly looping code with good hardware support to give accurate and reliable control allowing differentiation of short or long duration events with good feedback. Using a delay() generally brings things to a halt, which can be okay in the short term - but rarely.
 
mlu may be more right than you see ... Without a controlled piece of software well coded only by accident/luck will you get the right results - and then adding to that something else will never work - or prior work will regress.

Teensy is awesome and powerful - the library tools I hinted at and linked in p#18 will open up a controlled foray into the power you have in your hands. There are IDE :: Files / Examples you can work with that will give constantly looping code with good hardware support to give accurate and reliable control allowing differentiation of short or long duration events with good feedback. Using a delay() generally brings things to a halt, which can be okay in the short term - but rarely.

I totally agree about the use of delay. Below is a code I wrote that drives a pair of real aircraft Fuel Flow gauges using the Millis timer They need to se a pulsing signal and this is how I created it. Works great. Just didn't think I would need it for the trim wheels but now I will adapt it.
Code:
const int startPin = 2;
const int stopPin = 22;
const int start2Pin = 1;
const int stop2Pin = 23;

int startState = LOW; // Start Input
int stopState = HIGH; //Stop Input
int start2State = LOW; // Start Input
int stop2State = HIGH; //Stop Input
long previousMillis = 0;
long previousMillis2 = 0;
long previousMillis3 = 0;
long previousMillis4 = 0;


unsigned long interval1 = 0; 
unsigned long interval2 = 0;
FlightSimFloat fuelflow;
FlightSimFloat fuelflow2;
float calcff;
float calcff2;

void setup() {
  // set the digital pin as output:
  pinMode(stopPin, OUTPUT);
  pinMode(startPin, OUTPUT);
  pinMode(stop2Pin, OUTPUT);
  pinMode(start2Pin, OUTPUT);
  fuelflow  = XPlaneRef("sim/cockpit2/engine/indicators/fuel_flow_kg_sec[0]");
  fuelflow2 = XPlaneRef("sim/cockpit2/engine/indicators/fuel_flow_kg_sec[1]");
}

void loop()
{
  FlightSim.update();

  calcff = fuelflow * 7936; //converts kg/sec to PoundsPerHr
  interval1 = calcff / 80;  //scales to FF gauge

  unsigned long currentMillis = millis();
  unsigned long currentMillis2 = millis();

  if (currentMillis - previousMillis > interval1) { 
    previousMillis = currentMillis;

    if (startState == LOW)
      startState = HIGH;
    else
      startState = LOW;
    digitalWrite(startPin, startState);
  }

  if (currentMillis2 - previousMillis2 > interval1) {

    previousMillis2 = currentMillis2;

    if (stopState == LOW)
      stopState = HIGH;
    else
      stopState = LOW;
    digitalWrite(stopPin, stopState);
  }
  //test section below
 
    calcff2 = fuelflow2 * 7936; //converts kg/sec to PoundsPerHr
    interval2 = calcff2 / 80;  //scales to FF gauge

    unsigned long currentMillis3 = millis();
    unsigned long currentMillis4 = millis();
    
    if (currentMillis3 - previousMillis3 > interval2) { 
      previousMillis3 = currentMillis3;

      if (start2State == LOW)
        start2State = HIGH;
      else
        start2State = LOW;
      digitalWrite(start2Pin, start2State);
    }

    if (currentMillis4 - previousMillis4 > interval2) {

      previousMillis4 = currentMillis4;

      if (stop2State == LOW)
        stop2State = HIGH;
      else
        stop2State = LOW;
      digitalWrite(stop2Pin, stop2State);
    }
  
}
 
Last edited:
If you expand this, you might consider using elapsedMillis variables. Internally they use millis() and subtract. If you're ok with the concept of an elapsedMillis variable that automatically increments, using them can really simplify your code. Usually simpler is better.
 
Yes, I want it to change when the difference has been .02 or 2 after integer and trunc. You are correct that it moves for every 1 now. Not sure how to get to 2

For the other issue you mentioned. Haven't run into that yet but it shouldn't be an issue as normally there should not be an immediate reversal.

So now i'm at this point. Only want the relay's to activate on a difference of 2 after the conversion or .02 before. Some how make it work w/o the need for the delay as it has added some delays in other part of my code. Full code below just for reference.


Code:
define a global variable  unsigned long lasttrimchange = 0

val = elevTrim * 100;

if(millis() - lasttrimchange  > 200)
{
  if (((abs(val - oldval) >= 2) && canLight) && (digitalRead(0) == HIGH)) {
    if (val > oldval) {
      digitalWrite(7, LOW);
      digitalWrite(8, HIGH);
    }  if (val < oldval) {
      digitalWrite(8, LOW);
      digitalWrite(7, HIGH);
    }
  }
  else {
    digitalWrite(7, HIGH);
    digitalWrite(8, HIGH);
  }
  lasttrimchange = millis();
  oldval = val;
}

This would do:

1. change detected only if elevTrim changes .02 or greater
2. Make it so pin 7 and pin 8 can't be low at the same time
3. get rid of delay (this basically does manually what ellapsedmillis would do)

If I'm understanding what's going on here correctly (big IF!!), the delay, or the length of time in the "millis - lasttrimchange" is effectively the length of time the appropriate pin is held low to make the trim change. It probably was working without the delay but it was only holding the pin low for the length of time for one complete pass through the code, (elevTrim changes, pin held LOW, next pass through elevTrim has not changed, pin set back to HIGH) which the teensy probalby does in less than a millisecond.

I'm a little fuzzy on my understanding but combining the rate at which it checks for a change and the length of time it executes that change could cause for some "lag" in it's response that might not be desireable. 200 milliseconds might be noticeable to a human.

You could sepparate the "check for change" and the "execute the change" by haveing it constantly check for the change and only set the time for executing. Something like:

define a global variable unsigned long trimstart = 0

val = elevTrim * 100;


Code:
  if (((abs(val - oldval) >= 2) && canLight) && (digitalRead(0) == HIGH)) {
    trimstart = millis()
    if (millis - trimstart <200)
    {
    if (val > oldval) {
      digitalWrite(7, LOW);
      digitalWrite(8, HIGH);
    }  if (val < oldval) {
      digitalWrite(8, LOW);
      digitalWrite(7, HIGH);
    }
    }
  }
  else {
    digitalWrite(7, HIGH);
    digitalWrite(8, HIGH);
    oldval = val;
  }
 
Last edited:
Status
Not open for further replies.
Back
Top