Hexiwear library for communicating with the on-board KW40Z BLE device. KW40Z handles also the touch buttons.
Fork of Hexi_KW40Z by
Diff: Hexi_KW40Z.cpp
- Revision:
- 0:c2d52562f36b
- Child:
- 1:f6f9b24aea57
diff -r 000000000000 -r c2d52562f36b Hexi_KW40Z.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Hexi_KW40Z.cpp Mon Sep 19 02:46:28 2016 +0000 @@ -0,0 +1,395 @@ +/** BLE KW40Z Driver for Hexiwear + * This file contains BLE and Touch Buttons driver functionality for Hexiwear + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither the name of NXP, nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * visit: http://www.mikroe.com and http://www.nxp.com + * + * get support at: http://www.mikroe.com/forum and https://community.nxp.com + * + * Project HEXIWEAR, 2015 + */ + +#include "Hexi_KW40Z.h" + +#if defined (LIB_DEBUG) +RawSerial pc(USBTX, USBRX); // tx, rx +#endif + +KW40Z::KW40Z(PinName txPin,PinName rxPin) : device(txPin, rxPin), mainThread(&KW40Z::mainStarter, this, osPriorityNormal,1024), rxThread(&KW40Z::rxStarter, this, osPriorityNormal,1024) +{ +#if defined (LIB_DEBUG) + pc.baud(115200); + pc.printf("Initializing\r\n"); +#endif + + device.baud(230400); + device.format(8, Serial::None, 2); + + rxBuff = (uint8_t*)&hostInterface_rxPacket; + kw40z_Cbs = NULL; + //memset(&hexiwear_kw40version,0,sizeof(hexiwear_version_t)); + + /* intialization finalized, signal to start the threads */ + mainThread.signal_set(START_THREAD); + rxThread.signal_set(START_THREAD); +} + +KW40Z::~KW40Z(void) +{ +} + +void KW40Z::attach(kw40z_callbacks_t * callbacks) +{ + kw40z_Cbs = callbacks; +} + +void KW40Z::rxStarter(void const *p) { + KW40Z *instance = (KW40Z*)p; + instance->rxTask(); +} + +void KW40Z::mainStarter(void const *p) { + KW40Z *instance = (KW40Z*)p; + instance->mainTask(); +} + +void KW40Z::GetVersion() +{ + hostInterface_packet_t txPacket = {0}; + + txPacket.start1 = gHostInterface_startByte1, + txPacket.start2 = gHostInterface_startByte2, + txPacket.type = packetType_buildVersion, + txPacket.length = 3, + txPacket.data[0] = HEXIWEAR_VERSION_MAJOR; + txPacket.data[1] = HEXIWEAR_VERSION_MINOR; + txPacket.data[2] = HEXIWEAR_VERSION_PATCH; + txPacket.data[3] = gHostInterface_trailerByte; + + SendPacket(&txPacket, false); +} + +void KW40Z::SendPacket(hostInterface_packet_t * txPacket, bool confirmRequested) +{ + uint8_t retries = 0; + confirmReceived = false; + + do + { + char * txBuff = (char *)txPacket; + uint8_t length = txPacket->length + gHostInterface_headerSize + 1; + + if(confirmRequested == true) + { + txPacket->start2 |= 0x01; + } + + for(uint8_t i = 0; i < length; i++) + { + device.putc(*txBuff); + txBuff++; + } + +#if defined (LIB_DEBUG) + DebugPrintTxPacket(txPacket); +#endif + + retries++; + + #if defined (gHostInterface_RxConfirmationEnable) + if((confirmRequested == true) && (confirmReceived == false)) + { + Thread::wait(gHostInterface_retransmitTimeout); + } + #endif + } + while((confirmRequested == true) && + (confirmReceived == false) && + (retries < gHostInterface_retransmitCount)); +} + +void KW40Z::mainTask(void) +{ + mainThread.signal_wait(START_THREAD); + +#if defined (LIB_DEBUG) + pc.printf("MainTask Stared\r\n"); +#endif + + while(1) + { + osEvent evt = queue.get(); + if (evt.status == osEventMessage) + { + hostInterface_packet_t *rxPacket = (hostInterface_packet_t*)evt.value.p; + ProcessReceivedPacket(rxPacket); + mpool.free(rxPacket); + } + } +} + +void KW40Z::rxTask(void) +{ + rxThread.signal_wait(START_THREAD); + +#if defined (LIB_DEBUG) + pc.printf("RxTask Stared\r\n"); +#endif + + while(1) + { + if(device.readable()) + { + *rxBuff++ = device.getc(); + ProcessBuffer(); + + /* check for buffer overflow */ + if(rxBuff >= ((uint8_t*)&hostInterface_rxPacket + sizeof(hostInterface_rxPacket))) + { + rxBuff = (uint8_t*)&hostInterface_rxPacket; + } + } + } +} + +#if defined (LIB_DEBUG) +void KW40Z::DebugPrintTxPacket(hostInterface_packet_t * txPacket) +{ + char * txBuff = (char *)txPacket; + uint8_t length = txPacket->length + gHostInterface_headerSize + 1; + + pc.printf("Tx: "); + for(uint8_t i = 0; i < length; i++) + { + pc.printf("%02X ",*txBuff); + txBuff++; + } + pc.printf("\r\n"); +} + +void KW40Z::DebugPrintRxPacket() +{ + pc.printf("RX: "); + for(uint8_t * i = (uint8_t*)&hostInterface_rxPacket; i<rxBuff; i++) + { + pc.printf("%02X ",*i); + } + pc.printf("\r\n"); +} +#endif + +void KW40Z::ProcessBuffer() +{ + /* check if header has been received */ + if(rxBuff > ((uint8_t*)&hostInterface_rxPacket + gHostInterface_headerSize)) + { + /* check packet header */ + if((gHostInterface_startByte1 != hostInterface_rxPacket.start1)|| + (gHostInterface_startByte2 != (hostInterface_rxPacket.start2 & 0xFE))|| + (hostInterface_rxPacket.length > gHostInterface_dataSize)) + { +#if defined (LIB_DEBUG) + DebugPrintRxPacket(); + pc.printf("check header failed\r\n"); +#endif + + SearchStartByte(); + } + else + { + /* check data length */ + if(rxBuff > ((uint8_t*)&hostInterface_rxPacket + gHostInterface_headerSize + hostInterface_rxPacket.length)) + { + /* check trailer byte */ + if(gHostInterface_trailerByte != hostInterface_rxPacket.data[hostInterface_rxPacket.length]) + { +#if defined (LIB_DEBUG) + DebugPrintRxPacket(); + pc.printf("trailer byte failed\r\n"); +#endif + + SearchStartByte(); + } + else + { + + #if defined (gHostInterface_RxConfirmationEnable) + if(hostInterface_rxPacket.type == packetType_OK) + { + confirmReceived = true; + } + #endif + + /* send message to main task */ + hostInterface_packet_t *rxPacket = mpool.alloc(); + memcpy(rxPacket, &hostInterface_rxPacket, sizeof(hostInterface_packet_t)); + queue.put(rxPacket); + +#if defined (LIB_DEBUG) + DebugPrintRxPacket(); +#endif + /* reset buffer position */ + rxBuff = (uint8_t*)&hostInterface_rxPacket; + } + } + } + } +} + +void KW40Z::SearchStartByte() +{ + bool found = false; + uint8_t * rdIdx = (uint8_t*)&hostInterface_rxPacket + 1; + + while(rdIdx < rxBuff) + { + if(*rdIdx == gHostInterface_startByte1) + { + uint32_t len = rxBuff - rdIdx; + + memcpy(&hostInterface_rxPacket,rdIdx,len); + rxBuff -= len; + found = true; + +#if defined (LIB_DEBUG) + pc.printf("moving "); +#endif + break; + } + rdIdx++; + } + + if(!found) + { + /* reset buffer position */ + rxBuff = (uint8_t*)&hostInterface_rxPacket; + } + +#if defined (LIB_DEBUG) + pc.printf("search done\r\n"); + DebugPrintRxPacket(); +#endif +} + +void KW40Z::SendPacketOK() +{ + hostInterface_packet_t txPacket = {0}; + + txPacket.start1 = gHostInterface_startByte1, + txPacket.start2 = gHostInterface_startByte2, + txPacket.type = packetType_OK, + txPacket.length = 0, + txPacket.data[0] = gHostInterface_trailerByte; + + SendPacket(&txPacket, false); +} + +void KW40Z::ConfirmPacketOK() +{ +} + +void KW40Z::ProcessReceivedPacket(hostInterface_packet_t * rxPacket) +{ +#if defined (LIB_DEBUG) + pc.printf("packet found %d\r\n", rxPacket->type); +#endif + +#ifdef gHostInterface_TxConfirmationEnable + // acknowledge the packet reception + if ( 1 == ( rxPacket->start2 & 0x01 ) ) + { + SendPacketOK(); + } +#endif + + switch(rxPacket->type) + { + /* button presses */ + case packetType_pressUp: + case packetType_pressDown: + case packetType_pressLeft: + case packetType_pressRight: + case packetType_slide: + if((kw40z_Cbs != NULL) && (kw40z_Cbs->buttons != NULL)) + { + kw40z_Cbs->buttons((hexi_buttons_t)(rxPacket->type)); + } + break; + + /* Alert Service */ + case packetType_alertIn: + if((kw40z_Cbs != NULL) && (kw40z_Cbs->alert != NULL)) + { + kw40z_Cbs->alert(&rxPacket->data[0], rxPacket->length); + } + break; + + /* Passkey for pairing received */ + case packetType_passDisplay: + if((kw40z_Cbs != NULL) && (kw40z_Cbs->passkey != NULL)) + { + kw40z_Cbs->passkey(&rxPacket->data[0]); + } + break; + + /* OTAP messages */ + case packetType_otapCompleted: + case packetType_otapFailed: + break; + + /* TSI Status */ + case packetType_buttonsGroupSendActive: + break; + + /* Advertisement Mode Info */ + case packetType_advModeSend: + break; + + /* Link State */ + case packetType_linkStateSend: + break; + + /* ANCS Service Notification Received */ + case packetType_notification: + if((kw40z_Cbs != NULL) && (kw40z_Cbs->notifications != NULL)) + { + kw40z_Cbs->notifications(rxPacket->data[0],rxPacket->data[1]); + } + break; + + /* Build version */ + case packetType_buildVersion: + break; + + case packetType_OK: + /* do nothing, the flag is set in the RxTask */ + break; + + default: + break; + } +}