From 7c0e1f7ffb750813a788940fdd55e9800c2f701d Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Tue, 23 Dec 2014 13:38:57 +0100 Subject: o Initial import of trygvis.io soil moisture device. --- trygvisio_soil_moisture.ino | 420 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 420 insertions(+) create mode 100644 trygvisio_soil_moisture.ino (limited to 'trygvisio_soil_moisture.ino') diff --git a/trygvisio_soil_moisture.ino b/trygvisio_soil_moisture.ino new file mode 100644 index 0000000..b8b3bfd --- /dev/null +++ b/trygvisio_soil_moisture.ino @@ -0,0 +1,420 @@ +#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); +} + -- cgit v1.2.3