30 Second Timer + LED Display + Pager Motor HELP!!

Hello members,

Firstly, mods I wasn't too sure about which sub-forum I should open this thread in. Please move as per necessary.

Description of my project:
Our project consists of Transmitting device (ATmega128 MC) and a Receiving device (ATmega168 [Arduino Mini]).
The transmitter is going to send signals (characters/strings) over RF and receiver will execute a few commands.

Intro of sorts:
The receiver will have a pager motor, 2 digit LED Display and LED. LED display will countdown from a specific time and then cause the pager motor to vibrate and reset the countdown. Simultaneously, the receiver will be 'listening' for commands from the transmitter and execute specific commands - Vibrate pager motor and/or reset the countdown.

Problem:

I have managed to code something using the Serial library that listens for an input. If I press / it receives 'a' it activates the pager motor for a 5000 ms and if press / it receives 'b' it starts the countdown. I still need to incorporate the LED display have the segments turn on and off.

I realized that when I am using the countdown, I can't simultaneously execute other actions. I read up on interrupt commands, but could barely understand what I had to do. I, also, read up on how to change values in TIMER2 and I had hard time understanding that you. (Pardon me for being a Tissue engineer).

What I was hoping for, is a way that would keep the Countdown running and when I want the pager motor to vibrate, I could execute the command without interrupting the countdown.

I would appreciate any help from you guys! Thanks in advance.

Regards,
Shubs

/* include the SoftwareSerial library so you can use its functions:
#include <SoftwareSerial.h>
*/

int rxPin = 0;
int txPin = 1;
int motPin = 7;
int ledPin = 13;
Metro led_metro = Metro(500); 

/* set up a new serial port
SoftwareSerial rfSerial =  SoftwareSerial(rxPin, txPin);
*/
byte pinState = 0;


void setup()  {
  // define pin modes for tx, rx, led pins:
  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);
  pinMode(motPin, OUTPUT);
  pinMode(ledPin, OUTPUT);
  // set the data rate for the SoftwareSerial port
  Serial.begin(9600);
}

void loop() {
  // listen for new serial coming in:
  if (Serial.available() > 0)
  {  char someChar = Serial.read();
    // print out the character:
    Serial.print(someChar);
    // toggle an LED just so you see the thing's alive.  
    // this LED will go on with every OTHER character received:
    if (someChar == 'a'){
      motor(ledPin);
    }
    if (someChar == 'b'){
      timer(5000);
    }
  }

}

void timer(int time)
{
  for(int i = 0; i <= time; i++){
    Serial.println(i);
    ledBlink();
      }
      digitalWrite(ledPin, LOW);
  }

void ledBlink(){
  if (led_metro.check() == 1) { // check if the metro has passed it's interval .
    digitalWrite(ledPin,!digitalRead(ledPin)); // change the state of pin 13.
    if (digitalRead(ledPin)==HIGH)  { 
      led_metro.interval(500); // if the pin is HIGH, set the interval to 0.5 seconds.
    } 
    else {
      led_metro.interval(500); // if the pin is LOW, set the interval to 0.5 second.
    }
  }
}
void motor(int pinNum)
{
  digitalWrite(pinNum, HIGH);
  delayMicroseconds(5000);
  digitalWrite(pinNum, LOW);
}

My approach to this would not be to use interrupts, but to use state machines which are checked every milli second.

In pseudo code in the main loop I would havesomething like:

prvMilli = 0;

loop(){
  crtMilli = millis();
  if (crtMilli != prvMilli) {
    prvMilli = crtMilli;    

    // this bit runs every milli second - check for things to do

   if (serial.available){
     get the serial character
     if (serchar = 'a'){
      do the a thing
     }
     if (serchar = 'b'){
        do the 'b' thing
     } 
    
     TimerVal --;
     if (TimerVal =<0){
        TimerVal = 5000;

        do teh 5 second timer stuff
      }
      etc.

   }
  }
}

