aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile10
-rw-r--r--README.md9
-rw-r--r--app.cpp91
-rw-r--r--app.h66
-rw-r--r--services.h213
-rw-r--r--services_lock.h217
-rw-r--r--trygvisio_soil_moisture.ino420
-rw-r--r--trygvisio_soil_moisture.xml167
8 files changed, 1193 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..d52f7b7
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,10 @@
+BOARD_TAG = leonardo
+#BOARD_TAG = blendmicro16
+#DEVICE_PORT = /dev/ttyACM0
+MONITOR_BAUDRATE = 115200
+MONITOR_PORT ?= /dev/ttyACM0
+
+ARDUINO_LIBS = BLE SPI
+
+ARDMK_DIR ?= /usr/share/arduino
+include $(ARDMK_DIR)/Arduino.mk
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..428ce70
--- /dev/null
+++ b/README.md
@@ -0,0 +1,9 @@
+# Endpoints
+
+## Soil Moisture
+
+### Soil Moisture Level
+
+Format: two bytes of data. Byte #1: sensor id, byte #2: sensor level (unsigned int, 0-255).
+
+Sends notices, same data format.
diff --git a/app.cpp b/app.cpp
new file mode 100644
index 0000000..909a097
--- /dev/null
+++ b/app.cpp
@@ -0,0 +1,91 @@
+#include "app.h"
+
+#include <Arduino.h>
+#include <HardwareSerial.h>
+
+// http://bleaklow.com/2010/09/05/progmem_and_gcc_bug_34734.html
+#undef PROGMEM
+#define PROGMEM __attribute__((section(".progmem.data")))
+
+#define SENSOR_COUNT 1
+
+// See http://redbearlab.com/blendmicro/
+struct sm_sensor {
+ uint8_t pin;
+ uint8_t value;
+} gauges[SENSOR_COUNT] = {
+ { 9, 0},
+};
+
+void on_loop() {
+ static unsigned long last = 0, now;
+
+ static uint8_t x = 10;
+
+ now = millis();
+ if (now - last > 3000) {
+ last = now;
+ sm_res data;
+ data.len = sizeof(struct sm_get_sensor_level_res);
+ data.code = SM_CMD_GET_SENSOR_LEVEL;
+ data.get_sensor_level.level = x;
+ x++;
+ if(x == 0) {
+ x = 10;
+ }
+
+ if(x % 2 == 0) {
+ notify_soil_moisture(&data);
+ } else {
+ notify_battery_level(x);
+ }
+ }
+}
+
+void on_soil_moisture(uint8_t *data, uint8_t len) {
+ Serial.print(F("on_soil_moisture, data[0]="));
+ Serial.print(data[0], HEX);
+ Serial.print(F(", data[1]="));
+ Serial.println(data[1], HEX);
+
+ struct sm_req *cmd = (struct sm_req *) data;
+ struct sm_res res;
+
+ res.code = cmd->code;
+
+ Serial.print("code=");
+ Serial.println(cmd->code, DEC);
+
+ switch(cmd->code) {
+ case SM_CMD_GET_SENSOR_COUNT:
+ res.len = sizeof(struct sm_get_sensor_count_res);
+ res.get_sensor_count.count = SENSOR_COUNT;
+ break;
+ case SM_CMD_SET_SENSOR_THRESHOLD:
+// res.len = sizeof(struct fsp_set_gauge_res);
+// if (cmd->set_gauge.gauge < GAUGE_COUNT) {
+// analogWrite(gauges[cmd->set_gauge.gauge].pin, cmd->set_gauge.value);
+// gauges[cmd->set_gauge.gauge].value = cmd->set_gauge.value;
+// }
+
+ break;
+ case SM_CMD_GET_SENSOR_LEVEL:
+// res.len = sizeof(struct fsp_get_gauge_res);
+// if (cmd->get_gauge.gauge < GAUGE_COUNT) {
+// res.get_gauge.gauge = cmd->get_gauge.gauge;
+// res.get_gauge.value = gauges[cmd->get_gauge.gauge].value;
+// } else {
+// res.code = FSP_CMD_FAIL;
+// res.len = 0;
+// }
+ break;
+ default:
+ res.code = SM_CMD_FAIL;
+ res.len = 0;
+ break;
+ }
+
+// tx_soil_moisture(&res);
+}
+
+
diff --git a/app.h b/app.h
new file mode 100644
index 0000000..af3c39a
--- /dev/null
+++ b/app.h
@@ -0,0 +1,66 @@
+#ifndef APP_H
+#define APP_H
+
+#include <stdint.h>
+
+enum sm_cmd_code {
+ SM_CMD_GET_SENSOR_COUNT = 1,
+ SM_CMD_SET_SENSOR_THRESHOLD = 2,
+ SM_CMD_GET_SENSOR_LEVEL = 3,
+ SM_CMD_FAIL = 255,
+};
+
+struct sm_get_sensor_count_req {
+};
+
+struct sm_get_sensor_count_res {
+ uint8_t count;
+};
+
+struct sm_set_sensor_threshold_req {
+ uint8_t sensor;
+ uint8_t threshold;
+};
+
+struct sm_set_sensor_threshold_res {
+};
+
+struct sm_get_sensor_level_req {
+ uint8_t sensor;
+};
+
+struct sm_get_sensor_level_res {
+ uint8_t sensor;
+ uint8_t level;
+};
+
+struct sm_req {
+ uint8_t code;
+ union {
+ struct sm_get_sensor_count_req get_sensor_count;
+ struct sm_get_sensor_level_req get_sensor_level;
+ struct sm_set_sensor_threshold_req set_sensor_threshold;
+ };
+};
+
+struct sm_res {
+ uint8_t len;
+ uint8_t code;
+ union {
+ struct sm_get_sensor_count_res get_sensor_count;
+ struct sm_get_sensor_level_res get_sensor_level;
+ struct sm_set_sensor_threshold_res set_sensor_threshold;
+ };
+};
+
+void on_loop();
+
+bool tx_soil_moisture(struct sm_res *res);
+void notify_soil_moisture(struct sm_res *res);
+void notify_battery_level(uint8_t value);
+
+void on_soil_moisture(uint8_t *data, uint8_t len);
+
+#endif
+
+
diff --git a/services.h b/services.h
new file mode 100644
index 0000000..0344b5f
--- /dev/null
+++ b/services.h
@@ -0,0 +1,213 @@
+/**
+* This file is autogenerated by nRFgo Studio 1.17.1.3252
+*/
+
+#ifndef SETUP_MESSAGES_H__
+#define SETUP_MESSAGES_H__
+
+#include "hal_platform.h"
+#include "aci.h"
+
+
+#define SETUP_ID 0
+#define SETUP_FORMAT 3 /** nRF8001 D */
+#define ACI_DYNAMIC_DATA_SIZE 165
+
+/* Service: Gap - Characteristic: Device name - Pipe: SET */
+#define PIPE_GAP_DEVICE_NAME_SET 1
+#define PIPE_GAP_DEVICE_NAME_SET_MAX_SIZE 10
+
+/* Service: Battery - Characteristic: Battery Level - Pipe: BROADCAST */
+#define PIPE_BATTERY_BATTERY_LEVEL_BROADCAST 2
+#define PIPE_BATTERY_BATTERY_LEVEL_BROADCAST_MAX_SIZE 1
+
+/* Service: Battery - Characteristic: Battery Level - Pipe: TX */
+#define PIPE_BATTERY_BATTERY_LEVEL_TX 3
+#define PIPE_BATTERY_BATTERY_LEVEL_TX_MAX_SIZE 1
+
+/* Service: Battery - Characteristic: Battery Level - Pipe: SET */
+#define PIPE_BATTERY_BATTERY_LEVEL_SET 4
+#define PIPE_BATTERY_BATTERY_LEVEL_SET_MAX_SIZE 1
+
+/* Service: Battery - Characteristic: Battery Power State - Pipe: SET */
+#define PIPE_BATTERY_BATTERY_POWER_STATE_SET 5
+#define PIPE_BATTERY_BATTERY_POWER_STATE_SET_MAX_SIZE 1
+
+/* Service: Battery - Characteristic: Battery Level State - Pipe: SET */
+#define PIPE_BATTERY_BATTERY_LEVEL_STATE_SET 6
+#define PIPE_BATTERY_BATTERY_LEVEL_STATE_SET_MAX_SIZE 2
+
+/* Service: Soil Moisture - Characteristic: Soil Moisture Level - Pipe: BROADCAST */
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_BROADCAST 7
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_BROADCAST_MAX_SIZE 5
+
+/* Service: Soil Moisture - Characteristic: Soil Moisture Level - Pipe: TX */
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_TX 8
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_TX_MAX_SIZE 5
+
+/* Service: Soil Moisture - Characteristic: Soil Moisture Level - Pipe: SET */
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_SET 9
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_SET_MAX_SIZE 5
+
+/* Service: Soil Moisture - Characteristic: Soil Moisture Level - Pipe: BROADCAST */
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_BROADCAST_1 10
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_BROADCAST_1_MAX_SIZE 5
+
+/* Service: Soil Moisture - Characteristic: Soil Moisture Level - Pipe: TX */
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_TX_1 11
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_TX_1_MAX_SIZE 5
+
+/* Service: Soil Moisture - Characteristic: Soil Moisture Level - Pipe: SET */
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_SET_1 12
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_SET_1_MAX_SIZE 5
+
+
+#define NUMBER_OF_PIPES 12
+
+#define SERVICES_PIPE_TYPE_MAPPING_CONTENT {\
+ {ACI_STORE_LOCAL, ACI_SET}, \
+ {ACI_STORE_LOCAL, ACI_TX_BROADCAST}, \
+ {ACI_STORE_LOCAL, ACI_TX}, \
+ {ACI_STORE_LOCAL, ACI_SET}, \
+ {ACI_STORE_LOCAL, ACI_SET}, \
+ {ACI_STORE_LOCAL, ACI_SET}, \
+ {ACI_STORE_LOCAL, ACI_TX_BROADCAST}, \
+ {ACI_STORE_LOCAL, ACI_TX}, \
+ {ACI_STORE_LOCAL, ACI_SET}, \
+ {ACI_STORE_LOCAL, ACI_TX_BROADCAST}, \
+ {ACI_STORE_LOCAL, ACI_TX}, \
+ {ACI_STORE_LOCAL, ACI_SET}, \
+}
+
+#define GAP_PPCP_MAX_CONN_INT 0xffff /**< Maximum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific value requested */
+#define GAP_PPCP_MIN_CONN_INT 0xffff /**< Minimum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific value requested */
+#define GAP_PPCP_SLAVE_LATENCY 0
+#define GAP_PPCP_CONN_TIMEOUT 0xffff /** Connection Supervision timeout multiplier as a multiple of 10msec, 0xFFFF means no specific value requested */
+
+#define NB_SETUP_MESSAGES 21
+#define SETUP_MESSAGES_CONTENT {\
+ {0x00,\
+ {\
+ 0x07,0x06,0x00,0x00,0x03,0x02,0x42,0x07,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x0c,0x01,0x01,0x00,0x00,0x06,0x00,0x01,\
+ 0x81,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x10,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x90,0x00,0xff,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x10,0x38,0xff,0xff,0x02,0x58,0x0a,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x05,0x06,0x10,0x54,0x00,0x00,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0x00,0x04,0x04,0x02,0x02,0x00,0x01,0x28,0x00,0x01,0x00,0x18,0x04,0x04,0x05,0x05,0x00,\
+ 0x02,0x28,0x03,0x01,0x0e,0x03,0x00,0x00,0x2a,0x04,0x14,0x0a,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0x1c,0x03,0x00,0x03,0x2a,0x00,0x01,0x77,0x61,0x74,0x64,0x69,0x63,0x73,0x65,0x6d,0x69,\
+ 0x04,0x04,0x05,0x05,0x00,0x04,0x28,0x03,0x01,0x02,0x05,0x00,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0x38,0x01,0x2a,0x06,0x04,0x03,0x02,0x00,0x05,0x2a,0x01,0x01,0x00,0x00,0x04,0x04,0x05,\
+ 0x05,0x00,0x06,0x28,0x03,0x01,0x02,0x07,0x00,0x04,0x2a,0x06,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0x54,0x04,0x09,0x08,0x00,0x07,0x2a,0x04,0x01,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,\
+ 0x04,0x04,0x02,0x02,0x00,0x08,0x28,0x00,0x01,0x01,0x18,0x04,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0x70,0x04,0x02,0x02,0x00,0x09,0x28,0x00,0x01,0x0f,0x18,0x04,0x04,0x05,0x05,0x00,0x0a,\
+ 0x28,0x03,0x01,0x12,0x0b,0x00,0x19,0x2a,0x16,0x04,0x02,0x01,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0x8c,0x00,0x0b,0x2a,0x19,0x01,0x10,0x46,0x14,0x03,0x02,0x00,0x0c,0x29,0x02,0x01,0x00,\
+ 0x00,0x04,0x04,0x05,0x05,0x00,0x0d,0x28,0x03,0x01,0x02,0x0e,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0xa8,0x00,0x1a,0x2a,0x06,0x04,0x02,0x01,0x00,0x0e,0x2a,0x1a,0x01,0x00,0x04,0x04,0x05,\
+ 0x05,0x00,0x0f,0x28,0x03,0x01,0x02,0x10,0x00,0x1b,0x2a,0x06,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0xc4,0x04,0x03,0x02,0x00,0x10,0x2a,0x1b,0x01,0x00,0x00,0x04,0x04,0x02,0x02,0x00,0x11,\
+ 0x28,0x00,0x01,0x10,0x00,0x04,0x04,0x05,0x05,0x00,0x12,0x28,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0xe0,0x03,0x01,0x12,0x13,0x00,0x11,0x00,0x16,0x04,0x06,0x05,0x00,0x13,0x00,0x11,0x01,\
+ 0xaa,0x55,0xaa,0x55,0xaa,0x46,0x14,0x03,0x02,0x00,0x14,0x29,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0xfc,0x02,0x01,0x00,0x00,0x04,0x04,0x02,0x02,0x00,0x15,0x28,0x00,0x01,0x10,0x00,0x04,\
+ 0x04,0x05,0x05,0x00,0x16,0x28,0x03,0x01,0x12,0x17,0x00,0x11,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1e,0x06,0x21,0x18,0x00,0x14,0x04,0x05,0x00,0x00,0x17,0x00,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x46,\
+ 0x14,0x03,0x02,0x00,0x18,0x29,0x02,0x01,0x00,0x00,0x00,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x40,0x00,0x2a,0x00,0x01,0x00,0x80,0x04,0x00,0x03,0x00,0x00,0x2a,0x19,0x01,0x00,0x83,0x04,\
+ 0x00,0x0b,0x00,0x0c,0x2a,0x1a,0x01,0x00,0x80,0x04,0x00,0x0e,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x40,0x1c,0x00,0x00,0x2a,0x1b,0x01,0x00,0x80,0x04,0x00,0x10,0x00,0x00,0x00,0x11,0x01,0x00,\
+ 0x83,0x04,0x00,0x13,0x00,0x14,0x00,0x11,0x01,0x00,0x83,0x04,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x07,0x06,0x40,0x38,0x00,0x17,0x00,0x18,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x15,0x06,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x06,0x06,0xf0,0x00,0x03,0xda,0x64,\
+ },\
+ },\
+}
+
+#endif
diff --git a/services_lock.h b/services_lock.h
new file mode 100644
index 0000000..fe0057a
--- /dev/null
+++ b/services_lock.h
@@ -0,0 +1,217 @@
+/**
+* This file is autogenerated by nRFgo Studio 1.17.1.3252
+*/
+
+#ifndef SETUP_MESSAGES_H__
+#define SETUP_MESSAGES_H__
+
+#include "hal_platform.h"
+#include "aci.h"
+
+// You have now chosen to upload the configuration to OTP on the device.
+// This will result in a device that you can not modify afterwards. If this is your intention,
+// remove this comment and the #error below
+#error Generating configuration for OTP. Please verify usage by removing this error message from include file.
+
+#define SETUP_ID 0
+#define SETUP_FORMAT 3 /** nRF8001 D */
+#define ACI_DYNAMIC_DATA_SIZE 165
+
+/* Service: Gap - Characteristic: Device name - Pipe: SET */
+#define PIPE_GAP_DEVICE_NAME_SET 1
+#define PIPE_GAP_DEVICE_NAME_SET_MAX_SIZE 10
+
+/* Service: Battery - Characteristic: Battery Level - Pipe: BROADCAST */
+#define PIPE_BATTERY_BATTERY_LEVEL_BROADCAST 2
+#define PIPE_BATTERY_BATTERY_LEVEL_BROADCAST_MAX_SIZE 1
+
+/* Service: Battery - Characteristic: Battery Level - Pipe: TX */
+#define PIPE_BATTERY_BATTERY_LEVEL_TX 3
+#define PIPE_BATTERY_BATTERY_LEVEL_TX_MAX_SIZE 1
+
+/* Service: Battery - Characteristic: Battery Level - Pipe: SET */
+#define PIPE_BATTERY_BATTERY_LEVEL_SET 4
+#define PIPE_BATTERY_BATTERY_LEVEL_SET_MAX_SIZE 1
+
+/* Service: Battery - Characteristic: Battery Power State - Pipe: SET */
+#define PIPE_BATTERY_BATTERY_POWER_STATE_SET 5
+#define PIPE_BATTERY_BATTERY_POWER_STATE_SET_MAX_SIZE 1
+
+/* Service: Battery - Characteristic: Battery Level State - Pipe: SET */
+#define PIPE_BATTERY_BATTERY_LEVEL_STATE_SET 6
+#define PIPE_BATTERY_BATTERY_LEVEL_STATE_SET_MAX_SIZE 2
+
+/* Service: Soil Moisture - Characteristic: Soil Moisture Level - Pipe: BROADCAST */
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_BROADCAST 7
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_BROADCAST_MAX_SIZE 5
+
+/* Service: Soil Moisture - Characteristic: Soil Moisture Level - Pipe: TX */
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_TX 8
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_TX_MAX_SIZE 5
+
+/* Service: Soil Moisture - Characteristic: Soil Moisture Level - Pipe: SET */
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_SET 9
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_SET_MAX_SIZE 5
+
+/* Service: Soil Moisture - Characteristic: Soil Moisture Level - Pipe: BROADCAST */
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_BROADCAST_1 10
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_BROADCAST_1_MAX_SIZE 5
+
+/* Service: Soil Moisture - Characteristic: Soil Moisture Level - Pipe: TX */
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_TX_1 11
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_TX_1_MAX_SIZE 5
+
+/* Service: Soil Moisture - Characteristic: Soil Moisture Level - Pipe: SET */
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_SET_1 12
+#define PIPE_SOIL_MOISTURE_SOIL_MOISTURE_LEVEL_SET_1_MAX_SIZE 5
+
+
+#define NUMBER_OF_PIPES 12
+
+#define SERVICES_PIPE_TYPE_MAPPING_CONTENT {\
+ {ACI_STORE_LOCAL, ACI_SET}, \
+ {ACI_STORE_LOCAL, ACI_TX_BROADCAST}, \
+ {ACI_STORE_LOCAL, ACI_TX}, \
+ {ACI_STORE_LOCAL, ACI_SET}, \
+ {ACI_STORE_LOCAL, ACI_SET}, \
+ {ACI_STORE_LOCAL, ACI_SET}, \
+ {ACI_STORE_LOCAL, ACI_TX_BROADCAST}, \
+ {ACI_STORE_LOCAL, ACI_TX}, \
+ {ACI_STORE_LOCAL, ACI_SET}, \
+ {ACI_STORE_LOCAL, ACI_TX_BROADCAST}, \
+ {ACI_STORE_LOCAL, ACI_TX}, \
+ {ACI_STORE_LOCAL, ACI_SET}, \
+}
+
+#define GAP_PPCP_MAX_CONN_INT 0xffff /**< Maximum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific value requested */
+#define GAP_PPCP_MIN_CONN_INT 0xffff /**< Minimum connection interval as a multiple of 1.25 msec , 0xFFFF means no specific value requested */
+#define GAP_PPCP_SLAVE_LATENCY 0
+#define GAP_PPCP_CONN_TIMEOUT 0xffff /** Connection Supervision timeout multiplier as a multiple of 10msec, 0xFFFF means no specific value requested */
+
+#define NB_SETUP_MESSAGES 21
+#define SETUP_MESSAGES_CONTENT {\
+ {0x00,\
+ {\
+ 0x07,0x06,0x00,0x00,0x03,0x02,0x42,0x07,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x0c,0x01,0x01,0x00,0x00,0x06,0x00,0x01,\
+ 0x81,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x10,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x90,0x00,0xff,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x10,0x38,0xff,0xff,0x02,0x58,0x0a,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x05,0x06,0x10,0x54,0x00,0x00,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0x00,0x04,0x04,0x02,0x02,0x00,0x01,0x28,0x00,0x01,0x00,0x18,0x04,0x04,0x05,0x05,0x00,\
+ 0x02,0x28,0x03,0x01,0x0e,0x03,0x00,0x00,0x2a,0x04,0x14,0x0a,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0x1c,0x03,0x00,0x03,0x2a,0x00,0x01,0x77,0x61,0x74,0x64,0x69,0x63,0x73,0x65,0x6d,0x69,\
+ 0x04,0x04,0x05,0x05,0x00,0x04,0x28,0x03,0x01,0x02,0x05,0x00,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0x38,0x01,0x2a,0x06,0x04,0x03,0x02,0x00,0x05,0x2a,0x01,0x01,0x00,0x00,0x04,0x04,0x05,\
+ 0x05,0x00,0x06,0x28,0x03,0x01,0x02,0x07,0x00,0x04,0x2a,0x06,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0x54,0x04,0x09,0x08,0x00,0x07,0x2a,0x04,0x01,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,\
+ 0x04,0x04,0x02,0x02,0x00,0x08,0x28,0x00,0x01,0x01,0x18,0x04,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0x70,0x04,0x02,0x02,0x00,0x09,0x28,0x00,0x01,0x0f,0x18,0x04,0x04,0x05,0x05,0x00,0x0a,\
+ 0x28,0x03,0x01,0x12,0x0b,0x00,0x19,0x2a,0x16,0x04,0x02,0x01,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0x8c,0x00,0x0b,0x2a,0x19,0x01,0x10,0x46,0x14,0x03,0x02,0x00,0x0c,0x29,0x02,0x01,0x00,\
+ 0x00,0x04,0x04,0x05,0x05,0x00,0x0d,0x28,0x03,0x01,0x02,0x0e,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0xa8,0x00,0x1a,0x2a,0x06,0x04,0x02,0x01,0x00,0x0e,0x2a,0x1a,0x01,0x00,0x04,0x04,0x05,\
+ 0x05,0x00,0x0f,0x28,0x03,0x01,0x02,0x10,0x00,0x1b,0x2a,0x06,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0xc4,0x04,0x03,0x02,0x00,0x10,0x2a,0x1b,0x01,0x00,0x00,0x04,0x04,0x02,0x02,0x00,0x11,\
+ 0x28,0x00,0x01,0x10,0x00,0x04,0x04,0x05,0x05,0x00,0x12,0x28,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0xe0,0x03,0x01,0x12,0x13,0x00,0x11,0x00,0x16,0x04,0x06,0x05,0x00,0x13,0x00,0x11,0x01,\
+ 0xaa,0x55,0xaa,0x55,0xaa,0x46,0x14,0x03,0x02,0x00,0x14,0x29,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x20,0xfc,0x02,0x01,0x00,0x00,0x04,0x04,0x02,0x02,0x00,0x15,0x28,0x00,0x01,0x10,0x00,0x04,\
+ 0x04,0x05,0x05,0x00,0x16,0x28,0x03,0x01,0x12,0x17,0x00,0x11,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1e,0x06,0x21,0x18,0x00,0x14,0x04,0x05,0x00,0x00,0x17,0x00,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x46,\
+ 0x14,0x03,0x02,0x00,0x18,0x29,0x02,0x01,0x00,0x00,0x00,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x40,0x00,0x2a,0x00,0x01,0x00,0x80,0x04,0x00,0x03,0x00,0x00,0x2a,0x19,0x01,0x00,0x83,0x04,\
+ 0x00,0x0b,0x00,0x0c,0x2a,0x1a,0x01,0x00,0x80,0x04,0x00,0x0e,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x1f,0x06,0x40,0x1c,0x00,0x00,0x2a,0x1b,0x01,0x00,0x80,0x04,0x00,0x10,0x00,0x00,0x00,0x11,0x01,0x00,\
+ 0x83,0x04,0x00,0x13,0x00,0x14,0x00,0x11,0x01,0x00,0x83,0x04,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x07,0x06,0x40,0x38,0x00,0x17,0x00,0x18,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x15,0x06,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,\
+ },\
+ },\
+ {0x00,\
+ {\
+ 0x06,0x06,0xf0,0x00,0x83,0x4b,0xec,\
+ },\
+ },\
+}
+
+#endif
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 <SPI.h>
+#include <lib_aci.h>
+#include <aci_setup.h>
+
+#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<<PLOCK))); // wait for lock pll
+#elif defined(BLEND_MICRO_16MHZ)
+ // The CPU clock in bootloader is 8MHz, change to 16MHz for sketches to run (i.e. overclock running at 3.3v).
+ CLKPR = 0x80;
+ CLKPR = 0;
+#endif
+
+ Serial.begin((unsigned int)115200);
+
+ // Wait until the serial port is available (useful only for the Leonardo)
+ // As the Leonardo board is not reseted every time you open the Serial Monitor
+#if defined (__AVR_ATmega32U4__)
+ while(!Serial) {
+ }
+
+ delay(1000);
+#elif defined(__PIC32MX__)
+ delay(1000);
+#endif
+
+ setup_rf();
+}
+
+void setup_rf() {
+ Serial.println(F("setup_rf()"));
+ // Point ACI data structures to the the setup data that the nRFgo studio generated for the nRF8001
+ if (NULL != services_pipe_type_mapping) {
+ aci_state.aci_setup_info.services_pipe_type_mapping = &services_pipe_type_mapping[0];
+ }
+ else {
+ aci_state.aci_setup_info.services_pipe_type_mapping = NULL;
+ }
+ aci_state.aci_setup_info.number_of_pipes = NUMBER_OF_PIPES;
+ aci_state.aci_setup_info.setup_msgs = (hal_aci_data_t*)setup_msgs;
+ aci_state.aci_setup_info.num_setup_msgs = NB_SETUP_MESSAGES;
+
+ // Tell the ACI library, the MCU to nRF8001 pin connections.
+ // The Active pin is optional and can be marked UNUSED
+// aci_state.aci_pins.board_name = REDBEARLAB_SHIELD_V2;
+ aci_state.aci_pins.board_name = REDBEARLAB_SHIELD_V1_1;
+ aci_state.aci_pins.reqn_pin = 6;
+ aci_state.aci_pins.rdyn_pin = 7;
+ aci_state.aci_pins.mosi_pin = MOSI;
+ aci_state.aci_pins.miso_pin = MISO;
+ aci_state.aci_pins.sck_pin = SCK;
+
+ // SPI_CLOCK_DIV8 = 2MHz SPI speed
+ // SPI_CLOCK_DIV16 = 1MHz SPI speed
+ aci_state.aci_pins.spi_clock_divider = SPI_CLOCK_DIV8;
+ aci_state.aci_pins.reset_pin = UNUSED; //4 for Nordic board, UNUSED for REDBEARLAB_SHIELD_V1_1
+ aci_state.aci_pins.active_pin = UNUSED;
+ aci_state.aci_pins.optional_chip_sel_pin = UNUSED;
+
+ aci_state.aci_pins.interface_is_interrupt = false; //Interrupts still not available in Chipkit
+ aci_state.aci_pins.interrupt_number = 4;
+
+ // We reset the nRF8001 here by toggling the RESET line connected to the nRF8001
+ // If the RESET line is not available we call the ACI Radio Reset to soft reset the nRF8001
+ // then we initialize the data structures required to setup the nRF8001
+ // The second parameter is for turning debug printing on for the ACI Commands and Events so they be printed on the Serial
+ lib_aci_init(&aci_state, false);
+ Serial.println(F("lib_aci_init done"));
+}
+
+static bool rf_started = false;
+static bool setup_required = false;
+
+static void aci_loop() {
+ uint8_t pipe_number;
+ int ret;
+
+ // 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) {
+ 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);
+}
+
diff --git a/trygvisio_soil_moisture.xml b/trygvisio_soil_moisture.xml
new file mode 100644
index 0000000..3041f3a
--- /dev/null
+++ b/trygvisio_soil_moisture.xml
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE AttributeServer>
+<Profile Version="1.3">
+ <SetupId>0</SetupId>
+ <Device>nRF8001_Dx</Device>
+ <Service Type="local" PrimaryService="true">
+ <Name>Battery</Name>
+ <Uuid>180f</Uuid>
+ <Characteristic>
+ <Name>Battery Level</Name>
+ <Uuid>2a19</Uuid>
+ <DefaultValue>10</DefaultValue>
+ <UsePresentationFormat>0</UsePresentationFormat>
+ <MaxDataLength>1</MaxDataLength>
+ <AttributeLenType>1</AttributeLenType>
+ <ForceOpen>false</ForceOpen>
+ <ForceEncryption>false</ForceEncryption>
+ <Properties>
+ <WriteWithoutResponse>false</WriteWithoutResponse>
+ <Write>false</Write>
+ <Notify>false</Notify>
+ <Indicate>false</Indicate>
+ <Broadcast>false</Broadcast>
+ </Properties>
+ <SetPipe>true</SetPipe>
+ <AckIsAuto>false</AckIsAuto>
+ <PresentationFormatDescriptor Value="0000" Exponent="0" Format="1" NameSpace="01" Unit="0000"/>
+ <PeriodForReadingThisCharacteristic>0</PeriodForReadingThisCharacteristic>
+ <PeriodForProperties/>
+ </Characteristic>
+ <Characteristic>
+ <Name>Battery Power State</Name>
+ <Uuid>2a1a</Uuid>
+ <DefaultValue>0</DefaultValue>
+ <UsePresentationFormat>0</UsePresentationFormat>
+ <MaxDataLength>1</MaxDataLength>
+ <AttributeLenType>1</AttributeLenType>
+ <ForceOpen>false</ForceOpen>
+ <ForceEncryption>false</ForceEncryption>
+ <Properties>
+ <WriteWithoutResponse>false</WriteWithoutResponse>
+ <Write>false</Write>
+ <Notify>false</Notify>
+ <Indicate>false</Indicate>
+ <Broadcast>false</Broadcast>
+ </Properties>
+ <SetPipe>true</SetPipe>
+ <AckIsAuto>false</AckIsAuto>
+ <PresentationFormatDescriptor Value="0000" Exponent="0" Format="1" NameSpace="01" Unit="0000"/>
+ <PeriodForReadingThisCharacteristic>0</PeriodForReadingThisCharacteristic>
+ <PeriodForProperties/>
+ </Characteristic>
+ <Characteristic>
+ <Name>Battery Level State</Name>
+ <Uuid>2a1b</Uuid>
+ <DefaultValue>0</DefaultValue>
+ <UsePresentationFormat>0</UsePresentationFormat>
+ <MaxDataLength>2</MaxDataLength>
+ <AttributeLenType>1</AttributeLenType>
+ <ForceOpen>false</ForceOpen>
+ <ForceEncryption>false</ForceEncryption>
+ <Properties>
+ <WriteWithoutResponse>false</WriteWithoutResponse>
+ <Write>false</Write>
+ <Notify>false</Notify>
+ <Indicate>false</Indicate>
+ <Broadcast>false</Broadcast>
+ </Properties>
+ <SetPipe>true</SetPipe>
+ <AckIsAuto>false</AckIsAuto>
+ <PresentationFormatDescriptor Value="0000" Exponent="0" Format="1" NameSpace="01" Unit="0000"/>
+ <PeriodForReadingThisCharacteristic>0</PeriodForReadingThisCharacteristic>
+ <PeriodForProperties/>
+ </Characteristic>
+ </Service>
+ <Service Type="local" PrimaryService="true">
+ <Name>Soil Moisture</Name>
+ <Uuid>0010</Uuid>
+ <Characteristic>
+ <Name>Soil Moisture Level</Name>
+ <Uuid>0011</Uuid>
+ <DefaultValue>aa55aa55aa</DefaultValue>
+ <UsePresentationFormat>0</UsePresentationFormat>
+ <MaxDataLength>5</MaxDataLength>
+ <AttributeLenType>1</AttributeLenType>
+ <ForceOpen>false</ForceOpen>
+ <ForceEncryption>false</ForceEncryption>
+ <Properties>
+ <WriteWithoutResponse>false</WriteWithoutResponse>
+ <Write>false</Write>
+ <Notify>true</Notify>
+ <Indicate>false</Indicate>
+ <Broadcast>true</Broadcast>
+ </Properties>
+ <SetPipe>true</SetPipe>
+ <AckIsAuto>false</AckIsAuto>
+ <PresentationFormatDescriptor Value="0000" Exponent="0" Format="4" NameSpace="01" Unit="0000"/>
+ <PeriodForReadingThisCharacteristic>0</PeriodForReadingThisCharacteristic>
+ <PeriodForProperties/>
+ </Characteristic>
+ </Service>
+ <Gapsettings>
+ <Name>wat</Name>
+ <DeviceNameWriteLength>0</DeviceNameWriteLength>
+ <LocalPipeOnDeviceName>false</LocalPipeOnDeviceName>
+ <DeviceNameShortLength>1</DeviceNameShortLength>
+ <Apperance>0000</Apperance>
+ <SecurityLevel>0</SecurityLevel>
+ <AuthenticationReq>0</AuthenticationReq>
+ <IoCapabilities>0</IoCapabilities>
+ <BondTimeout>600</BondTimeout>
+ <SecurityRequestDelay>10</SecurityRequestDelay>
+ <MinimumKeySize>7</MinimumKeySize>
+ <MaximumKeySize>16</MaximumKeySize>
+ <AdvertisingDataBondedBitmap>0</AdvertisingDataBondedBitmap>
+ <AdvertisingDataGeneralBitmap>0</AdvertisingDataGeneralBitmap>
+ <AdvertisingDataBrodcastBitmap>0</AdvertisingDataBrodcastBitmap>
+ <AdvertisingDataBondedScanResponseBitmap>0</AdvertisingDataBondedScanResponseBitmap>
+ <AdvertisingDataGeneralScanResponseBitmap>1000</AdvertisingDataGeneralScanResponseBitmap>
+ <AdvertisingDataBrodcastScanResponseBitmap>0</AdvertisingDataBrodcastScanResponseBitmap>
+ <AdvertisingDataBondedBitmapCustom>0</AdvertisingDataBondedBitmapCustom>
+ <AdvertisingDataGeneralBitmapCustom>0</AdvertisingDataGeneralBitmapCustom>
+ <AdvertisingDataBrodcastBitmapCustom>0</AdvertisingDataBrodcastBitmapCustom>
+ <AdvertisingDataBondedScanResponseBitmapCustom>0</AdvertisingDataBondedScanResponseBitmapCustom>
+ <AdvertisingDataGeneralScanResponseBitmapCustom>0</AdvertisingDataGeneralScanResponseBitmapCustom>
+ <AdvertisingDataBrodcastScanResponseBitmapCustom>0</AdvertisingDataBrodcastScanResponseBitmapCustom>
+ <TxPowerLevelOffset>0</TxPowerLevelOffset>
+ <MinimumConnectionInterval>65535</MinimumConnectionInterval>
+ <MaximumConnectionInterval>65535</MaximumConnectionInterval>
+ <SlaveLatency>0</SlaveLatency>
+ <TimeoutMultipler>65535</TimeoutMultipler>
+ <AddServiceUpdateCharacteristic>false</AddServiceUpdateCharacteristic>
+ <AddServiceUpdateCharacteristicPipe>false</AddServiceUpdateCharacteristicPipe>
+ <TimingChangeDelay>5</TimingChangeDelay>
+ <ServiceToAdvertise>
+ <Uuid>0010</Uuid>
+ </ServiceToAdvertise>
+ <CustomAdTypes>
+ <AdType index="1">
+ <Type>19</Type>
+ <Value>0000</Value>
+ </AdType>
+ <AdType index="2">
+ <Type>18</Type>
+ <Value></Value>
+ </AdType>
+ </CustomAdTypes>
+ </Gapsettings>
+ <Hardwaresettings>
+ <Clocksource>1</Clocksource>
+ <ClockAccuracy>1</ClockAccuracy>
+ <InitialTxPower>3</InitialTxPower>
+ <HfClkSource>0</HfClkSource>
+ <DcDcConverter>0</DcDcConverter>
+ <ActiveSignalModeIndex>0</ActiveSignalModeIndex>
+ <ActiveSignalToTickDistance>0</ActiveSignalToTickDistance>
+ <DynamicWindowLimitingEnabled>false</DynamicWindowLimitingEnabled>
+ </Hardwaresettings>
+ <CurrentInput>
+ <BatteryCharge>220</BatteryCharge>
+ <Master32KhzClockAccuracy>10</Master32KhzClockAccuracy>
+ <ConnectionInterval>1000</ConnectionInterval>
+ <PercentOfTimeSleeping>0</PercentOfTimeSleeping>
+ <PercentOfTimeAdvertising>0</PercentOfTimeAdvertising>
+ <AdvertisingInterval>1280</AdvertisingInterval>
+ </CurrentInput>
+</Profile>