Wake up Tracker One via CAN

Hi there,

I am trying to wake up my Tracker One via CAN bus. I dug through the source code in order to find a suitable way of implementing it, unfortunately without luck. I tried to orientate myself at the SystemSleepConfiguration& usart(const USARTSerial& serial) function which is located in .particle\toolchains\deviceOS\4.0.1\system\inc\system_sleep_configuration.h and works similarly.

Here is what I tried:

SystemSleepConfiguration& can(const MCP_CAN& canInterface) {
        if (valid_) {
            // Check if USART has been configured as wakeup source.
            auto wakeup = wakeupSourceFeatured(HAL_WAKEUP_SOURCE_TYPE_CAN);
            while (wakeup) {
                auto canWakeup = reinterpret_cast<hal_wakeup_source_can_t*>(wakeup);
                if (canWakeup.canInterface == canInterface) {
                    return *this;
                wakeup = wakeupSourceFeatured(HAL_WAKEUP_SOURCE_TYPE_CAN, wakeup->next);
            // Otherwise, configure CAN as wakeup source.
            auto wakeupSource = new(std::nothrow) hal_wakeup_source_can_t(); // error here
            if (!wakeupSource) {
                valid_ = false;
                return *this;
            wakeupSource->base.size = sizeof(hal_wakeup_source_can_t);
            wakeupSource->base.version = HAL_SLEEP_VERSION;
            wakeupSource->base.type = HAL_WAKEUP_SOURCE_TYPE_CAN;
            wakeupSource->base.next = config_.wakeup_sources;
            wakeupSource->canInterface = canInterface;
            config_.wakeup_sources = reinterpret_cast<hal_wakeup_source_base_t*>(wakeupSource);
        return *this;

I also added this in the sleep_hal.h:

#include "mcp_can.h"

* HAL sleep wakeup source: CAN
typedef struct hal_wakeup_source_can_t {
    hal_wakeup_source_base_t base; // This must come first in order to use casting.
    MCP_CAN canInterface;
    uint8_t reserved[3];
} hal_wakeup_source_can_t;

However there are a few compile errors that I cannot solve.

I could see that in sleep_hal.h there is a flag for CAN source, however I don’t know how to use it.

typedef enum hal_wakeup_source_type_t {
} hal_wakeup_source_type_t;

If you have any idea or have done a successful CAN integration already, please let me know.

Wake on CAN is not currently implemented.

The values in the headers are placeholders should that feature be enabled in the future, but there’s currently no support in Device OS for it. You can’t just add a new wakeup source from user firmware, it would need to be added into Device OS itself.

If you can configure the CAN controller to generate an interrupt, you may be able to wake on GPIO for the CAN interrupt pin, however. That is probably the best thing to try first.

Thanks for the reply.

I was afraid that this is just a placeholder, however I didn’t find definite proof.
In the future, I will dig deeper into the CAN library to see if I can wake up the tracker via CAN. As you said, a wake up over a GPIO should work fine then.

Actually, I could now make it work.

I used the CAN_INT pin as a gpio wakeup source.

The lines of code that made it work are the following:

SystemSleepConfiguration config;
config.mode(SystemSleepMode::ULTRA_LOW_POWER).gpio(CAN_INT, FALLING);
1 Like

I bumped into another issue, which reactivates this thread again.

Unfortunately, the device (Tracker) is not staying in sleep mode for longer than ~47 s.

Here’s what I tried so far:

  1. Unplugging both power and CAN bus to make sure no interrupt is generated
  2. Using sleep modes HIBERNATE and ULTRA_LOW_POWER
  3. Enabled/ Disabled the Sleep Mode Setting in the Particle Console for that device.
  4. Tried both MANUAL and SEMI_AUTOMATIC options for SYSTEM_MODE
  5. Tried with the GPIO pins D2, D3 just to make sure it’s not the CAN_INT pin.

However, below the 47 s I am able to successfully wake up the device via CAN as expected.
It seems that under the hood something is still waking up the device after a fixed amount of time. How can I configure it, so that the device is only waking on gpio input?

Oh, sorry about that. You can’t call System.sleep() on a Tracker because it will conflict with the Tracker Edge wake functions.

Instead use the TrackerSleep API. It has the same sorts of calls to wake on GPIO, but mixes in the results with the Tracker Edge wake features.

Hi Rickkas,

thanks. That is of course something that differs to the other products. The tracker is somewhat special in a few ways.

I am now able to send the tracker to sleep but I am not able to wake it up properly. It reacts, switches on and prompts these infos via the Serial port:

0000138202 [net.pppncp] TRACE: NCP event 3
0000138202 [net.pppncp] TRACE: NCP power state changed: IF_POWER_STATE_UP
0000138203 [system.nm] TRACE: Interface 4 power state changed: 2
0000138203 [ncp.client] TRACE: Modem powered on
0000139206 [ncp.client] TRACE: NCP ready to accept AT commands
0000140228 [mux] INFO: Starting GSM07.10 muxer
0000140229 [mux] INFO: Openning mux channel 0
0000140229 [mux] INFO: GSM07.10 muxer thread started
0000140331 [mux] INFO: Openning mux channel 1
0000140384 [ncp.client] TRACE: NCP state changed: 1
0000140384 [net.pppncp] TRACE: NCP event 1

The RGB led pulses in (dark blue). Other than that, no reaction. I can’t even put the tracker back to sleep. Any idea?

@rickkas7 or @Colleen can you please support, thanks.

I don’t know how to make wake on CAN work properly. I think you’ll need to set up the filters in the MCP CAN chip to make sure the right messages as passed through to wake. And I suspect you need to clear the interrupt once you wake up, and that’s why you can’t go to sleep. But I don’t know the specifics.

Hi @johannes.nuwiel,

by any chance did you catch the latest updates to the develop branch in Tracker Edge public repo (GitHub - particle-iot/tracker-edge: Particle Tracker reference application) that were pushed recently…? There lots of improvements over the last few pushes including some CAN driver changes recently to hopefully address waking.

Another benefit of the latest Edge updates is that the build system and codebase has been updated so that it now builds with the latest DeviceOS 5.x releases! It is worth a try.

Would migrating to the latest develop branch work for you in this case?

Hi @gcl,

thanks for joining this discussion.

I updated the tracker-edge firmware to the latest version (5.0.2) but the issue persists. Please confirm that I understood the TrackerSleep API correctly.

  1. Is it required to switch to the enabled state in the sleep settings within the Particle Console? Or does that not touch sleeping initiated from code or waking up from external sources?

  2. How do initiate the sleep on the Tracker at all?
    Here’s the code I am currently using. Should it work?

#include "Particle.h"
#include "tracker_config.h"
#include "tracker.h"

#define BTN A3




void wakeCallback(TrackerSleepContext context)

void setup()

    /* ... */

    /* Sleep settings */
    TrackerSleep::instance().wakeFor(CAN_INT, FALLING);


void loop()

    if ( !digitalRead(BTN) )
        Log.info("Putting tracker to sleep");

It reaches the block where it says Putting tracker to sleep. Eventually after the maximum connection time, the tracker goes to sleep. Approximately 50s later, it wakes up even though no CAN traffic is recorded.

I could really use a working example to test if any of my hardware/ CAN2USB adapter or compiling/ flashing process isn’t set up correctly.


Interestingly, though a wake callback is registered, the function is not called when woken up after the time duration. From the looks of prompt from particle serial monitor, it looks like the tracker is rebooting and running the setup() again.


I almost got it right, but didn’t play well with resumeSleep() and pauseSleep(). Here’s what I learned:

1. Use the callback functions for preparing to sleep and waking up from sleep

Set the wakeup sources in the prepare function. Control if and how you want to wake up (activate cellular, do something, go back to sleep) in the callback function.

void on_prepare_sleep_cb(TrackerSleepContext context)
    TrackerSleep::instance().wakeFor(CAN_INT, FALLING);

The CAN_INT pin stays Low until read by digitalRead(CAN_INT).

2. Sleep has to be enabled…

…in the Particle Cloud in Asset Tracker Settings. You can check the state from the code by invoking bool TrackerSleep::instance().isSleepDisabled(void).

3. Use resumeSleep() and pauseSleep()

If you don’t want the device to fall into sleep mode automatically, use TrackerSleep::instance().pauseSleep(). Else, use TrackerSleep::instance().resumeSleep().

On a side note, the CAN tx buffer only stores 3 messages simultaneously. To send more than 3 messages at a time, use a FIFO queue (such as os_queue_t) to queue messages and empty the queue iteratively.

4. Recommendation

I can highly recommend outsourcing the CAN reading to a separate thread. You can then control how often the CAN buffer is checked for new messages by setting a delay with os_thread_delay_until().

os_thread_return_t task(void);

void setup()
    thread = new Thread("CAN", [this]() { return task(); }, OS_THREAD_PRIORITY_DEFAULT, 3072);

os_thread_return_t task(void)
    system_tick_t previousTime = 0;

    while ( true )
        os_thread_delay_until(&previousTime, 1);

void loop()
    if (!digitalRead(CAN_INT)) 
1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.