All processes would be implemented as state machines with none of the states taking longer than a milli second - that way there are no clock cycles wasted.

I hope that was clear(ish).

Sorry for not putting up proper code - my first coding language isn't c.

Mike

Shubs, I have a clock timer library that I wrote that may help you do what you want. It enables functions to be called at user definable periods of time. Under the covers, it uses similar logic to that mentioned in Mike's post, but it may be easier to use if you need to time multiple things in parallel (the current version supports eight different timers but more can be defined if needed. But If you only need to time a single interval at a time, it may be easier to implement Mikes's suggestion.

Unfortunately, I have not documented it sufficiently to post somewhere like the playground. But have a look at this example sketch and if it looks useful, I will post the library code in the playground for you

// this example sketch needs the Clock library, the code for this library has not yet been posted ! 
#include <Clock.h> 

int ledPin = 13;                // choose the pin for the LED
int motorPin = 12;              // pin to for the motor 

 TimerClass Timer1;  // this will be the 5 second timer


void setup(){
   Serial.begin(9600);  
   pinMode(ledPin, OUTPUT);      // declare LED as output
   pinMode(motorPin, OUTPUT);    // declare motor pin as output

  if( Clock.Register( Timer1 ) ) {        
     Timer1.Value = 5;                     // we want our timer to tick in 5 seconds
     Timer1.OnTickHandler = &OnTimer1Tick;  // and call this function when the timer matures 
  } 
  Serial.println("Finished setup");    
}

void OnTimer1Tick(void *Sender){
// this function is called when the timer matures
   Serial.println("timer has matured"); 
   digitalWrite(motorPin, LOW); // turn the motor off   
   Timer1.Disable();  // stop the timer from triggering until re-enabled
}

void  motor( int pin){
   // do some motor thing    
   digitalWrite(pin, HIGH);   // we just keep the LED on     
}
      
void loop(){
    // listen for new serial coming in:
    if (Serial.available() > 0)
    {
        char someChar = Serial.read();
       // print out the character:
       Serial.print(someChar);
       // toggle an LED just so you see the thing's alive.  
        digitalWrite(ledPin, HIGH);   // sets the LED on
        Clock.Delay(100);            // waits for 100 ms - note this is calling the Clock delay function!
        digitalWrite(ledPin, LOW);    // sets the LED off
        Clock.Delay(100);            // wait using clock delay function 
        // this LED will go on with every OTHER character received:
       if (someChar == 'a'){
           motor(motorPin); // activate the motor
        }
       if (someChar == 'b'){
          Timer1.Enable();  // this will call the timer function in 5 seconds.    
          Serial.println(" started Timer");
        }
    }
    Clock.Delay(1); // this must be called to service the clock timer
}

edit: Looking over the library code to get it ready to post, I realized that it requires a modification to wiring.c that eliminates the millis overflow problem. I posted this mod here: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1205791157/7#7 But this may be more then you really need to deal with.

Thanks for the replies BigMike & mem.

Mike, your code seems very easy and effective in terms of what I need to do. It was clear for the most part, although I had a couple of questions

a) what is the crtMillis value going to be when it loops through the first time? cause it seems like as soon as the value of crtMillis changes, you set it back to 0.

b) I didn't quite understand the TimerVal --; thing. Is it an int, I presume so. How does it know how much time has passed? Do I set it equal to another millis function?

Mem, I think your code makes a lot of sense. I just wanted to know in slight detail of some the functions that your library has.

a) I am guessing the Timer1.value sets the value of the first time in Seconds?

b) Does it run through the setup each time? I guess once you define the value in the setup, it checks if it's actually equal to that.

Finally, whatever the time it might be, I also need to run a 2 digit LED display. I already checked some inbuilt libraries, but I had a hard time understanding them. I just need it do a countdown from 30 seconds. Thus every second it counts, I need it change the display to 30, 29... and reset it back to 30 once it's matured.

