1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/include/ph_oem.h
Igor Knippenberg ead03d4a08 drivers/ph_oem: Initial implementation of the pH OEM sensor
The Atlas Scientific pH OEM sensor is a small circuit to be embedded in
end products to measure the pH value with any commercially available pH
electrode
2019-09-05 12:47:10 +02:00

363 lines
14 KiB
C

/*
* Copyright (C) 2019 University of Applied Sciences Emden / Leer
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup drivers_ph_oem pH OEM sensor device driver
* @ingroup drivers_sensors
* @ingroup drivers_saul
* @brief Device driver for Atlas Scientific pH OEM sensor with SMBus/I2C interface
*
* The Atlas Scientific pH OEM sensor can be used with or without the interrupt
* pin. Per default this pin is mapped to @ref GPIO_UNDEF if not otherwise defined
* in your makefile.
*
* If you use an electrical isolation for most accurate readings
* e.g. with the ADM3260, keep in mind that its not recommended to use the
* interrupt pin without also isolating it somehow. The preferred method,
* if not using an isolation on the interrupt line, would be polling. In this case
* leave the interrupt pin undefined.
*
* The Sensor has no integrated temperature sensor and for the highest possible
* precision it requires another device to provide the temperature for error
* compensation.
*
* Once the pH OEM is powered on it will be ready to receive commands and take
* readings after 1ms.
*
* @note This driver provides @ref drivers_saul capabilities.
* Reading (@ref saul_driver_t.read) from the device returns the current pH value.
* Writing (@ref saul_driver_t.write) a temperature value in celsius to the
* device sets the temperature compensation. A valid temperature range is
* 1 - 20000 (0.01 °C to 200.0 °C)
*
* @note Communication is done using SMBus/I2C protocol at speeds
* of 10-100 kHz. Set your board I2C speed to @ref I2C_SPEED_LOW or
* @ref I2C_SPEED_NORMAL
*
* @{
*
* @file
* @brief Device driver for Atlas Scientific pH OEM Sensor with SMBus/I2C interface
* @author Igor Knippenberg <igor.knippenberg@gmail.com>
*/
#ifndef PH_OEM_H
#define PH_OEM_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "periph/i2c.h"
#include "periph/gpio.h"
/**
* @brief Named return values
*/
typedef enum {
PH_OEM_OK = 0, /**< Everything was fine */
PH_OEM_NODEV = -1, /**< No device found on the bus */
PH_OEM_READ_ERR = -2, /**< Reading to device failed*/
PH_OEM_WRITE_ERR = -3, /**< Writing to device failed */
PH_OEM_NOT_PH = -4, /**< Not an Atlas Scientific pH OEM device */
PH_OEM_INTERRUPT_GPIO_UNDEF = -5, /**< Interrupt pin is @ref GPIO_UNDEF */
PH_OEM_GPIO_INIT_ERR = -6, /**< Error while initializing GPIO PIN */
PH_OEM_TEMP_OUT_OF_RANGE = -7 /**< Temperature is out of range */
} ph_oem_named_returns_t;
/**
* @brief LED state values
*/
typedef enum {
PH_OEM_LED_ON = 0x01, /**< LED on state */
PH_OEM_LED_OFF = 0x00, /**< LED off state */
} ph_oem_led_state_t;
/**
* @brief Device state values
*/
typedef enum {
PH_OEM_TAKE_READINGS = 0x01, /**< Device active state */
PH_OEM_STOP_READINGS = 0x00, /**< Device hibernate state */
} ph_oem_device_state_t;
/**
* @brief Interrupt pin option values
*/
typedef enum {
PH_OEM_IRQ_RISING = 0x02, /**< Pin high on new reading (manually reset) */
PH_OEM_IRQ_FALLING = 0x04, /**< Pin low on new reading (manually reset) */
PH_OEM_IRQ_BOTH = 0x08, /**< Invert state on new reading (automatically reset) */
} ph_oem_irq_option_t;
/**
* @brief Calibration option values
*/
typedef enum {
PH_OEM_CALIBRATE_LOW_POINT = 0x02, /**< Low point calibration option */
PH_OEM_CALIBRATE_MID_POINT = 0x03, /**< Mid point calibration option */
PH_OEM_CALIBRATE_HIGH_POINT = 0x04, /**< High point calibration option */
} ph_oem_calibration_option_t;
/**
* @brief pH OEM sensor params
*/
typedef struct ph_oem_params {
i2c_t i2c; /**< I2C device the sensor is connected to */
uint8_t addr; /**< the slave address of the sensor on the I2C bus */
gpio_t interrupt_pin; /**< interrupt pin (@ref GPIO_UNDEF if not defined) */
gpio_mode_t gpio_mode; /**< gpio mode of the interrupt pin */
ph_oem_irq_option_t irq_option; /**< behavior of the interrupt pin, disabled by default */
} ph_oem_params_t;
/**
* @brief pH OEM interrupt pin callback
*/
typedef void (*ph_oem_interrupt_pin_cb_t)(void *);
/**
* @brief pH OEM device descriptor
*/
typedef struct ph_oem {
ph_oem_params_t params; /**< device driver configuration */
ph_oem_interrupt_pin_cb_t cb; /**< interrupt pin callback */
void *arg; /**< interrupt pin callback param */
} ph_oem_t;
/**
* @brief Initialize a pH OEM sensor
*
* @param[in,out] dev device descriptor
* @param[in] params device configuration
*
* @return @ref PH_OEM_OK on success
* @return @ref PH_OEM_NODEV if no device is found on the bus
* @return @ref PH_OEM_NOT_PH if the device found at the address is not a pH OEM device
* @return
*/
int ph_oem_init(ph_oem_t *dev, const ph_oem_params_t *params);
/**
* @brief Sets a new address to the pH OEM device by unlocking the
* @ref PH_OEM_REG_UNLOCK register and writing a new address to
* the @ref PH_OEM_REG_ADDRESS register.
* The device address will also be updated in the device descriptor so
* it is still usable.
*
* Settings are retained in the sensor if the power is cut.
*
* The address in the device descriptor will reverse to the default
* address you provided through PH_OEM_PARAM_ADDR after the
* microcontroller restarts
*
* @param[in] dev device descriptor
* @param[in] addr new address for the device. Range: 0x01 - 0x7f
*
* @return @ref PH_OEM_OK on success
* @return @ref PH_OEM_WRITE_ERR if writing to the device failed
*/
int ph_oem_set_i2c_address(ph_oem_t *dev, uint8_t addr);
/**
* @brief Enable the pH OEM interrupt pin if @ref ph_oem_params_t.interrupt_pin
* is defined.
* @note @ref ph_oem_reset_interrupt_pin needs to be called in the
* callback if you use @ref PH_OEM_IRQ_FALLING or @ref PH_OEM_IRQ_RISING
*
* @note Provide the PH_OEM_PARAM_INTERRUPT_OPTION flag in your
* makefile. Valid options see: @ref ph_oem_irq_option_t.
* The default is @ref PH_OEM_IRQ_BOTH.
*
* @note Also provide the @ref gpio_mode_t as a CFLAG in your makefile.
* Most likely @ref GPIO_IN. If the pin is to sensitive use
* @ref GPIO_IN_PU for @ref PH_OEM_IRQ_FALLING or
* @ref GPIO_IN_PD for @ref PH_OEM_IRQ_RISING and
* @ref PH_OEM_IRQ_BOTH. The default is @ref GPIO_IN_PD
*
*
* @param[in] dev device descriptor
* @param[in] cb callback called when the pH OEM interrupt pin fires
* @param[in] arg callback argument
*
* @return @ref PH_OEM_OK on success
* @return @ref PH_OEM_WRITE_ERR if writing to the device failed
* @return @ref PH_OEM_INTERRUPT_GPIO_UNDEF if the interrupt pin is undefined
* @return @ref PH_OEM_GPIO_INIT_ERR if initializing the interrupt gpio pin failed
*/
int ph_oem_enable_interrupt(ph_oem_t *dev, ph_oem_interrupt_pin_cb_t cb,
void *arg);
/**
* @brief The interrupt pin will not auto reset on option @ref PH_OEM_IRQ_RISING
* and @ref PH_OEM_IRQ_FALLING after interrupt fires,
* so call this function again to reset the pin state.
*
* @note The interrupt settings are not retained if the power is cut,
* so you have to call this function again after powering on the device.
*
* @param[in] dev device descriptor
*
* @return @ref PH_OEM_OK on success
* @return @ref PH_OEM_WRITE_ERR if writing to the device failed
*/
int ph_oem_reset_interrupt_pin(const ph_oem_t *dev);
/**
* @brief Set the LED state of the pH OEM sensor by writing to the
* @ref PH_OEM_REG_LED register
*
* @param[in] dev device descriptor
* @param[in] state @ref ph_oem_led_state_t
*
* @return @ref PH_OEM_OK on success
* @return @ref PH_OEM_WRITE_ERR if writing to the device failed
*/
int ph_oem_set_led_state(const ph_oem_t *dev, ph_oem_led_state_t state);
/**
* @brief Sets the device state (active/hibernate) of the pH OEM sensor by
* writing to the @ref PH_OEM_REG_HIBERNATE register.
*
* @note Once the device has been woken up it will continuously take
* readings every 420ms. Waking the device is the only way to take a
* reading. Hibernating the device is the only way to stop taking readings.
*
* @param[in] dev device descriptor
* @param[in] state @ref ph_oem_device_state_t
*
* @return @ref PH_OEM_OK on success
* @return @ref PH_OEM_WRITE_ERR if writing to the device failed
*/
int ph_oem_set_device_state(const ph_oem_t *dev, ph_oem_device_state_t state);
/**
* @brief Starts a new reading by setting the device state to
* @ref PH_OEM_TAKE_READINGS.
*
* @note If the @ref ph_oem_params_t.interrupt_pin is @ref GPIO_UNDEF
* this function will poll every 20ms till a reading is done (~420ms)
* and stop the device from taking further readings
*
* @param[in] dev device descriptor
*
* @return @ref PH_OEM_OK on success
* @return @ref PH_OEM_WRITE_ERR if writing to the device failed
* @return @ref PH_OEM_READ_ERR if reading from the device failed
*/
int ph_oem_start_new_reading(const ph_oem_t *dev);
/**
* @brief Clears all calibrations previously done
*
* @param[in] dev device descriptor
*
* @return @ref PH_OEM_OK on success
* @return @ref PH_OEM_WRITE_ERR if writing to the device failed
* @return @ref PH_OEM_READ_ERR if reading from the device failed
*/
int ph_oem_clear_calibration(const ph_oem_t *dev);
/**
* @brief Sets the @ref PH_OEM_REG_CALIBRATION_BASE register to the
* calibration_value which the pH OEM sensor will be
* calibrated to. Multiply the floating point calibration value of your
* solution by 1000 e.g. pH calibration solution => 7.002 * 1000 = 7002 = 0x00001B5A
*
* The calibration value will be saved based on the given
* @ref ph_oem_calibration_option_t and retained after the power is cut.
*
* @note Calibrating with @ref PH_OEM_CALIBRATE_MID_POINT will reset the
* previous calibrations.
* Always start with @ref PH_OEM_CALIBRATE_MID_POINT if you doing
* 2 or 3 point calibration
*
* @param[in] dev device descriptor
* @param[in] calibration_value pH value multiplied by 1000 e.g 7,002 * 1000 = 7002
* @param[in] option @ref ph_oem_calibration_option_t
*
* @return @ref PH_OEM_OK on success
* @return @ref PH_OEM_WRITE_ERR if writing to the device failed
* @return @ref PH_OEM_READ_ERR if reading from the device failed
*/
int ph_oem_set_calibration(const ph_oem_t *dev, uint16_t calibration_value,
ph_oem_calibration_option_t option);
/**
* @brief Read the @ref PH_OEM_REG_CALIBRATION_CONFIRM register.
* After a calibration event has been successfully carried out, the
* calibration confirmation register will reflect what calibration has
* been done, by setting bits 0 - 2.
*
* @param[in] dev device descriptor
* @param[out] calibration_state calibration state reflected by bits 0 - 2 <br>
* (0 = low, 1 = mid, 2 = high)
*
* @return @ref PH_OEM_OK on success
* @return @ref PH_OEM_READ_ERR if reading from the device failed
*/
int ph_oem_read_calibration_state(const ph_oem_t *dev, uint16_t *calibration_state);
/**
* @brief Sets the @ref PH_OEM_REG_TEMP_COMPENSATION_BASE register to the
* temperature_compensation value which the pH OEM sensor will use
* to compensate the reading error.
* Multiply the floating point temperature value by 100
* e.g. temperature in degree Celsius = 34.26 * 100 = 3426
*
* @note The temperature compensation will not be retained if the power is cut.
*
* @param[in] dev device descriptor
* @param[in] temperature_compensation valid temperature range is
* 1 - 20000 (0.01 °C to 200.0 °C)
*
* @return @ref PH_OEM_OK on success
* @return @ref PH_OEM_WRITE_ERR if writing to the device failed
* @return @ref PH_OEM_TEMP_OUT_OF_RANGE if the temperature_compensation is not in
* the valid range
*/
int ph_oem_set_compensation(const ph_oem_t *dev,
uint16_t temperature_compensation);
/**
* @brief Reads the @ref PH_OEM_REG_TEMP_CONFIRMATION_BASE register to verify
* the temperature compensation value that was used to take the pH
* reading is set to the correct temperature.
*
* @param[in] dev device descriptor
* @param[out] temperature_compensation raw temperature compensation value. <br>
* Divide by 100 for floating point <br>
* e.g 3426 / 100 = 34.26
*
* @return @ref PH_OEM_OK on success
* @return @ref PH_OEM_READ_ERR if reading from the device failed
*/
int ph_oem_read_compensation(const ph_oem_t *dev,
uint16_t *temperature_compensation);
/**
* @brief Reads the @ref PH_OEM_REG_PH_READING_BASE register to get the current
* pH reading.
*
* @param[in] dev device descriptor
* @param[out] ph_value raw pH value <br>
* divide by 1000 for floating point <br>
* e.g 8347 / 1000 = 8.347
*
* @return @ref PH_OEM_OK on success
* @return @ref PH_OEM_READ_ERR if reading from the device failed
*/
int ph_oem_read_ph(const ph_oem_t *dev, uint16_t *ph_value);
#ifdef __cplusplus
}
#endif
#endif /* PH_OEM_H */
/** @} */