void aci_loop() { // We enter the if statement only when there is a ACI event available to be processed if (lib_aci_event_get(&aci_state, &aci_data)) { aci_evt_t * aci_evt; aci_evt = &aci_data.evt; switch(aci_evt->evt_opcode) { /** As soon as you reset the nRF8001 you will get an ACI Device Started Event */ case ACI_EVT_DEVICE_STARTED: { aci_state.data_credit_total = aci_evt->params.device_started.credit_available; switch(aci_evt->params.device_started.device_mode) { case ACI_DEVICE_SETUP: /** When the device is in the setup mode */ Serial.println(F("Evt Device Started: Setup")); if (ACI_STATUS_TRANSACTION_COMPLETE != do_aci_setup(&aci_state)) { Serial.println(F("Error in ACI Setup")); } break; case ACI_DEVICE_STANDBY: Serial.println(F("Evt Device Started: Standby")); { //Manage the bond in EEPROM of the AVR { uint8_t eeprom_status = 0; eeprom_status = EEPROM.read(0); if (eeprom_status != 0x00) { Serial.println(F("Previous Bond present. Restoring")); Serial.println(F("Using existing bond stored in EEPROM.")); Serial.println(F(" To delete the bond stored in EEPROM, connect Pin 6 to 3.3v and Reset.")); Serial.println(F(" Make sure that the bond on the phone/PC is deleted as well.")); //We must have lost power and restarted and must restore the bonding infromation using the ACI Write Dynamic Data if (ACI_STATUS_TRANSACTION_COMPLETE == bond_data_restore(&aci_state, eeprom_status, &bonded_first_time)) { Serial.println(F("Bond restored successfully")); } else { Serial.println(F("Bond restore failed. Delete the bond and try again.")); } } } // Start bonding as all proximity devices need to be bonded to be usable if (ACI_BOND_STATUS_SUCCESS != aci_state.bonded) { lib_aci_bond(180/* in seconds */, 0x0050 /* advertising interval 50ms*/); Serial.println(F("No Bond present in EEPROM.")); Serial.println(F("Advertising started : Waiting to be connected and bonded")); } else { //connect to an already bonded device //Use lib_aci_direct_connect for faster re-connections with PC, not recommended to use with iOS/OS X lib_aci_connect(100/* in seconds */, 0x0020 /* advertising interval 20ms*/); Serial.println(F("Already bonded : Advertising started : Waiting to be connected")); } } break; } } break; //ACI Device Started Event case ACI_EVT_CMD_RSP: //If an ACI command response event comes with an error -> stop if (ACI_STATUS_SUCCESS != aci_evt->params.cmd_rsp.cmd_status) { //ACI ReadDynamicData and ACI WriteDynamicData will have status codes of //TRANSACTION_CONTINUE and TRANSACTION_COMPLETE //all other ACI commands will have status code of ACI_STATUS_SCUCCESS for a successful command Serial.print(F("ACI Command ")); Serial.println(aci_evt->params.cmd_rsp.cmd_opcode, HEX); Serial.println(F("Evt Cmd respone: Error. Arduino is in an while(1); loop")); while (1); } if (ACI_CMD_GET_DEVICE_VERSION == aci_evt->params.cmd_rsp.cmd_opcode) { //Store the version and configuration information of the nRF8001 in the Hardware Revision String Characteristic lib_aci_set_local_data(&aci_state, PIPE_DEVICE_INFORMATION_HARDWARE_REVISION_STRING_SET, (uint8_t *)&(aci_evt->params.cmd_rsp.params.get_device_version), sizeof(aci_evt_cmd_rsp_params_get_device_version_t)); } break; case ACI_EVT_CONNECTED: Serial.println(F("Evt Connected")); Counter = BUZZER_OFF; aci_state.data_credit_available = aci_state.data_credit_total; timing_change_done = false; /* Get the device version of the nRF8001 and store it in the Hardware Revision String */ lib_aci_device_version(); break; case ACI_EVT_BOND_STATUS: aci_state.bonded = aci_evt->params.bond_status.status_code; break; case ACI_EVT_PIPE_STATUS: Serial.println(F("Evt Pipe Status")); //Link is encrypted when the PIPE_LINK_LOSS_ALERT_ALERT_LEVEL_RX_ACK_AUTO is available if ((false == timing_change_done) && lib_aci_is_pipe_available(&aci_state, PIPE_LINK_LOSS_ALERT_ALERT_LEVEL_RX_ACK_AUTO)) { lib_aci_change_timing_GAP_PPCP(); // change the timing on the link as specified in the nRFgo studio -> nRF8001 conf. -> GAP. // Used to increase or decrease bandwidth timing_change_done = true; } // The pipe will be available only in an encrpyted link to the phone if ((ACI_BOND_STATUS_SUCCESS == aci_state.bonded) && (lib_aci_is_pipe_available(&aci_state, PIPE_LINK_LOSS_ALERT_ALERT_LEVEL_RX_ACK_AUTO)) && (lib_aci_is_pipe_available(&aci_state, PIPE_IMMEDIATE_ALERT_ALERT_LEVEL_RX))) { //Note: This may be called multiple times after the Arduino has connected to the right phone Serial.println(F("phone Detected.")); Serial.println(F("Do more stuff here. when your phone is detected")); // TODO: Add something here!! } break; case ACI_EVT_TIMING: Serial.println(F("Evt link connection interval changed")); //Disconnect as soon as we are bonded and required pipes are available //This is used to store the bonding info on disconnect and then re-connect to verify the bond if((ACI_BOND_STATUS_SUCCESS == aci_state.bonded) && (true == bonded_first_time) && (GAP_PPCP_MAX_CONN_INT >= aci_state.connection_interval) && (GAP_PPCP_MIN_CONN_INT <= aci_state.connection_interval) && //Timing change already done: Provide time for the the peer to finish (lib_aci_is_pipe_available(&aci_state, PIPE_LINK_LOSS_ALERT_ALERT_LEVEL_RX_ACK_AUTO)) && (lib_aci_is_pipe_available(&aci_state, PIPE_IMMEDIATE_ALERT_ALERT_LEVEL_RX))) { lib_aci_disconnect(&aci_state, ACI_REASON_TERMINATE); } break; case ACI_EVT_DISCONNECTED: Serial.println(F("Evt Disconnected. Link Lost or Advertising timed out")); if (ACI_BOND_STATUS_SUCCESS == aci_state.bonded) { if (ACI_STATUS_EXTENDED == aci_evt->params.disconnected.aci_status) //Link was disconnected { if (bonded_first_time) { bonded_first_time = false; //Store away the dynamic data of the nRF8001 in the Flash or EEPROM of the MCU // so we can restore the bond information of the nRF8001 in the event of power loss if (bond_data_read_store(&aci_state)) { Serial.println(F("Dynamic Data read and stored successfully")); } } if (0x24 == aci_evt->params.disconnected.btle_status) { //The error code appears when phone or Arduino has deleted the pairing/bonding information. //The Arduino stores the bonding information in EEPROM, which is deleted only by // the user action of connecting pin 6 to 3.3v and then followed by a reset. //While deleting bonding information delete on the Arduino and on the phone. Serial.println(F("phone/Arduino has deleted the bonding/pairing information")); } proximity_disconect_evt_rcvd (aci_evt->params.disconnected.btle_status); } lib_aci_connect(180/* in seconds */, 0x0100 /* advertising interval 100ms*/); Serial.println(F("Using existing bond stored in EEPROM.")); Serial.println(F(" To delete the bond stored in EEPROM, connect Pin 6 to 3.3v and Reset.")); Serial.println(F(" Make sure that the bond on the phone/PC is deleted as well.")); Serial.println(F("Advertising started. Connecting.")); } else { //There is no existing bond. Try to bond. lib_aci_bond(180/* in seconds */, 0x0050 /* advertising interval 50ms*/); Serial.println(F("Advertising started. Bonding.")); } proximity_disconect_evt_rcvd (aci_evt->params.disconnected.btle_status); break; case ACI_EVT_DATA_RECEIVED: Serial.print(F("Pipe #")); Serial.print(aci_evt->params.data_received.rx_data.pipe_number, DEC); Serial.print(F("-> ")); Serial.println(aci_evt->params.data_received.rx_data.aci_data[0], DEC); link_loss_pipes_updated_evt_rcvd(aci_evt->params.data_received.rx_data.pipe_number, &aci_evt->params.data_received.rx_data.aci_data[0]); immediate_alert_pipes_updated_evt_rcvd(aci_evt->params.data_received.rx_data.pipe_number, &aci_evt->params.data_received.rx_data.aci_data[0]); button_alert_pipes_updated_evt_rcvd(aci_evt->params.data_received.rx_data.pipe_number, &aci_evt->params.data_received.rx_data.aci_data[0]); break; case ACI_EVT_DATA_CREDIT: aci_state.data_credit_available = aci_state.data_credit_available + aci_evt->params.data_credit.credit; break; case ACI_EVT_PIPE_ERROR: //See the appendix in the nRF8001 Product Specication for details on the error codes Serial.print(F("ACI Evt Pipe Error: Pipe #:")); Serial.print(aci_evt->params.pipe_error.pipe_number, DEC); Serial.print(F(" Pipe Error Code: 0x")); Serial.println(aci_evt->params.pipe_error.error_code, HEX); //Increment the credit available as the data packet was not sent. //The pipe error also represents the Attribute protocol Error Response sent from the peer and that should not be counted //for the credit. if (ACI_STATUS_ERROR_PEER_ATT_ERROR != aci_evt->params.pipe_error.error_code) { aci_state.data_credit_available++; } break; } } else { //Serial.println(F("No ACI Events available")); // No event in the ACI Event queue and if there is no event in the ACI command queue the arduino can go to sleep // Arduino can go to sleep now // Wakeup from sleep from the RDYN line } }
void ble_disconnect(void) { lib_aci_disconnect(&aci_state, ACI_REASON_TERMINATE); }
bool Adafruit_BLE_UART::uart_process_control_point_rx(uint8_t *byte, uint8_t length) { bool status = false; aci_ll_conn_params_t *conn_params; if (lib_aci_is_pipe_available(&aci_state, PIPE_UART_OVER_BTLE_UART_CONTROL_POINT_TX) ) { Serial.println(*byte, HEX); switch(*byte) { /* Queues a ACI Disconnect to the nRF8001 when this packet is received. May cause some of the UART packets being sent to be dropped */ case UART_OVER_BLE_DISCONNECT: /* Parameters: None */ lib_aci_disconnect(&aci_state, ACI_REASON_TERMINATE); status = true; break; /* Queues an ACI Change Timing to the nRF8001 */ case UART_OVER_BLE_LINK_TIMING_REQ: /* Parameters: Connection interval min: 2 bytes Connection interval max: 2 bytes Slave latency: 2 bytes Timeout: 2 bytes Same format as Peripheral Preferred Connection Parameters (See nRFgo studio -> nRF8001 Configuration -> GAP Settings Refer to the ACI Change Timing Request in the nRF8001 Product Specifications */ conn_params = (aci_ll_conn_params_t *)(byte+1); lib_aci_change_timing( conn_params->min_conn_interval, conn_params->max_conn_interval, conn_params->slave_latency, conn_params->timeout_mult); status = true; break; /* Clears the RTS of the UART over BLE */ case UART_OVER_BLE_TRANSMIT_STOP: /* Parameters: None */ uart_over_ble.uart_rts_local = false; status = true; break; /* Set the RTS of the UART over BLE */ case UART_OVER_BLE_TRANSMIT_OK: /* Parameters: None */ uart_over_ble.uart_rts_local = true; status = true; break; } } return status; }
/** * This is the default process control point method. * * @param buffer - the data buffer * @param length - the data length */ bool NRF8001Driver::_uart_process_control_point(uint8_t* buffer, uint8_t length){ // Status container bool status = false; // Connection parameters pointer aci_ll_conn_params_t* conn_params; // If hte TX pipe is available if (lib_aci_is_pipe_available(this->_aci_state, PIPE_UART_OVER_BTLE_UART_CONTROL_POINT_TX) ){ // We switch on the buffer pointer switch(*buffer){ /** * Queues a ACI Disconnect to the nRF8001 when this packet is received. * May cause some of the UART packets being sent to be dropped */ case UART_OVER_BLE_DISCONNECT: /** * Parameters: * None */ lib_aci_disconnect(this->_aci_state, ACI_REASON_TERMINATE); status = true; break; /** * Queues an ACI Change Timing to the nRF8001 */ case UART_OVER_BLE_LINK_TIMING_REQ: /** * Parameters: * Connection interval min: 2 bytes * Connection interval max: 2 bytes * Slave latency: 2 bytes * Timeout: 2 bytes * Same format as Peripheral Preferred Connection Parameters * (See nRFgo studio -> nRF8001 Configuration -> GAP Settings) * Refer to the ACI Change Timing Request in the nRF8001 Product Specifications */ #ifdef DEBUG Serial.print(F("UART over BLE linkin timing request.")); #endif conn_params = (aci_ll_conn_params_t*)(buffer + 1); lib_aci_change_timing(conn_params->min_conn_interval, conn_params->max_conn_interval, conn_params->slave_latency, conn_params->timeout_mult); status = true; break; /** * Clears the RTS of the UART over BLE */ case UART_OVER_BLE_TRANSMIT_STOP: /** * Parameters: * None */ this->_uart_over_ble.uart_rts_local = false; status = true; break; /** * Set the RTS of the UART over BLE */ case UART_OVER_BLE_TRANSMIT_OK: /** * Parameters: * None */ this->_uart_over_ble.uart_rts_local = true; status = true; break; } } return status; }
/** * This method is used to generic system wide actions * * @param command - The issued command */ void ConnectionProtocolHandler::server(uint8_t command, void* object){ //! Cast the object ConnectionProtocolHandler* access = (ConnectionProtocolHandler*) object; //! We switch on the command switch(command){ //! We reset the device case CONNECT_CALLBACK: // Set the new connection state and write the ACK access->_utils->connection_state = CONNECTED_STATE; access->_rf->write((uint8_t*)OK_CALLBACK, 0x01); #ifdef DEBUG Serial.println("Connected from Remote node."); #endif break; //! We start the device case REBOOT_CALLBACK: // Set the new connection state and write the ACK access->_utils->connection_state = CLOSED_STATE; access->_rf->write((uint8_t*)OK_CALLBACK, 0x01); #ifdef DEBUG Serial.println("Rebooting..."); #endif // Stop the main thread access->_utils->start_engine = false; // Disconnect and reset while(!lib_aci_disconnect(access->_rf->_aci_state, ACI_REASON_TERMINATE)); while(!lib_aci_radio_reset()); // Delay and then reboot the system delay(1000); access->_utils->reboot(); break; //! We stop the device case DISCONNECT_CALLBACK: // Set the new connection state and write the ACK access->_utils->connection_state = DISCONNECTED_STATE; access->_rf->write((uint8_t*)OK_CALLBACK, 0x01); // Disconnect while(!lib_aci_disconnect(access->_rf->_aci_state, ACI_REASON_TERMINATE)); #ifdef DEBUG Serial.println("Disconnected from Remote node."); #endif // We are now connected access->_utils->start_engine = true; break; //! Nothing happens default: break; } }