Again, I sincerely appreciate your help. They're pretty inane questions, but I just have a hard time understanding it.

EDIT - Would you able to upload the initial Clock library somewhere so I can use it? Also, wouldn't the delay function cause the whole code to stop for a the defined time?

Much respect.

I am guessing the Timer1.value sets the value of the first time in Seconds?
Yes, the event hander for Timer1 (OnTimer1Tick) will be called 5 seconds after Timer1 is enabled

Does it run through the setup each time? I guess once you define the value in the setup, it checks if it's actually equal to that.
Once the value is set it will be maintained until it is explicitly set to something else. The timer will actually continue to trigger every 5 seconds unless it is disabled, which is why it is disabled for your applicaiton when it matures, and re-enabled when a trigger arrives on the serial port.

Finally, whatever the time it might be, I also need to run a 2 digit LED display. I already checked some inbuilt libraries, but I had a hard time understanding them. I just need it do a countdown from 30 seconds. Thus every second it counts, I need it change the display to 30, 29... and reset it back to 30 once it's matured.

I would implement this using a timer with a value of 1 second. Each time the event handler is called I would decrement a counter initially set to 30 by 1. You could display this number on the lcd and turn the motor off when it gets to 0.
But, if this is the functionality you want, it may just be easier to do it using the millis counter, as per Mikes proposal.

Also, wouldn't the delay function cause the whole code to stop for a the defined time?
Calling Clock.Delay() blocks the foreground sketch for the given number of millis (one millisecond per loop in the example sketch) but the underlying code in the Clock.delay function is constantly checking the timer states to see if a timer has matured.

My suggestion if you have only a single delay going on at one time is to try Mikes approach first. But I will get you the code if you are comfortable modifying your copy of wiring.c

Thanks for the replies BigMike & mem.

Mike, your code seems very easy and effective in terms of what I need to do. It was clear for the most part, although I had a couple of questions

a) what is the crtMillis value going to be when it loops through the first time? cause it seems like as soon as the value of crtMillis changes, you set it back to 0.

Lack of comments and poorly named variables makes it harder to understand :slight_smile:

However, I can't see where I'm setting crtMilli back to zero. Each time through the loop I grab the current value of the millisecond counter using the line crtMilli = millis(); If it differs from the previous value (prvMilli) then at least one milli second has passed.

Doing a test for different (!=) rather then just greater than means that you don't need to fix the millis() function timer rollover bug mem mentions.

b) I didn't quite understand the TimerVal --; thing. Is it an int, I presume so. How does it know how much time has passed? Do I set it equal to another millis function?

TimerVal--; is a decrement by one instruction. Equivalent to TimerVal = TimerVal - 1;. There are pre- and post- decrement and increment instructions: i++; ++i; i-- and --i;

So the timer works by counting down to zero in milliseconds.

Regards,

Mike

Big Mike, I managed to figure out what everything stands for and actually have MOSTLY everything running. Thanks for the help again (mem & yourself) Here's my code:

int rxPin = 0;
int txPin = 1;
int motPin = 2;
int ledPin = 13;

unsigned long PreviousTime = 0;
unsigned long CurrentTime = 0;
int TimerValue = 0;
int Counter = 30;

void setup() {
// define pin modes for tx, rx, led pins:
pinMode(rxPin, INPUT);
pinMode(txPin, OUTPUT);
pinMode(motPin, OUTPUT);
pinMode(ledPin, OUTPUT);
for (int i = 3; i< 12; i++){
pinMode(i, OUTPUT);
}
// set the data rate for the SoftwareSerial port
Serial.begin(9600);
}

