├── .gitignore ├── API.md ├── README.md ├── aci.h ├── constants.h ├── data.h ├── examples └── heartrate │ ├── heartrate.ino │ └── services.h ├── hal_platform.h ├── heart_rate_sensor-redbear.xml ├── nRF8001.cpp ├── nRF8001.h ├── services.h.example └── services.h.redbear /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | .DS_Store 3 | *.sublime-* 4 | .tags 5 | -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | # Arduino nRF8001 library API documentation 2 | 3 | ## Setup 4 | 5 | The initializer is 6 | 7 | nRF8001(uint8_t reset_pin, uint8_t reqn_pin, uint8_t rdyn_pin); 8 | 9 | The three arguments indicate the three pins that are specific to each 10 | nRF8001 chip. You can have multiple chips attached and each will 11 | require 3 pins. The SPI pins (11, 12, and 13) are fixed and can be 12 | shared. 13 | 14 | nRFCmd setup(); 15 | 16 | Setup will send the setup messages stored in `services.h`. This header 17 | file is generated by nRFgo Studio and must be stored in the 18 | `libraries/nRF8001` folder. We ship with a sample services.h file which 19 | is Nordic’s heart rate monitor example. 20 | 21 | ## Event handlers 22 | 23 | None of the functions that represent a request or command to the nRF8001 24 | immediately return a response. Instead the response is received 25 | asynchronously, either the next time a command is transmitted to nRF8001, 26 | or by calling `poll`. When a response is received, any event handlers 27 | you have registered will be called. 28 | 29 | The exact definition of the event handlers will be listed with the 30 | function they belong to. Many commands that do not have an explicit 31 | response with data, but only a response indicating whether the command 32 | was successful, will call `nRFCommandResponseHandler`. The definition is: 33 | 34 | typedef void (*nRFCommandResponseHandler) (uint8_t opcode, uint8_t status); 35 | 36 | `opcode` is the numeric number for the operation. You may consult 37 | `constants.h` or the nRF8001 datasheet for these numbers. 38 | `status` indicates the result of the command. Of particular interest 39 | are `NRF_STATUS_SUCCESS` for success. Any number above `0x80` is an error. 40 | The command response handler is registered with 41 | 42 | void setCommandResponseHandler(nRFCommandResponseHandler handler); 43 | 44 | There is also the generic event handler `nRFEventHandler`: 45 | 46 | typedef void (*nRFEventHandler) (nRFEvent *); 47 | 48 | This event handler will be called on any event received from the nRF8001 49 | and is passed the internal data structure representing the actual 50 | bytes received. `data.h` contains the definition of this data structure. 51 | 52 | ## Polling 53 | 54 | The library will receive events from nRF8001 whenever a command or request 55 | is transmitted. You may also wait for a response without sending a command 56 | with `poll`. 57 | 58 | nRFTxStatus poll(uint16_t timeout); 59 | nRFTxStatus poll(); 60 | 61 | `timeout` is a timeout in milliseconds. 62 | 63 | ## State information 64 | 65 | uint8_t creditsAvailable(); 66 | 67 | Data related requests require a “credit” to ensure that data is not sent 68 | too fast. `creditsAvailable` returns the number of available credits. 69 | 70 | uint8_t isConnected(); 71 | 72 | Returns 1 if the device is currently connected to a master, 0 otherwise. 73 | 74 | nRFConnectionStatus getConnectionStatus(); 75 | 76 | Returns connection status. `nRFConnectionStatus` is an enum with values 77 | `Disconnected`, `Connected` and `Connecting`. 78 | 79 | uint8_t isPipeOpen(nRFPipe servicePipeNo); 80 | 81 | Returns 1 if the specified pipe is currently open, 0 otherwise. 82 | 83 | nRFDeviceState getDeviceState(); 84 | 85 | Returns the device state. `nRFDeviceState` is an enum defined in 86 | `constants.h`. Most normal operation occurs while in the `Standby` state. 87 | 88 | ## Device information 89 | 90 | nRFTxStatus getBatteryLevel(); 91 | void setBatteryLevelHandler(nRFBatteryLevelHandler handler); 92 | typedef void (*nRFBatteryLevelHandler) (float voltage); 93 | 94 | Requests battery level from the nRF8001’s ADC. The registered event handler 95 | is called with the response, as a floating point number in volts. 96 | (The library takes care of the conversion from nRF8001’s internal units.) 97 | 98 | nRFTxStatus getTemperature(); 99 | void setTemperatureHandler(nRFTemperatureHandler handler); 100 | typedef void (*nRFTemperatureHandler) (float tempC); 101 | 102 | Requests temperature from the nRF8001’s on-chip thermometer. The registered 103 | event handler is called with the response, as a floating point number in 104 | degrees Celcius. (The library takes care of the conversion from nRF8001’s 105 | internal units.) 106 | 107 | nRFTxStatus getDeviceAddress(); 108 | void setDeviceAddressHandler(nRFDeviceAddressHandler handler); 109 | typedef void (*nRFDeviceAddressHandler) (uint8_t *address, 110 | uint8_t addressType); 111 | 112 | Requests the nRF8001 device address. `address` is a 6 byte long array 113 | containing the 48-bit MAC address, unterminated, LSB first. 114 | See the nRF8001 datasheet for values of `addressType`. 115 | 116 | nRFTxStatus getDeviceVersion(); 117 | void setDeviceVersionHandler(nRFDeviceVersionHandler handler); 118 | typedef void (*nRFDeviceVersionHandler) (uint16_t configId, 119 | uint8_t aciVersion, uint8_t setupFormat, uint8_t configStatus); 120 | 121 | See the nRF8001 datasheet for the meaning of these values. 122 | 123 | ## Connections and data -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nRF8001 support for Arduino 2 | 3 | This is a work in progress. Most of the functionality of the nRF8001 chip 4 | is implemented, but not all commands and interactions have been fully 5 | tested. 6 | 7 | ## Installation 8 | 9 | On a Mac: 10 | 11 | cd ~/Documents/Arduino/libraries 12 | git clone https://github.com/guanix/arduino-nrf8001.git nRF8001 13 | 14 | The included example works with the Heart Rate function of the Nordic iOS app 15 | and sends the nRF8001’s internal temperature reading as heart rate and the 16 | fixed number 78 as battery level. It assumes the following pin assignments, 17 | easily changed: 18 | 19 | * RESET on digital pin 7 20 | * REQN on digital pin 9 21 | * RDYN on digital pin 8 22 | 23 | This library uses the AVR chip’s hardware SPI support, so the SCK (CLK), 24 | MISO and MOSI pins cannot be changed: 25 | 26 | * MOSI on digital pin 11 27 | * MISO on digital pin 12 28 | * SCK on digital pin 13 29 | 30 | Remember that nRF8001 expects 3.3V supply and logic levels. Connecting it 31 | to a typical Arduino will fry it. You must either use a level shifter/buffer, 32 | or a [3.3V Arduino][promini], or a clone such as [Seeeduino][seeeduino] 33 | that can switch between 3.3V and 5V modes. 34 | 35 | [promini]: https://www.sparkfun.com/products/11114 36 | [seeeduino]: http://www.seeedstudio.com/depot/seeeduino-v221-atmega-328p-p-669.html 37 | 38 | ## Technical Overview 39 | 40 | nRF8001 is a slave-only Bluetooth Low Energy (Bluetooth 4.0) transceiver. 41 | It requires an external antenna and matching components, but little else. 42 | At least one vendor markets a complete module incorporating the antenna, 43 | which only requires a decoupling cap and a ground plane. 44 | 45 | Communication with the nRF8001 uses a custom binary SPI protocol that is 46 | documented in the datasheet. 47 | Communication is full duplex and happens in _transactions_ where the master 48 | can send a request while the slave simultaneously sends a response or 49 | event. 50 | 51 | In addition to the typical SS pin to send a command (called REQN here), there 52 | is an additional pin, RDYN. When the master wants to initiate a transaction: 53 | 54 | 1. Master brings REQN low. 55 | 2. Slave (nRF8001) brings RDYN low. 56 | 3. SPI transaction proceeds. Master sends a command and simultaneously 57 | receives a message (an event or a response to a previous command.) 58 | 59 | When the slave initiates a transaction: 60 | 61 | 1. Slave brings RDYN low. 62 | 2. Master brings REQN low. 63 | 3. SPI transaction proceeds. 64 | 65 | Because nRF8001 SPI transactions are asynchronous, 66 | while the Arduino nRF8001 library 67 | contains a number of functions to send commands to the nRF8001, those 68 | functions almost never immediately return a response. Instead, you must 69 | register _handlers_, special functions that you define, which will in turn 70 | be called when the response (or an event) is received. A typical simple 71 | Arduino sketch that uses nRF8001 will use global variables to coordinate 72 | between handlers and the main event loop. 73 | 74 | This library is not interrupt driven. Responses from the nRF8001 may be 75 | received whenever you send a command, but if you are not ready to send a 76 | command immediately, you must call `nRF8001::poll()` (or its variant with 77 | a timeout) to poll for responses or events. 78 | 79 | In order to define services, you must use Nordic’s nRFgo Studio software. 80 | The software will generate a `services.h` file that you must place in the 81 | library’s folder inside your Arduino installation’s `libraries` folder. 82 | The software will define a number of _pipes_. The included `services.h` is 83 | Nordic’s heart rate monitor example, which allows the slave to send 84 | heart rate information on pipe 5 and battery level information on pipe 8. 85 | The exact format of the data messages are defined in the relevant 86 | [service specification][servicespec]. 87 | 88 | [servicespec]: http://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx 89 | 90 | ## The heart rate example 91 | 92 | The included heart rate example defines two event handlers, 93 | `temperatureHandler` for receiving temperature from the nRF8001’s 94 | built-in thermometer and stores it in a global variable, 95 | and the catch-all `eventHandler`, which only calls `nRF8001::debugEvent`, 96 | a function that prints extensive information about received events to the 97 | serial port. It works with Nordic’s [test app][nordicapp] on iPhone 4S, 98 | iPhone 5 and the third generation iPad. 99 | 100 | [nordicapp]: http://itunes.apple.com/us/app/nrfready-utility/id497679111?mt=8 101 | 102 | After initializing the `nRF8001` class, registering handlers and calling 103 | `nRF8001::setup` to send setup messages from `services.h` to the nRF8001 104 | chip, it then calls `nRF8001::getDeviceAddress()` to get the device 105 | address and `nRF8001::getTemperature()` to request the temperature. 106 | It then calls `nRF8001::connect` to wait for a connection from a peer 107 | device. 108 | 109 | In the loop, we continuously poll with `nRF8001::poll` using a 2-second 110 | timeout, and every 2 seconds or so, we read a new temperature and send 111 | that out as a heart rate. If the connection is disconnected, we call 112 | `nRF8001::connect` again to wait for a new connection. 113 | 114 | ## License 115 | Copyright © 2012 Guan Yang 116 | 117 | Permission is hereby granted, free of charge, to any person obtaining 118 | a copy of this software and associated documentation files (the 119 | “Software”), to deal in the Software without restriction, including 120 | without limitation the rights to use, copy, modify, merge, publish, 121 | distribute, sublicense, and/or sell copies of the Software, and to 122 | permit persons to whom the Software is furnished to do so, subject to 123 | the following conditions: 124 | 125 | The above copyright notice and this permission notice shall be 126 | included in all copies or substantial portions of the Software. 127 | 128 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 129 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 130 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 131 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 132 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 133 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 134 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 135 | -------------------------------------------------------------------------------- /aci.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010 Nordic Semiconductor. All Rights Reserved. 2 | * 3 | * The information contained herein is property of Nordic Semiconductor ASA. 4 | * Terms and conditions of usage are described in detail in NORDIC 5 | * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. 6 | * 7 | * Licensees are granted free, non-transferable use of the information. NO 8 | * WARRANTY of ANY KIND is provided. This heading must NOT be removed from 9 | * the file. 10 | * 11 | * $LastChangedRevision$ 12 | */ 13 | 14 | /** 15 | * @file 16 | * 17 | * @defgroup aci aci 18 | * @{ 19 | * @ingroup lib 20 | * 21 | * @brief Definitions for the ACI (Application Control Interface) 22 | * @remarks 23 | * 24 | * Flow control from application mcu to nRF8001 25 | * 26 | * Data flow control: 27 | * The flow control is credit based and the credit is initally given using the "device started" event. 28 | * A credit of more than 1 is given to the application mcu. 29 | * These credits are used only after the "pipes connected" event is sent to the application mcu. 30 | * 31 | * every send_data that is used decrements the credit available by 1. This is to be tracked by the application mcu. 32 | * When the credit available reaches 0, the application mcu shall not send any more send_data. 33 | * Credit is returned using the "credit event", this returned credit can then be used to send more send_data. 34 | * This flow control is not necessary and not available for Broadcast. 35 | * The entire credit available with the external mcu expires when a "disconnected" event arrives. 36 | * 37 | * Command flow control: 38 | * When a command is sent over the ACI, the next command shall not be sent until after a response 39 | * for the command sent has arrived. 40 | * 41 | */ 42 | 43 | #ifndef ACI_H__ 44 | #define ACI_H__ 45 | 46 | /** 47 | * @def ACI_VERSION 48 | * @brief Current ACI protocol version. 0 means a device that is not yet released. 49 | * A numer greater than 0 refers to a specific ACI version documented and released. 50 | * The ACI consists of the ACI commands, ACI events and error codes. 51 | */ 52 | #define ACI_VERSION (0x02) 53 | /** 54 | * @def BTLE_DEVICE_ADDRESS_SIZE 55 | * @brief Size in bytes of a Bluetooth Address 56 | */ 57 | #define BTLE_DEVICE_ADDRESS_SIZE (6) 58 | /** 59 | * @def ACI_PACKET_MAX_LEN 60 | * @brief Maximum length in bytes of a full ACI packet, including length prefix, opcode and payload 61 | */ 62 | #define ACI_PACKET_MAX_LEN (32) 63 | /** 64 | * @def ACI_ECHO_DATA_MAX_LEN 65 | * @brief Maximum length in bytes of the echo data portion 66 | */ 67 | #define ACI_ECHO_DATA_MAX_LEN (ACI_PACKET_MAX_LEN - 3) 68 | /** 69 | * @def ACI_DEVICE_MAX_PIPES 70 | * @brief Maximum number of ACI pipes 71 | */ 72 | #define ACI_DEVICE_MAX_PIPES (62) 73 | /** 74 | * @def ACI_PIPE_TX_DATA_MAX_LEN 75 | * @brief Maximum length in bytes of a transmission data pipe packet 76 | */ 77 | #define ACI_PIPE_TX_DATA_MAX_LEN (20) 78 | /** 79 | * @def ACI_PIPE_RX_DATA_MAX_LEN 80 | * @brief Maximum length in bytes of a reception data pipe packet 81 | */ 82 | #define ACI_PIPE_RX_DATA_MAX_LEN (22) 83 | /** 84 | * @def ACI_GAP_DEVNAME_MAX_LEN 85 | * @brief Maximum length in bytes of the GAP device name 86 | */ 87 | #define ACI_GAP_DEVNAME_MAX_LEN (20) 88 | /** 89 | * @def ACI_AD_PACKET_MAX_LEN 90 | * @brief Maximum length in bytes of an AD packet 91 | */ 92 | #define ACI_AD_PACKET_MAX_LEN (31) 93 | /** 94 | * @def ACI_AD_PACKET_MAX_USER_LEN 95 | * @brief Maximum usable length in bytes of an AD packet 96 | */ 97 | #define ACI_AD_PACKET_MAX_USER_LEN (31 - 3) 98 | /** 99 | * @def ACI_PIPE_INVALID 100 | * @brief Invalid pipe number 101 | */ 102 | #define ACI_PIPE_INVALID (0xFF) 103 | 104 | /** 105 | * @enum aci_pipe_store_t 106 | * @brief Storage type identifiers: local and remote 107 | */ 108 | typedef enum 109 | { 110 | ACI_STORE_INVALID = 0x0, 111 | ACI_STORE_LOCAL= 0x01, 112 | ACI_STORE_REMOTE= 0x02 113 | } aci_pipe_store_t; 114 | 115 | /** 116 | * @enum aci_pipe_type_t 117 | * @brief Pipe types 118 | */ 119 | typedef enum 120 | { 121 | ACI_TX_BROADCAST = 0x0001, 122 | ACI_TX = 0x0002, 123 | ACI_TX_ACK = 0x0004, 124 | ACI_RX = 0x0008, 125 | ACI_RX_ACK = 0x0010, 126 | ACI_TX_REQ = 0x0020, 127 | ACI_RX_REQ = 0x0040, 128 | ACI_SET = 0x0080, 129 | ACI_TX_SIGN = 0x0100, 130 | ACI_RX_SIGN = 0x0200, 131 | ACI_RX_ACK_AUTO = 0x0400 132 | } aci_pipe_type_t; 133 | 134 | /** 135 | * @enum aci_bd_addr_type_t 136 | * @brief Bluetooth Address types 137 | */ 138 | typedef enum 139 | { 140 | ACI_BD_ADDR_TYPE_INVALID = 0x00, 141 | ACI_BD_ADDR_TYPE_PUBLIC = 0x01, 142 | ACI_BD_ADDR_TYPE_RANDOM_STATIC = 0x02, 143 | ACI_BD_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE = 0x03, 144 | ACI_BD_ADDR_TYPE_RANDOM_PRIVATE_UNRESOLVABLE = 0x04 145 | } aci_bd_addr_type_t; 146 | 147 | /** 148 | * @enum aci_device_output_power_t 149 | * @brief Radio output power levels 150 | */ 151 | typedef enum 152 | { 153 | ACI_DEVICE_OUTPUT_POWER_MINUS_18DBM = 0x00, /**< Output power set to -18dBm */ 154 | ACI_DEVICE_OUTPUT_POWER_MINUS_12DBM = 0x01, /**< Output power set to -12dBm */ 155 | ACI_DEVICE_OUTPUT_POWER_MINUS_6DBM = 0x02, /**< Output power set to -6dBm */ 156 | ACI_DEVICE_OUTPUT_POWER_0DBM = 0x03 /**< Output power set to 0dBm - DEFAULT*/ 157 | } aci_device_output_power_t; 158 | 159 | /** 160 | * @enum aci_device_operation_mode_t 161 | * @brief Device operation modes 162 | */ 163 | typedef enum 164 | { 165 | ACI_DEVICE_INVALID =0x00, 166 | ACI_DEVICE_TEST =0x01, 167 | ACI_DEVICE_SETUP =0x02, 168 | ACI_DEVICE_STANDBY =0x03 169 | } aci_device_operation_mode_t; 170 | 171 | /** 172 | * @enum aci_disconnect_reason_t 173 | * @brief Reason enumeration for ACI_CMD_DISCONNECT 174 | */ 175 | typedef enum 176 | { 177 | ACI_REASON_TERMINATE =0x01, /**< Use this to disconnect (does a terminate request), you need to wait for the "disconnected" event */ 178 | ACI_REASON_BAD_TIMING =0x02 /* 6 | #include 7 | 8 | #include "services.h" 9 | 10 | hal_aci_data_t setup_msgs[NB_SETUP_MESSAGES] = SETUP_MESSAGES_CONTENT; 11 | 12 | // change nRF8001 reset pin to -1 if it's not connected 13 | // Redbear BLE Shield users: to my knowledge reset pin is not connected so use -1! 14 | // NOTE: if you choose -1, youll need to manually reset your device after powerup!! 15 | #define RESET_PIN 7 16 | #define REQN_PIN 9 17 | #define RDYN_PIN 8 18 | 19 | nRF8001 *nrf; 20 | 21 | float temperatureC; 22 | uint8_t pipeStatusReceived, dataSent; 23 | unsigned long lastSent; 24 | 25 | // This function is called when nRF8001 responds with the temperature 26 | void temperatureHandler(float tempC) 27 | { 28 | Serial.println("received temperature"); 29 | temperatureC = tempC; 30 | } 31 | 32 | // Generic event handler, here it's just for debugging all received events 33 | void eventHandler(nRFEvent *event) 34 | { 35 | Serial.println("event handler"); 36 | nrf->debugEvent(event); 37 | } 38 | 39 | void setup() { 40 | temperatureC = 0.0; 41 | pipeStatusReceived = 0; 42 | lastSent = 0; 43 | 44 | Serial.begin(115200); 45 | Serial.println("Hello"); 46 | 47 | // nRF8001 class initialized with pin numbers 48 | nrf = new nRF8001(RESET_PIN, REQN_PIN, RDYN_PIN); 49 | 50 | // Register event handles 51 | nrf->setEventHandler(&eventHandler); 52 | nrf->setTemperatureHandler(&temperatureHandler); 53 | if ((nrf->setup(setup_msgs, NB_SETUP_MESSAGES)) == cmdSuccess) { 54 | Serial.println("SUCCESS"); 55 | } else { 56 | Serial.println("FAIL"); 57 | while (1); 58 | } 59 | 60 | // These functions merely request device address and temperature, 61 | // actual responses are asynchronous. They'll return error codes 62 | // if somehow the request itself failed, for example because 63 | // the device is not ready for these commands. 64 | nrf->getDeviceAddress(); 65 | nrf->poll(); 66 | nrf->getTemperature(); 67 | nrf->poll(); 68 | 69 | if (temperatureC > 0.0) { 70 | Serial.print("Temperature: "); 71 | Serial.println(temperatureC, 2); 72 | } 73 | 74 | nrf->connect(0, 32); 75 | } 76 | 77 | void loop() { 78 | Serial.println("polling"); 79 | 80 | // Polling will block - times out after 2 seconds 81 | nrf->poll(2000); 82 | 83 | // If heart rate pipe is open 84 | if (nrf->isPipeOpen(PIPE_HEART_RATE_HEART_RATE_MEASUREMENT_TX) && (millis() - lastSent) > 1000 && temperatureC > 0.0 && nrf->creditsAvailable()) { 85 | Serial.println("ready to send data"); 86 | uint8_t temp[2]; 87 | temp[0] = 0; 88 | temp[1] = round(temperatureC); 89 | 90 | nrf->sendData(PIPE_HEART_RATE_HEART_RATE_MEASUREMENT_TX, 2, (uint8_t *)&temp); 91 | lastSent = millis(); 92 | uint8_t bat = 78; 93 | 94 | // If battery pipe is open 95 | if (nrf->isPipeOpen(PIPE_BATTERY_BATTERY_LEVEL_TX) && nrf->creditsAvailable()) { 96 | nrf->sendData(PIPE_BATTERY_BATTERY_LEVEL_TX, 1, &bat); 97 | } 98 | 99 | // get new temperature 100 | nrf->getTemperature(); 101 | } else if (nrf->getConnectionStatus() == Disconnected) { 102 | Serial.println("Reconnecting"); 103 | dataSent = 0; 104 | nrf->connect(0, 32); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /examples/heartrate/services.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010 Nordic Semiconductor. All Rights Reserved. 2 | * 3 | * The information contained herein is property of Nordic Semiconductor ASA. 4 | * Terms and conditions of usage are described in detail in NORDIC 5 | * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. 6 | * 7 | * Licensees are granted free, non-transferable use of the information. NO 8 | * WARRANTY of ANY KIND is provided. This heading must NOT be removed from 9 | * the file. 10 | */ 11 | 12 | /** 13 | * This file is autogenerated by 1.12.1.1992 14 | */ 15 | 16 | #ifndef SETUP_MESSAGES_H__ 17 | #define SETUP_MESSAGES_H__ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #define PIPE_DEVICE_INFORMATION_SERIAL_NUMBER_STRING_SET 1 24 | #define PIPE_DEVICE_INFORMATION_MANUFACTURER_NAME_STRING_SET 2 25 | #define PIPE_DEVICE_INFORMATION_SYSTEM_ID_SET 3 26 | #define PIPE_DEVICE_INFORMATION_FIRMWARE_REVISION_STRING_SET 4 27 | #define PIPE_HEART_RATE_HEART_RATE_MEASUREMENT_TX 5 28 | #define PIPE_HEART_RATE_HEART_RATE_SENSOR_LOCATION_SET 6 29 | #define PIPE_HEART_RATE_HEART_RATE_CONTROL_POINT_RX_ACK 7 30 | #define PIPE_BATTERY_BATTERY_LEVEL_TX 8 31 | #define PIPE_BATTERY_BATTERY_LEVEL_SET 9 32 | 33 | #define NUMBER_OF_PIPES 9 34 | typedef struct 35 | { 36 | aci_pipe_store_t location; 37 | aci_pipe_type_t pipe_type; 38 | } services_pipe_type_mapping_t; 39 | 40 | static services_pipe_type_mapping_t services_pipe_type_mapping[NUMBER_OF_PIPES] = 41 | { 42 | {ACI_STORE_LOCAL, ACI_SET}, 43 | {ACI_STORE_LOCAL, ACI_SET}, 44 | {ACI_STORE_LOCAL, ACI_SET}, 45 | {ACI_STORE_LOCAL, ACI_SET}, 46 | {ACI_STORE_LOCAL, ACI_TX}, 47 | {ACI_STORE_LOCAL, ACI_SET}, 48 | {ACI_STORE_LOCAL, ACI_RX_ACK}, 49 | {ACI_STORE_LOCAL, ACI_TX}, 50 | {ACI_STORE_LOCAL, ACI_SET}, 51 | }; 52 | 53 | #define GAP_PPCP_MAX_CONN_INT 0x320 /**< Maximum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific value requested */ 54 | #define GAP_PPCP_MIN_CONN_INT 0x190 /**< Minimum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific maximum*/ 55 | #define GAP_PPCP_SLAVE_LATENCY 0 56 | #define GAP_PPCP_CONN_TIMEOUT 0x1f6 /** Connection Supervision timeout multiplier as a multiple of 10msec, 0xFFFF means no specific value requested */ 57 | 58 | /** @brief do a set_local_data a new value for PIPE_DEVICE_INFORMATION_SERIAL_NUMBER_STRING_SET 59 | * @param src source buffer to send data from. 60 | * Presentation format: UTF-8 string 61 | * @param size the number of bytes to send. Maximum size is 4 62 | * @details use this function to do a set_local_data for PIPE_DEVICE_INFORMATION_SERIAL_NUMBER_STRING_SET. If no transaction are currently 63 | * running, the set will be immediate, otherwise, it will be done at the end of the current transaction 64 | * when services_update_pipes will be called. 65 | */ 66 | void services_set_device_information_serial_number_string(void *src, int size); 67 | 68 | /** @brief do a set_local_data a new value for PIPE_DEVICE_INFORMATION_MANUFACTURER_NAME_STRING_SET 69 | * @param src source buffer to send data from. 70 | * Presentation format: UTF-8 string 71 | * @param size the number of bytes to send. Maximum size is 20 72 | * @details use this function to do a set_local_data for PIPE_DEVICE_INFORMATION_MANUFACTURER_NAME_STRING_SET. If no transaction are currently 73 | * running, the set will be immediate, otherwise, it will be done at the end of the current transaction 74 | * when services_update_pipes will be called. 75 | */ 76 | void services_set_device_information_manufacturer_name_string(void *src, int size); 77 | 78 | /** @brief do a set_local_data a new value for PIPE_DEVICE_INFORMATION_SYSTEM_ID_SET 79 | * @param src source buffer to send data from 80 | * @param size the number of bytes to send. Maximum size is 8 81 | * @details use this function to do a set_local_data for PIPE_DEVICE_INFORMATION_SYSTEM_ID_SET. If no transaction are currently 82 | * running, the set will be immediate, otherwise, it will be done at the end of the current transaction 83 | * when services_update_pipes will be called. 84 | */ 85 | void services_set_device_information_system_id(void *src, int size); 86 | 87 | /** @brief do a set_local_data a new value for PIPE_DEVICE_INFORMATION_FIRMWARE_REVISION_STRING_SET 88 | * @param src source buffer to send data from. 89 | * Presentation format: UTF-8 string 90 | * @param size the number of bytes to send. Maximum size is 4 91 | * @details use this function to do a set_local_data for PIPE_DEVICE_INFORMATION_FIRMWARE_REVISION_STRING_SET. If no transaction are currently 92 | * running, the set will be immediate, otherwise, it will be done at the end of the current transaction 93 | * when services_update_pipes will be called. 94 | */ 95 | void services_set_device_information_firmware_revision_string(void *src, int size); 96 | 97 | /** @brief send a new value for PIPE_HEART_RATE_HEART_RATE_MEASUREMENT_TX 98 | * @param src source buffer to send data from 99 | * @param size the number of bytes to send. Maximum size is 19 100 | * @param is_freshest_sample set it to true if you want to overwrite an eventual pending transaction on this pipe. 101 | * @details use this function to send a new value for PIPE_HEART_RATE_HEART_RATE_MEASUREMENT_TX. If no transaction are currently 102 | * running, the send will be immediate, otherwise, it will be done at the end of the current transaction 103 | * when services_update_pipes will be called. If a transaction on this pipe is already pending, then this function 104 | * will not overwrite the data of the previous transaction and return false. 105 | * @return : true if is_freshest_sample true, otherwise return false if a transaction on this pipe is already pending, true otherwise. 106 | */ 107 | bool services_send_heart_rate_heart_rate_measurement(void *src, int size, bool is_freshest_sample); 108 | 109 | /** @brief do a set_local_data a new value for PIPE_HEART_RATE_HEART_RATE_SENSOR_LOCATION_SET 110 | * @param src the value to send 111 | * @details use this function to do a set_local_data for PIPE_HEART_RATE_HEART_RATE_SENSOR_LOCATION_SET. If no transaction are currently 112 | * running, the set will be immediate, otherwise, it will be done at the end of the current transaction 113 | * when services_update_pipes will be called. 114 | */ 115 | void services_set_heart_rate_heart_rate_sensor_location(uint8_t src); 116 | 117 | /** @brief send an acknowledge rx ack pipe for PIPE_HEART_RATE_HEART_RATE_CONTROL_POINT_RX_ACK 118 | * @details use this function to acknowledge PIPE_HEART_RATE_HEART_RATE_CONTROL_POINT_RX_ACK. If no transaction are pending, it 119 | * would be sent immediately, otherwise, it will be done at the end of the current transaction when services_update_pipes 120 | * will be called. 121 | */ 122 | void services_acknowledge_heart_rate_heart_rate_control_point(const unsigned char error_code); 123 | 124 | /** @brief send a new value for PIPE_BATTERY_BATTERY_LEVEL_TX 125 | * @param src the value to send 126 | * @param is_freshest_sample set it to true if you want to overwrite an eventual pending transaction on this pipe. 127 | * @details use this function to send a new value for PIPE_BATTERY_BATTERY_LEVEL_TX. If no transaction are currently 128 | * running, the send will be immediate, otherwise, it will be done at the end of the current transaction 129 | * when services_update_pipes will be called. If a transaction on this pipe is already pending, then this function 130 | * will not overwrite the data of the previous transaction and return false. 131 | * @return : true if is_freshest_sample true, otherwise return false if a transaction on this pipe is already pending, true otherwise. 132 | */ 133 | bool services_send_battery_battery_level(uint8_t src, bool is_freshest_sample); 134 | 135 | /** @brief do a set_local_data a new value for PIPE_BATTERY_BATTERY_LEVEL_SET 136 | * @param src the value to send 137 | * @details use this function to do a set_local_data for PIPE_BATTERY_BATTERY_LEVEL_SET. If no transaction are currently 138 | * running, the set will be immediate, otherwise, it will be done at the end of the current transaction 139 | * when services_update_pipes will be called. 140 | */ 141 | void services_set_battery_battery_level(uint8_t src); 142 | 143 | /** @brief function to trig pending transaction on pipes 144 | * @details This function check for each pipe if it has a pending transaction (send/rx_request/ack) 145 | * and if so executes this transaction. 146 | * This function should be called in the APP_RUN state of the process function of the application. 147 | */ 148 | void services_update_pipes(void); 149 | 150 | #define NB_SETUP_MESSAGES 27 151 | #define SETUP_MESSAGES_CONTENT {\ 152 | {0x00,\ 153 | {\ 154 | 0x07,0x06,0x00,0x00,0x03,0x02,0x40,0x6c,\ 155 | },\ 156 | },\ 157 | {0x00,\ 158 | {\ 159 | 0x1f,0x06,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x09,0x00,0x06,0x00,0x00,0x06,0x00,0x00,\ 160 | 0x81,0x0d,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 161 | },\ 162 | },\ 163 | {0x00,\ 164 | {\ 165 | 0x1f,0x06,0x10,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 166 | 0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x12,0x03,0x90,0x00,0x14,\ 167 | },\ 168 | },\ 169 | {0x00,\ 170 | {\ 171 | 0x1f,0x06,0x10,0x38,0x02,0xff,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 172 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 173 | },\ 174 | },\ 175 | {0x00,\ 176 | {\ 177 | 0x05,0x06,0x10,0x54,0x00,0x00,\ 178 | },\ 179 | },\ 180 | {0x00,\ 181 | {\ 182 | 0x1f,0x06,0x20,0x00,0x04,0x04,0x02,0x02,0x00,0x01,0x28,0x00,0x01,0x00,0x18,0x04,0x04,0x05,0x05,0x00,\ 183 | 0x02,0x28,0x03,0x01,0x0e,0x03,0x00,0x00,0x2a,0x04,0x14,0x0f,\ 184 | },\ 185 | },\ 186 | {0x00,\ 187 | {\ 188 | 0x1f,0x06,0x20,0x1c,0x0f,0x00,0x03,0x2a,0x00,0x01,0x4e,0x6f,0x72,0x64,0x69,0x63,0x20,0x48,0x52,0x4d,\ 189 | 0x20,0x56,0x31,0x2e,0x30,0x04,0x04,0x05,0x05,0x00,0x04,0x28,\ 190 | },\ 191 | },\ 192 | {0x00,\ 193 | {\ 194 | 0x1f,0x06,0x20,0x38,0x03,0x01,0x02,0x05,0x00,0x01,0x2a,0x06,0x04,0x03,0x02,0x00,0x05,0x2a,0x01,0x01,\ 195 | 0x00,0x00,0x04,0x04,0x05,0x05,0x00,0x06,0x28,0x03,0x01,0x02,\ 196 | },\ 197 | },\ 198 | {0x00,\ 199 | {\ 200 | 0x1f,0x06,0x20,0x54,0x07,0x00,0x04,0x2a,0x06,0x04,0x09,0x08,0x00,0x07,0x2a,0x04,0x01,0x90,0x01,0x20,\ 201 | 0x03,0x00,0x00,0xf6,0x01,0x04,0x04,0x02,0x02,0x00,0x08,0x28,\ 202 | },\ 203 | },\ 204 | {0x00,\ 205 | {\ 206 | 0x1f,0x06,0x20,0x70,0x00,0x01,0x01,0x18,0x04,0x04,0x02,0x02,0x00,0x09,0x28,0x00,0x01,0x0a,0x18,0x04,\ 207 | 0x04,0x05,0x05,0x00,0x0a,0x28,0x03,0x01,0x02,0x0b,0x00,0x25,\ 208 | },\ 209 | },\ 210 | {0x00,\ 211 | {\ 212 | 0x1f,0x06,0x20,0x8c,0x2a,0x06,0x04,0x05,0x04,0x00,0x0b,0x2a,0x25,0x01,0x31,0x35,0x38,0x37,0x06,0x04,\ 213 | 0x08,0x07,0x00,0x0c,0x29,0x04,0x01,0x19,0x00,0x00,0x00,0x01,\ 214 | },\ 215 | },\ 216 | {0x00,\ 217 | {\ 218 | 0x1f,0x06,0x20,0xa8,0x00,0x00,0x04,0x04,0x05,0x05,0x00,0x0d,0x28,0x03,0x01,0x02,0x0e,0x00,0x29,0x2a,\ 219 | 0x06,0x04,0x15,0x14,0x00,0x0e,0x2a,0x29,0x01,0x4e,0x6f,0x72,\ 220 | },\ 221 | },\ 222 | {0x00,\ 223 | {\ 224 | 0x1f,0x06,0x20,0xc4,0x64,0x69,0x63,0x53,0x65,0x6d,0x69,0x63,0x6f,0x6e,0x64,0x75,0x63,0x74,0x6f,0x72,\ 225 | 0x00,0x06,0x04,0x08,0x07,0x00,0x0f,0x29,0x04,0x01,0x19,0x00,\ 226 | },\ 227 | },\ 228 | {0x00,\ 229 | {\ 230 | 0x1f,0x06,0x20,0xe0,0x00,0x00,0x01,0x00,0x00,0x04,0x04,0x05,0x05,0x00,0x10,0x28,0x03,0x01,0x02,0x11,\ 231 | 0x00,0x23,0x2a,0x06,0x04,0x09,0x08,0x00,0x11,0x2a,0x23,0x01,\ 232 | },\ 233 | },\ 234 | {0x00,\ 235 | {\ 236 | 0x1f,0x06,0x20,0xfc,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x04,0x04,0x05,0x05,0x00,0x12,0x28,0x03,\ 237 | 0x01,0x02,0x13,0x00,0x26,0x2a,0x06,0x04,0x05,0x04,0x00,0x13,\ 238 | },\ 239 | },\ 240 | {0x00,\ 241 | {\ 242 | 0x1f,0x06,0x21,0x18,0x2a,0x26,0x01,0x30,0x31,0x2e,0x31,0x06,0x04,0x08,0x07,0x00,0x14,0x29,0x04,0x01,\ 243 | 0x19,0x00,0x00,0x00,0x01,0x00,0x00,0x04,0x04,0x02,0x02,0x00,\ 244 | },\ 245 | },\ 246 | {0x00,\ 247 | {\ 248 | 0x1f,0x06,0x21,0x34,0x15,0x28,0x00,0x01,0x0d,0x18,0x04,0x04,0x05,0x05,0x00,0x16,0x28,0x03,0x01,0x10,\ 249 | 0x17,0x00,0x37,0x2a,0x14,0x00,0x13,0x00,0x00,0x17,0x2a,0x37,\ 250 | },\ 251 | },\ 252 | {0x00,\ 253 | {\ 254 | 0x1f,0x06,0x21,0x50,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 255 | 0x00,0x00,0x00,0x00,0x46,0x14,0x03,0x02,0x00,0x18,0x29,0x02,\ 256 | },\ 257 | },\ 258 | {0x00,\ 259 | {\ 260 | 0x1f,0x06,0x21,0x6c,0x01,0x00,0x00,0x04,0x04,0x05,0x05,0x00,0x19,0x28,0x03,0x01,0x02,0x1a,0x00,0x38,\ 261 | 0x2a,0x06,0x04,0x02,0x01,0x00,0x1a,0x2a,0x38,0x01,0x03,0x04,\ 262 | },\ 263 | },\ 264 | {0x00,\ 265 | {\ 266 | 0x1f,0x06,0x21,0x88,0x04,0x05,0x05,0x00,0x1b,0x28,0x03,0x01,0x08,0x1c,0x00,0x39,0x2a,0x46,0x10,0x02,\ 267 | 0x01,0x00,0x1c,0x2a,0x39,0x01,0x00,0x04,0x04,0x02,0x02,0x00,\ 268 | },\ 269 | },\ 270 | {0x00,\ 271 | {\ 272 | 0x1f,0x06,0x21,0xa4,0x1d,0x28,0x00,0x01,0x0f,0x18,0x04,0x04,0x05,0x05,0x00,0x1e,0x28,0x03,0x01,0x12,\ 273 | 0x1f,0x00,0x19,0x2a,0x16,0x04,0x02,0x01,0x00,0x1f,0x2a,0x19,\ 274 | },\ 275 | },\ 276 | {0x00,\ 277 | {\ 278 | 0x11,0x06,0x21,0xc0,0x01,0x64,0x46,0x14,0x03,0x02,0x00,0x20,0x29,0x02,0x01,0x00,0x00,0x00,\ 279 | },\ 280 | },\ 281 | {0x00,\ 282 | {\ 283 | 0x1f,0x06,0x40,0x00,0x2a,0x25,0x01,0x00,0x80,0x04,0x00,0x0b,0x00,0x00,0x2a,0x29,0x01,0x00,0x80,0x04,\ 284 | 0x00,0x0e,0x00,0x00,0x2a,0x23,0x01,0x00,0x80,0x04,0x00,0x11,\ 285 | },\ 286 | },\ 287 | {0x00,\ 288 | {\ 289 | 0x1f,0x06,0x40,0x1c,0x00,0x00,0x2a,0x26,0x01,0x00,0x80,0x04,0x00,0x13,0x00,0x00,0x2a,0x37,0x01,0x00,\ 290 | 0x02,0x04,0x00,0x17,0x00,0x18,0x2a,0x38,0x01,0x00,0x80,0x04,\ 291 | },\ 292 | },\ 293 | {0x00,\ 294 | {\ 295 | 0x1b,0x06,0x40,0x38,0x00,0x1a,0x00,0x00,0x2a,0x39,0x01,0x00,0x10,0x04,0x00,0x1c,0x00,0x00,0x2a,0x19,\ 296 | 0x01,0x00,0x82,0x04,0x00,0x1f,0x00,0x20,\ 297 | },\ 298 | },\ 299 | {0x00,\ 300 | {\ 301 | 0x1b,0x06,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 302 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 303 | },\ 304 | },\ 305 | {0x00,\ 306 | {\ 307 | 0x06,0x06,0xf0,0x00,0x03,0x18,0x28,\ 308 | },\ 309 | },\ 310 | } 311 | 312 | #endif 313 | -------------------------------------------------------------------------------- /hal_platform.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guanix/arduino-nrf8001/6cb9f8cf2ddb2eec58ef1e45914a109af231525c/hal_platform.h -------------------------------------------------------------------------------- /heart_rate_sensor-redbear.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | nRF8001_Dx 6 | 7 | Device Information 8 | 180a 9 | 10 | Serial Number String 11 | 2a25 12 | 1587 13 | 1 14 | 15 | 4 16 | 1 17 | false 18 | 19 | false 20 | false 21 | false 22 | false 23 | false 24 | 25 | true 26 | true 27 | 28 | 0 29 | 30 | 31 | 32 | Manufacturer Name String 33 | 2a29 34 | NordicSemiconductor 35 | 1 36 | 37 | 20 38 | 1 39 | false 40 | 41 | false 42 | false 43 | false 44 | false 45 | false 46 | 47 | true 48 | true 49 | 50 | 0 51 | 52 | 53 | 54 | System ID 55 | 2a23 56 | 55AA55AA55AA55AA 57 | 0 58 | 59 | 8 60 | 1 61 | false 62 | 63 | false 64 | false 65 | false 66 | false 67 | false 68 | 69 | true 70 | true 71 | 72 | 0 73 | 74 | 75 | 76 | Firmware Revision String 77 | 2a26 78 | 01.1 79 | 1 80 | 81 | 4 82 | 1 83 | false 84 | 85 | false 86 | false 87 | false 88 | false 89 | false 90 | 91 | true 92 | true 93 | 94 | 0 95 | 96 | 97 | 98 | 99 | Heart Rate 100 | 180d 101 | 102 | Heart Rate Measurement 103 | 2a37 104 | 105 | 0 106 | 107 | 19 108 | 2 109 | false 110 | 111 | false 112 | false 113 | true 114 | false 115 | false 116 | 117 | false 118 | true 119 | 120 | 0 121 | 122 | 123 | 124 | Heart Rate Sensor Location 125 | 2a38 126 | 03 127 | 0 128 | 129 | 1 130 | 1 131 | false 132 | 133 | false 134 | false 135 | false 136 | false 137 | false 138 | 139 | true 140 | true 141 | 142 | 0 143 | 144 | 145 | 146 | Heart Rate Control Point 147 | 2a39 148 | 149 | 0 150 | 151 | 1 152 | 1 153 | false 154 | 155 | false 156 | true 157 | false 158 | false 159 | false 160 | 161 | false 162 | false 163 | 164 | 0 165 | 166 | 167 | 168 | 169 | Battery 170 | 180f 171 | 172 | Battery Level 173 | 2a19 174 | 64 175 | 0 176 | 177 | 1 178 | 1 179 | false 180 | 181 | false 182 | false 183 | true 184 | false 185 | false 186 | 187 | true 188 | true 189 | 190 | 0 191 | 192 | 193 | 194 | 195 | Nordic HRM V1.0 196 | 15 197 | false 198 | 0 199 | 0000 200 | 0 201 | 0 202 | 0 203 | 600 204 | 0 205 | 7 206 | 16 207 | 1f 208 | 1f 209 | 0 210 | 0 211 | 0 212 | 0 213 | 0 214 | 0 215 | 0 216 | 0 217 | 0 218 | 0 219 | 0 220 | 400 221 | 800 222 | 0 223 | 502 224 | 225 | 180d 226 | 227 | 228 | 229 | 17 230 | BC0DA5F8D155 231 | 232 | 233 | 18 234 | 01C8F36719C0 235 | 236 | 237 | 238 | 239 | 1 240 | 1 241 | 3 242 | 0 243 | 0 244 | 0 245 | 0 246 | true 247 | 248 | 249 | 220 250 | 10 251 | 1000 252 | 10 253 | 11.1111 254 | 1280 255 | 256 | 257 | -------------------------------------------------------------------------------- /nRF8001.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "nRF8001.h" 6 | 7 | #ifdef PROGMEM 8 | #undef PROGMEM 9 | #define PROGMEM __attribute__((section(".progmem.data"))) 10 | #endif 11 | 12 | nRFDeviceState nRF8001::getDeviceState() 13 | { 14 | return deviceState; 15 | } 16 | 17 | nRFCmd nRF8001::setup(hal_aci_data_t setup_msgs[], uint8_t nb_setup_msgs) 18 | { 19 | int previousMessageSent = -1; 20 | nrf_debug("Waiting for Standby state"); 21 | for (;;) { 22 | nrf_debug("Calling transmitReceive..."); 23 | transmitReceive(0, 0); 24 | if (deviceState == nRFSetupState || deviceState == PreSetup) { 25 | // Start the setup process 26 | nextSetupMessage = 0; 27 | break; 28 | } else { 29 | nrf_debug("Received message in setup but device not ready for setup"); 30 | } 31 | } 32 | 33 | for (;;) { 34 | if (nextSetupMessage >= 0 35 | && nextSetupMessage < nb_setup_msgs 36 | && nextSetupMessage > previousMessageSent) { 37 | #if NRF_DEBUG 38 | Serial.print(F("sending setup message number ")); 39 | Serial.println(nextSetupMessage); 40 | #endif 41 | transmitReceive((nRFCommand *)setup_msgs[nextSetupMessage] 42 | .buffer, 0); 43 | previousMessageSent = nextSetupMessage; 44 | } else if (nextSetupMessage >= 0 45 | && nextSetupMessage > previousMessageSent) { 46 | nrf_debug("we ran out of setup messages!"); 47 | deviceState = Invalid; 48 | return cmdSetupError; 49 | } else if (nextSetupMessage == -1) { 50 | nrf_debug("setup is complete!"); 51 | } 52 | 53 | if (deviceState == Standby) { 54 | nrf_debug("device in Standby state, returning"); 55 | return cmdSuccess; 56 | } 57 | 58 | transmitReceive(0, 0); 59 | } 60 | 61 | } 62 | 63 | nRF8001::nRF8001(uint8_t reset_pin_arg, 64 | uint8_t reqn_pin_arg, 65 | uint8_t rdyn_pin_arg) 66 | { 67 | nrf_debug("Initializing"); 68 | // Initialize data structures 69 | reset_pin = reset_pin_arg; 70 | reqn_pin = reqn_pin_arg; 71 | rdyn_pin = rdyn_pin_arg; 72 | 73 | deviceState = Initial; 74 | credits = 0; 75 | nextSetupMessage = -2; 76 | connectionStatus = Disconnected; 77 | 78 | // Zero all the handlers 79 | listener = 0; 80 | commandResponseHandler = 0; 81 | temperatureHandler = 0; 82 | batteryLevelHandler = 0; 83 | deviceVersionHandler = 0; 84 | deviceAddressHandler = 0; 85 | dynamicDataHandler = 0; 86 | connectedHandler = 0; 87 | disconnectedHandler = 0; 88 | bondStatusHandler = 0; 89 | keyRequestHandler = 0; 90 | pipeErrorHandler = 0; 91 | dataReceivedHandler = 0; 92 | dataAckHandler = 0; 93 | 94 | // Prepare pins and start SPI 95 | pinMode(reqn_pin, OUTPUT); 96 | pinMode(rdyn_pin, INPUT); 97 | digitalWrite(rdyn_pin, HIGH); 98 | digitalWrite(reqn_pin, HIGH); 99 | 100 | // Only do this if reset_pin is not -1 101 | if (reset_pin != -1) { 102 | pinMode(reset_pin, OUTPUT); 103 | digitalWrite(reset_pin, LOW); 104 | delayMicroseconds(1); 105 | digitalWrite(reset_pin, HIGH); 106 | } 107 | 108 | // inialize SPI 109 | pinMode(SCK, OUTPUT); 110 | pinMode(MOSI, OUTPUT); 111 | pinMode(SS, OUTPUT); 112 | 113 | digitalWrite(SCK, LOW); 114 | digitalWrite(MOSI, LOW); 115 | digitalWrite(SS, HIGH); 116 | 117 | // SPI mode 0; /16 clock divider 118 | SPI.setDataMode(SPI_MODE0); 119 | SPI.setBitOrder(LSBFIRST); 120 | SPI.setClockDivider(SPI_CLOCK_DIV16); 121 | SPI.begin(); 122 | 123 | // Load up the first setup message and start interrupts 124 | } 125 | 126 | void nRF8001::addressToString(char *str, uint8_t *address) 127 | { 128 | for (int i = NRF_ADDRESS_LENGTH - 1; i >= 0; i--) { 129 | uint8_t c = address[i]; 130 | sprintf(str + (NRF_ADDRESS_LENGTH-1-i)*2, "%02x", c); 131 | } 132 | } 133 | 134 | void nRF8001::debugAddress(uint8_t *address) 135 | { 136 | Serial.print(F("0x")); 137 | // Addresses are NRF_ADDRESS_LENGTH long, MSB to LSB 138 | char buf[NRF_ADDRESS_LENGTH*2+1]; 139 | addressToString(buf, address); 140 | Serial.print(buf); 141 | } 142 | 143 | void nRF8001::debugEvent(nRFEvent *event) 144 | { 145 | Serial.print(F("EVENT debug=")); 146 | Serial.print(event->debug); 147 | Serial.print(F(" length=")); 148 | Serial.print(event->length); 149 | Serial.print(F(" event=")); 150 | 151 | switch (event->event) { 152 | case NRF_DEVICESTARTEDEVENT: 153 | Serial.println(F("DeviceStartedEvent")); 154 | 155 | Serial.print(F("Operating mode: ")); 156 | switch (event->msg.deviceStarted.operatingMode) { 157 | case 0x01: 158 | Serial.println(F("Test")); 159 | break; 160 | case 0x02: 161 | Serial.println(F("Setup")); 162 | break; 163 | case 0x03: 164 | Serial.println(F("Standby")); 165 | break; 166 | default: 167 | Serial.println(event->msg.deviceStarted.operatingMode, HEX); 168 | break; 169 | } 170 | 171 | Serial.print(F("Hardware error: ")); 172 | switch (event->msg.deviceStarted.hwError) { 173 | case 0x00: 174 | Serial.println(F("No error")); 175 | break; 176 | case 0x01: 177 | Serial.println(F("Fatal error")); 178 | break; 179 | default: 180 | Serial.println(event->msg.deviceStarted.hwError, HEX); 181 | break; 182 | } 183 | 184 | Serial.print(F("DataCreditAvailable: ")); 185 | Serial.println(event->msg.deviceStarted.dataCreditAvailable); 186 | break; 187 | case NRF_ECHOEVENT: 188 | Serial.println(F("EchoEvent")); 189 | break; 190 | case NRF_HARDWAREERROREVENT: 191 | Serial.print(F("HardwareErrorEvent! Line: ")); 192 | Serial.print(event->msg.hardwareError.lineNo); 193 | Serial.print(F(", File: ")); 194 | Serial.println((char *)event->msg.hardwareError.fileName); 195 | break; 196 | case NRF_COMMANDRESPONSEEVENT: 197 | Serial.println(F("CommandResponseEvent")); 198 | 199 | Serial.print(F("Status: ")); 200 | switch (event->msg.commandResponse.status) { 201 | case NRF_STATUS_SUCCESS: 202 | Serial.println(F("Success")); 203 | break; 204 | case NRF_STATUS_TRANSACTION_CONTINUE: 205 | Serial.println(F("Transaction continuation status")); 206 | break; 207 | case NRF_STATUS_TRANSACTION_COMPLETE: 208 | Serial.println(F("Transaction completed")); 209 | break; 210 | case NRF_STATUS_EXTENDED: 211 | Serial.println(F("Extended status, further checks needed")); 212 | break; 213 | case NRF_STATUS_ERROR_UNKNOWN: 214 | Serial.println(F("Unknown error")); 215 | break; 216 | case NRF_STATUS_ERROR_INTERNAL: 217 | Serial.println(F("Internal error")); 218 | break; 219 | case NRF_STATUS_ERROR_CMD_UNKNOWN: 220 | Serial.println(F("Unknown command")); 221 | break; 222 | case NRF_STATUS_ERROR_DEVICE_STATE_INVALID: 223 | Serial.println(F("Command invalid in the current device state")); 224 | break; 225 | case NRF_STATUS_ERROR_INVALID_LENGTH: 226 | Serial.println(F("Invalid length")); 227 | break; 228 | case NRF_STATUS_ERROR_INVALID_PARAMETER: 229 | Serial.println(F("Invalid input parameters")); 230 | break; 231 | case NRF_STATUS_ERROR_BUSY: 232 | Serial.println(F("Busy")); 233 | break; 234 | case NRF_STATUS_ERROR_INVALID_DATA: 235 | Serial.println(F("Invalid data format or contents")); 236 | break; 237 | case NRF_STATUS_ERROR_CRC_MISMATCH: 238 | Serial.println(F("CRC mismatch")); 239 | break; 240 | case NRF_STATUS_ERROR_UNSUPPORTED_SETUP_FORMAT: 241 | Serial.println(F("Unsupported setup format")); 242 | break; 243 | case NRF_STATUS_ERROR_INVALID_SEQ_NO: 244 | Serial.println(F( 245 | "Invalid sequence number during " 246 | "a write dynamic data sequence")); 247 | break; 248 | case NRF_STATUS_ERROR_SETUP_LOCKED: 249 | Serial.println(F( 250 | "Setup data is locked and cannot be modified")); 251 | break; 252 | case NRF_STATUS_ERROR_LOCK_FAILED: 253 | Serial.println(F( 254 | "Setup error due to lock verification failure")); 255 | break; 256 | case NRF_STATUS_ERROR_BOND_REQUIRED: 257 | Serial.println(F( 258 | "Bond required: Local service pipes need " 259 | "bonded/trusted peer")); 260 | break; 261 | case NRF_STATUS_ERROR_REJECTED: 262 | Serial.println(F( 263 | "Command rejected as a transaction is still pending")); 264 | break; 265 | case NRF_STATUS_ERROR_DATA_SIZE: 266 | Serial.println(F( 267 | "Pipe Error Event: Data size exceeds size specified " 268 | "for pipe, Transmit failed")); 269 | break; 270 | case NRF_STATUS_ERROR_PIPE_INVALID: 271 | Serial.println(F( 272 | "Pipe Error Event: Transmit failed, " 273 | "Invalid or unavailable Pipe number or " 274 | "unknown pipe type")); 275 | break; 276 | case NRF_STATUS_ERROR_CREDIT_NOT_AVAILABLE: 277 | Serial.println(F( 278 | "Pipe Error Event: Credit not available")); 279 | break; 280 | case NRF_STATUS_ERROR_PEER_ATT_ERROR: 281 | Serial.println(F( 282 | "Pipe Error Event: Peer device has sent an error on " 283 | "an pipe operation on the remote characteristic")); 284 | break; 285 | case NRF_STATUS_ERROR_ADVT_TIMEOUT: 286 | Serial.println(F( 287 | "Connection was not established before the BTLE " 288 | "advertising was stopped")); 289 | break; 290 | case NRF_STATUS_ERROR_PEER_SMP_ERROR: 291 | Serial.println(F( 292 | "Remote device triggered a Security Manager Protocol " 293 | "error")); 294 | break; 295 | case NRF_STATUS_ERROR_PIPE_TYPE_INVALID: 296 | Serial.println(F( 297 | "Pipe Error Event: Pipe type invalid for the " 298 | "selected operation")); 299 | break; 300 | case NRF_STATUS_ERROR_PIPE_STATE_INVALID: 301 | Serial.println(F("Pipe Error Event: Pipe state invalid " 302 | "for the selected operation")); 303 | break; 304 | case NRF_STATUS_ERROR_INVALID_KEY_SIZE: 305 | Serial.println(F("Invalid key size provided")); 306 | break; 307 | case NRF_STATUS_ERROR_INVALID_KEY_DATA: 308 | Serial.println(F("Invalid key data provided")); 309 | break; 310 | default: 311 | Serial.println(event->msg.commandResponse.status, HEX); 312 | break; 313 | } 314 | 315 | Serial.print("Op: "); 316 | switch (event->msg.commandResponse.opcode) { 317 | case NRF_TEST_OP: 318 | Serial.println(F("Test")); 319 | break; 320 | case NRF_GETDEVICEVERSION_OP: 321 | Serial.println(F("GetDeviceVersion")); 322 | Serial.print(F("ConfigurationID=")); 323 | Serial.print(event->msg.commandResponse.data 324 | .getDeviceVersion.configurationId); 325 | Serial.print(F(" ACIVersion=")); 326 | Serial.print(event->msg.commandResponse.data 327 | .getDeviceVersion.aciVersion); 328 | Serial.print(F(" SetupFormat=")); 329 | Serial.print(event->msg.commandResponse.data 330 | .getDeviceVersion.setupFormat); 331 | Serial.print(F(" SetupID=")); 332 | Serial.print(event->msg.commandResponse.data 333 | .getDeviceVersion.setupId); 334 | Serial.print(F(" ConfigurationStatus=")); 335 | if (event->msg.commandResponse.data.getDeviceVersion 336 | .configurationStatus == 1) { 337 | Serial.println(F("SetupLocked")); 338 | } else { 339 | Serial.println(F("SetupOpen")); 340 | } 341 | break; 342 | case NRF_WAKEUP_OP: 343 | Serial.println(F("Wakeup")); 344 | break; 345 | case NRF_GETBATTERYLEVEL_OP: 346 | Serial.println(F("GetBatteryLevel")); 347 | Serial.print(event->msg.commandResponse 348 | .data.voltage*3.52, 2); 349 | Serial.println(F("mV")); 350 | break; 351 | case NRF_GETTEMPERATURE_OP: 352 | Serial.println(F("GetTemperature")); 353 | Serial.print(event->msg.commandResponse 354 | .data.temperature/4.0, 2); 355 | Serial.println(F(" C")); 356 | break; 357 | case NRF_SETUP_OP: 358 | Serial.println(F("Setup")); 359 | break; 360 | case NRF_SETTXPOWER_OP: 361 | Serial.println(F("SetTxPower")); 362 | break; 363 | case NRF_GETDEVICEADDRESS_OP: 364 | Serial.println(F("GetDeviceAddress")); 365 | Serial.print(F("Address: ")); 366 | debugAddress(event->msg.commandResponse.data 367 | .getDeviceAddress.deviceAddress); 368 | Serial.println(""); 369 | break; 370 | case NRF_CONNECT_OP: 371 | Serial.println(F("Connect")); 372 | break; 373 | case NRF_RADIORESET_OP: 374 | Serial.println(F("RadioReset")); 375 | break; 376 | case NRF_BOND_OP: 377 | Serial.println(F("Bond")); 378 | break; 379 | case NRF_DISCONNECT_OP: 380 | Serial.println(F("Disconnect")); 381 | break; 382 | case NRF_CHANGETIMINGREQUEST_OP: 383 | Serial.println(F("ChangeTimingRequest")); 384 | break; 385 | case NRF_OPENREMOTEPIPE_OP: 386 | Serial.println(F("OpenRemotePipe")); 387 | break; 388 | case NRF_CLOSEREMOTEPIPE_OP: 389 | Serial.println(F("CloseRemotePipe")); 390 | break; 391 | case NRF_DTMCOMMAND_OP: 392 | Serial.println(F("DtmCommand")); 393 | Serial.print(F("DTM data: ")); 394 | Serial.println(event->msg.commandResponse.data.dtmEvent, 395 | HEX); 396 | break; 397 | case NRF_READDYNAMICDATA_OP: 398 | Serial.println(F("ReadDynamicData")); 399 | Serial.print(F("Sequence no=")); 400 | Serial.println(event->msg.commandResponse.data 401 | .readDynamicData.sequenceNo); 402 | Serial.println(F("TODO: data here")); 403 | break; 404 | case NRF_WRITEDYNAMICDATA_OP: 405 | Serial.println(F("WriteDynamicData")); 406 | break; 407 | case NRF_SETAPPLICATIONLATENCY_OP: 408 | Serial.println(F("SetApplLatency")); 409 | break; 410 | case NRF_SETKEY_OP: 411 | Serial.println(F("SetKey")); 412 | break; 413 | case NRF_OPENADVPIPE_OP: 414 | Serial.println(F("OpenAdvPipe")); 415 | break; 416 | case NRF_BROADCAST_OP: 417 | Serial.println(F("Broadcast")); 418 | break; 419 | case NRF_BONDSECREQUEST_OP: 420 | Serial.println(F("BondSecurityRequest")); 421 | break; 422 | case NRF_DIRECTEDCONNECT_OP: 423 | Serial.println(F("DirectedConnect")); 424 | break; 425 | case NRF_SETLOCALDATA_OP: 426 | Serial.println(F("SetLocalData")); 427 | break; 428 | default: 429 | Serial.print(event->msg.commandResponse.opcode, HEX); 430 | break; 431 | } 432 | break; 433 | case NRF_CONNECTEDEVENT: 434 | Serial.println(F("ConnectedEvent")); 435 | Serial.print(F("Address type: ")); 436 | switch (event->msg.connected.addressType) { 437 | case 0x01: 438 | Serial.println(F("Public address")); 439 | break; 440 | case 0x02: 441 | Serial.println(F("Random static address")); 442 | break; 443 | case 0x03: 444 | Serial.println(F("Random private address (resolvable)")); 445 | break; 446 | case 0x04: 447 | Serial.println( 448 | F("Random private adddress (non-resolvable)")); 449 | break; 450 | default: 451 | Serial.println(event->msg.connected.addressType, HEX); 452 | break; 453 | } 454 | 455 | Serial.print(F("Peer address: ")); 456 | debugAddress(event->msg.connected.peerAddress); 457 | Serial.println(); 458 | 459 | Serial.print(F("Connection interval: ")); 460 | Serial.print(event->msg.connected.connectionInterval/1.25, 2); 461 | Serial.println("ms"); 462 | 463 | Serial.print(F("Slave latency: ")); 464 | Serial.println(event->msg.connected.slaveLatency); 465 | 466 | Serial.print(F("Supervision timeout: ")); 467 | Serial.println(event->msg.connected.supervisionTimeout); 468 | 469 | Serial.print(F("Master clock accuracy: ")); 470 | switch (event->msg.connected.masterClockAccuracy) { 471 | case 0x00: 472 | Serial.println(F("500 ppm")); 473 | break; 474 | case 0x01: 475 | Serial.println(F("250 ppm")); 476 | break; 477 | case 0x02: 478 | Serial.println(F("150 ppm")); 479 | break; 480 | case 0x03: 481 | Serial.println(F("100 ppm")); 482 | break; 483 | case 0x04: 484 | Serial.println(F("75 ppm")); 485 | break; 486 | case 0x05: 487 | Serial.println(F("50 ppm")); 488 | break; 489 | case 0x06: 490 | Serial.println(F("30 ppm")); 491 | break; 492 | case 0x07: 493 | Serial.println(F("20 ppm")); 494 | break; 495 | default: 496 | Serial.println(event->msg.connected.masterClockAccuracy, 497 | HEX); 498 | break; 499 | } 500 | break; 501 | case NRF_DISCONNECTEDEVENT: { 502 | Serial.println(F("DisconnectedEvent")); 503 | 504 | Serial.print(F("ACI status: ")); 505 | switch (event->msg.disconnected.aciStatus) { 506 | case 0x03: 507 | Serial.println( 508 | F("Check the Bluetooth low energy status code")); 509 | break; 510 | case 0x93: 511 | Serial.println(F( 512 | "Timeout while advertising, " 513 | "unable to establish connection")); 514 | break; 515 | case 0x8d: 516 | Serial.println(F( 517 | "Bond required to proceed with connection")); 518 | break; 519 | default: 520 | Serial.println(event->msg.disconnected.aciStatus, HEX); 521 | break; 522 | } 523 | 524 | Serial.print(F("BtLeStatus: 0x")); 525 | uint8_t btLeStatus = event->msg.disconnected.btLeStatus; 526 | if (btLeStatus < 0x10) { 527 | Serial.print(F("0")); 528 | } 529 | Serial.println(btLeStatus, HEX); 530 | break; 531 | } 532 | case NRF_BONDSTATUSEVENT: 533 | Serial.println(F("BondStatusEvent")); 534 | Serial.println(F("TODO: bond status data")); 535 | break; 536 | case NRF_PIPESTATUSEVENT: 537 | Serial.println(F("PipeStatusEvent")); 538 | 539 | Serial.print(F("Open: ")); 540 | for (int i = 0; i < 64; i++) { 541 | if (event->msg.pipeStatus.pipesOpen & ((uint64_t)1)<msg.pipeStatus.pipesClosed & ((uint64_t)1)<msg.timing.connectionInterval/1.25, 2); 562 | 563 | Serial.print(F("Slave latency: ")); 564 | Serial.println(event->msg.timing.slaveLatency, 2); 565 | 566 | Serial.print(F("Supervision timeout: ")); 567 | Serial.print(event->msg.timing.supervisionTimeout*10.0, 2); 568 | Serial.println("ms"); 569 | break; 570 | case NRF_DISPLAYKEYEVENT: 571 | Serial.println(F("DisplayKeyEvent")); 572 | Serial.println(F("Passkey: ")); 573 | for (int i = 0; i < NRF_PASSKEY_LENGTH; i++) { 574 | Serial.print(event->msg.passkey[i]); 575 | } 576 | Serial.println(); 577 | break; 578 | case NRF_KEYREQUESTEVENT: 579 | Serial.println(F("KeyRequestEvent")); 580 | break; 581 | case NRF_DATACREDITEVENT: 582 | Serial.print(F("DataCreditEvent credits=")); 583 | Serial.println(event->msg.dataCredits); 584 | break; 585 | case NRF_PIPEERROREVENT: 586 | Serial.println(F("PipeErrorEvent")); 587 | Serial.print(F("Pipe: ")); 588 | Serial.println(event->msg.pipeError.servicePipeNo); 589 | 590 | Serial.print("Error code: 0x"); 591 | Serial.println(event->msg.pipeError.errorCode, HEX); 592 | break; 593 | case NRF_DATARECEIVEDEVENT: 594 | Serial.println(F("DataReceivedEvent")); 595 | Serial.print(F("Pipe: ")); 596 | Serial.println(event->msg.dataReceived.servicePipeNo); 597 | break; 598 | case NRF_DATAACKEVENT: 599 | Serial.println(F("DataAckEvent")); 600 | Serial.print(F("Pipe: ")); 601 | Serial.println(event->msg.servicePipeNo); 602 | break; 603 | default: 604 | Serial.print(F("Unknown ")); 605 | Serial.println(event->event); 606 | break; 607 | } 608 | } 609 | 610 | // Transmit a command, and simultaneously receive a message from nRF8001 if 611 | // there is one. To just receive without transmitting anything, call this 612 | // function with a NULL argument. 613 | nRFTxStatus nRF8001::transmitReceive(nRFCommand *txCmd, uint16_t timeout) 614 | { 615 | // Buffer that we will receive into 616 | uint8_t rxBuffer[sizeof(nRFEvent)]; 617 | nRFEvent *rxEvent = (nRFEvent *)rxBuffer; 618 | memset(&rxBuffer, 0, sizeof(nRFEvent)); 619 | 620 | uint8_t *txBuffer = (uint8_t *)txCmd; 621 | 622 | // Transmit length 623 | uint8_t txLength, txCommand; 624 | if (txCmd != NULL) { 625 | txLength = txCmd->length; 626 | txCommand = txCmd->command; 627 | } else { 628 | txLength = 0; 629 | txCommand = 0; 630 | } 631 | 632 | assert(txLength <= NRF_MAX_PACKET_LENGTH); 633 | 634 | // Enough credits? 635 | if (txLength && 636 | (txCmd->command == NRF_SENDDATA_OP 637 | || txCmd->command == NRF_REQUESTDATA_OP 638 | //|| txCmd->command == NRF_SETLOCALDATA_OP 639 | || txCmd->command == NRF_SENDDATAACK_OP 640 | || txCmd->command == NRF_SENDDATANACK_OP)) { 641 | if (credits < 1) { 642 | nrf_debug("transmitReceive fail, not enough credits"); 643 | return InsufficientCredits; 644 | } 645 | 646 | // Use a credit 647 | credits--; 648 | } 649 | 650 | // Bring REQN low 651 | if (txLength > 0) { 652 | digitalWrite(reqn_pin, LOW); 653 | #if NRF_VERBOSE_DEBUG 654 | Serial.print(F("transmitReceive: called with transmission, command ")); 655 | Serial.println(txCommand); 656 | #endif 657 | } else { 658 | #if NRF_VERBOSE_DEBUG 659 | nrf_debug("transmitReceive: called in polling mode"); 660 | #endif 661 | } 662 | 663 | // TODO: Timeout 664 | 665 | if (txLength > 0 || timeout == 0) { 666 | // Wait for RDYN low indefinitely 667 | #if NRF_VERBOSE_DEBUG 668 | nrf_debug("waiting for RDYN low indefinitely"); 669 | #endif 670 | while (digitalRead(rdyn_pin) == HIGH); 671 | } else { 672 | #if NRF_VERBOSE_DEBUG 673 | Serial.print(F("polling for ")); 674 | Serial.print(timeout); 675 | Serial.println(F("ms")); 676 | #endif 677 | uint8_t rdy = 0; 678 | // Wait for RDYN low for 1 ms at a time 679 | for (uint16_t waitPeriods = 0; waitPeriods < timeout; waitPeriods++) { 680 | if (digitalRead(rdyn_pin) == LOW) { 681 | rdy = 1; 682 | break; 683 | } else { 684 | delay(1); 685 | } 686 | } 687 | 688 | if (!rdy) { 689 | return Timeout; 690 | } 691 | } 692 | 693 | if (txLength == 0) { 694 | digitalWrite(reqn_pin, LOW); 695 | } 696 | 697 | #if NRF_VERBOSE_DEBUG 698 | nrf_debug("Ready to transmitReceive full duplex!"); 699 | #endif 700 | 701 | // Send length and command bytes, 702 | // receive debug and length bytes 703 | rxEvent->debug = SPI.transfer(txLength); 704 | rxEvent->length = SPI.transfer(txCommand); 705 | 706 | assert(rxEvent->length <= NRF_MAX_PACKET_LENGTH); 707 | 708 | // nextByte points to the next byte to be transferred in 709 | // txBuffer, or the next byte to be received in rxBuffer 710 | // For TX, packets are data + 1 byte (length) 711 | // For RX, packets are data + 2 bytes (debug and length) 712 | for (uint8_t nextByte = 2; 713 | nextByte < txLength + 1 || nextByte < rxEvent->length + 2; 714 | nextByte++) { 715 | uint8_t c; 716 | if (nextByte < txLength + 1) { 717 | c = SPI.transfer(txBuffer[nextByte]); // transmit 718 | } else { 719 | c = SPI.transfer(0); // receive only 720 | } 721 | 722 | if (nextByte < rxEvent->length + 2) { // receive 723 | rxBuffer[nextByte] = c; 724 | } 725 | } 726 | 727 | // Bring REQN high 728 | digitalWrite(reqn_pin, HIGH); 729 | // Wait for RDYN high 730 | while (digitalRead(rdyn_pin) == LOW); 731 | 732 | // Return immediately if we didn't receive anything 733 | if (!rxEvent->length) { 734 | return Success; 735 | } 736 | 737 | // Handle response 738 | switch (rxEvent->event) { 739 | case NRF_DEVICESTARTEDEVENT: 740 | credits = rxEvent->msg.deviceStarted.dataCreditAvailable; 741 | 742 | switch (rxEvent->msg.deviceStarted.operatingMode) { 743 | case 0x01: 744 | deviceState = Test; 745 | break; 746 | case 0x02: 747 | deviceState = nRFSetupState; 748 | break; 749 | case 0x03: 750 | if (deviceState == Initial) { 751 | // Before we are setup, Nordic calls it "Standby", 752 | // but that's different from post-setup Standby 753 | deviceState = PreSetup; 754 | } else { 755 | deviceState = Standby; 756 | } 757 | } 758 | break; 759 | case NRF_COMMANDRESPONSEEVENT: { 760 | if (rxEvent->msg.commandResponse.status != 0x00) { 761 | #if NRF_DEBUG 762 | Serial.print(F("non-success command response event: 0x")); 763 | Serial.println(rxEvent->msg.commandResponse.status); 764 | #endif 765 | if (commandResponseHandler != 0) { 766 | commandResponseHandler( 767 | rxEvent->msg.commandResponse.opcode, 768 | rxEvent->msg.commandResponse.status); 769 | } 770 | 771 | if (rxEvent->msg.commandResponse.opcode == NRF_SETUP_OP && 772 | rxEvent->msg.commandResponse.status == 773 | NRF_STATUS_TRANSACTION_CONTINUE) { 774 | nextSetupMessage++; 775 | nrf_debug("ready for next setup message"); 776 | } else if (rxEvent->msg.commandResponse.opcode == NRF_SETUP_OP && 777 | rxEvent->msg.commandResponse.status == 778 | NRF_STATUS_TRANSACTION_COMPLETE) { 779 | nrf_debug("setup complete"); 780 | nextSetupMessage = -1; 781 | } 782 | break; 783 | } 784 | 785 | switch (rxEvent->msg.commandResponse.opcode) { 786 | // We only do handling of some of these messages 787 | case NRF_SETUP_OP: 788 | break; 789 | case NRF_GETTEMPERATURE_OP: 790 | if (temperatureHandler != 0) { 791 | temperatureHandler(rxEvent->msg.commandResponse 792 | .data.temperature / 4.0); 793 | } 794 | break; 795 | case NRF_GETBATTERYLEVEL_OP: 796 | if (batteryLevelHandler != 0) { 797 | batteryLevelHandler(rxEvent->msg.commandResponse 798 | .data.voltage * 0.00352); 799 | } 800 | break; 801 | case NRF_GETDEVICEVERSION_OP: 802 | if (deviceVersionHandler != 0) { 803 | deviceVersionHandler( 804 | rxEvent->msg.commandResponse 805 | .data.getDeviceVersion.configurationId, 806 | rxEvent->msg.commandResponse 807 | .data.getDeviceVersion.aciVersion, 808 | rxEvent->msg.commandResponse 809 | .data.getDeviceVersion.setupFormat, 810 | rxEvent->msg.commandResponse 811 | .data.getDeviceVersion.configurationStatus); 812 | } 813 | break; 814 | case NRF_GETDEVICEADDRESS_OP: 815 | if (deviceAddressHandler != 0) { 816 | deviceAddressHandler( 817 | rxEvent->msg.commandResponse 818 | .data.getDeviceAddress.deviceAddress, 819 | rxEvent->msg.commandResponse 820 | .data.getDeviceAddress.addressType); 821 | } 822 | break; 823 | case NRF_READDYNAMICDATA_OP: 824 | if (dynamicDataHandler != 0) { 825 | dynamicDataHandler( 826 | rxEvent->msg.commandResponse 827 | .data.readDynamicData.sequenceNo, 828 | rxEvent->msg.commandResponse 829 | .data.readDynamicData.dynamicData); 830 | } 831 | break; 832 | default: 833 | if (commandResponseHandler != 0) { 834 | commandResponseHandler( 835 | rxEvent->msg.commandResponse.opcode, 836 | rxEvent->msg.commandResponse.status); 837 | } 838 | break; 839 | } 840 | 841 | // Dispatch event 842 | break; 843 | } 844 | case NRF_CONNECTEDEVENT: 845 | connectionStatus = Connected; 846 | if (connectedHandler != 0) { 847 | connectedHandler( 848 | rxEvent->msg.connected.addressType, 849 | rxEvent->msg.connected.peerAddress, 850 | 0); 851 | // TODO: put in other data 852 | } 853 | break; 854 | case NRF_DISCONNECTEDEVENT: 855 | connectionStatus = Disconnected; 856 | pipesOpen = 0; 857 | if (disconnectedHandler != 0) { 858 | disconnectedHandler( 859 | rxEvent->msg.disconnected.aciStatus, 860 | rxEvent->msg.disconnected.btLeStatus); 861 | } 862 | break; 863 | case NRF_DATACREDITEVENT: 864 | credits += rxEvent->msg.dataCredits; 865 | break; 866 | case NRF_PIPESTATUSEVENT: 867 | pipesOpen = rxEvent->msg.pipeStatus.pipesOpen; 868 | break; 869 | case NRF_BONDSTATUSEVENT: 870 | if (bondStatusHandler != 0) { 871 | bondStatusHandler(0); 872 | } 873 | // TODO: put in bond status data 874 | break; 875 | case NRF_KEYREQUESTEVENT: 876 | if (keyRequestHandler != 0) { 877 | keyRequestHandler(rxEvent->msg.keyType); 878 | } 879 | break; 880 | case NRF_PIPEERROREVENT: 881 | if (pipeErrorHandler != 0) { 882 | pipeErrorHandler( 883 | rxEvent->msg.pipeError.servicePipeNo, 884 | rxEvent->msg.pipeError.errorCode, 885 | rxEvent->msg.pipeError.rawData); 886 | } 887 | break; 888 | case NRF_DATARECEIVEDEVENT: 889 | if (dataReceivedHandler != 0) { 890 | dataReceivedHandler( 891 | rxEvent->msg.dataReceived.servicePipeNo, 892 | rxEvent->msg.dataReceived.data); 893 | } 894 | break; 895 | case NRF_DATAACKEVENT: 896 | if (dataAckHandler != 0) { 897 | dataAckHandler(rxEvent->msg.servicePipeNo); 898 | } 899 | break; 900 | case NRF_HARDWAREERROREVENT: 901 | break; 902 | default: { 903 | break; 904 | } 905 | } 906 | 907 | // Dispatch event 908 | if (listener != 0) { 909 | // first to generic handler 910 | listener(rxEvent); 911 | } 912 | 913 | return Success; 914 | } 915 | 916 | // Informational functions 917 | 918 | uint8_t nRF8001::creditsAvailable() 919 | { 920 | return credits; 921 | } 922 | 923 | uint8_t nRF8001::isConnected() { 924 | return connectionStatus == Connected; 925 | } 926 | 927 | nRFConnectionStatus nRF8001::getConnectionStatus() 928 | { 929 | return connectionStatus; 930 | } 931 | 932 | // Receive functions 933 | 934 | nRFTxStatus nRF8001::poll(uint16_t timeout) 935 | { 936 | return transmitReceive(0, timeout); 937 | } 938 | 939 | nRFTxStatus nRF8001::poll() 940 | { 941 | return transmitReceive(0, 0); 942 | } 943 | 944 | uint8_t nRF8001::isPipeOpen(nRFPipe servicePipeNo) 945 | { 946 | return (pipesOpen & ((uint64_t)1) << servicePipeNo) != 0; 947 | } 948 | 949 | // Transmit functions 950 | 951 | nRFTxStatus nRF8001::transmitCommand(uint8_t command) 952 | { 953 | if (deviceState != Standby) { 954 | nrf_debug("device not in Standby state"); 955 | return InvalidState; 956 | } 957 | 958 | nrf_debug("calling transmitCommand"); 959 | 960 | nRFCommand cmd; 961 | cmd.length = 1; 962 | cmd.command = command; 963 | return transmitReceive(&cmd, 0); 964 | } 965 | 966 | nRFTxStatus nRF8001::transmitPipeCommand(uint8_t command, nRFPipe pipe) 967 | { 968 | if (deviceState != Standby) { 969 | nrf_debug("device not in Standby state"); 970 | return InvalidState; 971 | } 972 | 973 | nrf_debug("calling transmitPipeCommand"); 974 | 975 | nRFCommand cmd; 976 | cmd.length = 2; 977 | cmd.command = command; 978 | cmd.content.servicePipeNo = pipe; 979 | return transmitReceive(&cmd, 0); 980 | } 981 | 982 | nRFTxStatus nRF8001::test(uint8_t feature) 983 | { 984 | if (deviceState != Standby) { 985 | nrf_debug("device not in Standby state"); 986 | return InvalidState; 987 | } 988 | 989 | nrf_debug("calling test"); 990 | 991 | nRFCommand cmd; 992 | cmd.length = 2; 993 | cmd.command = NRF_TEST_OP; 994 | cmd.content.testFeature = feature; 995 | return transmitReceive(&cmd, 0); 996 | } 997 | 998 | nRFTxStatus nRF8001::sleep() 999 | { 1000 | return transmitCommand(NRF_SLEEP_OP); 1001 | } 1002 | 1003 | nRFTxStatus nRF8001::echo(nRFLen dataLength, uint8_t *data) 1004 | { 1005 | if (deviceState != Standby) { 1006 | nrf_debug("device not in Standby state"); 1007 | return InvalidState; 1008 | } 1009 | 1010 | if (dataLength > NRF_MAX_ECHO_MESSAGE_LENGTH) { 1011 | nrf_debug("data too long"); 1012 | return DataTooLong; 1013 | } 1014 | 1015 | nrf_debug("calling echo"); 1016 | 1017 | nRFCommand cmd; 1018 | cmd.length = dataLength + 1; 1019 | cmd.command = NRF_ECHO_OP; 1020 | memcpy(cmd.content.echoData, data, dataLength); 1021 | return transmitReceive(&cmd, 0); 1022 | } 1023 | 1024 | nRFTxStatus nRF8001::wakeup() 1025 | { 1026 | return transmitCommand(NRF_WAKEUP_OP); 1027 | } 1028 | 1029 | nRFTxStatus nRF8001::getBatteryLevel() 1030 | { 1031 | return transmitCommand(NRF_GETBATTERYLEVEL_OP); 1032 | } 1033 | 1034 | nRFTxStatus nRF8001::getTemperature() 1035 | { 1036 | return transmitCommand(NRF_GETTEMPERATURE_OP); 1037 | } 1038 | 1039 | nRFTxStatus nRF8001::setTxPower(uint8_t powerLevel) 1040 | { 1041 | if (deviceState != Standby) { 1042 | nrf_debug("device not in Standby state"); 1043 | return InvalidState; 1044 | } 1045 | 1046 | nRFCommand cmd; 1047 | cmd.length = 2; 1048 | cmd.command = NRF_SETTXPOWER_OP; 1049 | cmd.content.radioTxPowerLevel = powerLevel; 1050 | return transmitReceive(&cmd, 0); 1051 | } 1052 | 1053 | nRFTxStatus nRF8001::getDeviceAddress() 1054 | { 1055 | return transmitCommand(NRF_GETDEVICEADDRESS_OP); 1056 | } 1057 | 1058 | nRFTxStatus nRF8001::getDeviceVersion() 1059 | { 1060 | return transmitCommand(NRF_GETDEVICEVERSION_OP); 1061 | } 1062 | 1063 | nRFTxStatus nRF8001::connect(uint16_t timeout, uint16_t advInterval) 1064 | { 1065 | if (deviceState != Standby) { 1066 | nrf_debug("device not in Standby state"); 1067 | return InvalidState; 1068 | } 1069 | 1070 | connectionStatus = Connecting; 1071 | 1072 | nrf_debug("connecting"); 1073 | 1074 | nRFCommand cmd; 1075 | cmd.length = 5; 1076 | cmd.command = NRF_CONNECT_OP; 1077 | cmd.content.connect.timeout = timeout; 1078 | cmd.content.connect.advInterval = advInterval; 1079 | return transmitReceive(&cmd, 0); 1080 | } 1081 | 1082 | nRFTxStatus nRF8001::radioReset() 1083 | { 1084 | nrf_debug("sending radio reset"); 1085 | nRFCommand cmd; 1086 | cmd.length = 1; 1087 | cmd.command = NRF_RADIORESET_OP; 1088 | return transmitReceive(&cmd, 0); 1089 | } 1090 | 1091 | nRFTxStatus nRF8001::bond(uint16_t timeout, uint16_t advInterval) 1092 | { 1093 | if (deviceState != Standby) { 1094 | nrf_debug("device not in Standby state"); 1095 | return InvalidState; 1096 | } 1097 | 1098 | nRFCommand cmd; 1099 | cmd.length = 5; 1100 | cmd.command = NRF_BOND_OP; 1101 | cmd.content.bond.timeout = timeout; 1102 | cmd.content.bond.advInterval = advInterval; 1103 | return transmitReceive(&cmd, 0); 1104 | } 1105 | 1106 | nRFTxStatus nRF8001::disconnect(uint8_t reason) 1107 | { 1108 | if (deviceState != Standby || connectionStatus != Connected) { 1109 | nrf_debug("device not in standby and connected state"); 1110 | return InvalidState; 1111 | } 1112 | 1113 | nRFCommand cmd; 1114 | cmd.length = 2; 1115 | cmd.command = NRF_DISCONNECT_OP; 1116 | cmd.content.disconnectReason = reason; 1117 | return transmitReceive(&cmd, 0); 1118 | } 1119 | 1120 | nRFTxStatus nRF8001::changeTimingRequest(uint16_t intervalMin, 1121 | uint16_t intervalMax, 1122 | uint16_t slaveLatency, 1123 | uint16_t timeout) 1124 | { 1125 | if (deviceState != Standby) { 1126 | nrf_debug("device not in Standby state"); 1127 | return InvalidState; 1128 | } 1129 | 1130 | nRFCommand cmd; 1131 | cmd.command = NRF_CHANGETIMINGREQUEST_OP; 1132 | 1133 | if (intervalMin > 0 || intervalMax > 0 1134 | || slaveLatency > 0 || timeout >> 0) { 1135 | cmd.length = 9; 1136 | cmd.content.changeTimingRequest.intervalMin = intervalMin; 1137 | cmd.content.changeTimingRequest.intervalMax = intervalMax; 1138 | cmd.content.changeTimingRequest.slaveLatency = slaveLatency; 1139 | cmd.content.changeTimingRequest.timeout = timeout; 1140 | } else { 1141 | cmd.length = 1; 1142 | } 1143 | 1144 | return transmitReceive(&cmd, 0); 1145 | } 1146 | 1147 | nRFTxStatus nRF8001::openRemotePipe(nRFPipe servicePipeNo) 1148 | { 1149 | return transmitPipeCommand(NRF_OPENREMOTEPIPE_OP, servicePipeNo); 1150 | } 1151 | 1152 | nRFTxStatus nRF8001::closeRemotePipe(nRFPipe servicePipeNo) 1153 | { 1154 | return transmitPipeCommand(NRF_CLOSEREMOTEPIPE_OP, servicePipeNo); 1155 | } 1156 | 1157 | nRFTxStatus nRF8001::dtmCommand(uint16_t dtmCmd) 1158 | { 1159 | nRFCommand cmd; 1160 | cmd.length = 3; 1161 | cmd.command = NRF_DTMCOMMAND_OP; 1162 | cmd.content.dtmCommand = dtmCmd; 1163 | return transmitReceive(&cmd, 0); 1164 | } 1165 | 1166 | nRFTxStatus nRF8001::readDynamicData() 1167 | { 1168 | return transmitCommand(NRF_READDYNAMICDATA_OP); 1169 | } 1170 | 1171 | nRFTxStatus nRF8001::writeDynamicData(uint8_t seqNo, nRFLen dataLength, 1172 | uint8_t *data) 1173 | { 1174 | if (deviceState != Standby) { 1175 | nrf_debug("device not in Standby state"); 1176 | return InvalidState; 1177 | } 1178 | 1179 | nRFCommand cmd; 1180 | cmd.length = dataLength + 2; 1181 | cmd.command = NRF_WRITEDYNAMICDATA_OP; 1182 | cmd.content.writeDynamicData.sequenceNo = seqNo; 1183 | memcpy(cmd.content.writeDynamicData.dynamicData, data, dataLength); 1184 | return transmitReceive(&cmd, 0); 1185 | } 1186 | 1187 | nRFTxStatus nRF8001::setApplLatency(uint8_t applLatencyMode, uint16_t latency) 1188 | { 1189 | if (deviceState != Standby) { 1190 | nrf_debug("device not in Standby state"); 1191 | return InvalidState; 1192 | } 1193 | 1194 | nRFCommand cmd; 1195 | cmd.length = 4; 1196 | cmd.command = NRF_SETAPPLICATIONLATENCY_OP; 1197 | cmd.content.setApplLatency.applLatencyMode = applLatencyMode; 1198 | cmd.content.setApplLatency.latency = latency; 1199 | return transmitReceive(&cmd, 0); 1200 | } 1201 | 1202 | nRFTxStatus nRF8001::setKey(uint8_t keyType, uint8_t *key) 1203 | { 1204 | if (deviceState != Standby) { 1205 | nrf_debug("device not in Standby state"); 1206 | return InvalidState; 1207 | } 1208 | 1209 | nRFCommand cmd; 1210 | cmd.command = NRF_SETKEY_OP; 1211 | cmd.content.setKey.keyType = keyType; 1212 | 1213 | if (keyType == 0) { 1214 | cmd.length = 2; 1215 | } else if (keyType == 1) { 1216 | cmd.length = 2 + NRF_PASSKEY_LENGTH; 1217 | memcpy(cmd.content.setKey.key, key, NRF_PASSKEY_LENGTH); 1218 | } else { 1219 | return InvalidParameter; 1220 | } 1221 | 1222 | return transmitReceive(&cmd, 0); 1223 | } 1224 | 1225 | nRFTxStatus nRF8001::openAdvPipe(uint64_t advServiceDataPipes) 1226 | { 1227 | if (deviceState != Standby) { 1228 | nrf_debug("device not in Standby state"); 1229 | return InvalidState; 1230 | } 1231 | 1232 | nRFCommand cmd; 1233 | cmd.length = 9; 1234 | cmd.command = NRF_OPENADVPIPE_OP; 1235 | cmd.content.advServiceDataPipes = advServiceDataPipes; 1236 | 1237 | return transmitReceive(&cmd, 0); 1238 | } 1239 | 1240 | nRFTxStatus nRF8001::broadcast(uint16_t timeout, uint16_t advInterval) 1241 | { 1242 | if (deviceState != Standby) { 1243 | nrf_debug("device not in Standby state"); 1244 | return InvalidState; 1245 | } 1246 | 1247 | nRFCommand cmd; 1248 | cmd.length = 5; 1249 | cmd.command = NRF_BROADCAST_OP; 1250 | cmd.content.broadcast.timeout = timeout; 1251 | cmd.content.broadcast.advInterval = advInterval; 1252 | 1253 | return transmitReceive(&cmd, 0); 1254 | } 1255 | 1256 | nRFTxStatus nRF8001::bondSecurityRequest() 1257 | { 1258 | return transmitCommand(NRF_BONDSECREQUEST_OP); 1259 | } 1260 | 1261 | nRFTxStatus nRF8001::directedConnect() 1262 | { 1263 | return transmitCommand(NRF_DIRECTEDCONNECT_OP); 1264 | } 1265 | 1266 | nRFTxStatus nRF8001::sendData(nRFPipe servicePipeNo, 1267 | nRFLen dataLength, uint8_t *data) 1268 | { 1269 | if (deviceState != Standby) { 1270 | nrf_debug("device not in Standby state"); 1271 | return InvalidState; 1272 | } 1273 | 1274 | if (connectionStatus != Connected) { 1275 | nrf_debug("device not connected"); 1276 | return NotConnected; 1277 | } 1278 | 1279 | if (!(pipesOpen & ((uint64_t)1)< NRF_DATA_LENGTH) { 1290 | nrf_debug("data too long"); 1291 | return DataTooLong; 1292 | } 1293 | 1294 | nRFCommand cmd; 1295 | cmd.command = NRF_SENDDATA_OP; 1296 | cmd.length = dataLength + 2; 1297 | cmd.content.data.servicePipeNo = servicePipeNo; 1298 | memcpy(cmd.content.data.data, data, dataLength); 1299 | return transmitReceive(&cmd, 0); 1300 | } 1301 | 1302 | nRFTxStatus nRF8001::requestData(nRFPipe servicePipeNo) 1303 | { 1304 | return transmitPipeCommand(NRF_REQUESTDATA_OP, servicePipeNo); 1305 | } 1306 | 1307 | nRFTxStatus nRF8001::setLocalData(nRFPipe servicePipeNo, nRFLen dataLength, 1308 | uint8_t *data) 1309 | { 1310 | if (deviceState != Standby) { 1311 | nrf_debug("device not in Standby state"); 1312 | return InvalidState; 1313 | } 1314 | 1315 | nRFCommand cmd; 1316 | cmd.length = 2 + dataLength; 1317 | cmd.command = NRF_SETLOCALDATA_OP; 1318 | cmd.content.data.servicePipeNo = servicePipeNo; 1319 | 1320 | memcpy(cmd.content.data.data, data, dataLength); 1321 | 1322 | return transmitReceive(&cmd, 0); 1323 | } 1324 | 1325 | nRFTxStatus nRF8001::sendDataAck(nRFPipe servicePipeNo) 1326 | { 1327 | return transmitPipeCommand(NRF_SENDDATAACK_OP, servicePipeNo); 1328 | } 1329 | 1330 | nRFTxStatus nRF8001::sendDataNack(nRFPipe servicePipeNo, uint8_t errorCode) 1331 | { 1332 | if (deviceState != Standby) { 1333 | nrf_debug("device not in Standby state"); 1334 | return InvalidState; 1335 | } 1336 | 1337 | nRFCommand cmd; 1338 | cmd.length = 3; 1339 | cmd.command = NRF_SENDDATANACK_OP; 1340 | cmd.content.sendDataNack.servicePipeNo = servicePipeNo; 1341 | cmd.content.sendDataNack.errorCode = errorCode; 1342 | 1343 | return transmitReceive(&cmd, 0); 1344 | } 1345 | 1346 | // Event handler registration 1347 | 1348 | void nRF8001::setEventHandler(nRFEventHandler handler) 1349 | { 1350 | listener = handler; 1351 | } 1352 | 1353 | void nRF8001::setCommandResponseHandler(nRFCommandResponseHandler handler) 1354 | { 1355 | commandResponseHandler = handler; 1356 | } 1357 | 1358 | void nRF8001::setTemperatureHandler(nRFTemperatureHandler handler) 1359 | { 1360 | temperatureHandler = handler; 1361 | } 1362 | 1363 | void nRF8001::setBatteryLevelHandler(nRFBatteryLevelHandler handler) 1364 | { 1365 | batteryLevelHandler = handler; 1366 | } 1367 | 1368 | void nRF8001::setDeviceVersionHandler(nRFDeviceVersionHandler handler) 1369 | { 1370 | deviceVersionHandler = handler; 1371 | } 1372 | 1373 | void nRF8001::setDeviceAddressHandler(nRFDeviceAddressHandler handler) 1374 | { 1375 | deviceAddressHandler = handler; 1376 | } 1377 | 1378 | void nRF8001::setDynamicDataHandler(nRFDynamicDataHandler handler) 1379 | { 1380 | dynamicDataHandler = handler; 1381 | } 1382 | 1383 | void nRF8001::setConnectedHandler(nRFConnectedHandler handler) 1384 | { 1385 | connectedHandler = handler; 1386 | } 1387 | 1388 | void nRF8001::setDisconnectedHandler(nRFDisconnectedHandler handler) 1389 | { 1390 | disconnectedHandler = handler; 1391 | } 1392 | 1393 | void nRF8001::setBondStatusHandler(nRFBondStatusHandler handler) 1394 | { 1395 | bondStatusHandler = handler; 1396 | } 1397 | 1398 | void nRF8001::setKeyRequestHandler(nRFKeyRequestHandler handler) 1399 | { 1400 | keyRequestHandler = handler; 1401 | } 1402 | 1403 | void nRF8001::setPipeErrorHandler(nRFPipeErrorHandler handler) 1404 | { 1405 | pipeErrorHandler = handler; 1406 | } 1407 | 1408 | void nRF8001::setDataReceivedHandler(nRFDataReceivedHandler handler) 1409 | { 1410 | dataReceivedHandler = handler; 1411 | } 1412 | 1413 | void nRF8001::setDataAckHandler(nRFDataAckHandler handler) 1414 | { 1415 | dataAckHandler = handler; 1416 | } 1417 | -------------------------------------------------------------------------------- /nRF8001.h: -------------------------------------------------------------------------------- 1 | #ifndef _NRF8001_H 2 | #define _NRF8001_H 3 | 4 | #include 5 | 6 | #ifndef NRF_DEBUG 7 | #define NRF_DEBUG 1 8 | //#define NRF_VERBOSE_DEBUG 1 9 | #endif 10 | 11 | typedef uint8_t nRFLen; 12 | typedef uint8_t nRFPipe; 13 | 14 | #include "constants.h" 15 | #include "data.h" // data structures for requests and responses 16 | 17 | typedef struct { 18 | uint8_t status_byte; 19 | uint8_t buffer[32]; 20 | } hal_aci_data_t; 21 | 22 | #define NRF_RX_BUFFERS 5 23 | 24 | #if NRF_DEBUG 25 | #define nrf_debug(msg) Serial.println(F(msg)) 26 | #define nrf_debugnl(msg) Serial.print(F(msg)) 27 | #else 28 | #define nrf_debug(msg) 29 | #define nrf_debugnl(msg) 30 | #endif 31 | 32 | // event handlers 33 | typedef void (*nRFEventHandler) (nRFEvent *); 34 | typedef void (*nRFCommandResponseHandler) (uint8_t opcode, uint8_t status); 35 | typedef void (*nRFTemperatureHandler) (float tempC); 36 | typedef void (*nRFBatteryLevelHandler) (float voltage); 37 | typedef void (*nRFDeviceVersionHandler) (uint16_t configId, 38 | uint8_t aciVersion, uint8_t setupFormat, uint8_t configStatus); 39 | typedef void (*nRFDeviceAddressHandler) (uint8_t *address, 40 | uint8_t addressType); 41 | typedef void (*nRFDynamicDataHandler) (uint8_t seqNo, uint8_t *data); 42 | typedef void (*nRFConnectedHandler) (uint8_t addressType, uint8_t *peerAddress, 43 | void *connectionData); 44 | typedef void (*nRFDisconnectedHandler) (uint8_t aciStatus, uint8_t btLeStatus); 45 | typedef void (*nRFBondStatusHandler) (void *bondStatusData); 46 | typedef void (*nRFKeyRequestHandler) (uint8_t keyType); 47 | typedef void (*nRFPipeErrorHandler) (nRFPipe servicePipeNo, 48 | uint8_t errorCode, uint8_t *errorData); 49 | typedef void (*nRFDataReceivedHandler) (nRFPipe servicePipeNo, 50 | uint8_t *data); 51 | typedef void (*nRFDataAckHandler) (nRFPipe servicePipeNo); 52 | 53 | class nRF8001 54 | { 55 | private: 56 | uint64_t pipesOpen; 57 | uint8_t reset_pin; 58 | uint8_t reqn_pin; 59 | uint8_t rdyn_pin; 60 | nRFEventHandler listener; 61 | uint8_t credits; 62 | nRFDeviceState deviceState; 63 | int8_t nextSetupMessage; 64 | nRFConnectionStatus connectionStatus; 65 | 66 | nRFTxStatus transmitReceive(nRFCommand *txCmd, uint16_t timeout); 67 | nRFTxStatus transmitCommand(uint8_t command); 68 | nRFTxStatus transmitPipeCommand(uint8_t command, nRFPipe pipe); 69 | 70 | nRFCommandResponseHandler commandResponseHandler; 71 | nRFTemperatureHandler temperatureHandler; 72 | nRFBatteryLevelHandler batteryLevelHandler; 73 | nRFDeviceVersionHandler deviceVersionHandler; 74 | nRFDeviceAddressHandler deviceAddressHandler; 75 | nRFDynamicDataHandler dynamicDataHandler; 76 | nRFConnectedHandler connectedHandler; 77 | nRFDisconnectedHandler disconnectedHandler; 78 | nRFBondStatusHandler bondStatusHandler; 79 | nRFKeyRequestHandler keyRequestHandler; 80 | nRFPipeErrorHandler pipeErrorHandler; 81 | nRFDataReceivedHandler dataReceivedHandler; 82 | nRFDataAckHandler dataAckHandler; 83 | 84 | public: 85 | void debugEvent(nRFEvent *event); 86 | void debugAddress(uint8_t *address); 87 | void addressToString(char *str, uint8_t *address); 88 | 89 | nRFTxStatus poll(uint16_t timeout); 90 | nRFTxStatus poll(); 91 | nRFDeviceState getDeviceState(); 92 | nRFCmd setup(hal_aci_data_t setup_msgs[], uint8_t nb_setup_msgs); 93 | 94 | nRF8001(uint8_t reset_pin, 95 | uint8_t reqn_pin, 96 | uint8_t rdyn_pin); 97 | 98 | uint8_t creditsAvailable(); 99 | uint8_t isConnected(); 100 | nRFConnectionStatus getConnectionStatus(); 101 | 102 | uint8_t isPipeOpen(nRFPipe servicePipeNo); 103 | 104 | nRFTxStatus test(uint8_t feature); 105 | nRFTxStatus sleep(); 106 | nRFTxStatus getDeviceVersion(); 107 | nRFTxStatus echo(nRFLen dataLength, uint8_t *data); 108 | nRFTxStatus wakeup(); 109 | nRFTxStatus getBatteryLevel(); 110 | nRFTxStatus getTemperature(); 111 | nRFTxStatus setTxPower(uint8_t powerLevel); 112 | nRFTxStatus getDeviceAddress(); 113 | nRFTxStatus connect(uint16_t timeout, uint16_t advInterval); 114 | nRFTxStatus radioReset(); 115 | nRFTxStatus bond(uint16_t timeout, uint16_t advInterval); 116 | nRFTxStatus disconnect(uint8_t reason); 117 | nRFTxStatus changeTimingRequest(uint16_t intervalMin, 118 | uint16_t intervalMax, 119 | uint16_t slaveLatency, 120 | uint16_t timeout); 121 | nRFTxStatus openRemotePipe(nRFPipe servicePipeNo); 122 | nRFTxStatus closeRemotePipe(nRFPipe servicePipeNo); 123 | nRFTxStatus dtmCommand(uint16_t dtmCmd); 124 | nRFTxStatus readDynamicData(); 125 | nRFTxStatus writeDynamicData(uint8_t seqNo, 126 | nRFLen dataLength, 127 | uint8_t *data); 128 | nRFTxStatus setApplLatency(uint8_t applLatencyMode, 129 | uint16_t latency); 130 | nRFTxStatus setKey(uint8_t keyType, uint8_t *key); 131 | nRFTxStatus openAdvPipe(uint64_t advServiceDataPipes); 132 | nRFTxStatus broadcast(uint16_t timeout, uint16_t advInterval); 133 | nRFTxStatus bondSecurityRequest(); 134 | nRFTxStatus directedConnect(); 135 | nRFTxStatus sendData(nRFPipe servicePipeNo, 136 | nRFLen dataLength, 137 | uint8_t *data); 138 | nRFTxStatus requestData(nRFPipe servicePipeNo); 139 | nRFTxStatus setLocalData(nRFPipe servicePipeNo, 140 | nRFLen dataLength, 141 | uint8_t *data); 142 | nRFTxStatus sendDataAck(nRFPipe servicePipeNo); 143 | nRFTxStatus sendDataNack(nRFPipe servicePipeNo, 144 | uint8_t errorCode); 145 | 146 | void setEventHandler(nRFEventHandler handler); 147 | void setCommandResponseHandler(nRFCommandResponseHandler handler); 148 | void setTemperatureHandler(nRFTemperatureHandler handler); 149 | void setBatteryLevelHandler(nRFBatteryLevelHandler handler); 150 | void setDeviceVersionHandler(nRFDeviceVersionHandler handler); 151 | void setDeviceAddressHandler(nRFDeviceAddressHandler handler); 152 | void setDynamicDataHandler(nRFDynamicDataHandler handler); 153 | void setConnectedHandler(nRFConnectedHandler handler); 154 | void setDisconnectedHandler(nRFDisconnectedHandler handler); 155 | void setBondStatusHandler(nRFBondStatusHandler handler); 156 | void setKeyRequestHandler(nRFKeyRequestHandler handler); 157 | void setPipeErrorHandler(nRFPipeErrorHandler handler); 158 | void setDataReceivedHandler(nRFDataReceivedHandler handler); 159 | void setDataAckHandler(nRFDataAckHandler handler); 160 | }; 161 | 162 | #endif /* _NRF8001_H */ 163 | -------------------------------------------------------------------------------- /services.h.example: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010 Nordic Semiconductor. All Rights Reserved. 2 | * 3 | * The information contained herein is property of Nordic Semiconductor ASA. 4 | * Terms and conditions of usage are described in detail in NORDIC 5 | * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. 6 | * 7 | * Licensees are granted free, non-transferable use of the information. NO 8 | * WARRANTY of ANY KIND is provided. This heading must NOT be removed from 9 | * the file. 10 | */ 11 | 12 | /** 13 | * This file is autogenerated by 1.12.1.1992 14 | */ 15 | 16 | #ifndef SETUP_MESSAGES_H__ 17 | #define SETUP_MESSAGES_H__ 18 | 19 | #include "hal_platform.h" 20 | #include "aci.h" 21 | #define PIPE_DEVICE_INFORMATION_SERIAL_NUMBER_STRING_SET 1 22 | #define PIPE_DEVICE_INFORMATION_MANUFACTURER_NAME_STRING_SET 2 23 | #define PIPE_DEVICE_INFORMATION_SYSTEM_ID_SET 3 24 | #define PIPE_DEVICE_INFORMATION_FIRMWARE_REVISION_STRING_SET 4 25 | #define PIPE_HEART_RATE_HEART_RATE_MEASUREMENT_TX 5 26 | #define PIPE_HEART_RATE_HEART_RATE_SENSOR_LOCATION_SET 6 27 | #define PIPE_HEART_RATE_HEART_RATE_CONTROL_POINT_RX_ACK 7 28 | #define PIPE_BATTERY_BATTERY_LEVEL_TX 8 29 | #define PIPE_BATTERY_BATTERY_LEVEL_SET 9 30 | 31 | #define NUMBER_OF_PIPES 9 32 | typedef struct 33 | { 34 | aci_pipe_store_t location; 35 | aci_pipe_type_t pipe_type; 36 | } services_pipe_type_mapping_t; 37 | 38 | static services_pipe_type_mapping_t services_pipe_type_mapping[NUMBER_OF_PIPES] = 39 | { 40 | {ACI_STORE_LOCAL, ACI_SET}, 41 | {ACI_STORE_LOCAL, ACI_SET}, 42 | {ACI_STORE_LOCAL, ACI_SET}, 43 | {ACI_STORE_LOCAL, ACI_SET}, 44 | {ACI_STORE_LOCAL, ACI_TX}, 45 | {ACI_STORE_LOCAL, ACI_SET}, 46 | {ACI_STORE_LOCAL, ACI_RX_ACK}, 47 | {ACI_STORE_LOCAL, ACI_TX}, 48 | {ACI_STORE_LOCAL, ACI_SET}, 49 | }; 50 | 51 | #define GAP_PPCP_MAX_CONN_INT 0x320 /**< Maximum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific value requested */ 52 | #define GAP_PPCP_MIN_CONN_INT 0x190 /**< Minimum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific maximum*/ 53 | #define GAP_PPCP_SLAVE_LATENCY 0 54 | #define GAP_PPCP_CONN_TIMEOUT 0x1f6 /** Connection Supervision timeout multiplier as a multiple of 10msec, 0xFFFF means no specific value requested */ 55 | 56 | /** @brief do a set_local_data a new value for PIPE_DEVICE_INFORMATION_SERIAL_NUMBER_STRING_SET 57 | * @param src source buffer to send data from. 58 | * Presentation format: UTF-8 string 59 | * @param size the number of bytes to send. Maximum size is 4 60 | * @details use this function to do a set_local_data for PIPE_DEVICE_INFORMATION_SERIAL_NUMBER_STRING_SET. If no transaction are currently 61 | * running, the set will be immediate, otherwise, it will be done at the end of the current transaction 62 | * when services_update_pipes will be called. 63 | */ 64 | void services_set_device_information_serial_number_string(void *src, int size); 65 | 66 | /** @brief do a set_local_data a new value for PIPE_DEVICE_INFORMATION_MANUFACTURER_NAME_STRING_SET 67 | * @param src source buffer to send data from. 68 | * Presentation format: UTF-8 string 69 | * @param size the number of bytes to send. Maximum size is 20 70 | * @details use this function to do a set_local_data for PIPE_DEVICE_INFORMATION_MANUFACTURER_NAME_STRING_SET. If no transaction are currently 71 | * running, the set will be immediate, otherwise, it will be done at the end of the current transaction 72 | * when services_update_pipes will be called. 73 | */ 74 | void services_set_device_information_manufacturer_name_string(void *src, int size); 75 | 76 | /** @brief do a set_local_data a new value for PIPE_DEVICE_INFORMATION_SYSTEM_ID_SET 77 | * @param src source buffer to send data from 78 | * @param size the number of bytes to send. Maximum size is 8 79 | * @details use this function to do a set_local_data for PIPE_DEVICE_INFORMATION_SYSTEM_ID_SET. If no transaction are currently 80 | * running, the set will be immediate, otherwise, it will be done at the end of the current transaction 81 | * when services_update_pipes will be called. 82 | */ 83 | void services_set_device_information_system_id(void *src, int size); 84 | 85 | /** @brief do a set_local_data a new value for PIPE_DEVICE_INFORMATION_FIRMWARE_REVISION_STRING_SET 86 | * @param src source buffer to send data from. 87 | * Presentation format: UTF-8 string 88 | * @param size the number of bytes to send. Maximum size is 4 89 | * @details use this function to do a set_local_data for PIPE_DEVICE_INFORMATION_FIRMWARE_REVISION_STRING_SET. If no transaction are currently 90 | * running, the set will be immediate, otherwise, it will be done at the end of the current transaction 91 | * when services_update_pipes will be called. 92 | */ 93 | void services_set_device_information_firmware_revision_string(void *src, int size); 94 | 95 | /** @brief send a new value for PIPE_HEART_RATE_HEART_RATE_MEASUREMENT_TX 96 | * @param src source buffer to send data from 97 | * @param size the number of bytes to send. Maximum size is 19 98 | * @param is_freshest_sample set it to true if you want to overwrite an eventual pending transaction on this pipe. 99 | * @details use this function to send a new value for PIPE_HEART_RATE_HEART_RATE_MEASUREMENT_TX. If no transaction are currently 100 | * running, the send will be immediate, otherwise, it will be done at the end of the current transaction 101 | * when services_update_pipes will be called. If a transaction on this pipe is already pending, then this function 102 | * will not overwrite the data of the previous transaction and return false. 103 | * @return : true if is_freshest_sample true, otherwise return false if a transaction on this pipe is already pending, true otherwise. 104 | */ 105 | bool services_send_heart_rate_heart_rate_measurement(void *src, int size, bool is_freshest_sample); 106 | 107 | /** @brief do a set_local_data a new value for PIPE_HEART_RATE_HEART_RATE_SENSOR_LOCATION_SET 108 | * @param src the value to send 109 | * @details use this function to do a set_local_data for PIPE_HEART_RATE_HEART_RATE_SENSOR_LOCATION_SET. If no transaction are currently 110 | * running, the set will be immediate, otherwise, it will be done at the end of the current transaction 111 | * when services_update_pipes will be called. 112 | */ 113 | void services_set_heart_rate_heart_rate_sensor_location(uint8_t src); 114 | 115 | /** @brief send an acknowledge rx ack pipe for PIPE_HEART_RATE_HEART_RATE_CONTROL_POINT_RX_ACK 116 | * @details use this function to acknowledge PIPE_HEART_RATE_HEART_RATE_CONTROL_POINT_RX_ACK. If no transaction are pending, it 117 | * would be sent immediately, otherwise, it will be done at the end of the current transaction when services_update_pipes 118 | * will be called. 119 | */ 120 | void services_acknowledge_heart_rate_heart_rate_control_point(const unsigned char error_code); 121 | 122 | /** @brief send a new value for PIPE_BATTERY_BATTERY_LEVEL_TX 123 | * @param src the value to send 124 | * @param is_freshest_sample set it to true if you want to overwrite an eventual pending transaction on this pipe. 125 | * @details use this function to send a new value for PIPE_BATTERY_BATTERY_LEVEL_TX. If no transaction are currently 126 | * running, the send will be immediate, otherwise, it will be done at the end of the current transaction 127 | * when services_update_pipes will be called. If a transaction on this pipe is already pending, then this function 128 | * will not overwrite the data of the previous transaction and return false. 129 | * @return : true if is_freshest_sample true, otherwise return false if a transaction on this pipe is already pending, true otherwise. 130 | */ 131 | bool services_send_battery_battery_level(uint8_t src, bool is_freshest_sample); 132 | 133 | /** @brief do a set_local_data a new value for PIPE_BATTERY_BATTERY_LEVEL_SET 134 | * @param src the value to send 135 | * @details use this function to do a set_local_data for PIPE_BATTERY_BATTERY_LEVEL_SET. If no transaction are currently 136 | * running, the set will be immediate, otherwise, it will be done at the end of the current transaction 137 | * when services_update_pipes will be called. 138 | */ 139 | void services_set_battery_battery_level(uint8_t src); 140 | 141 | /** @brief function to trig pending transaction on pipes 142 | * @details This function check for each pipe if it has a pending transaction (send/rx_request/ack) 143 | * and if so executes this transaction. 144 | * This function should be called in the APP_RUN state of the process function of the application. 145 | */ 146 | void services_update_pipes(void); 147 | 148 | #define NB_SETUP_MESSAGES 27 149 | #define SETUP_MESSAGES_CONTENT {\ 150 | {0x00,\ 151 | {\ 152 | 0x07,0x06,0x00,0x00,0x03,0x02,0x40,0x6c,\ 153 | },\ 154 | },\ 155 | {0x00,\ 156 | {\ 157 | 0x1f,0x06,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x09,0x00,0x06,0x00,0x00,0x06,0x00,0x00,\ 158 | 0x81,0x0d,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 159 | },\ 160 | },\ 161 | {0x00,\ 162 | {\ 163 | 0x1f,0x06,0x10,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 164 | 0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x12,0x03,0x90,0x00,0x14,\ 165 | },\ 166 | },\ 167 | {0x00,\ 168 | {\ 169 | 0x1f,0x06,0x10,0x38,0x02,0xff,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 170 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 171 | },\ 172 | },\ 173 | {0x00,\ 174 | {\ 175 | 0x05,0x06,0x10,0x54,0x00,0x00,\ 176 | },\ 177 | },\ 178 | {0x00,\ 179 | {\ 180 | 0x1f,0x06,0x20,0x00,0x04,0x04,0x02,0x02,0x00,0x01,0x28,0x00,0x01,0x00,0x18,0x04,0x04,0x05,0x05,0x00,\ 181 | 0x02,0x28,0x03,0x01,0x0e,0x03,0x00,0x00,0x2a,0x04,0x14,0x0f,\ 182 | },\ 183 | },\ 184 | {0x00,\ 185 | {\ 186 | 0x1f,0x06,0x20,0x1c,0x0f,0x00,0x03,0x2a,0x00,0x01,0x4e,0x6f,0x72,0x64,0x69,0x63,0x20,0x48,0x52,0x4d,\ 187 | 0x20,0x56,0x31,0x2e,0x30,0x04,0x04,0x05,0x05,0x00,0x04,0x28,\ 188 | },\ 189 | },\ 190 | {0x00,\ 191 | {\ 192 | 0x1f,0x06,0x20,0x38,0x03,0x01,0x02,0x05,0x00,0x01,0x2a,0x06,0x04,0x03,0x02,0x00,0x05,0x2a,0x01,0x01,\ 193 | 0x00,0x00,0x04,0x04,0x05,0x05,0x00,0x06,0x28,0x03,0x01,0x02,\ 194 | },\ 195 | },\ 196 | {0x00,\ 197 | {\ 198 | 0x1f,0x06,0x20,0x54,0x07,0x00,0x04,0x2a,0x06,0x04,0x09,0x08,0x00,0x07,0x2a,0x04,0x01,0x90,0x01,0x20,\ 199 | 0x03,0x00,0x00,0xf6,0x01,0x04,0x04,0x02,0x02,0x00,0x08,0x28,\ 200 | },\ 201 | },\ 202 | {0x00,\ 203 | {\ 204 | 0x1f,0x06,0x20,0x70,0x00,0x01,0x01,0x18,0x04,0x04,0x02,0x02,0x00,0x09,0x28,0x00,0x01,0x0a,0x18,0x04,\ 205 | 0x04,0x05,0x05,0x00,0x0a,0x28,0x03,0x01,0x02,0x0b,0x00,0x25,\ 206 | },\ 207 | },\ 208 | {0x00,\ 209 | {\ 210 | 0x1f,0x06,0x20,0x8c,0x2a,0x06,0x04,0x05,0x04,0x00,0x0b,0x2a,0x25,0x01,0x31,0x35,0x38,0x37,0x06,0x04,\ 211 | 0x08,0x07,0x00,0x0c,0x29,0x04,0x01,0x19,0x00,0x00,0x00,0x01,\ 212 | },\ 213 | },\ 214 | {0x00,\ 215 | {\ 216 | 0x1f,0x06,0x20,0xa8,0x00,0x00,0x04,0x04,0x05,0x05,0x00,0x0d,0x28,0x03,0x01,0x02,0x0e,0x00,0x29,0x2a,\ 217 | 0x06,0x04,0x15,0x14,0x00,0x0e,0x2a,0x29,0x01,0x4e,0x6f,0x72,\ 218 | },\ 219 | },\ 220 | {0x00,\ 221 | {\ 222 | 0x1f,0x06,0x20,0xc4,0x64,0x69,0x63,0x53,0x65,0x6d,0x69,0x63,0x6f,0x6e,0x64,0x75,0x63,0x74,0x6f,0x72,\ 223 | 0x00,0x06,0x04,0x08,0x07,0x00,0x0f,0x29,0x04,0x01,0x19,0x00,\ 224 | },\ 225 | },\ 226 | {0x00,\ 227 | {\ 228 | 0x1f,0x06,0x20,0xe0,0x00,0x00,0x01,0x00,0x00,0x04,0x04,0x05,0x05,0x00,0x10,0x28,0x03,0x01,0x02,0x11,\ 229 | 0x00,0x23,0x2a,0x06,0x04,0x09,0x08,0x00,0x11,0x2a,0x23,0x01,\ 230 | },\ 231 | },\ 232 | {0x00,\ 233 | {\ 234 | 0x1f,0x06,0x20,0xfc,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x04,0x04,0x05,0x05,0x00,0x12,0x28,0x03,\ 235 | 0x01,0x02,0x13,0x00,0x26,0x2a,0x06,0x04,0x05,0x04,0x00,0x13,\ 236 | },\ 237 | },\ 238 | {0x00,\ 239 | {\ 240 | 0x1f,0x06,0x21,0x18,0x2a,0x26,0x01,0x30,0x31,0x2e,0x31,0x06,0x04,0x08,0x07,0x00,0x14,0x29,0x04,0x01,\ 241 | 0x19,0x00,0x00,0x00,0x01,0x00,0x00,0x04,0x04,0x02,0x02,0x00,\ 242 | },\ 243 | },\ 244 | {0x00,\ 245 | {\ 246 | 0x1f,0x06,0x21,0x34,0x15,0x28,0x00,0x01,0x0d,0x18,0x04,0x04,0x05,0x05,0x00,0x16,0x28,0x03,0x01,0x10,\ 247 | 0x17,0x00,0x37,0x2a,0x14,0x00,0x13,0x00,0x00,0x17,0x2a,0x37,\ 248 | },\ 249 | },\ 250 | {0x00,\ 251 | {\ 252 | 0x1f,0x06,0x21,0x50,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 253 | 0x00,0x00,0x00,0x00,0x46,0x14,0x03,0x02,0x00,0x18,0x29,0x02,\ 254 | },\ 255 | },\ 256 | {0x00,\ 257 | {\ 258 | 0x1f,0x06,0x21,0x6c,0x01,0x00,0x00,0x04,0x04,0x05,0x05,0x00,0x19,0x28,0x03,0x01,0x02,0x1a,0x00,0x38,\ 259 | 0x2a,0x06,0x04,0x02,0x01,0x00,0x1a,0x2a,0x38,0x01,0x03,0x04,\ 260 | },\ 261 | },\ 262 | {0x00,\ 263 | {\ 264 | 0x1f,0x06,0x21,0x88,0x04,0x05,0x05,0x00,0x1b,0x28,0x03,0x01,0x08,0x1c,0x00,0x39,0x2a,0x46,0x10,0x02,\ 265 | 0x01,0x00,0x1c,0x2a,0x39,0x01,0x00,0x04,0x04,0x02,0x02,0x00,\ 266 | },\ 267 | },\ 268 | {0x00,\ 269 | {\ 270 | 0x1f,0x06,0x21,0xa4,0x1d,0x28,0x00,0x01,0x0f,0x18,0x04,0x04,0x05,0x05,0x00,0x1e,0x28,0x03,0x01,0x12,\ 271 | 0x1f,0x00,0x19,0x2a,0x16,0x04,0x02,0x01,0x00,0x1f,0x2a,0x19,\ 272 | },\ 273 | },\ 274 | {0x00,\ 275 | {\ 276 | 0x11,0x06,0x21,0xc0,0x01,0x64,0x46,0x14,0x03,0x02,0x00,0x20,0x29,0x02,0x01,0x00,0x00,0x00,\ 277 | },\ 278 | },\ 279 | {0x00,\ 280 | {\ 281 | 0x1f,0x06,0x40,0x00,0x2a,0x25,0x01,0x00,0x80,0x04,0x00,0x0b,0x00,0x00,0x2a,0x29,0x01,0x00,0x80,0x04,\ 282 | 0x00,0x0e,0x00,0x00,0x2a,0x23,0x01,0x00,0x80,0x04,0x00,0x11,\ 283 | },\ 284 | },\ 285 | {0x00,\ 286 | {\ 287 | 0x1f,0x06,0x40,0x1c,0x00,0x00,0x2a,0x26,0x01,0x00,0x80,0x04,0x00,0x13,0x00,0x00,0x2a,0x37,0x01,0x00,\ 288 | 0x02,0x04,0x00,0x17,0x00,0x18,0x2a,0x38,0x01,0x00,0x80,0x04,\ 289 | },\ 290 | },\ 291 | {0x00,\ 292 | {\ 293 | 0x1b,0x06,0x40,0x38,0x00,0x1a,0x00,0x00,0x2a,0x39,0x01,0x00,0x10,0x04,0x00,0x1c,0x00,0x00,0x2a,0x19,\ 294 | 0x01,0x00,0x82,0x04,0x00,0x1f,0x00,0x20,\ 295 | },\ 296 | },\ 297 | {0x00,\ 298 | {\ 299 | 0x1b,0x06,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 300 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 301 | },\ 302 | },\ 303 | {0x00,\ 304 | {\ 305 | 0x06,0x06,0xf0,0x00,0x03,0x18,0x28,\ 306 | },\ 307 | },\ 308 | } 309 | 310 | #endif 311 | -------------------------------------------------------------------------------- /services.h.redbear: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012 Nordic Semiconductor. All Rights Reserved. 2 | * 3 | * The information contained herein is property of Nordic Semiconductor ASA. 4 | * Terms and conditions of usage are described in detail in NORDIC 5 | * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. 6 | * 7 | * Licensees are granted free, non-transferable use of the information. NO 8 | * WARRANTY of ANY KIND is provided. This heading must NOT be removed from 9 | * the file. 10 | */ 11 | 12 | /** 13 | * This file is autogenerated by nRFgo Studio 1.14.1.2369 14 | */ 15 | 16 | #ifndef SETUP_MESSAGES_H__ 17 | #define SETUP_MESSAGES_H__ 18 | 19 | #include "hal_platform.h" 20 | #include "aci.h" 21 | #define PIPE_DEVICE_INFORMATION_SERIAL_NUMBER_STRING_SET 1 22 | #define PIPE_DEVICE_INFORMATION_MANUFACTURER_NAME_STRING_SET 2 23 | #define PIPE_DEVICE_INFORMATION_SYSTEM_ID_SET 3 24 | #define PIPE_DEVICE_INFORMATION_FIRMWARE_REVISION_STRING_SET 4 25 | #define PIPE_HEART_RATE_HEART_RATE_MEASUREMENT_TX 5 26 | #define PIPE_HEART_RATE_HEART_RATE_SENSOR_LOCATION_SET 6 27 | #define PIPE_HEART_RATE_HEART_RATE_CONTROL_POINT_RX_ACK 7 28 | #define PIPE_BATTERY_BATTERY_LEVEL_TX 8 29 | #define PIPE_BATTERY_BATTERY_LEVEL_SET 9 30 | 31 | #define NUMBER_OF_PIPES 9 32 | 33 | typedef struct 34 | { 35 | aci_pipe_store_t location; 36 | aci_pipe_type_t pipe_type; 37 | } services_pipe_type_mapping_t; 38 | 39 | static services_pipe_type_mapping_t services_pipe_type_mapping[NUMBER_OF_PIPES] = 40 | { 41 | {ACI_STORE_LOCAL, ACI_SET}, 42 | {ACI_STORE_LOCAL, ACI_SET}, 43 | {ACI_STORE_LOCAL, ACI_SET}, 44 | {ACI_STORE_LOCAL, ACI_SET}, 45 | {ACI_STORE_LOCAL, ACI_TX}, 46 | {ACI_STORE_LOCAL, ACI_SET}, 47 | {ACI_STORE_LOCAL, ACI_RX_ACK}, 48 | {ACI_STORE_LOCAL, ACI_TX}, 49 | {ACI_STORE_LOCAL, ACI_SET}, 50 | }; 51 | 52 | #define GAP_PPCP_MAX_CONN_INT 0x320 /**< Maximum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific value requested */ 53 | #define GAP_PPCP_MIN_CONN_INT 0x190 /**< Minimum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific maximum*/ 54 | #define GAP_PPCP_SLAVE_LATENCY 0 55 | #define GAP_PPCP_CONN_TIMEOUT 0x1f6 /** Connection Supervision timeout multiplier as a multiple of 10msec, 0xFFFF means no specific value requested */ 56 | 57 | /** @brief do a set_local_data for PIPE_DEVICE_INFORMATION_SERIAL_NUMBER_STRING_SET 58 | * @param src source buffer to send data from. 59 | * Presentation format: UTF-8 string 60 | * @param size the number of bytes to send. Maximum size is 4 61 | * @details use this function to do a set_local_data for PIPE_DEVICE_INFORMATION_SERIAL_NUMBER_STRING_SET. If no transaction are currently 62 | * running, the set will be immediate, otherwise, it will be done at the end of the current transaction 63 | * when services_update_pipes will be called. 64 | */ 65 | void services_set_device_information_serial_number_string(void *src, int size); 66 | 67 | /** @brief do a set_local_data for PIPE_DEVICE_INFORMATION_MANUFACTURER_NAME_STRING_SET 68 | * @param src source buffer to send data from. 69 | * Presentation format: UTF-8 string 70 | * @param size the number of bytes to send. Maximum size is 20 71 | * @details use this function to do a set_local_data for PIPE_DEVICE_INFORMATION_MANUFACTURER_NAME_STRING_SET. If no transaction are currently 72 | * running, the set will be immediate, otherwise, it will be done at the end of the current transaction 73 | * when services_update_pipes will be called. 74 | */ 75 | void services_set_device_information_manufacturer_name_string(void *src, int size); 76 | 77 | /** @brief do a set_local_data for PIPE_DEVICE_INFORMATION_SYSTEM_ID_SET 78 | * @param src source buffer to send data from 79 | * @param size the number of bytes to send. Maximum size is 8 80 | * @details use this function to do a set_local_data for PIPE_DEVICE_INFORMATION_SYSTEM_ID_SET. If no transaction are currently 81 | * running, the set will be immediate, otherwise, it will be done at the end of the current transaction 82 | * when services_update_pipes will be called. 83 | */ 84 | void services_set_device_information_system_id(void *src, int size); 85 | 86 | /** @brief do a set_local_data for PIPE_DEVICE_INFORMATION_FIRMWARE_REVISION_STRING_SET 87 | * @param src source buffer to send data from. 88 | * Presentation format: UTF-8 string 89 | * @param size the number of bytes to send. Maximum size is 4 90 | * @details use this function to do a set_local_data for PIPE_DEVICE_INFORMATION_FIRMWARE_REVISION_STRING_SET. If no transaction are currently 91 | * running, the set will be immediate, otherwise, it will be done at the end of the current transaction 92 | * when services_update_pipes will be called. 93 | */ 94 | void services_set_device_information_firmware_revision_string(void *src, int size); 95 | 96 | /** @brief send a new value for PIPE_HEART_RATE_HEART_RATE_MEASUREMENT_TX 97 | * @param src source buffer to send data from 98 | * @param size the number of bytes to send. Maximum size is 19 99 | * @param is_freshest_sample set it to true if you want to overwrite an eventual pending transaction on this pipe. 100 | * @details use this function to send a new value for PIPE_HEART_RATE_HEART_RATE_MEASUREMENT_TX. If no transaction are currently 101 | * running, the send will be immediate, otherwise, it will be done at the end of the current transaction 102 | * when services_update_pipes will be called. If a transaction on this pipe is already pending, then this function 103 | * will not overwrite the data of the previous transaction and return false. 104 | * @return : true if is_freshest_sample true, otherwise return false if a transaction on this pipe is already pending, true otherwise. 105 | */ 106 | bool services_send_heart_rate_heart_rate_measurement(void *src, int size, bool is_freshest_sample); 107 | 108 | /** @brief do a set_local_data for PIPE_HEART_RATE_HEART_RATE_SENSOR_LOCATION_SET 109 | * @param src the value to send 110 | * @details use this function to do a set_local_data for PIPE_HEART_RATE_HEART_RATE_SENSOR_LOCATION_SET. If no transaction are currently 111 | * running, the set will be immediate, otherwise, it will be done at the end of the current transaction 112 | * when services_update_pipes will be called. 113 | */ 114 | void services_set_heart_rate_heart_rate_sensor_location(uint8_t src); 115 | 116 | /** @brief send an acknowledge rx ack pipe for PIPE_HEART_RATE_HEART_RATE_CONTROL_POINT_RX_ACK 117 | * @details use this function to acknowledge PIPE_HEART_RATE_HEART_RATE_CONTROL_POINT_RX_ACK. If no transaction are pending, it 118 | * would be sent immediately, otherwise, it will be done at the end of the current transaction when services_update_pipes 119 | * will be called. 120 | */ 121 | void services_acknowledge_heart_rate_heart_rate_control_point(const unsigned char error_code); 122 | 123 | /** @brief send a new value for PIPE_BATTERY_BATTERY_LEVEL_TX 124 | * @param src the value to send 125 | * @param is_freshest_sample set it to true if you want to overwrite an eventual pending transaction on this pipe. 126 | * @details use this function to send a new value for PIPE_BATTERY_BATTERY_LEVEL_TX. If no transaction are currently 127 | * running, the send will be immediate, otherwise, it will be done at the end of the current transaction 128 | * when services_update_pipes will be called. If a transaction on this pipe is already pending, then this function 129 | * will not overwrite the data of the previous transaction and return false. 130 | * @return : true if is_freshest_sample true, otherwise return false if a transaction on this pipe is already pending, true otherwise. 131 | */ 132 | bool services_send_battery_battery_level(uint8_t src, bool is_freshest_sample); 133 | 134 | /** @brief do a set_local_data for PIPE_BATTERY_BATTERY_LEVEL_SET 135 | * @param src the value to send 136 | * @details use this function to do a set_local_data for PIPE_BATTERY_BATTERY_LEVEL_SET. If no transaction are currently 137 | * running, the set will be immediate, otherwise, it will be done at the end of the current transaction 138 | * when services_update_pipes will be called. 139 | */ 140 | void services_set_battery_battery_level(uint8_t src); 141 | 142 | /** @brief function to trig pending transaction on pipes 143 | * @details This function check for each pipe if it has a pending transaction (send/rx_request/ack) 144 | * and if so executes this transaction. 145 | * This function should be called in the APP_RUN state of the process function of the application. 146 | */ 147 | void services_update_pipes(void); 148 | 149 | #define NB_SETUP_MESSAGES 28 150 | #define SETUP_MESSAGES_CONTENT {\ 151 | {0x00,\ 152 | {\ 153 | 0x07,0x06,0x00,0x00,0x03,0x02,0x41,0xd7,\ 154 | },\ 155 | },\ 156 | {0x00,\ 157 | {\ 158 | 0x1f,0x06,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x09,0x00,0x09,0x01,0x01,0x00,0x00,0x06,0x00,0x00,\ 159 | 0x81,0x0d,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 160 | },\ 161 | },\ 162 | {0x00,\ 163 | {\ 164 | 0x1f,0x06,0x10,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 165 | 0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x12,0x03,0x90,0x00,0x64,\ 166 | },\ 167 | },\ 168 | {0x00,\ 169 | {\ 170 | 0x1f,0x06,0x10,0x38,0x02,0xff,0x02,0x58,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 171 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 172 | },\ 173 | },\ 174 | {0x00,\ 175 | {\ 176 | 0x05,0x06,0x10,0x54,0x00,0x00,\ 177 | },\ 178 | },\ 179 | {0x00,\ 180 | {\ 181 | 0x1f,0x06,0x20,0x00,0x04,0x04,0x02,0x02,0x00,0x01,0x28,0x00,0x01,0x00,0x18,0x04,0x04,0x05,0x05,0x00,\ 182 | 0x02,0x28,0x03,0x01,0x0e,0x03,0x00,0x00,0x2a,0x04,0x14,0x0f,\ 183 | },\ 184 | },\ 185 | {0x00,\ 186 | {\ 187 | 0x1f,0x06,0x20,0x1c,0x0f,0x00,0x03,0x2a,0x00,0x01,0x4e,0x6f,0x72,0x64,0x69,0x63,0x20,0x48,0x52,0x4d,\ 188 | 0x20,0x56,0x31,0x2e,0x30,0x04,0x04,0x05,0x05,0x00,0x04,0x28,\ 189 | },\ 190 | },\ 191 | {0x00,\ 192 | {\ 193 | 0x1f,0x06,0x20,0x38,0x03,0x01,0x02,0x05,0x00,0x01,0x2a,0x06,0x04,0x03,0x02,0x00,0x05,0x2a,0x01,0x01,\ 194 | 0x00,0x00,0x04,0x04,0x05,0x05,0x00,0x06,0x28,0x03,0x01,0x02,\ 195 | },\ 196 | },\ 197 | {0x00,\ 198 | {\ 199 | 0x1f,0x06,0x20,0x54,0x07,0x00,0x04,0x2a,0x06,0x04,0x09,0x08,0x00,0x07,0x2a,0x04,0x01,0x90,0x01,0x20,\ 200 | 0x03,0x00,0x00,0xf6,0x01,0x04,0x04,0x02,0x02,0x00,0x08,0x28,\ 201 | },\ 202 | },\ 203 | {0x00,\ 204 | {\ 205 | 0x1f,0x06,0x20,0x70,0x00,0x01,0x01,0x18,0x04,0x04,0x02,0x02,0x00,0x09,0x28,0x00,0x01,0x0a,0x18,0x04,\ 206 | 0x04,0x05,0x05,0x00,0x0a,0x28,0x03,0x01,0x02,0x0b,0x00,0x25,\ 207 | },\ 208 | },\ 209 | {0x00,\ 210 | {\ 211 | 0x1f,0x06,0x20,0x8c,0x2a,0x06,0x04,0x05,0x04,0x00,0x0b,0x2a,0x25,0x01,0x31,0x35,0x38,0x37,0x06,0x04,\ 212 | 0x08,0x07,0x00,0x0c,0x29,0x04,0x01,0x19,0x00,0x00,0x00,0x01,\ 213 | },\ 214 | },\ 215 | {0x00,\ 216 | {\ 217 | 0x1f,0x06,0x20,0xa8,0x00,0x00,0x04,0x04,0x05,0x05,0x00,0x0d,0x28,0x03,0x01,0x02,0x0e,0x00,0x29,0x2a,\ 218 | 0x06,0x04,0x15,0x14,0x00,0x0e,0x2a,0x29,0x01,0x4e,0x6f,0x72,\ 219 | },\ 220 | },\ 221 | {0x00,\ 222 | {\ 223 | 0x1f,0x06,0x20,0xc4,0x64,0x69,0x63,0x53,0x65,0x6d,0x69,0x63,0x6f,0x6e,0x64,0x75,0x63,0x74,0x6f,0x72,\ 224 | 0x00,0x06,0x04,0x08,0x07,0x00,0x0f,0x29,0x04,0x01,0x19,0x00,\ 225 | },\ 226 | },\ 227 | {0x00,\ 228 | {\ 229 | 0x1f,0x06,0x20,0xe0,0x00,0x00,0x01,0x00,0x00,0x04,0x04,0x05,0x05,0x00,0x10,0x28,0x03,0x01,0x02,0x11,\ 230 | 0x00,0x23,0x2a,0x06,0x04,0x09,0x08,0x00,0x11,0x2a,0x23,0x01,\ 231 | },\ 232 | },\ 233 | {0x00,\ 234 | {\ 235 | 0x1f,0x06,0x20,0xfc,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x04,0x04,0x05,0x05,0x00,0x12,0x28,0x03,\ 236 | 0x01,0x02,0x13,0x00,0x26,0x2a,0x06,0x04,0x05,0x04,0x00,0x13,\ 237 | },\ 238 | },\ 239 | {0x00,\ 240 | {\ 241 | 0x1f,0x06,0x21,0x18,0x2a,0x26,0x01,0x30,0x31,0x2e,0x31,0x06,0x04,0x08,0x07,0x00,0x14,0x29,0x04,0x01,\ 242 | 0x19,0x00,0x00,0x00,0x01,0x00,0x00,0x04,0x04,0x02,0x02,0x00,\ 243 | },\ 244 | },\ 245 | {0x00,\ 246 | {\ 247 | 0x1f,0x06,0x21,0x34,0x15,0x28,0x00,0x01,0x0d,0x18,0x04,0x04,0x05,0x05,0x00,0x16,0x28,0x03,0x01,0x10,\ 248 | 0x17,0x00,0x37,0x2a,0x14,0x00,0x13,0x00,0x00,0x17,0x2a,0x37,\ 249 | },\ 250 | },\ 251 | {0x00,\ 252 | {\ 253 | 0x1f,0x06,0x21,0x50,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 254 | 0x00,0x00,0x00,0x00,0x46,0x14,0x03,0x02,0x00,0x18,0x29,0x02,\ 255 | },\ 256 | },\ 257 | {0x00,\ 258 | {\ 259 | 0x1f,0x06,0x21,0x6c,0x01,0x00,0x00,0x04,0x04,0x05,0x05,0x00,0x19,0x28,0x03,0x01,0x02,0x1a,0x00,0x38,\ 260 | 0x2a,0x06,0x04,0x02,0x01,0x00,0x1a,0x2a,0x38,0x01,0x03,0x04,\ 261 | },\ 262 | },\ 263 | {0x00,\ 264 | {\ 265 | 0x1f,0x06,0x21,0x88,0x04,0x05,0x05,0x00,0x1b,0x28,0x03,0x01,0x08,0x1c,0x00,0x39,0x2a,0x46,0x10,0x02,\ 266 | 0x01,0x00,0x1c,0x2a,0x39,0x01,0x00,0x04,0x04,0x02,0x02,0x00,\ 267 | },\ 268 | },\ 269 | {0x00,\ 270 | {\ 271 | 0x1f,0x06,0x21,0xa4,0x1d,0x28,0x00,0x01,0x0f,0x18,0x04,0x04,0x05,0x05,0x00,0x1e,0x28,0x03,0x01,0x12,\ 272 | 0x1f,0x00,0x19,0x2a,0x16,0x04,0x02,0x01,0x00,0x1f,0x2a,0x19,\ 273 | },\ 274 | },\ 275 | {0x00,\ 276 | {\ 277 | 0x11,0x06,0x21,0xc0,0x01,0x64,0x46,0x14,0x03,0x02,0x00,0x20,0x29,0x02,0x01,0x00,0x00,0x00,\ 278 | },\ 279 | },\ 280 | {0x00,\ 281 | {\ 282 | 0x1f,0x06,0x40,0x00,0x2a,0x00,0x01,0x00,0x00,0x04,0x00,0x03,0x00,0x00,0x2a,0x25,0x01,0x00,0x80,0x04,\ 283 | 0x00,0x0b,0x00,0x00,0x2a,0x29,0x01,0x00,0x80,0x04,0x00,0x0e,\ 284 | },\ 285 | },\ 286 | {0x00,\ 287 | {\ 288 | 0x1f,0x06,0x40,0x1c,0x00,0x00,0x2a,0x23,0x01,0x00,0x80,0x04,0x00,0x11,0x00,0x00,0x2a,0x26,0x01,0x00,\ 289 | 0x80,0x04,0x00,0x13,0x00,0x00,0x2a,0x37,0x01,0x00,0x02,0x04,\ 290 | },\ 291 | },\ 292 | {0x00,\ 293 | {\ 294 | 0x1f,0x06,0x40,0x38,0x00,0x17,0x00,0x18,0x2a,0x38,0x01,0x00,0x80,0x04,0x00,0x1a,0x00,0x00,0x2a,0x39,\ 295 | 0x01,0x00,0x10,0x04,0x00,0x1c,0x00,0x00,0x2a,0x19,0x01,0x00,\ 296 | },\ 297 | },\ 298 | {0x00,\ 299 | {\ 300 | 0x09,0x06,0x40,0x54,0x82,0x04,0x00,0x1f,0x00,0x20,\ 301 | },\ 302 | },\ 303 | {0x00,\ 304 | {\ 305 | 0x1e,0x06,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 306 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ 307 | },\ 308 | },\ 309 | {0x00,\ 310 | {\ 311 | 0x06,0x06,0xf0,0x00,0x03,0x10,0x44,\ 312 | },\ 313 | },\ 314 | } 315 | 316 | #endif 317 | --------------------------------------------------------------------------------