CC Blog Projects Research & Design Hub

Bicycle Light Using ANT+ Protocol

Written by Carlo Tauraso

Tail Light Control via Garmin Navigator

ANT+ is a wireless protocol used in sports devices. I use this technology, along with Nordic Semiconductor’s nRF52832 microcontroller (MCU), to build a bicycle tail light. The light is activated by a Garmin GPSMAP 64st navigator, with its existing integrated features repurposed.

In my last article (“Sports Wearable Uses ANT+ Protocol,” Circuit Cellar #377, December, 2021) [1], I promised myself to continue my journey in developing ANT+ devices—perhaps integrating them with commercial sports products. The inspiration for this article was born from a bike training session. Like many others, I use a Garmin device to record my rides. As a cyclist, I love just riding, and the fewer buttons I have to press and menus to explore, the better.

Many bicycle computers, navigators, and smart watches integrate control functions of external devices, such as action cameras, mp3 players, or several kinds of sensors. Thus, I decided to make an ANT+ networked bike light. You’d be able to activate this light directly from a Garmin Navigator, such as a GPSMAP 64st, by taking advantage of the integrated features for the Garmin VIRB action camera. Thus, you wouldn’t need to stop your workout to activate the rear light, when it’s time to be seen rather than to see. It would be useful for the cyclist to have control of the rear light from a convenient remote device, such as a watch or bike computer placed on the handlebar. I reused the microcontroller (MCU) from my last project, the nRF52832 system-on-a-chip (SoC) from Nordic Semiconductors, Inc.

The goal of this month’s project is to use again the profile-oriented development system made available by Dynastream Inc. I would like to distribute a sample code of the so-called “Controls Device Profile.” I thought that this solution could be useful for those who are entering the world of MCU development for the first time, since this profile has documentation, but it isn’t present in the SDKs. Having no reference code could discourage a newbie from having to “reinvent the wheel.” So, I thought about modifying my latest firmware as needed. I hope I have simplified the logical flow behind the development of any other profile.

You have to consider the bike light as just an application example. It allows you to easily experiment and test the firmware directly with everyday devices. There are already similar ANT+ devices on the market, including ACE lights and Garmin’s Varia (a radar tail light that warns of vehicles approaching from the rear). My solution is cheaper and limited, but has the advantage that you can build it entirely by yourself, experimenting with alternative customizations and solutions.

I also designed a light box model, using SketchUp software, that you can make with a 3D printer. Hopefully this will be of us in other scenarios the readers comes up with.

The initial prototype for my project consists of a video-controlled device that could be used both through the integrated functions of a GPS navigator (Garmin GPSMAP 64st) and a remote control for selfies (Garmin VIRB Action CAM Remote Control). Then I added a command interpreter, which uses the same command identification codes as those transmitted by commercial devices, to turn the light on and off. You can use the same system to control other devices, such as a relay or an inductive load.

Reading the documentation, I realized that the ANT+ protocol is well suited for sending control information between devices. The Controls Device Profile defines data formats for a large variety of remotely controllable devices—from the most generic (Keypad Control) to the most specialized (Audio Control). The profile even provides a multi-use case implementation. You can combine different use cases such that a remote control is able to control multiple types of controllable devices, and a controllable device may support multiple-use cases, broadcasting information as needed. Using this kind of implementation, you can develop a multipurpose device capable of associating, in seconds, with different types of commercial outdoor devices.

I wanted to share this small project—from its firmware, an ANT+ Controls Device Profile based on the Nordic Semiconductor S212 SoftDevice, to its realization in a small circuit with an nRF52832 core, complete with a 3D-printed plastic case. So, hopefully having sparked your curiosity, let’s start with an analysis of the Video Control use case. Consider it a sort of least common denominator of all the other use cases. It’s the simplest to understand, but it also offers all the logical structures you need to create more complex systems.


The ANT+ Controls Device Profile supports multiple use cases—Audio, Video, Generic, and Keypad. In this project, I implemented the Video Control use case, because the navigator I use frequently (GPSMAP 64st) has a special remote-control function for controlling a Garmin VIRB Action CAM. The device acts as a video recording device, while the navigator serves as a remote control. By default, the ANT+ video device will broadcast its video status. The remote control will send a video command to change the device status (“Stop/Start Recording,” “Take a Photo”). The “CtrlANT+,” as I call the device I developed in this project, will interpret the Start Recording and Take a Photo commands as “light on,” and Stop Recording as “light off.” Remote commands will be sent sequentially, and are independent. Each command has a special sequential progressive number, which allows any retransmissions of the same command to be recognized.

Each ANT+ Device Profile contains the network rules relevant to a specific use case. For a video-controlled device we set the channel type to 16 (0x10 master channel), the network key to the obtained sequence number to “ANT+ Adopter,” the RF channel frequency to 57 (2,457MHz), the transmission type as standard (not extended), the device type as controlled device 16 (0x10), the device number as video-controlled device (0x207), and the channel period as 4Hz so the messages have a rate of four per second.

Remember that to obtain the ANT+ network key for your personal devices, you need to become an “ANT+ Adopter” and agree to the terms of use. Devices use the network key to identify and access the ANT+ network; the radio module will not hear transmissions occurring on other networks. In the source code this field is all zeros, so everyone can use their own key. If you are only experimenting with ANT+, you can use the public network key, too.

You can find all the details of these configuration parameters in the ANT+ protocol documentation made available on the ANT+ website [2]. For details on the use case development, I suggest you read the ANT+ Controls Device Profile [3].

The CtrlANT+ acts as a controller, and each controller in an ANT+ network has an ANT channel ID, as well as a value comprised of the device number, device type, and transmission type. This identifier is critical during the pairing process, when the remote searches for the controller to determine the specific channel ID. After devices are paired, the remote device control can begin.

To develop a profile correctly, you need to understand the sequence of messages that controller and remote exchange during execution. In my case, the controlled device broadcasts Data Page 7, which contains its status and expects to receive a user-requested video command through Data Page 16, sent from the remote control using an acknowledged message. You can see a summary of the message sequence in Figure 1.

Figure 1 
Video Control Use Case
Figure 1
Video Control Use Case

Let’s analyze the two fundamental data pages of this profile, 7 and 16, in detail. Data Page 7 is a required page, and it is the primary data page sent from a video device. It has six parameters, formatted as shown in Table 1.

Table 1 
Data Page 7 parameters and their formats.
Table 1
Data Page 7 parameters and their formats.

The fields used in this project are: Time Progressed, Capabilities, and Status. We have interpreted their meaning in a different way, because we are controlling a bike light instead of an action CAM. Time Progressed is the current progression in seconds from the moment the light is turned on. This allows the cyclist to know that the rear light is actually on and for how long. Capabilities has the value 1, so we use the commands reserved for devices supporting video recording. Status is the most important field. It indicates the current state of the device: 3 = light is off, and 7 = light is on. This field can take on several other values, but for my project, these two states are more than enough.

To make the firmware more understandable, I have only implemented the Minimum Data Page Requirements: Data Page 7 and Data Page 16, along with Data Page 80 and 81. The latter two are Background Pages, containing the manufacturer’s identification and product information.

The controlled device will transmit Data Page 7 at a 4Hz message rate—four times per second. The remote device uses an acknowledged message to send the Data Page 16. So, the application can determine if the data page was successfully received or if it needs to be resent. The application will determine if the data page needs to be resent. This page has seven fields formatted (Table 2).

Table 2 
Data Page 16 fields and their formats.
Table 2
Data Page 16 fields and their formats.

For this project, the most important fields are Serial Number, Sequence, and Command Number. The Serial Number contains the identification number of the remote control. The controlled device chooses to ignore or act on the command request. Typically, it discards commands from the remote control with a different identification number. The Sequence is a number that is incremented with each new command. The field rolls over when it reaches the value 256 (0xFF). It is possible that the remote control resends a command if there is a transmission error or if the controlled device fails to receive the ACK. In this way, our bike light can differentiate between a new command and a repeated command, and it discards repeated commands.

The Command Number determines the command sent. There are several types of commands. The first 20 are well known and are not customizable, but the next 107 can be used as needed. In this project I use three main commands: Record (id = 19), Take photo (id = 21), and Stop (id = 3). The first two can be used interchangeably to turn on the light, whereas the second is used to turn it off.

During development, you have to ensure that in the numeric field, the most significant bit is set to 1, because you are controlling a video device. Since we simulate a video device, 128 must be added, due to the value 1 assigned to bit 7. So, the commands received from the CtrlANT+ will be Light ON = 19 + 128 = (147) or 21 + 128 = (149), and Light OFF = 3 + 128 = (131).


I used the same breakout board described in my previous article [1]—a core module, Yj-16002 (Holyiot Technology Co., Ltd.) based on the nRF52832. (Note: The Yj-16002 is no longer available, except from It is built around the nRF52832, a multiprotocol SoC for ULP wireless applications produced by Nordic Semiconductors. It incorporates an ARM Cortex M4 CPU with floating point unit running at 64MHz, 512KB flash memory, 64KB RAM, and a powerful radio transceiver on 2.4GHz. It is on-air compatible with the nRF51 and nRF24AP series.

The nRF52 series RF transceiver is interoperable with ANT+, BLE 5 (Bluetooth Low Energy), and other 2Mbps 2.4GHz proprietary stacks. Figure 2 is the schematic and parts list. As you can see, the nRF52832 is in the basic configuration with a 32MHz oscillator crystal and a little impedance network adapter for the integrated PCB antenna. The switching on/off control of the four high-brightness, red LEDs takes place through logic line P0.17 connected through resistor R1 at the base of transistor Q1. The NPN transistor acts as a switch with an open-collector output and the emitter terminal connected directly to ground.

Figure 2 The CtrlANT+ schematic
Figure 2
The CtrlANT+ schematic

When GPIO pin P0.17 is high, Q1 is fully ON (Saturation Region). To find the minimum base current required to turn on the transistor for a load that requires 80mA (four LEDs x 20mA), we divide 80 by 200 (the BC547 transistor’s minimum Beta), so its value is 0.4mA. You can calculate the transistor base resistance by subtracting from 3V the base-emitter voltage of 0.7V and dividing by 0.4mA, to get 5.75kΩ. I round this to the commercial value 4.7kΩ.

When GPIO pin P0.17 is low, Q1 turns off, acting like an open switch, and zero collector current flows (Cut-Off Region). Therefore, a small base current (0.4mA) controls a much larger collector current (80mA).

The current limiting resistor for the four LEDs is derived from the equation: 3V = R2 x Ic + 2V + 0.2, where Ic is the current needed (80mA), 2V is the nominal forward voltage for the red LED, and 0.2 is the collector-emitter saturation voltage. I get a resistor value of 10Ω. I used four 5mm diffused, flat-top, red LEDs with high luminosity (8,000mcd), and a wide viewing angle of 120 degrees. This means that the light is more diffused and not so direct, reducing glare for other bike riders and motor vehicle drivers.

For a simple, small assembly, I created a rectangular PCB with dimensions 50mm x 32mm (Figure 3). The four red LEDs are positioned at the four corners of the rectangle to increase its visibility. In my previous projects I used standard boxes, but this time I designed a custom box using SketchUp software. You can make it yourself with the BikeLightBox.stl file and a 3D printer. For the best result, the cover must be made of transparent resin; otherwise, you would have to drill holes in the cover corresponding to the locations of the LEDs on the board.

Figure 3 The assembled project. The sequence of component insertion into the custom-made box (from right to left) is: battery holder, PCB with LEDs in each of the four corners, screws (shown as blue ovals) to secure the circuit board to the box, and transparent resin cover.
Figure 3
The assembled project. The sequence of component insertion into the custom-made box (from right to left) is: battery holder, PCB with LEDs in each of the four corners, screws (shown as blue ovals) to secure the circuit board to the box, and transparent resin cover.

It is necessary to add a battery holder for two 1.5V AAA batteries—connect the positive to the pad marked with the VCC label and the negative to the one marked with the GND label. Then insert the batteries and board into the box, fix the board with two screws, and close it with the transparent resin cover. The assembly sequence is in Figure 3. The board also includes a connection strip for the JTAG bus (SWCLK, SWDIO, GND, and VCC) to update the firmware.

Figure 4 
The CtrlANT+ PCB circuitry details.
Figure 4
The CtrlANT+ PCB circuitry details.

CtrlANT+ PCB Assembly: Figure 4 is the top and bottom copper layer of the PCB. It’s a small card with a simple scheme to connect the nRF52832 breakout board to the boundary components. On the right and left sides of the board there are two 2mm holes to fix the circuit into the box. Assembly takes place by first soldering the LEDs, resistors, and transistor to the top of the board, then soldering the nRF52832 module to the bottom of the board. Figure 5 shows the assembled prototype board, including the pinout of the JTAG connector required for the firmware update.

Figure 5 
The assembled prototype board top (left) and bottom (right).
Figure 5
The assembled prototype board top (left) and bottom (right).

The firmware development is based on SDK version 16 for the nRF52 SoC family and the S212 SoftDevice, both provided by Nordic Semiconductor. The S212 SoftDevice contains a highly flexible ANT+ protocol stack that supports all the features needed to build a communication network for ANT+ compliant devices.

I’ll now describe the data structure of the Controls Device Profile, so you can compare it with the Environment Profile presented in my last article (Circuit Cellar #377, December, 2021) [1].

Three key points of the firmware are: 1) the logical structure of Data Page 16, which contains the identification code of the command to be executed (light on/off); 2) the decoding function that extracts the command ID from the ANT+ message payload; and 3) the events handler that executes a specific procedure, depending on the command ID received.

