The examples included with the EvoLink library provide detailed information on usage, but here’s the gist of interacting with the EVO-All, here.

Basic Usage

You’ll need to include the header, of course

  #include <EvoLink.h>

and you may want to add a little

  using namespace EvoLink;

to avoid needing to prefix everything–e.g. it allows you to write Brake::Off rather than EvoLink::Brake::Off.


There is a global instance of the main driver available by default–you don’t need to instantiate anything.  But you must, at some point (for Arduino, this would normally be in the setup() function) let it know how to talk to the EVO-All.  This is done using EVO’s begin() method, and passing it a (platform-specific) SerialSetup parameter.  The simplified version for Arduino might look like so:

void setup() {

Issuing commands

To issue commands, you need only do two things:

  1. wake up the EVO-All
  2. tell it what to do

Everytime you are about to issue one or more requests on the EVO object, you should wake it up and ensure it’s ready for action. This involves a simple:


The library is setup to give the EVO-All sufficient time to get ready, and is smart enough to skip extraneous wake-up signals.  I.e. should you do this:


then a single (at most) wake-up command will be issued.  The moral of the story is: don’t be affraid to abuse the wakeUp() method–use it before any sequence of commands/requests and you’ll be certain the module is ready for you.

After the wake-up, you can tell the EVO-All to do whatever it needs to.  The list of commands available is extensive (see the included driver.h file for details).  Many commands include both an On/Off pair and a version with a parameter (so XXXOn()/XXXOff() and XXX(OnOrOff)), but the short list of these simple toggles includes:









A few commands use a parameter, such as

lock(Driver::Selection d=Driver::One)
unlock(Driver::Selection d=Driver::One)
priorityUnlock(Driver::Selection d)

ignitionOn(Source::TriggerSource src)
ignitionOff(Source::TriggerSource src)

starterOn(Source::TriggerSource src)
starterOff(Source::TriggerSource src)

horn(Horn::Action action)

auxiliary(uint8_t index)

And then there’s also:

// ping/pong -- check EVO-All response

/* remote start/stop: Simplified engine control*/

If you don’t care about event notifications (like brake or door signals) then that’s all you really need.

Events and Requests

Events are asynchronous–they can happen any time–and if you want to see them arrive there are two things you need to do:

  1. setup callbacks of interest
  2. check for activity periodically

When you check for incoming data from the EVO-All, EvoLink will figure out what is coming through the wire and send it to the appropriate callback (if so configured).  Events have been split by type such that if you’re only interested in, say, brake events you can only worry about those by creating and specifying the appropriate callback.

For brake-related events, this could look like (assuming you’re using the EvoLink namespace, as described above, otherwise prefix everything with EvoLink::):

void brake_event_received(Brake::Event brake_event) {

   // we get here when something related to the brakes
   // happens...

   DEBUG_SERIAL.println(' ');

   switch (brake_event) {
    case Brake::On:
      DEBUG_SERIAL.println(F("Brake ON"));
    case Brake::Off:
      DEBUG_SERIAL.println(F("Brake off"));
    case Brake::HandBrake_On:
      DEBUG_SERIAL.println(F("Hand break ON"));
    case Brake::HandBrake_Off:
      DEBUG_SERIAL.println(F("Hand break off"));

With the callback created, we also need to tell EvoLink about it so it will actually be used.  This can be done as part of your setup:

void setup() {

   // Tell EVO about the callback we created above:
   EVO.callbacks.brake_event = brake_event_received;

Finally, in your main loop, you need to give EvoLink a chance to check the serial line for events of interest.  This is done with a call to checkActivity():

void loop() {
   // parse any EVO-All events:

   // other stuff...

You can pass checkActivity() a timeout parameter, and it will wait  up to that amount of milliseconds for incoming data.  If you want to impose a fixed delay, while still checking for activity periodically, you can instead call delayWhileCheckingActivity(delayTimeMs [, checkActivityEveryMs]) like:

   EVO.delayWhileCheckingActivity(2000, 100); // delay for 2s, checking every 100ms

or just

   EVO.delayWhileCheckingActivity(2000); // delay for 2s, checking every 250ms

Requesting Information

You can also requests a few pieces of info from the EVO-All, such as tachometer, temperature etc.  There are two ways of doing this.  You may implement a QueryResponseHandler callback, and assign it to EVO.callbacks.requested_data_received (see the CallbackSystem example).  Once that’s done, issuing one of:

requestStatus() or

should trigger the callback with the response (suitably massaged to a valid value).

The other way is synchronous but simpler.  You need only issue one of there methods:

int16_t getTach(uint16_t timeout = SYNCHRONOUS_GETTER_DEFAULT_TIMEOUT_MS);
int16_t getVSS(uint16_t timeout = SYNCHRONOUS_GETTER_DEFAULT_TIMEOUT_MS) ;
int16_t getTemperature(uint16_t timeout =

InputStatus getStatus(uint16_t timeout=SYNCHRONOUS_GETTER_DEFAULT_TIMEOUT_MS);

The InputStatus returned from getStatus is actually a structure with the following attributes:

State::OpenClose door;
State::OpenClose hood;
State::OpenClose trunk;
State::SetTo     tach;
State::SetTo     hand_brake;
State::SetTo     brake;

It’s useful, but shouldn’t be used too much as it’s demanding on the EVO-All and you’re better off using it once at startup to establish state and then using callbacks to track changes of interest.


All EvoLink pages:
EvoLink Overview