Timing end of sensor input (or other solutions)

Hi everybody.
I've come to enjoy this forum, but only as a reader until now. But here's a code hurdle I just can't figure out (might be I've just stared too long at the code to think clearly).

Basically, what I'm doing is building a distributed system of 16 units, each with a minimal arduino (based on the uDuino over at instructables with power supply as shown in ITP's breadboard arduino), an LED as output and an LDR as input.

What I want is to let every unit have three different states:

  1. Normal: as long as you get input often enough (as in the neighbouring unit is in normal state) you keep blinking normally.
  2. Agitation: If the input for some reason is not coming often enough (as in someone or something blocks the light from the neighbour) you start to blink more and more rapidly "trying to reconnect".
  3. Mimic: If the input is coming too rapidly (as in the neighbour is in the agitated state) you start getting influenced and start blinking rapidly as well.

I hope that makes sense.

I've managed to get the two first states working satisfactorily, but continue to struggle with the last one.

Here's the code for the first two states:

int motorPin = 7;
int LDRpin = 0;
int LEDpin = 13;
long lastLDR = 0;
int val = 0;
int threshold = 100;
int lastLED = 0;
int stress = 4000;
int mimic = 1000;

int value1 = LOW;                // previous value of the LED
long time1 = millis();
long time2 = millis();

long interval1 = 3000;           // interval at which to blink (milliseconds)
long interval2 = 500;


void setup()                    
{
  Serial.begin(9600);
  pinMode(LEDpin, OUTPUT);
  pinMode(motorPin, OUTPUT);
}

void loop()                    
{
  unsigned long now = millis();
  val = analogRead (LDRpin);
  if (val > threshold){
    lastLDR = millis();
  }

  // ** defines the timing used to turn LED on and off, based on LDR input  
  if (now - lastLDR < stress && now - lastLDR > mimic){
    interval1 = 3000;
    interval2 = 500;
  } 
  else if (now - lastLDR > stress){
    if (interval1 > 250){
      interval1 = interval1 - 0.001;
    }
    if (interval2 > 30){
      interval2 = interval2 - 0.005;
    }
  }

//  Serial.println (now - lastLDR);

  //if (val > threshold){
//    Serial.println (val);
  //}



  // ** makes the LED blink according to timing
  if (now - time1 > interval1){
    time1 = now;
    digitalWrite (LEDpin, HIGH);
  } 
  else if (now - time1 > interval2 && now - time1 < interval1){
    digitalWrite (LEDpin, LOW);
  }

}

My first solution was to do something like this:

  if (now - lastLDR < mimic){
    interval1 = now - lastLDR;
    interval2 = 100;
  }

But the problem is that lastLDR updates as long as it gets input over the threshold, and the input lasts for up to 500ms, thus returning 0 and occasionally 4294967295. This upsets the behaviour.

So, any suggestions?

I would appreciate any help!

Cheers,
Kjetil.

Overall, it looks plausible.

This is a bit suspicious:

  else if (now - lastLDR > stress){
    if (interval1 > 250){
      interval1 = interval1 - 0.001;
    }
    if (interval2 > 30){
      interval2 = interval2 - 0.005;
    }
  }

The declarations

long interval1 = 3000;
long interval2 = 500;

say that interval1 and interval2 can only hold a whole number.

The rules of the C language applied to this are: turn the long into a real number (with decimal point, a double), subtract 0.0001 from it, then truncate to a whole number that fits into a long (interval1).
For example,
interval1 = 3000 - 0.0001
= 2999.9999 (ish) which becomes
interval1 = 2999 when it is put back into the variable.

As the interval is measure in milliseconds, subtracting a whole number is likely okay.

I don't think I understand the comment about the second part. Would you please try to explain that?

I would add that millis() gives an unsigned long, so now and lastLDR should be unsigned long too. I'd have to think very hard (or someone else might help) to explain what happens when long and unsigned long are mixed. Suffice it to say that they are the same number of bits, but in one case a negative long looks like a huge unsigned long.

HTH
GB

I had a bit more ponder.

I plugged 4294967295 into my Mac's programmers calculator, and looked at the Hexadecimal value (easier for an old codger like me to see bit patterns) and it said 0xFFFFFFFF

That is -1.
4294967295 is -1 stored into a long, which is later treated as an unsigned long.

So that might explain that.

GB

GB, thanks for your swift reply.

I will definitely change now and last LDR to unsigned longs, just in case. And about 4294967295 as -1, that cleared that question up for me. Thanks.

Now, over to the main issue, state 3 - Mimic; I'll try to explain what I'm trying to achieve in more detail.