To avoid compilation errors, allow me to briefly introduce the Network Key and the integration of SoftDevice S212. After SoftDevice download and before compiling the firmware code, you have to copy the headers file (/components/softdevice/s212/headers) to your SDK Install Folder, and uncomment the ant_license_key in the file nrf_sdm.h. When you get your network key or the public one, you have to insert it into the defines of the file ant_key_manager_config.h. These values are fundamental, and will be used during channel and profile initialization.

This profile, like the Environment Profile, isn’t included in the nRF52 SDK. I started with the profile presented in the previous article, adding support for the data page requests that might be sent from the remote control to the CtrlANT+, and then handled how it should respond to these requests.

As I discussed in the section, the main data pages are Data Page 7 and Data Page 16. Each page is always implemented in the same way, by defining a data structure containing the page values and the initialization constants for the default values, and two functions—one for encoding, and one for decoding. Listing 1 is a summary of the main instructions used for Data Page 16 development.

The initial struct follows the fields sequence in Table 2. In the decoding function, the pointer to the data buffer and the pointer to the data page are passed as input parameters. This function extracts the payload data of the ANT+ message received. In Listing 1 you can see the instructions of the decoding function. First, the payload is transferred to the data page logical structure (ant_ctrl_page_data_layout), so all fields are valued and formatted correctly (INSTRUCTION 1). Then the numeric ID of the received command is extracted by accessing the field referenced from the p_incoming_data pointer (INSTRUCTION 2). Finally, the data page field referenced by the p_page_data pointer is updated with the received command ID (INSTRUCTION 3).

