From f016c4a145c57a2df61984d7040be6d68e239cc5 Mon Sep 17 00:00:00 2001 From: br Date: Wed, 19 Jun 2019 17:34:23 +0100 Subject: [PATCH] DG-15: Updates to device-grove service for Edinburgh release Signed-off-by: br --- Attribution.txt | 52 +- CHANGES | 4 + README.md | 2 +- VERSION | 2 +- res/Grove_Device.yaml | 60 +- res/configuration.toml | 42 +- ...rfile.alpine-3.8 => Dockerfile.alpine-3.9} | 28 +- scripts/build_deps.sh | 22 +- src/c/bme680_mraa.c | 866 ++++++++++++++++++ src/c/bme680_mraa.h | 179 ++++ src/c/bme680_mraa_regs.h | 280 ++++++ src/c/device_grove.h | 19 +- src/c/grove_bme680.c | 113 +++ src/c/grove_bme680.h | 16 + src/c/main.c | 390 +++++--- 15 files changed, 1890 insertions(+), 185 deletions(-) create mode 100644 CHANGES rename scripts/{Dockerfile.alpine-3.8 => Dockerfile.alpine-3.9} (55%) create mode 100644 src/c/bme680_mraa.c create mode 100644 src/c/bme680_mraa.h create mode 100644 src/c/bme680_mraa_regs.h create mode 100644 src/c/grove_bme680.c create mode 100644 src/c/grove_bme680.h diff --git a/Attribution.txt b/Attribution.txt index 6b060d0..7e82882 100644 --- a/Attribution.txt +++ b/Attribution.txt @@ -1,4 +1,4 @@ -This software includes code copyrighted from mraa & upm as follows: +This software includes code copyrighted from mraa, upm and BME680 driver as follows: mraa ---- @@ -34,4 +34,52 @@ https://github.com/intel-iot-devkit/upm LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + +--------------------------------------------------------------------------- + + BME680_driver + ------------- +Copyright (C) 2017 - 2018 Bosch Sensortec GmbH +https://github.com/BoschSensortec/BME680_driver + +--------------------------------------------------------------------------- + + Copyright Information + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the copyright holder nor the names of the + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER + OR CONTRIBUTORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + + The information provided is believed to be accurate and reliable. + The copyright holder assumes no responsibility + for the consequences of use + of such information nor for any infringement of patents or + other rights of third parties which may result from its use. + No license is granted by implication or otherwise under any patent or + patent rights of the copyright holder. diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..5c7a351 --- /dev/null +++ b/CHANGES @@ -0,0 +1,4 @@ +Changes for Edinburgh release: + +- Device service updated to use C SDK (v1.0.0) +- Added support to read temperature, humidity and pressure from grove Bosch Sensortec BME680 diff --git a/README.md b/README.md index 8363750..aaa05c9 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ The repository can be found on git at [libmraa](https://github.com/intel-iot-dev 2. Build a docker image by using the following command ``` sh> cd device-grove-c -sh> docker build . -t < device-grove-c > -f ./scripts/Dockerfile.alpine-3.8 +sh> docker build . -t < device-grove-c > -f ./scripts/Dockerfile.alpine-3.9 ``` This command shall build the dependencies - libmraa and device-c-sdk library to build device-grove-c release image. diff --git a/VERSION b/VERSION index 3eefcb9..7dea76e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0 +1.0.1 diff --git a/res/Grove_Device.yaml b/res/Grove_Device.yaml index 539213c..1f1b67a 100644 --- a/res/Grove_Device.yaml +++ b/res/Grove_Device.yaml @@ -2,7 +2,7 @@ name: "Grove_Device" manufacturer: "Seeed" model: "" labels: -- "Grove LED, Grove Button, Grove Buzzer, Grove LCD, Grove LightSensor, Grove Relay, Grove RotarySensor, Grove SoundSensor" +- "Grove LED, Grove Button, Grove Buzzer, Grove LCD, Grove LightSensor, Grove Relay, Grove RotarySensor, Grove SoundSensor, Grove BME680Sensor" description: "Grove Device to Read/Write sensors connected to GrovePI." deviceResources: @@ -93,7 +93,7 @@ deviceResources: { Pin_Num: "A0", Interface: "AIO", Type: "IN" } properties: value: - { type: "Float32", readWrite: "RW", minimum: "0", maximum: "", defaultValue: "0" } + { type: "Float32", floatEncoding: "eNotation", readWrite: "RW", minimum: "0", maximum: "", defaultValue: "0" } units: { type: "String", readWrite: "R", defaultValue: "lumen" } - name: SoundIntensity @@ -102,7 +102,7 @@ deviceResources: { Pin_Num: "A1", Interface: "AIO", Type: "IN", normalize: "false"} properties: value: - { type: "Float32", readWrite: "RW", minimum: "0", maximum: "5", scale: "0.0049", defaultValue: "0" } + { type: "Float32", floatEncoding: "eNotation", readWrite: "RW", minimum: "0", maximum: "5", scale: "0.0049", defaultValue: "0" } units: { type: "String", readWrite: "R", defaultValue: "Vbiased" } - name: RotaryAngle @@ -111,7 +111,7 @@ deviceResources: { Pin_Num: "A2", Interface: "AIO", Type: "IN", normalize: "false" } properties: value: - { type: "Float32", readWrite: "RW", minimum: "0", maximum: "300", scale: "0.2933", defaultValue: "0" } + { type: "Float32", floatEncoding: "eNotation", readWrite: "RW", minimum: "0", maximum: "300", scale: "0.2933", defaultValue: "0" } units: { type: "String", readWrite: "R", defaultValue: "Degree" } - name: RotaryVoltage @@ -120,12 +120,37 @@ deviceResources: { Pin_Num: "A2", Interface: "AIO", Type: "IN", normalize: "false"} properties: value: - { type: "Float32", readWrite: "RW", minimum: "0", maximum: "5", scale: "0.0049", defaultValue: "0" } + { type: "Float32", floatEncoding: "eNotation", readWrite: "RW", minimum: "0", maximum: "5", scale: "0.0049", defaultValue: "0" } units: { type: "String", readWrite: "R", defaultValue: "V" } - - -resources: +- name: Temperature + description: "Read Temperature from BME680" + attributes: + { Pin_Num: "I2C-1", Interface: "I2C", Type: "BME680"} + properties: + value: + { type: "Float32", floatEncoding: "eNotation", readWrite: "RW", minimum: "-40", maximum: "85", defaultValue: "0" } + units: + { type: "String", readWrite: "R", defaultValue: "°C" } +- name: Pressure + description: "Read Pressure from BME680" + attributes: + { Pin_Num: "I2C-1", Interface: "I2C", Type: "BME680", normalize: "false"} + properties: + value: + { type: "Float32", floatEncoding: "eNotation", readWrite: "RW", minimum: "300", maximum: "1100", scale: "0.01", defaultValue: "0" } + units: + { type: "String", readWrite: "R", defaultValue: "hPa" } +- name: Humidity + description: "Read Humidity from BME680" + attributes: + { Pin_Num: "I2C-1", Interface: "I2C", Type: "BME680"} + properties: + value: + { type: "Float32", floatEncoding: "eNotation", readWrite: "RW", minimum: "10", maximum: "90", defaultValue: "0" } + units: + { type: "String", readWrite: "R", defaultValue: "rH" } +deviceCommands: - name: Get_ButtonState get: - { operation: "get", object: "ButtonState", property: "value", parameter: "ButtonState" } @@ -139,6 +164,11 @@ resources: - name: Get_SoundIntensity get: - { operation: "get", object: "SoundIntensity", property: "value", parameter: "SoundIntensity" } +- name: Get_TempPressHumidity + get: + - { index: "1", operation: "get", object: "Temperature", property: "value", parameter: "Temperature" } + - { index: "2", operation: "get", object: "Pressure", property: "value", parameter: "Pressure" } + - { index: "3", operation: "get", object: "Humidity", property: "value", parameter: "Humidity" } - name: Set_GreenLed set: - { operation: "set", object: "Green-LED", property: "value", parameter: "Green-LED" } @@ -160,7 +190,7 @@ resources: set: - { operation: "set", object: "Grove-Relay", property: "value", parameter: "Grove-Relay" } -commands: +coreCommands: - name: Get_ButtonState get: path: "/api/v1/device/{deviceId}/Get_ButtonState" @@ -205,6 +235,17 @@ commands: description: "service unavailable" expectedValues: [] +- name: Get_TempPressHumidity + get: + path: "/api/v1/device/{deviceId}/Get_TempPressHumidity" + responses: + - code: "200" + description: "valid and accepted" + expectedValues: ["Temperature", "Pressure", "Humidity"] + - code: "503" + description: "service unavailable" + expectedValues: [] + - name: Set_GreenLed put: path: "/api/v1/device/{deviceId}/Set_GreenLed" @@ -294,3 +335,4 @@ commands: - code: "503" description: "service unavailable" expectedValues: [] + diff --git a/res/configuration.toml b/res/configuration.toml index f61b4aa..65b3d58 100644 --- a/res/configuration.toml +++ b/res/configuration.toml @@ -28,34 +28,30 @@ ProfilesDir = "" SendReadingsOnChanged = true +[Driver] + BME680_Temp_Offset = -1.0 + [Logging] RemoteURL = "" File = "-" -[[Schedules]] - Name = "Schedule_Grove" - Frequency = "PT2S" - -[[ScheduleEvents]] - Name = "RotarySensorMeasurements" - Schedule = "Schedule_Grove" - Path = "/api/v1/device/all/Get_RotarySensorMeasurements" - -[[ScheduleEvents]] - Name = "LightIntensity" - Schedule = "Schedule_Grove" - Path = "/api/v1/device/all/Get_LightIntensity" - -[[ScheduleEvents]] - Name = "SoundIntensity" - Schedule = "Schedule_Grove" - Path = "/api/v1/device/all/Get_SoundIntensity" - [[DeviceList]] Name = "GroveDevice" Profile = "Grove_Device" Description = "Grove Device to Read/Write sensors connected to GrovePI" - Labels = [ "Grove LED, Grove Button, Grove Buzzer, Grove LCD, Grove LightSensor, Grove Relay, Grove RotarySensor, Grove SoundSensor" ] - [DeviceList.Addressable] - Address = "/api/v1/device/GroveDevice" - + Labels = [ "Grove LED, Grove Button, Grove Buzzer, Grove LCD, Grove LightSensor, Grove Relay, Grove RotarySensor, Grove SoundSensor, Grove BME680Sensor" ] + [DeviceList.Protocols] + [DeviceList.Protocols.Other] + Address = "/api/v1/device/GroveDevice" + [[DeviceList.AutoEvents]] + Resource = "Get_RotarySensorMeasurements" + OnChange = false + Frequency = "2s" + [[DeviceList.AutoEvents]] + Resource = "Get_LightIntensity" + OnChange = false + Frequency = "2s" + [[DeviceList.AutoEvents]] + Resource = "Get_SoundIntensity" + OnChange = false + Frequency = "2s" diff --git a/scripts/Dockerfile.alpine-3.8 b/scripts/Dockerfile.alpine-3.9 similarity index 55% rename from scripts/Dockerfile.alpine-3.8 rename to scripts/Dockerfile.alpine-3.9 index 21edf3c..14b1641 100644 --- a/scripts/Dockerfile.alpine-3.8 +++ b/scripts/Dockerfile.alpine-3.9 @@ -1,33 +1,33 @@ -FROM alpine:3.8 as builder +FROM alpine:3.9 as builder MAINTAINER Bindu Rao -RUN apk add --update --no-cache build-base git gcc cmake make linux-headers yaml-dev libmicrohttpd-dev curl-dev +RUN apk add --update --no-cache build-base git gcc cmake make linux-headers yaml-dev libmicrohttpd-dev curl-dev util-linux-dev COPY scripts /device-grove/scripts COPY src /device-grove/src/ RUN mkdir -p /device-grove/build WORKDIR /device-grove -RUN /device-grove/scripts/build_deps.sh +RUN /device-grove/scripts/build_deps.sh 1 RUN /device-grove/scripts/build.sh -FROM alpine:3.8 +FROM alpine:3.9 -RUN apk add --update --no-cache linux-headers yaml libmicrohttpd curl-dev +RUN apk add --update --no-cache linux-headers yaml libmicrohttpd curl libuuid -COPY --from=builder /device-grove/build/release/device-grove /device-grove/build/release/ +COPY --from=builder /device-grove/build/release/device-grove /. COPY --from=builder /usr/lib/libcsdk.so /usr/lib COPY --from=builder /usr/include/edgex /usr/include/edgex -COPY --from=builder /usr/include/thpool.h /usr/include/thpool.h COPY --from=builder /usr/include/iot /usr/include/iot COPY --from=builder /usr/local/include/mraa /usr/local/include/ COPY --from=builder /usr/local/include/mraa.h /usr/local/include/ COPY --from=builder /usr/local/lib/libmraa.so* /usr/local/lib/ +COPY --from=builder /usr/local/lib64/libcbor.so* /usr/local/lib64/ -COPY VERSION /device-grove/ -COPY LICENSE /device-grove/ -COPY Attribution.txt /device-grove/ -COPY res /device-grove/res/ -COPY profiles /device-grove/profiles +COPY VERSION /. +COPY LICENSE /. +COPY Attribution.txt /. +COPY res /res +COPY profiles /profiles -ENTRYPOINT ["/device-grove/build/release/device-grove", "-c"] -CMD ["/device-grove/res/"] +ENTRYPOINT ["/device-grove"] +CMD ["--confdir=/res"] diff --git a/scripts/build_deps.sh b/scripts/build_deps.sh index 92f7b58..6f1a04e 100755 --- a/scripts/build_deps.sh +++ b/scripts/build_deps.sh @@ -1,6 +1,8 @@ #!/bin/sh set -e -x +BUILD_CSDK=$1 + # Dependencies if [ ! -d deps ] then @@ -15,21 +17,29 @@ then # patch for raspberryPI patch -p1 < /device-grove/scripts/rpi_patch - mkdir -p build && cd build # always install in lib folder cmake -DBUILDSWIG=OFF -DCMAKE_INSTALL_LIBDIR=lib ../. make && make install -# get c-sdk from edgexfoundry +# get c-sdk from edgexfoundry and build +if [ "$BUILD_CSDK" = "1" ] +then cd /device-grove/deps - wget https://github.com/edgexfoundry/device-sdk-c/archive/0.7.1.tar.gz - tar -xzf 0.7.1.tar.gz - cd device-sdk-c-0.7.1 + + git clone https://github.com/PJK/libcbor + sed -e 's/-flto//' -i libcbor/CMakeLists.txt + cmake -DCMAKE_BUILD_TYPE=Release -DCBOR_CUSTOM_ALLOC=ON libcbor + make + make install + + wget https://github.com/edgexfoundry/device-sdk-c/archive/edinburgh.tar.gz + tar -xzf edinburgh.tar.gz + cd device-sdk-c-edinburgh ./scripts/build.sh cp -rf include/* /usr/include/ cp build/release/c/libcsdk.so /usr/lib/ - +fi rm -rf /device-grove/deps fi diff --git a/src/c/bme680_mraa.c b/src/c/bme680_mraa.c new file mode 100644 index 0000000..d55edad --- /dev/null +++ b/src/c/bme680_mraa.c @@ -0,0 +1,866 @@ +/* + * Copyright (c) 2018 + * IoTech Ltd + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +/* Based on code from https://github.com/BoschSensortec/BME680_driver */ +/**\mainpage + * Copyright (C) 2017 - 2018 Bosch Sensortec GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of the + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + * + * The information provided is believed to be accurate and reliable. + * The copyright holder assumes no responsibility + * for the consequences of use + * of such information nor for any infringement of patents or + * other rights of third parties which may result from its use. + * No license is granted by implication or otherwise under any patent or + * patent rights of the copyright holder. + */ + +#include +#include + +#include "bme680_mraa.h" + +static bme680_results_t bme680_write_reg (const bme680_context dev, uint8_t reg, uint8_t val) +{ + mraa_result_t status = MRAA_SUCCESS; + bme680_results_t result = BME680_OK; + assert (dev != NULL); + + status = mraa_i2c_write_byte_data (dev->i2c, val, reg); + if (status) + { + result = BME680_E_COM_FAIL; + } + return result; +} + +static bme680_results_t bme680_read_regs (const bme680_context dev, uint8_t reg, uint16_t len, uint8_t *buffer) +{ + bme680_results_t result = BME680_OK; + assert (dev != NULL); + + if (mraa_i2c_read_bytes_data (dev->i2c, reg, buffer, len) != len) + { + result = BME680_E_COM_FAIL; + } + return result; +} + +/*! + * @brief This internal API is used to get the gas configuration of the sensor. + * @note heatr_temp and heatr_dur values are currently register data + * and not the actual values set + */ +static bme680_results_t get_gas_config (const bme680_context dev) +{ + bme680_results_t result = BME680_OK; + /* starting address of the register array for burst read*/ + uint8_t reg_addr1 = BME680_ADDR_SENS_CONF_START; + uint8_t reg_addr2 = BME680_ADDR_GAS_CONF_START; + uint8_t reg_data = 0; + + /* Check for null pointer in the device structure*/ + assert (dev != NULL); + + result = bme680_read_regs (dev, reg_addr1, 1, ®_data); + assert (result == BME680_OK); + dev->gas_sett.heatr_temp = reg_data; + + result = bme680_read_regs (dev, reg_addr2, 1, ®_data); + assert (result == BME680_OK); + /* Heating duration register value */ + dev->gas_sett.heatr_dur = reg_data; + + return result; +} + +/*! + * @brief This internal API is used to calculate the + * temperature value in float format + */ +static float calc_temperature (uint32_t temp_adc, const bme680_context dev) +{ + float var1 = 0; + float var2 = 0; + float calc_temp = 0; + + /* calculate var1 data */ + var1 = ((((float) temp_adc / 16384.0f) - ((float) dev->calib.par_t1 / 1024.0f)) * ((float) dev->calib.par_t2)); + + /* calculate var2 data */ + var2 = (((((float) temp_adc / 131072.0f) - ((float) dev->calib.par_t1 / 8192.0f)) * + (((float) temp_adc / 131072.0f) - ((float) dev->calib.par_t1 / 8192.0f))) * + ((float) dev->calib.par_t3 * 16.0f)); + + /* t_fine value*/ + dev->calib.t_fine = (var1 + var2) + (float) (dev->calib.offset_temp_in_t_fine); + + /* compensated temperature data*/ + calc_temp = ((dev->calib.t_fine) / 5120.0f); + return calc_temp; +} + +/*! + * @brief This internal API is used to calculate the + * pressure value in float format + */ +static float calc_pressure (uint32_t pres_adc, const bme680_context dev) +{ + float var1 = 0; + float var2 = 0; + float var3 = 0; + float calc_pres = 0; + + var1 = (((float) dev->calib.t_fine / 2.0f) - 64000.0f); + var2 = var1 * var1 * (((float) dev->calib.par_p6) / (131072.0f)); + var2 = var2 + (var1 * ((float) dev->calib.par_p5) * 2.0f); + var2 = (var2 / 4.0f) + (((float) dev->calib.par_p4) * 65536.0f); + var1 = (((((float) dev->calib.par_p3 * var1 * var1) / 16384.0f) + + ((float) dev->calib.par_p2 * var1)) / 524288.0f); + var1 = ((1.0f + (var1 / 32768.0f)) * ((float) dev->calib.par_p1)); + calc_pres = (1048576.0f - ((float) pres_adc)); + + /* Avoid exception caused by division by zero */ + if ((int) var1 != 0) + { + calc_pres = (((calc_pres - (var2 / 4096.0f)) * 6250.0f) / var1); + var1 = (((float) dev->calib.par_p9) * calc_pres * calc_pres) / 2147483648.0f; + var2 = calc_pres * (((float) dev->calib.par_p8) / 32768.0f); + var3 = ((calc_pres / 256.0f) * (calc_pres / 256.0f) * (calc_pres / 256.0f) + * (dev->calib.par_p10 / 131072.0f)); + calc_pres = (calc_pres + (var1 + var2 + var3 + ((float) dev->calib.par_p7 * 128.0f)) / 16.0f); + } + else + { + calc_pres = 0; + } + return calc_pres; +} + +/*! + * @brief This internal API is used to calculate the + * humidity value in float format + */ +static float calc_humidity (uint16_t hum_adc, const bme680_context dev) +{ + float calc_hum = 0; + float var1 = 0; + float var2 = 0; + float var3 = 0; + float var4 = 0; + float temp_comp; + + /* compensated temperature data*/ + temp_comp = ((dev->calib.t_fine) / 5120.0f); + + var1 = (float) ((float) hum_adc) - (((float) dev->calib.par_h1 * 16.0f) + (((float) dev->calib.par_h3 / 2.0f) + * temp_comp)); + + var2 = var1 * ((float) (((float) dev->calib.par_h2 / 262144.0f) * (1.0f + (((float) dev->calib.par_h4 / 16384.0f) + * temp_comp) + + (((float) dev->calib.par_h5 / 1048576.0f) * + temp_comp * temp_comp)))); + + var3 = (float) dev->calib.par_h6 / 16384.0f; + + var4 = (float) dev->calib.par_h7 / 2097152.0f; + + calc_hum = var2 + ((var3 + (var4 * temp_comp)) * var2 * var2); + + if (calc_hum > 100.0f) + { + calc_hum = 100.0f; + } + else if (calc_hum < 0.0f) + { + calc_hum = 0.0f; + } + + return calc_hum; +} + +/*! + * @brief This internal API is used to calculate the + * gas resistance value in float format + */ +static float calc_gas_resistance (uint16_t gas_res_adc, uint8_t gas_range, const bme680_context dev) +{ + float calc_gas_res; + float var1 = 0; + float var2 = 0; + float var3 = 0; + + const float lookup_k1_range[16] = { + 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, -0.8, + 0.0, 0.0, -0.2, -0.5, 0.0, -1.0, 0.0, 0.0}; + const float lookup_k2_range[16] = { + 0.0, 0.0, 0.0, 0.0, 0.1, 0.7, 0.0, -0.8, + -0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + + var1 = (1340.0f + (5.0f * dev->calib.range_sw_err)); + var2 = (var1) * (1.0f + lookup_k1_range[gas_range] / 100.0f); + var3 = 1.0f + (lookup_k2_range[gas_range] / 100.0f); + + calc_gas_res = 1.0f / (float) (var3 * (0.000000125f) * (float) (1 << gas_range) * (((((float) gas_res_adc) + - 512.0f) / var2) + 1.0f)); + return calc_gas_res; +} + +/*! + * @brief This internal API is used to calculate the + * heater resistance value in float format + */ +static float calc_heater_res (uint16_t temp, const bme680_context dev) +{ + float var1 = 0; + float var2 = 0; + float var3 = 0; + float var4 = 0; + float var5 = 0; + float res_heat = 0; + + if (temp > 400) + { /* Cap temperature */ + temp = 400; + } + + var1 = (((float) dev->calib.par_gh1 / (16.0f)) + 49.0f); + var2 = ((((float) dev->calib.par_gh2 / (32768.0f)) * (0.0005f)) + 0.00235f); + var3 = ((float) dev->calib.par_gh3 / (1024.0f)); + var4 = (var1 * (1.0f + (var2 * (float) temp))); + var5 = (var4 + (var3 * (float) dev->amb_temp)); + res_heat = (uint8_t) (3.4f * ((var5 * (4 / (4 + (float) dev->calib.res_heat_range)) * + (1 / (1 + ((float) dev->calib.res_heat_val * 0.002f)))) - 25)); + return res_heat; +} + +/*! + * @brief This internal API is used to calculate the Heat duration value. + */ +static uint8_t calc_heater_dur (uint16_t dur) +{ + uint8_t factor = 0; + uint8_t durval; + + if (dur >= 0xfc0) + { + durval = 0xff; /* Max duration*/ + } + else + { + while (dur > 0x3F) + { + dur = dur / 4; + factor += 1; + } + durval = (uint8_t) (dur + (factor * 64)); + } + return durval; +} + +static bme680_results_t bme680_read_field_data (struct bme680_field_data *data, const bme680_context dev) +{ + // Ref: bme680.c - read_field_data() from BoschSensorTec + bme680_results_t result = BME680_OK; + uint8_t buff[BME680_FIELD_LENGTH] = {0}; + uint8_t gas_range; + uint32_t adc_temp; + uint32_t adc_pres; + uint16_t adc_hum; + uint16_t adc_gas_res; + uint8_t tries = 5; + + assert (dev != NULL); + + do + { + result = bme680_read_regs (dev, ((uint8_t) (BME680_FIELD0_ADDR)), (uint16_t) BME680_FIELD_LENGTH, buff); + assert (result == BME680_OK); + + data->status = buff[0] & BME680_NEW_DATA_MSK; + data->gas_index = buff[0] & BME680_GAS_INDEX_MSK; + data->meas_index = buff[1]; + + /* read the raw data from the sensor */ + adc_pres = (uint32_t) (((uint32_t) buff[2] * 4096) | ((uint32_t) buff[3] * 16) | ((uint32_t) buff[4] / 16)); + adc_temp = (uint32_t) (((uint32_t) buff[5] * 4096) | ((uint32_t) buff[6] * 16) | ((uint32_t) buff[7] / 16)); + adc_hum = (uint16_t) (((uint32_t) buff[8] * 256) | (uint32_t) buff[9]); + adc_gas_res = (uint16_t) ((uint32_t) buff[13] * 4 | (((uint32_t) buff[14]) / 64)); + gas_range = buff[14] & BME680_GAS_RANGE_MSK; + + data->status |= buff[14] & BME680_GASM_VALID_MSK; + data->status |= buff[14] & BME680_HEAT_STAB_MSK; + + if (data->status & BME680_NEW_DATA_MSK) + { + data->temperature = calc_temperature (adc_temp, dev); + data->pressure = calc_pressure (adc_pres, dev); + data->humidity = calc_humidity (adc_hum, dev); + data->gas_resistance = calc_gas_resistance (adc_gas_res, gas_range, dev); + break; + } + usleep (10000); //10ms + } while (--tries); + + if (!tries) + { + result = BME680_W_NO_NEW_DATA; + } + + return result; +} + +static bme680_results_t set_gas_config (const bme680_context dev) +{ + bme680_results_t result = BME680_OK; + assert (dev != NULL); + uint8_t reg_addr = 0; + uint8_t reg_data = 0; + + if (dev->mode == BME680_FORCED_MODE) + { + reg_addr = BME680_RES_HEAT0_ADDR; + reg_data = calc_heater_res (dev->gas_sett.heatr_temp, dev); + + result = bme680_write_reg (dev, reg_addr, reg_data); + assert (result == BME680_OK); + + reg_addr = BME680_GAS_WAIT0_ADDR; + reg_data = calc_heater_dur (dev->gas_sett.heatr_dur); + dev->gas_sett.nb_conv = 0; + + result |= bme680_write_reg (dev, reg_addr, reg_data); + assert (result == BME680_OK); + } + return result; +} + + +/*! + * @brief This internal API is used to validate the boundary + * conditions. + */ +static bme680_results_t boundary_check (uint8_t *value, uint8_t min, uint8_t max, const bme680_context dev) +{ + bme680_results_t result = BME680_OK; + if (value != NULL) + { + /* Check if value is below minimum value */ + if (*value < min) + { + /* Auto correct the invalid value to minimum value */ + *value = min; + } + /* Check if value is above maximum value */ + if (*value > max) + { + /* Auto correct the invalid value to maximum value */ + *value = max; + } + } + else + { + result = BME680_E_NULL_PTR; + } + return result; +} + +static bme680_results_t bme680_get_calibration_data (const bme680_context dev) +{ + bme680_results_t result = BME680_OK; + uint8_t coeff_array[BME680_COEFF_SIZE] = {0}; + uint8_t temp_var = 0; + + assert (dev != NULL); + + result = bme680_read_regs (dev, BME680_COEFF_ADDR1, BME680_COEFF_ADDR1_LEN, coeff_array); + assert (result == BME680_OK); + + /* Append the second half in the same array */ + result |= bme680_read_regs (dev, BME680_COEFF_ADDR2, BME680_COEFF_ADDR2_LEN, &coeff_array[BME680_COEFF_ADDR1_LEN]); + assert (result == BME680_OK); + + /* Temperature related coefficients */ + dev->calib.par_t1 = (uint16_t) (BME680_CONCAT_BYTES(coeff_array[BME680_T1_MSB_REG], coeff_array[BME680_T1_LSB_REG])); + dev->calib.par_t2 = (int16_t) (BME680_CONCAT_BYTES(coeff_array[BME680_T2_MSB_REG], coeff_array[BME680_T2_LSB_REG])); + dev->calib.par_t3 = (int8_t) (coeff_array[BME680_T3_REG]); + + /* Pressure related coefficients */ + dev->calib.par_p1 = (uint16_t) (BME680_CONCAT_BYTES(coeff_array[BME680_P1_MSB_REG], coeff_array[BME680_P1_LSB_REG])); + dev->calib.par_p2 = (int16_t) (BME680_CONCAT_BYTES(coeff_array[BME680_P2_MSB_REG], coeff_array[BME680_P2_LSB_REG])); + dev->calib.par_p3 = (int8_t) coeff_array[BME680_P3_REG]; + dev->calib.par_p4 = (int16_t) (BME680_CONCAT_BYTES(coeff_array[BME680_P4_MSB_REG], coeff_array[BME680_P4_LSB_REG])); + dev->calib.par_p5 = (int16_t) (BME680_CONCAT_BYTES(coeff_array[BME680_P5_MSB_REG], coeff_array[BME680_P5_LSB_REG])); + dev->calib.par_p6 = (int8_t) (coeff_array[BME680_P6_REG]); + dev->calib.par_p7 = (int8_t) (coeff_array[BME680_P7_REG]); + dev->calib.par_p8 = (int16_t) (BME680_CONCAT_BYTES(coeff_array[BME680_P8_MSB_REG], coeff_array[BME680_P8_LSB_REG])); + dev->calib.par_p9 = (int16_t) (BME680_CONCAT_BYTES(coeff_array[BME680_P9_MSB_REG], coeff_array[BME680_P9_LSB_REG])); + dev->calib.par_p10 = (uint8_t) (coeff_array[BME680_P10_REG]); + + /* Humidity related coefficients */ + dev->calib.par_h1 = (uint16_t) (((uint16_t) coeff_array[BME680_H1_MSB_REG] << BME680_HUM_REG_SHIFT_VAL) + | (coeff_array[BME680_H1_LSB_REG] & BME680_BIT_H1_DATA_MSK)); + dev->calib.par_h2 = (uint16_t) (((uint16_t) coeff_array[BME680_H2_MSB_REG] << BME680_HUM_REG_SHIFT_VAL) + | ((coeff_array[BME680_H2_LSB_REG]) >> BME680_HUM_REG_SHIFT_VAL)); + dev->calib.par_h3 = (int8_t) coeff_array[BME680_H3_REG]; + dev->calib.par_h4 = (int8_t) coeff_array[BME680_H4_REG]; + dev->calib.par_h5 = (int8_t) coeff_array[BME680_H5_REG]; + dev->calib.par_h6 = (uint8_t) coeff_array[BME680_H6_REG]; + dev->calib.par_h7 = (int8_t) coeff_array[BME680_H7_REG]; + + /* Gas heater related coefficients */ + dev->calib.par_gh1 = (int8_t) coeff_array[BME680_GH1_REG]; + dev->calib.par_gh2 = (int16_t) ( + BME680_CONCAT_BYTES(coeff_array[BME680_GH2_MSB_REG], coeff_array[BME680_GH2_LSB_REG])); + dev->calib.par_gh3 = (int8_t) coeff_array[BME680_GH3_REG]; + + result |= bme680_read_regs (dev, BME680_ADDR_RES_HEAT_RANGE_ADDR, 1, &temp_var); + assert (result == BME680_OK); + + dev->calib.res_heat_range = ((temp_var & BME680_RHRANGE_MSK) / 16); + + result |= bme680_read_regs (dev, BME680_ADDR_RES_HEAT_VAL_ADDR, 1, &temp_var); + assert (result == BME680_OK); + + dev->calib.res_heat_val = (int8_t) temp_var; + result |= bme680_read_regs (dev, BME680_ADDR_RANGE_SW_ERR_ADDR, 1, &temp_var); + assert (result == BME680_OK); + + dev->calib.range_sw_err = ((int8_t) temp_var & (int8_t) BME680_RSERROR_MSK) / 16; + + return result; +} + +bme680_results_t bme680_reset (const bme680_context dev) +{ + bme680_results_t result = BME680_OK; + assert (dev != NULL); + + result = bme680_write_reg (dev, BME680_SOFT_RESET_ADDR, BME680_SOFT_RESET_CMD); + usleep (5000); //5ms + + return result; +} + +bme680_results_t bme680_set_sensor_mode (const bme680_context dev) +{ + bme680_results_t result = BME680_OK; + uint8_t tmp_pow_mode; + uint8_t pow_mode = 0; + uint8_t reg_addr = BME680_CONF_T_P_MODE_ADDR; + + /* Check for null pointer in the device structure*/ + assert (dev != NULL); + if (result == BME680_OK) + { + /* Call repeatedly until in sleep */ + do + { + result = bme680_read_regs (dev, reg_addr, 1, &tmp_pow_mode); + assert (result == BME680_OK); + + if (result == BME680_OK) + { + /* Put to sleep before changing mode */ + pow_mode = (tmp_pow_mode & BME680_MODE_MSK); + + if (pow_mode != BME680_SLEEP_MODE) + { + tmp_pow_mode = tmp_pow_mode & (~BME680_MODE_MSK); /* Set to sleep */ + result = bme680_write_reg (dev, reg_addr, tmp_pow_mode); + usleep (10 * 1000); + } + } + } while (pow_mode != BME680_SLEEP_MODE); + + /* Already in sleep */ + if (dev->mode != BME680_SLEEP_MODE) + { + pow_mode = (tmp_pow_mode & ~BME680_MODE_MSK) | (dev->mode & BME680_MODE_MSK); + if (result == BME680_OK) + { + result = bme680_write_reg (dev, reg_addr, pow_mode); + } + } + } + return result; +} + +bme680_results_t bme680_get_sensor_mode (const bme680_context dev) +{ + bme680_results_t result = BME680_OK; + uint8_t mode; + assert (dev != NULL); + + result = bme680_read_regs (dev, BME680_CONF_T_P_MODE_ADDR, 1, &mode); + dev->mode = mode & BME680_MODE_MSK; + + return result; +} + +bme680_context bme680_init (int bus, int addr) +{ + bme680_results_t result = BME680_OK; + bme680_context dev = + (bme680_context) malloc (sizeof (struct _bme680_context)); + + if (!dev) + { + return NULL; + } + + memset ((void *) dev, 0, sizeof (struct _bme680_context)); + + if (addr < 0) + { + printf ("SPI support not available\n"); + bme680_close (dev); + dev = NULL; + } + + if (!(dev->i2c = mraa_i2c_init (bus))) + { + bme680_close (dev); + dev = NULL; + } + + if (mraa_i2c_address (dev->i2c, addr)) + { + bme680_close (dev); + dev = NULL; + } + + result = bme680_reset (dev); + assert (result == BME680_OK); + + dev->mode = BME680_SLEEP_MODE; + result = bme680_set_sensor_mode (dev); + assert (result == BME680_OK); + + //read calibration data + result = bme680_get_calibration_data (dev); + assert (result == BME680_OK); + + if (result != BME680_OK) + { + bme680_close (dev); + dev = NULL; + } + + return dev; +} + +void bme680_close (bme680_context dev) +{ + assert (dev != NULL); + + if (dev->i2c) + { + mraa_i2c_stop (dev->i2c); + } + + free (dev); +} + +void bme680_set_temp_offset (bme680_context dev, float temp_offset) +{ + assert (dev != NULL); + + dev->calib.offset_temp_in_t_fine = (((((int) (temp_offset) * 100) << 8) - 128) / 5); +} + +bme680_results_t bme680_get_sensor_data (const bme680_context dev, struct bme680_field_data *data) +{ + bme680_results_t result = BME680_OK; + assert (dev != NULL); + + result = bme680_read_field_data (data, dev); + assert (result == BME680_OK); + + return result; +} + +bme680_results_t bme680_set_sensor_settings (uint16_t desired_settings, const bme680_context dev) +{ + bme680_results_t result = BME680_OK; + uint8_t reg_addr; + uint8_t data = 0; + uint8_t intended_power_mode = dev->mode; /* Save intended power mode */ + + assert (dev != NULL); + if (desired_settings & BME680_GAS_MEAS_SEL) + { + result = set_gas_config (dev); + assert (result == BME680_OK); + } + + dev->mode = BME680_SLEEP_MODE; + result |= bme680_set_sensor_mode (dev); + assert (result == BME680_OK); + + /* Selecting the filter */ + if (desired_settings & BME680_FILTER_SEL) + { + result |= boundary_check (&dev->tph_sett.filter, BME680_FILTER_SIZE_0, BME680_FILTER_SIZE_127, dev); + reg_addr = BME680_CONF_ODR_FILT_ADDR; + + if (result == BME680_OK) + { + result = bme680_read_regs (dev, reg_addr, 1, &data); + } + + if (desired_settings & BME680_FILTER_SEL) + { + data = BME680_SET_BITS(data, BME680_FILTER, dev->tph_sett.filter); + } + + result |= bme680_write_reg (dev, reg_addr, data); + assert (result == BME680_OK); + } + + /* Selecting heater control for the sensor */ + if (desired_settings & BME680_HCNTRL_SEL) + { + result = boundary_check (&dev->gas_sett.heatr_ctrl, BME680_ENABLE_HEATER, + BME680_DISABLE_HEATER, dev); + reg_addr = BME680_CONF_HEAT_CTRL_ADDR; + + if (result == BME680_OK) + { + result = bme680_read_regs (dev, reg_addr, 1, &data); + assert (result == BME680_OK); + } + data = BME680_SET_BITS_POS_0(data, BME680_HCTRL, dev->gas_sett.heatr_ctrl); + + result = bme680_write_reg (dev, reg_addr, data); + assert (result == BME680_OK); + } + + /* Selecting heater T,P oversampling for the sensor */ + if (desired_settings & (BME680_OST_SEL | BME680_OSP_SEL)) + { + result = boundary_check (&dev->tph_sett.os_temp, BME680_OS_NONE, BME680_OS_16X, dev); + reg_addr = BME680_CONF_T_P_MODE_ADDR; + + if (result == BME680_OK) + { + result = bme680_read_regs (dev, reg_addr, 1, &data); + } + + if (desired_settings & BME680_OST_SEL) + { + data = BME680_SET_BITS(data, BME680_OST, dev->tph_sett.os_temp); + } + + if (desired_settings & BME680_OSP_SEL) + { + data = BME680_SET_BITS(data, BME680_OSP, dev->tph_sett.os_pres); + } + + result = bme680_write_reg (dev, reg_addr, data); + assert (result == BME680_OK); + } + + /* Selecting humidity oversampling for the sensor */ + if (desired_settings & BME680_OSH_SEL) + { + result = boundary_check (&dev->tph_sett.os_hum, BME680_OS_NONE, BME680_OS_16X, dev); + reg_addr = BME680_CONF_OS_H_ADDR; + + if (result == BME680_OK) + { + result = bme680_read_regs (dev, reg_addr, 1, &data); + } + + data = BME680_SET_BITS_POS_0(data, BME680_OSH, dev->tph_sett.os_hum); + + result = bme680_write_reg (dev, reg_addr, data); + assert (result == BME680_OK); + } + + /* Selecting the runGas and NB conversion settings for the sensor */ + if (desired_settings & (BME680_RUN_GAS_SEL | BME680_NBCONV_SEL)) + { + result = boundary_check (&dev->gas_sett.run_gas, BME680_RUN_GAS_DISABLE, + BME680_RUN_GAS_ENABLE, dev); + if (result == BME680_OK) + { + /* Validate boundary conditions */ + result = boundary_check (&dev->gas_sett.nb_conv, BME680_NBCONV_MIN, + BME680_NBCONV_MAX, dev); + } + + reg_addr = BME680_CONF_ODR_RUN_GAS_NBC_ADDR; + + if (result == BME680_OK) + { + result |= bme680_read_regs (dev, reg_addr, 1, &data); + assert (result == BME680_OK); + } + + if (desired_settings & BME680_RUN_GAS_SEL) + { + data = BME680_SET_BITS(data, BME680_RUN_GAS, dev->gas_sett.run_gas); + } + + if (desired_settings & BME680_NBCONV_SEL) + { + data = BME680_SET_BITS_POS_0(data, BME680_NBCONV, dev->gas_sett.nb_conv); + } + + result = bme680_write_reg (dev, reg_addr, data); + assert (result == BME680_OK); + } + + /* Restore previous intended power mode */ + dev->mode = intended_power_mode; + + return result; +} + +/*! + * @brief This API is used to get the oversampling, filter and T,P,H, gas selection + * settings in the sensor. + */ +bme680_results_t bme680_get_sensor_settings (uint16_t desired_settings, const bme680_context dev) +{ + bme680_results_t result; + /* starting address of the register array for burst read*/ + uint8_t reg_addr = BME680_CONF_HEAT_CTRL_ADDR; + uint8_t data_array[BME680_REG_BUFFER_LENGTH] = {0}; + + /* Check for null pointer in the device structure*/ + assert (dev != NULL); + result = bme680_read_regs (dev, reg_addr, BME680_REG_BUFFER_LENGTH, data_array); + assert (result == BME680_OK); + + if (desired_settings & BME680_GAS_MEAS_SEL) + { + result = get_gas_config (dev); + + /* get the T,P,H ,Filter,ODR settings here */ + if (desired_settings & BME680_FILTER_SEL) + { + dev->tph_sett.filter = BME680_GET_BITS(data_array[BME680_REG_FILTER_INDEX], + BME680_FILTER); + } + + if (desired_settings & (BME680_OST_SEL | BME680_OSP_SEL)) + { + dev->tph_sett.os_temp = BME680_GET_BITS(data_array[BME680_REG_TEMP_INDEX], BME680_OST); + dev->tph_sett.os_pres = BME680_GET_BITS(data_array[BME680_REG_PRES_INDEX], BME680_OSP); + } + + if (desired_settings & BME680_OSH_SEL) + { + dev->tph_sett.os_hum = BME680_GET_BITS_POS_0(data_array[BME680_REG_HUM_INDEX], + BME680_OSH); + } + + /* get the gas related settings */ + if (desired_settings & BME680_HCNTRL_SEL) + { + dev->gas_sett.heatr_ctrl = BME680_GET_BITS_POS_0(data_array[BME680_REG_HCTRL_INDEX], + BME680_HCTRL); + } + + if (desired_settings & (BME680_RUN_GAS_SEL | BME680_NBCONV_SEL)) + { + dev->gas_sett.nb_conv = BME680_GET_BITS_POS_0(data_array[BME680_REG_NBCONV_INDEX], + BME680_NBCONV); + dev->gas_sett.run_gas = BME680_GET_BITS(data_array[BME680_REG_RUN_GAS_INDEX], + BME680_RUN_GAS); + } + } + return result; +} + +/*! + * @brief This API is used to set the profile duration of the sensor. + */ +void bme680_set_profile_dur (uint16_t duration, const bme680_context dev) +{ + uint32_t tph_dur; /* Calculate in us */ + uint32_t meas_cycles; + uint8_t os_to_meas_cycles[6] = {0, 1, 2, 4, 8, 16}; + + meas_cycles = os_to_meas_cycles[dev->tph_sett.os_temp]; + meas_cycles += os_to_meas_cycles[dev->tph_sett.os_pres]; + meas_cycles += os_to_meas_cycles[dev->tph_sett.os_hum]; + + /* TPH measurement duration */ + tph_dur = meas_cycles * UINT32_C (1963); + tph_dur += UINT32_C (477 * 4); /* TPH switching duration */ + tph_dur += UINT32_C (477 * 5); /* Gas measurement duration */ + tph_dur += UINT32_C (500); /* Get it to the closest whole number.*/ + tph_dur /= UINT32_C (1000); /* Convert to ms */ + + tph_dur += UINT32_C (1); /* Wake up duration of 1ms */ + /* The remaining time should be used for heating */ + dev->gas_sett.heatr_dur = duration - (uint16_t) tph_dur; +} + +/*! + * @brief This API is used to get the profile duration of the sensor. + */ +void bme680_get_profile_dur (uint16_t *duration, const bme680_context dev) +{ + uint32_t tph_dur; /* Calculate in us */ + uint32_t meas_cycles; + uint8_t os_to_meas_cycles[6] = {0, 1, 2, 4, 8, 16}; + + meas_cycles = os_to_meas_cycles[dev->tph_sett.os_temp]; + meas_cycles += os_to_meas_cycles[dev->tph_sett.os_pres]; + meas_cycles += os_to_meas_cycles[dev->tph_sett.os_hum]; + + /* TPH measurement duration */ + tph_dur = meas_cycles * UINT32_C (1963); + tph_dur += UINT32_C (477 * 4); /* TPH switching duration */ + tph_dur += UINT32_C (477 * 5); /* Gas measurement duration */ + tph_dur += UINT32_C (500); /* Get it to the closest whole number.*/ + tph_dur /= UINT32_C (1000); /* Convert to ms */ + + tph_dur += UINT32_C (1); /* Wake up duration of 1ms */ + + *duration = (uint16_t) tph_dur; + + /* Get the gas duration only when the run gas is enabled */ + if (dev->gas_sett.run_gas) + { + /* The remaining time should be used for heating */ + *duration += dev->gas_sett.heatr_dur; + } +} diff --git a/src/c/bme680_mraa.h b/src/c/bme680_mraa.h new file mode 100644 index 0000000..a2ba01c --- /dev/null +++ b/src/c/bme680_mraa.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2018 + * IoTech Ltd + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +/* Based on code from https://github.com/BoschSensortec/BME680_driver */ +/**\mainpage + * Copyright (C) 2017 - 2018 Bosch Sensortec GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of the + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + * + * The information provided is believed to be accurate and reliable. + * The copyright holder assumes no responsibility + * for the consequences of use + * of such information nor for any infringement of patents or + * other rights of third parties which may result from its use. + * No license is granted by implication or otherwise under any patent or + * patent rights of the copyright holder. + */ + + +#include +#include +#include +#include + +#include "bme680_mraa_regs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct bme680_calib_data +{ + uint16_t par_h1; + uint16_t par_h2; + int8_t par_h3; + int8_t par_h4; + int8_t par_h5; + uint8_t par_h6; + int8_t par_h7; + int8_t par_gh1; + int16_t par_gh2; + int8_t par_gh3; + uint16_t par_t1; + int16_t par_t2; + int8_t par_t3; + uint16_t par_p1; + int16_t par_p2; + int8_t par_p3; + int16_t par_p4; + int16_t par_p5; + int8_t par_p6; + int8_t par_p7; + int16_t par_p8; + int16_t par_p9; + uint8_t par_p10; + float t_fine; + float offset_temp_in_t_fine; + uint8_t res_heat_range; + int8_t res_heat_val; + int8_t range_sw_err; +}; + +/*! + * @brief BME680 sensor settings structure which comprises of ODR, + * over-sampling and filter settings. + */ +struct bme680_tph_sett +{ + /*! Humidity oversampling */ + uint8_t os_hum; + /*! Temperature oversampling */ + uint8_t os_temp; + /*! Pressure oversampling */ + uint8_t os_pres; + /*! Filter coefficient */ + uint8_t filter; +}; + +/*! + * @brief BME680 gas sensor which comprises of gas settings + * and status parameters + */ +struct bme680_gas_sett +{ + /*! Variable to store nb conversion */ + uint8_t nb_conv; + /*! Variable to store heater control */ + uint8_t heatr_ctrl; + /*! Run gas enable value */ + uint8_t run_gas; + /*! Heater temperature value */ + uint16_t heatr_temp; + /*! Duration profile value */ + uint16_t heatr_dur; +}; + +typedef struct _bme680_context +{ + mraa_i2c_context i2c; + BME680_MODES_T mode; + int8_t amb_temp; + struct bme680_calib_data calib; + struct bme680_tph_sett tph_sett; + struct bme680_gas_sett gas_sett; +} *bme680_context; + +typedef struct bme680_field_data +{ + uint8_t status; + uint8_t gas_index; + uint8_t meas_index; + float temperature; /*! Temperature in degree celsius */ + float pressure; /*! Pressure in Pascal */ + float humidity; /*! Humidity in % relative humidity x1000 */ + float gas_resistance; /*! Gas resistance in Ohms */ +} bme680_data; + + +bme680_context bme680_init (int bus, int addr); + +void bme680_close (bme680_context dev); + +void bme680_set_temp_offset (bme680_context dev, float temp_offset); + +bme680_results_t bme680_get_sensor_data (const bme680_context dev, struct bme680_field_data *data); + +bme680_results_t bme680_reset (const bme680_context dev); + +bme680_results_t bme680_set_sensor_settings (uint16_t desired_settings, const bme680_context dev); + +bme680_results_t bme680_get_sensor_settings (uint16_t desired_settings, const bme680_context dev); + +bme680_results_t bme680_set_sensor_mode (const bme680_context dev); + +bme680_results_t bme680_get_sensor_mode (const bme680_context dev); + +void bme680_set_profile_dur (uint16_t duration, const bme680_context dev); + +void bme680_get_profile_dur (uint16_t *duration, const bme680_context dev); + +#ifdef __cplusplus +} +#endif + + + diff --git a/src/c/bme680_mraa_regs.h b/src/c/bme680_mraa_regs.h new file mode 100644 index 0000000..3ab7cb7 --- /dev/null +++ b/src/c/bme680_mraa_regs.h @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2018 + * IoTech Ltd + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +/* Based on code from https://github.com/BoschSensortec/BME680_driver */ +/**\mainpage + * Copyright (C) 2017 - 2018 Bosch Sensortec GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of the + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + * + * The information provided is believed to be accurate and reliable. + * The copyright holder assumes no responsibility + * for the consequences of use + * of such information nor for any infringement of patents or + * other rights of third parties which may result from its use. + * No license is granted by implication or otherwise under any patent or + * patent rights of the copyright holder. + */ + +#pragma once + +/** Macro to combine two 8 bit data's to form a 16 bit data */ +#define BME680_CONCAT_BYTES(msb, lsb) (((uint16_t)msb << 8) | (uint16_t)lsb) + +/** Macro to SET and GET BITS of a register */ +#define BME680_SET_BITS(reg_data, bitname, data) \ + ((reg_data & ~(bitname##_MSK)) | ((data << bitname##_POS) & bitname##_MSK)) +#define BME680_GET_BITS(reg_data, bitname) ((reg_data & (bitname##_MSK)) >> (bitname##_POS)) + +/** Macro variant to handle the bitname position if it is zero */ +#define BME680_SET_BITS_POS_0(reg_data, bitname, data) \ + ((reg_data & ~(bitname##_MSK)) | (data & bitname##_MSK)) +#define BME680_GET_BITS_POS_0(reg_data, bitname) (reg_data & (bitname##_MSK)) + +//BME680 +#define BME680_DEFAULT_I2C_BUS 0 +#define BME680_DEFAULT_ADDR 0x76 +#define BME680_CHIPID 0x61 + +/** Soft reset command */ +#define BME680_SOFT_RESET_CMD 0xb6 + +/** Over-sampling settings */ +#define BME680_OS_NONE 0 +#define BME680_OS_1X 1 +#define BME680_OS_2X 2 +#define BME680_OS_4X 3 +#define BME680_OS_8X 4 +#define BME680_OS_16X 5 + +/** IIR filter settings */ +#define BME680_FILTER_SIZE_0 0 +#define BME680_FILTER_SIZE_1 1 +#define BME680_FILTER_SIZE_3 2 +#define BME680_FILTER_SIZE_7 3 +#define BME680_FILTER_SIZE_15 4 +#define BME680_FILTER_SIZE_31 5 +#define BME680_FILTER_SIZE_63 6 +#define BME680_FILTER_SIZE_127 7 + +/** Settings selector */ +#define BME680_OST_SEL UINT16_C(1) +#define BME680_OSP_SEL UINT16_C(2) +#define BME680_OSH_SEL UINT16_C(4) +#define BME680_GAS_MEAS_SEL UINT16_C(8) +#define BME680_FILTER_SEL UINT16_C(16) +#define BME680_HCNTRL_SEL UINT16_C(32) +#define BME680_RUN_GAS_SEL UINT16_C(64) +#define BME680_NBCONV_SEL UINT16_C(128) +#define BME680_GAS_SENSOR_SEL (BME680_GAS_MEAS_SEL | BME680_RUN_GAS_SEL | BME680_NBCONV_SEL) + +/** Number of conversion settings*/ +#define BME680_NBCONV_MIN UINT8_C(0) +#define BME680_NBCONV_MAX UINT8_C(10) + +/** BME680 coefficients related defines */ +#define BME680_COEFF_SIZE 41 +#define BME680_COEFF_ADDR1_LEN 25 +#define BME680_COEFF_ADDR2_LEN 16 + +/** Register map */ +/** Other coefficient's address */ +#define BME680_ADDR_RES_HEAT_VAL_ADDR 0x00 +#define BME680_ADDR_RES_HEAT_RANGE_ADDR 0x02 +#define BME680_ADDR_RANGE_SW_ERR_ADDR 0x04 +#define BME680_ADDR_SENS_CONF_START 0x5A +#define BME680_ADDR_GAS_CONF_START 0x64 + +/** BME680 field_x related defines */ +#define BME680_FIELD_LENGTH (15) +#define BME680_FIELD_ADDR_OFFSET (17) + +/** BME680 register buffer index settings*/ +#define BME680_REG_FILTER_INDEX UINT8_C(5) +#define BME680_REG_TEMP_INDEX UINT8_C(4) +#define BME680_REG_PRES_INDEX UINT8_C(4) +#define BME680_REG_HUM_INDEX UINT8_C(2) +#define BME680_REG_NBCONV_INDEX UINT8_C(1) +#define BME680_REG_RUN_GAS_INDEX UINT8_C(1) +#define BME680_REG_HCTRL_INDEX UINT8_C(0) + +/** Array Index to Field data mapping for Calibration Data*/ +#define BME680_T2_LSB_REG (1) +#define BME680_T2_MSB_REG (2) +#define BME680_T3_REG (3) +#define BME680_P1_LSB_REG (5) +#define BME680_P1_MSB_REG (6) +#define BME680_P2_LSB_REG (7) +#define BME680_P2_MSB_REG (8) +#define BME680_P3_REG (9) +#define BME680_P4_LSB_REG (11) +#define BME680_P4_MSB_REG (12) +#define BME680_P5_LSB_REG (13) +#define BME680_P5_MSB_REG (14) +#define BME680_P7_REG (15) +#define BME680_P6_REG (16) +#define BME680_P8_LSB_REG (19) +#define BME680_P8_MSB_REG (20) +#define BME680_P9_LSB_REG (21) +#define BME680_P9_MSB_REG (22) +#define BME680_P10_REG (23) +#define BME680_H2_MSB_REG (25) +#define BME680_H2_LSB_REG (26) +#define BME680_H1_LSB_REG (26) +#define BME680_H1_MSB_REG (27) +#define BME680_H3_REG (28) +#define BME680_H4_REG (29) +#define BME680_H5_REG (30) +#define BME680_H6_REG (31) +#define BME680_H7_REG (32) +#define BME680_T1_LSB_REG (33) +#define BME680_T1_MSB_REG (34) +#define BME680_GH2_LSB_REG (35) +#define BME680_GH2_MSB_REG (36) +#define BME680_GH1_REG (37) +#define BME680_GH3_REG (38) + +/** Ambient humidity shift value for compensation */ +#define BME680_HUM_REG_SHIFT_VAL 4 + +/** Heater control settings */ +#define BME680_ENABLE_HEATER 0x00 +#define BME680_DISABLE_HEATER 0x08 + +/** Run gas enable and disable settings */ +#define BME680_RUN_GAS_DISABLE 0 +#define BME680_RUN_GAS_ENABLE 1 + +/** Register map */ + +/** Field settings */ +#define BME680_FIELD0_ADDR UINT8_C(0x1d) + +/** Heater settings */ +#define BME680_RES_HEAT0_ADDR UINT8_C(0x5a) +#define BME680_GAS_WAIT0_ADDR UINT8_C(0x64) + +/** Sensor configuration registers */ +#define BME680_CONF_HEAT_CTRL_ADDR UINT8_C(0x70) +#define BME680_CONF_ODR_RUN_GAS_NBC_ADDR UINT8_C(0x71) +#define BME680_CONF_OS_H_ADDR UINT8_C(0x72) +#define BME680_MEM_PAGE_ADDR UINT8_C(0xf3) +#define BME680_CONF_T_P_MODE_ADDR UINT8_C(0x74) +#define BME680_CONF_ODR_FILT_ADDR UINT8_C(0x75) + +/** Coefficient's address */ +#define BME680_COEFF_ADDR1 UINT8_C(0x89) +#define BME680_COEFF_ADDR2 UINT8_C(0xe1) + +/** Chip identifier */ +#define BME680_CHIP_ID_ADDR UINT8_C(0xd0) + +/** Soft reset register */ +#define BME680_SOFT_RESET_ADDR UINT8_C(0xe0) + +/** Gas measurement settings */ +#define BME680_DISABLE_GAS_MEAS UINT8_C(0x00) +#define BME680_ENABLE_GAS_MEAS UINT8_C(0x01) + +/** Buffer length macro declaration */ +#define BME680_TMP_BUFFER_LENGTH UINT8_C(40) +#define BME680_REG_BUFFER_LENGTH UINT8_C(6) +#define BME680_FIELD_DATA_LENGTH UINT8_C(3) +#define BME680_GAS_REG_BUF_LENGTH UINT8_C(20) + +/** Settings selector */ +#define BME680_OST_SEL UINT16_C(1) +#define BME680_OSP_SEL UINT16_C(2) +#define BME680_OSH_SEL UINT16_C(4) +#define BME680_GAS_MEAS_SEL UINT16_C(8) +#define BME680_FILTER_SEL UINT16_C(16) +#define BME680_HCNTRL_SEL UINT16_C(32) +#define BME680_RUN_GAS_SEL UINT16_C(64) +#define BME680_NBCONV_SEL UINT16_C(128) +#define BME680_GAS_SENSOR_SEL (BME680_GAS_MEAS_SEL | BME680_RUN_GAS_SEL | BME680_NBCONV_SEL) + +/** Number of conversion settings*/ +#define BME680_NBCONV_MIN UINT8_C(0) +#define BME680_NBCONV_MAX UINT8_C(10) + +/** Mask definitions */ +#define BME680_GAS_MEAS_MSK UINT8_C(0x30) +#define BME680_NBCONV_MSK UINT8_C(0X0F) +#define BME680_FILTER_MSK UINT8_C(0X1C) +#define BME680_OST_MSK UINT8_C(0XE0) +#define BME680_OSP_MSK UINT8_C(0X1C) +#define BME680_OSH_MSK UINT8_C(0X07) +#define BME680_HCTRL_MSK UINT8_C(0x08) +#define BME680_RUN_GAS_MSK UINT8_C(0x10) +#define BME680_MODE_MSK UINT8_C(0x03) +#define BME680_RHRANGE_MSK UINT8_C(0x30) +#define BME680_RSERROR_MSK UINT8_C(0xf0) +#define BME680_NEW_DATA_MSK UINT8_C(0x80) +#define BME680_GAS_INDEX_MSK UINT8_C(0x0f) +#define BME680_GAS_RANGE_MSK UINT8_C(0x0f) +#define BME680_GASM_VALID_MSK UINT8_C(0x20) +#define BME680_HEAT_STAB_MSK UINT8_C(0x10) +#define BME680_MEM_PAGE_MSK UINT8_C(0x10) +#define BME680_SPI_RD_MSK UINT8_C(0x80) +#define BME680_SPI_WR_MSK UINT8_C(0x7f) +#define BME680_BIT_H1_DATA_MSK UINT8_C(0x0F) + +/** Bit position definitions for sensor settings */ +#define BME680_GAS_MEAS_POS UINT8_C(4) +#define BME680_FILTER_POS UINT8_C(2) +#define BME680_OST_POS UINT8_C(5) +#define BME680_OSP_POS UINT8_C(2) +#define BME680_RUN_GAS_POS UINT8_C(4) + +typedef enum +{ + BME680_SLEEP_MODE = 0, + BME680_FORCED_MODE = 1 +} BME680_MODES_T; + +typedef enum +{ + BME680_OK = 0, + + BME680_E_NULL_PTR = -1, + BME680_E_COM_FAIL = -2, + BME680_E_DEV_NOT_FOUND = -3, + BME680_E_INVALID_LENGTH = -4, + + BME680_W_DEFINE_PWR_MODE = 1, + BME680_W_NO_NEW_DATA = 2 +} bme680_results_t; + diff --git a/src/c/device_grove.h b/src/c/device_grove.h index 1c02990..3108c27 100644 --- a/src/c/device_grove.h +++ b/src/c/device_grove.h @@ -15,7 +15,9 @@ #include #include #include +#include "edgex/edgex.h" #include "edgex/devsdk.h" +#include "edgex/device-mgmt.h" #if defined (__cplusplus) extern "C" { @@ -35,14 +37,16 @@ extern "C" { #define GROVE_ADC_REF 5 #define GROVE_SVC "Device-Grove" +#define VERSION "1.0.1" typedef enum { - GROVE_GPIO = 0, - GROVE_PWM = 1, - GROVE_AIO = 2, - GROVE_I2C = 3, - GROVE_SERIAL = 4 + GROVE_GPIO = 0, + GROVE_PWM = 1, + GROVE_AIO = 2, + GROVE_I2C = 3, + GROVE_SERIAL = 4, + GROVE_UNKNOWN_INTF = -1 } grove_interface_type_t; typedef struct @@ -62,14 +66,15 @@ typedef struct typedef struct { - grove_interface_type_t pin_type; + grove_interface_type_t intf_type; + char *pin_type; char *pin_number; void *dev_ctxt; } grove_dev_ctxt_t; typedef struct { - iot_logging_client *lc; + iot_logger_t *lc; edgex_device_service *svc; grove_dev_ctxt_t *dev[GROVE_NO_PORTS]; pthread_mutex_t mutex; diff --git a/src/c/grove_bme680.c b/src/c/grove_bme680.c new file mode 100644 index 0000000..3215f0c --- /dev/null +++ b/src/c/grove_bme680.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2018 + * IoTech Ltd + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +/* Based on code from https://github.com/BoschSensortec/BME680_driver - Self Test */ +/**\mainpage + * Copyright (C) 2017 - 2018 Bosch Sensortec GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of the + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + * + * The information provided is believed to be accurate and reliable. + * The copyright holder assumes no responsibility + * for the consequences of use + * of such information nor for any infringement of patents or + * other rights of third parties which may result from its use. + * No license is granted by implication or otherwise under any patent or + * patent rights of the copyright holder. + */ + + +#include +#include + +#include "device_grove.h" +#include "grove_bme680.h" + +bme680_context grove_bme680_initialize (int bus, int addr, float temp_offset) +{ + bme680_results_t result = BME680_OK; + uint16_t settings_sel; + bme680_context tph_dev = bme680_init (bus, addr); + assert (tph_dev != NULL); + + if (tph_dev != NULL) + { + /* Set the temperature, pressure and humidity & filter settings */ + tph_dev->tph_sett.os_hum = BME680_OS_1X; + tph_dev->tph_sett.os_pres = BME680_OS_16X; + tph_dev->tph_sett.os_temp = BME680_OS_2X; + + /* Set the remaining gas sensor settings and link the heating profile */ + tph_dev->gas_sett.run_gas = BME680_ENABLE_GAS_MEAS; + + settings_sel = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_GAS_SENSOR_SEL; + + result = bme680_set_sensor_settings (settings_sel, tph_dev); + assert (result == BME680_OK); + + /* Note: Set the temperature offset as the sensor readings are high - source: Issue as in https://github.com/pimoroni/bme680-python/issues/20 */ + bme680_set_temp_offset (tph_dev, temp_offset); + + if (result != BME680_OK) + { + tph_dev = NULL; + } + } + return tph_dev; +} + +bool grove_bme_read_data (bme680_context tph_dev, struct bme680_field_data *field_data) +{ + bme680_results_t result = BME680_OK; + uint16_t profile_dur = 0; + + bme680_get_profile_dur (&profile_dur, tph_dev); + + tph_dev->mode = BME680_FORCED_MODE; + result = bme680_set_sensor_mode (tph_dev); + + /* Set the sensor mode to read data and wait before measuring values */ + usleep (profile_dur * 1000); + + result = bme680_get_sensor_data (tph_dev, field_data); + if (result != BME680_OK) + { + return false; + } + else + { + return true; + } +} diff --git a/src/c/grove_bme680.h b/src/c/grove_bme680.h new file mode 100644 index 0000000..cb50860 --- /dev/null +++ b/src/c/grove_bme680.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2018 + * IoTech Ltd + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +#include +#include "bme680_mraa.h" + +/* Pascals to hPa conversion */ +#define hPA_FACTOR 100 + +bme680_context grove_bme680_initialize (int bus, int addr, float temp_offset); + +bool grove_bme_read_data (bme680_context tph_dev, struct bme680_field_data *field_data); diff --git a/src/c/main.c b/src/c/main.c index e0c6b3d..0de5fe3 100644 --- a/src/c/main.c +++ b/src/c/main.c @@ -9,27 +9,31 @@ #include #include #include +#include #include "device_grove.h" #include "grove_lcd.h" +#include "grove_bme680.h" -static bool stop = false; +static sem_t grove_sem; +static float BME680_Temp_Offset = 0.0; static void grove_inthandler (int i) { - stop = true; + sem_post (&grove_sem); } static void usage (void) { printf ("Options: \n"); - printf (" -h, --help : Show this text\n"); - printf (" -r, --registry : Use the registry service\n"); - printf (" -p, --profile : Set the profile name\n"); - printf (" -c, --confdir : Set the configuration directory\n"); + printf (" -h, --help : Show this text\n"); + printf (" --registry= : Use the registry service\n"); + printf (" --profile= : Set the profile name\n"); + printf (" --confdir= : Set the configuration directory\n"); + printf (" --name= : Set the service name\n"); } -static grove_attributes_t *get_groveattributes (edgex_nvpairs *device_attr) +static grove_attributes_t *get_groveattributes (const edgex_nvpairs *device_attr) { grove_attributes_t *grove_attr = (grove_attributes_t *) malloc (sizeof (grove_attributes_t)); for (; device_attr != NULL; device_attr = device_attr->next) @@ -54,7 +58,7 @@ static grove_attributes_t *get_groveattributes (edgex_nvpairs *device_attr) return grove_attr; } -static grove_dev_ctxt_t * grove_device_lookup (grove_pidriver_t *impln, char *pin) +static grove_dev_ctxt_t *grove_device_lookup (grove_pidriver_t *impln, char *pin) { for (int index = 0; index < GROVE_NO_PORTS; index++) { @@ -74,17 +78,18 @@ static grove_dev_ctxt_t * grove_device_lookup (grove_pidriver_t *impln, char *pi } static grove_dev_ctxt_t *grove_set_devctxt - (grove_pidriver_t *impln, void *dev, char *pin, grove_interface_type_t interface_type) + (grove_pidriver_t *impln, void *dev, char *pin, char *type) { int index = 0; grove_dev_ctxt_t *mraa_dev = NULL; + grove_interface_type_t interface_type = GROVE_UNKNOWN_INTF; for (; index < GROVE_NO_PORTS; index++) { if (impln->dev[index] == NULL) { mraa_dev = malloc (sizeof (grove_dev_ctxt_t)); - if (strstr (pin, "I2C")) + if ((strstr (pin, "I2C")) && (strcmp (type, "LCD") == 0)) { grove_i2c_dev_ctxt_t *i2c_dev = malloc (sizeof (grove_i2c_dev_ctxt_t)); i2c_dev->dev = dev; @@ -96,7 +101,23 @@ static grove_dev_ctxt_t *grove_set_devctxt } mraa_dev->pin_number = malloc (strlen (pin)); strcpy (mraa_dev->pin_number, pin); - mraa_dev->pin_type = interface_type; + + mraa_dev->pin_type = malloc (strlen (type)); + strcpy (mraa_dev->pin_type, type); + + if (strstr (pin, "D")) + { + interface_type = GROVE_GPIO; + } + else if (strstr (pin, "A")) + { + interface_type = GROVE_AIO; + } + else if (strstr (pin, "I2C")) + { + interface_type = GROVE_I2C; + } + mraa_dev->intf_type = interface_type; impln->dev[index] = mraa_dev; break; @@ -108,7 +129,6 @@ static grove_dev_ctxt_t *grove_set_devctxt static mraa_result_t grove_gpio_init (grove_pidriver_t *impln, char *pin, char *type) { mraa_result_t status = MRAA_SUCCESS; - grove_interface_type_t pin_type = GROVE_GPIO; int pin_number = GROVE_SUBPLATFORM_OFFSET + (pin[strlen (pin) - 1] - '0'); mraa_gpio_context dev = mraa_gpio_init (pin_number); @@ -139,7 +159,7 @@ static mraa_result_t grove_gpio_init (grove_pidriver_t *impln, char *pin, char * } else { - if ((grove_set_devctxt (impln, (void *) dev, pin, pin_type)) == NULL) + if ((grove_set_devctxt (impln, (void *) dev, pin, type)) == NULL) { iot_log_error (impln->lc, "Unable to set the mraadev_ctxt at %s", pin, type); status = MRAA_ERROR_UNSPECIFIED; @@ -148,10 +168,9 @@ static mraa_result_t grove_gpio_init (grove_pidriver_t *impln, char *pin, char * return status; } -static mraa_result_t grove_aio_init (grove_pidriver_t *impln, char *pin) +static mraa_result_t grove_aio_init (grove_pidriver_t *impln, char *pin, char *type) { mraa_result_t status = MRAA_SUCCESS; - grove_interface_type_t pin_type = GROVE_AIO; int pin_number = GROVE_SUBPLATFORM_OFFSET + (pin[strlen (pin) - 1] - '0'); mraa_aio_context dev = mraa_aio_init (pin_number); @@ -163,7 +182,7 @@ static mraa_result_t grove_aio_init (grove_pidriver_t *impln, char *pin) } else { - if ((grove_set_devctxt (impln, (void *) dev, pin, pin_type)) == NULL) + if ((grove_set_devctxt (impln, (void *) dev, pin, type)) == NULL) { iot_log_error (impln->lc, "Unable to set the mraadev_ctxt at %s", pin); status = MRAA_ERROR_UNSPECIFIED; @@ -172,7 +191,7 @@ static mraa_result_t grove_aio_init (grove_pidriver_t *impln, char *pin) return status; } -static mraa_result_t grove_lcd_init (grove_pidriver_t *impln, char *pin) +static mraa_result_t grove_lcd_init (grove_pidriver_t *impln, char *pin, char *type) { /* Initialize I2C bus for LCD */ mraa_i2c_context lcd_dev = NULL; @@ -218,8 +237,7 @@ static mraa_result_t grove_lcd_init (grove_pidriver_t *impln, char *pin) if (status == MRAA_SUCCESS) { /* store the lcd_dev context */ - grove_interface_type_t pin_type = GROVE_I2C; - grove_dev_ctxt_t *lcd_ctx = grove_set_devctxt (impln, (void *) lcd_dev, pin, pin_type); + grove_dev_ctxt_t *lcd_ctx = grove_set_devctxt (impln, (void *) lcd_dev, pin, type); if (lcd_ctx == NULL) { iot_log_error (impln->lc, "Unable to set the mraadev_ctxt at %s", pin); @@ -235,13 +253,40 @@ static mraa_result_t grove_lcd_init (grove_pidriver_t *impln, char *pin) return status; } +static mraa_result_t grove_bme680_init (grove_pidriver_t *impln, char *pin, char *type) +{ + mraa_result_t status = MRAA_SUCCESS; + + bme680_context tph_dev = grove_bme680_initialize (GROVE_I2C_BUS, BME680_DEFAULT_ADDR, BME680_Temp_Offset); + + if (tph_dev != NULL) + { + /* store the context */ + if (grove_set_devctxt (impln, (void *) tph_dev, pin, type) == NULL) + { + iot_log_error (impln->lc, "Unable to set the mraadev_ctxt at %s", pin); + status = MRAA_ERROR_UNSPECIFIED; + } + } + else + { + status = MRAA_ERROR_UNSPECIFIED; + } + + return status; +} + static mraa_result_t grove_i2c_init (grove_pidriver_t *impln, char *pin, char *type) { mraa_result_t status = MRAA_SUCCESS; if (strcmp (type, "LCD") == 0) { - status = grove_lcd_init (impln, pin); + status = grove_lcd_init (impln, pin, type); + } + else if (strcmp (type, "BME680") == 0) + { + status = grove_bme680_init (impln, pin, type); } else { @@ -251,7 +296,7 @@ static mraa_result_t grove_i2c_init (grove_pidriver_t *impln, char *pin, char *t return status; } -static bool grove_init (void *impl, struct iot_logging_client *lc, const edgex_nvpairs *config) +static bool grove_init (void *impl, struct iot_logger_t *lc, const edgex_nvpairs *config) { mraa_result_t status = MRAA_SUCCESS; grove_pidriver_t *impln = (grove_pidriver_t *) impl; @@ -260,7 +305,6 @@ static bool grove_init (void *impl, struct iot_logging_client *lc, const edgex_n iot_log_debug (lc, "driver initialization"); { - uint32_t nprofiles = 0; edgex_deviceprofile *profiles = NULL; status = mraa_init (); @@ -272,16 +316,25 @@ static bool grove_init (void *impl, struct iot_logging_client *lc, const edgex_n mraa_add_subplatform (MRAA_GROVEPI, "0"); + /* read Driver specific configuration: BME680: Require temp_offset to be set to fine tune measurements */ + for (; config != NULL; config = config->next) + { + if (strcmp (config->name, "BME680_Temp_Offset") == 0) + { + BME680_Temp_Offset = (strtof (config->value, NULL)); + } + } + /* read the attributes from the device profile to initialize the driver */ - edgex_device_service_getprofiles (impln->svc, &nprofiles, &profiles); + profiles = edgex_device_profiles (impln->svc); - while (nprofiles--) + while (profiles) { - edgex_deviceobject *dev_obj = profiles[nprofiles].device_resources; + edgex_deviceresource *dev_res = profiles->device_resources; grove_attributes_t *grove_attr = NULL; - for (; dev_obj != NULL; dev_obj = dev_obj->next) + for (; dev_res != NULL; dev_res = dev_res->next) { - edgex_nvpairs *dev_attr = dev_obj->attributes; + edgex_nvpairs *dev_attr = dev_res->attributes; assert (dev_attr != NULL); grove_attr = get_groveattributes (dev_attr); @@ -301,7 +354,7 @@ static bool grove_init (void *impl, struct iot_logging_client *lc, const edgex_n } else if (strcmp (grove_attr->pin_type, "AIO") == 0) { - status = grove_aio_init (impln, grove_attr->pin_no); + status = grove_aio_init (impln, grove_attr->pin_no, grove_attr->type); assert (!status); } else if (strcmp (grove_attr->pin_type, "I2C") == 0) @@ -317,26 +370,28 @@ static bool grove_init (void *impl, struct iot_logging_client *lc, const edgex_n } free (grove_attr); } + profiles = profiles->next; } - free (profiles); + } return (status == MRAA_SUCCESS); } static bool grove_gethandler -( - void *impl, - const edgex_addressable *devaddr, - uint32_t nresults, - const edgex_device_commandrequest *requests, - edgex_device_commandresult *readings -) + ( + void *impl, + const char *devname, + const edgex_protocols *protocols, + uint32_t nreadings, + const edgex_device_commandrequest *requests, + edgex_device_commandresult *readings + ) { grove_pidriver_t *impln = (grove_pidriver_t *) impl; pthread_mutex_lock (&impln->mutex); - edgex_nvpairs *dev_attr = requests[0].devobj->attributes; + const edgex_nvpairs *dev_attr = requests->attributes; assert (dev_attr != NULL); grove_attributes_t *grove_attr = get_groveattributes (dev_attr); bool ret_status = true; @@ -350,15 +405,16 @@ static bool grove_gethandler mraa_gpio_context gpio_dev = (mraa_gpio_context) mraa_devctxt->dev_ctxt; read_value = mraa_gpio_read (gpio_dev); - assert (nresults == 1); + assert (nreadings == 1); if (read_value == -1) { /* error */ - ret_status = false; + iot_log_error (impln->lc, "error in GPIO read"); + ret_status = false; } - /* Grove Button */ - else if (strcmp (requests->devobj->properties->value->type, "Uint8") == 0) + /* Grove Button */ + else if (requests->type == Uint8) { readings->value.ui8_result = (uint8_t) read_value; readings->type = Uint8; @@ -366,6 +422,7 @@ static bool grove_gethandler else { /* No other type support available for GPIO in the profile */ + iot_log_error (impln->lc, "error in GPIO read for request: %d", requests->type); ret_status = false; } } /* GPIO */ @@ -376,16 +433,17 @@ static bool grove_gethandler if (read_value == -1) { /* error */ - ret_status = false; + iot_log_error (impln->lc, "error in AIO read"); + ret_status = false; } else { // get adc bit range int16_t range = (1 << mraa_aio_get_bit (aio_dev)) - 1; - if (strcmp (requests->devobj->name, "SoundIntensity") == 0) + if (strcmp (requests->resname, "SoundIntensity") == 0) { - assert (nresults == 1); - assert (!strcmp (requests->devobj->properties->value->type, "Float32")); + assert (nreadings == 1); + assert (requests->type == Float32); readings->type = Float32; if (grove_attr->normalize) { @@ -396,23 +454,29 @@ static bool grove_gethandler readings->value.f32_result = (float) read_value; } } - else if (strcmp (requests->devobj->name, "LightIntensity") == 0) + else if (strcmp (requests->resname, "LightIntensity") == 0) { - assert (nresults == 1); - assert (!strcmp (requests->devobj->properties->value->type, "Float32")); + assert (nreadings == 1); + assert (requests->type == Float32); /* Ref: https://github.com/intel-iot-devkit/upm/src/light/light.c */ - readings->value.f32_result = (float) (10000.0 / powf ((((float) (range) - read_value) * 10.0 / read_value) * 15.0, 4.0 / 3.0)); + readings->value.f32_result = (float) (10000.0 / + powf ((((float) (range) - read_value) * 10.0 / read_value) * 15.0, + 4.0 / 3.0)); readings->type = Float32; } else { - assert (nresults == 2); /* For RotarySensorMeasurements */ - for (int index = 0; (requests[index].devobj != NULL && index < nresults); index++) + assert (nreadings == 2); /* For RotarySensorMeasurements */ + grove_attributes_t *rotarysensor_attr = NULL; + for (int index = 0; index < nreadings; index++) { readings[index].type = Float32; - if (strcmp ((requests[index].devobj)->name, "RotaryAngle") == 0) + /* Get attribute for each device object to apply scale if applicable */ + rotarysensor_attr = get_groveattributes (requests[index].attributes); + + if (strcmp (requests[index].resname, "RotaryAngle") == 0) { - if (grove_attr->normalize) + if (rotarysensor_attr->normalize) { readings[index].value.f32_result = read_value * (float) GROVE_ROTARY_MAX_ANGLE / range; } @@ -421,9 +485,9 @@ static bool grove_gethandler readings[index].value.f32_result = read_value; } } - else if (strcmp ((requests[index].devobj)->name, "RotaryVoltage") == 0) + else if (strcmp (requests[index].resname, "RotaryVoltage") == 0) { - if (grove_attr->normalize) + if (rotarysensor_attr->normalize) { readings[index].value.f32_result = read_value * (float) GROVE_ADC_REF / range; } @@ -432,13 +496,64 @@ static bool grove_gethandler readings[index].value.f32_result = read_value; } } + free (rotarysensor_attr); } } } } /* AIO */ + else if ((strcmp (grove_attr->pin_type, "I2C") == 0) && (strcmp (grove_attr->type, "BME680") == 0)) + { + bme680_context tph_dev = (bme680_context) mraa_devctxt->dev_ctxt; + struct bme680_field_data read_data; + + ret_status = grove_bme_read_data (tph_dev, &read_data); + if (ret_status == false) + { + iot_log_error (impln->lc, "Unable to read new data from BME680 sensor()"); + } + else + { + assert (nreadings == 3); // Temperature, Pressure and Humidity + grove_attributes_t *bme_attr = NULL; + for (int index = 0; index < nreadings; index++) + { + /* Get attribute for each device object to apply scale if applicable */ + bme_attr = get_groveattributes (requests[index].attributes); + + readings[index].type = Float32; + + if (strcmp (requests[index].resname, "Temperature") == 0) + { + readings[index].value.f32_result = read_data.temperature; + } + else if (strcmp (requests[index].resname, "Pressure") == 0) + { + if (bme_attr->normalize) + { + readings[index].value.f32_result = (float) (read_data.pressure / hPA_FACTOR); + } + else + { + readings[index].value.f32_result = read_data.pressure; + } + } + else if (strcmp (requests[index].resname, "Humidity") == 0) + { + readings[index].value.f32_result = read_data.humidity; + } + else + { + iot_log_error (impln->lc, "Undefined %s device resource for BME680", requests[index].resname); + } + free (bme_attr); + bme_attr = NULL; + } + } + } /* BME680 */ else { /* Only GPIO, AIO and I2C interface types are supported */ + iot_log_error (impln->lc, "Unsupported type, error in grove_gethandler()"); ret_status = false; } } /* dev_ctxt != NULL */ @@ -448,20 +563,21 @@ static bool grove_gethandler } static bool grove_puthandler -( - void *impl, - const edgex_addressable *devaddr, - uint32_t nrequests, - const edgex_device_commandrequest *requests, - const edgex_device_commandresult *readings -) + ( + void *impl, + const char *devname, + const edgex_protocols *protocols, + uint32_t nvalues, + const edgex_device_commandrequest *requests, + const edgex_device_commandresult *readings + ) { mraa_result_t status = MRAA_SUCCESS; grove_pidriver_t *impln = (grove_pidriver_t *) impl; pthread_mutex_lock (&impln->mutex); /* Get the device context */ - edgex_nvpairs *dev_attr = requests[0].devobj->attributes; + const edgex_nvpairs *dev_attr = requests[0].attributes; assert (dev_attr != NULL); grove_attributes_t *grove_attr = get_groveattributes (dev_attr); @@ -472,14 +588,18 @@ static bool grove_puthandler { mraa_gpio_context gpio_dev = (mraa_gpio_context) mraa_devctxt->dev_ctxt; - assert (!strcmp (requests->devobj->properties->value->type, "Bool")); - assert (nrequests == 1); + assert (requests->type == Bool); + assert (nvalues == 1); - status = mraa_gpio_write (gpio_dev, readings[--nrequests].value.bool_result); + status = mraa_gpio_write (gpio_dev, readings[--nvalues].value.bool_result); + if (status != MRAA_SUCCESS) + { + iot_log_error (impln->lc, "gpio write failure = %d\n", status); + } } else if (strcmp (grove_attr->pin_type, "I2C") == 0) { - assert (nrequests == 3); /* for lcd */ + assert (nvalues == 3); /* for lcd */ grove_i2c_dev_ctxt_t *i2c_dev = (grove_i2c_dev_ctxt_t *) (mraa_devctxt->dev_ctxt); mraa_i2c_context mraa_i2cdev = (mraa_i2c_context) (i2c_dev->dev); @@ -488,26 +608,34 @@ static bool grove_puthandler uint8_t row = 0; char *display_string = NULL; - for (int index = 0; (requests[index].devobj != NULL && index < nrequests); index++) + for (int index = 0; index < nvalues; index++) { - struct edgex_deviceobject *devobj = (struct edgex_deviceobject *) requests[index].devobj; - if (strcmp (devobj->name, "Display-String") == 0) + if (strcmp (requests[index].resname, "Display-String") == 0) { - display_string = strdup (readings[index].value.string_result); + display_string = readings[index].value.string_result; } - else if (strcmp (devobj->name, "Row") == 0) + else if (strcmp (requests[index].resname, "Row") == 0) { row = (readings[index]).value.ui8_result; } - else if (strcmp (devobj->name, "Column") == 0) + else if (strcmp (requests[index].resname, "Column") == 0) { column = (readings[index]).value.ui8_result; } } - status |= grove_lcd_set_cursor (mraa_i2cdev, row, column); - status |= grove_lcd_write (mraa_i2cdev, display_string, strlen (display_string)); - - free (display_string); + status = grove_lcd_set_cursor (mraa_i2cdev, row, column); + if (status != MRAA_SUCCESS) + { + iot_log_error (impln->lc, "lcd set_cursor error status = %d", status); + } + else + { + status = grove_lcd_write (mraa_i2cdev, display_string, strlen (display_string)); + if (status != MRAA_SUCCESS) + { + iot_log_error (impln->lc, "lcd write error status = %d", status); + } + } } else { @@ -518,9 +646,9 @@ static bool grove_puthandler free (grove_attr); pthread_mutex_unlock (&impln->mutex); return (status == MRAA_SUCCESS); - } +} -static bool grove_disconnect (void *impl, edgex_addressable *device) +static bool grove_disconnect (void *impl, edgex_protocols *device) { free (impl); return true; @@ -531,14 +659,14 @@ static void grove_stop (void *impl, bool force) grove_pidriver_t *impln = (grove_pidriver_t *) impl; mraa_result_t status = MRAA_SUCCESS; - iot_log_debug (impln->lc, "grove stop call"); + iot_log_debug (impln->lc, "Calling grove_stop()"); /* Release the resources */ for (int index = 0; index < GROVE_NO_PORTS; index++) { if (impln->dev[index] != NULL) { - switch (impln->dev[index]->pin_type) + switch (impln->dev[index]->intf_type) { case GROVE_GPIO: { @@ -554,27 +682,40 @@ static void grove_stop (void *impl, bool force) } case GROVE_I2C: { - grove_i2c_dev_ctxt_t *i2c_dev = (grove_i2c_dev_ctxt_t *) impln->dev[index]->dev_ctxt; - if (i2c_dev->is_lcd) + if (!(strcmp (impln->dev[index]->pin_type, "LCD"))) { - grove_lcd_cleardisplay (i2c_dev->dev); - grove_rgb_backlight_on (i2c_dev->rgb_dev, false); + grove_i2c_dev_ctxt_t *i2c_dev = (grove_i2c_dev_ctxt_t *) impln->dev[index]->dev_ctxt; + if (i2c_dev->is_lcd) + { + grove_lcd_cleardisplay (i2c_dev->dev); + grove_rgb_backlight_on (i2c_dev->rgb_dev, false); - status |= mraa_i2c_stop (i2c_dev->rgb_dev); - i2c_dev->rgb_dev = NULL; - i2c_dev->is_lcd = false; + status |= mraa_i2c_stop (i2c_dev->rgb_dev); + i2c_dev->rgb_dev = NULL; + i2c_dev->is_lcd = false; + } + status |= mraa_i2c_stop (i2c_dev->dev); + } + else if (!(strcmp (impln->dev[index]->pin_type, "BME680"))) + { + bme680_context tph_dev = (bme680_context) impln->dev[index]->dev_ctxt; + bme680_close (tph_dev); + } + else + { + iot_log_error (impln->lc, "grove_stop(), invalid pin_type: %s\n", impln->dev[index]->pin_type); } - status |= mraa_i2c_stop (i2c_dev->dev); break; } - default: + default: { status |= MRAA_ERROR_FEATURE_NOT_IMPLEMENTED; - iot_log_error (impln->lc, "grove_stop(), interface type %d not implemented", impln->dev[index]->pin_type); + iot_log_error (impln->lc, "grove_stop(), interface type %d not implemented", impln->dev[index]->intf_type); break; } } free (impln->dev[index]->pin_number); + free (impln->dev[index]->pin_type); free (impln->dev[index]); impln->dev[index] = NULL; @@ -593,12 +734,13 @@ int main (int argc, char *argv[]) { const char *profile = ""; char *confdir = ""; + char *regURL = NULL; + char *service_name = GROVE_SVC; edgex_error err; - bool useRegistry = false; grove_pidriver_t *implObject = malloc (sizeof (grove_pidriver_t)); memset (implObject, 0, sizeof (grove_pidriver_t)); - implObject->lc = iot_log_default; + sem_init (&grove_sem, 0, 0); int n = 1; while (n < argc) @@ -608,25 +750,30 @@ int main (int argc, char *argv[]) usage (); return 0; } - if (strcmp (argv[n], "-r") == 0 || strcmp (argv[n], "--registry") == 0) + if (strstr (argv[n], "--registry=") != NULL) { - useRegistry = true; + regURL = argv[n] + strlen ("--registry="); n++; continue; } - if (strcmp (argv[n], "-p") == 0 || strcmp (argv[n], "--profile") == 0) + if (strstr (argv[n], "--profile=") != NULL) { - profile = argv[n + 1]; - n += 2; + profile = argv[n] + strlen ("--profile="); + n++; continue; } - if (strcmp (argv[n], "-c") == 0 || strcmp (argv[n], "--confdir") == 0) + if (strstr (argv[n], "--confdir=") != NULL) { - confdir = argv[n + 1]; - n = n + 2; + confdir = argv[n] + strlen ("--confdir="); + n++; + continue; + } + if (strstr (argv[n], "--name=") != NULL) + { + service_name = argv[n] + strlen ("--name="); + n++; continue; } - printf ("Unknown option %s\n", argv[n]); usage (); return 0; @@ -635,36 +782,35 @@ int main (int argc, char *argv[]) err.code = 0; edgex_device_callbacks myImpls = - { - grove_init, - NULL, - grove_gethandler, - grove_puthandler, - grove_disconnect, - grove_stop - }; - - edgex_device_service *grove_service = edgex_device_service_new (GROVE_SVC, "1.0", implObject, myImpls, &err); + { + grove_init, + NULL, + grove_gethandler, + grove_puthandler, + grove_disconnect, + grove_stop + }; + + edgex_device_service *grove_service = edgex_device_service_new (service_name, VERSION, implObject, myImpls, &err); GROVE_ERR_CHECK (err); implObject->svc = grove_service; - edgex_device_service_start (grove_service, useRegistry, NULL, 0, profile, confdir, &err); + err.code = 0; + edgex_device_service_start (grove_service, regURL, profile, confdir, &err); GROVE_ERR_CHECK (err); printf ("\nRunning - press ctrl-c to exit\n"); - signal (SIGTERM, grove_inthandler); signal (SIGINT, grove_inthandler); + signal (SIGTERM, grove_inthandler); - while (!stop) - { - sleep (1); - } + // wait until the service is interrupted + sem_wait (&grove_sem); - edgex_error e; - e.code = 0; - edgex_device_service_stop (grove_service, true, &e); - GROVE_ERR_CHECK (e); + err.code = 0; + edgex_device_service_stop (grove_service, true, &err); + GROVE_ERR_CHECK (err); free (implObject); + sem_destroy (&grove_sem); return 0; }