When defining the interval1 and interval2 for the normal and agitated states, I only work with numbers from 1000ms (int mimic) and upwards. But in the initial mimic code as shown above I ask for anything lower than 1000ms (int mimic) which returns true a lot more than I want, as now - lastLDR returns 0 (and sometimes 4294967295) as long as the input is lasting. The behaviour I'm trying to acchieve is something along the lines of going about you regular business until a threshold is reached (int mimic) when you then start to mimic the input. Does that make any more sense?
(here's a quick pdf with a behavioural "schematic" http://ka-d.net/wp-content/uploads/2010/04/principles_program_behaviour.pdf).

About the decimal trick. The thing is that I wanted a longer fade time for the agitated state, when I used whole numbers it was too fast as I have 2500ms delays between blinks and the chip runs at 16MHz, thus counting down the intervals before the LED blinks again, making it go from regular blinking to frantic blinking in one go. Using decimals, even though they prove to be a bit irregular, still let's the transition from normal behaviour to agitated behaviour take a bit more time...

Please ask again if anything is unclear.

Cheers.
Kjetil.

Okay, I think I understand.
The creature is in three states:

  1. calm - Business as usual
  2. stressed - can't see anything
  3. mimicking

One approach is to exactly mirror this, using a variable and three values:

const int calmState = 0;
const int stessedState = 1;
const int mimicState = 2;
int state = calmState; // have to choose something;

Then the overall shape of the program looks like:

void loop()
{
unsigned long now = millis();
val = analogRead (LDRpin);

if (state == calmState) {
//... stuff looking to change to mimic or stressed
//... set timing for led blink when calm
} else if (state == stessedState) {
//... stuff looking to change to calm or mimic
//... set timing for led blink when stressed
} else if (state == mimicState) {
//... stuff looking to change to calm or stressed
//... set timing for led blink when mimicking
} else { // something odd has happened - go to a known state
state = calmState; // should never happen
}

// ** makes the LED blink according to timing
if (now - time1 > interval1){
time1 = now;
digitalWrite (LEDpin, HIGH);
}
else if (now - time1 > interval2 && now - time1 < interval1){
digitalWrite (LEDpin, LOW);
}

}

Then put the code for each type of behaviour inside the appropriate 'if'.
When it is 'calm' I guess it is looking to see if the conditions have changed so that it should be stressed or a mimic.
Similarly, when it is stressed, it looks for things which would change it to calm or a mimic.
Similarly, mimic looks to become calm or stressed.

(if you are familiar with the switch statement, you could use that instead of the if tests, but IMHO it isn't worth the effort unless you are very comfortable with it).

HTH
GB

WARNING - I have only Verify'ed this code, and not tested it in any way

Thanks again for a swift reply.

Not to sound ungrateful, but I'm quite happy with the code I already have going for the two first states, it's tested and runs fine, my only problem is concerning the last state.

Basically, the problem is that when I want to measure something smaller than (int mimic) it returns something smaller every time: whenever it receives input it starts counting from 0 until next time it receives input. This means that it will start blinking erratically the moment it doesn't answer the conditions of the first two states.

I tried another fix, hardcoding a value and inverting it:

timeVal = (3000 - (now - lastLDR));

but of course this doesn't really help at all, since now, when it receives rapid input the value will always remain between 3000 and 2900.

I'm really sorry if I'm being daft, but shouldn't there be an easy solution to this?

I understand that my initial thought of saying

  • if < 4000 and > 3000 go normal
  • if > 4000 go agitated
  • if < 1000 go mimic
    is faulty, as mimic will always be true part of the time.

Perhaps introduce an extra variable that measure the difference between highest and lowest timeVal?

Something like:
int lowTimeVal = 0;
int highTimeVal = 0;

if (timeVal < lowTimeVal){
lowTimeVal = timeVal;
}
if (timeVal > highTimeVal){
highTimeVal = timeVal;
}

and then using the difference between highTimeVal and lowTimeVal as trigger?
But of course, this is missing something, as I would only get a larger and larger difference...

Argh, it's frustrating almost being there without actually getting there...

Sincere apologies for taking up your time, even if I keep nagging, I am truly grateful for your help.

Cheers,
Kjetil.

May I suggest that

... happy with the code I already have going for the two first states, it's tested and runs fine, my only problem is concerning the last state.

might be misleading.
As long as the third state is broken, it is premature to assume the first two states are fine. The reason the third state is hard to fix may be because of the way the first two work. So it may be worth re-examining the system.

My reading is that the program is trying to keep track of the three states by manipulating values of now, lastLDR, timeVal, lowTimeVal, highTimeVal, ...

You can do that, and likely get it to work.

I'd suggest writing down, on paper, how the different states are made uniquely different, what has happened to get from one to another, and should happen when it is in each state and go from them. State diagrams are very helpful. Create a 'state' circle for each state and write down the conditions to get into the state, and which keep it in the same state. You might spot from the picture what you need to do.

I am sorry if I sound inflexible, but my experience is keeping track of the state of a system explicitly, e.g. using the 'state' variable, makes it straightforward to see how things are working.

This becomes very important if you want to have some form of 'hysteresis'. For example, if you want the state to move from calm to stressed when a value goes above, say, 4000, but you want it to only return to calm when it has come below, say 3500, is easy with explicit states, but may become awkward to program otherwise.

HTH
GB

(I have written a 'creature' which has similar behaviour to the one you describe, but using the LED as both output and sensor, which makes it more complex, so I think I have a useful perspective)

Thank you again (smiling).

I will try to do as you say, and come back with an update later.

Cheers,
Kjetil.

Kjetil - thanks for being okay with my forcefulness.

Something like a state diagram (on paper, no need for fancy software), may be the most useful document about the program that you make. I've had programs with one, and they made them much easier to understand.

State diagrams are helpful to think about adding extra states too.
After all, your creature may want to 'attract a mate' or 'be hungry', or 'sleep', or something ... :slight_smile:

Have fun
GB

GB, I wish to thank you for all your help, my problems (for now) are solved!
I did as you suggested, started to make state diagrams (-ish) on paper. Even though they might not be the most comprehensive diagrams I've ever seen, they enabled me to see things slightly differently, from the outside so to speak.
However, I might have been a bad boy, as the solution I came up with is a rather dirty one.
First, the code:

int motorPin = 7;
int LDRpin = 0;
int LEDpin = 13;
unsigned long lastLDR = 0;
int val = 0;
int threshold = 100;
int stress = 5000;
int mimic = 1500;

int value1 = LOW;                // previous value of the LED
unsigned long time1 = millis();
unsigned long time2 = millis();

long interval1 = 4000;           // interval at which to blink (milliseconds)
long interval2 = 200;

int interLDR = 0;
unsigned long lastInterLDR = 0;

void setup()                    
{
  Serial.begin(9600);
  pinMode(LEDpin, OUTPUT);
  pinMode(motorPin, OUTPUT);
}

void loop()                    
{
  unsigned long now = millis();
  val = analogRead (LDRpin);
  if (val > threshold){
    lastLDR = millis();
  }

if (now - lastLDR == 6 || now - lastLDR == 7 || now - lastLDR == 8){
  interLDR = now - lastInterLDR;
//  Serial.println (interLDR);
  lastInterLDR = now;
}


  // ** defines the timing used to turn LED on and off, based on LDR input  
  //normal state:
  if (now - lastLDR < stress && now - lastLDR > mimic){
    interval1 = 4000;
    interval2 = 200;
  } 
  //agitated state
  else if (now - lastLDR > stress){
    if (interval1 > 250){
      interval1 = interval1 - 0.001;
    }
    if (interval2 > 30){
      interval2 = interval2 - 0.005;
    }
    //mimicking state
  }  else if (interLDR < mimic){
        if (interval1 > 250){
      interval1 = interval1 - 0.001;
    }
    if (interval2 > 30){
      interval2 = interval2 - 0.005;
    }

  }


    Serial.println (now - lastLDR);

  //if (val > threshold){
  //    Serial.println (val);
  //}


  // ** makes the LED blink according to timing
  if (now - time1 > interval1){
    time1 = now;
    digitalWrite (LEDpin, HIGH);
  } 
  else if (now - time1 > interval2 && now - time1 < interval1){
    digitalWrite (LEDpin, LOW);
  }

}

Basically, what I did, was to register the difference in time, not between input in itself (as this was a problem with 0 and 4294967295 values), but between the last value 6,7 or 8 and the next 6, 7 or 8. The Serial.println can't keep up with millis(), so that's why I've coded in all three.

I really wanted to do things properly, using the setup you provided, but my problem is that I have a deadline in exactly three weeks (this is part of my diploma project for my interaction design masters) and I really need to get going with the other things that need to be done (apart from all the theoretical work): tweek the details of behaviour - timing and such; add sound (I have solenoids connected) and tweek that; finish the overall look of the objects; deciding exactly how they are to be placed in the room...

If I do get some extra time on my hands, I will try to rewrite the program to achieve a neater one, but for now, this is how it must be. (My supervisor is already pestering me for not having this done... :wink: )

But again, thank you for being forceful. Even if I didn't (yet) end up with a neat program, I now have a (slightly) better understanding of how to write one, AND by following your suggestions I have come to a solution, even though it is not necessarily one you would recommend. :slight_smile:

Again, thank you!

Kjetil.

... my problems (for now) are solved!

Excellent!

Kjetil - It is much more important that you understand, and it makes sense to you (and your supervisor :))

I like to organise my thoughts in diagrams. I find diagrams are often a useful way to encapsulate and communicate processes in a single 'page', so I'm pleased making it may have been helpful to you.

Have fun
GB