Listing 2 contains the events handler instructions. It updates the status of the broadcasted Data Page 7, and executes the instructions for switching the light on and off.

Listing 1
Main Data Page 16 structure and decoding

typedef struct{
uint8_t sequence;  		//COMMAND SEQUENCE NUMBER
uint8_t res1;		//RESERVED
uint8_t res2;		//RESERVED
uint8_t vol_inc;		//INCREASE/DECREASE VOLUME IN %
uint8_t command;		//COMMAND ID
} ant_ctrl_page16_data_t;

.serialNR   = 65535,           
.sequence  = 0,    
 .res1 = 255,
 .res2 = 255,
 .vol_inc = 255,
 .command = 0

void ant_ctrl_page_16_encode(uint8_t  * p_page_buffer, ant_ctrl_page16_data_t const * p_page_data);
void ant_ctrl_page_16_decode(uint8_t const  * p_page_buffer, ant_ctrl_page16_data_t * p_page_data);

void ant_ctrl_page_16_decode(uint8_t const * p_page_buffer, ant_ctrl_page16_data_t * p_page_data)
     ant_ctrl_page16_data_layout_t const * p_incoming_data = (ant_ctrl_page16_data_layout_t *)p_page_buffer; //INSTRUCTION 1
     uint8_t comando = (uint8_t)(p_incoming_data->command);  //extracting command from remote control  //INSTRUCTION 2

     p_page_data->command  = comando; //INSTRUCTION 3
