1 /*
2  * Author: Yevgeniy Kiveisha <yevgeniy.kiveisha@intel.com>
3  * Copyright (c) 2014 Intel Corporation.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include <unistd.h>
26 #include <iostream>
27 #include "nrf8001.h"
28 #include "nrf8001-helloworld.h"
29 #include <lib_aci.h>
30 #include <aci_setup.h>
31 #include <signal.h>
32 #include "uart_over_ble.h"
33 
34 /*
35  * You can use the nRF UART app in the Apple iOS app store and Google Play for Android 4.3 for Samsung Galaxy S4
36  * with this helloworld application
37  */
38 
39 #ifdef SERVICES_PIPE_TYPE_MAPPING_CONTENT
40     static services_pipe_type_mapping_t
41         services_pipe_type_mapping[NUMBER_OF_PIPES] = SERVICES_PIPE_TYPE_MAPPING_CONTENT;
42 #else
43     #define NUMBER_OF_PIPES 0
44     static services_pipe_type_mapping_t * services_pipe_type_mapping = NULL;
45 #endif
46 
47 /**
48  * Store the setup for the nRF8001 in the flash of the AVR to save on RAM
49  */
50 static hal_aci_data_t setup_msgs[NB_SETUP_MESSAGES] = SETUP_MESSAGES_CONTENT;
51 
52 /**
53  * aci_struct that will contain
54  * total initial credits
55  * current credit
56  * current state of the aci (setup/standby/active/sleep)
57  * open remote pipe pending
58  * close remote pipe pending
59  * Current pipe available bitmap
60  * Current pipe closed bitmap
61  * Current connection interval, slave latency and link supervision timeout
62  * Current State of the the GATT client (Service Discovery)
63  * Status of the bond (R) Peer address
64  */
65 static struct aci_state_t aci_state;
66 
67 /**
68  * Temporary buffers for sending ACI commands
69  */
70 static hal_aci_evt_t  aci_data;
71 
72 /*
73 Timing change state variable
74 */
75 static bool timing_change_done          = false;
76 
77 /*
78 Used to test the UART TX characteristic notification
79 */
80 static uart_over_ble_t uart_over_ble;
81 static uint8_t         uart_buffer[20];
82 static uint8_t         uart_buffer_len = 0;
83 static uint8_t         dummychar = 0;
84 
85 void
sig_handler(int signo)86 sig_handler(int signo)
87 {
88     printf("got signal\n");
89     if (signo == SIGINT) {
90         printf("exiting application\n");
91     }
92 }
93 
94 void
init_aci_setup()95 init_aci_setup () {
96     /**
97      * Point ACI data structures to the the setup data that the nRFgo studio generated for the nRF8001
98      */
99     if (NULL != services_pipe_type_mapping) {
100         aci_state.aci_setup_info.services_pipe_type_mapping = &services_pipe_type_mapping[0];
101     } else {
102         aci_state.aci_setup_info.services_pipe_type_mapping = NULL;
103     }
104 
105     aci_state.aci_setup_info.number_of_pipes    = NUMBER_OF_PIPES;
106     aci_state.aci_setup_info.setup_msgs         = setup_msgs;
107     aci_state.aci_setup_info.num_setup_msgs     = NB_SETUP_MESSAGES;
108 }
109 
110 void
uart_over_ble_init(void)111 uart_over_ble_init (void) {
112     uart_over_ble.uart_rts_local = true;
113 }
114 
115 bool
uart_tx(uint8_t * buffer,uint8_t buffer_len)116 uart_tx (uint8_t *buffer, uint8_t buffer_len) {
117     bool status = false;
118 
119     if  (lib_aci_is_pipe_available(&aci_state, PIPE_UART_OVER_BTLE_UART_TX_TX) &&
120         (aci_state.data_credit_available >= 1)) {
121         status = lib_aci_send_data(PIPE_UART_OVER_BTLE_UART_TX_TX, buffer, buffer_len);
122         if (status) {
123             aci_state.data_credit_available--;
124         }
125     }
126 
127     return status;
128 }
129 
130 bool
uart_process_control_point_rx(uint8_t * byte,uint8_t length)131 uart_process_control_point_rx(uint8_t *byte, uint8_t length) {
132     bool status = false;
133     aci_ll_conn_params_t *conn_params;
134 
135     if (lib_aci_is_pipe_available(&aci_state, PIPE_UART_OVER_BTLE_UART_CONTROL_POINT_TX) ) {
136         switch (*byte) {
137             /*
138             Queues a ACI Disconnect to the nRF8001 when this packet is received.
139             May cause some of the UART packets being sent to be dropped
140             */
141             case UART_OVER_BLE_DISCONNECT:
142                 /*
143                 Parameters:
144                 None
145                 */
146                 lib_aci_disconnect(&aci_state, ACI_REASON_TERMINATE);
147                 status = true;
148                 break;
149 
150             /*
151             Queues an ACI Change Timing to the nRF8001
152             */
153             case UART_OVER_BLE_LINK_TIMING_REQ:
154                 /*
155                 Parameters:
156                 Connection interval min: 2 bytes
157                 Connection interval max: 2 bytes
158                 Slave latency:           2 bytes
159                 Timeout:                 2 bytes
160                 Same format as Peripheral Preferred Connection Parameters (See nRFgo studio -> nRF8001 Configuration -> GAP Settings
161                 Refer to the ACI Change Timing Request in the nRF8001 Product Specifications
162                 */
163                 conn_params = (aci_ll_conn_params_t *)(byte+1);
164                 lib_aci_change_timing( conn_params->min_conn_interval,
165                                         conn_params->max_conn_interval,
166                                         conn_params->slave_latency,
167                                         conn_params->timeout_mult);
168                 status = true;
169                 break;
170 
171             /*
172             Clears the RTS of the UART over BLE
173             */
174             case UART_OVER_BLE_TRANSMIT_STOP:
175                 /*
176                 Parameters:
177                 None
178                 */
179                 uart_over_ble.uart_rts_local = false;
180                 status = true;
181                 break;
182 
183 
184             /*
185             Set the RTS of the UART over BLE
186             */
187             case UART_OVER_BLE_TRANSMIT_OK:
188                 /*
189                 Parameters:
190                 None
191                 */
192                 uart_over_ble.uart_rts_local = true;
193                 status = true;
194                 break;
195         }
196     }
197 
198     return status;
199 }
200 
201 int
main(int argc,char ** argv)202 main(int argc, char **argv)
203 {
204     //! [Interesting]
205 
206     init_aci_setup ();
207     init_local_interfaces (&aci_state, 10, 8, 4);
208 
209     while (1) {
210         static bool setup_required = false;
211 
212         // We enter the if statement only when there is a ACI event available to be processed
213         if (lib_aci_event_get(&aci_state, &aci_data)) {
214             aci_evt_t * aci_evt;
215             aci_evt = &aci_data.evt;
216             switch(aci_evt->evt_opcode) {
217                 /**
218                 As soon as you reset the nRF8001 you will get an ACI Device Started Event
219                 */
220                 case ACI_EVT_DEVICE_STARTED: {
221                     aci_state.data_credit_total = aci_evt->params.device_started.credit_available;
222                     switch(aci_evt->params.device_started.device_mode) {
223                         case ACI_DEVICE_SETUP:
224                             /**
225                             When the device is in the setup mode
226                             */
227                             printf ("Evt Device Started: Setup \n");
228                             setup_required = true;
229                             break;
230 
231                         case ACI_DEVICE_STANDBY:
232                             printf ("Evt Device Started: Standby \n");
233                             // Looking for an iPhone by sending radio advertisements
234                             // When an iPhone connects to us we will get an ACI_EVT_CONNECTED event from the nRF8001
235                             if (aci_evt->params.device_started.hw_error) {
236                                 usleep (20000); //Handle the HW error event correctly.
237                             } else {
238                                 lib_aci_connect(0/* in seconds : 0 means forever */, 0x0050 /* advertising interval 50ms*/);
239                                 printf ("Advertising started \n");
240                             }
241                             break;
242                     }
243                 }
244                 break; // ACI Device Started Event
245 
246                 case ACI_EVT_CMD_RSP:
247                     //If an ACI command response event comes with an error -> stop
248                     if (ACI_STATUS_SUCCESS != aci_evt->params.cmd_rsp.cmd_status) {
249                         //ACI ReadDynamicData and ACI WriteDynamicData will have status codes of
250                         //TRANSACTION_CONTINUE and TRANSACTION_COMPLETE
251                         //all other ACI commands will have status code of ACI_STATUS_SCUCCESS for a successful command
252                         printf ("ACI_EVT_CMD_RSP \n");
253                     }
254                     if (ACI_CMD_GET_DEVICE_VERSION == aci_evt->params.cmd_rsp.cmd_opcode) {
255                         //Store the version and configuration information of the nRF8001 in the Hardware Revision String Characteristic
256                         lib_aci_set_local_data(&aci_state, PIPE_DEVICE_INFORMATION_HARDWARE_REVISION_STRING_SET,
257                         (uint8_t *)&(aci_evt->params.cmd_rsp.params.get_device_version), sizeof(aci_evt_cmd_rsp_params_get_device_version_t));
258                     }
259                     break;
260 
261                 case ACI_EVT_CONNECTED:
262                     printf ("ACI_EVT_CONNECTED");
263                     uart_over_ble_init ();
264                     timing_change_done = false;
265                     aci_state.data_credit_available = aci_state.data_credit_total;
266 
267                     /*
268                     Get the device version of the nRF8001 and store it in the Hardware Revision String
269                     */
270                     lib_aci_device_version();
271                     break;
272 
273                 case ACI_EVT_PIPE_STATUS:
274                     printf ("ACI_EVT_PIPE_STATUS \n");
275                     if (lib_aci_is_pipe_available(&aci_state, PIPE_UART_OVER_BTLE_UART_TX_TX) && (false == timing_change_done)) {
276                         lib_aci_change_timing_GAP_PPCP();   // change the timing on the link as specified in the nRFgo studio -> nRF8001 conf. -> GAP.
277                                                             // Used to increase or decrease bandwidth
278                         timing_change_done = true;
279 
280                         char hello[]="Hello World, works";
281                         uart_tx((uint8_t *)&hello[0], strlen(hello));
282                     }
283                     break;
284 
285                 case ACI_EVT_TIMING:
286                     printf ("Evt link connection interval changed \n");
287                     lib_aci_set_local_data(&aci_state,
288                                 PIPE_UART_OVER_BTLE_UART_LINK_TIMING_CURRENT_SET,
289                                 (uint8_t *)&(aci_evt->params.timing.conn_rf_interval), /* Byte aligned */
290                                 PIPE_UART_OVER_BTLE_UART_LINK_TIMING_CURRENT_SET_MAX_SIZE);
291                     break;
292 
293                 case ACI_EVT_DISCONNECTED:
294                     printf ("ACI_EVT_DISCONNECTED \n");
295                     lib_aci_connect(0/* in seconds  : 0 means forever */, 0x0050 /* advertising interval 50ms*/);
296                     printf ("Advertising started \n");
297                     break;
298 
299                 case ACI_EVT_DATA_RECEIVED:
300                     if (PIPE_UART_OVER_BTLE_UART_RX_RX == aci_evt->params.data_received.rx_data.pipe_number) {
301                         for(int i=0; i<aci_evt->len - 2; i++) {
302                           uart_buffer[i] = aci_evt->params.data_received.rx_data.aci_data[i];
303                         }
304 
305                         uart_buffer_len = aci_evt->len - 2;
306                         if (lib_aci_is_pipe_available(&aci_state, PIPE_UART_OVER_BTLE_UART_TX_TX)) {
307                         }
308                     }
309 
310                     if (PIPE_UART_OVER_BTLE_UART_CONTROL_POINT_RX == aci_evt->params.data_received.rx_data.pipe_number) {
311                         //Subtract for Opcode and Pipe number
312                         uart_process_control_point_rx(&aci_evt->params.data_received.rx_data.aci_data[0], aci_evt->len - 2);
313                     }
314 
315                     printf ("Incomming data - %s\n", uart_buffer);
316                     break;
317 
318                 case ACI_EVT_DATA_CREDIT:
319                     printf ("ACI_EVT_DATA_CREDIT \n");
320                     aci_state.data_credit_available = aci_state.data_credit_available + aci_evt->params.data_credit.credit;
321                     break;
322 
323                 case ACI_EVT_PIPE_ERROR:
324                     printf ("ACI_EVT_PIPE_ERROR \n");
325                     //Increment the credit available as the data packet was not sent.
326                     //The pipe error also represents the Attribute protocol Error Response sent from the peer and that should not be counted
327                     //for the credit.
328                     if (ACI_STATUS_ERROR_PEER_ATT_ERROR != aci_evt->params.pipe_error.error_code) {
329                         aci_state.data_credit_available++;
330                     }
331                     break;
332 
333                 case ACI_EVT_HW_ERROR:
334                     printf ("ACI_EVT_HW_ERROR \n");
335                     lib_aci_connect(0/* in seconds, 0 means forever */, 0x0050 /* advertising interval 50ms*/);
336                     printf ("Advertising started \n");
337                 break;
338 
339             }
340         }
341 
342         /* setup_required is set to true when the device starts up and enters setup mode.
343         * It indicates that do_aci_setup() should be called. The flag should be cleared if
344         * do_aci_setup() returns ACI_STATUS_TRANSACTION_COMPLETE.
345         */
346         if(setup_required) {
347             if (SETUP_SUCCESS == do_aci_setup(&aci_state)) {
348                 setup_required = false;
349             }
350         }
351 
352         usleep (100);
353     }
354 
355     close_local_interfaces (&aci_state);
356 
357     //! [Interesting]
358 
359     std::cout << "exiting application" << std::endl;
360 
361     return 0;
362 }
363