├── ANTPlus.cpp ├── ANTPlus.h ├── README.md ├── antdefines.h ├── antmessage.h ├── examples └── ANTPlus_HearRateMonitor │ └── ANTPlus_HearRateMonitor.ino └── types.h /ANTPlus.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2013 Brody Kenrick. 2 | //An Ant+ library over UART ('Serial' or SoftwareSerial) 3 | 4 | #define __ASSERT_USE_STDERR 5 | #include 6 | 7 | #include "ANTPlus.h" 8 | 9 | 10 | #if defined(ANTPLUS_DEBUG) 11 | #define ANTPLUS_DEBUG_PRINT(x) (Serial.print(x)) 12 | #define ANTPLUS_DEBUG_PRINTLN(x) (Serial.println(x)) 13 | #else 14 | #define ANTPLUS_DEBUG_PRINT(x) 15 | #define ANTPLUS_DEBUG_PRINTLN(x) 16 | //NOTE: The printPacket function still calls Serial directly. TODO: Adjust that. 17 | #endif 18 | 19 | ANTPlus::ANTPlus( 20 | byte RTS_PIN, 21 | byte SUSPEND_PIN, 22 | byte SLEEP_PIN, 23 | byte RESET_PIN 24 | ) 25 | { 26 | this->RTS_PIN = RTS_PIN; 27 | this->SUSPEND_PIN = SUSPEND_PIN; 28 | this->SLEEP_PIN = SLEEP_PIN; 29 | this->RESET_PIN = RESET_PIN; 30 | 31 | hw_reset_count = 0; 32 | } 33 | 34 | 35 | void ANTPlus::begin(Stream &serial) 36 | { 37 | mySerial = &serial; 38 | 39 | pinMode(SUSPEND_PIN, OUTPUT); 40 | pinMode(SLEEP_PIN, OUTPUT); 41 | pinMode(RESET_PIN, OUTPUT); 42 | pinMode(RTS_PIN, INPUT); 43 | 44 | //Per datasheet 45 | digitalWrite(RESET_PIN, HIGH); 46 | digitalWrite(SUSPEND_PIN, HIGH); 47 | digitalWrite(SLEEP_PIN, LOW); 48 | 49 | //Interrupts are set in the main program currently 50 | //TODO: Look to see if they should be brought 'in lib' 51 | 52 | 53 | //This should not be strictly necessary - the device should always come up by itself.... 54 | //But let's make sure we didn't miss the first RTS in a power-up race 55 | hardwareReset(); 56 | } 57 | 58 | 59 | void ANTPlus::hardwareReset() 60 | { 61 | ANTPLUS_DEBUG_PRINTLN("H/w Reset"); 62 | 63 | sleep(false); 64 | digitalWrite(RESET_PIN, LOW); 65 | delay(5); 66 | //Reset all variables before we release the ANT 67 | clear_to_send = false; 68 | msgResponseExpected = MESG_START_UP; 69 | rxBufCnt = 0; 70 | rx_packet_count = 0; 71 | tx_packet_count = 0; 72 | hw_reset_count++; 73 | delay(5); 74 | digitalWrite(RESET_PIN, HIGH); 75 | } 76 | 77 | // Data 78 | // always 0xa4 79 | // 0x40==MESG_RESPONSE_EVENT_ID denoting a channel response / event 80 | // 0x4E==MESG_BROADCAST_DATA_ID denoting a broadcast (e.g. HRM or SDM) 81 | // success is 0. See page 84 of ANT MPaU for other codes 82 | //readTimeoutMs -- is amount of time to wait for first byte to appeaer (can be 0) 83 | MESSAGE_READ ANTPlus::readPacketInternal( ANT_Packet * packet, int packetSize, unsigned int readTimeoutMs) 84 | { 85 | unsigned char byteIn; 86 | unsigned char chksum = 0; 87 | unsigned long timeoutExit = millis() + readTimeoutMs; 88 | 89 | while (timeoutExit >= millis()) //First loop will go through always 90 | { 91 | //This is a busy read 92 | if (mySerial->available() > 0) 93 | { 94 | byteIn = mySerial->read(); 95 | //We have a byte -- so we want to finish off this message (increase timeout) 96 | timeoutExit += ANT_PACKET_READ_NEXT_BYTE_TIMEOUT_MS; 97 | if ((byteIn == MESG_TX_SYNC) && (rxBufCnt == 0)) 98 | { 99 | rxBuf[rxBufCnt++] = byteIn; 100 | chksum = byteIn; 101 | } 102 | else if ((rxBufCnt == 0) && (byteIn != MESG_TX_SYNC)) 103 | { 104 | return MESSAGE_READ_ERROR_MISSING_SYNC; 105 | } 106 | else if (rxBufCnt == 1) 107 | { 108 | rxBuf[rxBufCnt++] = byteIn; // second byte will be size 109 | chksum ^= byteIn; 110 | } 111 | else if (rxBufCnt < rxBuf[1]+3) 112 | { // read rest of data taking into account sync, size, and checksum that are each 1 byte 113 | rxBuf[rxBufCnt++] = byteIn; 114 | chksum ^= byteIn; 115 | } 116 | else 117 | { 118 | rxBuf[rxBufCnt++] = byteIn; 119 | if (rxBufCnt > packetSize) 120 | { 121 | //Likely we are missing something.... 122 | //we reset our buffer count 123 | rxBufCnt = 0; 124 | return MESSAGE_READ_ERROR_PACKET_SIZE_EXCEEDED; 125 | } 126 | else 127 | { 128 | memcpy(packet, &rxBuf, rxBufCnt); // Should be a complete packet. copy data to packet variable, check checksum and return 129 | rx_packet_count++; 130 | if (chksum != ANT_PACKET_CHECKSUM(packet)) 131 | { 132 | rxBufCnt = 0; 133 | return MESSAGE_READ_ERROR_BAD_CHECKSUM; 134 | } 135 | else 136 | { 137 | //Good packet 138 | rxBufCnt = 0; 139 | return MESSAGE_READ_INTERNAL; 140 | } 141 | } 142 | } 143 | } 144 | } 145 | 146 | if(rxBufCnt != 0) 147 | { 148 | //This may be recoverable but it is likely not worth the effort 149 | //we reset our buffer count 150 | rxBufCnt = 0; 151 | 152 | return MESSAGE_READ_INFO_TIMEOUT_MIDMESSAGE; 153 | } 154 | return MESSAGE_READ_NONE; 155 | } 156 | 157 | //! Write out a single byte and return the updated checksum 158 | unsigned char ANTPlus::writeByte(unsigned char out, unsigned char chksum) 159 | { 160 | #ifdef ANTPLUS_DEBUG 161 | serial_print_byte_padded_hex(out); 162 | Serial.print(" "); 163 | #endif 164 | mySerial->write(out); 165 | chksum ^= out; 166 | return chksum; 167 | } 168 | 169 | //TODO: DEBUG: Convert (or add function) for a packet struct for quicker/easier printing.... 170 | //TODO: Extend the return types 171 | // msgId_ResponseExpected if set to another ID than MESG_INVALID_ID will not allow a subsequent send until that message is received. 172 | // NOTE: This request/response check still has the potentioal for holes in it but it is sufficient for now 173 | boolean ANTPlus::send(unsigned msgId, unsigned msgId_ResponseExpected, unsigned char argCnt, ...) 174 | { 175 | va_list arg; 176 | va_start (arg, argCnt); 177 | unsigned char byteOut; 178 | unsigned char chksum = 0; 179 | int cnt = 0; 180 | 181 | boolean ret_val = false; 182 | 183 | if(clear_to_send && (msgResponseExpected == MESG_INVALID_ID)) 184 | { 185 | #ifdef ANTPLUS_DEBUG 186 | Serial.print("TX["); 187 | serial_print_int_padded_dec( tx_packet_count, 6 ); 188 | Serial.print("] @ "); 189 | serial_print_int_padded_dec( millis(), 8 ); 190 | Serial.print(" ms > "); 191 | #if defined(ANTPLUS_MSG_STR_DECODE) 192 | Serial.print( get_msg_id_str(msgId) ); 193 | Serial.print("[0x"); 194 | serial_print_byte_padded_hex(msgId); 195 | Serial.print("]"); 196 | #else 197 | Serial.print("0x"); 198 | serial_print_byte_padded_hex(msgId); 199 | #endif //defined(ANTPLUS_MSG_STR_DECODE) 200 | Serial.print(" - 0x"); 201 | #endif 202 | tx_packet_count++; 203 | 204 | chksum = writeByte(MESG_TX_SYNC, chksum); // send sync 205 | chksum = writeByte(argCnt, chksum); // send length 206 | chksum = writeByte(msgId, chksum); // send message id 207 | 208 | // Send data 209 | for (cnt=1; cnt <= argCnt; cnt++) 210 | { 211 | byteOut = va_arg(arg, unsigned int); 212 | chksum = writeByte(byteOut, chksum); 213 | } 214 | va_end(arg); 215 | 216 | writeByte(chksum,chksum); // send checksum 217 | 218 | clear_to_send = false; 219 | ret_val = true; 220 | 221 | //We are now waiting for this message (if it was not set as INVALID) 222 | //There are other functions that take care of the checks 223 | //and eventually will have timeouts... and possibly callbacks... 224 | msgResponseExpected = msgId_ResponseExpected; 225 | #ifdef ANTPLUS_DEBUG 226 | Serial.println(); 227 | #endif 228 | } 229 | else 230 | { 231 | //ANTPLUS_DEBUG_PRINTLN("Can't send -- not clear to send or awaiting a response"); 232 | ret_val = false; 233 | } 234 | 235 | return ret_val; 236 | } 237 | 238 | 239 | //! Read a packet into ANT_Packet struct 240 | //readTimeoutMs -- is amount of time to wait for first byte to appeaer (can be 0) 241 | //Return an indication of error, no packet received, the expected packet was received or another packet was received. 242 | MESSAGE_READ ANTPlus::readPacket( ANT_Packet * packet, int packetSize, int wait_timeout = 0 ) 243 | { 244 | MESSAGE_READ ret_val = MESSAGE_READ_NONE; 245 | { 246 | ret_val = readPacketInternal(packet, packetSize, wait_timeout); 247 | if (ret_val == MESSAGE_READ_INTERNAL) 248 | { 249 | if( packet->msg_id == msgResponseExpected ) 250 | { 251 | //ANTPLUS_DEBUG_PRINTLN("Received expected message!"); 252 | msgResponseExpected = MESG_INVALID_ID; //Not waiting on anything anymore 253 | ret_val = MESSAGE_READ_EXPECTED; 254 | } 255 | else 256 | { 257 | //ANTPLUS_DEBUG_PRINTLN("Received unexpected message!"); 258 | ret_val = MESSAGE_READ_OTHER; 259 | } 260 | } 261 | } 262 | return ret_val; 263 | } 264 | 265 | 266 | 267 | 268 | 269 | //TODO: Move these to progmem 270 | #ifdef ANTPLUS_MSG_STR_DECODE 271 | //! returns msg_id converted into a human readable string. 272 | const char * ANTPlus::get_msg_id_str(byte msg_id) 273 | { 274 | switch (msg_id) 275 | { 276 | case MESG_RESPONSE_EVENT_ID: 277 | return "RESPONSE_EVENT"; 278 | case MESG_CAPABILITIES_ID: 279 | return "CAPABILITIES"; 280 | case MESG_BROADCAST_DATA_ID: 281 | return "BROADCAST_DATA"; 282 | case MESG_ASSIGN_CHANNEL_ID: 283 | return "ASSIGN_CHANNEL"; 284 | case MESG_CHANNEL_MESG_PERIOD_ID: 285 | return "CHANNEL_MESG_PERIOD"; 286 | case MESG_CHANNEL_SEARCH_TIMEOUT_ID: 287 | return "CHANNEL_SEARCH_TIMEOUT"; 288 | case MESG_CHANNEL_RADIO_FREQ_ID: 289 | return "CHANNEL_RADIO_FREQ"; 290 | 291 | case MESG_REQUEST_ID: 292 | return "REQUEST"; 293 | 294 | case MESG_START_UP: 295 | return "START_UP"; 296 | 297 | case MESG_NETWORK_KEY_ID: 298 | return "NETWORK_KEY"; 299 | case MESG_SYSTEM_RESET_ID: 300 | return "SYSTEM_RESET"; 301 | case MESG_OPEN_CHANNEL_ID: 302 | return "OPEN_CHANNEL"; 303 | case MESG_CHANNEL_ID_ID: 304 | return "CHANNEL_ID"; 305 | 306 | default: 307 | return "..."; 308 | 309 | } 310 | } 311 | #endif 312 | 313 | 314 | //NOTE: This function calls Serial.println directly 315 | void ANTPlus::serial_print_byte_padded_hex(byte value) 316 | { 317 | if(value <= 0x0F) 318 | { 319 | Serial.print(0, HEX); 320 | } 321 | Serial.print(value, HEX); 322 | } 323 | 324 | //NOTE: This function calls Serial.println directly 325 | void ANTPlus::serial_print_int_padded_dec(long int value, unsigned int width, boolean final_carriage_return) 326 | { 327 | int div_num = value; 328 | int div_cnt = 0; 329 | while( div_num /= 10 ) 330 | { 331 | div_cnt++; 332 | } 333 | if(div_cnt < width) 334 | { 335 | div_cnt = width - div_cnt - 1; 336 | } 337 | else 338 | { 339 | div_cnt = 0; 340 | } 341 | while( div_cnt-- ) 342 | { 343 | Serial.print("0"); 344 | } 345 | if(final_carriage_return) 346 | { 347 | Serial.println(value); 348 | } 349 | else 350 | { 351 | Serial.print(value); 352 | } 353 | } 354 | 355 | 356 | //! Print a packet for debugging. Does decoding of some ids/codes 357 | //NOTE: This function calls Serial.println directly 358 | void ANTPlus::printPacket(const ANT_Packet * packet, boolean final_carriage_return = true) 359 | { 360 | Serial.print("RX["); 361 | serial_print_int_padded_dec( rx_packet_count, 6, false ); 362 | Serial.print("] @ "); 363 | serial_print_int_padded_dec( millis(), 8, false ); 364 | Serial.print(" ms > "); 365 | // Serial.print("0x"); 366 | // serial_print_byte_padded_hex(packet->sync); 367 | // Serial.print(" "); 368 | Serial.print( packet->length ); 369 | Serial.print("B "); 370 | 371 | #if defined(ANTPLUS_MSG_STR_DECODE) 372 | Serial.print( get_msg_id_str (packet->msg_id) ); 373 | Serial.print("[0x"); 374 | serial_print_byte_padded_hex(packet->msg_id); 375 | Serial.print("]"); 376 | #else 377 | Serial.print("0x"); 378 | serial_print_byte_padded_hex(packet->msg_id); 379 | #endif //defined(ANTPLUS_MSG_STR_DECODE) 380 | Serial.print(" : "); 381 | Serial.print("0x"); 382 | int cnt = 0; 383 | while( cnt < ( packet->length ) ) 384 | { 385 | serial_print_byte_padded_hex( packet->data[cnt] ); 386 | Serial.print (" "); 387 | cnt++; 388 | } 389 | if(final_carriage_return) 390 | { 391 | Serial.println(""); 392 | } 393 | else 394 | { 395 | Serial.print(" "); 396 | } 397 | } 398 | 399 | //Must be called with the same channel until an error or established (i.e. don't start with a different channel in the middle -- one channel at a time) 400 | //TODO: Test that interleaved calls is relaxed (s.b. with moving of state_counter to struct) 401 | //Must not be called with the same channel after it returns ESTABLISHED as that will attempt to reopen.... 402 | ANT_CHANNEL_ESTABLISH ANTPlus::progress_setup_channel( ANT_Channel * channel ) 403 | { 404 | boolean sent_ok = true; //Defaults as true as we want to progress the state counter 405 | 406 | ANT_CHANNEL_ESTABLISH ret_val = ANT_CHANNEL_ESTABLISH_PROGRESSING; 407 | 408 | if(channel->state_counter == 0) 409 | { 410 | //ANTPLUS_DEBUG_PRINTLN("progress_setup_channel() - Begin"); 411 | } 412 | else 413 | if(channel->state_counter == 1) 414 | { 415 | //Request CAPs 416 | sent_ok = send(MESG_REQUEST_ID, MESG_CAPABILITIES_ID/*Expected response*/, 2, 0/*Channel number always 0*/, MESG_CAPABILITIES_ID); 417 | } 418 | else 419 | if(channel->state_counter == 2) 420 | { 421 | // Assign Channel 422 | // Channel: 0 423 | // Channel Type: for Receive Channel 424 | // Network Number: 0 for Public Network 425 | sent_ok = send(MESG_ASSIGN_CHANNEL_ID, MESG_RESPONSE_EVENT_ID/*Expected response*/, 3, channel->channel_number, 0, channel->network_number); 426 | } 427 | else 428 | if(channel->state_counter == 3) 429 | { 430 | 431 | // Set Channel ID 432 | // Channel Number: 0 433 | // Device Number LSB: 0 for a slave to match any device 434 | // Device Number MSB: 0 for a slave to match any device 435 | // Device Type: bit 7 0 for pairing request bit 6..0 for device type 436 | // Transmission Type: 0 to match any transmission type 437 | sent_ok = send(MESG_CHANNEL_ID_ID, MESG_RESPONSE_EVENT_ID/*Expected response*/, 5, channel->channel_number, 0, 0, channel->device_type, 0); 438 | } 439 | else 440 | if(channel->state_counter == 4) 441 | { 442 | // Set Network Key 443 | // Network Number 444 | // Key 445 | sent_ok = send(MESG_NETWORK_KEY_ID, MESG_RESPONSE_EVENT_ID/*Expected response*/, 9, channel->network_number, channel->ant_net_key[0], channel->ant_net_key[1], channel->ant_net_key[2], channel->ant_net_key[3], channel->ant_net_key[4], channel->ant_net_key[5], channel->ant_net_key[6], channel->ant_net_key[7]); 446 | } 447 | else 448 | if(channel->state_counter == 5) 449 | { 450 | // Set Channel Search Timeout 451 | // Channel 452 | // Timeout: time for timeout in 2.5 sec increments 453 | sent_ok = send(MESG_CHANNEL_SEARCH_TIMEOUT_ID, MESG_RESPONSE_EVENT_ID/*Expected response*/, 2, channel->channel_number, channel->timeout); 454 | } 455 | else 456 | if(channel->state_counter == 6) 457 | { 458 | //ANT_send(1+2, MESG_CHANNEL_RADIO_FREQ_ID, CHAN0, FREQ); 459 | // Set Channel RF Frequency 460 | // Channel 461 | // Frequency = 2400 MHz + (FREQ * 1 MHz) (See page 59 of ANT MPaU) 0x39 = 2457 MHz 462 | sent_ok = send(MESG_CHANNEL_RADIO_FREQ_ID, MESG_RESPONSE_EVENT_ID/*Expected response*/, 2, channel->channel_number, channel->freq); 463 | } 464 | else 465 | if(channel->state_counter == 7) 466 | { 467 | // Set Channel Period 468 | sent_ok = send(MESG_CHANNEL_MESG_PERIOD_ID, MESG_RESPONSE_EVENT_ID/*Expected response*/, 3, channel->channel_number, (channel->period & 0x00FF), ((channel->period & 0xFF00) >> 8)); 469 | } 470 | else 471 | if(channel->state_counter == 8) 472 | { 473 | //Open Channel 474 | sent_ok = send(MESG_OPEN_CHANNEL_ID, MESG_RESPONSE_EVENT_ID/*Expected response*/, 1, channel->channel_number); 475 | } 476 | else 477 | if(channel->state_counter == 9) 478 | { 479 | //Check if the last message has been responded to 480 | if(!awaitingResponseLastSent()) 481 | { 482 | ret_val = ANT_CHANNEL_ESTABLISH_COMPLETE; 483 | //ANTPLUS_DEBUG_PRINTLN("progress_setup_channel() - Complete"); 484 | } 485 | else 486 | { 487 | sent_ok = false; //Set this to false as it enables the error checking below. 488 | } 489 | } 490 | 491 | 492 | if(sent_ok) 493 | { 494 | channel->state_counter++; 495 | } 496 | else 497 | { 498 | //Not always an error - as sometimes there are messages in the queue that are awaiting a response 499 | //ANTPLUS_DEBUG_PRINTLN("Issue sending...."); 500 | { 501 | if( digitalRead(RTS_PIN) == LOW) 502 | { 503 | static unsigned int sending_issue_counter = 0; 504 | sending_issue_counter++; 505 | 506 | //This should clear on the next loop ( this should be after ANT asserts -- but could conceivably be before it has even responded) 507 | // The ISR sets a loop flag and the ISR should be triggered within 50 usecs 508 | if(sending_issue_counter >= 50) 509 | { 510 | //Seems like we missed an RTS assertion..... 511 | ANTPLUS_DEBUG_PRINTLN( "Missed an RTS or none was executed by ANT. Restarting...." ); 512 | sending_issue_counter = 0; 513 | hardwareReset(); 514 | 515 | ret_val = ANT_CHANNEL_ESTABLISH_ERROR; 516 | } 517 | } 518 | } 519 | } 520 | 521 | channel->channel_establish = ret_val; 522 | 523 | return ret_val; 524 | } 525 | 526 | //! A function that is called when an RTS interrupt is received in the main program 527 | void ANTPlus::rTSHighAssertion() 528 | { 529 | //"Waiting for ANT to RTS (let us send again)." 530 | //Need to make sure it is low again 531 | while( digitalRead(RTS_PIN) != LOW ) 532 | { 533 | //TODO: Is this a bad idea in an ISR? 534 | delayMicroseconds(50); 535 | } 536 | clear_to_send = true; 537 | } 538 | 539 | 540 | //!Put ANT module into sleep mode. NOTE: This seems to have some issues. 541 | void ANTPlus::sleep(boolean activate_sleep) 542 | { 543 | int logic_level = HIGH; //Sleep 544 | if(!activate_sleep) 545 | { 546 | logic_level = LOW; //Wake 547 | } 548 | digitalWrite(SLEEP_PIN, logic_level); 549 | } 550 | 551 | //!Put ANT module into suspend mode. NOTE: Not implemented 552 | void ANTPlus::suspend(boolean activate_suspend) 553 | { 554 | //TODO: 555 | assert(false); 556 | } 557 | 558 | // SDM -- 6.2.2 559 | //Distance, time and stride count 560 | int ANTPlus::update_sdm_rollover( byte MessageValue, unsigned long int * Cumulative, byte * PreviousMessageValue ) 561 | { 562 | //Initialize CumulativeDistance to 0 563 | //Above is external to this function 564 | //PreviousMessageDistance is set to -1 to indicate no previous message -- external to this function 565 | //initialize PreviousMessageDistance to the distance in the first SDM data message. 566 | if((*PreviousMessageValue) == -1) 567 | { 568 | (*PreviousMessageValue) = MessageValue; 569 | //This assumes that the first measurement we get from device is at 'point 0' -- any first measurement is therefore not counted in the cumulative 570 | } 571 | else 572 | { 573 | //For each subsequent SDM sensor Data message 574 | //a. CumulativeDistance += MessageDistance – PreviousMessageDistance 575 | (*Cumulative) += (MessageValue - (*PreviousMessageValue)); 576 | 577 | //b. If PreviousMessageDistance > MessageDistance, CumulativeDistance += 256m 578 | if ((*PreviousMessageValue) > MessageValue) 579 | { 580 | (*Cumulative) += 256; //All fields rollover on this amount 581 | } 582 | //c. PreviousMessageDistance = MessageDistance 583 | (*PreviousMessageValue) = MessageValue; 584 | } 585 | return (*Cumulative); 586 | } 587 | 588 | -------------------------------------------------------------------------------- /ANTPlus.h: -------------------------------------------------------------------------------- 1 | //Copyright 2013 Brody Kenrick. 2 | //An Ant+ library over UART ('Serial' or SoftwareSerial) 3 | 4 | //Provides message send and receive functionality 5 | //Also provides stringisers for a few messages 6 | //Provides higher-level functionality for setting up listening channels for a sensor 7 | 8 | //This works with a Nordic nRF24AP2 (should work with AP1 also) 9 | //Tested with this nRF24AP2 module : http://www.goodluckbuy.com/nrf24ap2-networking-module-zigbee-module-with-ant-transceiver-.html 10 | 11 | //Thanks to DigitalHack 12 | //http://digitalhacksblog.blogspot.com.au/2012_10_01_archive.html 13 | //That code was taken as a starting point 14 | 15 | //Works with the hardware 'Serial' or a SoftwareSerial 16 | //NOTE: That hardware 'Serial' might have issues when other interrupts 17 | // are present (e.g. SPI) and might not receive all messages. 18 | // SS does not have this issue. 19 | 20 | #ifndef ANTPLus_h 21 | #define ANTPLus_h 22 | 23 | #include 24 | #include 25 | 26 | #define ANTPLUS_MINIMAL_RECEIVE_BUFFER_FOR_BROADCAST_DATA // 30 seconds 77 | #define DEVCE_GPS_FREQ (50) //!< 2400 + N MHz : 50 > 2450 78 | #define DEVCE_SENSOR_FREQ (57) //!< 2400 + N MHz : 57 > 2457 79 | 80 | //TODO: add other rates 81 | #define DEVCE_SDM_LOWEST_RATE (16268) 82 | #define DEVCE_HRM_LOWEST_RATE (32280) 83 | 84 | #define DEVCE_GPS_RATE (8070) 85 | #define DEVCE_CADENCE_RATE (8085) 86 | 87 | 88 | //! ANT Packet coming off the wire. 89 | typedef struct ANT_Packet_struct 90 | { 91 | byte sync; 92 | byte length; 93 | byte msg_id; 94 | byte data[];//Variable -- elements == length 95 | //byte checksum... This is data[length]. See ANT_PACKET_CHECKSUM macro "function". 96 | } ANT_Packet; 97 | 98 | #define ANT_PACKET_CHECKSUM(/*Ant_Packet * */ packet) (packet->data[packet->length]) 99 | 100 | //TODO: Rename? 101 | typedef struct ANT_Broadcast_struct 102 | { 103 | byte channel_number; 104 | byte data[8]; 105 | } ANT_Broadcast; 106 | 107 | typedef struct ANT_DataPage_struct 108 | { 109 | byte data_page_number; 110 | byte sensor_specific_data[7]; 111 | } ANT_DataPage; 112 | 113 | 114 | typedef struct ANT_HRMDataPage_struct 115 | { 116 | byte data_page_number:7; 117 | byte page_change_toggle:1; 118 | //TODO: Union below 119 | byte who_cares_1; 120 | byte who_cares_2; 121 | byte who_cares_3; 122 | byte who_cares_4; 123 | byte who_cares_5; 124 | //TODO: Union above 125 | byte heart_beat_count; 126 | byte computed_heart_rate; 127 | 128 | } ANT_HRMDataPage; 129 | 130 | typedef struct ANT_SDMDataPage1_struct 131 | { 132 | byte data_page_number; 133 | byte last_time_frac; // 1/200 of a second 134 | byte last_time_int; 135 | byte distance_int; 136 | byte inst_speed_int:4; 137 | byte distance_frac:4; // 1/16 of metre 138 | byte inst_speed_frac; 139 | byte stride_count; 140 | byte update_latency; 141 | } ANT_SDMDataPage1; 142 | 143 | 144 | typedef struct ANT_SDMDataPage2_struct 145 | { 146 | byte data_page_number; 147 | byte reserved1; 148 | byte reserved2; 149 | byte cadence_int; 150 | byte inst_speed_int:4; 151 | byte cadence_frac:4; 152 | byte inst_speed_frac; 153 | byte reserved6; 154 | byte status; 155 | } ANT_SDMDataPage2; 156 | 157 | //! See progress_setup_channel(). 158 | typedef enum 159 | { 160 | ANT_CHANNEL_ESTABLISH_PROGRESSING, 161 | ANT_CHANNEL_ESTABLISH_COMPLETE, 162 | ANT_CHANNEL_ESTABLISH_ERROR, 163 | 164 | } ANT_CHANNEL_ESTABLISH; 165 | 166 | #define ANT_CHANNEL_NUMBER_INVALID (-1) 167 | 168 | //! Details required to establish an ANT+ (and ANT?) channel. See progress_setup_channel(). 169 | typedef struct ANT_Channel_struct 170 | { 171 | //Configuration items for channel 172 | int channel_number; //TODO: Look at making this internally assigned 173 | int network_number; 174 | int timeout; 175 | int device_type; 176 | int freq; 177 | int period; 178 | unsigned char ant_net_key[8]; 179 | 180 | ANT_CHANNEL_ESTABLISH channel_establish; //Read-only from external 181 | boolean data_rx; //Broadcast data received. For now this is only updated from external. TODO: Move internally 182 | int state_counter; //Private for internal use only 183 | } ANT_Channel; 184 | 185 | 186 | 187 | //! See readPacket(). 188 | typedef enum 189 | { 190 | MESSAGE_READ_NONE, //No message available (immediately or after timeout period) 191 | MESSAGE_READ_ERROR_BAD_CHECKSUM, 192 | MESSAGE_READ_ERROR_MISSING_SYNC, 193 | MESSAGE_READ_ERROR_PACKET_SIZE_EXCEEDED, 194 | MESSAGE_READ_INFO_TIMEOUT_MIDMESSAGE, //!< This might be recoverable on a subsequent call 195 | MESSAGE_READ_INTERNAL, //This is remapped to one of the next two in the internal read function 196 | MESSAGE_READ_OTHER, 197 | MESSAGE_READ_EXPECTED 198 | 199 | } MESSAGE_READ; 200 | 201 | 202 | 203 | 204 | //TODO: Look at ANT and ANT+ and work out the appropriate breakdown for a subclass/separate class 205 | class ANTPlus 206 | { 207 | public: 208 | ANTPlus( 209 | byte RTS_PIN, 210 | byte SUSPEND_PIN, 211 | byte SLEEP_PIN, 212 | byte RESET_PIN 213 | ); 214 | 215 | void begin(Stream &serial); 216 | void hardwareReset( ); 217 | 218 | boolean send(unsigned msgId, unsigned msgId_ResponseExpected, unsigned char argCnt, ...); 219 | MESSAGE_READ readPacket( ANT_Packet * packet, int packetSize, int wait_timeout ); 220 | 221 | void printPacket(const ANT_Packet * packet, boolean final_carriage_return); 222 | 223 | void sleep( boolean activate_sleep=true ); 224 | void suspend(boolean activate_suspend=true ); 225 | 226 | //Callback from the main code 227 | void rTSHighAssertion(); 228 | 229 | boolean awaitingResponseLastSent() {return (msgResponseExpected != MESG_INVALID_ID);}; 230 | 231 | //!ANT+ to setup a channel 232 | ANT_CHANNEL_ESTABLISH progress_setup_channel( ANT_Channel * channel ); 233 | 234 | #if defined(ANTPLUS_MSG_STR_DECODE) 235 | static const char * get_msg_id_str(byte msg_id); 236 | #endif /*defined(ANTPLUS_MSG_STR_DECODE)*/ 237 | 238 | static int update_sdm_rollover( byte MessageValue, unsigned long int * Cumulative, byte * PreviousMessageValue ); 239 | 240 | private: 241 | MESSAGE_READ readPacketInternal( ANT_Packet * packet, int packetSize, unsigned int readTimeout); 242 | unsigned char writeByte(unsigned char out, unsigned char chksum); 243 | 244 | static void serial_print_byte_padded_hex(byte value); 245 | static void serial_print_int_padded_dec(long int value, unsigned int width, boolean final_carriage_return = false); 246 | 247 | private: 248 | Stream* mySerial; //!< Serial -- Software serial or Hardware serial 249 | 250 | public: //TODO: Just temp (to eventually be removed -- or added to the interface properly) 251 | long rx_packet_count; 252 | long tx_packet_count; 253 | long hw_reset_count; 254 | 255 | private: 256 | unsigned msgResponseExpected; //TODO: This should be an enum..... 257 | 258 | volatile boolean clear_to_send; 259 | 260 | int rxBufCnt; 261 | unsigned char rxBuf[ANT_MAX_PACKET_LEN]; 262 | 263 | byte RTS_PIN; 264 | byte SUSPEND_PIN; 265 | byte SLEEP_PIN; 266 | byte RESET_PIN; 267 | 268 | }; 269 | 270 | #endif //ANTPLus_h 271 | 272 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | An Ant+ library for Arduino over UART ('Serial' or SoftwareSerial) 3 | 4 | Provides message send and receive functionality 5 | Also provides stringisers for a few messages 6 | Provides higher-level functionality for setting up listening channels for a sensor 7 | 8 | This works with a Nordic nRF24AP2 (should work with AP1 also) 9 | 10 | Tested with this nRF24AP2 module : http://www.goodluckbuy.com/nrf24ap2-networking-module-zigbee-module-with-ant-transceiver-.html 11 | 12 | Thanks to DigitalHack @ http://digitalhacksblog.blogspot.com.au/2012_10_01_archive.html 13 | 14 | -------------------------------------------------------------------------------- /antdefines.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012 Dynastream Innovations, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef ANTDEFINES_H 18 | #define ANTDEFINES_H 19 | 20 | #include "types.h" 21 | 22 | ////////////////////////////////////////////// 23 | // ANT Clock Definition 24 | ////////////////////////////////////////////// 25 | #define ANT_CLOCK_FREQUENCY ((ULONG)32768) ///< ANT system clock frequency 26 | 27 | ////////////////////////////////////////////// 28 | // Radio TX Power Definitions 29 | ////////////////////////////////////////////// 30 | #define RADIO_TX_POWER_MASK ((UCHAR)0x03) 31 | #define RADIO_TX_POWER_MINUS20DB ((UCHAR)0x00) 32 | #define RADIO_TX_POWER_MINUS10DB ((UCHAR)0x01) 33 | #define RADIO_TX_POWER_MINUS5DB ((UCHAR)0x02) 34 | #define RADIO_TX_POWER_0DB ((UCHAR)0x03) 35 | 36 | ////////////////////////////////////////////// 37 | // Default System Definitions 38 | ////////////////////////////////////////////// 39 | #define DEFAULT_CHANNEL_MESSAGE_FREQUENCY ((ULONG)4) 40 | #define DEFAULT_CHANNEL_MESSAGE_PERIOD ((USHORT)( ANT_CLOCK_FREQUENCY / DEFAULT_CHANNEL_MESSAGE_FREQUENCY )) ///< 8192 (4Hz) 41 | #define DEFAULT_RADIO_TX_POWER RADIO_TX_POWER_0DB ///< ANT default RF power 42 | #define DEFAULT_RADIO_CHANNEL ((UCHAR)66) ///< 2400MHz + 1MHz * Channel Number = 2466MHz 43 | #define DEFAULT_RX_SEARCH_TIMEOUT ((UCHAR)12) ///< 12 * 2.5 seconds = 30 seconds 44 | 45 | ////////////////////////////////////////////// 46 | // ID Definitions 47 | ////////////////////////////////////////////// 48 | #define ID_MANUFACTURER_OFFSET ((UCHAR)3) 49 | #define ID_DEVICE_TYPE_OFFSET ((UCHAR)2) 50 | #define ID_DEVICE_NUMBER_HIGH_OFFSET ((UCHAR)1) 51 | #define ID_DEVICE_NUMBER_LOW_OFFSET ((UCHAR)0) 52 | #define ID_DEVICE_TYPE_PAIRING_FLAG ((UCHAR)0x80) 53 | 54 | ////////////////////////////////////////////// 55 | // Assign Channel Parameters 56 | ////////////////////////////////////////////// 57 | #define PARAMETER_TX_NOT_RX ((UCHAR)0x10) 58 | #define PARAMETER_MULTIPLE_ACCESS_CHANNEL ((UCHAR)0x20) 59 | #define PARAMETER_NO_TX_GUARD_BAND ((UCHAR)0x40) 60 | #define PARAMETER_ALWAYS_RX_WILD_CARD_SEARCH_ID ((UCHAR)0x40) 61 | 62 | ////////////////////////////////////////////// 63 | // Assign Channel Types 64 | ////////////////////////////////////////////// 65 | #define CHANNEL_TYPE_SLAVE ((UCHAR) 0x00) 66 | #define CHANNEL_TYPE_MASTER ((UCHAR) 0x10) 67 | #define CHANNEL_TYPE_MASTER_TX_ONLY ((UCHAR) 0x50) 68 | #define CHANNEL_TYPE_SHARED_SLAVE ((UCHAR) 0x20) 69 | #define CHANNEL_TYPE_SHARED_MASTER ((UCHAR) 0x30) 70 | 71 | ////////////////////////////////////////////// 72 | // Channel Status 73 | ////////////////////////////////////////////// 74 | #define STATUS_UNASSIGNED_CHANNEL ((UCHAR)0x00) 75 | #define STATUS_ASSIGNED_CHANNEL ((UCHAR)0x01) 76 | #define STATUS_SEARCHING_CHANNEL ((UCHAR)0x02) 77 | #define STATUS_TRACKING_CHANNEL ((UCHAR)0x03) 78 | #define STATUS_OVERRUN ((UCHAR)0x40) 79 | #define STATUS_UNDERRUN ((UCHAR)0x80) 80 | 81 | ////////////////////////////////////////////// 82 | // Standard capabilities defines 83 | ////////////////////////////////////////////// 84 | #define CAPABILITIES_NO_RX_CHANNELS ((UCHAR)0x01) 85 | #define CAPABILITIES_NO_TX_CHANNELS ((UCHAR)0x02) 86 | #define CAPABILITIES_NO_RX_MESSAGES ((UCHAR)0x04) 87 | #define CAPABILITIES_NO_TX_MESSAGES ((UCHAR)0x08) 88 | #define CAPABILITIES_NO_ACKD_MESSAGES ((UCHAR)0x10) 89 | #define CAPABILITIES_NO_BURST_TRANSFER ((UCHAR)0x20) 90 | 91 | ////////////////////////////////////////////// 92 | // Advanced capabilities defines 93 | ////////////////////////////////////////////// 94 | #define CAPABILITIES_DETECT_OVERRUN_UNDERRUN ((UCHAR)0x01) 95 | #define CAPABILITIES_NETWORK_ENABLED ((UCHAR)0x02) 96 | 97 | ////////////////////////////////////////////// 98 | // Burst Message Sequence 99 | ////////////////////////////////////////////// 100 | #define CHANNEL_NUMBER_MASK ((UCHAR)0x1F) 101 | #define SEQUENCE_NUMBER_MASK ((UCHAR)0xE0) 102 | #define SEQUENCE_NUMBER_INC ((UCHAR)0x20) 103 | #define SEQUENCE_NUMBER_ROLLOVER ((UCHAR)0x60) 104 | #define SEQUENCE_LAST_MESSAGE ((UCHAR)0x80) 105 | 106 | ////////////////////////////////////////////// 107 | // Shared Channel Commands / Datatypes 108 | ////////////////////////////////////////////// 109 | #define SHARED_CMD_SLOT_AVALIBLE ((UCHAR)0xFF) 110 | #define SHARED_CMD_BUSY_ACQUIRING ((UCHAR)0xFE) 111 | #define SHARED_CMD_COMMAND_REQUEST_TO_ACQUIRE ((UCHAR)0xFD) 112 | #define SHARED_CMD_CONFIRM_ACQUIRED ((UCHAR)0xFC) 113 | #define SHARED_CMD_NO_SLOTS_AVAILABLE ((UCHAR)0xFB) 114 | //... 115 | #define SHARED_TYPE_RELAY ((UCHAR)0x43) 116 | #define SHARED_TYPE_COUNTER ((UCHAR)0x42) 117 | #define SHARED_TYPE_A_TO_D ((UCHAR)0x41) 118 | #define SHARED_TYPE_DIGITAL ((UCHAR)0x40) 119 | #define SHARED_TYPE_UNDEFINED ((UCHAR)0x00) 120 | 121 | /////////////////////////////////////////////////////////////////////// 122 | // AtoD SubTypes 123 | /////////////////////////////////////////////////////////////////////// 124 | #define TEMPERATURE ((UCHAR)0xFE) 125 | #define BATT_VOLTAGE ((UCHAR)0xFF) 126 | 127 | ////////////////////////////////////////////// 128 | // Response / Event Codes 129 | ////////////////////////////////////////////// 130 | #define RESPONSE_NO_ERROR ((UCHAR)0x00) 131 | 132 | #define EVENT_RX_SEARCH_TIMEOUT ((UCHAR)0x01) 133 | #define EVENT_RX_FAIL ((UCHAR)0x02) 134 | #define EVENT_TX ((UCHAR)0x03) 135 | #define EVENT_TRANSFER_RX_FAILED ((UCHAR)0x04) 136 | #define EVENT_TRANSFER_TX_COMPLETED ((UCHAR)0x05) 137 | #define EVENT_TRANSFER_TX_FAILED ((UCHAR)0x06) 138 | #define EVENT_CHANNEL_CLOSED ((UCHAR)0x07) 139 | #define EVENT_RX_FAIL_GO_TO_SEARCH ((UCHAR)0x08) 140 | #define EVENT_CHANNEL_COLLISION ((UCHAR)0x09) 141 | 142 | 143 | #define CHANNEL_IN_WRONG_STATE ((UCHAR)0x15) // returned on attempt to perform an action from the wrong channel state 144 | #define CHANNEL_NOT_OPENED ((UCHAR)0x16) // returned on attempt to communicate on a channel that is not open 145 | #define CHANNEL_ID_NOT_SET ((UCHAR)0x18) // returned on attempt to open a channel without setting the channel ID 146 | 147 | #define TRANSFER_IN_PROGRESS ((UCHAR)0x1F) // returned on attempt to communicate on a channel with a TX transfer in progress 148 | #define TRANSFER_SEQUENCE_NUMBER_ERROR ((UCHAR)0x20) // returned when sequence number is out of order on a Burst transfer 149 | 150 | #define INVALID_MESSAGE ((UCHAR)0x28) // returned when the message has an invalid parameter 151 | #define INVALID_NETWORK_NUMBER ((UCHAR)0x29) // returned when an invalid network number is provided 152 | #define NO_RESPONSE_MESSAGE ((UCHAR)0x50) // returned to the Command_SerialMessageProcess function, so no reply message is generated 153 | 154 | #define FIT_ACTIVE_SEARCH_TIMEOUT ((UCHAR)0x60) // event added for timeout of the pairing state after the Fit module becomes active 155 | #define FIT_WATCH_PAIRED ((UCHAR)0x61) // event added for timeout of the pairing state after the Fit module becomes active 156 | #define FIT_WATCH_UNPAIRED ((UCHAR)0x62) // event added for timeout of the pairing state after the Fit module becomes active 157 | 158 | 159 | #define EVENT_COMMAND_TIMEOUT ((UCHAR)0xA9) // (Host Only)returned when no response is recieved from ANT module after a command has been sent 160 | #define EVENT_ACK_TIMEOUT ((UCHAR)0xAA) // (Host Only) returned if not response to an Ack command is recieved for a set period of time. 161 | 162 | 163 | 164 | /////////////////////////////////////////////////////////////// 165 | // Application Level defines 166 | /////////////////////////////////////////////////////////////// 167 | #define USE_FREQUENCY_HOPPER 168 | // #define USE_AUTO_SHARED_MASTER 169 | 170 | #define NUM_CHANNELS ((UCHAR)0x04) // Number of channels to be initialized and used by the application 171 | #define NUM_FREQUENCY_HOPS ((UCHAR)0x04) // Number of frequency hops to make if using frequency hopper 172 | 173 | /////////////////////////////////////////////////////////////// 174 | // +LINK Mode Commands 175 | /////////////////////////////////////////////////////////////// 176 | #define PLUS_LINK_MSG_STATUS ((UCHAR)0x01) 177 | #define PLUS_LINK_MSG_CONNECT ((UCHAR)0x02) 178 | #define PLUS_LINK_MSG_DISCONNECT ((UCHAR)0x03) 179 | #define PLUS_LINK_MSG_AUTHENTICATE ((UCHAR)0x04) 180 | #define PLUS_LINK_MSG_PAIR ((UCHAR)0x05) 181 | #define PLUS_LINK_MSG_DOWNLOAD ((UCHAR)0x06) 182 | #define PLUS_LINK_MSG_UPLOAD ((UCHAR)0x07) 183 | #define PLUS_LINK_MSG_ERASE ((UCHAR)0x08) 184 | 185 | #define TRANSFER_BUSY ((UCHAR)0x22) 186 | 187 | #define CHANNEL_0 0 188 | /////////////////////////////////////////////////////////////// 189 | // State Machine Return Flags 190 | /////////////////////////////////////////////////////////////// 191 | #define STATE_STATUS_NONE ((UCHAR)0x00) // State machine did not handle event in any way 192 | #define STATE_STATUS_TRANSMIT ((UCHAR)0x01) // State machine requires tx buffer to be transmitted. 193 | #define STATE_STATUS_HANDLED ((UCHAR)0x02) // State machine handled input events, no further processing required 194 | 195 | 196 | 197 | 198 | ////////////////////////////////////////////// 199 | // PC Application Event Codes 200 | ////////////////////////////////////////////// 201 | //NOTE: These events are not generated by the embedded ANT module 202 | 203 | #define EVENT_RX_BROADCAST ((UCHAR)0x9A) // returned when module receives broadcast data 204 | #define EVENT_RX_ACKNOWLEDGED ((UCHAR)0x9B) // returned when module receives acknowledged data 205 | #define EVENT_RX_BURST_PACKET ((UCHAR)0x9C) // returned when module receives burst data 206 | 207 | #define EVENT_RX_EXT_BROADCAST ((UCHAR)0x9D) // returned when module receives broadcast data 208 | #define EVENT_RX_EXT_ACKNOWLEDGED ((UCHAR)0x9E) // returned when module receives acknowledged data 209 | #define EVENT_RX_EXT_BURST_PACKET ((UCHAR)0x9F) // returned when module receives burst data 210 | 211 | #define EVENT_RX_RSSI_BROADCAST ((UCHAR)0xA0) // returned when module receives broadcast data 212 | #define EVENT_RX_RSSI_ACKNOWLEDGED ((UCHAR)0xA1) // returned when module receives acknowledged data 213 | #define EVENT_RX_RSSI_BURST_PACKET ((UCHAR)0xA2) // returned when module receives burst data 214 | 215 | #define EVENT_RX_BTH_BROADCAST ((UCHAR)0xA3) // returned when module receives broadcast data 216 | #define EVENT_RX_BTH_ACKNOWLEDGED ((UCHAR)0xA4) // returned when module receives acknowledged data 217 | #define EVENT_RX_BTH_BURST_PACKET ((UCHAR)0xA5) // returned when module receives burst data 218 | 219 | #define EVENT_RX_BTH_EXT_BROADCAST ((UCHAR)0xA6) // returned when module receives broadcast data 220 | #define EVENT_RX_BTH_EXT_ACKNOWLEDGED ((UCHAR)0xA7) // returned when module receives acknowledged data 221 | #define EVENT_RX_BTH_EXT_BURST_PACKET ((UCHAR)0xA8) // returned when module receives burst data 222 | ////////////////////////////////////////////// 223 | // NVM Command Codes 224 | ////////////////////////////////////////////// 225 | 226 | /////////////////////////////////////////////////////////////// 227 | // Macros 228 | /////////////////////////////////////////////////////////////// 229 | #define HIGH_BYTE(usWord) (UCHAR)((usWord >> 8) & 0x00FF) 230 | #define LOW_BYTE(usWord) (UCHAR)(usWord & 0x00FF) 231 | #define BYTE0(x) ((UCHAR) x & 0xFF) 232 | #define BYTE1(x) ((UCHAR) (x >> 8) & 0xFF) 233 | #define BYTE2(x) ((UCHAR) (x >> 16) & 0xFF) 234 | #define BYTE3(x) ((UCHAR) (x >> 24) & 0xFF) 235 | #define MIN(x,y) (((x)<(y))?(x):(y)) 236 | #define MAX(x,y) (((x)>(y))?(x):(y)) 237 | 238 | 239 | #endif // !ANTDEFINES_H 240 | 241 | 242 | -------------------------------------------------------------------------------- /antmessage.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012 Dynastream Innovations, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef ANTMESSAGE_H 18 | #define ANTMESSAGE_H 19 | 20 | #include "types.h" 21 | 22 | ///////////////////////////////////////////////////////////////////////////// 23 | // Message Format 24 | // Messages are in the format: 25 | // 26 | // AX XX YY -------- CK 27 | // 28 | // where: AX is the 1 byte sync byte either transmit or recieve 29 | // XX is the 1 byte size of the message (0-249) NOTE: THIS WILL BE LIMITED BY THE EMBEDDED RECEIVE BUFFER SIZE 30 | // YY is the 1 byte ID of the message (1-255, 0 is invalid) 31 | // ----- is the data of the message (0-249 bytes of data) 32 | // CK is the 1 byte Checksum of the message 33 | ///////////////////////////////////////////////////////////////////////////// 34 | #define MESG_TX_SYNC ((UCHAR)0xA4) 35 | #define MESG_RX_SYNC ((UCHAR)0xA5) 36 | #define MESG_SIZE_OFFSET ((UCHAR)0) 37 | #define MESG_ID_OFFSET ((UCHAR)1) 38 | #define MESG_SYNC_SIZE ((UCHAR)1) 39 | #define MESG_SIZE_SIZE ((UCHAR)1) 40 | #define MESG_ID_SIZE ((UCHAR)1) 41 | #define MESG_CHECKSUM_SIZE ((UCHAR)1) 42 | #define MESG_MAX_DATA_SIZE ((UCHAR)9) 43 | #define MESG_MAX_DATA_BYTES ((UCHAR)8) 44 | #define MESG_HEADER_SIZE (MESG_SYNC_SIZE + MESG_SIZE_SIZE + MESG_ID_SIZE) 45 | #define MESG_DATA_OFFSET MESG_HEADER_SIZE-1 //2 46 | #define MESG_FRAME_SIZE (MESG_HEADER_SIZE + MESG_CHECKSUM_SIZE) 47 | #define MESG_SAVED_FRAME_SIZE (MESG_SIZE_SIZE + MESG_ID_SIZE) 48 | #define MESG_MAX_SIZE (MESG_MAX_DATA_SIZE + MESG_FRAME_SIZE) 49 | #define MESG_BUFFER_SIZE (MESG_MAX_DATA_SIZE + MESG_SAVED_FRAME_SIZE) 50 | 51 | #define ANT_DATA_SIZE 8 // ANT message payload size. 52 | #define ANT_TX_SYNC ((UCHAR) 0xA4) 53 | #define ANT_RX_SYNC ((UCHAR) 0xA5) 54 | #define ANT_SIZE_OFFSET ((UCHAR) 0x00) 55 | #define ANT_ID_OFFSET ((UCHAR) 0x01) 56 | #define ANT_SYNC_SIZE ((UCHAR) 0x01) 57 | #define ANT_SIZE_SIZE ((UCHAR) 0x01) 58 | #define ANT_ID_SIZE ((UCHAR) 0x01) 59 | #define ANT_RESPONSE_ERROR_SIZE ((UCHAR) 0x01) 60 | #define ANT_CHECKSUM_SIZE ((UCHAR) 0x01) 61 | #define ANT_MAX_DATA_SIZE ((UCHAR) 0x17) 62 | #define ANT_HEADER_SIZE (ANT_SYNC_SIZE + ANT_SIZE_SIZE + ANT_ID_SIZE) 63 | #define ANT_REPLY_HEADER_SIZE ANT_HEADER_SIZE + ANT_RESPONSE_ERROR_SIZE 64 | #define ANT_DATA_OFFSET ANT_HEADER_SIZE-1 65 | #define ANT_FRAME_SIZE (ANT_HEADER_SIZE + ANT_CHECKSUM_SIZE) 66 | #define ANT_MAX_SIZE (ANT_MAX_DATA_SIZE + ANT_FRAME_SIZE) 67 | 68 | 69 | ////////////////////////////////////////////// 70 | // Buffer Indices 71 | ////////////////////////////////////////////// 72 | #define BUFFER_INDEX_MESG_SIZE ((UCHAR)0x00) 73 | #define BUFFER_INDEX_MESG_ID ((UCHAR)0x01) 74 | #define BUFFER_INDEX_CHANNEL_NUM ((UCHAR)0x02) 75 | #define BUFFER_INDEX_MESG_DATA ((UCHAR)0x03) 76 | #define BUFFER_INDEX_RESPONSE_MESG_ID ((UCHAR)0x03) 77 | #define BUFFER_INDEX_RESPONSE_CODE ((UCHAR)0x04) 78 | #define BUFFER_INDEX_DATA0 ((UCHAR)0x03) 79 | #define BUFFER_INDEX_SHARED_ADDRESS_LSB ((UCHAR)0x03) 80 | #define BUFFER_INDEX_SHARED_ADDRESS_MSB ((UCHAR)0x04) 81 | #define BUFFER_INDEX_SHARED_DATA_TYPE ((UCHAR)0x05) 82 | 83 | ////////////////////////////////////////////// 84 | // Message ID's 85 | ////////////////////////////////////////////// 86 | #define MESG_INVALID_ID ((UCHAR)0x00) 87 | #define MESG_EVENT_ID ((UCHAR)0x01) 88 | 89 | #define MESG_APPVERSION_ID ((UCHAR)0x3D) ///< application interface version 90 | #define MESG_VERSION_ID ((UCHAR)0x3E) ///< protocol library version 91 | #define MESG_RESPONSE_EVENT_ID ((UCHAR)0x40) 92 | 93 | #define MESG_UNASSIGN_CHANNEL_ID ((UCHAR)0x41) 94 | #define MESG_ASSIGN_CHANNEL_ID ((UCHAR)0x42) 95 | #define MESG_CHANNEL_MESG_PERIOD_ID ((UCHAR)0x43) 96 | #define MESG_CHANNEL_SEARCH_TIMEOUT_ID ((UCHAR)0x44) 97 | #define MESG_CHANNEL_RADIO_FREQ_ID ((UCHAR)0x45) 98 | #define MESG_NETWORK_KEY_ID ((UCHAR)0x46) 99 | #define MESG_RADIO_TX_POWER_ID ((UCHAR)0x47) 100 | #define MESG_RADIO_CW_MODE_ID ((UCHAR)0x48) 101 | #define MESG_SEARCH_WAVEFORM_ID ((UCHAR)0x49) 102 | 103 | #define MESG_SYSTEM_RESET_ID ((UCHAR)0x4A) 104 | #define MESG_OPEN_CHANNEL_ID ((UCHAR)0x4B) 105 | #define MESG_CLOSE_CHANNEL_ID ((UCHAR)0x4C) 106 | #define MESG_REQUEST_ID ((UCHAR)0x4D) 107 | 108 | #define MESG_BROADCAST_DATA_ID ((UCHAR)0x4E) 109 | #define MESG_ACKNOWLEDGED_DATA_ID ((UCHAR)0x4F) 110 | #define MESG_BURST_DATA_ID ((UCHAR)0x50) 111 | 112 | #define MESG_CHANNEL_ID_ID ((UCHAR)0x51) 113 | #define MESG_CHANNEL_STATUS_ID ((UCHAR)0x52) 114 | #define MESG_RADIO_CW_INIT_ID ((UCHAR)0x53) 115 | #define MESG_CAPABILITIES_ID ((UCHAR)0x54) 116 | #define MESG_SENSRCORE_DIGITAL_DATA ((UCHAR)0x40) 117 | 118 | #define MESG_PIN_DIODE_CONTROL_ID ((UCHAR)0x90) 119 | #define MESG_RUN_SCRIPT_ID ((UCHAR)0x91) 120 | 121 | #define MESG_START_UP ((UCHAR) 0x6F) 122 | 123 | ////////////////////////////////////////////// 124 | // Message Sizes 125 | ////////////////////////////////////////////// 126 | #define MESG_INVALID_SIZE ((UCHAR)0) 127 | 128 | #define MESG_RESPONSE_EVENT_SIZE ((UCHAR)3) 129 | #define MESG_CHANNEL_STATUS_SIZE ((UCHAR)2) 130 | #define MESG_VERSION_SIZE ((UCHAR)9) 131 | 132 | #define MESG_UNASSIGN_CHANNEL_SIZE ((UCHAR)1) 133 | #define MESG_ASSIGN_CHANNEL_SIZE ((UCHAR)3) 134 | #define MESG_CHANNEL_ID_SIZE ((UCHAR)5) 135 | #define MESG_CHANNEL_MESG_PERIOD_SIZE ((UCHAR)3) 136 | #define MESG_CHANNEL_SEARCH_TIMEOUT_SIZE ((UCHAR)2) 137 | #define MESG_CHANNEL_RADIO_FREQ_SIZE ((UCHAR)2) 138 | #define MESG_NETWORK_KEY_SIZE ((UCHAR)9) 139 | #define MESG_RADIO_TX_POWER_SIZE ((UCHAR)2) 140 | #define MESG_RADIO_CW_MODE_SIZE ((UCHAR)3) 141 | #define MESG_RADIO_CW_INIT_SIZE ((UCHAR)1) 142 | #define MESG_SEARCH_WAVEFORM_SIZE ((UCHAR)3) 143 | 144 | #define MESG_SYSTEM_RESET_SIZE ((UCHAR)1) 145 | #define MESG_OPEN_CHANNEL_SIZE ((UCHAR)1) 146 | #define MESG_CLOSE_CHANNEL_SIZE ((UCHAR)1) 147 | #define MESG_REQUEST_SIZE ((UCHAR)2) 148 | #define MESG_CAPABILITIES_SIZE ((UCHAR)4) 149 | 150 | #define MESG_DATA_SIZE ((UCHAR)9) 151 | 152 | #define MESG_PIN_DIODE_CONTROL_ID_SIZE ((UCHAR)2) 153 | #define MESG_RUN_SCRIPT_SIZE ((UCHAR)2) 154 | 155 | 156 | #endif // !ANTMESSAGE_H 157 | -------------------------------------------------------------------------------- /examples/ANTPlus_HearRateMonitor/ANTPlus_HearRateMonitor.ino: -------------------------------------------------------------------------------- 1 | /* Example for the ANT+ Library @ https://github.com/brodykenrick/ANTPlus_Arduino 2 | Copyright 2013 Brody Kenrick. 3 | 4 | Developed for http://retrorunnerreadout.blogspot.com 5 | 6 | Interfacing of Garmin ANT+ device (via a cheap Nordic nRF24AP UART module) to an Arduino. 7 | 8 | Opens an ANT+ channel listening for HRM. Prints out computed hear rate. 9 | 10 | Hardware 11 | An Arduino Pro Mini 3v3 connected to this nRF24AP2 module : http://www.goodluckbuy.com/nrf24ap2-networking-module-zigbee-module-with-ant-transceiver-.html 12 | 13 | The connector on nRF24AP2 board is (looking from the front, pin 1 is marked []): 14 | []GND(=VSS) | VDD(=3.3 volts) 15 | UART_TX | UART_RX 16 | !(SUSP) | SLEEP 17 | RTS | !(RESET) 18 | 19 | Wiring to the Arduino Pro Mini 3v3 can be seen in 'antplus' below. 20 | */ 21 | 22 | #include 23 | 24 | //#define NDEBUG 25 | #define __ASSERT_USE_STDERR 26 | #include 27 | 28 | //#define ANTPLUS_ON_HW_UART //!< H/w UART (i.e. Serial) instead of software serial. NOTE: There seems to be issues in not getting as many broadcast packets when using hardware serial......... 29 | 30 | 31 | #if !defined(ANTPLUS_ON_HW_UART) 32 | #include 33 | #endif 34 | 35 | #include 36 | 37 | 38 | #define USE_SERIAL_CONSOLE //!msg_id ) 147 | { 148 | case MESG_BROADCAST_DATA_ID: 149 | { 150 | const ANT_Broadcast * broadcast = (const ANT_Broadcast *) packet->data; 151 | SERIAL_DEBUG_PRINT_F( "CHAN " ); 152 | SERIAL_DEBUG_PRINT( broadcast->channel_number ); 153 | SERIAL_DEBUG_PRINT_F( " " ); 154 | const ANT_DataPage * dp = (const ANT_DataPage *) broadcast->data; 155 | 156 | //Update received data 157 | if( broadcast->channel_number == hrm_channel.channel_number ) 158 | { 159 | hrm_channel.data_rx = true; 160 | //To determine the device type -- and the data pages -- check channel setups 161 | if(hrm_channel.device_type == DEVCE_TYPE_HRM) 162 | { 163 | switch(dp->data_page_number) 164 | { 165 | case DATA_PAGE_HEART_RATE_0: 166 | case DATA_PAGE_HEART_RATE_0ALT: 167 | case DATA_PAGE_HEART_RATE_1: 168 | case DATA_PAGE_HEART_RATE_1ALT: 169 | case DATA_PAGE_HEART_RATE_2: 170 | case DATA_PAGE_HEART_RATE_2ALT: 171 | case DATA_PAGE_HEART_RATE_3: 172 | case DATA_PAGE_HEART_RATE_3ALT: 173 | case DATA_PAGE_HEART_RATE_4: 174 | case DATA_PAGE_HEART_RATE_4ALT: 175 | { 176 | //As we only care about the computed heart rate 177 | // we use a same struct for all HRM pages 178 | const ANT_HRMDataPage * hrm_dp = (const ANT_HRMDataPage *) dp; 179 | SERIAL_DEBUG_PRINT_F( "HR[any_page] : BPM = "); 180 | SERIAL_DEBUG_PRINTLN( hrm_dp->computed_heart_rate ); 181 | } 182 | break; 183 | 184 | default: 185 | SERIAL_DEBUG_PRINT_F(" HRM DP# "); 186 | SERIAL_DEBUG_PRINTLN( dp->data_page_number ); 187 | break; 188 | } 189 | } 190 | } 191 | break; 192 | } 193 | 194 | default: 195 | SERIAL_DEBUG_PRINTLN_F("Non-broadcast data received."); 196 | break; 197 | } 198 | } 199 | 200 | 201 | 202 | 203 | // ************************************************************************************************** 204 | // ************************************ Setup ***************************************************** 205 | // ************************************************************************************************** 206 | void setup() 207 | { 208 | #if defined(USE_SERIAL_CONSOLE) 209 | Serial.begin(115200); 210 | #endif //defined(USE_SERIAL_CONSOLE) 211 | 212 | SERIAL_DEBUG_PRINTLN("ANTPlus HRM Test!"); 213 | SERIAL_DEBUG_PRINTLN_F("Setup."); 214 | 215 | SERIAL_DEBUG_PRINTLN_F("ANT+ Config."); 216 | 217 | //We setup an interrupt to detect when the RTS is received from the ANT chip. 218 | //This is a 50 usec HIGH signal at the end of each valid ANT message received from the host at the chip 219 | attachInterrupt(RTS_PIN_INT, isr_rts_ant, RISING); 220 | 221 | 222 | #if defined(ANTPLUS_ON_HW_UART) 223 | //Using hardware UART 224 | Serial.begin(ANTPLUS_BAUD_RATE); 225 | antplus.begin( Serial ); 226 | #else 227 | //Using soft serial 228 | ant_serial.begin( ANTPLUS_BAUD_RATE ); 229 | antplus.begin( ant_serial ); 230 | #endif 231 | 232 | SERIAL_DEBUG_PRINTLN_F("ANT+ Config Finished."); 233 | SERIAL_DEBUG_PRINTLN_F("Setup Finished."); 234 | } 235 | 236 | // ************************************************************************************************** 237 | // ************************************ Loop ******************************************************* 238 | // ************************************************************************************************** 239 | 240 | void loop() 241 | { 242 | byte packet_buffer[ANT_MAX_PACKET_LEN]; 243 | ANT_Packet * packet = (ANT_Packet *) packet_buffer; 244 | MESSAGE_READ ret_val = MESSAGE_READ_NONE; 245 | 246 | if(rts_ant_received == 1) 247 | { 248 | SERIAL_DEBUG_PRINTLN_F("Received RTS Interrupt. "); 249 | antplus.rTSHighAssertion(); 250 | //Clear the ISR flag 251 | rts_ant_received = 0; 252 | } 253 | 254 | //Read messages until we get a none 255 | while( (ret_val = antplus.readPacket(packet, ANT_MAX_PACKET_LEN, 0 )) != MESSAGE_READ_NONE ) 256 | { 257 | if((ret_val == MESSAGE_READ_EXPECTED) || (ret_val == MESSAGE_READ_OTHER)) 258 | { 259 | SERIAL_DEBUG_PRINT_F( "ReadPacket success = " ); 260 | if( (ret_val == MESSAGE_READ_EXPECTED) ) 261 | { 262 | SERIAL_DEBUG_PRINTLN_F( "Expected packet" ); 263 | } 264 | else 265 | if( (ret_val == MESSAGE_READ_OTHER) ) 266 | { 267 | SERIAL_DEBUG_PRINTLN_F( "Other packet" ); 268 | } 269 | process_packet(packet); 270 | } 271 | else 272 | { 273 | SERIAL_DEBUG_PRINT_F( "ReadPacket Error = " ); 274 | SERIAL_DEBUG_PRINTLN( ret_val ); 275 | if(ret_val == MESSAGE_READ_ERROR_MISSING_SYNC) 276 | { 277 | //Nothing -- allow a re-read to get back in sync 278 | } 279 | else 280 | if(ret_val == MESSAGE_READ_ERROR_BAD_CHECKSUM) 281 | { 282 | //Nothing -- fully formed package just bit errors 283 | } 284 | else 285 | { 286 | break; 287 | } 288 | } 289 | } 290 | 291 | 292 | if(hrm_channel.channel_establish != ANT_CHANNEL_ESTABLISH_COMPLETE) 293 | { 294 | antplus.progress_setup_channel( &hrm_channel ); 295 | if(hrm_channel.channel_establish == ANT_CHANNEL_ESTABLISH_COMPLETE) 296 | { 297 | SERIAL_DEBUG_PRINT( hrm_channel.channel_number ); 298 | SERIAL_DEBUG_PRINTLN_F( " - Established." ); 299 | } 300 | else 301 | if(hrm_channel.channel_establish == ANT_CHANNEL_ESTABLISH_PROGRESSING) 302 | { 303 | SERIAL_DEBUG_PRINT( hrm_channel.channel_number ); 304 | SERIAL_DEBUG_PRINTLN_F( " - Progressing." ); 305 | } 306 | else 307 | { 308 | SERIAL_DEBUG_PRINT( hrm_channel.channel_number ); 309 | SERIAL_DEBUG_PRINTLN_F( " - ERROR!" ); 310 | } 311 | } 312 | } 313 | 314 | -------------------------------------------------------------------------------- /types.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012 Dynastream Innovations, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #if !defined(TYPES_H) 18 | #define TYPES_H 19 | #define LITTLE_ENDIAN 20 | 21 | #if defined(PC_SIM) 22 | #include 23 | #endif 24 | 25 | 26 | ////////////////////////////////////////////////////////////////////////////////// 27 | // Public Definitions 28 | ////////////////////////////////////////////////////////////////////////////////// 29 | 30 | #define TRUE 1 31 | #define FALSE 0 32 | 33 | #if !defined(NULL) 34 | #define NULL ((void *) 0) 35 | #endif 36 | 37 | #define MAX_UCHAR 0xFF 38 | #define MAX_SCHAR 0x7F 39 | #define MIN_SCHAR 0x80 40 | 41 | #define MAX_SHORT 0x7FFF 42 | #define MIN_SHORT 0x8000 43 | #define MAX_USHORT 0xFFFF 44 | #define MAX_SSHORT 0x7FFF 45 | #define MIN_SSHORT 0x8000 46 | 47 | #define MAX_LONG 0x7FFFFFFF 48 | #define MIN_LONG 0x80000000 49 | #define MAX_ULONG 0xFFFFFFFF 50 | #define MAX_SLONG 0x7FFFFFFF 51 | #define MIN_SLONG 0x80000000 52 | 53 | #if !defined(BASETYPES) // windef.h compatibility 54 | typedef unsigned char BOOL; 55 | #endif 56 | 57 | typedef unsigned char UCHAR; 58 | typedef signed char SCHAR; 59 | 60 | typedef short SHORT; 61 | typedef unsigned short USHORT; 62 | typedef signed short SSHORT; 63 | 64 | #if !defined(LONG) 65 | typedef long LONG; 66 | #endif 67 | typedef unsigned long ULONG; 68 | typedef signed long SLONG; 69 | 70 | typedef float FLOAT; 71 | typedef double DOUBLE; 72 | 73 | typedef union 74 | { 75 | USHORT usData; 76 | struct 77 | { 78 | #if defined(LITTLE_ENDIAN) 79 | UCHAR ucLow; 80 | UCHAR ucHigh; 81 | #elif defined(BIG_ENDIAN) 82 | UCHAR ucHigh; 83 | UCHAR ucLow; 84 | #else 85 | #error 86 | #endif 87 | } stBytes; 88 | } USHORT_UNION; 89 | 90 | typedef union 91 | { 92 | ULONG ulData; 93 | UCHAR aucBytes[4]; 94 | struct 95 | { 96 | // The least significant byte of the ULONG in this structure is 97 | // referenced by ucByte0. 98 | UCHAR ucByte0; 99 | UCHAR ucByte1; 100 | UCHAR ucByte2; 101 | UCHAR ucByte3; 102 | } stBytes; 103 | } ULONG_UNION; 104 | 105 | // The following macro computes offset (in bytes) of a member in a structure. This compiles to a constant. 106 | #define STRUCT_OFFSET(MEMBER, STRUCT_POINTER) ( ((UCHAR *) &((STRUCT_POINTER) -> MEMBER)) - ((UCHAR *) (STRUCT_POINTER)) ) 107 | 108 | #endif // !defined(TYPES_H) 109 | --------------------------------------------------------------------------------