I picked up an Arduino for Christmas, and have worked through a number of tutorials online. I'm now trying to program my first "real world" interaction.
I have a Lego train motor set up on a circular track. A light sensor detects the presence of the train and pauses the motor for a few seconds before restarting. Here's the code:
int ENA=13; //train PWM pin
int LDR = 0; //light sensor pin
int LDRValue = 0;
int light_sensitivity = 500; //intermediary light sensor value (light is around 700, dark is 0)
unsigned long Timer
void setup()
{
Serial.begin(9600);
pinMode(13, OUTPUT);
}
void loop()
{
LDRValue = analogRead(LDR); //reads the ldr's value through LDFRR which we have set to Analog inut 0 "A0"
Serial.println(LDRValue); //prints the LDR values to seial monitor
delay(50); //This is the speed at which LDR sends value to Arduino
if (LDRValue > light_sensitivity)
{
analogWrite(13, 0); //stop train motor
analogRead(LDR) OFF; //stop reading values from light sensor
delay(3000);
analogWrite(13, 90); //force train motor to 35% speed
delay(3000);
}
else
{
analogWrite(13, 120); //drive train motor at 50% speed
analogRead(LDR) ON; //read values from light sensor
}
}
The issue is that, of course, once the delay finishes, the train will still be in front of the sensor. Rather than pause the motor, I'd like to delay the sensor reading so that the train can move forward.
I know "delay" is not good for this and that I should be using "millis" instead, but I'm not sure how to mix this with analogRead OFF. Can anyone give me some hints?
Just before you start the train again save the value of millis() then each time through loop() only read the LDR if millis() at that time minus the train start time is greater than an interval that will have allowed the train to have moved out of range of the sensor.
or better
Stop the train when the LDR value BECOMES greater than light_sensitivity not when is IS greater than light_sensitivity. Look at the StateChangeDetection example in the IDE to see how it is done.
Grrrmachine:
<...>
I know "delay" is not good for this and that I should be using "millis" instead, but I'm not sure how to mix this with analogRead OFF. Can anyone give me some hints?
Delay() is not a "bad" thing, like any tool, you must use it correctly! So, when developing code, use it when you trying to generalize a problem; in this case, delay so many milliseconds so the full length of the train has moved forward before the sensor is again active.
When you get the above code to work, then implement using millis() and t-delta since you know the original logic worked and you have an idea of how many milliseconds will be required for the train to move forward (hint... get this experimentally by mucking with delay().)
Now, seasoned programmers will flip when they read I suggested you utilize delay() but i am only suggesting you use it prudently and then remove it later.
Ray
Ok, I've managed to incorporate ONE milli, using the following code:
int IN1=8; //train motor pin 1
int IN2=9; //train motor pin 2
int ENA=10; //train motor PWM
int LDR = 0; //
int LDRValue = 0;
int light_sensitivity = 500; //set switching point for light sensor (LDR)
long previousMillis = 0; //will store last time light sensor updated
long interval = 3000; //pause period
void setup()
{
Serial.begin(9600);
pinMode(10, OUTPUT);
}
void loop()
{
LDRValue = analogRead(LDR); //reads the LDR's value through LDFRR which we have set to Analog inut 0 "A0"
Serial.println(LDRValue); //prints the LDR values to seial monitor
delay(50); //speed at which LDR sends value to Arduino
digitalWrite(IN1,LOW);// rotate forward
digitalWrite(IN2,HIGH);
unsigned long currentMillis = millis();
if (LDRValue > light_sensitivity)
{
analogWrite(10, 0); //power off train
if(currentMillis - previousMillis > interval)
{
previousMillis = currentMillis; // save the last time LDR was read
analogWrite(10, 120); //power on train
}
}
else
{
analogWrite(10, 120); //power on train
}
}
However, after the interval, the train motor activates and immediately shuts off again because it's still on the sensor. I know I need to add a second variable ("previousOn"), but I'm not sure how to add this to the existing code.
Some comments:
analogWrite(10, 0);
Why use the, hard to remember, pinnumber instead of the, self explaining, name for it => ENA
delay(50); //speed at which LDR sends value to Arduino
It delays the hole loop. And I can tell you, all the other code in the loop is way faster then the 50ms delay. 50ms isn't that long so in this case you're fine. But delaying a loop isn't a good idea, most of the time you like a fast loop. It's like buying a fast car, drive around a circuit in 10 seconds and then wait 2 minutes and say your laptime is 2:10 
Use unsigned long vars to save a millis().
Do a pinMode() for all output pins!
Make pin variables a const (from constant = you can not change them in the loop) (and start them with a Capital). Normal variables should not start with a Capital so you know which vars you can change and which not.
Something I wrote for it:
//Pins
const int In1=8; //train motor pin 1
const int In2=9; //train motor pin 2
const int EnA=10; //train motor PWM
const int Ldr = 0; //LDR pin
//Vars
int ldrValue = 0; //save the LDR val of the last pass
unsigned long previousMillis = 0; //will store last time light sensor updated
//Settings
int interval = 3000; //pause period
int lightSensitivity = 500; //set switching point for light sensor (LDR)
void setup()
{
Serial.begin(9600);
pinMode(EnA, OUTPUT);
pinMode(In1, OUTPUT);
pinMode(In2, OUTPUT);
//Placed it here because it doesn't change (or I give it a default direction)
digitalWrite(In1,LOW);// rotate forward
digitalWrite(In2,HIGH);
}
void loop()
{
int ldrValNow;
Serial.println(ldrValNow); //prints the LDR values to seial monitor
delay(50); //speed at which LDR sends value to Arduino
ldrValNow = analogRead(LDR); //reads the LDR's value
if(ldrValNow > lightSensitivity){ //train in front of sensor
if(lrdValue <= lightSensitivity){ //but last time the train wasn't in front (so this is the transition between in front and not in front)
analogWrite(EnA, 0); //only then power off train
previousMillis = millis(); //and save the time
}
else if(millis() - previousMillis > interval){ //Time to start the train again?
analogWrite(EnA, 120);
}
}
else{ //Not in front, alse drive!
analogWrite(EnA, 120);
}
ldrValue = ldrValNow; //save for the next time so we can detect a transition
}
I try to find the moment the train comes in front of the LDR so the transition and only stop the train (and save the time) if I see the transition. I do nothing as long as the train is in front of the sensor and the time has not elapsed. Restart the train if it's in front of the sensor and time HAS elapsed.
Good luck!
septillion, your code worked perfectly (after a few spellchecks). I'm really very grateful!
Thank you also for your other tips about laying out the code. I'm very much a beginner here, so I really appreciate having a good example to analyse so that I can improve my own work.
Thanks again to everyone who helped.
Grrrmachine:
(after a few spellchecks)
Sorry for that! I didn't compile it (or checked for spelling at all (A) ) Great I could help 