Hello,
I'm a newby when it comes to the arduino and although I have some coding experience, I would never consider myself to be in any way good. In fact, I'm probably terrible!
I have been working on the Arduino for a few days when I get a few minutes. The various tutorials here and on ardx.org are fantastic but of course, I need to stop using the delay in my code. I installed the "milis" library and it looks like it can do the job but I'm missing something.
What I'm trying to do is really simple.
Turn on and off the LED every 1000 milliseconds
Move the servo left every 1500 milliseconds and right the next time the function is called.
Here's what's happening:
The servo is moving left then right very quickly.
This movement coincides with the LED blinking.
Can you suggest where I have gone wrong in the code or should I be doing this differently?
Of course, this isn't a real world problem that I'm trying to solve. It's a learning excersize so I can figure out how to move from using the delay to using a timer. I've spent ages trying to figure this out so I'd really appreciate any help.
code follows:
#include <elapsedMillis.h>
#include <Servo.h>
#define LED_PIN 13
elapsedMillis timer0;
#define interval 1500
int LInterval = 1000; // The length of time the light is on and off for.
int SInterval = 1500; // The interval in mS that the Servo function waits.
boolean ServoMoved = false; // True if Servo is not at position 0
Servo myservo; // create servo object to control a servo
int pos = 0; // variable to store the servo position
void setup() {
pinMode(LED_PIN, OUTPUT); // initialize the digital pin as an output.
Serial.begin(9600); // Start the serial connection with the computer
myservo.attach(9); // attaches the servo on pin 9 to the servo object
}
void loop()
{
if (timer0 >= interval){
Serial.println("In the first if block checking the timer against the interval.");
timer0 = timer0 - timer0;
if (ServoMoved == false){
StartServoL();
}
if (ServoMoved == true){
StartServoR();
}
BlinkLed();
}
if (timer0 == SInterval) {
if (ServoMoved == false){
StartServoL();
}
if (ServoMoved == true){
StartServoR();
}
SInterval = timer0 + 1500; // Sends the SInterval 1500MS into the
// future so the code in this block
// won't be executed again for a while.
}
if (timer0 == LInterval){
Serial.println("In the LED if block");
BlinkLed();
LInterval = timer0 + 1500; // Sends the LInterval 1500MS into the
// future so the code in this block
// won't be executed again for a while
}
}
void StartServoL()
{
Serial.print(ServoMoved); Serial.println (" Value of ServoMoved variable. It should be false. ");
for(pos = 0; pos < 180; pos += 1) // goes from 0 degrees to 180 degrees
{ // in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
ServoMoved = true; // Servo is at 180 degrees.
}
}
void StartServoR()
{
Serial.print(ServoMoved); Serial.println (" Value of ServoMoved variable. It should be true.");
for(pos = 180; pos>=1; pos-=1) // goes from 180 degrees to 0 degrees
{
myservo.write(pos); // tell servo to go to position in variable 'pos'
ServoMoved = false; // Servo is at 0 degrees
}
}
void BlinkLed()
{
int ledPin = digitalRead(LED_PIN); // read the current state and write the opposite
digitalWrite(LED_PIN, !ledPin);
}
First, kudos for not using delay(). It is good for helo world blinky but often a dead end, programming wise. You don't want to call from loop() functions that block for long time, have very quick loop() calls.
It seems that the servo and led timings you want to achieve are independent of each other, which is fine, use two timers.
Do not use == for timers, you can pass it by one tick and miss the event. Use <= <, >, >=. Don't assume that you will do a test on each count.
Beware of printing too long strings. You can overflow the serial output buffer (anybody knows it's size?). If this happen the function will block or you will loose characters.
Here is a proposed outline for the led. The servos are just the same. It's loose code, did not compile it.
// State for the LED logic.
PassiveTimer led_timer()
boolean led_is_on;
void led_setup {
led_is_on = false;
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW)
}
void led_loop() {
// 300 ms on time
// (Intentionally using non 50% blinking to demonstrate the state handling. )
if (led_is_on) {
if (led_timer.time_millis() >= 300) {
led_is_on = false;
digitalWrite(LED_PIN, LOW)
led_timer.restart();
}
return;
}
// 700 ms off time.
if (led_timer.time_millis() >= 700) {
led_is_on = true;
digitalWrite(LED_PIN, HIGH)
led_timer.restart();
}
}
setup() {
led_setup();
// in a similar way, add servo_setup();
}
loop() {
led_loop();
// In a similar way, add servo_loop();
}
zapta:
First, kudos for not using delay(). It is good for helo world blinky but often a dead end, programming wise. You don't want to call from loop() functions that block for long time, have very quick loop() calls.
+1 very nice.
I will add...
Some of the variable names aren't quite as good as they could be... "ServoMoved" doesn't tell that the servo moved, it tells that the servo is not currently centered. "timer0" could be changed also, to indicate what is being measured.
Similar with the function names - BlinkLed doesn't make the LED blink, it toggles the LED.
I love the idea of reading the pin instead of maintaining a state variable for the LED status.
Watch for exclusive conditions - things that can or can't be true if other things are true. For example, in the IF blocks, is it possible for timer0 to equal all three of those other values on the same pass through the loop function? If not, you can use "else if" for a slight performance improvement.
Thanks to you both for the help.
That example got me working with the passive timer library with some tweaking. Here's my code.
Now, I'm having a problem with the servo but I'm going to post about that separately.
#include <passive_timer.h>
#include <Servo.h>
#define LED_PIN 13
Servo myservo; // create servo object to control the servo.
int pos = 0; // variable to store the servo position.
int last_pos = 0; // Variable to store the last position.
int sensorPin = 0; // the analog pin the TMP36's Vout (sense) pin is connected to
PassiveTimer led_timer; // Tracks the Led time.
PassiveTimer servo_timer; // tracks the servo time
PassiveTimer temperature_timer; // tracks the temperature test time
boolean servo_has_moved; // Used for servo timer logic.
boolean led_is_on; // Used for LED timer logic.
boolean room_is_cold; // Indicates if temperature levels have reached cold. .
void led_setup (){
led_is_on = false;
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
}
void servo_setup (){
servo_has_moved = false;
myservo.attach(9); // attaches the servo on pin 9 to the servo object
}
void temperature_loop (){
if (temperature_timer.time_millis() >= 3000) {
int reading = analogRead(sensorPin); // Getting the voltage reading from the temperature sensor
float voltage = reading * 5.0;
voltage /= 1024.0;
float temperatureC = (voltage - 0.5) * 100 ;
if (temperatureC <=16) {
digitalWrite(LED_PIN, HIGH);
room_is_cold = true;
}else{
digitalWrite(LED_PIN, LOW);
room_is_cold = false;
}
Serial.print(temperatureC); Serial.println(" degrees C");
temperature_timer.restart();
}
}
void led_loop() {
// 300 ms on time
// (Intentionally using non 50% blinking to demonstrate the state handling. )
if (led_is_on) {
if (led_timer.time_millis() >= 300) {
if (!room_is_cold) {
led_is_on = false;
digitalWrite(LED_PIN, LOW);
}
led_timer.restart();
}
return;
}
// 700 ms off time.
if (led_timer.time_millis() >= 700) {
led_is_on = true;
digitalWrite(LED_PIN, HIGH);
led_timer.restart();
}
}
void servo_loop() {
if (servo_has_moved) {
if (servo_timer.time_millis() >= 1500) {
servo_has_moved = false;
for(pos = 180; pos>=1; pos-=1) // goes from 180 degrees to 0 degrees
{ // in steps of 1 degree
if (last_pos != pos) {
myservo.write(pos); // tell servo to go to position in variable 'pos'
last_pos = pos;
}
}
servo_timer.restart();
}
return;
}
if (servo_timer.time_millis() >= 1500) {
servo_has_moved = true;
for(pos = 0; pos < 180; pos += 1) // goes from 0 degrees to 180 degrees
{ // in steps of 1 degree
if (last_pos != pos) {
myservo.write(pos); // tell servo to go to position in variable 'pos'
last_pos = pos;
}
}
servo_timer.restart();
}
}
void setup() {
led_setup();
servo_setup();
Serial.begin(9600); //Start the serial connection with the computer
}
void loop() {
led_loop();
servo_loop();
temperature_loop();
}
Sometimes the best answer for a newby is given by someone at the same level but a bit ahead - comprehensive replies are valuable, but usually a beginner will get it's full value when revisiting it again down the road.
When you explain in "beginner" language, it's easier to understand, and then if the explanation has some inaccuracies for one more advanced, the 'pros' will step in to set it straight !
This link helped me best in grasping the millis() function over the delay() one - most threads on this topic get into the theory of State Machine and Grumpy_Mike's tutorial walks you through it in simple language ! http://www.thebox.myzen.co.uk/Tutorial/State_Machine.html
I was a beginner at this stuff in 1983... so I will honestly admit I don't remember what it's like. I do remember what I did to become better though, and we don't have those resources available today. Well, we do, but nobody uses them. So, learning today is different than when I learned, but the same concepts still apply. So, sometimes it's hard to explain things at a low enough level. And, I try to avoid insulting people by going directly to the lowest level stuff, assuming they don't know it. I usually hit for a middle-of-the-road complexity level and let the student tell me if that's too much info or not enough.
BTW, I am planning a video series where I will "explain the code" like I do in this video... lots of folks have said this was really helpful. Please let me know if you have any suggestions. Send me a PM.