It's almost weekend in our ongoing project to create a semi-autonomous vessel, using and Arduino MEGA and some affordable shields and hardware supplements, and so we are going to take it easy today. No additions to the hardware...just a few software tricks!
In the past four days we built a controller for our model boat, which can read GPS signals, send the coordinates to the Aquabots Client, after which the vessel will get heading and thrust data to send it somewhere over the globe. However, it would be a bit presumptious to put your vessel in a major shipping lane right now, because this feedback loop is very - and I mean VERY- vulnerable! The GPS can loose its signal and the communications may be down, which leaves your vessel completely blind. In effect, all we have done so far is create a remote control unit and, let's be honest, a joystick is probably a much better option than what we've brewed here!
So on this last day before the weekend, we are going to make some preparations to give the vessel more autonomy. in order to do so, we are going to implement a sort of 'multi-tasking' framework, which will allow us to:
- Add more hardware that can work simultaneousy, like a compass or a 9-degrees board
- implement some load balancing functionality, especially regarding the network traffic
- Develop various levels of alarms to which the vessel can choose a safety strategy.
These functions improve the robustness of our design tremendously, and funnily enough, it doesn't require a lot of effort! All we need is one of Arduino's timer interrupts!
Basically the interrupt will give us a clock that allows us to differentiate between faster and slower processes. For instance, the GPS unit is updated every second or so, but we do not need such a fast pace to check if we are on the right track of a predefined route. Also log messages and NMEA code so not need to transmitted at such a high rate.
The header of our interrupt module is as follows:
#ifndef Interrupts_h
#define Interrupts_h
#define SECONDS 10 //0.1 sec
#define TEN_SEC 40
#define MINUTES 600
class Interrupts {
private:
//Create a sempahore
bool lock;
public: Interrupts(void);
int int_counter;
bool flank; //10Hz
bool sec_flank;//seconds
bool min_flank;//minutes
bool tensec_flank;//10 sec.
void setup();
void clear();
bool getLock();
void setLock( bool lock );
bool getFlank();
bool getSecondsFlank();
void clearSecondsFlank();
void clearFlank();
int getCounter();
};
#endif
The implementation, including setting up timer 2 of the Arduino is given below:
#define TENTH_SECONDS 1
#define SECONDS 10
//Activate interrupt Timer2
ISR(TIMER2_COMPA_vect) {
interrupt.flank = true;
interrupt.int_counter++;
interrupt.int_counter %= SECONDS;
if ( interrupt.int_counter > 0 )
return;
interrupt.sec_flank = true;
}
Interrupts::Interrupts() {};
/**
Set timer0 to increment with steps of 10 Hz
*/
void Interrupts::setup() {
lock = false;
flank = false;
sec_flank = false;
cli();//stop interrupts
//set timer2 interrupt at 2kHz
TCCR2A = 0;// set entire TCCR2A register to 0
TCCR2B = 0;// same for TCCR2B
TCNT2 = 0;//initialize counter value to 0
// set compare match register for 10 Hz increments
OCR2A = 156;// = (16*10^6) / (10*102) - 1 (must be <256)
// turn on CTC mode
TCCR2A |= (1 << WGM21);
// Set CS00 and CS02 bits for 1024 prescaler
TCCR2B |= (1 << CS20) | (1 << CS21) | (1 << CS22);
// enable timer compare interrupt
TIMSK2 |= (1 << OCIE2A);
sei();
}
boolean Interrupts::getLock() {
return lock;
}
void Interrupts::setLock( boolean lck ) {
lock = lck;
}
int Interrupts::getCounter() {
return int_counter;
}
boolean Interrupts::getFlank() {
return flank;
}
boolean Interrupts::getSecondsFlank() {
return sec_flank;
}
void Interrupts::clearSecondsFlank() {
sec_flank = false;
}
void Interrupts::clearFlank() {
flank = false;
}
All we need to do now is to activate the interrupt in the main thread:
...
#define REFRESH 3
...
static Interrupts interrupt;
int load;
void setup() {
...
load = 0;
}
void loop() {
...
if ( interrupt.getSecondsFlank()) {
interrupt.clearSecondsFlank();
//we allow for tasks that only need 2 minutes
// to refresh
load = ( load + 1 ) % 120;
int balance = load % REFRESH;
switch ( balance ) {
case 0:
break;
case 1:
break;
...
default:
break;
}
}
}
The above code highlights the main additions to the code we already made in the previous days. We try to maintain a modular structure, using some elements of object-oriented programming, which helps us to keep oversight and will make it easier to make improvements on the long run.
If you look at the code in the GitHub directory, you will see that it also contains a logger module and an options module. These tasks typically do not need to be updated at a high interval level, and they are not very critical and so they provide a perfect test for the multi-tasking platform we developed here. The Logger unit provides a 'print' and 'println' command, which sends the log messages to the Aquabots Client and displays them in the 'log' tab, just like the regular Arduino console.
Tomorrow we will use the interrupt routine to add a compass module, so that the vessel becomes more responsive.
Comments
Please log in or sign up to comment.