void loop() {
CurrentTime = millis();
if(CurrentTime != PreviousTime){
PreviousTime = CurrentTime;
if (Serial.available() > 0){
char someChar = Serial.read();
Serial.print(someChar);
if (someChar == 'a'){
motor(ledPin);
motor(motPin);
}
if (someChar == 'b'){
TimerValue = 1000;
Counter = 30;
}
}
TimerValue --;
if(TimerValue <= 0){
TimerValue = 1000;
Serial.println(Counter);
Display(Counter);
if(Counter <= 0){
Counter = 31;
}
Counter --;
}
}
}

void motor(int pinNum)
{
digitalWrite(pinNum, HIGH);
delayMicroseconds(5000);
digitalWrite(pinNum, LOW);
}

void Display(int number){
TurnOffDisp();
int ones;
int tens;
ones = number%10;
number = number/10;
tens = number%10;

if(ones==0) {
digitalWrite(8, LOW);
digitalWrite(9, LOW);
digitalWrite(10, LOW);
digitalWrite(3, LOW);
digitalWrite(4, LOW);
digitalWrite(5, LOW);
} .....
}

Final question, I need to drive 2 LED Displays (7-segment, common anode) with the countdown running on it. I managed to get one of the digits running (ones) but realized that I don't have enough pins to run the second digit (tens).

I googled a couple of things and read about multilexing/charlieplexing. This requires me to use 2 transistors (1 for the CA pin of each display) which require 2 pins and 7 other pins that run in parallel to the 2 LED displays. Thus, requiring me to use only 9 pins. I also read that it quickly keeps scanning and refreshing the 2 digits fast enough so that the naked eye cannot see if one of them is off.

I just wanted a guideline or a brief idea of what my code should look like. Currently, I am asking my Arduino to change the Pin state based on the number it reads.

Thanks in advance,
Shubs

If you need 7 outputs for each of the two lEDs and one for the motor you may have enough pins without multiplexing. You can use the six analog pins as digital outputs 14-19.

While I was reading over my code, I read that I have used delayMicroseconds() in the motor function.
Is there any way that I can use the millis() to prevent it from delaying the code?

void motor(int pinNum)
{
digitalWrite(pinNum, HIGH);
delayMicroseconds(5000);
digitalWrite(pinNum, LOW);
}

I was hoping that I could shove in another millis() and modify it as follows

void motor(int pinNum){ 
unsigned long time = 0;
  time = millis();
  if (time == 0){
    digitalWrite(pinNum, HIGH);
  }
  if (time >= 5000){
  digialWrite(pinNum, LOW);
  time = 0;
  }
}

Although, I have a feeling that 2 millis functions might cause a slight problem. Please shed some light.

Regards,
Shubs

You can simply change
delayMicroseconds(5000); // delay 5000 microseconds = 5milliseconds
to
delay(5); // delay 5 milliseconds

If you need 7 outputs for each of the two lEDs and one for the motor you may have enough pins without multiplexing. You can use the six analog pins as digital outputs 14-19.

Even on the Arduino Mini?
Analog Pin 0 = Digital I/O Pin 14?

You can simply change
delayMicroseconds(5000); // delay 5000 microseconds = 5milliseconds
to
delay(5); // delay 5 milliseconds

But that would off set my CurrentTime & PreviousTime by 5 milliseconds correct? Because it would stop the execution of the rest of the code.

Even on the Arduino Mini? Analog Pin 0 = Digital I/O Pin 14?
according to this page Arduino Reference - Arduino Reference, you have two extra analog ports on the mini. I would think pins 14-19 work as digital pins on the mini but not analog pins 6 & 7.

Try it and see, set pins 14-21 as outputs and do a digitalWrite to see if it lights an LED on those pins.

But that would off set my CurrentTime & PreviousTime by 5 milliseconds correct? Because it would stop the execution of the rest of the code.

Execution will be blocked when calling delayMicroseconds or delay. If this is not what you want then you need to something a little more complicated. Could you clarify what you want to happen if you get a trigger from the serial port while a previous trigger is still counting down.

But that would off set my CurrentTime & PreviousTime by 5 milliseconds correct? Because it would stop the execution of the rest of the code.