Listing 2
CtrlANT+ Events handler instructions

void ant_ctrl_evt_handler(ant_ctrl_profile_t * p_profile, ant_ctrl_evt_t event)
  switch (event)
		switch (m_ant_ctrl.page_16.command)
		case 147:  //Recording
		case 148:  //Photo	
		m_ant_ctrl.page_7.state = 7;
		if (tot>=4)	{tot=0;	m_ant_ctrl.page_7.time_prog = m_ant_ctrl.page_7.time_prog + 1;}
		case 131:  //Stop
		m_ant_ctrl.page_7.state = 3;
	case ANT_CTRL_PAGE_16_UPDATED:   /* fall through */
	case ANT_CTRL_PAGE_80_UPDATED:   /* fall through */
        	case ANT_CTRL_PAGE_81_UPDATED:  /* fall through */			

The events handler consists of two nested switch statements. The first establishes the data page to be updated, and the second discriminates the command received from the remote control. Let’s focus our attention on the latter.

The two command IDs related to “Recording” and “Take a Photo” allow you to turn on the light in two alternate ways provided by the GPS graphical interface. The instructions executed are the same. The status of the main Data Page 7 is updated, bringing it to the value 7 (Recording: the video device is recording; the light is ON). The Time Progressed field is updated; it indicates the number of seconds elapsed in the recording mode (Light ON). The same broadcast transmission frequency is used (4Hz)—an increase of 1 second every four events. Therefore, a counter called “tot” is incremented at each event. As soon as it reaches the value 4, the m_ant_ctrl.page_7.time_prog variable is updated. Finally, GPIO line P0.17 goes to a high logic level to polarize the transistor base and turn on the light for 250ms. Then it goes to a low logic level for another 250ms. And again, it turns the light on for 250ms and off for another 250ms. In this way, the light flashes with a frequency equal to half the broadcast frequency (4/2 = 2MHz), or two times per second.

