Welcome Guest,Register Now
Log In

ANT Forum

Welcome guest, please Login or Register

   

Using Demo.cpp as base for display?

Avatar
RankRankRankRank

Total Posts: 123

Joined 2013-10-07

PM

Hi guys,

Just a general question, I want to implement the Heart Rate profile in my application. My application would only show the Heart rate in real time, and also general info on the sensor (battery low, etc.) for the newer HR straps that permits it.

Right now I just started with ANT, I used the Demo.cpp included in "ANT_Windows_Library_Package_v3.2/DEMO_LIB" and tweaked it a little bit. My question is; is demo.cpp a good starting point to develop the profile? Right now I can retrieve the computed heart rate fine with this demo. I am unsure about more functionality with this code though.

Thanks if you can guide me a little bit!

Current running demo.exe example :
https://www.dropbox.com/s/u9i7x0ybn3a32dr/run.png

code used :
// If we recieved a data message, display its contents here.
    
if(bPrintBuffer)
    
{
        
if(bDisplay)
        
{
            printf
("[x],[x],[x],[x],[x],[x],[x],[x]\n",
                   
stMessage.aucData[ucDataOffset 0],
                    
stMessage.aucData[ucDataOffset 1],
                    
stMessage.aucData[ucDataOffset 2],
                    
stMessage.aucData[ucDataOffset 3],
                    
stMessage.aucData[ucDataOffset 4],
                    
stMessage.aucData[ucDataOffset 5],
                    
stMessage.aucData[ucDataOffset 6],
                    
stMessage.aucData[ucDataOffset 7]);
            
printf("Computed HR %d : \n"stMessage.aucData[ucDataOffset 7]);
        
}
        
else
        
{
            
static int iIndex 0;
            static 
char ac[] {'|','/','-','\\'};
            
printf("Rx: %c\r",ac[iIndex++]); fflush(stdout);
            
iIndex &= 3;
        
}
    } 
     

Signature

——————————————————
Free Indoor Cycling Software - https://maximumtrainer.com

Avatar
RankRankRankRank

Total Posts: 745

Joined 2012-09-14

PM

Hi,

The DEMO_LIB is as good a place to start if you'd prefer to use the raw library on it's own in your application.

