diff --git a/examples/c++/CMakeLists.txt b/examples/c++/CMakeLists.txt index f89462f5..1dd1daeb 100644 --- a/examples/c++/CMakeLists.txt +++ b/examples/c++/CMakeLists.txt @@ -323,6 +323,7 @@ add_example (dfrec) add_example (sht1x) add_example (ms5803) add_example (ims) +add_example (ecezo) # These are special cases where you specify example binary, source file and module(s) include_directories (${PROJECT_SOURCE_DIR}/src) diff --git a/examples/c++/ecezo.cxx b/examples/c++/ecezo.cxx new file mode 100644 index 00000000..2e17f777 --- /dev/null +++ b/examples/c++/ecezo.cxx @@ -0,0 +1,81 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * 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. + */ + +#include +#include + +#include +#include + +using namespace std; +using namespace upm; + +bool shouldRun = true; + +void sig_handler(int signo) +{ + if (signo == SIGINT) + shouldRun = false; +} + +int main() +{ + signal(SIGINT, sig_handler); + + //! [Interesting] + + // Instantiate a ECEZO sensor on uart 0 at 9600 baud. + upm::ECEZO *sensor = new upm::ECEZO(0, 9600, false); + + // For I2C, assuming the device is configured for address 0x64 on + // I2C bus 0, you could use something like: + // + // upm::ECEZO *sensor = new upm::ECEZO(0, 0x64, true); + + while (shouldRun) + { + // this will take about 1 second to complete + sensor->update(); + + cout << "EC " + << sensor->getEC() + << " uS/cm, TDS " + << sensor->getTDS() + << " mg/L, Salinity " + << sensor->getSalinity() + << " PSS-78, SG " + << sensor->getSG() + << endl; + + upm_delay(5); + } + + //! [Interesting] + + cout << "Exiting..." << endl; + + delete sensor; + + return 0; +} diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index ddbda942..01ec246d 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -140,6 +140,7 @@ add_example (button_intr) add_example (my9221) add_example (ms5803) add_example (ims) +add_example (ecezo) # Custom examples add_custom_example (nmea_gps_i2c-example-c nmea_gps_i2c.c nmea_gps) diff --git a/examples/c/ecezo.c b/examples/c/ecezo.c new file mode 100644 index 00000000..302fdb00 --- /dev/null +++ b/examples/c/ecezo.c @@ -0,0 +1,85 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * 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. + */ + +#include +#include + +#include +#include + +bool shouldRun = true; + +void sig_handler(int signo) +{ + if (signo == SIGINT) + shouldRun = false; +} + +int main() +{ + signal(SIGINT, sig_handler); + + //! [Interesting] + + // Instantiate a ECEZO sensor on uart 0 at 9600 baud. + ecezo_context sensor = ecezo_uart_init(0, 9600); + + // For I2C, assuming the device is configured for address 0x64 on + // I2C bus 0, you could use something like: + // + // ecezo_context sensor = ecezo_i2c_init(0, 0x64); + + if (!sensor) + { + printf("ecezo_init() failed.\n"); + return 1; + } + + while (shouldRun) + { + // this will take about 1 second to complete + if (ecezo_update(sensor)) + { + printf("ecezo_update() failed\n"); + } + else + { + printf("EC %f uS/cm, TDS %f mg/L, Salinity %f PSS-78, SG %f\n", + ecezo_get_ec(sensor), + ecezo_get_tds(sensor), + ecezo_get_salinity(sensor), + ecezo_get_sg(sensor)); + } + + upm_delay(5); + } + + //! [Interesting] + + printf("Exiting\n"); + + ecezo_close(sensor); + + return 0; +} diff --git a/examples/java/CMakeLists.txt b/examples/java/CMakeLists.txt index cd0f2833..9342ecf2 100644 --- a/examples/java/CMakeLists.txt +++ b/examples/java/CMakeLists.txt @@ -157,6 +157,7 @@ add_example(DFRORP_Example dfrorp) add_example(DFREC_Example dfrec) add_example(SHT1X_Example sht1x) add_example(MS5803_Example ms5803) +add_example(ECEZO_Example ecezo) add_example_with_path(Jhd1313m1_lcdSample lcd i2clcd) add_example_with_path(Jhd1313m1Sample lcd i2clcd) diff --git a/examples/java/ECEZO_Example.java b/examples/java/ECEZO_Example.java new file mode 100644 index 00000000..ddc2425b --- /dev/null +++ b/examples/java/ECEZO_Example.java @@ -0,0 +1,60 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * 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. + */ + +import upm_ecezo.ECEZO; + +public class ECEZO_Example +{ + public static void main(String[] args) throws InterruptedException + { +// ! [Interesting] + + // Instantiate a ECEZO sensor on uart 0 at 9600 baud. + ECEZO sensor = new ECEZO(0, 9600, false); + + // For I2C, assuming the device is configured for address 0x64 on + // I2C bus 0, you could use something like: + // + // ECEZO sensor = new ECEZO(0, 0x64, true); + + while (true) + { + // update our values from the sensor + sensor.update(); + + System.out.println("EC " + + sensor.getEC() + + " uS/cm, TDS " + + sensor.getTDS() + + " mg/L, Salinity " + + sensor.getSalinity() + + " PSS-78, SG " + + sensor.getSG()); + + Thread.sleep(5000); + } + +// ! [Interesting] + } +} diff --git a/examples/javascript/ecezo.js b/examples/javascript/ecezo.js new file mode 100644 index 00000000..e3d5b229 --- /dev/null +++ b/examples/javascript/ecezo.js @@ -0,0 +1,59 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * 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. + */ + +var sensorObj = require('jsupm_ecezo'); + +// Instantiate a ECEZO sensor on uart 0 at 9600 baud. +var sensor = new sensorObj.ECEZO(0, 9600, false); + +// For I2C, assuming the device is configured for address 0x64 on +// I2C bus 0, you could use something like: +// +// var sensor = new sensorObj.ECEZO(0, 0x64, true); + +setInterval(function() +{ + // update our values from the sensor + sensor.update(); + + console.log("EC " + + sensor.getEC() + + " uS/cm, TDS " + + sensor.getTDS() + + " mg/L, Salinity " + + sensor.getSalinity() + + " PSS-78, SG " + + sensor.getSG()); + +}, 5000); + +// exit on ^C +process.on('SIGINT', function() +{ + sensor = null; + sensorObj.cleanUp(); + sensorObj = null; + console.log("Exiting."); + process.exit(0); +}); diff --git a/examples/python/ecezo.py b/examples/python/ecezo.py new file mode 100755 index 00000000..e3a68df5 --- /dev/null +++ b/examples/python/ecezo.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +# Author: Jon Trulson +# Copyright (c) 2016 Intel Corporation. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# 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. + +from __future__ import print_function +import time, sys, signal, atexit +from upm import pyupm_ecezo as sensorObj + +def main(): + # Instantiate a ECEZO sensor on uart 0 at 9600 baud. + sensor = sensorObj.ECEZO(0, 9600, False); + + # For I2C, assuming the device is configured for address 0x64 on + # I2C bus 0, you could use something like: + # + # sensor = sensorObj.ECEZO(0, 0x64, True); + + ## Exit handlers ## + # This function stops python from printing a stacktrace when you + # hit control-C + def SIGINTHandler(signum, frame): + raise SystemExit + + # This function lets you run code on exit + def exitHandler(): + print("Exiting") + sys.exit(0) + + # Register exit handlers + atexit.register(exitHandler) + signal.signal(signal.SIGINT, SIGINTHandler) + + while (1): + sensor.update() + + print("EC " + + str(sensor.getEC()) + + " uS/cm, TDS " + + str(sensor.getTDS()) + + " mg/L, Salinity " + + str(sensor.getSalinity()) + + " PSS-78, SG " + + str(sensor.getSG())); + time.sleep(5) + +if __name__ == '__main__': + main() diff --git a/src/ecezo/CMakeLists.txt b/src/ecezo/CMakeLists.txt new file mode 100644 index 00000000..1fbd8783 --- /dev/null +++ b/src/ecezo/CMakeLists.txt @@ -0,0 +1,9 @@ +upm_mixed_module_init (NAME ecezo + DESCRIPTION "EC EZO Atlas Scientific EC circuit" + C_HDR ecezo.h ecezo_defs.h + C_SRC ecezo.c + CPP_HDR ecezo.hpp + CPP_SRC ecezo.cxx + FTI_SRC ecezo_fti.c + CPP_WRAPS_C + REQUIRES mraa) diff --git a/src/ecezo/ecezo.c b/src/ecezo/ecezo.c new file mode 100644 index 00000000..1da72cd3 --- /dev/null +++ b/src/ecezo/ecezo.c @@ -0,0 +1,666 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * 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. + */ + +#include +#include +#include +#include + +#include + +#include "ecezo.h" + +// "Typical" command completion delay in ms +#define CMD_DELAY (350) + +// uncomment for dubugging +//#define ECEZO_DEBUG (1) + +// I2C read helper +static int readBytes(const ecezo_context dev, uint8_t *buffer, int len) +{ + assert(dev != NULL); + assert(dev->i2c != NULL); + + bool done = false; + int rv; + int retries = 10; + + while (!done && (retries-- > 0)) + { + if ((rv = mraa_i2c_read(dev->i2c, buffer, len)) < 0) + { + printf("%s: mraa_i2c_read(code) failed.\n", __FUNCTION__); + return rv; + } + +#if defined(ECEZO_DEBUG) + printf("CODE: %02x\n", buffer[0]); +#endif + + if (buffer[0] == 0xff || buffer[0] == 0x02) + { + // no data available, or error + return -1; + } + else if (buffer[0] == 0x01) + { + // data is ready + done = true; + + // now we need to move the data one byte down so the rest + // of this driver can work as-is. + memmove(buffer, (buffer + 1), len - 1); + } + else + { + // buffer[0] 0xfe - data is pending. wait and loop again. + upm_delay_ms(CMD_DELAY); + } + } + + if (retries <= 0) + { + printf("%s: timed out waiting for correct response.\n", __FUNCTION__); + return -1; + } + +#if defined(ECEZO_DEBUG) + printf("%s: Got %d bytes\n", __FUNCTION__, rv); + + for (int i=0; iuart) + { + if (ecezo_send_command(dev, "Response,0", NULL, 0) < 0) + error = true; + } + + // turn off continuous sampling + if (ecezo_set_continuous(dev, false)) + error = true; + + // make sure all parameters are enabled + if (ecezo_send_command(dev, "O,EC,1", NULL, 0) < 0) + error = true; + if (ecezo_send_command(dev, "O,TDS,1", NULL, 0) < 0) + error = true; + if (ecezo_send_command(dev, "O,S,1", NULL, 0) < 0) + error = true; + if (ecezo_send_command(dev, "O,SG,1", NULL, 0) < 0) + error = true; + + if (error) + return UPM_ERROR_OPERATION_FAILED; + else + return UPM_SUCCESS; +} + +static upm_result_t decode_report(const ecezo_context dev, char *data) +{ + assert(dev != NULL); + + char *startptr = data; + char *endptr = NULL; + float val; + + // the format of the data string should be: ec,tds,s,sg + + // ec + val = strtof(startptr, &endptr); + + if (startptr == endptr) + { + // error + return UPM_ERROR_OPERATION_FAILED; + } + + dev->ec = val; + startptr = endptr + 1; + + // tds + val = strtof(startptr, &endptr); + + // error + if (startptr == endptr) + return UPM_ERROR_OPERATION_FAILED; + + dev->tds = val; + startptr = endptr + 1; + + // salinity + val = strtof(startptr, &endptr); + + // error + if (startptr == endptr) + return UPM_ERROR_OPERATION_FAILED; + + dev->salinity = val; + startptr = endptr + 1; + + // sg + val = strtof(startptr, &endptr); + + if (startptr == endptr) + return UPM_ERROR_OPERATION_FAILED; + + dev->sg = val; + + return UPM_SUCCESS; +} + +static bool ecezo_data_available(const ecezo_context dev, unsigned int millis) +{ + assert(dev != NULL); + + // i2c, we don't support this + if (dev->i2c) + { + return false; + } + + // uart + if (mraa_uart_data_available(dev->uart, millis)) + return true; + else + return false; +} + +// uart init +ecezo_context ecezo_uart_init(unsigned int uart, unsigned int baudrate) +{ + ecezo_context dev = + (ecezo_context)malloc(sizeof(struct _ecezo_context)); + + if (!dev) + return NULL; + + // zero out context + memset((void *)dev, 0, sizeof(struct _ecezo_context)); + + // initialize the MRAA contexts + + // uart, default should be 8N1 + if (!(dev->uart = mraa_uart_init(uart))) + { + printf("%s: mraa_uart_init() failed.\n", __FUNCTION__); + ecezo_close(dev); + return NULL; + } + + if (mraa_uart_set_baudrate(dev->uart, baudrate)) + { + printf("%s: mraa_uart_set_baudrate() failed.\n", __FUNCTION__); + ecezo_close(dev); + return NULL; + } + + mraa_uart_set_flowcontrol(dev->uart, false, false); + + if (generic_init(dev)) + { + printf("%s: generic_init() failed.\n", __FUNCTION__); + ecezo_close(dev); + return NULL; + } + + return dev; +} + +// i2c ublox init +ecezo_context ecezo_i2c_init(unsigned int bus, uint8_t addr) +{ + // make sure MRAA is initialized + int mraa_rv; + if ((mraa_rv = mraa_init()) != MRAA_SUCCESS) + { + printf("%s: mraa_init() failed (%d).\n", __FUNCTION__, mraa_rv); + return NULL; + } + + ecezo_context dev = + (ecezo_context)malloc(sizeof(struct _ecezo_context)); + + if (!dev) + return NULL; + + // zero out context + memset((void *)dev, 0, sizeof(struct _ecezo_context)); + + // initialize the MRAA contexts + + if (!(dev->i2c = mraa_i2c_init(bus))) + { + printf("%s: mraa_i2c_init() failed.\n", __FUNCTION__); + ecezo_close(dev); + return NULL; + } + + if (mraa_i2c_address(dev->i2c, addr)) + { + printf("%s: mraa_i2c_address() failed.\n", __FUNCTION__); + ecezo_close(dev); + return NULL; + } + + if (generic_init(dev)) + { + printf("%s: generic_init() failed.\n", __FUNCTION__); + ecezo_close(dev); + return NULL; + } + + return dev; +} + +void ecezo_close(ecezo_context dev) +{ + assert(dev != NULL); + + if (dev->uart) + mraa_uart_stop(dev->uart); + if (dev->i2c) + mraa_i2c_stop(dev->i2c); + + free(dev); +} + +upm_result_t ecezo_set_continuous(const ecezo_context dev, bool enable) +{ + int rv; + + if (enable) + rv = ecezo_send_command(dev, "C,1", NULL, 0); + else + rv = ecezo_send_command(dev, "C,0", NULL, 0); + + return ((rv < 0) ? UPM_ERROR_OPERATION_FAILED : UPM_SUCCESS); +} + +upm_result_t ecezo_set_temperature(const ecezo_context dev, float temp) +{ + char buffer[ECEZO_MAX_BUFFER_LEN]; + + snprintf(buffer, ECEZO_MAX_BUFFER_LEN, "T,%f", temp); + + int rv = ecezo_send_command(dev, buffer, NULL, 0); + + return ((rv < 0) ? UPM_ERROR_OPERATION_FAILED : UPM_SUCCESS); +} + +upm_result_t ecezo_set_led(const ecezo_context dev, bool enable) +{ + int rv; + + if (enable) + rv = ecezo_send_command(dev, "L,1", NULL, 0); + else + rv = ecezo_send_command(dev, "L,0", NULL, 0); + + return ((rv < 0) ? UPM_ERROR_OPERATION_FAILED : UPM_SUCCESS); +} + +upm_result_t ecezo_set_k_value(const ecezo_context dev, float k) +{ + char buffer[ECEZO_MAX_BUFFER_LEN]; + + // the K value must be between 0.1 and 10.0 + if (k < 0.1 || k > 10.0) + { + printf("%s: K value must be between 0.1 and 10.0\n", __FUNCTION__); + return UPM_ERROR_OUT_OF_RANGE; + } + + snprintf(buffer, ECEZO_MAX_BUFFER_LEN, "K,%f", k); + + int rv = ecezo_send_command(dev, buffer, NULL, 0); + + return ((rv < 0) ? UPM_ERROR_OPERATION_FAILED : UPM_SUCCESS); +} + +upm_result_t ecezo_set_sleep(const ecezo_context dev, bool enable) +{ + int rv = 0; + + if (enable) + rv = ecezo_send_command(dev, "SLEEP", NULL, 0); + else + { + // "WAKE" isn't a real command, but should wake the device up. + // We ignore the return value, as it will likely be an error + // anyway. + ecezo_send_command(dev, "WAKE", NULL, 0); + } + + return ((rv < 0) ? UPM_ERROR_OPERATION_FAILED : UPM_SUCCESS); +} + +int ecezo_read(const ecezo_context dev, char *buffer, size_t len) +{ + assert(dev != NULL); + + upm_delay_ms(CMD_DELAY); // delay CMD_DELAY ms to make sure cmd completed + + // i2c + if (dev->i2c) + { + return readBytes(dev, (uint8_t *)buffer, len); + } + else + { + // UART + int bytesRead = 0; + + while(bytesRead < len) + { + // we read one byte at a time, exiting when either len is + // reached, or a '\r' is found indicating the end of a + // sentence. Most commands (except 'R') require a minimum + // of 300ms to execute, so we wait up to CMD_DELAY ms after all + // data (if any) is read. + if (ecezo_data_available(dev, CMD_DELAY)) + { + int br = mraa_uart_read(dev->uart, &buffer[bytesRead], 1); + + if (br <= 0) + return br; + + if (buffer[bytesRead] == '\r') + { + // if we found a CR, replace it with a 0 byte + buffer[bytesRead++] = 0; + return bytesRead; + } + bytesRead++; + } + else + { + // timed out - ok with responses disabled + return 0; + } + } + } + + // anything else is an error + return -1; +} + +upm_result_t ecezo_write(const ecezo_context dev, char *buffer, size_t len) +{ + assert(dev != NULL); + + if (dev->uart) + { + if (mraa_uart_write(dev->uart, buffer, len) != len) + { + printf("%s: mraa_uart_write() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + } + else + { + // I2C + if (mraa_i2c_write(dev->i2c, (uint8_t *)buffer, len)) + { + printf("%s: mraa_i2c_write() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + } + + return UPM_SUCCESS; +} + +int ecezo_send_command(const ecezo_context dev, char *cmd, char *buffer, + int len) +{ + assert(dev != NULL); + + if (!cmd) + return -1; + + // Our local buffer in case one isn't supplied + char localBuffer[ECEZO_MAX_BUFFER_LEN]; + + // our read buffer ptr + char *readBuffer = NULL; + + if (!buffer || !len) + { + readBuffer = localBuffer; + len = ECEZO_MAX_BUFFER_LEN; + } + else + { + readBuffer = buffer; + } + +#if defined(ECEZO_DEBUG) + printf("Command: %s\n", cmd); +#endif // ECEZO_DEBUG + + // our write buffer + char writeBuffer[ECEZO_MAX_BUFFER_LEN]; + + strncpy(writeBuffer, cmd, ECEZO_MAX_BUFFER_LEN); + writeBuffer[ECEZO_MAX_BUFFER_LEN - 1] = 0; + + int writelen = strlen(writeBuffer); + + if (dev->uart) + { + if (strlen(writeBuffer) >= ECEZO_MAX_BUFFER_LEN - 2) + { + // too big. Should never happen in real life. + printf("%s: cmd writeBuffer too big.\n", __FUNCTION__); + return -1; + } + + strcat(writeBuffer, "\r"); + } + + // for the uart this will now include the added CR, for I2C, this + // will now include the already existing \0 terminator. + writelen++; + + // Let the games begin... + int retries = 10; + + while (retries-- > 0) + { + if (ecezo_write(dev, writeBuffer, writelen)) + { + printf("%s: ecezo_write() failed\n", __FUNCTION__); + return -1; + } + + // we wait up to CMD_DELAY ms for an error response, which should be + // more than enough time. No response is also ok, since we + // disable the "*OK" response in the init. + + memset((void *)readBuffer, 0, len); + int bytesRead = 0; + if ((bytesRead = ecezo_read(dev, readBuffer, len)) < 0) + { + return -1; + } + + // for I2C, we are done at this point + if (dev->i2c) + break; + + // for UART, we need some more checks + if (bytesRead && strstr(readBuffer, "*ER")) + { + // need to retry the command +#if defined(ECEZO_DEBUG) + printf("%s: *ER DETECTED, retry\n", __FUNCTION__); +#endif // ECEZO_DEBUG + continue; + } + else if (bytesRead && strchr(readBuffer, '*')) + { + // Some other diagnostic code, output it. +#if defined(ECEZO_DEBUG) + printf("%s: * diagnostic code detected (%s), retry\n", + __FUNCTION__, buffer); +#endif // ECEZO_DEBUG + continue; + } + else + { + // we are done here +#if defined(ECEZO_DEBUG) + printf("%s: bytesRead = %d\n", __FUNCTION__, bytesRead); +#endif // ECEZO_DEBUG + + break; + } + } + + if (retries <= 0) + { + printf("%s: read timed out and/or and retries exhausted\n", + __FUNCTION__); + return 0; + } + + return len; +} + +upm_result_t ecezo_update(const ecezo_context dev) +{ + assert(dev != NULL); + + // first we send a 'R' command to get a reading (takes a minimum + // of 1 second), then we parse out the string values into the + // context variables. + + char buffer[ECEZO_MAX_BUFFER_LEN]; + + // first issue the report command + int rv = ecezo_send_command(dev, "R", buffer, ECEZO_MAX_BUFFER_LEN); + if (rv == 0) + { + printf("%s: timed out waiting for data\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + else if (rv < 0) + { + printf("%s: error retrieving data\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + // decode + if (decode_report(dev, buffer)) + { + printf("%s: decode_report() failed\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +float ecezo_get_ec(const ecezo_context dev) +{ + assert(dev != NULL); + + return dev->ec; +} + +float ecezo_get_tds(const ecezo_context dev) +{ + assert(dev != NULL); + + return dev->tds; +} + +float ecezo_get_salinity(const ecezo_context dev) +{ + assert(dev != NULL); + + return dev->salinity; +} + +float ecezo_get_sg(const ecezo_context dev) +{ + assert(dev != NULL); + + return dev->sg; +} + +upm_result_t ecezo_calibrate(const ecezo_context dev, ECEZO_CALIBRATION_T cal, + float ec) +{ + assert(dev != NULL); + + char cmdBuffer[ECEZO_MAX_BUFFER_LEN]; + + switch(cal) + { + case ECEZO_CALIBRATE_CLEAR: + snprintf(cmdBuffer, ECEZO_MAX_BUFFER_LEN, "cal,clear"); + break; + + case ECEZO_CALIBRATE_DRY: + snprintf(cmdBuffer, ECEZO_MAX_BUFFER_LEN, "cal,dry"); + break; + + case ECEZO_CALIBRATE_ONE: + snprintf(cmdBuffer, ECEZO_MAX_BUFFER_LEN, "cal,one,%f", ec); + break; + + case ECEZO_CALIBRATE_LOW: + snprintf(cmdBuffer, ECEZO_MAX_BUFFER_LEN, "cal,low,%f", ec); + break; + + case ECEZO_CALIBRATE_HIGH: + snprintf(cmdBuffer, ECEZO_MAX_BUFFER_LEN, "cal,high,%f", ec); + break; + + default: + // should be able to happen + printf("%s: invalid cal parameter\n", __FUNCTION__); + return UPM_ERROR_INVALID_PARAMETER; + } + + return ecezo_send_command(dev, cmdBuffer, NULL, 0); +} diff --git a/src/ecezo/ecezo.cxx b/src/ecezo/ecezo.cxx new file mode 100644 index 00000000..1bbcd815 --- /dev/null +++ b/src/ecezo/ecezo.cxx @@ -0,0 +1,146 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * 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. + */ + +#include +#include + +#include "ecezo.hpp" + +using namespace upm; +using namespace std; + +ECEZO::ECEZO(unsigned int bus, unsigned int addrBaud, bool isI2C) : + m_ecezo(nullptr) +{ + if (isI2C) + m_ecezo = ecezo_i2c_init(bus, addrBaud); + else + m_ecezo = ecezo_uart_init(bus, addrBaud); + + if (!m_ecezo) + throw std::runtime_error(string(__FUNCTION__) + + ": ecezo_*_init() failed"); +} + +ECEZO::~ECEZO() +{ + ecezo_close(m_ecezo); +} + +void ECEZO::update() +{ + if (ecezo_update(m_ecezo)) + throw std::runtime_error(string(__FUNCTION__) + + ": ecezo_update() failed"); +} + +void ECEZO::setTemperature(float temp) +{ + if (ecezo_set_temperature(m_ecezo, temp)) + throw std::runtime_error(string(__FUNCTION__) + + ": ecezo_set_temperature() failed"); +} + +void ECEZO::setKValue(float k) +{ + if (ecezo_set_k_value(m_ecezo, k)) + throw std::runtime_error(string(__FUNCTION__) + + ": ecezo_set_k_value() failed"); +} + +void ECEZO::setSleep(bool enable) +{ + if (ecezo_set_sleep(m_ecezo, enable)) + throw std::runtime_error(string(__FUNCTION__) + + ": ecezo_set_sleep() failed"); +} + +float ECEZO::getEC() +{ + return ecezo_get_ec(m_ecezo); +} + +float ECEZO::getTDS() +{ + return ecezo_get_tds(m_ecezo); +} + +float ECEZO::getSalinity() +{ + return ecezo_get_salinity(m_ecezo); +} + +float ECEZO::getSG() +{ + return ecezo_get_sg(m_ecezo); +} + +void ECEZO::calibrate(ECEZO_CALIBRATION_T cal, float ec) +{ + if (ecezo_calibrate(m_ecezo, cal, ec)) + throw std::runtime_error(string(__FUNCTION__) + + ": ecezo_calibrate() failed"); +} + +void ECEZO::setContinuous(bool enable) +{ + if (ecezo_set_continuous(m_ecezo, enable)) + throw std::runtime_error(string(__FUNCTION__) + + ": ecezo_set_continuous() failed"); +} + +string ECEZO::sendCommand(string cmd) +{ + char buffer[ECEZO_MAX_BUFFER_LEN]; + + int rv; + + if ((rv = ecezo_send_command(m_ecezo, (char *)cmd.c_str(), + buffer, ECEZO_MAX_BUFFER_LEN)) < 0) + throw std::runtime_error(string(__FUNCTION__) + + ": ecezo_send_command() failed"); + + return string(buffer, rv); +} + +string ECEZO::read() +{ + char buffer[ECEZO_MAX_BUFFER_LEN]; + + int rv; + + if ((rv = ecezo_read(m_ecezo, buffer, ECEZO_MAX_BUFFER_LEN)) < 0) + throw std::runtime_error(string(__FUNCTION__) + + ": ecezo_read() failed"); + + return string(buffer, rv); +} + +void ECEZO::write(std::string data) +{ + if (ecezo_write(m_ecezo, (char*)data.data(), + data.size())) + throw std::runtime_error(string(__FUNCTION__) + + ": ecezo_write() failed"); +} diff --git a/src/ecezo/ecezo.h b/src/ecezo/ecezo.h new file mode 100644 index 00000000..a6a738a8 --- /dev/null +++ b/src/ecezo/ecezo.h @@ -0,0 +1,267 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include "ecezo_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @file ecezo.h + * @library ecezo + * @brief C API for the EC-EZO EC Sensor + * + * @include ecezo.c + */ + + /** + * Device context + */ + typedef struct _ecezo_context { + mraa_uart_context uart; + mraa_i2c_context i2c; + + // our values + float ec; // electrical conductivity + float tds; // total dissolved solids + float salinity; + float sg; // specific gravity + + } *ecezo_context; + + /** + * ECEZO Initializer for UART operation + * + * @param uart Specify which uart to use. + * @param baudrate Specify the baudrate to use. The device defaults + * to 9600 baud, though the datasheet implies the default is 38400. + * @return an initialized device context on success, NULL on error. + */ + ecezo_context ecezo_uart_init(unsigned int uart, unsigned int baudrate); + + /** + * ECEZO Initializer for I2C operation + * + * @param bus Specify which the I2C bus to use. + * @param addr Specify the I2C address to use. This is + * configurable on the device, so there is no default. + * @return an initialized device context on success, NULL on error. + */ + ecezo_context ecezo_i2c_init(unsigned int bus, uint8_t addr); + + /** + * ECEZO sensor close function + */ + void ecezo_close(ecezo_context dev); + + /** + * Query the device for a reading, parse the response, and store + * the read values into the device context. This function must be + * called prior to calling any function that returns the data, + * like ecezo_get_ec(). + * + * @param dev Device context + * @return UPM result + */ + upm_result_t ecezo_update(const ecezo_context dev); + + /** + * For accurate readings, the temperature of the liquid being + * measured should be known. This function allows you to specify + * the liquid's temperature (in Celsius) so that proper + * compensation can take place. How you measure this temperature + * is up to you. By default, the device will assume a temperature + * of 25C. + * + * @param dev Device context + * @param temp The temperature of the liquid being measured + * @return UPM result + */ + upm_result_t ecezo_set_temperature(const ecezo_context dev, float temp); + + /** + * Set the K value of the probe being used. By default, this is + * 1.0. Valid values are between 0.1 and 10.0. + * + * @param dev Device context + * @param k The K value of the probe + * @return UPM result + */ + upm_result_t ecezo_set_k_value(const ecezo_context dev, float k); + + /** + * Enable or disable Sleep mode. + * + * @param dev Device context + * @param enable True to enable sleep mode, false to wake up + * @return UPM result + */ + upm_result_t ecezo_set_sleep(const ecezo_context dev, bool enable); + + /** + * Retrieve the last measured Electrical Conductivity (EC) value + * in microsiemens. ecezo_update() must have been called before + * calling this function. + * + * @param dev Device context + * @return EC value in microsiemens + */ + float ecezo_get_ec(const ecezo_context dev); + + /** + * Retrieve the last measured Total Dissolved solids (TDS) value. + * ecezo_update() must have been called before calling this + * function. + * + * @param dev Device context + * @return TDS value + */ + float ecezo_get_tds(const ecezo_context dev); + + /** + * Retrieve the last measured Salinity value. ecezo_update() must + * have been called before calling this function. + * + * @param dev Device context + * @return Salinity value + */ + float ecezo_get_salinity(const ecezo_context dev); + + /** + * Retrieve the last measured Specific Gravity (SG) value. + * ecezo_update() must have been called before calling this + * function. + * + * @param dev Device context + * @return SG value + */ + float ecezo_get_sg(const ecezo_context dev); + + /** + * Specify calibration data for calibrating the device. See the + * datasheet for details on how calibration is performed. This + * function provides a mechanism for clearing out, and setting + * calibration data. + * + * A simple one point calibration might work as follows: + * + * 1. CLEAR the calibration data + * 2. with a dry probe, set the DRY point. + * 3. with the probe immersed in a standardized solution, set the + * ONE parameter to the solution's known EC value in microsiemens. + * + * A two point calibration might work as follows: + * + * 1. CLEAR the calibration data + * 2. with a dry probe, set the DRY point. + * 3. with the probe immersed in the lowest EC standardized + * solution, set the LOW parameter to the solution's known EC + * value in microsiemens. + * 4. with the probe immersed in the highest EC standardized + * solution, set the HIGH parameter to the solution's known EC + * value in microsiemens. + * + * @param dev Device context + * @param cal One of the ECEZO_CALIBRATION_T values + * @param ec The EC value of the calibration fluid. This + * parameter is ignored when cal is either ECEZO_CALIBRATE_CLEAR + * or ECEZO_CALIBRATE_DRY. + * @return UPM result + */ + upm_result_t ecezo_calibrate(const ecezo_context dev, + ECEZO_CALIBRATION_T cal, + float ec); + + /** + * Enable or disable "continuous" operation. In continuous + * operation, the device will sample and emit readings every + * second. The driver disables this mode by default. If you wish + * to use continuous mode, you will be responsible for reading and + * parsing the returned data yourself. + * + * The functionality of this driver depends on continuous mode + * being disabled. When disabled, the driver will manually + * request a reading when desired via ecezo_update(). + * + * @param dev Device context + * @param enable true to enable continuous mode, false to disable. + * @return UPM result + */ + upm_result_t ecezo_set_continuous(const ecezo_context dev, bool enable); + + /** + * Directly send a command to the device and optionally get a + * response. This is a low level function and should not be + * called unless you know what you are doing. + * + * @param dev Device context + * @param cmd command to send to the device. See the datasheet + * for valid commands. + * @param buffer Optional buffer in which to return any data. + * NULL if you are not interested in any returned data. + * @param len Length of the buffer, or 0 if you are not interested + * in returned data + * @return Number of characters read back, 0 if a timeout or no + * data, -1 if an error + */ + int ecezo_send_command(const ecezo_context dev, char *cmd, + char *buffer, int len); + + /** + * Read character data from the device. This is a low level + * function and should not be called unless you know what you are + * doing. + * + * @param dev sensor context + * @param buffer The character buffer to read data into. + * @param len The maximum size of the buffer + * @return The number of bytes successfully read, or -1 on error + */ + int ecezo_read(const ecezo_context dev, char *buffer, size_t len); + + /** + * Write character data to the device. This is a low level + * function and should not be called unless you know what you are + * doing. + * + * @param dev sensor context + * @param buffer The character buffer containing data to write. + * @param len The number of bytes to write. + * @return The number of bytes successfully written, or -1 on error. + */ + upm_result_t ecezo_write(const ecezo_context dev, char *buffer, size_t len); + + +#ifdef __cplusplus +} +#endif diff --git a/src/ecezo/ecezo.hpp b/src/ecezo/ecezo.hpp new file mode 100644 index 00000000..1750b3b3 --- /dev/null +++ b/src/ecezo/ecezo.hpp @@ -0,0 +1,238 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * 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. + */ +#pragma once + +#include +#include + +#include +#include + +#include "ecezo.h" + +namespace upm { + /** + * @brief API for the EC-EZO EC Sensor + * @defgroup ecezo libupm-ecezo + * @ingroup uart i2c liquid + */ + + /** + * @library ecezo + * @sensor ecezo + * @comname EC-EZO EC Sensor + * @type liquid + * @man sparkfun + * @con uart i2c + * @web https://www.sparkfun.com/products/12908 + * + * @brief API for the EC-EZO EC Sensor + * + * This driver was tested with the Atlas Scientific Electrical + * Conductivity kit. + * + * This device can operate in either UART or I2C modes. + * + * @snippet ecezo.cxx Interesting + */ + + class ECEZO { + public: + + /** + * ECEZO object constructor. This can use either UART + * (default) or I2C communications. For UART, specify the + * uart number as the bus parameter, the baudrate as the + * addrBaud parameter, and false for the isI2C parameter. + * + * For I2C, specify the bus parameter, the I2C address as the + * addrBaud parameter, and true for the isI2C parameter. + * + * @param bus Specify which uart or I2C bus to use + * @param addrBaud Specify the baudrate if using UART, or the + * I2C address of the device if using I2C. + * @param isI2C true if using I2C, false if using a UART + */ + ECEZO(unsigned int bus=0, unsigned int addrBaud=9600, + bool isI2C=false); + + /** + * ECEZO object destructor + */ + ~ECEZO(); + + /** + * Query the device for a reading, parse the response, and store + * the read values into the device context. This function must be + * called prior to calling any function that returns the data, + * like getEC(). + */ + void update(); + + /** + * For accurate readings, the temperature of the liquid being + * measured should be known. This function allows you to specify + * the liquid's temperature (in Celsius) so that proper + * compensation can take place. How you measure this temperature + * is up to you. By default, the device will assume a temperature + * of 25C. + * + * @param temp The temperature of the liquid being measured + * @return UPM result + */ + void setTemperature(float temp); + + /** + * Set the K value of the probe being used. By default, this is + * 1.0. Valid values are between 0.1 and 10.0. + * + * @param k The K value of the probe + */ + void setKValue(float k); + + /** + * Enable or disable Sleep mode. + * + * @param enable True to enable sleep mode, false to wake up + */ + void setSleep(bool enable); + + /** + * Retrieve the last measured Electrical Conductivity (EC) + * value in microsiemens. update() must have been called + * before calling this function. + * + * @return EC value in microsiemens + */ + float getEC(); + + /** + * Retrieve the last measured Total Dissolved solids (TDS) value. + * update() must have been called before calling this + * function. + * + * @return TDS value + */ + float getTDS(); + + /** + * Retrieve the last measured Salinity value. update() must + * have been called before calling this function. + * + * @return Salinity value + */ + float getSalinity(); + + /** + * Retrieve the last measured Specific Gravity (SG) value. + * update() must have been called before calling this + * function. + * + * @return SG value + */ + float getSG(); + + /** + * Specify calibration data for calibrating the device. See the + * datasheet for details on how calibration is performed. This + * function provides a mechanism for clearing out, and setting + * calibration data. + * + * A simple one point calibration might work as follows: + * + * 1. CLEAR the calibration data + * 2. with a dry probe, set the DRY point. + * 3. with the probe immersed in a standardized solution, set the + * ONE parameter to the solution's known EC value in microsiemens. + * + * A two point calibration might work as follows: + * + * 1. CLEAR the calibration data + * 2. with a dry probe, set the DRY point. + * 3. with the probe immersed in the lowest EC standardized + * solution, set the LOW parameter to the solution's known EC + * value in microsiemens. + * 4. with the probe immersed in the highest EC standardized + * solution, set the HIGH parameter to the solution's known EC + * value in microsiemens. + * + * @param cal One of the ECEZO_CALIBRATION_T values + * @param ec The EC value of the calibration fluid. This + * parameter is ignored when cal is either ECEZO_CALIBRATE_CLEAR + * or ECEZO_CALIBRATE_DRY. + */ + void calibrate(ECEZO_CALIBRATION_T cal, float ec); + + + protected: + // ecezo device context + ecezo_context m_ecezo; + + /** + * Enable or disable "continuous" operation. In continuous + * operation, the device will sample and emit readings every + * second. The driver disables this mode by default. If you wish + * to use continuous mode, you will be responsible for reading and + * parsing the returned data yourself. + * + * The functionality of this driver depends on continuous mode + * being disabled. When disabled, the driver will manually + * request a reading when desired via ecezo_update(). + * + * @param enable true to enable continuous mode, false to disable. + */ + void setContinuous(bool enable); + + /** + * Directly send a command to the device and optionally get a + * response. This is a low level function and should not be + * called unless you know what you are doing. + * + * @param cmd a String containing the command to send to the + * device. See the datasheet for valid commands. + * @return The string response, if any + */ + std::string sendCommand(std::string cmd); + + /** + * Read character data from the device. This is a low level + * function and should not be called unless you know what you are + * doing. + * + * @return A string containing the data read back, if any + */ + std::string read(); + + /** + * Write character data to the device. This is a low level + * function and should not be called unless you know what you are + * doing. + * + * @param data The string containing data to write. + */ + void write(std::string data); + + private: + }; +} diff --git a/src/ecezo/ecezo_defs.h b/src/ecezo/ecezo_defs.h new file mode 100644 index 00000000..4773644c --- /dev/null +++ b/src/ecezo/ecezo_defs.h @@ -0,0 +1,45 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * 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. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +// our maximum buffer size +#define ECEZO_MAX_BUFFER_LEN (64) + + // calibration commands + typedef enum { + ECEZO_CALIBRATE_CLEAR = 0, // clear calibration + ECEZO_CALIBRATE_DRY, // cal dry point + ECEZO_CALIBRATE_ONE, // single point cal EC value + ECEZO_CALIBRATE_LOW, // 2-point cal, LOW EC value + ECEZO_CALIBRATE_HIGH // 2-point cal, HIGH EC value + } ECEZO_CALIBRATION_T; + +#ifdef __cplusplus +} +#endif diff --git a/src/ecezo/ecezo_fti.c b/src/ecezo/ecezo_fti.c new file mode 100644 index 00000000..8038015a --- /dev/null +++ b/src/ecezo/ecezo_fti.c @@ -0,0 +1,88 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * 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. + */ + +#include "ecezo.h" +#include "upm_fti.h" + +/** + * This file implements the Function Table Interface (FTI) for this sensor + */ + +const char upm_ecezo_name[] = "ECEZO"; +const char upm_ecezo_description[] = "EC-EZO EC sensor"; +const upm_protocol_t upm_ecezo_protocol[] = {UPM_UART, UPM_I2C}; +const upm_sensor_t upm_ecezo_category[] = {UPM_EC}; + +// forward declarations +const void* upm_ecezo_get_ft(upm_sensor_t sensor_type); +void* upm_ecezo_init_name(); +void upm_ecezo_close(void *dev); +upm_result_t upm_ecezo_get_value(const void *dev, float *value); + +static const upm_sensor_ft ft = +{ + .upm_sensor_init_name = &upm_ecezo_init_name, + .upm_sensor_close = &upm_ecezo_close, +}; + +static const upm_ec_ft ecft = +{ + .upm_ec_get_value = upm_ecezo_get_value +}; + +const void* upm_ecezo_get_ft(upm_sensor_t sensor_type) +{ + switch(sensor_type) + { + case UPM_SENSOR: + return &ft; + + case UPM_EC: + return &ecft; + + default: + return NULL; + } +} + +void* upm_ecezo_init_name() +{ + return NULL; +} + +void upm_ecezo_close(void *dev) +{ + ecezo_close((ecezo_context)dev); +} + +upm_result_t upm_ecezo_get_value(const void *dev, float *value) +{ + upm_result_t rv; + + if ((rv = ecezo_update((ecezo_context)dev))) + return rv; + + *value = ecezo_get_ec((ecezo_context)dev); + return UPM_SUCCESS; +} diff --git a/src/ecezo/javaupm_ecezo.i b/src/ecezo/javaupm_ecezo.i new file mode 100644 index 00000000..ea18ce84 --- /dev/null +++ b/src/ecezo/javaupm_ecezo.i @@ -0,0 +1,22 @@ +%module javaupm_ecezo +%include "../upm.i" +%include "std_string.i" +%include "stdint.i" +%include "typemaps.i" + +%include "ecezo_defs.h" +%include "ecezo.hpp" +%{ + #include "ecezo.hpp" +%} + +%pragma(java) jniclasscode=%{ + static { + try { + System.loadLibrary("javaupm_ecezo"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. \n" + e); + System.exit(1); + } + } +%} diff --git a/src/ecezo/jsupm_ecezo.i b/src/ecezo/jsupm_ecezo.i new file mode 100644 index 00000000..e7116ef0 --- /dev/null +++ b/src/ecezo/jsupm_ecezo.i @@ -0,0 +1,9 @@ +%module jsupm_ecezo +%include "../upm.i" +%include "std_string.i" + +%include "ecezo_defs.h" +%include "ecezo.hpp" +%{ + #include "ecezo.hpp" +%} diff --git a/src/ecezo/pyupm_ecezo.i b/src/ecezo/pyupm_ecezo.i new file mode 100644 index 00000000..f6f934ce --- /dev/null +++ b/src/ecezo/pyupm_ecezo.i @@ -0,0 +1,13 @@ +// Include doxygen-generated documentation +%include "pyupm_doxy2swig.i" +%module pyupm_ecezo +%include "../upm.i" +%include "std_string.i" + +%feature("autodoc", "3"); + +%include "ecezo_defs.h" +%include "ecezo.hpp" +%{ + #include "ecezo.hpp" +%}