1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-17 04:52:59 +01:00

Merge pull request #10983 from skullbox305/driver_ph_oem

drivers/ph_oem: support for Atlas Scientific pH OEM sensor
This commit is contained in:
Leandro Lanzieri 2019-09-09 10:18:49 +02:00 committed by GitHub
commit 563a053bc1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1499 additions and 0 deletions

View File

@ -387,6 +387,12 @@ ifneq (,$(filter pcd8544,$(USEMODULE)))
USEMODULE += xtimer
endif
ifneq (,$(filter ph_oem,$(USEMODULE)))
FEATURES_REQUIRED += periph_gpio_irq
FEATURES_REQUIRED += periph_i2c
USEMODULE += xtimer
endif
ifneq (,$(filter pir,$(USEMODULE)))
FEATURES_REQUIRED += periph_gpio
FEATURES_REQUIRED += periph_gpio_irq

View File

@ -214,6 +214,10 @@ ifneq (,$(filter pcd8544,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/pcd8544/include
endif
ifneq (,$(filter ph_oem,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/ph_oem/include
endif
ifneq (,$(filter pir,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/pir/include
endif

362
drivers/include/ph_oem.h Normal file
View File

@ -0,0 +1,362 @@
/*
* 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 */
/** @} */

View File

@ -104,6 +104,7 @@ enum {
SAUL_SENSE_PM = 0x96, /**< sensor: particulate matter */
SAUL_SENSE_CAPACITANCE = 0x97, /**< sensor: capacitance */
SAUL_SENSE_VOLTAGE = 0x98, /**< sensor: voltage */
SAUL_SENSE_PH = 0x99, /**< sensor: pH */
SAUL_CLASS_ANY = 0xff /**< any device - wildcard */
/* extend this list as needed... */
};

1
drivers/ph_oem/Makefile Normal file
View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,83 @@
/*
* 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.
*/
/**
* @ingroup drivers_ph_oem
* @{
*
* @file
* @brief Default configuration for Atlas Scientific pH OEM sensors
*
* @author Igor Knippenberg <igor.knippenberg@gmail.com>
*/
#ifndef PH_OEM_PARAMS_H
#define PH_OEM_PARAMS_H
#include "board.h" /* THIS INCLUDE IS MANDATORY */
#include "saul_reg.h"
#include "ph_oem.h"
#include "ph_oem_regs.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name Set default configuration parameters for the Atlas Scientific pH OEM driver
* @{
*/
#ifndef PH_OEM_PARAM_I2C
#define PH_OEM_PARAM_I2C (I2C_DEV(0))
#endif
#ifndef PH_OEM_PARAM_ADDR
#define PH_OEM_PARAM_ADDR (0x65)
#endif
#ifndef PH_OEM_PARAM_INTERRUPT_PIN
#define PH_OEM_PARAM_INTERRUPT_PIN (GPIO_UNDEF)
#endif
#ifndef PH_OEM_PARAM_INTERRUPT_OPTION
#define PH_OEM_PARAM_INTERRUPT_OPTION (PH_OEM_IRQ_BOTH)
#endif
#ifndef PH_OEM_PARAM_INTERRUPT_GPIO_MODE
#define PH_OEM_PARAM_INTERRUPT_GPIO_MODE (GPIO_IN_PD)
#endif
#ifndef PH_OEM_PARAMS
#define PH_OEM_PARAMS { .i2c = PH_OEM_PARAM_I2C, \
.addr = PH_OEM_PARAM_ADDR, \
.interrupt_pin = PH_OEM_PARAM_INTERRUPT_PIN, \
.gpio_mode = PH_OEM_PARAM_INTERRUPT_GPIO_MODE, \
.irq_option = PH_OEM_PARAM_INTERRUPT_OPTION }
#endif
#ifndef PH_OEM_SAUL_INFO
#define PH_OEM_SAUL_INFO { .name = "pH OEM sensor" }
#endif
/** @} */
/**
* @brief pH OEM defaults if not defined for a board or application
*/
static const ph_oem_params_t ph_oem_params[] =
{
PH_OEM_PARAMS
};
/**
* @brief Additional meta information to keep in the SAUL registry
*/
static const saul_reg_info_t ph_oem_saul_info[] =
{
PH_OEM_SAUL_INFO
};
#ifdef __cplusplus
}
#endif
#endif /* PH_OEM_PARAMS_H */
/** @} */

View File

@ -0,0 +1,61 @@
/*
* 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.
*/
/**
* @ingroup drivers_ph_oem
* @{
*
* @file
* @brief Register definitions for the Atlas Scientific pH OEM sensor.
*
* @author Igor Knippenberg <igor.knippenberg@gmail.com>
*/
#ifndef PH_OEM_REGS_H
#define PH_OEM_REGS_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Atlas Scientific pH OEM register addresses
*
* All registers in the pH OEM are 8 bit wide and transmitted MSB first.
*
*/
typedef enum ph_oem_reg {
PH_OEM_REG_DEVICE_TYPE = 0x00, /**< Device type register (read only) */
PH_OEM_REG_FIRMWARE_VERSION = 0x01, /**< Firmware version register (read only) */
PH_OEM_REG_UNLOCK = 0x02, /**< SMBus/I²C address lock/unlock register (read/write) */
PH_OEM_REG_ADDRESS = 0x03, /**< SMBus/I²C address register (read/write) */
PH_OEM_REG_INTERRUPT = 0x04, /**< Interrupt control register (read/write) */
PH_OEM_REG_LED = 0x05, /**< LED control register (read/write) */
PH_OEM_REG_HIBERNATE = 0x06, /**< Active/hibernate register (read/write) */
PH_OEM_REG_NEW_READING = 0x07, /**< New reading available register (read/write) */
PH_OEM_REG_CALIBRATION_BASE = 0x08, /**< Calibration value register base address. Register order is: MSB, high byte, low byte, LSB (0x08-0x0B) (read/write) */
PH_OEM_REG_CALIBRATION_REQUEST = 0x0C, /**< Calibration request register (read/write) */
PH_OEM_REG_CALIBRATION_CONFIRM = 0x0D, /**< Calibration confirm register (read/write) */
PH_OEM_REG_TEMP_COMPENSATION_BASE = 0x0E, /**< Temperature compensation register base address. Register order is: MSB, high byte, low byte, LSB (0x0E-0x11) (read/write) */
PH_OEM_REG_TEMP_CONFIRMATION_BASE = 0x12, /**< Temperature confirm register base address. Register order is: MSB, high byte, low byte, LSB (0x12-0x15) (read only) */
PH_OEM_REG_PH_READING_BASE = 0x16, /**< pH reading register base address, order= MSB, high byte, low byte, LSB (0x16-0x19) (read only) */
} ph_oem_reg_t;
/**
* @brief Device ID of the @ref PH_OEM_REG_DEVICE_TYPE register of a pH OEM sensor
*/
#define PH_OEM_DEVICE_TYPE_ID 0x01
#ifdef __cplusplus
}
#endif
#endif /* PH_OEM_REGS_H */
/** @} */

497
drivers/ph_oem/ph_oem.c Normal file
View File

@ -0,0 +1,497 @@
/*
* 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.
*/
/**
* @ingroup drivers_ph_oem
* @{
*
* @file
* @brief pH OEM device driver
*
* @author Igor Knippenberg <igor.knippenberg@gmail.com>
* @}
*/
#include "xtimer.h"
#include "assert.h"
#include "periph/i2c.h"
#include "periph/gpio.h"
#include "ph_oem.h"
#include "ph_oem_params.h"
#include "ph_oem_regs.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#define DEV_I2C (dev->params.i2c)
#define ADDR (dev->params.addr)
#define IRQ_OPTION (dev->params.irq_option)
/**
* @brief Unlocks the PH_OEM_REG_UNLOCK register to be able to change the
* I2C device address, by writing 0x55 and 0xAA to the register
*
* @param[in] dev device descriptor
*
* @return PH_OEM_OK on success
* @return PH_OEM_WRITE_ERR if writing to the device failed
*/
static int _unlock_address_reg(ph_oem_t *dev);
/**
* @brief Setting the pH OEM interrupt mode to the defined mode provided
* in the device descriptor
*
* @param[in] dev device descriptor
*
* @return PH_OEM_OK on success
* @return PH_OEM_WRITE_ERR if writing to the device failed
*/
static int _set_interrupt_pin(const ph_oem_t *dev);
/**
* @brief Polls the PH_OEM_REG_NEW_READING register as long as it does not
* equal 0x01, which indicates that a new pH reading is available.
* Polling is done in an interval of 20ms. Estimated completion ~420ms
*
* @param[in] dev device descriptor
*
* @return PH_OEM_OK on success
* @return PH_OEM_READ_ERR if reading from the register failed
* @return PH_OEM_WRITE_ERR if reseting the register failed
*/
static int _new_reading_available(const ph_oem_t *dev);
/**
* @brief Sets the PH_OEM_REG_CALIBRATION_BASE register to the pH
* @p calibration_value which the device will be calibrated to.
*
* @param[in] dev device descriptor
* @param[in] calibration_value pH value the device will be calibrated to
*
* @return PH_OEM_OK on success
* @return PH_OEM_READ_ERR if reading from the register failed
* @return PH_OEM_WRITE_ERR if writing the calibration_value to the device failed
*/
static int _set_calibration_value(const ph_oem_t *dev,
uint16_t calibration_value);
int ph_oem_init(ph_oem_t *dev, const ph_oem_params_t *params)
{
assert(dev && params);
dev->params = *params;
uint8_t reg_data;
i2c_acquire(DEV_I2C);
/* Register read test */
if (i2c_read_regs(DEV_I2C, ADDR, PH_OEM_REG_DEVICE_TYPE,
&reg_data, 1, 0x0) < 0) {
DEBUG("\n[ph_oem debug] init - error: unable to read reg %x\n",
PH_OEM_REG_DEVICE_TYPE);
i2c_release(DEV_I2C);
return PH_OEM_NODEV;
}
/* Test if the device ID of the attached pH OEM sensor equals the
* value of the PH_OEM_REG_DEVICE_TYPE register
* */
if (reg_data != PH_OEM_DEVICE_TYPE_ID) {
DEBUG("\n[ph_oem debug] init - error: the attached device is not a pH OEM "
"Sensor. Read Device Type ID is: %i\n",
reg_data);
i2c_release(DEV_I2C);
return PH_OEM_NOT_PH;
}
i2c_release(DEV_I2C);
return PH_OEM_OK;
}
static int _unlock_address_reg(ph_oem_t *dev)
{
uint8_t reg_value = 1;
i2c_acquire(DEV_I2C);
i2c_write_reg(DEV_I2C, ADDR, PH_OEM_REG_UNLOCK, 0x55, 0x0);
i2c_write_reg(DEV_I2C, ADDR, PH_OEM_REG_UNLOCK, 0xAA, 0x0);
/* if successfully unlocked the register will equal 0x00 */
i2c_read_reg(DEV_I2C, ADDR, PH_OEM_REG_UNLOCK, &reg_value, 0x0);
if (reg_value != 0x00) {
DEBUG("\n[ph_oem debug] Failed at unlocking I2C address register. \n");
i2c_release(DEV_I2C);
return PH_OEM_WRITE_ERR;
}
i2c_release(DEV_I2C);
return PH_OEM_OK;
}
int ph_oem_set_i2c_address(ph_oem_t *dev, uint8_t addr)
{
assert(dev);
if (_unlock_address_reg(dev) != PH_OEM_OK) {
return PH_OEM_WRITE_ERR;
}
i2c_acquire(DEV_I2C);
if (i2c_write_reg(DEV_I2C, ADDR, PH_OEM_REG_ADDRESS, addr, 0x0) < 0) {
DEBUG("\n[ph_oem debug] Setting I2C address to %x failed\n", addr);
i2c_release(DEV_I2C);
return PH_OEM_WRITE_ERR;
}
dev->params.addr = addr;
i2c_release(DEV_I2C);
return PH_OEM_OK;
}
static int _set_interrupt_pin(const ph_oem_t *dev)
{
assert(dev);
i2c_acquire(DEV_I2C);
if (i2c_write_reg(DEV_I2C, ADDR, PH_OEM_REG_INTERRUPT, IRQ_OPTION,
0x0) < 0) {
DEBUG("\n[ph_oem debug] Setting interrupt pin to option %d failed.\n",
IRQ_OPTION);
i2c_release(DEV_I2C);
return PH_OEM_WRITE_ERR;
}
i2c_release(DEV_I2C);
return PH_OEM_OK;
}
int ph_oem_enable_interrupt(ph_oem_t *dev, ph_oem_interrupt_pin_cb_t cb,
void *arg)
{
if (dev->params.interrupt_pin == GPIO_UNDEF) {
return PH_OEM_INTERRUPT_GPIO_UNDEF;
}
if (_set_interrupt_pin(dev) < 0) {
return PH_OEM_WRITE_ERR;
}
int gpio_flank = 0;
switch (IRQ_OPTION) {
case PH_OEM_IRQ_FALLING:
gpio_flank = GPIO_FALLING;
break;
case PH_OEM_IRQ_RISING:
gpio_flank = GPIO_RISING;
break;
case PH_OEM_IRQ_BOTH:
gpio_flank = GPIO_BOTH;
break;
}
dev->arg = arg;
dev->cb = cb;
if (gpio_init_int(dev->params.interrupt_pin,
dev->params.gpio_mode, gpio_flank, cb, arg) < 0) {
DEBUG("\n[ph_oem debug] Initializing interrupt gpio pin failed.\n");
return PH_OEM_GPIO_INIT_ERR;
}
return PH_OEM_OK;
}
int ph_oem_reset_interrupt_pin(const ph_oem_t *dev)
{
/* no reset needed for mode PH_OEM_IRQ_BOTH */
if (dev->params.irq_option == PH_OEM_IRQ_BOTH) {
return PH_OEM_OK;
}
if (_set_interrupt_pin(dev) < 0) {
return PH_OEM_WRITE_ERR;
}
return PH_OEM_OK;
}
int ph_oem_set_led_state(const ph_oem_t *dev, ph_oem_led_state_t state)
{
assert(dev);
i2c_acquire(DEV_I2C);
if (i2c_write_reg(DEV_I2C, ADDR, PH_OEM_REG_LED, state, 0x0) < 0) {
DEBUG("\n[ph_oem debug] Setting LED state to %d failed.\n", state);
i2c_release(DEV_I2C);
return PH_OEM_WRITE_ERR;
}
i2c_release(DEV_I2C);
return PH_OEM_OK;
}
int ph_oem_set_device_state(const ph_oem_t *dev, ph_oem_device_state_t state)
{
assert(dev);
i2c_acquire(DEV_I2C);
if (i2c_write_reg(DEV_I2C, ADDR, PH_OEM_REG_HIBERNATE, state, 0x0) < 0) {
DEBUG("\n[ph_oem debug] Setting device state to %d failed\n", state);
i2c_release(DEV_I2C);
return PH_OEM_WRITE_ERR;
}
i2c_release(DEV_I2C);
return PH_OEM_OK;
}
static int _new_reading_available(const ph_oem_t *dev)
{
int8_t new_reading_available;
assert(dev);
i2c_acquire(DEV_I2C);
do {
if (i2c_read_reg(DEV_I2C, ADDR, PH_OEM_REG_NEW_READING,
&new_reading_available, 0x0) < 0) {
DEBUG("\n[ph_oem debug] Failed at reading PH_OEM_REG_NEW_READING\n");
i2c_release(DEV_I2C);
return PH_OEM_READ_ERR;
}
xtimer_usleep(20 * US_PER_MS);
} while (new_reading_available == 0);
/* need to manually reset register back to 0x00 */
if (i2c_write_reg(DEV_I2C, ADDR, PH_OEM_REG_NEW_READING, 0x00, 0x0) < 0) {
DEBUG("\n[ph_oem debug] Resetting PH_OEM_REG_NEW_READING failed\n");
i2c_release(DEV_I2C);
return PH_OEM_WRITE_ERR;
}
i2c_release(DEV_I2C);
return PH_OEM_OK;
}
int ph_oem_start_new_reading(const ph_oem_t *dev)
{
if (ph_oem_set_device_state(dev, PH_OEM_TAKE_READINGS) < 0) {
return PH_OEM_WRITE_ERR;
}
/* if interrupt pin is undefined, poll till new reading was taken and stop
* device form taking further readings */
if (dev->params.interrupt_pin == GPIO_UNDEF) {
int result = _new_reading_available(dev);
if (result < 0) {
return result;
}
if (ph_oem_set_device_state(dev, PH_OEM_STOP_READINGS) < 0) {
return PH_OEM_WRITE_ERR;
}
}
return PH_OEM_OK;
}
int ph_oem_clear_calibration(const ph_oem_t *dev)
{
uint8_t reg_value;
assert(dev);
i2c_acquire(DEV_I2C);
if (i2c_write_reg(DEV_I2C, ADDR, PH_OEM_REG_CALIBRATION_REQUEST, 0x01,
0) < 0) {
DEBUG("\n[ph_oem debug] Clearing calibration failed \n");
i2c_release(DEV_I2C);
return PH_OEM_WRITE_ERR;
}
do {
if (i2c_read_reg(DEV_I2C, ADDR, PH_OEM_REG_CALIBRATION_REQUEST,
&reg_value,
0) < 0) {
i2c_release(DEV_I2C);
return PH_OEM_READ_ERR;
}
} while (reg_value != 0x00);
i2c_release(DEV_I2C);
return PH_OEM_OK;
}
static int _set_calibration_value(const ph_oem_t *dev,
uint16_t calibration_value)
{
uint8_t reg_value[4];
reg_value[0] = 0x00;
reg_value[1] = 0x00;
reg_value[2] = (uint8_t)(calibration_value >> 8);
reg_value[3] = (uint8_t)(calibration_value & 0x00FF);
i2c_acquire(DEV_I2C);
if (i2c_write_regs(DEV_I2C, ADDR, PH_OEM_REG_CALIBRATION_BASE, &reg_value,
4, 0) < 0) {
DEBUG("\n[ph_oem debug] Writing calibration value failed \n");
i2c_release(DEV_I2C);
return PH_OEM_WRITE_ERR;
}
/* Calibration is critical, so check if written value is in fact correct */
if (i2c_read_regs(DEV_I2C, ADDR, PH_OEM_REG_CALIBRATION_BASE, &reg_value, 4,
0) < 0) {
DEBUG("\n[ph_oem debug] Reading the calibration value failed \n");
i2c_release(DEV_I2C);
return PH_OEM_READ_ERR;
}
uint16_t confirm_value = (int16_t)(reg_value[2] << 8)
| (int16_t)(reg_value[3]);
if (confirm_value != calibration_value) {
DEBUG("\n[ph_oem debug] Setting calibration register to pH raw %d "
"failed \n", calibration_value);
i2c_release(DEV_I2C);
return PH_OEM_WRITE_ERR;
}
i2c_release(DEV_I2C);
return PH_OEM_OK;
}
int ph_oem_set_calibration(const ph_oem_t *dev, uint16_t calibration_value,
ph_oem_calibration_option_t option)
{
assert(dev);
if (_set_calibration_value(dev, calibration_value) != PH_OEM_OK) {
return PH_OEM_WRITE_ERR;
}
uint8_t reg_value;
i2c_acquire(DEV_I2C);
if (i2c_write_reg(DEV_I2C, ADDR, PH_OEM_REG_CALIBRATION_REQUEST,
option, 0) < 0) {
DEBUG("\n[ph_oem debug] Sending calibration request failed\n");
return PH_OEM_WRITE_ERR;
}
do {
if (i2c_read_reg(DEV_I2C, ADDR, PH_OEM_REG_CALIBRATION_REQUEST,
&reg_value,
0) < 0) {
DEBUG("\n[ph_oem debug] Reading calibration request status failed\n");
i2c_release(DEV_I2C);
return PH_OEM_READ_ERR;
}
} while (reg_value != 0x00);
i2c_release(DEV_I2C);
return PH_OEM_OK;
}
int ph_oem_read_calibration_state(const ph_oem_t *dev,
uint16_t *calibration_state)
{
assert(dev);
i2c_acquire(DEV_I2C);
if (i2c_read_reg(DEV_I2C, ADDR, PH_OEM_REG_CALIBRATION_CONFIRM,
calibration_state, 0) < 0) {
DEBUG(
"\n[ph_oem debug] Failed at reading calibration confirm register\n");
i2c_release(DEV_I2C);
return PH_OEM_READ_ERR;
}
i2c_release(DEV_I2C);
return PH_OEM_OK;
}
int ph_oem_set_compensation(const ph_oem_t *dev,
uint16_t temperature_compensation)
{
if (!(temperature_compensation >= 1 && temperature_compensation <= 20000)) {
return PH_OEM_TEMP_OUT_OF_RANGE;
}
assert(dev);
uint8_t reg_value[4];
reg_value[0] = 0x00;
reg_value[1] = 0x00;
reg_value[2] = (uint8_t)(temperature_compensation >> 8);
reg_value[3] = (uint8_t)(temperature_compensation & 0x00FF);
i2c_acquire(DEV_I2C);
if (i2c_write_regs(DEV_I2C, ADDR, PH_OEM_REG_TEMP_COMPENSATION_BASE,
&reg_value, 4, 0) < 0) {
DEBUG("\n[ph_oem debug] Setting temperature compensation of device to "
"%d failed\n", temperature_compensation);
i2c_release(DEV_I2C);
return PH_OEM_WRITE_ERR;
}
i2c_release(DEV_I2C);
return PH_OEM_OK;
}
int ph_oem_read_compensation(const ph_oem_t *dev,
uint16_t *temperature_compensation)
{
uint8_t reg_value[4];
assert(dev);
i2c_acquire(DEV_I2C);
if (i2c_read_regs(DEV_I2C, ADDR, PH_OEM_REG_TEMP_CONFIRMATION_BASE,
&reg_value, 4, 0) < 0) {
DEBUG("[ph_oem debug] Getting temperature compensation value failed\n");
i2c_release(DEV_I2C);
return PH_OEM_READ_ERR;
}
*temperature_compensation = (int16_t)(reg_value[2] << 8) |
(int16_t)(reg_value[3]);
i2c_release(DEV_I2C);
return PH_OEM_OK;
}
int ph_oem_read_ph(const ph_oem_t *dev, uint16_t *ph_value)
{
uint8_t reg_value[4];
assert(dev);
i2c_acquire(DEV_I2C);
if (i2c_read_regs(DEV_I2C, ADDR, PH_OEM_REG_PH_READING_BASE,
&reg_value, 4, 0) < 0) {
DEBUG("[ph_oem debug] Getting pH value failed\n");
i2c_release(DEV_I2C);
return PH_OEM_READ_ERR;
}
*ph_value = (int16_t)(reg_value[2] << 8) | (int16_t)(reg_value[3]);
i2c_release(DEV_I2C);
return PH_OEM_OK;
}

View File

@ -0,0 +1,73 @@
/*
* 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.
*/
/**
* @ingroup drivers_ph_oem
* @{
*
* @file
* @brief pH OEM adaption to the sensor/actuator abstraction layer
*
* @author Igor Knippenberg <igor.knippenberg@gmail.com>
*
* @}
*/
#include <string.h>
#include <stdio.h>
#include "saul.h"
#include "ph_oem.h"
#include "ph_oem_regs.h"
static int read_ph(const void *dev, phydat_t *res)
{
const ph_oem_t *mydev = dev;
uint16_t ph_reading;
if (mydev->params.interrupt_pin != GPIO_UNDEF) {
puts("interrupt pin not supported with SAUL yet");
return -ENOTSUP;
}
if (ph_oem_start_new_reading(mydev) < 0) {
return -ECANCELED;
}
/* Read raw pH value */
if (ph_oem_read_ph(mydev, &ph_reading) < 0) {
return -ECANCELED;
}
res->val[0] = (int16_t)ph_reading;
res->unit = UNIT_PH;
res->scale = -3;
return 1;
}
/* Sets the temperature compensation for taking accurate pH readings.
* Valid temperature range is 1 - 20000 (0.01 °C to 200.0 °C) */
static int set_temp_compensation(const void *dev, phydat_t *res)
{
const ph_oem_t *mydev = dev;
if (!(res->val[0] >= 1 && res->val[0] <= 20000)) {
return -ECANCELED;
}
if (ph_oem_set_compensation(mydev, res->val[0]) < 0) {
return -ECANCELED;
}
return 1;
}
const saul_driver_t ph_oem_saul_driver = {
.read = read_ph,
.write = set_temp_compensation,
.type = SAUL_SENSE_PH,
};

View File

@ -61,6 +61,7 @@ const char *saul_class_to_str(const uint8_t class_id)
case SAUL_SENSE_PM: return "SENSE_PM";
case SAUL_SENSE_CAPACITANCE: return "SENSE_CAPACITANCE";
case SAUL_SENSE_VOLTAGE: return "SENSE_VOLTAGE";
case SAUL_SENSE_PH: return "SENSE_PH";
case SAUL_CLASS_ANY: return "CLASS_ANY";
default: return "CLASS_UNKNOWN";
}

View File

@ -479,6 +479,10 @@ void auto_init(void)
extern void auto_init_mpu9150(void);
auto_init_mpu9150();
#endif
#ifdef MODULE_PH_OEM
extern void auto_init_ph_oem(void);
auto_init_ph_oem();
#endif
#ifdef MODULE_PIR
extern void auto_init_pir(void);
auto_init_pir();

View File

@ -0,0 +1,76 @@
/*
* 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.
*
*/
/**
* @ingroup sys_auto_init_saul
* @{
*
* @file
* @brief Auto initialization of Atlas Scientific pH OEM sensor
*
* @author igor.knippenberg@gmail.com
*
* @}
*/
#ifdef MODULE_PH_OEM
#include "assert.h"
#include "log.h"
#include "saul_reg.h"
#include "ph_oem.h"
#include "ph_oem_params.h"
/**
* @brief Define the number of configured sensors
*/
#define PH_OEM_NUM (sizeof(ph_oem_params) / sizeof(ph_oem_params[0]))
/**
* @brief Allocate memory for the device descriptors
*/
static ph_oem_t ph_oem_devs[PH_OEM_NUM];
/**
* @brief Memory for the SAUL registry entries
*/
static saul_reg_t saul_entries[PH_OEM_NUM];
/**
* @brief Define the number of saul info
*/
#define PH_OEM_INFO_NUM (sizeof(ph_oem_saul_info) / sizeof(ph_oem_saul_info[0]))
/**
* @brief Reference the driver struct
*/
extern saul_driver_t ph_oem_saul_driver;
void auto_init_ph_oem(void)
{
assert(PH_OEM_INFO_NUM == PH_OEM_NUM);
for (unsigned i = 0; i < PH_OEM_NUM; i++) {
LOG_DEBUG("[auto_init_saul] initializing ph_oem #%d\n", i);
if (ph_oem_init(&ph_oem_devs[i], &ph_oem_params[i]) < 0) {
LOG_ERROR("[auto_init_saul] error initializing ph_oem #%d\n", i);
continue;
}
saul_entries[i].dev = &(ph_oem_devs[i]);
saul_entries[i].name = ph_oem_saul_info[i].name;
saul_entries[i].driver = &ph_oem_saul_driver;
saul_reg_add(&(saul_entries[i]));
}
}
#else
typedef int dont_be_pedantic;
#endif /* MODULE_PH_OEM */

View File

@ -98,6 +98,8 @@ enum {
UNIT_DBM, /**< decibel-milliwatts */
UNIT_COULOMB, /**< coulomb */
UNIT_F, /**< Farad */
/* electrochemical */
UNIT_PH, /**< pH */
/* pressure */
UNIT_BAR, /**< Beer? */
UNIT_PA, /**< Pascal */

View File

@ -104,6 +104,7 @@ const char *phydat_unit_to_str(uint8_t unit)
case UNIT_COULOMB: return "C";
case UNIT_GPM3: return "g/m^3";
case UNIT_F: return "F";
case UNIT_PH: return "pH";
default: return "";
}
}

View File

@ -0,0 +1,25 @@
include ../Makefile.tests_common
USEMODULE += ph_oem
USEMODULE += event_callback
# Example for providing settings through CFLAGS. Change them based on your setup
PH_OEM_PARAM_I2C ?= I2C_DEV\(0\)
PH_OEM_PARAM_ADDR ?= 0x65
PH_OEM_PARAM_INTERRUPT_PIN ?= GPIO_PIN\(2,6\)
# Most likely just GPIO_IN. Else GPIO_IN_PU for PH_OEM_IRQ_FALLING and GPIO_IN_PD
# for PH_OEM_IRQ _RISING and PH_OEM_IRQ_BOTH
PH_OEM_PARAM_INTERRUPT_GPIO_MODE ?= GPIO_IN_PD
#PH_OEM_IRQ_BOTH, PH_OEM_IRQ_RISING or PH_OEM_IRQ_FALLING
PH_OEM_PARAM_INTERRUPT_OPTION ?= PH_OEM_IRQ_RISING
# export parameters. Uncomment the one you want to use
#CFLAGS += -DPH_OEM_PARAM_I2C=$(PH_OEM_PARAM_I2C)
#CFLAGS += -DPH_OEM_PARAM_ADDR=$(PH_OEM_PARAM_ADDR)
#CFLAGS += -DPH_OEM_PARAM_INTERRUPT_PIN=$(PH_OEM_PARAM_INTERRUPT_PIN)
#CFLAGS += -DPH_OEM_PARAM_INTERRUPT_GPIO_MODE=$(PH_OEM_PARAM_INTERRUPT_GPIO_MODE)
#CFLAGS += -DPH_OEM_PARAM_INTERRUPT_OPTION=$(PH_OEM_PARAM_INTERRUPT_OPTION)
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,15 @@
## About
This is a manual test application for the Atlas Scientific pH OEM sensor driver.
## Usage
This application will verify the correct operation of the pH OEM sensor by testing
all possible functionality, based on the pin configuration you use.
The enable and interrupt pin are undefined by default. If you want to use them
define them for example in the makefile (an example is already there).
The calibration test is disabled by default, so it will not reset your
previously done calibrations.
After initialization and successful tests, the sensor reads the pH value every
5 seconds and prints them to the STDOUT.

287
tests/driver_ph_oem/main.c Normal file
View File

@ -0,0 +1,287 @@
/*
* 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.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief Test application for the Atlas Scientific pH OEM sensor driver
*
* @author Igor Knippenberg <igor.knippenberg@gmail.com>
*
* @}
*/
#include "xtimer.h"
#include "event/callback.h"
#include "ph_oem.h"
#include "ph_oem_params.h"
#include "ph_oem_regs.h"
#define SLEEP_SEC (5)
/* calibration test is off by default, so it won't reset your previous calibration */
#define CALIBRATION_TEST_ENABLED (false)
static void reading_available_event_callback(event_t *event);
static ph_oem_t dev;
static event_queue_t event_queue;
static event_t event = { .handler = reading_available_event_callback };
static void reading_available_event_callback(event_t *event)
{
(void)event;
uint16_t data;
puts("\n[EVENT - reading pH value from the device]");
/* stop pH sensor from taking further readings*/
ph_oem_set_device_state(&dev, PH_OEM_STOP_READINGS);
/* reset interrupt pin in case of falling or rising flank */
ph_oem_reset_interrupt_pin(&dev);
ph_oem_read_ph(&dev, &data);
printf("pH value raw: %d\n", data);
ph_oem_read_compensation(&dev, &data);
printf("pH reading was taken at %d Celsius\n", data);
}
static void interrupt_pin_callback(void *arg)
{
puts("\n[IRQ - Reading done. Writing read-event to event queue]");
(void)arg;
/* Posting event to the event queue. Main is blocking with "event_wait"
* and will execute the event callback after posting */
event_post(&event_queue, &event);
/* initiate new reading with "ph_oem_start_new_reading()" for this callback
to be called again */
}
int main(void)
{
uint16_t data;
puts("Atlas Scientific pH OEM sensor driver test application\n");
printf("Initializing pH OEM sensor at I2C_%i, address 0x%02x...",
PH_OEM_PARAM_I2C, PH_OEM_PARAM_ADDR);
if (ph_oem_init(&dev, ph_oem_params) == PH_OEM_OK) {
puts("[OK]");
}
else {
puts("[Failed]");
return -1;
}
printf("Turning LED off... ");
if (ph_oem_set_led_state(&dev, PH_OEM_LED_OFF) == PH_OEM_OK) {
puts("[OK]");
/* Sleep 2 seconds to actually see it turning off */
xtimer_sleep(2);
}
else {
puts("[Failed]");
return -1;
}
printf("Turning LED on... ");
if (ph_oem_set_led_state(&dev, PH_OEM_LED_ON) == PH_OEM_OK) {
puts("[OK]");
}
else {
puts("[Failed]");
return -1;
}
/* Test changing the pH OEM i2c address to 0x66 and back to 0x65 in the
* sensor as well as dev->params.addr
*/
printf("Setting device address to 0x66... ");
if (ph_oem_set_i2c_address(&dev, 0x66) == PH_OEM_OK) {
puts("[OK]");
}
else {
puts("[Failed]");
return -1;
}
printf("Setting device address back to the default address 0x65... ");
if (ph_oem_set_i2c_address(&dev, 0x65) == PH_OEM_OK) {
puts("[OK]");
}
else {
puts("[Failed]");
return -1;
}
/* Test calibration process and if it is applied correctly in the pH OEM register */
if (CALIBRATION_TEST_ENABLED) {
printf("Clearing all previous calibrations... ");
if (ph_oem_clear_calibration(&dev) == PH_OEM_OK) {
puts("[OK]");
}
else {
puts("[Failed]");
return -1;
}
printf("Reading calibration state, should be 0... ");
if (ph_oem_read_calibration_state(&dev, &data) == PH_OEM_OK
&& data == 0) {
puts("[OK]");
}
else {
puts("[Failed]");
return -1;
}
/* Don't forget to provide temperature compensation for the calibration */
printf("Setting temperature compensation to 22 Celsius... ");
if (ph_oem_set_compensation(&dev, 2200)) {
puts("[OK]");
}
else {
puts("[Failed]");
return -1;
}
/* Always start with mid point when doing a new calibration */
printf("Calibrating to midpoint... ");
if (ph_oem_set_calibration(&dev, 6870, PH_OEM_CALIBRATE_MID_POINT)
== PH_OEM_OK) {
puts("[OK]");
}
else {
puts("[Failed]");
return -1;
}
printf("Reading calibration state, should be 2... ");
if (ph_oem_read_calibration_state(&dev, &data) == PH_OEM_OK
&& data == 2) {
puts("[OK]");
}
else {
puts("[Failed]");
return -1;
}
printf("Calibrating to lowpoint... ");
if (ph_oem_set_calibration(&dev, 4000, PH_OEM_CALIBRATE_LOW_POINT)
== PH_OEM_OK) {
puts("[OK]");
}
else {
puts("[Failed]");
return -1;
}
printf("Reading calibration state, should be 3... ");
if (ph_oem_read_calibration_state(&dev, &data) == PH_OEM_OK
&& data == 3) {
puts("[OK]");
}
else {
puts("[Failed]");
return -1;
}
printf("Calibrating to highpoint... ");
if (ph_oem_set_calibration(&dev, 9210, PH_OEM_CALIBRATE_HIGH_POINT)
== PH_OEM_OK) {
puts("[OK]");
}
else {
puts("[Failed]");
return -1;
}
printf("Reading calibration state, should be 7... ");
if (ph_oem_read_calibration_state(&dev, &data) == PH_OEM_OK
&& data == 7) {
puts("[OK]");
}
else {
puts("[Failed]");
return -1;
}
}
if (dev.params.interrupt_pin != GPIO_UNDEF) {
/* Setting up and enabling the interrupt pin of the pH OEM */
printf("Enabling interrupt pin... ");
if (ph_oem_enable_interrupt(&dev, interrupt_pin_callback,
&data) == PH_OEM_OK) {
puts("[OK]");
}
else {
puts("[Failed]");
return -1;
}
/* initiate an event-queue. An event will be posted by the
* "interrupt_pin_callback" after an IRQ occurs. */
event_queue_init(&event_queue);
}
else {
puts("Interrupt pin undefined");
}
printf("Setting temperature compensation to 22 °C... ");
if (ph_oem_set_compensation(&dev, 2200) == PH_OEM_OK) {
puts("[OK]");
}
else {
puts("[Failed]");
return -1;
}
while (1) {
puts("\n[MAIN - Initiate reading]");
/* blocking for ~420ms till reading is done if no interrupt pin defined */
ph_oem_start_new_reading(&dev);
if (dev.params.interrupt_pin != GPIO_UNDEF) {
/* when interrupt is defined, wait for the IRQ to fire and
* the event to be posted, so the "reading_available_event_callback"
* can be executed after */
event_t *ev = event_wait(&event_queue);
ev->handler(ev);
}
if (dev.params.interrupt_pin == GPIO_UNDEF) {
if (ph_oem_read_ph(&dev, &data) == PH_OEM_OK) {
printf("pH value raw: %d\n", data);
}
else {
puts("[Reading pH failed]");
}
if (ph_oem_read_compensation(&dev, &data) == PH_OEM_OK) {
printf("pH reading was taken at %d Celsius\n", data);
}
else {
puts("[Reading compensation failed]");
}
}
xtimer_sleep(SLEEP_SEC);
}
return 0;
}