#include #include #include #include "services.h" #include "app.h" static services_pipe_type_mapping_t services_pipe_type_mapping[NUMBER_OF_PIPES] = SERVICES_PIPE_TYPE_MAPPING_CONTENT; static hal_aci_data_t setup_msgs[NB_SETUP_MESSAGES] PROGMEM = SETUP_MESSAGES_CONTENT; static struct aci_state_t aci_state; static hal_aci_evt_t aci_data; static bool timing_change_done = false; void __ble_assert(const char *file, uint16_t line) { Serial.print("ERROR "); Serial.print(file); Serial.print(": "); Serial.print(line); Serial.print("\n"); while(1); } void setup() { #if defined(BLEND_MICRO_8MHZ) // As the F_CPU = 8000000UL, the USB core make the PLLCSR = 0x02 // But the external xtal is 16000000Hz, so correct it here. PLLCSR |= 0x10; // Need 16 MHz xtal while (!(PLLCSR & (1<evt_opcode) { case ACI_EVT_DEVICE_STARTED: Serial.println(F("ACI_EVT_DEVICE_STARTED")); aci_state.data_credit_total = aci_evt->params.device_started.credit_available; Serial.print(F("aci_state.data_credit_total=")); Serial.println(aci_state.data_credit_total, DEC); switch(aci_evt->params.device_started.device_mode) { case ACI_DEVICE_SETUP: Serial.println(F("ACI_DEVICE_SETUP")); rf_started = true; setup_required = true; break; case ACI_DEVICE_STANDBY: Serial.println(F("ACI_DEVICE_STANDBY")); if (aci_evt->params.device_started.hw_error) { delay(20); // Magic number used to make sure the HW error event is handled correctly. } else { lib_aci_connect(180/* in seconds */, 0x0050 /* advertising interval 50ms*/); // lib_aci_broadcast(10/* in seconds */, 0x0100 /* advertising interval 100ms */); ret = lib_aci_open_adv_pipe(PIPE_BATTERY_BATTERY_LEVEL_BROADCAST); ret = lib_aci_open_adv_pipe(PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_BROADCAST); Serial.print(F("Advertising started, ret=")); Serial.println(ret, DEC); } break; case ACI_DEVICE_INVALID: case ACI_DEVICE_TEST: case ACI_DEVICE_SLEEP: // Ignored break; } break; case ACI_EVT_CMD_RSP: // Serial.println(F("ACI_EVT_CMD_RSP")); // Serial.print(F("aci_evt->params.cmd_rsp.cmd_opcode=")); // Serial.println(aci_evt->params.cmd_rsp.cmd_opcode, HEX); // Serial.print(F("aci_evt->params.cmd_rsp.cmd_status=")); // Serial.println(aci_evt->params.cmd_rsp.cmd_status, HEX); //If an ACI command response event comes with an error -> stop if (aci_evt->params.cmd_rsp.cmd_status != ACI_STATUS_SUCCESS) { //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.print(F("Evt Cmd respone: Status ")); // Serial.println(aci_evt->params.cmd_rsp.cmd_status, HEX); } if (aci_evt->params.cmd_rsp.cmd_opcode == ACI_CMD_GET_DEVICE_VERSION) { //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("ACI_EVT_CONNECTED")); timing_change_done = false; aci_state.data_credit_available = aci_state.data_credit_total; Serial.print(F("aci_state.data_credit_available=")); Serial.println(aci_state.data_credit_available, DEC); // Get the device version of the nRF8001 and store it in the Hardware Revision String. // This will trigger a ACI_CMD_GET_DEVICE_VERSION. lib_aci_device_version(); break; case ACI_EVT_PIPE_STATUS: Serial.println(F("ACI_EVT_PIPE_STATUS")); show_pipes(); break; case ACI_EVT_TIMING: Serial.println(F("ACI_EVT_TIMING")); break; case ACI_EVT_DISCONNECTED: Serial.println(F("ACI_EVT_DISCONNECTED")); lib_aci_connect(180/* in seconds */, 0x0100 /* advertising interval 100ms*/); ret = lib_aci_open_adv_pipe(PIPE_BATTERY_BATTERY_LEVEL_BROADCAST); ret = lib_aci_open_adv_pipe(PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_BROADCAST); Serial.print(F("Advertising started, ret=")); Serial.println(ret, DEC); // Serial.print(F("ACI_EVT_DISCONNECTED: ")); // Serial.println(ACI_STATUS_ERROR_ADVT_TIMEOUT == aci_evt->params.disconnected.aci_status ? // F("Broadcasting timed out") : F("Link loss")); // lib_aci_broadcast(10/* in seconds */, 0x0100 /* advertising interval 100ms */); // Serial.print(F("Broadcast advertising started, ret=")); // Serial.println(ret, DEC); break; case ACI_EVT_DATA_RECEIVED: pipe_number = aci_evt->params.data_received.rx_data.pipe_number; Serial.print(F("ACI_EVT_DATA_RECEIVED: pipe_number=")); Serial.println(pipe_number, DEC); /* if (pipe_number == PIPE_FIKEN_STATUS_PANEL_GAUGE_DATA_RX_ACK) { if (aci_evt->len != PIPE_FIKEN_STATUS_PANEL_GAUGE_DATA_RX_ACK_MAX_SIZE) { break; } on_gauge_data(aci_evt->params.data_received.rx_data.aci_data, aci_evt->len - 2); lib_aci_send_ack(&aci_state, PIPE_FIKEN_STATUS_PANEL_GAUGE_DATA_RX_ACK); } if (pipe_number == PIPE_FIKEN_STATUS_PANEL_GAUGE_CONTROL_RX) { uint8_t len = aci_evt->len - 2; // if (aci_evt->len != PIPE_FIKEN_STATUS_PANEL_GAUGE_CONTROL_RX_MAX_SIZE) { // break; // } on_gauge_ctrl(aci_evt->params.data_received.rx_data.aci_data, len); } */ break; case ACI_EVT_DATA_CREDIT: Serial.println(F("ACI_EVT_DATA_CREDIT")); aci_state.data_credit_available = aci_state.data_credit_available + aci_evt->params.data_credit.credit; Serial.print(F("aci_state.data_credit_available=")); Serial.println(aci_state.data_credit_available, DEC); break; case ACI_EVT_PIPE_ERROR: Serial.println(F("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; case ACI_EVT_HW_ERROR: Serial.println(F("ACI_EVT_HW_ERROR")); Serial.print(F("HW error: ")); Serial.println(aci_evt->params.hw_error.line_num, DEC); for(uint8_t counter = 0; counter <= (aci_evt->len - 3); counter++) { Serial.write(aci_evt->params.hw_error.file_name[counter]); //uint8_t file_name[20]; } Serial.println(); lib_aci_connect(180/* in seconds */, 0x0050 /* advertising interval 50ms*/); Serial.println(F("Advertising started")); break; case ACI_EVT_INVALID: Serial.println(F("ACI_EVT_INVALID")); break; case ACI_EVT_ECHO: Serial.println(F("ACI_EVT_ECHO")); break; case ACI_EVT_BOND_STATUS: Serial.println(F("ACI_EVT_BOND_STATUS")); break; case ACI_EVT_DATA_ACK: Serial.println(F("ACI_EVT_DATA_ACK")); break; case ACI_EVT_DISPLAY_PASSKEY: Serial.println(F("ACI_EVT_DISPLAY_PASSKEY")); break; case ACI_EVT_KEY_REQUEST: Serial.println(F("ACI_EVT_KEY_REQUEST")); 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 } /* setup_required is set to true when the device starts up and enters setup mode. * It indicates that do_aci_setup() should be called. The flag should be cleared if * do_aci_setup() returns ACI_STATUS_TRANSACTION_COMPLETE. */ if (setup_required) { int ret = do_aci_setup(&aci_state); Serial.print(F("do_aci_setup ret=")); Serial.println(ret, DEC); if (SETUP_SUCCESS == ret) { setup_required = false; } } } static uint8_t value = 0; void loop() { static unsigned long last = 0, now; aci_loop(); if (Serial.available()) { Serial.write(Serial.read()); } now = millis(); if (now - last > 3000) { last = now; if (!rf_started) { static int count = 0; static boolean reset_attempted = false; count++; if (!reset_attempted) { if (count == 3) { reset_attempted = true; Serial.println(F("RF did not start, resetting RF")); // asm volatile ("jmp 0"); // lib_aci_pin_reset(); setup_rf(); count = 0; return; } else { Serial.println(F("waiting for RF to start")); } } /**/ } else if (!setup_required) { value++; // lib_aci_set_local_data(&aci_state, PIPE_FIKEN_STATUS_PANEL_GAUGE_DATA_SET, &value, 1); // Serial.print(F("value=")); // Serial.println(value, HEX); // show_pipes(); } } on_loop(); } void show_pipes() { for (uint8_t i = 1; i <= NUMBER_OF_PIPES; i++) { uint8_t x = lib_aci_is_pipe_available(&aci_state, i); Serial.print(F("pipe #")); Serial.print(i, DEC); Serial.print(F(", available=?")); Serial.println(x, DEC); } } bool tx__moisture(sm_res *res) { static const uint8_t pipe = PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_TX; uint8_t *data = (uint8_t *)res; uint8_t len = 2 + res->len; bool status = false; bool available = lib_aci_is_pipe_available(&aci_state, pipe); Serial.print(F("tx_soil_moisture, len=")); Serial.println(len, DEC); Serial.print(F("aci_state.data_credit_available=")); Serial.println(aci_state.data_credit_available, DEC); Serial.print(F("available=")); Serial.println(available, DEC); if (available && aci_state.data_credit_available > 0) { status = lib_aci_send_data(pipe, data, len); if (status) { aci_state.data_credit_available--; } } return status; } void notify_battery_level(uint8_t value) { static const uint8_t pipe = PIPE_BATTERY_BATTERY_LEVEL_SET; Serial.print(F("notify_battery_level, value=")); Serial.println(value, DEC); value = value % 101; lib_aci_set_local_data(&aci_state, pipe, &value, 1); } void notify_soil_moisture(sm_res *res) { static const uint8_t pipe = PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_SET; uint8_t *data = (uint8_t *)res; uint8_t len = 2 + res->len; bool status = false; Serial.print(F("notify_soil_moisture, len=")); Serial.println(len, DEC); Serial.print(F("aci_state.data_credit_available=")); Serial.println(aci_state.data_credit_available, DEC); // bool available = lib_aci_is_pipe_available(&aci_state, pipe); // Serial.print(F("available=")); // Serial.println(available, DEC); status = lib_aci_set_local_data(&aci_state, pipe, data, len); }