Otherwise I would recommend building your application around the ANT_DLL (C++) or the ANT_NET (C#) library wrappers so you can take advantage of new library updates without merging and recompiling code.

There is DEMO_DLL for building around the C++ wrapper library and there are multiple examples for building around the C# library such as DEMO_NET or ANT_NetDll_Demo.

In terms of decoding the Heart Rate Monitor Embedded Example isn't a bad place to look for tips, in general just determine if it's a page supporting device by seeing if the toggle bit is changing, and then switch off the page numbers to determine how to read the rest of the payload.

Cheers      
Avatar
RankRankRankRank

Total Posts: 123

Joined 2013-10-07

PM

Hi thanks for taking the time to help a newbie!

I thought of using the ANT_DLL, but I was unsure if this solution would be cross-platform compatible. Right now I just imported all the ANT_LIB code in QtCreator and compile it to ANT_LIB.lib, and my DEMO_LIB make use of this static lib. In case of a new version, I would just overwrite the ANT_LIB code (I will not modify it) and compile again.
If there is a better solution you can hint me feel free! but I can't use any C# in my software.

Is there a documentation that cover all the functionality provided in ANT_LIB or we must browse in the code to find out? Right now what I'm looking to achieve is to connect my usb stick to multiple sensor at the same time. The demo.cpp works fine for pairing one. In other words, I would like to add multiple MessageThread to the demo.cpp example but I'm struggling a little bit.
Does ANT provide consulting to help start up?

Thank you!

     

Signature

——————————————————
Free Indoor Cycling Software - https://maximumtrainer.com

Avatar
RankRankRankRank

Total Posts: 745

Joined 2012-09-14

PM

Hi,

In this case yes, the ANT_LIB would be the best choice for cross-platform compatibility. As a note the Mac OS X library shares the same high level interface as the Windows ANT_LIB but differs on the low level. You can compare between the two to see where the high level interface resides.

Virtually all of the functions in the library except for a few convenience/logging functions are just wrappers for the embedded ANT interface which is detailed in the ANT Message Protocol and Usage document.

While the threading is not externally documented, you do not need to create any additional threads. You can open additional slave channels to receive from additional devices and use the same receive thread to process the messages. An additional note, if 8-channels is insufficient for your application, you will need to use Continuous Scanning Mode or Background Scanning Channel (on USB-m devices) to communicate with more devices.

We do not currently offer direct consultancy services at this time, although ANT+ Alliance Membership does confer some additional support benefits, you can e-mail info @thisisant.com for more information.

Cheers      
Avatar
RankRankRankRank

Total Posts: 123

Joined 2013-10-07

PM

Wow thanks for the valuable information on ANT_LIB.

I read the "ANT_AN01_Implementing_a_Receiver_for_Tx_only_ANT_devices.pdf" and this is exactly what I want to achieve, The documentation is really good.
One last thing, If you look at page 6 of "ANT_AN01.pdf", the schema illustrate a switch on the device channel to decode the correct message type. Is using the device type okay? (see pseudo-code below)
Thanks!


// If we recieved a data message, we decode its content here and display it
    
if(bPrintBuffer)
    
{
        
if(bDisplay)
        
{
            printf
("[x],[x],[x],[x],[x],[x],[x],[x]\n",
                   
stMessage.aucData[ucDataOffset 0],
                    
stMessage.aucData[ucDataOffset 1],
                    
stMessage.aucData[ucDataOffset 2],
                    
stMessage.aucData[ucDataOffset 3],
                    
stMessage.aucData[ucDataOffset 4],
                    
stMessage.aucData[ucDataOffset 5],
                    
stMessage.aucData[ucDataOffset 6],
                    
stMessage.aucData[ucDataOffset 7]);

            
// Get the deviceType so we correctly decode the message.
            
UCHAR ucDeviceType =  stMessage.aucData[MESSAGE_BUFFER_DATA13_INDEX];

            if (
ucDeviceType == 120{
                
//TODO: use antplus_heart_rate_monitor_v1.3.0 decoding classes to decode correctly
               
printf("DECODE HR PAGE...\n");
               
printf("Computed HR %d : \n"stMessage.aucData[ucDataOffset 7]);
            
}
            
else if (ucDeviceType == 121{
                printf
("DECODE S&C PAGE...\n");
            
}
            
else if (ucDeviceType == 11{
                printf
("DECODE POWER PAGE...\n");
            
}
            
else {
                printf
("Device not support by app...\n");
            
}
        }
... 
     

Signature

——————————————————
Free Indoor Cycling Software - https://maximumtrainer.com

Avatar
RankRankRankRank

Total Posts: 745

Joined 2012-09-14

PM

This implementation should work fine, although it will probably make handling device pairing more difficult when there are several devices of the same device type in the area.

For more information on Device Pairing I would recommend reading AN02 Device Pairing.      
Avatar
RankRankRankRank

Total Posts: 123

Joined 2013-10-07

PM

I'm getting more familar with the demo.cpp code.
I'm not there yet but what I want to implement is the Search list for each device type.

About what we discussed, opening a separate channel for each device.
I'm trying to catch the device type in the "ProcessMessage" function.
For some reason the device number doesn't match with my device (120)
Have any idea what could cause that? See code below

Thanks

[Edit]
I guess the format of the message is not the same as "MESG_EXT_BURST_DATA_ID"

switch(stMessage.aucData[1])
        
{
        
case MESG_NETWORK_KEY_ID:
        
{
            
if(stMessage.aucData[2] != RESPONSE_NO_ERROR)
            
{
                printf
("Error configuring network key: Code 0%d\n"stMessage.aucData[2]);
                break;
            
}
            qDebug
() << "Network key set";
            
qDebug() << "Assigning channel...";


            
UCHAR ucDeviceType =  stMessage.aucData[MESSAGE_BUFFER_DATA4_INDEX];
            
printf("Device Type: %d\n "ucDeviceType);  // return 144, my device is a HR strap (120) ??

            //HR
            
if (ucDeviceType == 120{
                bStatus 
pclMessageObject->AssignChannel(000MESSAGE_TIMEOUT);
            
}
            
//S&C
            
else if (ucDeviceType == 121{
                bStatus 
pclMessageObject->AssignChannel(100MESSAGE_TIMEOUT);
            
}
            
//Power
            
else if (ucDeviceType == 11{
                bStatus 
pclMessageObject->AssignChannel(200MESSAGE_TIMEOUT);
            
}
            
else {
                bStatus 
pclMessageObject->AssignChannel(USER_ANTCHANNEL00MESSAGE_TIMEOUT);
            
}

            
break;
        
     

Signature

——————————————————
Free Indoor Cycling Software - https://maximumtrainer.com

Avatar
RankRankRankRank

Total Posts: 745

Joined 2012-09-14

PM

The easiest way to implement connecting to multiple sensors is to set up each channel you open to only acquire each specific device type you need (Device Number - Wildcard, Transmission Type - Wildcard, Device Type - 120 or 121 or 11, etc), and then use your switch statement on the channel number. This ensures you know what type of device each channel will be acquiring beforehand.

There are a couple of ways of getting the device ID (device number, device type, transmission type) once the slave channel acquires a master.

1. Request the channel ID, this will cause the slave channel to return the device ID of the master channel it acquired. Once a wildcarded slave channel acquires a master channel, it's device ID changes to that of the acquired master. You would then process the response from your message receiver. This is likely the easiest solution.

2. Enable extended messaging for the USB stick. This will cause every broadcast/acknowledged/burst message received by the slave channel to carry the channel ID of the master channel it has acquired. This function is used by the RSSI Demo and in the AN14 Continuous Scanning Mode application note, where requesting the channel ID is not useful under continuous scan mode or background scanning channel as the slave "channel" will remain wildcarded.

All of these functions are documented under the ANT Message Protocol and Usage document. Pages 54-55 of revision 5.0 shows what types of messages can be requested.      
Avatar
RankRankRankRank

Total Posts: 123

Joined 2013-10-07

PM

Harrison - 11 November 2013 01:15 PM
The easiest way to implement connecting to multiple sensors is to set up each channel you open to only acquire each specific device type you need (Device Number - Wildcard, Transmission Type - Wildcard, Device Type - 120 or 121 or 11, etc)



In the code up there, what i'm trying to achieve is to open a specific channel for each device type, so that later I can switch on that channel and know which device i'm dealing with.

The first message (before opening a channel) that I receive from a master is "MESG_NETWORK_KEY_ID"
In there I need to find the master device type, so that I open the right channel. The problem is that I don't really know the format of the message "MESG_NETWORK_KEY_ID" so I'm failing at retrieving the device type.

/////////////////////////////////////////////////////////////////////////////
// Message Format
// Messages are in the format:
//
// AX XX YY -------- CK
//
// where: AX    is the 1 byte sync byte either transmit or recieve
//        XX    is the 1 byte size of the message (0-249) NOTE: THIS WILL BE LIMITED BY THE EMBEDDED RECEIVE BUFFER SIZE
//        YY    is the 1 byte ID of the message (1-255, 0 is invalid)
//        ----- is the data of the message (0-249 bytes of data)
//        CK    is the 1 byte Checksum of the message 



Thanks and sorry for all the questions wink

[EDIT: Nevermind, I guess I should open the correct channel in the message MESG_ASSIGN_CHANNEL_ID instead, where you can acces the device type ID.
Thanks]      

Signature

——————————————————
Free Indoor Cycling Software - https://maximumtrainer.com

Avatar
RankRankRankRank

Total Posts: 745

Joined 2012-09-14

PM

MESG_NETWORK_KEY_ID is actually a type of response from the master, indicating that you attempted to set the network key for the channel.

This code is probably nested underneath something that looks like this:
switch(stMessage.ucMessageID)
{
    
//RESPONSE MESG
    
case MESG_RESPONSE_EVENT_ID:
    
{
        
//RESPONSE TYPE
         
switch(stMessage.aucData[1])
         
{         
            
case MESG_NETWORK_KEY_ID:
            
{
              
.... 


What is happening is that you're currently switching on response messages. Whenever you send ANT commands, ANT sends a message back in return as a "response". MESG_NETWORK_KEY_ID is actually also an int value which represents the command, which in this case is 0x46, and bytes after that represent the response.

There is also a convenience method to do this called GetChannelID() in the DSIFramerANT class which will automatically process the response message after you request the channel ID.      
Avatar
RankRankRankRank

Total Posts: 123

Joined 2013-10-07

PM

Yes the first message I receive from the master is a "MESG_NETWORK_KEY_ID "
I receive this after setting the network key in my Slave to 0 (wildcard) in the function "InitANT(void)"
pclMessageObject->SetNetworkKey(USER_NETWORK_NUMucNetKeyMESSAGE_TIMEOUT); 


After that, any master will respond and that's where i'm trying to open the good channel, maybe it's not the right place.
Basically my program needs to :

- Init ANT+ usb stick : done
- Pair HR sensor to a specific channel:
- Pair S&C sensor to a specific channel:
- Pair Power sensor to a specific channel:

Then my main program will communicate to retrieve the data when it's ready from the demo.cpp(hub) thread.
Thanks for your help and sorry for the spam, just keeping a road map to myself smile


////////////////////////////////////////////////////////////////////////////////
// InitANT
//
// Resets the system and starts the test
//
////////////////////////////////////////////////////////////////////////////////
BOOL Hub::InitANT(void)
{

    qDebug
() << "Init Ant...";
    
BOOL bStatus;

    
// Reset system
    
printf("Resetting module...\n");
    
bStatus pclMessageObject->ResetSystem();
    
DSIThread_Sleep(1000);

    
// Start the test by setting network key
    
printf("Setting network key...\n");
    
UCHAR ucNetKey[8] USER_NETWORK_KEY;

    
//TODO: IMPORTANT FIRST MSG HERE
    
bStatus pclMessageObject->SetNetworkKey(USER_NETWORK_NUMucNetKeyMESSAGE_TIMEOUT);


    return 
bStatus;


current app demo :
https://www.dropbox.com/s/l5co71b80ifd6gt/demoAnt.png
     

Signature

——————————————————
Free Indoor Cycling Software - https://maximumtrainer.com

Avatar
RankRankRankRank

Total Posts: 123

Joined 2013-10-07

PM

I found the problem..
I thought the response message where coming from the master, but they were in fact response to config message from the slave itself.

I removed the config message response in the "processMessage function" and now doing a linear approach to setting up the channel and it's working fine.
Thank you


bStatus = pclMessageObject->SetNetworkKey(USER_NETWORK_NUM, ucNetKey, MESSAGE_TIMEOUT);
bStatus = pclMessageObject->AssignChannel(USER_ANTCHANNEL, 0, 0, MESSAGE_TIMEOUT);
bStatus = pclMessageObject->SetChannelID(USER_ANTCHANNEL, USER_DEVICENUM, 121, USER_TRANSTYPE, MESSAGE_TIMEOUT);
bStatus = pclMessageObject->SetChannelRFFrequency(USER_ANTCHANNEL, USER_RADIOFREQ, MESSAGE_TIMEOUT);
bStatus = pclMessageObject->SetChannelPeriod(USER_ANTCHANNEL, usMessagePeriod);
bStatus = pclMessageObject->OpenChannel(USER_ANTCHANNEL, MESSAGE_TIMEOUT);
     

Signature

——————————————————
Free Indoor Cycling Software - https://maximumtrainer.com