Execution will be blocked when calling delayMicroseconds or delay. If this is not what you want then you need to something a little more complicated. Could you clarify what you want to happen if you get a trigger from the serial port while a previous trigger is still counting down.

If I execute the motor() function, it will cause the motor to vibrate, when it is being executed the 30 second timer would effectively become a 30 + 5 millisecond timer. Every time the pager motor vibrates, it's going to add 0.5 seconds to the difference between CurrentTime & PreviousTime.

Since It will be receiving a lot of inputs to activate the motor function, the error would become too big, hence I need both the motor execution and timer function to be dependent on only one variable, that is time.

I hope that it is clear enough. I think my team has NEARLY fine tuned everything that we want from the device, now the only thing we have left to do is test it out.

Thanks for your help.

The millis function returns the number of milliseconds since the Arduino started. So when you read this value into a variable holding the start time, you can get the amount of time elapsed by subtracting the current millis reading from the start time. It won't matter if you delay for 5 milliseconds as long as the delay finishes before the desired end time, the calculation for start millisconds – millis will return the elapsed time to the nearest millisecond.

In other words, your timings will not be increased by this 5ms delay.

Sorry if I've confused you, but the pager motor vibration length is supposed to be half a second so it's 500 milliseconds. I could make the timing slightly smaller but I want refrain from that.

We've set the TimerValue = 1000 that's 1 second.
If the delay on the motor function is 500 and each time it receives an execution command then
CurrentTime = PreviousTime + 500 am I Correct? Because of the delay.
Thus, if I had 4 consecutive beats, my TimerValue that should have decreased by 2x TimerValue--, will only be reduced once. Right?

You have no idea how much I appreciate your help!!!!

While I was reading over my code, I read that I have used delayMicroseconds() in the motor function.
Is there any way that I can use the millis() to prevent it from delaying the code?

void motor(int pinNum)
{
digitalWrite(pinNum, HIGH);
delayMicroseconds(5000);
digitalWrite(pinNum, LOW);
}

Shubs,

As 5000 micro seconds is 5milliseconds, that can be implemented as a state machine.

Define the states and set up two variables, one to hold the state and one to hold the time.

#define mtrStable = 0
#define mtrOn = 1
#define mtrOff = 2
var byte mtrState;
var byte mtrCount;

mtrState = mtrStable;
mtrCount = 0;

Then in the loop, instead of calling the motor() function, change the motor state variable:

if (someChar == 'a'){ 

mtrState = mtrOn;
 mtrCount = 500;
}




then later check the motor state variable:


if (mtrState == mtrOn){
 digitalWrite(motPIN, HIGH);
 mtrCount = 500;
 mtrCount --;

if (mtrCount <= 0){
 // time's up, so stop
 mtrState == mtrOff;
 }
}
if (mtrState == mtrOff){
 digitalWrite(motPIN, LOW);
 mtrState = mtrStable;
}
...




Something like that should work.

Regards,

Mike

Sorry if I've confused you, but the pager motor vibration length is supposed to be half a second so it's 500 milliseconds. I could make the timing slightly smaller but I want refrain from that.

Ah - so in my previous post, change the mtrCount variable to an int and set it to 500 in the loop.

Mike

That's exactly the code I need to implement. Thanks BigMike. Seems like I'm sorted for now.

I can only hope that all of this works when we put it together. I'll keep you guys posted.

Thanks again!

Should the second part of the code, the checking of the state of the motor, be in the main loop or should I modify my motor function?

void motor(int counter){

mtrCount = counter;

if (mtrState == mtrOn){
  digitalWrite(motPIN, HIGH); 
  mtrCount --;

  if (mtrCount <= 0){
  // time's up, so stop
  mtrState == mtrOff;
  }
}
if (mtrState == mtrOff){
  digitalWrite(motPIN, LOW);
  mtrState = mtrStable;
}
...