From 57ca257e9b96bf0dadb647c8fb3c3ddfa45910ca Mon Sep 17 00:00:00 2001 From: PaulStoffregen Date: Mon, 16 Oct 2017 06:06:51 -0700 Subject: [PATCH] AntPlus from Michael McElligott (work in progress...) --- USBHost_t36.h | 162 +++++- antplus.cpp | 1516 +++++++++++++++++++++++++++++++++++++++++++++++++ antplusdefs.h | 482 ++++++++++++++++ 3 files changed, 2159 insertions(+), 1 deletion(-) create mode 100644 antplus.cpp create mode 100644 antplusdefs.h diff --git a/USBHost_t36.h b/USBHost_t36.h index c9a61c7..25f3950 100644 --- a/USBHost_t36.h +++ b/USBHost_t36.h @@ -56,7 +56,7 @@ // your best effort to read chapter 4 before asking USB questions! -//#define USBHOST_PRINT_DEBUG +#define USBHOST_PRINT_DEBUG /************************************************/ /* Data Types */ @@ -837,6 +837,166 @@ private: }; +class AntPlus: public USBDriver { +public: + AntPlus(USBHost &host) : /* txtimer(this),*/ updatetimer(this) { init(); } + void begin(const uint8_t key=0); +protected: + virtual void Task(); + virtual bool claim(Device_t *device, int type, const uint8_t *descriptors, uint32_t len); + virtual void disconnect(); + virtual void timer_event(USBDriverTimer *whichTimer); +private: + static void rx_callback(const Transfer_t *transfer); + static void tx_callback(const Transfer_t *transfer); + void rx_data(const Transfer_t *transfer); + void tx_data(const Transfer_t *transfer); + void init(); + size_t write(const void *data, const size_t size); + int read(void *data, const size_t size); + void transmit(); +private: + Pipe_t mypipes[2] __attribute__ ((aligned(32))); + Transfer_t mytransfers[3] __attribute__ ((aligned(32))); + //USBDriverTimer txtimer; + USBDriverTimer updatetimer; + Pipe_t *rxpipe; + Pipe_t *txpipe; + bool first_update; + uint8_t txbuffer[240]; + uint8_t rxpacket[64]; + volatile uint16_t txhead; + volatile uint16_t txtail; + volatile bool txready; + volatile uint8_t rxlen; +private: + enum _eventi { + EVENTI_MESSAGE = 0, + EVENTI_CHANNEL, + EVENTI_TOTAL + }; + enum _profiles { + PROFILE_HRM = 0, + PROFILE_SPDCAD, + PROFILE_POWER, + PROFILE_STRIDE, + PROFILE_SPEED, + PROFILE_CADENCE, + PROFILE_TOTAL + }; + typedef struct { + uint16_t deviceId; + uint8_t deviceType; + uint8_t transType; + } TDEVICET; + typedef struct { + uint8_t channel; + uint8_t RFFreq; + uint8_t networkNumber; + uint8_t stub; + uint8_t searchTimeout; + uint8_t channelType; + uint8_t deviceType; + uint8_t transType; + uint16_t channelPeriod; + uint16_t searchWaveform; + uint32_t deviceNumber; // deviceId + TDEVICET dev; + struct { + uint8_t chanIdOnce; + uint8_t keyAccepted; + uint8_t profileValid; + uint8_t channelStatus; + uint8_t channelStatusOld; + } flags; + } TDCONFIG; + //typedef struct { + //int (*cbPtr) (const int channel, const int messageId, + //const uint8_t *payLoad, const size_t dataLength, void *userPtr); + //void *uPtr; // user variable sent with each event + //} TEVENTFUNC; + //typedef struct { + //void (*cbPtr) (TDCONFIG *cfg, const uint8_t *payLoad, + //const size_t dataLength, void *userPtr); + //void *uPtr; // user variable sent with each payload + //} TPAYLOADFUNC; + typedef struct { + uint8_t initOnce; + uint8_t key; // key index + int iDevice; // index to the antplus we're interested in, if > one found + //TEVENTFUNC eventCb[EVENTI_TOTAL]; + //TPAYLOADFUNC payloadCb[PROFILE_TOTAL]; + TDCONFIG dcfg[PROFILE_TOTAL]; // channel config, we're using one channel per device + } TLIBANTPLUS; + TLIBANTPLUS ant; + int (*callbackFunc)(uint32_t msg, intptr_t *value1, uint32_t value2); + //int dispatchMessage(const uint8_t *stream, const int len); + void dispatchPayload(TDCONFIG *cfg, const uint8_t *payload, const int len); + static const uint8_t *getAntKey(const uint8_t keyIdx); + static uint8_t calcMsgChecksum (const uint8_t *buffer, const uint8_t len); + static uint8_t * findStreamSync(uint8_t *stream, const size_t rlen, int *pos); + static int msgCheckIntegrity(uint8_t *stream, const int len); + static int msgGetLength(uint8_t *stream); + int handleMessages(uint8_t *buffer, int tBytes); + //int registerEventCallback(const int which, void *eventFunc, void *userPtr); + //int registerPayloadCallback(const int profile, void *eventFunc, void *userPtr); + + void setCallbackFunc(int (*func)(uint32_t msg, intptr_t *value1, uint32_t value2)); + void sendMessage(uint32_t msg, intptr_t *value1, uint32_t value2); + void sendMessageChannelStatus(TDCONFIG *cfg, const uint32_t channelStatus); + + void message_channel(const int chan, const int eventId, + const uint8_t *payload, const size_t dataLength); + void message_response(const int chan, const int msgId, + const uint8_t *payload, const size_t dataLength); + void message_event(const int channel, const int msgId, + const uint8_t *payload, const size_t dataLength); + + + //int SetPayloadHandler(const int profile, void *eventFunc, void *userPtr); + //int SetEventHandler(const int which, void *eventFunc, void *userPtr); + int ResetSystem(); + int RequestMessage(const int channel, const int message); + int SetNetworkKey(const int netNumber, const uint8_t *key); + int SetChannelSearchTimeout(const int channel, const int searchTimeout); + int SetChannelPeriod(const int channel, const int period); + int SetChannelRFFreq(const int channel, const int freq); + int SetSearchWaveform(const int channel, const int wave); + int OpenChannel(const int channel); + int CloseChannel(const int channel); + int AssignChannel(const int channel, const int channelType, const int network); + int SetChannelId(const int channel, const int deviceNum, const int deviceType, + const int transmissionType); + int SendBurstTransferPacket(const int channelSeq, const uint8_t *data); + int SendBurstTransfer(const int channel, const uint8_t *data, const int nunPackets); + int SendBroadcastData(const int channel, const uint8_t *data); + int SendAcknowledgedData(const int channel, const uint8_t *data); + int SendExtAcknowledgedData(const int channel, const int devNum, const int devType, + const int TranType, const uint8_t *data); + int SendExtBroadcastData(const int channel, const int devNum, const int devType, + const int TranType, const uint8_t *data); + int SendExtBurstTransferPacket(const int chanSeq, const int devNum, + const int devType, const int TranType, const uint8_t *data); + int SendExtBurstTransfer(const int channel, const int devNum, const int devType, + const int tranType, const uint8_t *data, const int nunPackets); + + static void profileSetup_HRM(TDCONFIG *cfg, const uint32_t deviceId); + static void profileSetup_SPDCAD(TDCONFIG *cfg, const uint32_t deviceId); + static void profileSetup_POWER(TDCONFIG *cfg, const uint32_t deviceId); + static void profileSetup_STRIDE(TDCONFIG *cfg, const uint32_t deviceId); + static void profileSetup_SPEED(TDCONFIG *cfg, const uint32_t deviceId); + static void profileSetup_CADENCE(TDCONFIG *cfg, const uint32_t deviceId); + + void payload_HRM(TDCONFIG *cfg, const uint8_t *data, const size_t dataLength); + void payload_SPDCAD(TDCONFIG *cfg, const uint8_t *data, const size_t dataLength); + void payload_POWER(TDCONFIG *cfg, const uint8_t *data, const size_t dataLength); + void payload_STRIDE(TDCONFIG *cfg, const uint8_t *data, const size_t dataLength); + void payload_SPEED(TDCONFIG *cfg, const uint8_t *data, const size_t dataLength); + void payload_CADENCE(TDCONFIG *cfg, const uint8_t *data, const size_t dataLength); + +}; + + class MouseController : public USBHIDInput { public: MouseController(USBHost &host) { USBHIDParser::driver_ready_for_hid_collection(this); } diff --git a/antplus.cpp b/antplus.cpp new file mode 100644 index 0000000..e818e1f --- /dev/null +++ b/antplus.cpp @@ -0,0 +1,1516 @@ +/* USB EHCI Host for Teensy 3.6 + * Copyright 2017 Michael McElligott + * Copyright 2017 Paul Stoffregen (paul@pjrc.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +// https://forum.pjrc.com/threads/43110 +// https://github.com/PaulStoffregen/antplus + +#include +#include "USBHost_t36.h" // Read this header first for key info + +#include "antplusdefs.h" // Ant internal defines, not for Arduino sketches + +#define ANTPLUS_VID 0x0FCF +#define ANTPLUS_2_PID 0x1008 +#define ANTPLUS_M_PID 0x1009 + +#define print USBHost::print_ +#define println USBHost::println_ + + +#define ENABLE_SERIALPRINTF 1 + +#if ENABLE_SERIALPRINTF +#undef printf +#define printf(...) Serial.printf(__VA_ARGS__); Serial.write("\r\n") +#else +#undef printf +#define printf(...) +#endif + + +void AntPlus::init() +{ + contribute_Pipes(mypipes, sizeof(mypipes)/sizeof(Pipe_t)); + contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t)); + driver_ready_for_device(this); + callbackFunc = NULL; +} + +bool AntPlus::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) +{ + if (type != 1) return false; + println("AntPlus claim this=", (uint32_t)this, HEX); + if (dev->idVendor != ANTPLUS_VID) return false; + if (dev->idProduct != ANTPLUS_2_PID && dev->idProduct != ANTPLUS_M_PID) return false; + println("found AntPlus, pid=", dev->idProduct, HEX); + rxpipe = txpipe = NULL; + const uint8_t *p = descriptors; + const uint8_t *end = p + len; + int descriptorLength = p[0]; + int descriptorType = p[1]; + if (descriptorLength < 9 || descriptorType != 4) return false; + p += descriptorLength; + while (p < end) { + descriptorLength = p[0]; + if (p + descriptorLength > end) return false; // reject if beyond end of data + descriptorType = p[1]; + if (descriptorType == 5) { // 5 = endpoint + uint8_t epAddr = p[2]; + uint8_t epType = p[3] & 0x03; + uint16_t epSize = p[4] | (p[5] << 8); + if (epType == 2 && (epAddr & 0xF0) == 0x00) { // Bulk OUT + txpipe = new_Pipe(dev, 2, epAddr, 0, epSize); + } else if (epType == 2 && (epAddr & 0xF0) == 0x80) { // Bulk IN + rxpipe = new_Pipe(dev, 2, epAddr & 0x0F, 1, epSize); + } + } + p += descriptorLength; + } + if (rxpipe && txpipe) { + rxpipe->callback_function = rx_callback; + txpipe->callback_function = tx_callback; + txhead = 0; + txtail = 0; + //rxhead = 0; + //rxtail = 0; + memset(txbuffer, 0, sizeof(txbuffer)); + first_update = true; + txready = true; + updatetimer.start(500000); + queue_Data_Transfer(rxpipe, rxpacket, 64, this); + rxlen = 0; + return true; + } + return false; +} + +void AntPlus::disconnect() +{ + updatetimer.stop(); + //txtimer.stop(); +} + + +void AntPlus::rx_callback(const Transfer_t *transfer) +{ + if (!transfer->driver) return; + ((AntPlus *)(transfer->driver))->rx_data(transfer); +} + +void AntPlus::tx_callback(const Transfer_t *transfer) +{ + if (!transfer->driver) return; + ((AntPlus *)(transfer->driver))->tx_data(transfer); +} + +void AntPlus::rx_data(const Transfer_t *transfer) +{ + uint32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF); + //println("ant rx, len=", len); + //print_hexbytes(transfer->buffer, len); + if (len < 1 || len > 64) { + queue_Data_Transfer(rxpipe, rxpacket, 64, this); + rxlen = 0; + } else { + rxlen = len; // signal arrival of data to Task() + } +} + +void AntPlus::tx_data(const Transfer_t *transfer) +{ + uint8_t *p = (uint8_t *)transfer->buffer; + //print("tx_data, len=", *(p-1)); + //print(", tail=", (p-1) - txbuffer); + //println(", tail=", txtail); + uint32_t tail = txtail; + tail += *(p-1) + 1; + txtail = tail; + //println("new tail=", tail); + txready = true; + transmit(); + //txtimer.start(8000); + // adjust tail... + // start timer if more data to send +} + + +size_t AntPlus::write(const void *data, const size_t size) +{ + //print("write ", size); + //print(" bytes: "); + //print_hexbytes(data, size); + if (size > 64) return 0; + uint32_t head = txhead; + if (++head >= sizeof(txbuffer)) head = 0; + uint32_t remain = sizeof(txbuffer) - head; + if (remain < size + 1) { + // not enough space at end of buffer + txbuffer[head] = 0xFF; + head = 0; + } + uint32_t avail; + do { + uint32_t tail = txtail; + if (head > tail) { + avail = sizeof(txbuffer) - head + tail; + } else { + avail = tail - head; + } + } while (avail < size + 1); // wait for space in buffer + txbuffer[head] = size; + memcpy(txbuffer + head + 1, data, size); + txhead = head + size; + //print("head=", txhead); + //println(", tail=", txtail); + //print_hexbytes(txbuffer, 60); + NVIC_DISABLE_IRQ(IRQ_USBHS); + transmit(); + NVIC_ENABLE_IRQ(IRQ_USBHS); + return size; +} + +void AntPlus::transmit() +{ + if (!txready) return; + uint32_t head = txhead; + uint32_t tail = txtail; + if (head == tail) { + //println("no data to transmit"); + return; // no data to transit + } + if (++tail >= sizeof(txbuffer)) tail = 0; + uint32_t size = txbuffer[tail]; + //print("tail=", tail); + //println(", tx size=", size); + if (size == 0xFF) { + txtail = 0; + tail = 0; + size = txbuffer[0]; + //println("tx size=", size); + } + //txtail = tail + size; + queue_Data_Transfer(txpipe, txbuffer + tail + 1, size, this); + //txtimer.start(8000); + txready = false; +} + +void AntPlus::timer_event(USBDriverTimer *whichTimer) +{ + if (whichTimer == &updatetimer) { + updatetimer.start(250000); + if (first_update) { + ResetSystem(); + first_update = false; + } + //println("ant update timer"); + } + /* else if (whichTimer == &txtimer) { + println("ant tx timer"); + //txtimer.stop(); // TODO: why is this needed? + txready = true; + transmit(); + } */ +} + + +void AntPlus::Task() +{ + uint32_t len = rxlen; + if (len) { + handleMessages(rxpacket, len); + NVIC_DISABLE_IRQ(IRQ_USBHS); + queue_Data_Transfer(rxpipe, rxpacket, 64, this); + rxlen = 0; + NVIC_ENABLE_IRQ(IRQ_USBHS); + } + + + +} + + + + + + +enum _akeys { + KEY_ANTSPORT, + KEY_SUUNTO, + KEY_GARMIN, + KEY_ANTPLUS, + KEY_TOTAL, + KEY_DEFAULT = KEY_ANTSPORT +}; +static const uint8_t antkeys[KEY_TOTAL][8] = { +{0xB9,0xA5,0x21,0xFB,0xBD,0x72,0xC3,0x45}, // Ant+ sport key KEY_ANTSPORT +{0xB9,0xAD,0x32,0x28,0x75,0x7E,0xC7,0x4D}, // Suunto KEY_SUUNTO +{0xA8,0xA4,0x23,0xB9,0xF5,0x5E,0x63,0xC1}, // Garmin KEY_GARMIN +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} // Ant+ key KEY_ANTPLUS (add your key here) +//{0xFD,0x38,0xBE,0xA6,0x40,0x5D,0x26,0x99} +}; + + + +/*int AntPlus::dispatchMessage(const uint8_t *stream, const int len) +{ + return ant.eventCb[EVENTI_MESSAGE].cbPtr(stream[STREAM_CHANNEL], + stream[STREAM_MESSAGE], &stream[STREAM_DATA], + (size_t)stream[STREAM_LENGTH], ant.eventCb[EVENTI_MESSAGE].uPtr); +}*/ + +uint8_t AntPlus::calcMsgChecksum(const uint8_t *buffer, const uint8_t len) +{ + uint8_t checksum = 0x00; + for (uint8_t i = 0; i < len; i++) + checksum ^= buffer[i]; + return checksum; +} + +uint8_t * AntPlus::findStreamSync(uint8_t *stream, const size_t rlen, int *pos) +{ + // find and sync with input stream + *pos = 0; + while (*pos < (int)rlen /*&& *pos < INPUTBUFFERSIZE-3*/){ + if (stream[*pos] == MESG_TX_SYNC) + return stream + *pos; + (*pos)++; + } + return NULL; +} + +int AntPlus::msgCheckIntegrity(uint8_t *stream, const int len) +{ + // min message length is 5 + if (len < 5) return 0; + + int crc = stream[STREAM_SYNC]; + crc ^= stream[STREAM_LENGTH]; + crc ^= stream[STREAM_MESSAGE]; + int mlen = 0; + + do{ + crc ^= stream[STREAM_DATA+mlen]; + } while (++mlen < stream[STREAM_LENGTH]); + + //printf("crc == 0x%X: msg crc = 0x%X\n", crc, stream[stream[STREAM_LENGTH] + 3]); + return (crc == stream[stream[STREAM_LENGTH] + 3]); +} + +int AntPlus::msgGetLength (uint8_t *stream) +{ + // eg; {A4 1 6F 20 EA} = {SYNC DATALEN MSGID DATA CRC} + return stream[STREAM_LENGTH] + 4; +} + +int AntPlus::handleMessages(uint8_t *buffer, int tBytes) +{ + int syncOffset = 0; + //uint8_t buffer[ANTPLUS_MAXPACKETSIZE]; + uint8_t *stream = buffer; + + //int tBytes = antplus_read(ant, buffer, ant->ioVar.readBufferSize); + //if (tBytes <= 0) return tBytes; + + //int tBytes = ANTPLUS_MAXPACKETSIZE; + + while (tBytes > 0){ + stream = findStreamSync(stream, tBytes, &syncOffset); + if (stream == NULL){ + //printf("stream sync not found {size:%i}\n", tBytes); + return 0; + } + tBytes -= syncOffset; + + if (!msgCheckIntegrity(stream, tBytes)){ + //printf("stream integrity failed {size:%i}\n", tBytes); + return 0; + } + + //we have a valid message + //if (dispatchMessage(stream, tBytes) == -1){ + //printf("quiting..\n"); + //return 0; + //} + message_event(stream[STREAM_CHANNEL], stream[STREAM_MESSAGE], + &stream[STREAM_DATA], (size_t)stream[STREAM_LENGTH]); + + + + int len = msgGetLength(stream); + stream += len; + tBytes -= len; + } + return 1; +} + +/*int AntPlus::registerEventCallback(const int which, void *eventFunc, void *userPtr) +{ + if (which < EVENTI_TOTAL) { + ant.eventCb[which].cbPtr = (int (*)(int, int, const uint8_t*, size_t, void*))eventFunc; + ant.eventCb[which].uPtr = userPtr; + return 1; + } + //printf("invalid callback id {%i}\n.", which); + return 0; +}*/ + +/*int AntPlus::registerPayloadCallback(const int profile, void *eventFunc, void *userPtr) +{ + if (profile < PROFILE_TOTAL) { + ant.payloadCb[profile].cbPtr = (void (*)(TDCONFIG*, const uint8_t*, size_t, void*))eventFunc; + ant.payloadCb[profile].uPtr = userPtr; + return 1; + } + //printf("invalid callback id {%i}\n.", profile); + return 0; +}*/ + + + +// TODO: replace this with multiple Arduino style OnXYZ callbacks +void AntPlus::setCallbackFunc(int (*func)(uint32_t msg, intptr_t *value1, uint32_t value2)) +{ + callbackFunc = func; +} +// TODO: replace this with multiple Arduino style OnXYZ callbacks +void AntPlus::sendMessage(uint32_t msg, intptr_t *value1, uint32_t value2) +{ + if (callbackFunc) (*callbackFunc)(msg, value1, value2); +} + +void AntPlus::sendMessageChannelStatus(TDCONFIG *cfg, const uint32_t channelStatus) +{ + cfg->flags.channelStatus = channelStatus; + if (cfg->flags.channelStatus != cfg->flags.channelStatusOld) { + uint32_t status = cfg->flags.channelStatus&0x0F; + status |= ((cfg->channel&0x0F)<<4); + sendMessage(ANTP_MSG_CHANNELSTATUS, NULL, status); + cfg->flags.channelStatusOld = cfg->flags.channelStatus; + } +} + +void AntPlus::message_channel(const int chan, const int eventId, + const uint8_t *payload, const size_t dataLength) +{ + //printf(" $ chan event: chan:%i, msgId:0x%.2X, payload:%p, dataLen:%i, uPtr:%p", chan, eventId, payload, (int)dataLength, uPtr); + //dump_hexbytes(payload, dataLength); + + //TLIBANTPLUS *ant = (AntPlus::TLIBANTPLUS*)uPtr; + TDCONFIG *cfg = &(ant.dcfg[chan]); + + switch (eventId){ + case EVENT_RX_SEARCH_TIMEOUT: + printf(" $ event RX search timeout"); + break; + + case EVENT_RX_FAIL: + //printf(" $ event RX fail"); + break; + + case EVENT_TX: + //printf(" $ event TX"); + break; + + case EVENT_RX_BROADCAST: + //printf(" $ event RX broadcast "); + if (!cfg->flags.chanIdOnce) { + cfg->flags.chanIdOnce = 1; + RequestMessage(cfg->channel, MESG_CHANNEL_ID_ID); + } + //dump_hexbytes(payload, dataLength); + dispatchPayload(cfg, payload, dataLength); + break; + } +} + +void AntPlus::message_response(const int chan, const int msgId, + const uint8_t *payload, const size_t dataLength) +{ + //printf(" # response event: msgId:0x%.2X, payload:%p, dataLen:%i, uPtr:%p", msgId, payload, dataLength, uPtr); + TDCONFIG *cfg = (TDCONFIG*)&(ant.dcfg[chan]); + + switch (msgId){ + case MESG_EVENT_ID: + //printf(" * event"); + message_channel(chan, payload[STREAM_EVENT_EVENTID], payload, dataLength); + break; + + case MESG_NETWORK_KEY_ID: + printf("[%i] * network key accepted", chan); + cfg->flags.keyAccepted = 1; + if (cfg->transType == ANT_TRANSMISSION_MASTER) + AssignChannel(cfg->channel, PARAMETER_TX_NOT_RX, cfg->networkNumber); + else + AssignChannel(cfg->channel, cfg->channelType, cfg->networkNumber); + break; + + case MESG_ASSIGN_CHANNEL_ID: + printf("[%i] * channel assign accepted", chan); + SetChannelPeriod(cfg->channel, cfg->channelPeriod); + break; + + case MESG_CHANNEL_MESG_PERIOD_ID: + printf("[%i] * channel mesg period accepted", chan); + SetChannelSearchTimeout(cfg->channel, cfg->searchTimeout); + break; + + case MESG_CHANNEL_SEARCH_TIMEOUT_ID: + printf("[%i] * search timeout period accepted", chan); + SetChannelRFFreq(cfg->channel, cfg->RFFreq); + break; + + case MESG_CHANNEL_RADIO_FREQ_ID: + printf("[%i] * radio freq accepted", chan); + SetSearchWaveform(cfg->channel, cfg->searchWaveform); + break; + + case MESG_SEARCH_WAVEFORM_ID: + printf("[%i] * search waveform accepted", chan); + SetChannelId(cfg->channel, cfg->deviceNumber, cfg->deviceType, cfg->transType); + break; + + case MESG_CHANNEL_ID_ID: + printf("[%i] * set channel id accepted", chan); + OpenChannel(cfg->channel); + break; + + case MESG_OPEN_CHANNEL_ID: + printf("[%i] * open channel accepted", chan); + //cfg->flags.channelStatus = 1; + RequestMessage(cfg->channel, MESG_CHANNEL_STATUS_ID); + RequestMessage(cfg->channel, MESG_CAPABILITIES_ID); + RequestMessage(cfg->channel, MESG_VERSION_ID); + break; + + case MESG_UNASSIGN_CHANNEL_ID: + printf("[%i] * channel Unassigned", chan); + break; + + case MESG_CLOSE_CHANNEL_ID: + printf("[%i] * channel CLOSED", chan); + cfg->flags.keyAccepted = 0; + sendMessageChannelStatus(cfg, ANT_CHANNEL_STATUS_UNASSIGNED); + break; + + case CHANNEL_IN_WRONG_STATE: + printf("[%i] * channel in wrong state", chan); + break; + + case CHANNEL_NOT_OPENED: + printf("[%i] * channel not opened", chan); + break; + + case CHANNEL_ID_NOT_SET: //?? + printf("[%i] * channel ID not set", chan); + break; + + case CLOSE_ALL_CHANNELS: // Start RX Scan mode + printf("[%i] * close all channels", chan); + break; + + case TRANSFER_IN_PROGRESS: // TO ack message ID + printf("[%i] * tranfer in progress", chan); + break; + + case TRANSFER_SEQUENCE_NUMBER_ERROR: + printf("[%i] * transfer sequence number error", chan); + break; + + case TRANSFER_IN_ERROR: + printf("[%i] * transfer in error", chan); + break; + + case INVALID_MESSAGE: + printf("[%i] * invalid message", chan); + break; + + case INVALID_NETWORK_NUMBER: + printf("[%i] * invalid network number", chan); + break; + + case INVALID_LIST_ID: + printf("[%i] * invalid list ID", chan); + break; + + case INVALID_SCAN_TX_CHANNEL: + printf("[%i] * invalid Scanning transmit channel", chan); + break; + + case INVALID_PARAMETER_PROVIDED: + printf("[%i] * invalid parameter provided", chan); + break; + + case EVENT_QUE_OVERFLOW: + printf("[%i] * queue overflow", chan); + break; + + default: + printf("[i] #### unhandled response id %i", chan, msgId); + ; + }; +} + + +void AntPlus::message_event(const int channel, const int msgId, + const uint8_t *payload, const size_t dataLength) +{ + //printf(" @ msg event cb: Chan:%i, Id:0x%.2X, payload:%p, len:%i, ptr:%p", channel, msgId, payload, (int)dataLength, uPtr); + //dump_hexbytes(payload, dataLength); + + //TLIBANTPLUS *ant = (TLIBANTPLUS*)uPtr; + uint8_t chan = 0; + if (channel >= 0 && channel < PROFILE_TOTAL) chan = channel; + + switch(msgId) { + case MESG_BROADCAST_DATA_ID: + //printf(" @ broadcast data \n"); + //dumpPayload(payload, dataLength); + message_channel(chan, EVENT_RX_BROADCAST, payload, dataLength); + break; + + case MESG_STARTUP_MESG_ID: + // reason == ANT_STARTUP_RESET_xxxx + //printf(" @ start up mesg reason: 0x%X", payload[STREAM_STARTUP_REASON]); + //TDCONFIG *cfg = &(ant.dcfg[0]); + //SetNetworkKey(cfg->networkNumber, getAntKey(cfg->keyIdx)); + SetNetworkKey(ant.dcfg[0].networkNumber, getAntKey(ant.key)); + break; + + case MESG_RESPONSE_EVENT_ID: + message_response(payload[STREAM_EVENT_CHANNEL_ID], + payload[STREAM_EVENT_RESPONSE_ID], payload, dataLength); + break; + + case MESG_CHANNEL_STATUS_ID: + //printf(" @ channel status for channel %i is %i", + // payload[STREAM_CHANNEL_ID], payload[STREAM_CHANNEL_STATUS]); + //TDCONFIG *cfg = (TDCONFIG*)&ant->dcfg[payload[STREAM_CHANNEL_ID]]; + sendMessageChannelStatus(&(ant.dcfg[payload[STREAM_CHANNEL_ID]]), + payload[STREAM_CHANNELSTATUS_STATUS] & ANT_CHANNEL_STATUS_MASK); + //if (cfg->flags.channelStatus != STATUS_TRACKING_CHANNEL) + // printf("channel %i status: %s", payload[STREAM_CHANNEL_ID], + // channelStatusStr[cfg->flags.channelStatus]); + break; + + case MESG_CAPABILITIES_ID: + //printf(" @ capabilities:"); + //printf(" Max ANT Channels: %i",payload[STREAM_CAP_MAXCHANNELS]); + //printf(" Max ANT Networks: %i",payload[STREAM_CAP_MAXNETWORKS]); + //printf(" Std. option: 0x%X",payload[STREAM_CAP_STDOPTIONS]); + //printf(" Advanced: 0x%X",payload[STREAM_CAP_ADVANCED]); + //printf(" Advanced2: 0x%X",payload[STREAM_CAP_ADVANCED2]); + break; + + case MESG_CHANNEL_ID_ID: + //TDCONFIG *cfg = (TDCONFIG*)&ant->dcfg[chan]; + ant.dcfg[chan].dev.deviceId = payload[STREAM_CHANNELID_DEVNO_LO] | (payload[STREAM_CHANNELID_DEVNO_HI] << 8); + ant.dcfg[chan].dev.deviceType = payload[STREAM_CHANNELID_DEVTYPE]; + ant.dcfg[chan].dev.transType = payload[STREAM_CHANNELID_TRANTYPE]; + //printf(" @ CHANNEL ID: channel %i, deviceId:%i, deviceType:%i, transType:%i)", chan, cfg->dev.deviceId, cfg->dev.deviceType, cfg->dev.transType); + sendMessage(ANTP_MSG_DEVICEID, (intptr_t *)&(ant.dcfg[chan].dev), chan); +#if 0 + if (cfg->dev.scidDeviceType != cfg->deviceType){ + printf(" @ CHANNEL ID: this is not the device we're looking for"); + printf(" @ CHANNEL ID: expecting 0x%X but found 0x%X", cfg->deviceType, cfg->dev.scidDeviceType); + //CloseChannel(cfg->channel); + return; + } +#endif + break; + + case MESG_VERSION_ID: + //printf(" @ version: '%s'", (char*)&payload[STREAM_VERSION_STRING]); + break; + }; +} + + +//int AntPlus::SetPayloadHandler(const int profile, void *eventFunc, void *userPtr) +//{ + //return registerPayloadCallback(profile, eventFunc, userPtr); +//} + +//int AntPlus::SetEventHandler(const int which, void *eventFunc, void *userPtr) +//{ + //return registerEventCallback(which, eventFunc, userPtr); +//} + +int AntPlus::ResetSystem() +{ + //println("libantplus_ResetSystem"); + uint8_t msg[5]; + msg[0] = MESG_TX_SYNC; // sync + msg[1] = 1; // length + msg[2] = MESG_SYSTEM_RESET_ID; // msg id + msg[3] = 0; // nop + msg[4] = calcMsgChecksum(msg, 4); + return write(msg, 5); +} + +int AntPlus::RequestMessage(const int channel, const int message) +{ + //println("libantplus_RequestMessage"); + uint8_t msg[6]; + msg[0] = MESG_TX_SYNC; // sync + msg[1] = 2; // length + msg[2] = MESG_REQUEST_ID; // msg id + msg[3] = (uint8_t)channel; + msg[4] = (uint8_t)message; + msg[5] = calcMsgChecksum(msg, 5); + return write(msg, 6); +} + +int AntPlus::SetNetworkKey(const int netNumber, const uint8_t *key) +{ + uint8_t msg[13]; + msg[0] = MESG_TX_SYNC; + msg[1] = 9; + msg[2] = MESG_NETWORK_KEY_ID; + msg[3] = (uint8_t)netNumber; + msg[4] = key[0]; + msg[5] = key[1]; + msg[6] = key[2]; + msg[7] = key[3]; + msg[8] = key[4]; + msg[9] = key[5]; + msg[10] = key[6]; + msg[11] = key[7]; + msg[12] = calcMsgChecksum(msg, 12); // xor checksum + return write(msg, 13); +} + +int AntPlus::SetChannelSearchTimeout(const int channel, const int searchTimeout) +{ + uint8_t msg[6]; + msg[0] = MESG_TX_SYNC; // sync + msg[1] = 2; // length + msg[2] = MESG_CHANNEL_SEARCH_TIMEOUT_ID; + msg[3] = (uint8_t)channel; + msg[4] = (uint8_t)searchTimeout; + msg[5] = calcMsgChecksum(msg, 5); + return write(msg, 6); +} + +int AntPlus::SetChannelPeriod(const int channel, const int period) +{ + uint8_t msg[7]; + msg[0] = MESG_TX_SYNC; // sync + msg[1] = 3; // length + msg[2] = MESG_CHANNEL_MESG_PERIOD_ID; + msg[3] = (uint8_t)channel; + msg[4] = (uint8_t)(period & 0xFF); + msg[5] = (uint8_t)(period >> 8); + msg[6] = calcMsgChecksum(msg, 6); + return write(msg, 7); +} + +int AntPlus::SetChannelRFFreq(const int channel, const int freq) +{ + uint8_t msg[6]; + msg[0] = MESG_TX_SYNC; // sync + msg[1] = 2; // length + msg[2] = MESG_CHANNEL_RADIO_FREQ_ID; + msg[3] = (uint8_t)channel; + msg[4] = (uint8_t)freq; + msg[5] = calcMsgChecksum(msg, 5); + return write(msg, 6); +} + +int AntPlus::SetSearchWaveform(const int channel, const int wave) +{ + uint8_t msg[7]; + msg[0] = MESG_TX_SYNC; // sync + msg[1] = 3; // length + msg[2] = MESG_SEARCH_WAVEFORM_ID; // msg id + msg[3] = (uint8_t)channel; + msg[4] = (uint8_t)wave & 0xFF; + msg[5] = (uint8_t)wave >> 8; + msg[6] = calcMsgChecksum(msg, 6); + return write(msg, 7); +} + +int AntPlus::OpenChannel(const int channel) +{ + uint8_t msg[5]; + msg[0] = MESG_TX_SYNC; // sync + msg[1] = 1; // length + msg[2] = MESG_OPEN_CHANNEL_ID; // msg id + msg[3] = (uint8_t)channel; + msg[4] = calcMsgChecksum(msg, 4); + return write(msg, 5); +} + +int AntPlus::CloseChannel(const int channel) +{ + uint8_t msg[5]; + msg[0] = MESG_TX_SYNC; // sync + msg[1] = 1; // length + msg[2] = MESG_CLOSE_CHANNEL_ID; // msg id + msg[3] = (uint8_t)channel; + msg[4] = calcMsgChecksum(msg, 4); + return write(msg, 5); +} + +int AntPlus::AssignChannel(const int channel, const int channelType, const int network) +{ + uint8_t msg[7]; + msg[0] = MESG_TX_SYNC; + msg[1] = 3; + msg[2] = MESG_ASSIGN_CHANNEL_ID; + msg[3] = (uint8_t)channel; + msg[4] = (uint8_t)channelType; + msg[5] = (uint8_t)network; + msg[6] = calcMsgChecksum(msg, 6); + return write(msg, 7); +} + +int AntPlus::SetChannelId(const int channel, const int deviceNum, const int deviceType, const int transmissionType) +{ + uint8_t msg[9]; + msg[0] = MESG_TX_SYNC; + msg[1] = 5; + msg[2] = MESG_CHANNEL_ID_ID; + msg[3] = (uint8_t)channel; + msg[4] = (uint8_t)(deviceNum & 0xFF); + msg[5] = (uint8_t)(deviceNum >> 8); + msg[6] = (uint8_t)deviceType; + msg[7] = (uint8_t)transmissionType; + msg[8] = calcMsgChecksum(msg, 8); + return write(msg, 9); +} + +int AntPlus::SendBurstTransferPacket(const int channelSeq, const uint8_t *data) +{ + uint8_t msg[13]; + msg[0] = MESG_TX_SYNC; + msg[1] = 9; + msg[2] = MESG_BURST_DATA_ID; + msg[3] = (uint8_t)channelSeq; + msg[4] = data[0]; + msg[5] = data[1]; + msg[6] = data[2]; + msg[7] = data[3]; + msg[8] = data[4]; + msg[9] = data[5]; + msg[10] = data[6]; + msg[11] = data[7]; + msg[12] = calcMsgChecksum(msg, 12); // xor checksum + return write(msg, 13); +} + +int AntPlus::SendBurstTransfer(const int channel, const uint8_t *data, const int nunPackets) +{ + int ret = 0; + int seq = 0; + for (int i = 0; i < nunPackets; i++) { + if (i == nunPackets-1) seq |= 0x04; + ret = SendBurstTransferPacket((seq<<5) | (channel&0x1F), data+(i<<3)); + seq = (seq+1)&0x03; + } + return ret; +} + +int AntPlus::SendBroadcastData(const int channel, const uint8_t *data) +{ + uint8_t msg[13]; + msg[0] = MESG_TX_SYNC; + msg[1] = 9; + msg[2] = MESG_BROADCAST_DATA_ID; + msg[3] = (uint8_t)channel; + msg[4] = data[0]; + msg[5] = data[1]; + msg[6] = data[2]; + msg[7] = data[3]; + msg[8] = data[4]; + msg[9] = data[5]; + msg[10] = data[6]; + msg[11] = data[7]; + msg[12] = calcMsgChecksum(msg, 12); + return write(msg, 13); +} + +int AntPlus::SendAcknowledgedData(const int channel, const uint8_t *data) +{ + uint8_t msg[13]; + msg[0] = MESG_TX_SYNC; + msg[1] = 9; + msg[2] = MESG_ACKNOWLEDGED_DATA_ID; + msg[3] = (uint8_t)channel; + msg[4] = data[0]; + msg[5] = data[1]; + msg[6] = data[2]; + msg[7] = data[3]; + msg[8] = data[4]; + msg[9] = data[5]; + msg[10] = data[6]; + msg[11] = data[7]; + msg[12] = calcMsgChecksum(msg, 12); + return write(msg, 13); +} + +int AntPlus::SendExtAcknowledgedData(const int channel, const int devNum, const int devType, const int TranType, const uint8_t *data) +{ + uint8_t msg[17]; + msg[0] = MESG_TX_SYNC; + msg[1] = 13; + msg[2] = MESG_EXT_ACKNOWLEDGED_DATA_ID; + msg[3] = (uint8_t)channel; + msg[4] = (uint8_t)(devNum & 0xFF); + msg[5] = (uint8_t)(devNum >> 8); + msg[6] = (uint8_t)devType; + msg[7] = (uint8_t)TranType; + msg[8] = data[0]; + msg[9] = data[1]; + msg[10] = data[2]; + msg[11] = data[3]; + msg[12] = data[4]; + msg[13] = data[5]; + msg[14] = data[6]; + msg[15] = data[7]; + msg[16] = calcMsgChecksum(msg, 16); + return write(msg, 17); +} + +int AntPlus::SendExtBroadcastData(const int channel, const int devNum, const int devType, const int TranType, const uint8_t *data) +{ + uint8_t msg[17]; + msg[0] = MESG_TX_SYNC; + msg[1] = 13; + msg[2] = MESG_EXT_BROADCAST_DATA_ID; + msg[3] = (uint8_t)channel; + msg[4] = (uint8_t)(devNum & 0xFF); + msg[5] = (uint8_t)(devNum >> 8); + msg[6] = (uint8_t)devType; + msg[7] = (uint8_t)TranType; + msg[8] = data[0]; + msg[9] = data[1]; + msg[10] = data[2]; + msg[11] = data[3]; + msg[12] = data[4]; + msg[13] = data[5]; + msg[14] = data[6]; + msg[15] = data[7]; + msg[16] = calcMsgChecksum(msg, 16); + return write(msg, 17); +} + +int AntPlus::SendExtBurstTransferPacket(const int chanSeq, const int devNum, const int devType, + const int TranType, const uint8_t *data) +{ + uint8_t msg[17]; + msg[0] = MESG_TX_SYNC; + msg[1] = 13; + msg[2] = MESG_EXT_BROADCAST_DATA_ID; + msg[3] = (uint8_t)chanSeq; + msg[4] = (uint8_t)(devNum & 0xFF); + msg[5] = (uint8_t)(devNum >> 8); + msg[6] = (uint8_t)devType; + msg[7] = (uint8_t)TranType; + msg[8] = data[0]; + msg[9] = data[1]; + msg[10] = data[2]; + msg[11] = data[3]; + msg[12] = data[4]; + msg[13] = data[5]; + msg[14] = data[6]; + msg[15] = data[7]; + msg[16] = calcMsgChecksum(msg, 16); + return write(msg, 17); +} + +int AntPlus::SendExtBurstTransfer(const int channel, const int devNum, const int devType, + const int tranType, const uint8_t *data, const int nunPackets) +{ + int ret = 0; + int seq = 0; + for (int i = 0; i < nunPackets; i++){ + if (i == nunPackets-1) seq |= 0x04; + ret = SendExtBurstTransferPacket((seq<<5) | (channel&0x1F), + devNum, devType, tranType, data+(i<<3)); + seq = (seq+1)&0x03; + } + return ret; +} + +//const uint8_t *libantplus_GetNetworkKey (const uint8_t keyIdx) +//{ + //return antplus_getAntKey(keyIdx); +//} + + + + +void AntPlus::profileSetup_HRM(TDCONFIG *cfg, const uint32_t deviceId) +{ + cfg->deviceNumber = deviceId; // 0 + cfg->deviceType = ANT_DEVICE_HRM; + cfg->transType = ANT_TRANSMISSION_SLAVE; + cfg->channelType = ANT_CHANNEL_TYPE_SLAVE; + cfg->networkNumber = 0; + cfg->channel = PROFILE_HRM; + cfg->channelPeriod = ANT_PERIOD_HRM; + cfg->RFFreq = ANT_FREQUENCY_SPORT; + cfg->searchTimeout = 255; + cfg->searchWaveform = 0x53;//316;//0x53; + //cfg->keyIdx = KEY_ANTSPORT; + cfg->flags.chanIdOnce = 0; + cfg->flags.channelStatus = ANT_CHANNEL_STATUS_UNASSIGNED; + cfg->flags.channelStatusOld = 0xFF; + cfg->flags.keyAccepted = 0; + cfg->flags.profileValid = 1; +} + +void AntPlus::profileSetup_SPDCAD(TDCONFIG *cfg, const uint32_t deviceId) +{ + cfg->deviceNumber = deviceId; // 0 + cfg->deviceType = ANT_DEVICE_SPDCAD; + cfg->transType = ANT_TRANSMISSION_SLAVE; // 5 + cfg->channelType = ANT_CHANNEL_TYPE_SLAVE; + cfg->networkNumber = 0; + cfg->channel = PROFILE_SPDCAD; + cfg->channelPeriod = ANT_PERIOD_SPDCAD; + cfg->RFFreq = ANT_FREQUENCY_SPORT; + cfg->searchTimeout = 255; + cfg->searchWaveform = 0x53; + //cfg->keyIdx = KEY_ANTSPORT; + cfg->flags.chanIdOnce = 0; + cfg->flags.channelStatus = ANT_CHANNEL_STATUS_UNASSIGNED; + cfg->flags.channelStatusOld = 0xFF; + cfg->flags.keyAccepted = 0; + cfg->flags.profileValid = 1; +} + +void AntPlus::profileSetup_POWER(TDCONFIG *cfg, const uint32_t deviceId) +{ + cfg->deviceNumber = deviceId; // 0 + cfg->deviceType = ANT_DEVICE_POWER; + cfg->transType = ANT_TRANSMISSION_SLAVE; // 5 + cfg->channelType = ANT_CHANNEL_TYPE_SLAVE; + cfg->networkNumber = 0; + cfg->channel = PROFILE_POWER; + cfg->channelPeriod = ANT_PERIOD_POWER; + cfg->RFFreq = ANT_FREQUENCY_SPORT; + cfg->searchTimeout = 255; + cfg->searchWaveform = 0x53; + //cfg->keyIdx = KEY_ANTSPORT; + cfg->flags.chanIdOnce = 0; + cfg->flags.channelStatus = ANT_CHANNEL_STATUS_UNASSIGNED; + cfg->flags.channelStatusOld = 0xFF; + cfg->flags.keyAccepted = 0; + cfg->flags.profileValid = 1; +} + +void AntPlus::profileSetup_STRIDE(TDCONFIG *cfg, const uint32_t deviceId) +{ + cfg->deviceNumber = deviceId; // 0 + cfg->deviceType = ANT_DEVICE_STRIDE; + cfg->transType = ANT_TRANSMISSION_SLAVE; // 5 + cfg->channelType = ANT_CHANNEL_TYPE_SLAVE; + cfg->networkNumber = 0; + cfg->channel = PROFILE_STRIDE; + cfg->channelPeriod = ANT_PERIOD_STRIDE; + cfg->RFFreq = ANT_FREQUENCY_STRIDE; + cfg->searchTimeout = 255; + cfg->searchWaveform = 0x53; + //cfg->keyIdx = KEY_ANTSPORT; + cfg->flags.chanIdOnce = 0; + cfg->flags.channelStatus = ANT_CHANNEL_STATUS_UNASSIGNED; + cfg->flags.channelStatusOld = 0xFF; + cfg->flags.keyAccepted = 0; + cfg->flags.profileValid = 1; +} + +void AntPlus::profileSetup_SPEED(TDCONFIG *cfg, const uint32_t deviceId) +{ + cfg->deviceNumber = deviceId; // 0 + cfg->deviceType = ANT_DEVICE_SPEED; + cfg->transType = ANT_TRANSMISSION_SLAVE; // 5 + cfg->channelType = ANT_CHANNEL_TYPE_SLAVE; + cfg->networkNumber = 0; + cfg->channel = PROFILE_SPEED; + cfg->channelPeriod = ANT_PERIOD_SPEED; + cfg->RFFreq = ANT_FREQUENCY_SPORT; + cfg->searchTimeout = 255; + cfg->searchWaveform = 0x53; + //cfg->keyIdx = KEY_ANTSPORT; + cfg->flags.chanIdOnce = 0; + cfg->flags.channelStatus = ANT_CHANNEL_STATUS_UNASSIGNED; + cfg->flags.channelStatusOld = 0xFF; + cfg->flags.keyAccepted = 0; + cfg->flags.profileValid = 1; +} + +void AntPlus::profileSetup_CADENCE(TDCONFIG *cfg, const uint32_t deviceId) +{ + cfg->deviceNumber = deviceId; // 0 + cfg->deviceType = ANT_DEVICE_CADENCE; + cfg->transType = ANT_TRANSMISSION_SLAVE; // 5 + cfg->channelType = ANT_CHANNEL_TYPE_SLAVE; + cfg->networkNumber = 0; + cfg->channel = PROFILE_CADENCE; + cfg->channelPeriod = ANT_PERIOD_CADENCE; + cfg->RFFreq = ANT_FREQUENCY_SPORT; + cfg->searchTimeout = 255; + cfg->searchWaveform = 0x53; + //cfg->keyIdx = KEY_ANTSPORT; + cfg->flags.chanIdOnce = 0; + cfg->flags.channelStatus = ANT_CHANNEL_STATUS_UNASSIGNED; + cfg->flags.channelStatusOld = 0xFF; + cfg->flags.keyAccepted = 0; + cfg->flags.profileValid = 1; +} + +#if 0 + +/* +uint64_t factory_passkey (uint64_t device_id, uint8_t *buffer) +{ + uint64_t n = (((uint64_t)device_id ^ 0x7d215abb) << 32) + ((uint64_t)device_id ^ 0x42b93f06); + + for (uint8_t i = 0; i < 8; i++) + buffer[i] = n >> (8*i)&0xFF; + + return n; +} + +*/ +int libantplus_Start () +{ +#if 0 + uint8_t buffer[8]; + factory_passkey(3825666043, buffer); + dump_hexbytes(buffer, 8); +#endif + + int ct = 0; + uint32_t deviceId; + + for (int i = 0; i < PROFILE_TOTAL; i++){ + deviceId = 0; + if (antplus_sendMessage(ANTP_MSG_PROFILE_SELECT, (intptr_t*)&deviceId, i) != 1) + continue; + + ct++; + //printf("enabling profile %i", i); + + switch (i){ + case PROFILE_HRM: + profileSetup_HRM(&ant->dcfg[PROFILE_HRM], deviceId); + libantplus_SetPayloadHandler(PROFILE_HRM, (void*)payload_HRM, (void*)NULL); + break; + case PROFILE_SPDCAD: + profileSetup_SPDCAD(&ant->dcfg[PROFILE_SPDCAD], deviceId); + libantplus_SetPayloadHandler(PROFILE_SPDCAD, (void*)payload_SPDCAD, (void*)NULL); + break; + case PROFILE_POWER: + profileSetup_POWER(&ant->dcfg[PROFILE_POWER], deviceId); + libantplus_SetPayloadHandler(PROFILE_POWER, (void*)payload_POWER, (void*)NULL); + break; + case PROFILE_STRIDE: + profileSetup_STRIDE(&ant->dcfg[PROFILE_STRIDE], deviceId); + libantplus_SetPayloadHandler(PROFILE_STRIDE, (void*)payload_STRIDE, (void*)NULL); + break; + case PROFILE_SPEED: + profileSetup_SPEED(&ant->dcfg[PROFILE_SPEED], deviceId); + libantplus_SetPayloadHandler(PROFILE_SPEED, (void*)payload_SPEED, (void*)NULL); + break; + case PROFILE_CADENCE: + profileSetup_CADENCE(&ant->dcfg[PROFILE_CADENCE], deviceId); + libantplus_SetPayloadHandler(PROFILE_CADENCE, (void*)payload_CADENCE, (void*)NULL); + break; + } + } + + return ct; +} + + +TLIBANTPLUS *libantplus_Init (const uint8_t networkKey) +{ + if (networkKey >= KEY_TOTAL){ + //printf("libantplus_Init(): invalid networkKey (%i)"); + //return NULL; + ant->key = KEY_DEFAULT; + }else{ + ant->key = networkKey; + } + + libantplus_SetEventHandler(EVENTI_MESSAGE, (void*)message_event, (void*)ant); + return ant; +} +#endif + + +void AntPlus::begin(const uint8_t key) +{ + ant.key = (key < KEY_TOTAL) ? key : 0; + + int deviceId = 0; // TODO: user API to set this? + profileSetup_HRM(&ant.dcfg[PROFILE_HRM], deviceId); + profileSetup_SPDCAD(&ant.dcfg[PROFILE_SPDCAD], deviceId); + profileSetup_POWER(&ant.dcfg[PROFILE_POWER], deviceId); + profileSetup_STRIDE(&ant.dcfg[PROFILE_STRIDE], deviceId); + profileSetup_SPEED(&ant.dcfg[PROFILE_SPEED], deviceId); + profileSetup_CADENCE(&ant.dcfg[PROFILE_CADENCE], deviceId); + + //ant.eventCb[EVENTI_MESSAGE].cbPtr = &message_event; + //SetEventHandler(EVENTI_MESSAGE, (void*)message_event, (void*)ant); +} + +/* +int AntPlus::registerEventCallback(const int which, void *eventFunc, void *userPtr) +{ + if (which < EVENTI_TOTAL) { + ant.eventCb[which].cbPtr = (int (*)(int, int, const uint8_t*, size_t, void*))eventFunc; + +*/ + + + + + + + +void AntPlus::dispatchPayload(TDCONFIG *cfg, const uint8_t *payload, const int len) +{ + switch (cfg->channel) { + case PROFILE_HRM: + payload_HRM(cfg, payload, len); + break; + case PROFILE_SPDCAD: + payload_SPDCAD(cfg, payload, len); + break; + case PROFILE_POWER: + payload_POWER(cfg, payload, len); + break; + case PROFILE_STRIDE: + payload_STRIDE(cfg, payload, len); + break; + case PROFILE_SPEED: + payload_SPEED(cfg, payload, len); + break; + case PROFILE_CADENCE: + payload_CADENCE(cfg, payload, len); + break; + } +} + +const uint8_t * AntPlus::getAntKey(const uint8_t keyIdx) +{ + if (keyIdx >= KEY_TOTAL) return NULL; + return antkeys[keyIdx]; +} + + +#define WHEEL_CIRCUMFERENCE 2122 + + +typedef struct { + struct { + uint16_t time; + uint16_t interval; + uint8_t bpm; // heart rate in beats per minute + uint8_t sequence; + }current; + + struct { + uint8_t bpm; + uint8_t sequence; + uint16_t time; + uint16_t interval; + }previous; +}payload_HRM_t; + +typedef struct { + struct { + uint16_t cadenceTime; + uint16_t cadence; + uint16_t cadenceCt; + uint16_t speedTime; + uint16_t speed; + uint16_t speedCt; + uint32_t distance; + }current; + + struct { + uint16_t cadenceTime; + uint16_t cadence; + uint16_t cadenceCt; + uint16_t speedTime; + uint16_t speed; + uint16_t speedCt; + uint32_t distance; + }previous; + + uint16_t wheelCircumference; // default is WHEEL_CIRCUMFERENCE (2122cm) + uint8_t spdChange; + uint8_t cadChange; +}payload_SPDCAD_t; + + + +typedef struct { + struct { + uint8_t sequence; + uint16_t pedalPowerContribution; + uint8_t pedalPower; + uint8_t instantCadence; + uint16_t sumPower; + uint16_t instantPower; + } current; + struct { + uint16_t stub; + } previous; +}payload_POWER_t; + +typedef struct { + struct { + uint16_t speed; + uint16_t cadence; + uint8_t strides; + }current; + + struct { + uint8_t strides; + uint16_t speed; + uint16_t cadence; + }previous; +}payload_STRIDE_t; + +typedef struct { + struct { + uint16_t speedTime; + uint16_t speed; + uint16_t speedCt; + uint32_t distance; + }current; + + struct { + uint16_t speedTime; + uint16_t speed; + uint16_t speedCt; + uint32_t distance; + }previous; + + uint16_t wheelCircumference; // default is WHEEL_CIRCUMFERENCE (2122cm) + uint8_t spdChange; +}payload_SPEED_t; + +typedef struct { + struct { + uint16_t cadenceTime; + uint16_t cadence; + uint16_t cadenceCt; + }current; + + struct { + uint16_t cadenceTime; + uint16_t cadence; + uint16_t cadenceCt; + }previous; + + uint8_t cadChange; +}payload_CADENCE_t; + + + +typedef struct { + payload_HRM_t hrm; + payload_SPDCAD_t spdcad; + payload_POWER_t power; + payload_STRIDE_t stride; + payload_SPEED_t spd; + payload_CADENCE_t cad; +}payload_genstorage_t; + +static payload_genstorage_t payload; // FIXME: should be in the AntPlus object + + + +void AntPlus::payload_HRM(TDCONFIG *cfg, const uint8_t *data, const size_t dataLength) +{ + payload_HRM_t *hrm = &payload.hrm; + hrm->current.bpm = data[STREAM_RXBROADCAST_DEV120_HR]; + hrm->current.sequence = data[STREAM_RXBROADCAST_DEV120_SEQ]; + + //const int page = data[1]&0x0F; + if (hrm->previous.sequence != hrm->current.sequence || hrm->previous.bpm != hrm->current.bpm){ + if (hrm->current.bpm){ + hrm->current.time = (data[STREAM_RXBROADCAST_DEV120_BEATLO] + (data[STREAM_RXBROADCAST_DEV120_BEATHI] << 8)); + hrm->current.interval = hrm->current.time - hrm->previous.time; + hrm->current.interval = (((int32_t)hrm->current.interval) * (int32_t)1000) / (int32_t)1024; + + //printf("page %i", page); + sendMessage(ANTP_MSG_PROFILE_DATA, (intptr_t*)hrm, PROFILE_HRM); + + hrm->previous.time = hrm->current.time; + hrm->previous.interval = hrm->current.interval; + hrm->previous.sequence = hrm->current.sequence; + hrm->previous.bpm = hrm->current.bpm; + } + } + + //int page = data[1]&0x0F; + //printf("payload_HRM: page:%i, Sequence:%i, BPM:%i, %i %i", page, hrm->current.sequence, hrm->current.bpm, hrm->current.time, hrm->current.interval); +} + +void AntPlus::payload_SPDCAD(TDCONFIG *cfg, const uint8_t *data, const size_t dataLength) +{ + payload_SPDCAD_t *spdcad = &payload.spdcad; + + spdcad->current.cadenceTime = data[1]; + spdcad->current.cadenceTime |= (data[2] << 8); + spdcad->current.cadenceCt = data[3]; + spdcad->current.cadenceCt |= (data[4] << 8); + spdcad->current.speedTime = data[5]; + spdcad->current.speedTime |= (data[6] << 8); + spdcad->current.speedCt = data[7]; + spdcad->current.speedCt |= (data[8] << 8); + + spdcad->cadChange = (spdcad->current.cadenceTime != spdcad->previous.cadenceTime || spdcad->current.cadenceCt != spdcad->previous.cadenceCt); + spdcad->spdChange = (spdcad->current.speedTime != spdcad->previous.speedTime || spdcad->current.speedCt != spdcad->previous.speedCt); + + if (spdcad->cadChange || spdcad->spdChange){ + uint16_t cadence = (60 * (spdcad->current.cadenceCt - spdcad->previous.cadenceCt) * 1024) / (spdcad->current.cadenceTime - spdcad->previous.cadenceTime); + spdcad->current.cadence = cadence; + + if (!spdcad->wheelCircumference) spdcad->wheelCircumference = WHEEL_CIRCUMFERENCE; + uint32_t speedRotationDelta = spdcad->current.speedCt - spdcad->previous.speedCt; // number wheel revolutions + float speedTimeDelta = (float)(spdcad->current.speedTime - spdcad->previous.speedTime) / 1024.0f; // time for above revolutions + float distance = (speedRotationDelta * (float)spdcad->wheelCircumference) / 1000.0f; // calculated distance (meters) travelled as per above + float speed = (distance / (speedTimeDelta / 3600.0f)) / 1000.0f; // its why we're here + spdcad->current.speed = speed * 100; + spdcad->current.distance += distance; + + sendMessage(ANTP_MSG_PROFILE_DATA, (intptr_t*)spdcad, PROFILE_SPDCAD); + + spdcad->previous.cadenceTime = spdcad->current.cadenceTime; + spdcad->previous.cadence = spdcad->current.cadence; + spdcad->previous.cadenceCt = spdcad->current.cadenceCt; + + spdcad->previous.speedTime = spdcad->current.speedTime; + spdcad->previous.speedCt = spdcad->current.speedCt; + spdcad->previous.speed = spdcad->current.speed; + spdcad->previous.distance = spdcad->current.distance; + + //printf("payload_SPDCAD: speed: %.2f, cadence: %i, total distance: %.2f", spdcad->current.speed/100.0f, spdcad->current.cadence, spdcad->current.distance/1000.0f); + } +} + +void AntPlus::payload_POWER(TDCONFIG *cfg, const uint8_t *data, const size_t dataLength) +{ + //printf("payload_POWER: len:%i", dataLength); + payload_POWER_t *pwr = &payload.power; +/* + uint8_t eventCount = data[2]; + uint8_t pedalPowerContribution = ((data[3] != 0xFF) && (data[3]&0x80)) ; // left/right is defined if NOT 0xFF (= no Pedal Power) AND BIT 7 is set + uint8_t pedalPower = (data[3]&0x7F); // right pedalPower % - stored in bit 0-6 + uint8_t instantCadence = data[4]; + uint16_t sumPower = data[5] + (data[6]<<8); + uint16_t instantPower = data[7] + (data[8]<<8); + */ + sendMessage(ANTP_MSG_PROFILE_DATA, (intptr_t*)pwr, PROFILE_POWER); +} + +void AntPlus::payload_STRIDE(TDCONFIG *cfg, const uint8_t *data, const size_t dataLength) +{ + //printf("payload_STRIDE: len:%i", dataLength); + payload_STRIDE_t *stride = &payload.stride; + + + int page = data[1]; + if (page == 0){ + stride->current.strides = data[7]; + sendMessage(ANTP_MSG_PROFILE_DATA, (intptr_t*)stride, PROFILE_STRIDE); + stride->previous.strides = stride->current.strides; + + }else if (page == 1){ + stride->current.speed = ((float)(data[4]&0x0f) + (float)(data[5]/256.0f)); + stride->current.cadence = ((float)data[3] + (float)((data[4] << 4) / 16.0f)); + + sendMessage(ANTP_MSG_PROFILE_DATA, (intptr_t*)stride, PROFILE_STRIDE); + + stride->previous.speed = stride->current.speed; + stride->previous.cadence = stride->current.cadence; + } + +} + +void AntPlus::payload_SPEED(TDCONFIG *cfg, const uint8_t *data, const size_t dataLength) +{ + //printf("payload_SPEED: len:%i", dataLength); + payload_SPEED_t *spd = &payload.spd; + + spd->current.speedTime = data[5]; + spd->current.speedTime |= (data[6] << 8); + spd->current.speedCt = data[7]; + spd->current.speedCt |= (data[8] << 8); + + spd->spdChange = (spd->current.speedTime != spd->previous.speedTime || spd->current.speedCt != spd->previous.speedCt); + + if (spd->spdChange){ + uint32_t speedRotationDelta = spd->current.speedCt - spd->previous.speedCt; // number wheel revolutions + float speedTimeDelta = (float)(spd->current.speedTime - spd->previous.speedTime) / 1024.0f; // time for above revolutions + if (!spd->wheelCircumference) spd->wheelCircumference = WHEEL_CIRCUMFERENCE; + float distance = (speedRotationDelta * (float)spd->wheelCircumference) / 1000.0f; // calculated distance (meters) travelled as per above + float speed = (distance / (speedTimeDelta / 3600.0f)) / 1000.0f; // its why we're here + spd->current.speed = speed * 100; + spd->current.distance += distance; + + sendMessage(ANTP_MSG_PROFILE_DATA, (intptr_t*)spd, PROFILE_SPEED); + + spd->previous.speedTime = spd->current.speedTime; + spd->previous.speedCt = spd->current.speedCt; + spd->previous.speed = spd->current.speed; + spd->previous.distance = spd->current.distance; + } +} + +void AntPlus::payload_CADENCE(TDCONFIG *cfg, const uint8_t *data, const size_t dataLength) +{ + //printf("payload_CADENCE: len:%i", dataLength); + payload_CADENCE_t *cad = &payload.cad; + + cad->current.cadenceTime = data[5]; + cad->current.cadenceTime |= (data[6] << 8); + cad->current.cadenceCt = data[7]; + cad->current.cadenceCt |= (data[8] << 8); + + cad->cadChange = (cad->current.cadenceTime != cad->previous.cadenceTime || cad->current.cadenceCt != cad->previous.cadenceCt); + + if (cad->cadChange){ + uint16_t cadence = (60 * (cad->current.cadenceCt - cad->previous.cadenceCt) * 1024) / (cad->current.cadenceTime - cad->previous.cadenceTime); + cad->current.cadence = cadence; + + sendMessage(ANTP_MSG_PROFILE_DATA, (intptr_t*)cad, PROFILE_CADENCE); + + cad->previous.cadenceTime = cad->current.cadenceTime; + cad->previous.cadence = cad->current.cadence; + cad->previous.cadenceCt = cad->current.cadenceCt; + } +} + + + + diff --git a/antplusdefs.h b/antplusdefs.h new file mode 100644 index 0000000..e04a5aa --- /dev/null +++ b/antplusdefs.h @@ -0,0 +1,482 @@ +#ifndef _LIBANT_H_ +#define _LIBANT_H_ + +#define ANTP_MSG_PROFILE_SELECT 1000 +#define ANTP_MSG_PROFILE_DATA 1010 +#define ANTP_MSG_CHANNELSTATUS 1011 +#define ANTP_MSG_DEVICEID 1012 + + +#define ANT_TRANSMISSION_SLAVE 0x00 +#define ANT_TRANSMISSION_MASTER 0x05 + +#define ANT_PERIOD_HRM 8070 +#define ANT_PERIOD_SPDCAD 8086 +#define ANT_PERIOD_POWER 8182 +#define ANT_PERIOD_STRIDE 8134 // footpod +#define ANT_PERIOD_SPEED 8118 +#define ANT_PERIOD_CADENCE 8102 + +#define ANT_PERIOD_CONTROL 8192 +#define ANT_PERIOD_KICKR 2048 +#define ANT_PERIOD_MOXY 8192 +#define ANT_PERIOD_TACX_VORTEX 8192 +#define ANT_PERIOD_ITNESS_EQUIPMENT 8192 +#define ANT_PERIOD_QUARQ (8182*4) +#define ANT_PERIOD_QUARQ_FAST (8182/16) + + +#define ANT_FREQUENCY_SPORT 57 +#define ANT_FREQUENCY_STRIDE 57 +#define ANT_FREQUENCY_FAST_QUARQ 61 +#define ANT_FREQUENCY_QUARQ 61 +#define ANT_FREQUENCY_KICKR 52 +#define ANT_FREQUENCY_MOXY 57 +#define ANT_FREQUENCY_TACX_VORTEX 66 +#define ANT_FREQUENCY_FITNESS_EQUIPMENT 57 + + +#define ANT_DEVICE_ANTFS 1 +#define ANT_DEVICE_POWER 11 +//#define ANT_DEVICE_QUARQ_FAST_OLD 11 +#define ANT_DEVICE_ENVIRONMENT_LEGACY 12 +#define ANT_DEVICE_MULTISPORT_SPDST 15 +#define ANT_DEVICE_CONTROL 16 +#define ANT_DEVICE_FITNESS_EQUIPMENT 17 +#define ANT_DEVICE_BLOOD_PRESSURE 18 +#define ANT_DEVICE_GEOCACHE_NODE 19 +#define ANT_DEVICE_LIGHT_VEHICLE 20 +#define ANT_DEVICE_ENV_SENSOR 25 +#define ANT_DEVICE_MOXY 31 +#define ANT_DEVICE_TACX_VORTEX 61 +#define ANT_DEVICE_QUARQ_FAST 96 +#define ANT_DEVICE_QUARQ 96 +#define ANT_DEVICE_WEIGHT_SCALE 119 +#define ANT_DEVICE_HRM 120 +#define ANT_DEVICE_SPDCAD 121 +#define ANT_DEVICE_CADENCE 122 +#define ANT_DEVICE_SPEED 123 +#define ANT_DEVICE_STRIDE 124 // footpod + + +// ANT_DEVICE_POWER broadcast types +#define ANT_POWER_STANDARD 0x10 +#define ANT_POWER_WHEELTORQUE 0x11 +#define ANT_POWER_CRANKTORQUE 0x12 +#define ANT_POWER_TE_AND_PS 0x13 +#define ANT_POWER_CRANKSRM 0x20 + + +#define ANT_CHANNEL_STATUS_UNASSIGNED 0 +#define ANT_CHANNEL_STATUS_ASSIGNED 1 +#define ANT_CHANNEL_STATUS_SEARCHING 2 +#define ANT_CHANNEL_STATUS_TRACKING 3 +#define ANT_CHANNEL_STATUS_UNKNOWN 4 /*shouldn't see this */ +#define ANT_CHANNEL_STATUS_MASK 0x03 + + +#define ANT_CHANNEL_TYPE_SLAVE ((uint8_t)0x00) //Slave channel (PARAMETER_RX_NOT_TX). +#define ANT_CHANNEL_TYPE_MASTER ((uint8_t)0x10) //Master channel (PARAMETER_TX_NOT_RX). +#define ANT_CHANNEL_TYPE_SLAVE_RX_ONLY ((uint8_t)0x40) //Slave rx only channel (PARAMETER_RX_NOT_TX | PARAMETER_RX_ONLY). +#define ANT_CHANNEL_TYPE_MASTER_TX_ONLY ((uint8_t)0x50) //Master tx only channel (PARAMETER_TX_NOT_RX | PARAMETER_NO_TX_GUARD_BAND). +#define ANT_CHANNEL_TYPE_SHARED_SLAVE ((uint8_t)0x20) //Shared slave channel (PARAMETER_RX_NOT_TX | PARAMETER_SHARED_CHANNEL). +#define ANT_CHANNEL_TYPE_SHARED_MASTER ((uint8_t)0x30) //Shared master channel (PARAMETER_TX_NOT_RX | PARAMETER_SHARED_CHANNEL). + + +#define ANT_STARTUP_RESET_POWERON 0x00 +#define ANT_STARTUP_RESET_HARDWARE 0x01 +#define ANT_STARTUP_RESET_WATCHDOG 0x02 +#define ANT_STARTUP_RESET_COMMAND 0x20 +#define ANT_STARTUP_RESET_SYNC 0x40 +#define ANT_STARTUP_RESET_SUSPEND 0x80 + + +#define STREAM_SYNC 0 +#define STREAM_LENGTH 1 +#define STREAM_MESSAGE 2 +#define STREAM_DATA 3 +#define STREAM_CHANNEL STREAM_DATA + +#define STREAM_CHANNEL_ID 0 +#define STREAM_CHANNEL_STATUS 1 + +#define STREAM_EVENT_CHANNEL_ID 0 +#define STREAM_EVENT_RESPONSE_ID 1 + +#define STREAM_CAP_MAXCHANNELS 0 +#define STREAM_CAP_MAXNETWORKS 1 +#define STREAM_CAP_STDOPTIONS 2 +#define STREAM_CAP_ADVANCED 3 +#define STREAM_CAP_ADVANCED2 4 + +#define STREAM_STARTUP_REASON 0 + +#define STREAM_CHANNELSTATUS_STATUS 1 + +#define STREAM_VERSION_STRING 0 + +#define STREAM_CHANNELID_DEVNO_LO 1 +#define STREAM_CHANNELID_DEVNO_HI 2 +#define STREAM_CHANNELID_DEVTYPE 3 +#define STREAM_CHANNELID_TRANTYPE 4 + +#define STREAM_EVENT_EVENTID 2 + +#define STREAM_RXBROADCAST_DEV120_BEATLO 5 +#define STREAM_RXBROADCAST_DEV120_BEATHI 6 +#define STREAM_RXBROADCAST_DEV120_SEQ 7 +#define STREAM_RXBROADCAST_DEV120_HR 8 + + +#define RESET_FLAGS_MASK ((uint8_t)0xE0) +#define RESET_SUSPEND ((uint8_t)0x80) // this must follow bitfield def +#define RESET_SYNC ((uint8_t)0x40) // this must follow bitfield def +#define RESET_CMD ((uint8_t)0x20) // this must follow bitfield def +#define RESET_WDT ((uint8_t)0x02) +#define RESET_RST ((uint8_t)0x01) +#define RESET_POR ((uint8_t)0x00) + + + +////////////////////////////////////////////// +// Assign Channel Parameters +////////////////////////////////////////////// +#define PARAMETER_RX_NOT_TX ((uint8_t)0x00) +#define PARAMETER_TX_NOT_RX ((uint8_t)0x10) +#define PARAMETER_SHARED_CHANNEL ((uint8_t)0x20) +#define PARAMETER_NO_TX_GUARD_BAND ((uint8_t)0x40) +#define PARAMETER_ALWAYS_RX_WILD_CARD_SEARCH_ID ((uint8_t)0x40) //Pre-AP2 +#define PARAMETER_RX_ONLY ((uint8_t)0x40) + +////////////////////////////////////////////// +// Ext. Assign Channel Parameters +////////////////////////////////////////////// +#define EXT_PARAM_ALWAYS_SEARCH ((uint8_t)0x01) +#define EXT_PARAM_FREQUENCY_AGILITY ((uint8_t)0x04) + + +///////////////////////////////////////////////////////////////////////////// +// 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 +///////////////////////////////////////////////////////////////////////////// +#define MESG_TX_SYNC ((uint8_t)0xA4) +#define MESG_RX_SYNC ((uint8_t)0xA5) +#define MESG_SYNC_SIZE ((uint8_t)1) +#define MESG_SIZE_SIZE ((uint8_t)1) +#define MESG_ID_SIZE ((uint8_t)1) +#define MESG_CHANNEL_NUM_SIZE ((uint8_t)1) +#define MESG_EXT_MESG_BF_SIZE ((uint8_t)1) // NOTE: this could increase in the future +#define MESG_CHECKSUM_SIZE ((uint8_t)1) +#define MESG_DATA_SIZE ((uint8_t)9) + +// The largest serial message is an ANT data message with all of the extended fields +#define MESG_ANT_MAX_PAYLOAD_SIZE ANT_STANDARD_DATA_PAYLOAD_SIZE + +#define MESG_MAX_EXT_DATA_SIZE (ANT_EXT_MESG_DEVICE_ID_FIELD_SIZE + 4 + 2) // ANT device ID (4 bytes) + (4 bytes) + (2 bytes) + +#define MESG_MAX_DATA_SIZE (MESG_ANT_MAX_PAYLOAD_SIZE + MESG_EXT_MESG_BF_SIZE + MESG_MAX_EXT_DATA_SIZE) // ANT data payload (8 bytes) + extended bitfield (1 byte) + extended data (10 bytes) +#define MESG_MAX_SIZE_VALUE (MESG_MAX_DATA_SIZE + MESG_CHANNEL_NUM_SIZE) // this is the maximum value that the serial message size value is allowed to be +#define MESG_BUFFER_SIZE (MESG_SIZE_SIZE + MESG_ID_SIZE + MESG_CHANNEL_NUM_SIZE + MESG_MAX_DATA_SIZE + MESG_CHECKSUM_SIZE) +#define MESG_FRAMED_SIZE (MESG_ID_SIZE + MESG_CHANNEL_NUM_SIZE + MESG_MAX_DATA_SIZE) +#define MESG_HEADER_SIZE (MESG_SYNC_SIZE + MESG_SIZE_SIZE + MESG_ID_SIZE) +#define MESG_FRAME_SIZE (MESG_HEADER_SIZE + MESG_CHECKSUM_SIZE) +#define MESG_MAX_SIZE (MESG_MAX_DATA_SIZE + MESG_FRAME_SIZE) + +#define MESG_SIZE_OFFSET (MESG_SYNC_SIZE) +#define MESG_ID_OFFSET (MESG_SYNC_SIZE + MESG_SIZE_SIZE) +#define MESG_DATA_OFFSET (MESG_HEADER_SIZE) +#define MESG_RECOMMENDED_BUFFER_SIZE ((uint8_t)ANTPLUS_MAXPACKETSIZE) + + +////////////////////////////////////////////// +// Message ID's +////////////////////////////////////////////// +#define MESG_INVALID_ID ((uint8_t)0x00) +#define MESG_EVENT_ID ((uint8_t)0x01) + +#define MESG_VERSION_ID ((uint8_t)0x3E) +#define MESG_RESPONSE_EVENT_ID ((uint8_t)0x40) + +#define MESG_UNASSIGN_CHANNEL_ID ((uint8_t)0x41) +#define MESG_ASSIGN_CHANNEL_ID ((uint8_t)0x42) +#define MESG_CHANNEL_MESG_PERIOD_ID ((uint8_t)0x43) +#define MESG_CHANNEL_SEARCH_TIMEOUT_ID ((uint8_t)0x44) +#define MESG_CHANNEL_RADIO_FREQ_ID ((uint8_t)0x45) +#define MESG_NETWORK_KEY_ID ((uint8_t)0x46) +#define MESG_RADIO_TX_POWER_ID ((uint8_t)0x47) +#define MESG_RADIO_CW_MODE_ID ((uint8_t)0x48) +#define MESG_SEARCH_WAVEFORM_ID ((uint8_t)0x49) + +#define MESG_SYSTEM_RESET_ID ((uint8_t)0x4A) +#define MESG_OPEN_CHANNEL_ID ((uint8_t)0x4B) +#define MESG_CLOSE_CHANNEL_ID ((uint8_t)0x4C) +#define MESG_REQUEST_ID ((uint8_t)0x4D) + +#define MESG_BROADCAST_DATA_ID ((uint8_t)0x4E) +#define MESG_ACKNOWLEDGED_DATA_ID ((uint8_t)0x4F) +#define MESG_BURST_DATA_ID ((uint8_t)0x50) + +#define MESG_CHANNEL_ID_ID ((uint8_t)0x51) +#define MESG_CHANNEL_STATUS_ID ((uint8_t)0x52) +#define MESG_RADIO_CW_INIT_ID ((uint8_t)0x53) +#define MESG_CAPABILITIES_ID ((uint8_t)0x54) + +#define MESG_STACKLIMIT_ID ((uint8_t)0x55) + +#define MESG_SCRIPT_DATA_ID ((uint8_t)0x56) +#define MESG_SCRIPT_CMD_ID ((uint8_t)0x57) + +#define MESG_ID_LIST_ADD_ID ((uint8_t)0x59) +#define MESG_ID_LIST_CONFIG_ID ((uint8_t)0x5A) +#define MESG_OPEN_RX_SCAN_ID ((uint8_t)0x5B) + +#define MESG_EXT_CHANNEL_RADIO_FREQ_ID ((uint8_t)0x5C) // OBSOLETE: (for 905 radio) +#define MESG_EXT_BROADCAST_DATA_ID ((uint8_t)0x5D) +#define MESG_EXT_ACKNOWLEDGED_DATA_ID ((uint8_t)0x5E) +#define MESG_EXT_BURST_DATA_ID ((uint8_t)0x5F) + +#define MESG_CHANNEL_RADIO_TX_POWER_ID ((uint8_t)0x60) +#define MESG_GET_SERIAL_NUM_ID ((uint8_t)0x61) +#define MESG_GET_TEMP_CAL_ID ((uint8_t)0x62) +#define MESG_SET_LP_SEARCH_TIMEOUT_ID ((uint8_t)0x63) +#define MESG_SET_TX_SEARCH_ON_NEXT_ID ((uint8_t)0x64) +#define MESG_SERIAL_NUM_SET_CHANNEL_ID_ID ((uint8_t)0x65) +#define MESG_RX_EXT_MESGS_ENABLE_ID ((uint8_t)0x66) +#define MESG_RADIO_CONFIG_ALWAYS_ID ((uint8_t)0x67) +#define MESG_ENABLE_LED_FLASH_ID ((uint8_t)0x68) +#define MESG_XTAL_ENABLE_ID ((uint8_t)0x6D) +#define MESG_STARTUP_MESG_ID ((uint8_t)0x6F) +#define MESG_AUTO_FREQ_CONFIG_ID ((uint8_t)0x70) +#define MESG_PROX_SEARCH_CONFIG_ID ((uint8_t)0x71) + +#define MESG_CUBE_CMD_ID ((uint8_t)0x80) + +#define MESG_GET_PIN_DIODE_CONTROL_ID ((uint8_t)0x8D) +#define MESG_PIN_DIODE_CONTROL_ID ((uint8_t)0x8E) +#define MESG_FIT1_SET_AGC_ID ((uint8_t)0x8F) + +#define MESG_FIT1_SET_EQUIP_STATE_ID ((uint8_t)0x91) // *** CONFLICT: w/ Sensrcore, Fit1 will never have sensrcore enabled + +// Sensrcore Messages +#define MESG_SET_CHANNEL_INPUT_MASK_ID ((uint8_t)0x90) +#define MESG_SET_CHANNEL_DATA_TYPE_ID ((uint8_t)0x91) +#define MESG_READ_PINS_FOR_SECT_ID ((uint8_t)0x92) +#define MESG_TIMER_SELECT_ID ((uint8_t)0x93) +#define MESG_ATOD_SETTINGS_ID ((uint8_t)0x94) +#define MESG_SET_SHARED_ADDRESS_ID ((uint8_t)0x95) +#define MESG_ATOD_EXTERNAL_ENABLE_ID ((uint8_t)0x96) +#define MESG_ATOD_PIN_SETUP_ID ((uint8_t)0x97) +#define MESG_SETUP_ALARM_ID ((uint8_t)0x98) +#define MESG_ALARM_VARIABLE_MODIFY_TEST_ID ((uint8_t)0x99) +#define MESG_PARTIAL_RESET_ID ((uint8_t)0x9A) +#define MESG_OVERWRITE_TEMP_CAL_ID ((uint8_t)0x9B) +#define MESG_SERIAL_PASSTHRU_SETTINGS_ID ((uint8_t)0x9C) + +#define MESG_READ_SEGA_ID ((uint8_t)0xA0) +#define MESG_SEGA_CMD_ID ((uint8_t)0xA1) +#define MESG_SEGA_DATA_ID ((uint8_t)0xA2) +#define MESG_SEGA_ERASE_ID ((uint8_t)0xA3) +#define MESG_SEGA_WRITE_ID ((uint8_t)0xA4) +#define AVOID_USING_SYNC_BYTES_FOR_MESG_IDS ((uint8_t)0xA5) + +#define MESG_SEGA_LOCK_ID ((uint8_t)0xA6) +#define MESG_FLASH_PROTECTION_CHECK_ID ((uint8_t)0xA7) +#define MESG_UARTREG_ID ((uint8_t)0xA8) +#define MESG_MAN_TEMP_ID ((uint8_t)0xA9) +#define MESG_BIST_ID ((uint8_t)0xAA) +#define MESG_SELFERASE_ID ((uint8_t)0xAB) +#define MESG_SET_MFG_BITS_ID ((uint8_t)0xAC) +#define MESG_UNLOCK_INTERFACE_ID ((uint8_t)0xAD) +#define MESG_SERIAL_ERROR_ID ((uint8_t)0xAE) +#define MESG_SET_ID_STRING_ID ((uint8_t)0xAF) + +#define MESG_IO_STATE_ID ((uint8_t)0xB0) +#define MESG_CFG_STATE_ID ((uint8_t)0xB1) +#define MESG_BLOWFUSE_ID ((uint8_t)0xB2) +#define MESG_MASTERIOCTRL_ID ((uint8_t)0xB3) +#define MESG_PORT_GET_IO_STATE_ID ((uint8_t)0xB4) +#define MESG_PORT_SET_IO_STATE_ID ((uint8_t)0xB5) + +#define MESG_SLEEP_ID ((uint8_t)0xC5) +#define MESG_GET_GRMN_ESN_ID ((uint8_t)0xC6) + +#define MESG_DEBUG_ID ((uint8_t)0xF0) // use 2 byte sub-index identifier + +////////////////////////////////////////////// +// Message Sizes +////////////////////////////////////////////// +#define MESG_INVALID_SIZE ((uint8_t)0) + +#define MESG_VERSION_SIZE ((uint8_t)13) +#define MESG_RESPONSE_EVENT_SIZE ((uint8_t)3) +#define MESG_CHANNEL_STATUS_SIZE ((uint8_t)2) + +#define MESG_UNASSIGN_CHANNEL_SIZE ((uint8_t)1) +#define MESG_ASSIGN_CHANNEL_SIZE ((uint8_t)3) +#define MESG_CHANNEL_ID_SIZE ((uint8_t)5) +#define MESG_CHANNEL_MESG_PERIOD_SIZE ((uint8_t)3) +#define MESG_CHANNEL_SEARCH_TIMEOUT_SIZE ((uint8_t)2) +#define MESG_CHANNEL_RADIO_FREQ_SIZE ((uint8_t)2) +#define MESG_CHANNEL_RADIO_TX_POWER_SIZE ((uint8_t)2) +#define MESG_NETWORK_KEY_SIZE ((uint8_t)9) +#define MESG_RADIO_TX_POWER_SIZE ((uint8_t)2) +#define MESG_RADIO_CW_MODE_SIZE ((uint8_t)3) +#define MESG_RADIO_CW_INIT_SIZE ((uint8_t)1) +#define MESG_SYSTEM_RESET_SIZE ((uint8_t)1) +#define MESG_OPEN_CHANNEL_SIZE ((uint8_t)1) +#define MESG_CLOSE_CHANNEL_SIZE ((uint8_t)1) +#define MESG_REQUEST_SIZE ((uint8_t)2) + +#define MESG_CAPABILITIES_SIZE ((uint8_t)6) +#define MESG_STACKLIMIT_SIZE ((uint8_t)2) + +#define MESG_SCRIPT_DATA_SIZE ((uint8_t)10) +#define MESG_SCRIPT_CMD_SIZE ((uint8_t)3) + +#define MESG_ID_LIST_ADD_SIZE ((uint8_t)6) +#define MESG_ID_LIST_CONFIG_SIZE ((uint8_t)3) +#define MESG_OPEN_RX_SCAN_SIZE ((uint8_t)1) +#define MESG_EXT_CHANNEL_RADIO_FREQ_SIZE ((uint8_t)3) + +#define MESG_RADIO_CONFIG_ALWAYS_SIZE ((uint8_t)2) +#define MESG_RX_EXT_MESGS_ENABLE_SIZE ((uint8_t)2) +#define MESG_SET_TX_SEARCH_ON_NEXT_SIZE ((uint8_t)2) +#define MESG_SET_LP_SEARCH_TIMEOUT_SIZE ((uint8_t)2) + +#define MESG_SERIAL_NUM_SET_CHANNEL_ID_SIZE ((uint8_t)3) +#define MESG_ENABLE_LED_FLASH_SIZE ((uint8_t)2) +#define MESG_GET_SERIAL_NUM_SIZE ((uint8_t)4) +#define MESG_GET_TEMP_CAL_SIZE ((uint8_t)4) +#define MESG_CLOCK_DRIFT_DATA_SIZE ((uint8_t)9) + +#define MESG_AGC_CONFIG_SIZE ((uint8_t)2) +#define MESG_RUN_SCRIPT_SIZE ((uint8_t)2) +#define MESG_ANTLIB_CONFIG_SIZE ((uint8_t)2) +#define MESG_XTAL_ENABLE_SIZE ((uint8_t)1) +#define MESG_STARTUP_MESG_SIZE ((uint8_t)1) +#define MESG_AUTO_FREQ_CONFIG_SIZE ((uint8_t)4) +#define MESG_PROX_SEARCH_CONFIG_SIZE ((uint8_t)2) + +#define MESG_GET_PIN_DIODE_CONTROL_SIZE ((uint8_t)1) +#define MESG_PIN_DIODE_CONTROL_ID_SIZE ((uint8_t)2) +#define MESG_FIT1_SET_EQUIP_STATE_SIZE ((uint8_t)2) +#define MESG_FIT1_SET_AGC_SIZE ((uint8_t)3) + +#define MESG_READ_SEGA_SIZE ((uint8_t)2) +#define MESG_SEGA_CMD_SIZE ((uint8_t)3) +#define MESG_SEGA_DATA_SIZE ((uint8_t)10) +#define MESG_SEGA_ERASE_SIZE ((uint8_t)0) +#define MESG_SEGA_WRITE_SIZE ((uint8_t)3) +#define MESG_SEGA_LOCK_SIZE ((uint8_t)1) +#define MESG_FLASH_PROTECTION_CHECK_SIZE ((uint8_t)1) +#define MESG_UARTREG_SIZE ((uint8_t)2) +#define MESG_MAN_TEMP_SIZE ((uint8_t)2) +#define MESG_BIST_SIZE ((uint8_t)6) +#define MESG_SELFERASE_SIZE ((uint8_t)2) +#define MESG_SET_MFG_BITS_SIZE ((uint8_t)2) +#define MESG_UNLOCK_INTERFACE_SIZE ((uint8_t)1) +#define MESG_SET_SHARED_ADDRESS_SIZE ((uint8_t)3) + +#define MESG_GET_GRMN_ESN_SIZE ((uint8_t)5) + +#define MESG_IO_STATE_SIZE ((uint8_t)2) +#define MESG_CFG_STATE_SIZE ((uint8_t)2) +#define MESG_BLOWFUSE_SIZE ((uint8_t)1) +#define MESG_MASTERIOCTRL_SIZE ((uint8_t)1) +#define MESG_PORT_SET_IO_STATE_SIZE ((uint8_t)5) + +#define MESG_SLEEP_SIZE ((uint8_t)1) +#define MESG_EXT_DATA_SIZE ((uint8_t)13) + +////////////////////////////////////////////// +// PC Application Event Codes +////////////////////////////////////////////// + +#define EVENT_RX_BROADCAST ((uint8_t)0x9A) // returned when module receives broadcast data +#define EVENT_RX_ACKNOWLEDGED ((uint8_t)0x9B) // returned when module receives acknowledged data +#define EVENT_RX_BURST_PACKET ((uint8_t)0x9C) // returned when module receives burst data + +#define EVENT_RX_EXT_BROADCAST ((uint8_t)0x9D) // returned when module receives broadcast data +#define EVENT_RX_EXT_ACKNOWLEDGED ((uint8_t)0x9E) // returned when module receives acknowledged data +#define EVENT_RX_EXT_BURST_PACKET ((uint8_t)0x9F) // returned when module receives burst data + + +#define EVENT_RX_FLAG_BROADCAST ((uint8_t)0xA3) // returned when module receives broadcast data with flag attached +#define EVENT_RX_FLAG_ACKNOWLEDGED ((uint8_t)0xA4) // returned when module receives acknowledged data with flag attached +#define EVENT_RX_FLAG_BURST_PACKET ((uint8_t)0xA5) // returned when module receives burst data with flag attached + + +////////////////////////////////////////////// +// Response / Event Codes +////////////////////////////////////////////// +#define RESPONSE_NO_ERROR ((uint8_t)0x00) +#define NO_EVENT ((uint8_t)0x00) + +#define EVENT_RX_SEARCH_TIMEOUT ((uint8_t)0x01) +#define EVENT_RX_FAIL ((uint8_t)0x02) +#define EVENT_TX ((uint8_t)0x03) +#define EVENT_TRANSFER_RX_FAILED ((uint8_t)0x04) +#define EVENT_TRANSFER_TX_COMPLETED ((uint8_t)0x05) +#define EVENT_TRANSFER_TX_FAILED ((uint8_t)0x06) +#define EVENT_CHANNEL_CLOSED ((uint8_t)0x07) +#define EVENT_RX_FAIL_GO_TO_SEARCH ((uint8_t)0x08) +#define EVENT_CHANNEL_COLLISION ((uint8_t)0x09) +#define EVENT_TRANSFER_TX_START ((uint8_t)0x0A) // a pending transmit transfer has begun + +#define EVENT_CHANNEL_ACTIVE ((uint8_t)0x0F) + +#define EVENT_TRANSFER_TX_NEXT_MESSAGE ((uint8_t)0x11) // only enabled in FIT1 + +#define CHANNEL_IN_WRONG_STATE ((uint8_t)0x15) // returned on attempt to perform an action from the wrong channel state +#define CHANNEL_NOT_OPENED ((uint8_t)0x16) // returned on attempt to communicate on a channel that is not open +#define CHANNEL_ID_NOT_SET ((uint8_t)0x18) // returned on attempt to open a channel without setting the channel ID +#define CLOSE_ALL_CHANNELS ((uint8_t)0x19) // returned when attempting to start scanning mode, when channels are still open + +#define TRANSFER_IN_PROGRESS ((uint8_t)0x1F) // returned on attempt to communicate on a channel with a TX transfer in progress +#define TRANSFER_SEQUENCE_NUMBER_ERROR ((uint8_t)0x20) // returned when sequence number is out of order on a Burst transfer +#define TRANSFER_IN_ERROR ((uint8_t)0x21) +#define TRANSFER_BUSY ((uint8_t)0x22) + +#define MESSAGE_SIZE_EXCEEDS_LIMIT ((uint8_t)0x27) // returned if a data message is provided that is too large +#define INVALID_MESSAGE ((uint8_t)0x28) // returned when the message has an invalid parameter +#define INVALID_NETWORK_NUMBER ((uint8_t)0x29) // returned when an invalid network number is provided +#define INVALID_LIST_ID ((uint8_t)0x30) // returned when the provided list ID or size exceeds the limit +#define INVALID_SCAN_TX_CHANNEL ((uint8_t)0x31) // returned when attempting to transmit on channel 0 when in scan mode +#define INVALID_PARAMETER_PROVIDED ((uint8_t)0x33) // returned when an invalid parameter is specified in a configuration message + +#define EVENT_QUE_OVERFLOW ((uint8_t)0x35) // ANT event que has overflowed and lost 1 or more events + +#define EVENT_CLK_ERROR ((uint8_t)0x36) //!! debug XOSC16M + +#define SCRIPT_FULL_ERROR ((uint8_t)0x40) // error writing to script, memory is full +#define SCRIPT_WRITE_ERROR ((uint8_t)0x41) // error writing to script, bytes not written correctly +#define SCRIPT_INVALID_PAGE_ERROR ((uint8_t)0x42) // error accessing script page +#define SCRIPT_LOCKED_ERROR ((uint8_t)0x43) // the scripts are locked and can't be dumped + +#define NO_RESPONSE_MESSAGE ((uint8_t)0x50) // returned to the Command_SerialMessageProcess function, so no reply message is generated +#define RETURN_TO_MFG ((uint8_t)0x51) // default return to any mesg when the module determines that the mfg procedure has not been fully completed + +#define FIT_ACTIVE_SEARCH_TIMEOUT ((uint8_t)0x60) // Fit1 only event added for timeout of the pairing state after the Fit module becomes active +#define FIT_WATCH_PAIR ((uint8_t)0x61) // Fit1 only +#define FIT_WATCH_UNPAIR ((uint8_t)0x62) // Fit1 only + +// Internal only events below this point +#define INTERNAL_ONLY_EVENTS ((uint8_t)0x80) +#define EVENT_RX ((uint8_t)0x80) // INTERNAL: Event for a receive message +#define EVENT_NEW_CHANNEL ((uint8_t)0x81) // INTERNAL: EVENT for a new active channel +#define EVENT_PASS_THRU ((uint8_t)0x82) // INTERNAL: Event to allow an upper stack events to pass through lower stacks +#define EVENT_TRANSFER_RX_COMPLETED ((uint8_t)0x83) // INTERNAL: Event for RX completed that indicates ANT library is ready for new messasges + +#define EVENT_BLOCKED ((uint8_t)0xFF) // INTERNAL: Event to replace any event we do not wish to go out, will also zero the size of the Tx message + + +#endif +