The instructions relating to Stop determine the light switching off. The function updates the main Data Page 7 status, bringing it to the value 3 (Stop: the video device is stopped; the light is OFF). The status change also determines the position update of the central button on the GPS handheld interface. In Recording, it moves upwards, and in Stop, it moves downwards. Subsequently, GPIO line P0.17 goes to a low logic level, bringing the base current of the transistor to 0 and turning off the four LEDs.


I tested the CtrlANT+ circuit with a Garmin 64st GPS navigator. After the batteries have been connected, we pair our device with the navigator. To do this, go to the Settings menu, and select “VIRB Remote item.” After a few seconds of searching, the status will switch to “Connected.” A form with two buttons will be displayed—“Record” and “Take a Photo.”

Let’s go to the first button that turns blue. If we press the Enter key, it will shift down, a seconds counter will appear in the upper right corner, and the small rectangle will start flashing. The light turns on. If we press Enter again, the button will shift down and the light will turn off.

If we move to the “Take a Photo” button and press Enter, the button will shift up, and the seconds counter will be displayed again. Figure 6 is the instructions sequence through the GPS screens.

Figure 6 
CtrlANT+ and GPSMAP 64st screens for pairing and turning the LEDs on and off
Figure 6
CtrlANT+ and GPSMAP 64st screens for pairing and turning the LEDs on and off

Once again, the ANT+ protocol has proved useful for implementing a sport device—specifically, integrating it with a commercial device such as a handheld GPS. This project can be compared with the previous one (a multipurpose sports-wearable device) [1], to understand how the ANT+ device profile development takes place. The template can be used as a basis for the development of generic control profiles or to experiment with other profiles.


Advertise Here

The source code may be useful in the future for experiments on MCUs even richer in hardware resources, such as the popular nRF52840, or to try out some new features made available by the Nordic Semiconductor SDK.

In the files for this project, in addition to the source code and schematic, you’ll find the SketchUp box model that can you can reproduce with a 3D printer. For now, I hope this circuit is useful to those who want to experience a device system integration on ANT+. See you in my next article! 

Parts List
C1,C2,C11,C12=22pF cap
C3=0.8pF cap
C4,C5,C8,C9=100nF cap
C10=1.0uF cap
C13=Not Connected cap
C14,C15=12pF cap
C6,C7=100pF cap
R1=4.7KΩ res
R2=10Ω res
X1=32MHz crystal
L1=3.9nH ind
L2=10uH ind
L3=15nH ind
D1,D2,D3,D4=5mm ultra bright red led
BT1=3.0v battery 2xAAA
Q1=BC547 NPN transistor
P1=4pin header

[1] Carlos Tauraso, “Sports Wearable Uses ANT+ Protocol” Circuit Cellar 377, December 2021.
[2] ANT+ configuration parameters:
[3] ANT+ Device Profiles:

Nordic Semiconductor |
Sketchup |

Garmin GPSMAP 64st:
Garmin VIRB Action CAM Remote Control:

Code and Supporting Files


Keep up-to-date with our FREE Weekly Newsletter!

Don't miss out on upcoming issues of Circuit Cellar.

Note: We’ve made the Dec 2022 issue of Circuit Cellar available as a free sample issue. In it, you’ll find a rich variety of the kinds of articles and information that exemplify a typical issue of the current magazine.

Would you like to write for Circuit Cellar? We are always accepting articles/posts from the technical community. Get in touch with us and let's discuss your ideas.

Sponsor this Article
+ posts

Carlo Tauraso ( studied computer engineering at the University of Trieste in Italy and wrote his first assembler code for the Sinclair Research ZX Spectrum. He is currently a senior software engineer, who does firmware development on network devices and various types of micro-interfaces for a variety of European companies. Several of Carlo’s articles and programming courses about Microchip Technology PIC MCUs (USB-PIC, CAN bus PIC, SD CARD, C18) have been published in Italy, France and Spain. In his spare time, Carlo enjoys playing with radio scanners and homemade metal detectors.

Supporting Companies

Upcoming Events

Copyright © KCK Media Corp.
All Rights Reserved

Copyright © 2024 KCK Media Corp.

Bicycle Light Using ANT+ Protocol

by Carlo Tauraso time to read: 16 min