mirror of https://github.com/arendst/Tasmota.git
Merge pull request #10481 from vic42/seesaw_soil_pr
New Feature: Support Adafruit STEMMA Seesaw capacitive soil moisture sensor (i2c)
This commit is contained in:
commit
236e3c9cf6
|
@ -89,3 +89,4 @@ Index | Define | Driver | Device | Address(es) | Description
|
|||
55 | USE_EZODO | xsns_78 | EZODO | 0x61 - 0x70 | Disolved Oxygen sensor
|
||||
55 | USE_EZORGB | xsns_78 | EZORGB | 0x61 - 0x70 | Color sensor
|
||||
55 | USE_EZOPMP | xsns_78 | EZOPMP | 0x61 - 0x70 | Peristaltic Pump
|
||||
56 | USE_SEESAW_SOIL | xsns_81 | SEESOIL | 0x36 - 0x39 | Adafruit seesaw soil moisture sensor
|
||||
|
|
|
@ -0,0 +1,917 @@
|
|||
/*!
|
||||
* @file Adafruit_seesaw.cpp
|
||||
*
|
||||
* @mainpage Adafruit seesaw arduino driver
|
||||
*
|
||||
* @section intro_sec Introduction
|
||||
*
|
||||
* This is part of Adafruit's seesaw driver for the Arduino platform. It is
|
||||
* designed specifically to work with the Adafruit products that use seesaw
|
||||
* technology.
|
||||
*
|
||||
* These chips use I2C to communicate, 2 pins (SCL+SDA) are required
|
||||
* to interface with the board.
|
||||
*
|
||||
* Adafruit invests time and resources providing this open source code,
|
||||
* please support Adafruit and open-source hardware by purchasing
|
||||
* products from Adafruit!
|
||||
*
|
||||
* @section author Author
|
||||
*
|
||||
* Written by Dean Miller for Adafruit Industries.
|
||||
*
|
||||
* @section license License
|
||||
*
|
||||
* BSD license, all text here must be included in any redistribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Adafruit_seesaw.h"
|
||||
|
||||
//#define SEESAW_I2C_DEBUG
|
||||
|
||||
/*!
|
||||
*****************************************************************************************
|
||||
* @brief Create a seesaw object on a given I2C bus
|
||||
*
|
||||
* @param i2c_bus the I2C bus connected to the seesaw, defaults to "Wire"
|
||||
****************************************************************************************/
|
||||
Adafruit_seesaw::Adafruit_seesaw(TwoWire *i2c_bus) {
|
||||
if (i2c_bus == NULL) {
|
||||
_i2cbus = &Wire;
|
||||
} else {
|
||||
_i2cbus = i2c_bus;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
*****************************************************************************************
|
||||
* @brief Start the seesaw
|
||||
*
|
||||
* This should be called when your sketch is
|
||||
*connecting to the seesaw
|
||||
*
|
||||
* @param addr the I2C address of the seesaw
|
||||
* @param flow the flow control pin to use
|
||||
* @param reset pass true to reset the seesaw on startup. Defaults
|
||||
*to true.
|
||||
*
|
||||
* @return true if we could connect to the seesaw, false otherwise
|
||||
****************************************************************************************/
|
||||
bool Adafruit_seesaw::begin(uint8_t addr, int8_t flow, bool reset) {
|
||||
_i2caddr = addr;
|
||||
_flow = flow;
|
||||
|
||||
if (_flow != -1)
|
||||
::pinMode(_flow, INPUT);
|
||||
|
||||
_i2c_init();
|
||||
|
||||
if (reset) {
|
||||
SWReset();
|
||||
delay(500);
|
||||
}
|
||||
|
||||
uint8_t c = this->read8(SEESAW_STATUS_BASE, SEESAW_STATUS_HW_ID);
|
||||
if (c != SEESAW_HW_ID_CODE) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
*******************************************************************
|
||||
* @brief perform a software reset. This resets all seesaw registers to
|
||||
*their default values.
|
||||
* This is called automatically from
|
||||
*Adafruit_seesaw.begin()
|
||||
|
||||
********************************************************************/
|
||||
void Adafruit_seesaw::SWReset() {
|
||||
this->write8(SEESAW_STATUS_BASE, SEESAW_STATUS_SWRST, 0xFF);
|
||||
}
|
||||
|
||||
/*!
|
||||
**************************************************************************
|
||||
* @brief Returns the available options compiled into the seesaw firmware.
|
||||
* @return the available options compiled into the seesaw firmware. If the
|
||||
*option is included, the corresponding bit is set. For example, if the ADC
|
||||
*module is compiled in then (ss.getOptions() & (1UL << SEESAW_ADC_BASE)) > 0
|
||||
***********************************************************************/
|
||||
uint32_t Adafruit_seesaw::getOptions() {
|
||||
uint8_t buf[4];
|
||||
this->read(SEESAW_STATUS_BASE, SEESAW_STATUS_OPTIONS, buf, 4);
|
||||
uint32_t ret = ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) |
|
||||
((uint32_t)buf[2] << 8) | (uint32_t)buf[3];
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
*********************************************************************
|
||||
* @brief Returns the version of the seesaw
|
||||
* @return The version code. Bits [31:16] will be a date code, [15:0] will
|
||||
*be the product id.
|
||||
********************************************************************/
|
||||
uint32_t Adafruit_seesaw::getVersion() {
|
||||
uint8_t buf[4];
|
||||
this->read(SEESAW_STATUS_BASE, SEESAW_STATUS_VERSION, buf, 4);
|
||||
uint32_t ret = ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) |
|
||||
((uint32_t)buf[2] << 8) | (uint32_t)buf[3];
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
**************************************************************************
|
||||
* @brief Set the mode of a GPIO pin.
|
||||
*
|
||||
* @param pin the pin number. On the SAMD09 breakout, this corresponds to
|
||||
*the number on the silkscreen.
|
||||
* @param mode the mode to set the pin. One of INPUT, OUTPUT, or
|
||||
*INPUT_PULLUP.
|
||||
|
||||
************************************************************************/
|
||||
void Adafruit_seesaw::pinMode(uint8_t pin, uint8_t mode) {
|
||||
if (pin >= 32)
|
||||
pinModeBulk(0, 1ul << (pin - 32), mode);
|
||||
else
|
||||
pinModeBulk(1ul << pin, mode);
|
||||
}
|
||||
|
||||
/*!
|
||||
***************************************************************************
|
||||
* @brief Set the output of a GPIO pin
|
||||
*
|
||||
* @param pin the pin number. On the SAMD09 breakout, this corresponds to
|
||||
*the number on the silkscreen.
|
||||
* @param value the value to write to the GPIO pin. This should be
|
||||
*HIGH or LOW.
|
||||
***************************************************************************/
|
||||
void Adafruit_seesaw::digitalWrite(uint8_t pin, uint8_t value) {
|
||||
if (pin >= 32)
|
||||
digitalWriteBulk(0, 1ul << (pin - 32), value);
|
||||
else
|
||||
digitalWriteBulk(1ul << pin, value);
|
||||
}
|
||||
|
||||
/*!
|
||||
****************************************************************************
|
||||
* @brief Read the current status of a GPIO pin
|
||||
*
|
||||
* @param pin the pin number. On the SAMD09 breakout, this corresponds to
|
||||
*the number on the silkscreen.
|
||||
*
|
||||
* @return the status of the pin. HIGH or LOW (1 or 0).
|
||||
***********************************************************************/
|
||||
bool Adafruit_seesaw::digitalRead(uint8_t pin) {
|
||||
if (pin >= 32)
|
||||
return digitalReadBulkB((1ul << (pin - 32))) != 0;
|
||||
else
|
||||
return digitalReadBulk((1ul << pin)) != 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
****************************************************************************
|
||||
* @brief read the status of multiple pins on port A.
|
||||
*
|
||||
* @param pins a bitmask of the pins to write. On the SAMD09 breakout,
|
||||
*this corresponds to the number on the silkscreen. For example, passing 0b0110
|
||||
*will return the values of pins 2 and 3.
|
||||
*
|
||||
* @return the status of the passed pins. If 0b0110 was passed and pin 2 is
|
||||
*high and pin 3 is low, 0b0010 (decimal number 2) will be returned.
|
||||
*******************************************************************/
|
||||
uint32_t Adafruit_seesaw::digitalReadBulk(uint32_t pins) {
|
||||
uint8_t buf[4];
|
||||
this->read(SEESAW_GPIO_BASE, SEESAW_GPIO_BULK, buf, 4);
|
||||
uint32_t ret = ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) |
|
||||
((uint32_t)buf[2] << 8) | (uint32_t)buf[3];
|
||||
return ret & pins;
|
||||
}
|
||||
|
||||
/*!
|
||||
**************************************************************************
|
||||
* @brief read the status of multiple pins on port B.
|
||||
*
|
||||
* @param pins a bitmask of the pins to write.
|
||||
*
|
||||
* @return the status of the passed pins. If 0b0110 was passed and pin 2 is
|
||||
*high and pin 3 is low, 0b0010 (decimal number 2) will be returned.
|
||||
************************************************************************/
|
||||
uint32_t Adafruit_seesaw::digitalReadBulkB(uint32_t pins) {
|
||||
uint8_t buf[8];
|
||||
this->read(SEESAW_GPIO_BASE, SEESAW_GPIO_BULK, buf, 8);
|
||||
uint32_t ret = ((uint32_t)buf[4] << 24) | ((uint32_t)buf[5] << 16) |
|
||||
((uint32_t)buf[6] << 8) | (uint32_t)buf[7];
|
||||
return ret & pins;
|
||||
}
|
||||
|
||||
/*!
|
||||
**********************************************************************
|
||||
* @brief Enable or disable GPIO interrupts on the passed pins
|
||||
*
|
||||
* @param pins a bitmask of the pins to write. On the SAMD09 breakout,
|
||||
*this corresponds to the number on the silkscreen. For example, passing 0b0110
|
||||
*will enable or disable interrups on pins 2 and 3.
|
||||
* @param enabled pass true to enable the interrupts on the passed
|
||||
*pins, false to disable the interrupts on the passed pins.
|
||||
***********************************************************************/
|
||||
void Adafruit_seesaw::setGPIOInterrupts(uint32_t pins, bool enabled) {
|
||||
uint8_t cmd[] = {(uint8_t)(pins >> 24), (uint8_t)(pins >> 16),
|
||||
(uint8_t)(pins >> 8), (uint8_t)pins};
|
||||
if (enabled)
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_INTENSET, cmd, 4);
|
||||
else
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_INTENCLR, cmd, 4);
|
||||
}
|
||||
|
||||
/*!
|
||||
****************************************************************
|
||||
* @brief read the analog value on an ADC-enabled pin.
|
||||
*
|
||||
* @param pin the number of the pin to read. On the SAMD09 breakout, this
|
||||
*corresponds to the number on the silkscreen. On the default seesaw firmware on
|
||||
*the SAMD09 breakout, pins 2, 3, and 4 are ADC-enabled.
|
||||
*
|
||||
* @return the analog value. This is an integer between 0 and 1023
|
||||
***********************************************************************/
|
||||
uint16_t Adafruit_seesaw::analogRead(uint8_t pin) {
|
||||
uint8_t buf[2];
|
||||
uint8_t p;
|
||||
switch (pin) {
|
||||
case ADC_INPUT_0_PIN:
|
||||
p = 0;
|
||||
break;
|
||||
case ADC_INPUT_1_PIN:
|
||||
p = 1;
|
||||
break;
|
||||
case ADC_INPUT_2_PIN:
|
||||
p = 2;
|
||||
break;
|
||||
case ADC_INPUT_3_PIN:
|
||||
p = 3;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
this->read(SEESAW_ADC_BASE, SEESAW_ADC_CHANNEL_OFFSET + p, buf, 2, 500);
|
||||
uint16_t ret = ((uint16_t)buf[0] << 8) | buf[1];
|
||||
delay(1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
******************************************************************************
|
||||
* @brief read the analog value on an capacitive touch-enabled pin.
|
||||
*
|
||||
* @param pin the number of the pin to read.
|
||||
*
|
||||
* @return the analog value. This is an integer between 0 and 1023
|
||||
****************************************************************************/
|
||||
uint16_t Adafruit_seesaw::touchRead(uint8_t pin) {
|
||||
uint8_t buf[2];
|
||||
uint8_t p = pin;
|
||||
uint16_t ret = 65535;
|
||||
do {
|
||||
delay(1);
|
||||
this->read(SEESAW_TOUCH_BASE, SEESAW_TOUCH_CHANNEL_OFFSET + p, buf, 2,
|
||||
1000);
|
||||
ret = ((uint16_t)buf[0] << 8) | buf[1];
|
||||
} while (ret == 65535);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
***************************************************************************
|
||||
* @brief set the mode of multiple GPIO pins at once.
|
||||
*
|
||||
* @param pins a bitmask of the pins to write. On the SAMD09 breakout,
|
||||
*this corresponds to the number on the silkscreen. For example, passing 0b0110
|
||||
*will set the mode of pins 2 and 3.
|
||||
* @param mode the mode to set the pins to. One of INPUT, OUTPUT,
|
||||
*or INPUT_PULLUP.
|
||||
************************************************************************/
|
||||
void Adafruit_seesaw::pinModeBulk(uint32_t pins, uint8_t mode) {
|
||||
uint8_t cmd[] = {(uint8_t)(pins >> 24), (uint8_t)(pins >> 16),
|
||||
(uint8_t)(pins >> 8), (uint8_t)pins};
|
||||
switch (mode) {
|
||||
case OUTPUT:
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_DIRSET_BULK, cmd, 4);
|
||||
break;
|
||||
case INPUT:
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_DIRCLR_BULK, cmd, 4);
|
||||
break;
|
||||
case INPUT_PULLUP:
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_DIRCLR_BULK, cmd, 4);
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_PULLENSET, cmd, 4);
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_BULK_SET, cmd, 4);
|
||||
break;
|
||||
case INPUT_PULLDOWN:
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_DIRCLR_BULK, cmd, 4);
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_PULLENSET, cmd, 4);
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_BULK_CLR, cmd, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
*****************************************************************************************
|
||||
* @brief set the mode of multiple GPIO pins at once. This supports both
|
||||
*ports A and B.
|
||||
*
|
||||
* @param pinsa a bitmask of the pins to write on port A. On the SAMD09
|
||||
*breakout, this corresponds to the number on the silkscreen. For example,
|
||||
*passing 0b0110 will set the mode of pins 2 and 3.
|
||||
* @param pinsb a bitmask of the pins to write on port B.
|
||||
* @param mode the mode to set the pins to. One of INPUT, OUTPUT,
|
||||
*or INPUT_PULLUP.
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::pinModeBulk(uint32_t pinsa, uint32_t pinsb,
|
||||
uint8_t mode) {
|
||||
uint8_t cmd[] = {(uint8_t)(pinsa >> 24), (uint8_t)(pinsa >> 16),
|
||||
(uint8_t)(pinsa >> 8), (uint8_t)pinsa,
|
||||
(uint8_t)(pinsb >> 24), (uint8_t)(pinsb >> 16),
|
||||
(uint8_t)(pinsb >> 8), (uint8_t)pinsb};
|
||||
switch (mode) {
|
||||
case OUTPUT:
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_DIRSET_BULK, cmd, 8);
|
||||
break;
|
||||
case INPUT:
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_DIRCLR_BULK, cmd, 8);
|
||||
break;
|
||||
case INPUT_PULLUP:
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_DIRCLR_BULK, cmd, 8);
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_PULLENSET, cmd, 8);
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_BULK_SET, cmd, 8);
|
||||
break;
|
||||
case INPUT_PULLDOWN:
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_DIRCLR_BULK, cmd, 8);
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_PULLENSET, cmd, 8);
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_BULK_CLR, cmd, 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
*****************************************************************************************
|
||||
* @brief write a value to multiple GPIO pins at once.
|
||||
*
|
||||
* @param pins a bitmask of the pins to write. On the SAMD09 breakout,
|
||||
*this corresponds to the number on the silkscreen. For example, passing 0b0110
|
||||
*will write the passed value to pins 2 and 3.
|
||||
* @param value pass HIGH to set the output on the passed pins to
|
||||
*HIGH, low to set the output on the passed pins to LOW.
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::digitalWriteBulk(uint32_t pins, uint8_t value) {
|
||||
uint8_t cmd[] = {(uint8_t)(pins >> 24), (uint8_t)(pins >> 16),
|
||||
(uint8_t)(pins >> 8), (uint8_t)pins};
|
||||
if (value)
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_BULK_SET, cmd, 4);
|
||||
else
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_BULK_CLR, cmd, 4);
|
||||
}
|
||||
|
||||
/*!
|
||||
*****************************************************************************************
|
||||
* @brief write a value to multiple GPIO pins at once. This supports both
|
||||
*ports A and B
|
||||
*
|
||||
* @param pinsa a bitmask of the pins to write on port A. On the SAMD09
|
||||
*breakout, this corresponds to the number on the silkscreen. For example,
|
||||
*passing 0b0110 will write the passed value to pins 2 and 3.
|
||||
* @param pinsb a bitmask of the pins to write on port B.
|
||||
* @param value pass HIGH to set the output on the passed pins to
|
||||
*HIGH, low to set the output on the passed pins to LOW.
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::digitalWriteBulk(uint32_t pinsa, uint32_t pinsb,
|
||||
uint8_t value) {
|
||||
uint8_t cmd[] = {(uint8_t)(pinsa >> 24), (uint8_t)(pinsa >> 16),
|
||||
(uint8_t)(pinsa >> 8), (uint8_t)pinsa,
|
||||
(uint8_t)(pinsb >> 24), (uint8_t)(pinsb >> 16),
|
||||
(uint8_t)(pinsb >> 8), (uint8_t)pinsb};
|
||||
if (value)
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_BULK_SET, cmd, 8);
|
||||
else
|
||||
this->write(SEESAW_GPIO_BASE, SEESAW_GPIO_BULK_CLR, cmd, 8);
|
||||
}
|
||||
|
||||
/*!
|
||||
*****************************************************************************************
|
||||
* @brief write a PWM value to a PWM-enabled pin
|
||||
*
|
||||
* @param pin the number of the pin to write. On the SAMD09 breakout, this
|
||||
*corresponds to the number on the silkscreen. on the default seesaw firmware on
|
||||
*the SAMD09 breakout, pins 5, 6, and 7 are PWM enabled.
|
||||
* @param value the value to write to the pin
|
||||
* @param width the width of the value to write. Defaults to 8. If
|
||||
*16 is passed a 16 bit value will be written.
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::analogWrite(uint8_t pin, uint16_t value, uint8_t width) {
|
||||
int8_t p = -1;
|
||||
switch (pin) {
|
||||
case PWM_0_PIN:
|
||||
p = 0;
|
||||
break;
|
||||
case PWM_1_PIN:
|
||||
p = 1;
|
||||
break;
|
||||
case PWM_2_PIN:
|
||||
p = 2;
|
||||
break;
|
||||
case PWM_3_PIN:
|
||||
p = 3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (p > -1) {
|
||||
if (width == 16) {
|
||||
uint8_t cmd[] = {(uint8_t)p, (uint8_t)(value >> 8), (uint8_t)value};
|
||||
this->write(SEESAW_TIMER_BASE, SEESAW_TIMER_PWM, cmd, 3);
|
||||
} else {
|
||||
uint16_t mappedVal = map(value, 0, 255, 0, 65535);
|
||||
uint8_t cmd[] = {(uint8_t)p, (uint8_t)(mappedVal >> 8),
|
||||
(uint8_t)mappedVal};
|
||||
this->write(SEESAW_TIMER_BASE, SEESAW_TIMER_PWM, cmd, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief set the PWM frequency of a PWM-enabled pin. Note that on SAMD09,
|
||||
* SAMD11 boards the frequency will be mapped to closest match
|
||||
* fixed frequencies. Also note that PWM pins 4 and 5 share a
|
||||
*timer, and PWM pins 6 and 7 share a timer. Changing the frequency for one pin
|
||||
*will change the frequency for the other pin that is on the timer.
|
||||
*
|
||||
* @param pin the number of the pin to change frequency of. On the SAMD09
|
||||
* breakout, this corresponds to the number on the silkscreen.
|
||||
* on the default seesaw firmware on the SAMD09 breakout, pins 5,
|
||||
*6, and 7 are PWM enabled.
|
||||
* @param freq the frequency to set.
|
||||
******************************************************************************/
|
||||
void Adafruit_seesaw::setPWMFreq(uint8_t pin, uint16_t freq) {
|
||||
int8_t p = -1;
|
||||
switch (pin) {
|
||||
case PWM_0_PIN:
|
||||
p = 0;
|
||||
break;
|
||||
case PWM_1_PIN:
|
||||
p = 1;
|
||||
break;
|
||||
case PWM_2_PIN:
|
||||
p = 2;
|
||||
break;
|
||||
case PWM_3_PIN:
|
||||
p = 3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (p > -1) {
|
||||
uint8_t cmd[] = {(uint8_t)p, (uint8_t)(freq >> 8), (uint8_t)freq};
|
||||
this->write(SEESAW_TIMER_BASE, SEESAW_TIMER_FREQ, cmd, 3);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Enable the data ready interrupt on the passed sercom. Note that
|
||||
*both the interrupt module and the passed sercom must be compiled into the
|
||||
*seesaw firmware for this to function. If both of these things are true, the
|
||||
*interrupt pin on the seesaw will fire when there is data to be read from the
|
||||
*passed sercom. On the default seesaw firmeare on the SAMD09 breakout, no
|
||||
*sercoms are enabled.
|
||||
*
|
||||
* @param sercom the sercom to enable the interrupt on.
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::enableSercomDataRdyInterrupt(uint8_t sercom) {
|
||||
_sercom_inten.bit.DATA_RDY = 1;
|
||||
this->write8(SEESAW_SERCOM0_BASE + sercom, SEESAW_SERCOM_INTEN,
|
||||
_sercom_inten.reg);
|
||||
}
|
||||
|
||||
/*!
|
||||
***************************************************************************************
|
||||
* @brief Disable the data ready interrupt on the passed sercom.
|
||||
*
|
||||
* @param sercom the sercom to disable the interrupt on.
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::disableSercomDataRdyInterrupt(uint8_t sercom) {
|
||||
_sercom_inten.bit.DATA_RDY = 0;
|
||||
this->write8(SEESAW_SERCOM0_BASE + sercom, SEESAW_SERCOM_INTEN,
|
||||
_sercom_inten.reg);
|
||||
}
|
||||
|
||||
/*!
|
||||
*****************************************************************************************
|
||||
* @brief Reads a character from the passed sercom if one is available.
|
||||
*Note that on the default seesaw firmware on the SAMD09 breakout no sercoms are
|
||||
*enabled.
|
||||
*
|
||||
* @param sercom the sercom to read data from.
|
||||
* @returns One byte of data
|
||||
****************************************************************************************/
|
||||
char Adafruit_seesaw::readSercomData(uint8_t sercom) {
|
||||
return this->read8(SEESAW_SERCOM0_BASE + sercom, SEESAW_SERCOM_DATA);
|
||||
}
|
||||
|
||||
/*!
|
||||
*****************************************************************************************
|
||||
* @brief Set the seesaw I2C address. This will automatically call
|
||||
*Adafruit_seesaw.begin() with the new address.
|
||||
*
|
||||
* @param addr the new address for the seesaw. This must be a valid 7 bit
|
||||
*I2C address.
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::setI2CAddr(uint8_t addr) {
|
||||
this->EEPROMWrite8(SEESAW_EEPROM_I2C_ADDR, addr);
|
||||
delay(250);
|
||||
this->begin(addr); // restart w/ the new addr
|
||||
}
|
||||
|
||||
/*!
|
||||
*****************************************************************************************
|
||||
* @brief Read the I2C address of the seesaw
|
||||
*
|
||||
* @return the 7 bit I2C address of the seesaw... which you probably
|
||||
*already know because you just read data from it.
|
||||
****************************************************************************************/
|
||||
uint8_t Adafruit_seesaw::getI2CAddr() {
|
||||
return this->read8(SEESAW_EEPROM_BASE, SEESAW_EEPROM_I2C_ADDR);
|
||||
}
|
||||
|
||||
/*!
|
||||
*****************************************************************************************
|
||||
* @brief Write a 1 byte to an EEPROM address
|
||||
*
|
||||
* @param addr the address to write to. On the default seesaw firmware on
|
||||
*the SAMD09 breakout this is between 0 and 63.
|
||||
* @param val to write between 0 and 255
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::EEPROMWrite8(uint8_t addr, uint8_t val) {
|
||||
this->EEPROMWrite(addr, &val, 1);
|
||||
}
|
||||
|
||||
/*!
|
||||
*****************************************************************************************
|
||||
* @brief write a string of bytes to EEPROM starting at the passed address
|
||||
*
|
||||
* @param addr the starting address to write the first byte. This will be
|
||||
*automatically incremented with each byte written.
|
||||
* @param buf the buffer of bytes to be written.
|
||||
* @param size the number of bytes to write. Writing past the end
|
||||
*of available EEPROM may result in undefined behavior.
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::EEPROMWrite(uint8_t addr, uint8_t *buf, uint8_t size) {
|
||||
this->write(SEESAW_EEPROM_BASE, addr, buf, size);
|
||||
}
|
||||
|
||||
/*!
|
||||
*****************************************************************************************
|
||||
* @brief Read 1 byte from the specified EEPROM address.
|
||||
*
|
||||
* @param addr the address to read from. One the default seesaw firmware
|
||||
*on the SAMD09 breakout this is between 0 and 63.
|
||||
*
|
||||
* @return the value between 0 and 255 that was read from the passed
|
||||
*address.
|
||||
****************************************************************************************/
|
||||
uint8_t Adafruit_seesaw::EEPROMRead8(uint8_t addr) {
|
||||
return this->read8(SEESAW_EEPROM_BASE, addr);
|
||||
}
|
||||
|
||||
/*!
|
||||
*****************************************************************************************
|
||||
* @brief Set the baud rate on SERCOM0.
|
||||
*
|
||||
* @param baud the baud rate to set. This is an integer value. Baud rates
|
||||
*up to 115200 are supported.
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::UARTSetBaud(uint32_t baud) {
|
||||
uint8_t cmd[] = {(uint8_t)(baud >> 24), (uint8_t)(baud >> 16),
|
||||
(uint8_t)(baud >> 8), (uint8_t)baud};
|
||||
this->write(SEESAW_SERCOM0_BASE, SEESAW_SERCOM_BAUD, cmd, 4);
|
||||
}
|
||||
|
||||
/*!
|
||||
*****************************************************************************************
|
||||
* @brief activate or deactivate a key and edge on the keypad module
|
||||
*
|
||||
* @param key the key number to activate
|
||||
* @param edge the edge to trigger on
|
||||
* @param enable passing true will enable the passed event,
|
||||
*passing false will disable it.
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::setKeypadEvent(uint8_t key, uint8_t edge, bool enable) {
|
||||
keyState ks;
|
||||
ks.bit.STATE = enable;
|
||||
ks.bit.ACTIVE = (1 << edge);
|
||||
uint8_t cmd[] = {key, ks.reg};
|
||||
this->write(SEESAW_KEYPAD_BASE, SEESAW_KEYPAD_EVENT, cmd, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
*****************************************************************************************
|
||||
* @brief enable the keypad interrupt that fires when events are in the
|
||||
*fifo.
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::enableKeypadInterrupt() {
|
||||
this->write8(SEESAW_KEYPAD_BASE, SEESAW_KEYPAD_INTENSET, 0x01);
|
||||
}
|
||||
|
||||
/**
|
||||
*****************************************************************************************
|
||||
* @brief disable the keypad interrupt that fires when events are in the
|
||||
*fifo.
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::disableKeypadInterrupt() {
|
||||
this->write8(SEESAW_KEYPAD_BASE, SEESAW_KEYPAD_INTENCLR, 0x01);
|
||||
}
|
||||
|
||||
/**
|
||||
*****************************************************************************************
|
||||
* @brief Get the number of events currently in the fifo
|
||||
* @return the number of events in the fifo
|
||||
****************************************************************************************/
|
||||
uint8_t Adafruit_seesaw::getKeypadCount() {
|
||||
return this->read8(SEESAW_KEYPAD_BASE, SEESAW_KEYPAD_COUNT, 500);
|
||||
}
|
||||
|
||||
/**
|
||||
*****************************************************************************************
|
||||
* @brief Read all keyEvents into the passed buffer
|
||||
*
|
||||
* @param buf pointer to where the keyEvents should be stored
|
||||
* @param count the number of events to read
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::readKeypad(keyEventRaw *buf, uint8_t count) {
|
||||
return this->read(SEESAW_KEYPAD_BASE, SEESAW_KEYPAD_FIFO, (uint8_t *)buf,
|
||||
count, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
*****************************************************************************************
|
||||
* @brief Read the temperature of the seesaw board in degrees Celsius.
|
||||
*NOTE: not all seesaw firmwares have the temperature sensor enabled.
|
||||
* @return Temperature in degrees Celsius as a floating point value.
|
||||
****************************************************************************************/
|
||||
float Adafruit_seesaw::getTemp() {
|
||||
uint8_t buf[4];
|
||||
this->read(SEESAW_STATUS_BASE, SEESAW_STATUS_TEMP, buf, 4, 1000);
|
||||
int32_t ret = ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) |
|
||||
((uint32_t)buf[2] << 8) | (uint32_t)buf[3];
|
||||
return (1.0 / (1UL << 16)) * ret;
|
||||
}
|
||||
|
||||
/**
|
||||
*****************************************************************************************
|
||||
* @brief Read the current position of the encoder
|
||||
* @return The encoder position as a 32 bit signed integer.
|
||||
****************************************************************************************/
|
||||
int32_t Adafruit_seesaw::getEncoderPosition() {
|
||||
uint8_t buf[4];
|
||||
this->read(SEESAW_ENCODER_BASE, SEESAW_ENCODER_POSITION, buf, 4);
|
||||
int32_t ret = ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) |
|
||||
((uint32_t)buf[2] << 8) | (uint32_t)buf[3];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
*****************************************************************************************
|
||||
* @brief Set the current position of the encoder
|
||||
* @param pos the position to set the encoder to.
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::setEncoderPosition(int32_t pos) {
|
||||
uint8_t buf[] = {(uint8_t)(pos >> 24), (uint8_t)(pos >> 16),
|
||||
(uint8_t)(pos >> 8), (uint8_t)(pos & 0xFF)};
|
||||
this->write(SEESAW_ENCODER_BASE, SEESAW_ENCODER_POSITION, buf, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
*****************************************************************************************
|
||||
* @brief Read the change in encoder position since it was last read.
|
||||
* @return The encoder change as a 32 bit signed integer.
|
||||
****************************************************************************************/
|
||||
int32_t Adafruit_seesaw::getEncoderDelta() {
|
||||
uint8_t buf[4];
|
||||
this->read(SEESAW_ENCODER_BASE, SEESAW_ENCODER_DELTA, buf, 4);
|
||||
int32_t ret = ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) |
|
||||
((uint32_t)buf[2] << 8) | (uint32_t)buf[3];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
*****************************************************************************************
|
||||
* @brief Enable the interrupt to fire when the encoder changes position.
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::enableEncoderInterrupt() {
|
||||
this->write8(SEESAW_ENCODER_BASE, SEESAW_ENCODER_INTENSET, 0x01);
|
||||
}
|
||||
|
||||
/**
|
||||
*****************************************************************************************
|
||||
* @brief Disable the interrupt from firing when the encoder changes
|
||||
*position.
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::disableEncoderInterrupt() {
|
||||
this->write8(SEESAW_ENCODER_BASE, SEESAW_ENCODER_INTENCLR, 0x01);
|
||||
}
|
||||
|
||||
/**
|
||||
*****************************************************************************************
|
||||
* @brief Write 1 byte to the specified seesaw register.
|
||||
*
|
||||
* @param regHigh the module address register (ex. SEESAW_NEOPIXEL_BASE)
|
||||
* @param regLow the function address register (ex.
|
||||
*SEESAW_NEOPIXEL_PIN)
|
||||
* @param value the value between 0 and 255 to write
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::write8(byte regHigh, byte regLow, byte value) {
|
||||
this->write(regHigh, regLow, &value, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
*****************************************************************************************
|
||||
* @brief read 1 byte from the specified seesaw register.
|
||||
*
|
||||
* @param regHigh the module address register (ex. SEESAW_STATUS_BASE)
|
||||
* @param regLow the function address register (ex.
|
||||
*SEESAW_STATUS_VERSION)
|
||||
* @param delay a number of microseconds to delay before reading
|
||||
*out the data. Different delay values may be necessary to ensure the seesaw
|
||||
*chip has time to process the requested data. Defaults to 125.
|
||||
*
|
||||
* @return the value between 0 and 255 read from the passed register
|
||||
****************************************************************************************/
|
||||
uint8_t Adafruit_seesaw::read8(byte regHigh, byte regLow, uint16_t delay) {
|
||||
uint8_t ret;
|
||||
this->read(regHigh, regLow, &ret, 1, delay);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
*****************************************************************************************
|
||||
* @brief Initialize I2C. On arduino this just calls i2c->begin()
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::_i2c_init() {
|
||||
#ifdef SEESAW_I2C_DEBUG
|
||||
Serial.println("I2C Begin");
|
||||
#endif
|
||||
_i2cbus->begin();
|
||||
}
|
||||
|
||||
/**
|
||||
*****************************************************************************************
|
||||
* @brief Read a specified number of bytes into a buffer from the seesaw.
|
||||
*
|
||||
* @param regHigh the module address register (ex. SEESAW_STATUS_BASE)
|
||||
* @param regLow the function address register (ex.
|
||||
*SEESAW_STATUS_VERSION)
|
||||
* @param buf the buffer to read the bytes into
|
||||
* @param num the number of bytes to read.
|
||||
* @param delay an optional delay in between setting the read
|
||||
*register and reading out the data. This is required for some seesaw functions
|
||||
*(ex. reading ADC data)
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::read(uint8_t regHigh, uint8_t regLow, uint8_t *buf,
|
||||
uint8_t num, uint16_t delay) {
|
||||
uint8_t pos = 0;
|
||||
|
||||
// on arduino we need to read in 32 byte chunks
|
||||
while (pos < num) {
|
||||
uint8_t read_now = min(32, num - pos);
|
||||
_i2cbus->beginTransmission((uint8_t)_i2caddr);
|
||||
_i2cbus->write((uint8_t)regHigh);
|
||||
_i2cbus->write((uint8_t)regLow);
|
||||
#ifdef SEESAW_I2C_DEBUG
|
||||
Serial.print("I2C read $");
|
||||
Serial.print((uint16_t)regHigh << 8 | regLow, HEX);
|
||||
Serial.print(" : ");
|
||||
#endif
|
||||
|
||||
if (_flow != -1)
|
||||
while (!::digitalRead(_flow))
|
||||
;
|
||||
_i2cbus->endTransmission();
|
||||
|
||||
// TODO: tune this
|
||||
delayMicroseconds(delay);
|
||||
|
||||
if (_flow != -1)
|
||||
while (!::digitalRead(_flow))
|
||||
;
|
||||
_i2cbus->requestFrom((uint8_t)_i2caddr, read_now);
|
||||
|
||||
for (int i = 0; i < read_now; i++) {
|
||||
buf[pos] = _i2cbus->read();
|
||||
#ifdef SEESAW_I2C_DEBUG
|
||||
Serial.print("0x");
|
||||
Serial.print(buf[pos], HEX);
|
||||
Serial.print(",");
|
||||
#endif
|
||||
pos++;
|
||||
}
|
||||
#ifdef SEESAW_I2C_DEBUG
|
||||
Serial.println();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
*****************************************************************************************
|
||||
* @brief Write a specified number of bytes to the seesaw from the passed
|
||||
*buffer.
|
||||
*
|
||||
* @param regHigh the module address register (ex. SEESAW_GPIO_BASE)
|
||||
* @param regLow the function address register (ex. SEESAW_GPIO_BULK_SET)
|
||||
* @param buf the buffer the the bytes from
|
||||
* @param num the number of bytes to write.
|
||||
****************************************************************************************/
|
||||
void Adafruit_seesaw::write(uint8_t regHigh, uint8_t regLow, uint8_t *buf,
|
||||
uint8_t num) {
|
||||
_i2cbus->beginTransmission((uint8_t)_i2caddr);
|
||||
_i2cbus->write((uint8_t)regHigh);
|
||||
_i2cbus->write((uint8_t)regLow);
|
||||
_i2cbus->write((uint8_t *)buf, num);
|
||||
#ifdef SEESAW_I2C_DEBUG
|
||||
Serial.print("I2C write $");
|
||||
Serial.print((uint16_t)regHigh << 8 | regLow, HEX);
|
||||
Serial.print(" : ");
|
||||
for (int i = 0; i < num; i++) {
|
||||
Serial.print("0x");
|
||||
Serial.print(buf[i], HEX);
|
||||
Serial.print(",");
|
||||
}
|
||||
Serial.println();
|
||||
#endif
|
||||
|
||||
if (_flow != -1)
|
||||
while (!::digitalRead(_flow))
|
||||
;
|
||||
_i2cbus->endTransmission();
|
||||
}
|
||||
|
||||
/*!
|
||||
*****************************************************************************************
|
||||
* @brief The print wrapper for the seesaw class. Calling this allows you
|
||||
*to use ss.print() or ss.println() and write to the UART on SERCOM0 of the
|
||||
*seesaw. Note that this functionality is only available when the UART (sercom)
|
||||
*module is compiled into the seesaw firmware. On the default seesaw firmware on
|
||||
*the SAMD09 breakout this functionality is not available.
|
||||
*
|
||||
* @param character the character to write.
|
||||
* @returns The number of bytes written (1)
|
||||
****************************************************************************************/
|
||||
size_t Adafruit_seesaw::write(uint8_t character) {
|
||||
// TODO: add support for multiple sercoms
|
||||
this->write8(SEESAW_SERCOM0_BASE, SEESAW_SERCOM_DATA, character);
|
||||
delay(1); // TODO: this can be optimized... it's only needed for longer writes
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*!
|
||||
************************************************************************
|
||||
* @brief The print wrapper for the seesaw class allowing the user to
|
||||
*print a string. Calling this allows you to use ss.print() or ss.println() and
|
||||
*write to the UART on SERCOM0 of the seesaw. Note that this functionality is
|
||||
*only available when the UART (sercom) module is compiled into the seesaw
|
||||
*firmware. On the default seesaw firmware on the SAMD09 breakout this
|
||||
*functionality is not available.
|
||||
*
|
||||
* @param str the string to write
|
||||
* @return number of bytes written (not including trailing 0)
|
||||
*********************************************************************/
|
||||
size_t Adafruit_seesaw::write(const char *str) {
|
||||
uint8_t buf[32];
|
||||
uint8_t len = 0;
|
||||
while (*str) {
|
||||
buf[len] = *str;
|
||||
str++;
|
||||
len++;
|
||||
}
|
||||
this->write(SEESAW_SERCOM0_BASE, SEESAW_SERCOM_DATA, buf, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
/*!
|
||||
**********************************************************************
|
||||
* @brief Write only the module base address register and the function
|
||||
*address register.
|
||||
*
|
||||
* @param regHigh the module address register (ex. SEESAW_STATUS_BASE)
|
||||
* @param regLow the function address register (ex.
|
||||
*SEESAW_STATUS_SWRST)
|
||||
**********************************************************************/
|
||||
void Adafruit_seesaw::writeEmpty(uint8_t regHigh, uint8_t regLow) {
|
||||
_i2cbus->beginTransmission((uint8_t)_i2caddr);
|
||||
_i2cbus->write((uint8_t)regHigh);
|
||||
_i2cbus->write((uint8_t)regLow);
|
||||
if (_flow != -1)
|
||||
while (!::digitalRead(_flow))
|
||||
;
|
||||
_i2cbus->endTransmission();
|
||||
}
|
|
@ -0,0 +1,314 @@
|
|||
/*!
|
||||
* @file Adafruit_seesaw.h
|
||||
*
|
||||
* This is part of Adafruit's seesaw driver for the Arduino platform. It is
|
||||
* designed specifically to work with the Adafruit products that use seesaw
|
||||
* technology.
|
||||
*
|
||||
* These chips use I2C to communicate, 2 pins (SCL+SDA) are required
|
||||
* to interface with the board.
|
||||
*
|
||||
* Adafruit invests time and resources providing this open source code,
|
||||
* please support Adafruit and open-source hardware by purchasing
|
||||
* products from Adafruit!
|
||||
*
|
||||
* Written by Dean Miller for Adafruit Industries.
|
||||
*
|
||||
* BSD license, all text here must be included in any redistribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LIB_SEESAW_H
|
||||
#define LIB_SEESAW_H
|
||||
|
||||
#if (ARDUINO >= 100)
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
/*=========================================================================
|
||||
I2C ADDRESS/BITS
|
||||
-----------------------------------------------------------------------*/
|
||||
#define SEESAW_ADDRESS (0x49) ///< Default Seesaw I2C address
|
||||
/*=========================================================================*/
|
||||
|
||||
/*=========================================================================
|
||||
REGISTERS
|
||||
-----------------------------------------------------------------------*/
|
||||
|
||||
/** Module Base Addreses
|
||||
* The module base addresses for different seesaw modules.
|
||||
*/
|
||||
enum {
|
||||
SEESAW_STATUS_BASE = 0x00,
|
||||
SEESAW_GPIO_BASE = 0x01,
|
||||
SEESAW_SERCOM0_BASE = 0x02,
|
||||
|
||||
SEESAW_TIMER_BASE = 0x08,
|
||||
SEESAW_ADC_BASE = 0x09,
|
||||
SEESAW_DAC_BASE = 0x0A,
|
||||
SEESAW_INTERRUPT_BASE = 0x0B,
|
||||
SEESAW_DAP_BASE = 0x0C,
|
||||
SEESAW_EEPROM_BASE = 0x0D,
|
||||
SEESAW_NEOPIXEL_BASE = 0x0E,
|
||||
SEESAW_TOUCH_BASE = 0x0F,
|
||||
SEESAW_KEYPAD_BASE = 0x10,
|
||||
SEESAW_ENCODER_BASE = 0x11,
|
||||
};
|
||||
|
||||
/** GPIO module function addres registers
|
||||
*/
|
||||
enum {
|
||||
SEESAW_GPIO_DIRSET_BULK = 0x02,
|
||||
SEESAW_GPIO_DIRCLR_BULK = 0x03,
|
||||
SEESAW_GPIO_BULK = 0x04,
|
||||
SEESAW_GPIO_BULK_SET = 0x05,
|
||||
SEESAW_GPIO_BULK_CLR = 0x06,
|
||||
SEESAW_GPIO_BULK_TOGGLE = 0x07,
|
||||
SEESAW_GPIO_INTENSET = 0x08,
|
||||
SEESAW_GPIO_INTENCLR = 0x09,
|
||||
SEESAW_GPIO_INTFLAG = 0x0A,
|
||||
SEESAW_GPIO_PULLENSET = 0x0B,
|
||||
SEESAW_GPIO_PULLENCLR = 0x0C,
|
||||
};
|
||||
|
||||
/** status module function addres registers
|
||||
*/
|
||||
enum {
|
||||
SEESAW_STATUS_HW_ID = 0x01,
|
||||
SEESAW_STATUS_VERSION = 0x02,
|
||||
SEESAW_STATUS_OPTIONS = 0x03,
|
||||
SEESAW_STATUS_TEMP = 0x04,
|
||||
SEESAW_STATUS_SWRST = 0x7F,
|
||||
};
|
||||
|
||||
/** timer module function addres registers
|
||||
*/
|
||||
enum {
|
||||
SEESAW_TIMER_STATUS = 0x00,
|
||||
SEESAW_TIMER_PWM = 0x01,
|
||||
SEESAW_TIMER_FREQ = 0x02,
|
||||
};
|
||||
|
||||
/** ADC module function addres registers
|
||||
*/
|
||||
enum {
|
||||
SEESAW_ADC_STATUS = 0x00,
|
||||
SEESAW_ADC_INTEN = 0x02,
|
||||
SEESAW_ADC_INTENCLR = 0x03,
|
||||
SEESAW_ADC_WINMODE = 0x04,
|
||||
SEESAW_ADC_WINTHRESH = 0x05,
|
||||
SEESAW_ADC_CHANNEL_OFFSET = 0x07,
|
||||
};
|
||||
|
||||
/** Sercom module function addres registers
|
||||
*/
|
||||
enum {
|
||||
SEESAW_SERCOM_STATUS = 0x00,
|
||||
SEESAW_SERCOM_INTEN = 0x02,
|
||||
SEESAW_SERCOM_INTENCLR = 0x03,
|
||||
SEESAW_SERCOM_BAUD = 0x04,
|
||||
SEESAW_SERCOM_DATA = 0x05,
|
||||
};
|
||||
|
||||
/** neopixel module function addres registers
|
||||
*/
|
||||
enum {
|
||||
SEESAW_NEOPIXEL_STATUS = 0x00,
|
||||
SEESAW_NEOPIXEL_PIN = 0x01,
|
||||
SEESAW_NEOPIXEL_SPEED = 0x02,
|
||||
SEESAW_NEOPIXEL_BUF_LENGTH = 0x03,
|
||||
SEESAW_NEOPIXEL_BUF = 0x04,
|
||||
SEESAW_NEOPIXEL_SHOW = 0x05,
|
||||
};
|
||||
|
||||
/** touch module function addres registers
|
||||
*/
|
||||
enum {
|
||||
SEESAW_TOUCH_CHANNEL_OFFSET = 0x10,
|
||||
};
|
||||
|
||||
/** keypad module function addres registers
|
||||
*/
|
||||
enum {
|
||||
SEESAW_KEYPAD_STATUS = 0x00,
|
||||
SEESAW_KEYPAD_EVENT = 0x01,
|
||||
SEESAW_KEYPAD_INTENSET = 0x02,
|
||||
SEESAW_KEYPAD_INTENCLR = 0x03,
|
||||
SEESAW_KEYPAD_COUNT = 0x04,
|
||||
SEESAW_KEYPAD_FIFO = 0x10,
|
||||
};
|
||||
|
||||
/** keypad module edge definitions
|
||||
*/
|
||||
enum {
|
||||
SEESAW_KEYPAD_EDGE_HIGH = 0,
|
||||
SEESAW_KEYPAD_EDGE_LOW,
|
||||
SEESAW_KEYPAD_EDGE_FALLING,
|
||||
SEESAW_KEYPAD_EDGE_RISING,
|
||||
};
|
||||
|
||||
/** encoder module edge definitions
|
||||
*/
|
||||
enum {
|
||||
SEESAW_ENCODER_STATUS = 0x00,
|
||||
SEESAW_ENCODER_INTENSET = 0x02,
|
||||
SEESAW_ENCODER_INTENCLR = 0x03,
|
||||
SEESAW_ENCODER_POSITION = 0x04,
|
||||
SEESAW_ENCODER_DELTA = 0x05,
|
||||
};
|
||||
|
||||
#define ADC_INPUT_0_PIN 2 ///< default ADC input pin
|
||||
#define ADC_INPUT_1_PIN 3 ///< default ADC input pin
|
||||
#define ADC_INPUT_2_PIN 4 ///< default ADC input pin
|
||||
#define ADC_INPUT_3_PIN 5 ///< default ADC input pin
|
||||
|
||||
#define PWM_0_PIN 4 ///< default PWM output pin
|
||||
#define PWM_1_PIN 5 ///< default PWM output pin
|
||||
#define PWM_2_PIN 6 ///< default PWM output pin
|
||||
#define PWM_3_PIN 7 ///< default PWM output pin
|
||||
|
||||
#ifndef INPUT_PULLDOWN
|
||||
#define INPUT_PULLDOWN \
|
||||
0x03 ///< for compatibility with platforms that do not already define
|
||||
///< INPUT_PULLDOWN
|
||||
#endif
|
||||
|
||||
/*=========================================================================*/
|
||||
|
||||
#define SEESAW_HW_ID_CODE 0x55 ///< seesaw HW ID code
|
||||
#define SEESAW_EEPROM_I2C_ADDR \
|
||||
0x3F ///< EEPROM address of i2c address to start up with (for devices that
|
||||
///< support this feature)
|
||||
|
||||
/** raw key event stucture for keypad module */
|
||||
union keyEventRaw {
|
||||
struct {
|
||||
uint8_t EDGE : 2; ///< the edge that was triggered
|
||||
uint8_t NUM : 6; ///< the event number
|
||||
} bit; ///< bitfield format
|
||||
uint8_t reg; ///< register format
|
||||
};
|
||||
|
||||
/** extended key event stucture for keypad module */
|
||||
union keyEvent {
|
||||
struct {
|
||||
uint8_t EDGE : 2; ///< the edge that was triggered
|
||||
uint16_t NUM : 14; ///< the event number
|
||||
} bit; ///< bitfield format
|
||||
uint16_t reg; ///< register format
|
||||
};
|
||||
|
||||
/** key state struct that will be written to seesaw chip keypad module */
|
||||
union keyState {
|
||||
struct {
|
||||
uint8_t STATE : 1; ///< the current state of the key
|
||||
uint8_t ACTIVE : 4; ///< the registered events for that key
|
||||
} bit; ///< bitfield format
|
||||
uint8_t reg; ///< register format
|
||||
};
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief Class that stores state and functions for interacting with seesaw
|
||||
helper IC
|
||||
*/
|
||||
/**************************************************************************/
|
||||
class Adafruit_seesaw : public Print {
|
||||
public:
|
||||
// constructors
|
||||
Adafruit_seesaw(TwoWire *Wi = NULL);
|
||||
~Adafruit_seesaw(void){};
|
||||
|
||||
bool begin(uint8_t addr = SEESAW_ADDRESS, int8_t flow = -1,
|
||||
bool reset = true);
|
||||
uint32_t getOptions();
|
||||
uint32_t getVersion();
|
||||
void SWReset();
|
||||
|
||||
void pinMode(uint8_t pin, uint8_t mode);
|
||||
void pinModeBulk(uint32_t pins, uint8_t mode);
|
||||
void pinModeBulk(uint32_t pinsa, uint32_t pinsb, uint8_t mode);
|
||||
virtual void analogWrite(uint8_t pin, uint16_t value, uint8_t width = 8);
|
||||
void digitalWrite(uint8_t pin, uint8_t value);
|
||||
void digitalWriteBulk(uint32_t pins, uint8_t value);
|
||||
void digitalWriteBulk(uint32_t pinsa, uint32_t pinsb, uint8_t value);
|
||||
|
||||
bool digitalRead(uint8_t pin);
|
||||
uint32_t digitalReadBulk(uint32_t pins);
|
||||
uint32_t digitalReadBulkB(uint32_t pins);
|
||||
|
||||
void setGPIOInterrupts(uint32_t pins, bool enabled);
|
||||
|
||||
virtual uint16_t analogRead(uint8_t pin);
|
||||
|
||||
uint16_t touchRead(uint8_t pin);
|
||||
|
||||
virtual void setPWMFreq(uint8_t pin, uint16_t freq);
|
||||
|
||||
void enableSercomDataRdyInterrupt(uint8_t sercom = 0);
|
||||
void disableSercomDataRdyInterrupt(uint8_t sercom = 0);
|
||||
|
||||
char readSercomData(uint8_t sercom = 0);
|
||||
|
||||
void EEPROMWrite8(uint8_t addr, uint8_t val);
|
||||
void EEPROMWrite(uint8_t addr, uint8_t *buf, uint8_t size);
|
||||
uint8_t EEPROMRead8(uint8_t addr);
|
||||
|
||||
void setI2CAddr(uint8_t addr);
|
||||
uint8_t getI2CAddr();
|
||||
|
||||
void UARTSetBaud(uint32_t baud);
|
||||
|
||||
void setKeypadEvent(uint8_t key, uint8_t edge, bool enable = true);
|
||||
void enableKeypadInterrupt();
|
||||
void disableKeypadInterrupt();
|
||||
uint8_t getKeypadCount();
|
||||
void readKeypad(keyEventRaw *buf, uint8_t count);
|
||||
|
||||
float getTemp();
|
||||
|
||||
int32_t getEncoderPosition();
|
||||
int32_t getEncoderDelta();
|
||||
void enableEncoderInterrupt();
|
||||
void disableEncoderInterrupt();
|
||||
void setEncoderPosition(int32_t pos);
|
||||
|
||||
virtual size_t write(uint8_t);
|
||||
virtual size_t write(const char *str);
|
||||
|
||||
protected:
|
||||
uint8_t _i2caddr; /*!< The I2C address used to communicate with the seesaw */
|
||||
TwoWire *_i2cbus; /*!< The I2C Bus used to communicate with the seesaw */
|
||||
int8_t _flow; /*!< The flow control pin to use */
|
||||
|
||||
void write8(byte regHigh, byte regLow, byte value);
|
||||
uint8_t read8(byte regHigh, byte regLow, uint16_t delay = 125);
|
||||
|
||||
void read(uint8_t regHigh, uint8_t regLow, uint8_t *buf, uint8_t num,
|
||||
uint16_t delay = 125);
|
||||
void write(uint8_t regHigh, uint8_t regLow, uint8_t *buf, uint8_t num);
|
||||
void writeEmpty(uint8_t regHigh, uint8_t regLow);
|
||||
void _i2c_init();
|
||||
|
||||
/*=========================================================================
|
||||
REGISTER BITFIELDS
|
||||
-----------------------------------------------------------------------*/
|
||||
|
||||
/** Sercom interrupt enable register
|
||||
*/
|
||||
union sercom_inten {
|
||||
struct {
|
||||
uint8_t DATA_RDY : 1; ///< this bit is set when data becomes available
|
||||
} bit; ///< bitfields
|
||||
uint8_t reg; ///< full register
|
||||
};
|
||||
sercom_inten _sercom_inten; ///< sercom interrupt enable register instance
|
||||
|
||||
/*=========================================================================*/
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,5 @@
|
|||
# Adafruit_Seesaw [![Build Status](https://github.com/adafruit/Adafruit_Seesaw/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit_Seesaw/actions)[![Documentation](https://github.com/adafruit/ci-arduino/blob/master/assets/doxygen_badge.svg)](http://adafruit.github.io/Adafruit_Seesaw/html/index.html)
|
||||
|
||||
Arduino driver for seesaw multi-use chip
|
||||
|
||||
Check out the [documentation](https://adafruit.github.io/Adafruit_Seesaw/html/class_adafruit__seesaw.html) for a listing and explanation of the available methods!
|
|
@ -0,0 +1,10 @@
|
|||
name=Adafruit seesaw Library
|
||||
version=1.3.1
|
||||
author=Adafruit
|
||||
maintainer=Adafruit <info@adafruit.com>
|
||||
sentence=This is a library for the Adafruit seesaw helper IC.
|
||||
paragraph=This is a library for the Adafruit seesaw helper IC.
|
||||
category=Other
|
||||
url=https://github.com/adafruit/Adafruit_Seesaw
|
||||
architectures=*
|
||||
depends=Adafruit ST7735 and ST7789 Library
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
xsns_81_seesaw_soil - I2C Capacitance & temperature sensor support for Tasmota
|
||||
|
||||
Copyright (C) 2021 Wayne Ross, Theo Arends, Peter Franck
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef USE_I2C
|
||||
#ifdef USE_SEESAW_SOIL
|
||||
/*********************************************************************************************\
|
||||
* SEESAW_SOIL - Capacitance & Temperature Sensor
|
||||
*
|
||||
* I2C Address: 0x36, 0x37, 0x38, 0x39
|
||||
*
|
||||
* NOTE: #define SEESAW_SOIL_PUBLISH enables immediate MQTT on soil moisture change
|
||||
* otherwise the moisture value will only be emitted every TelePeriod
|
||||
* #define SEESAW_SOIL_RAW enables displaying analog capacitance input in the
|
||||
* web page for calibration purposes
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XSNS_81 81
|
||||
#define XI2C_56 56 // See I2CDEVICES.md
|
||||
|
||||
#include "Adafruit_seesaw.h"
|
||||
|
||||
#define SEESAW_SOIL_MAX_SENSORS 4
|
||||
#define SEESAW_SOIL_START_ADDRESS 0x36
|
||||
|
||||
const char SeeSoilName[] = "SeeSoil"; // spaces not allowed for Homeassistant integration/mqtt topics
|
||||
uint8_t SeeSoilCount = 0; // global sensor count
|
||||
|
||||
struct SEESAW_SOIL {
|
||||
Adafruit_seesaw *ss; // instance pointer
|
||||
uint16_t capacitance;
|
||||
float temperature;
|
||||
uint8_t address;
|
||||
} SeeSoil[SEESAW_SOIL_MAX_SENSORS];
|
||||
|
||||
// Used to convert capacitance into a moisture.
|
||||
// From observation, a free air reading is at 320
|
||||
// Immersed in tap water, reading is 1014
|
||||
// Appears to be a 10-bit device, readings close to 1020
|
||||
// So let's make a scale that converts those (apparent) facts into a percentage
|
||||
#define MAX_CAPACITANCE 1020.0f // subject to calibration
|
||||
#define MIN_CAPACITANCE 320 // subject to calibration
|
||||
#define CAP_TO_MOIST(c) ((max((int)(c),MIN_CAPACITANCE)-MIN_CAPACITANCE)/(MAX_CAPACITANCE-MIN_CAPACITANCE))
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
void SEESAW_SOILDetect(void) {
|
||||
Adafruit_seesaw *SSptr=0;
|
||||
|
||||
for (uint32_t i = 0; i < SEESAW_SOIL_MAX_SENSORS; i++) {
|
||||
int addr = SEESAW_SOIL_START_ADDRESS + i;
|
||||
if (!I2cSetDevice(addr)) { continue; }
|
||||
|
||||
if (!SSptr) { // don't have an object,
|
||||
SSptr = new Adafruit_seesaw(); // allocate one
|
||||
}
|
||||
if (SSptr->begin(addr)) {
|
||||
SeeSoil[SeeSoilCount].ss = SSptr; // save copy of pointer
|
||||
SSptr = 0; // mark that we took it
|
||||
SeeSoil[SeeSoilCount].address = addr;
|
||||
SeeSoil[SeeSoilCount].temperature = NAN;
|
||||
SeeSoil[SeeSoilCount].capacitance = 0;
|
||||
I2cSetActiveFound(SeeSoil[SeeSoilCount].address, SeeSoilName);
|
||||
SeeSoilCount++;
|
||||
}
|
||||
}
|
||||
if (SSptr) {
|
||||
delete SSptr; // used object for detection, didn't find anything so we don't need this object
|
||||
}
|
||||
}
|
||||
|
||||
void SEESAW_SOILEverySecond(void) { // update sensor values and publish if changed
|
||||
#ifdef SEESAW_SOIL_PUBLISH
|
||||
uint32_t old_moist;
|
||||
#endif // SEESAW_SOIL_PUBLISH
|
||||
|
||||
for (uint32_t i = 0; i < SeeSoilCount; i++) {
|
||||
SeeSoil[i].temperature = ConvertTemp(SeeSoil[i].ss->getTemp());
|
||||
#ifdef SEESAW_SOIL_PUBLISH
|
||||
old_moist = uint32_t (CAP_TO_MOIST(SeeSoil[i].capacitance)*100);
|
||||
#endif // SEESAW_SOIL_PUBLISH
|
||||
SeeSoil[i].capacitance = SeeSoil[i].ss->touchRead(0);
|
||||
#ifdef SEESAW_SOIL_PUBLISH
|
||||
if (uint32_t (CAP_TO_MOIST(SeeSoil[i].capacitance)*100) != old_moist) {
|
||||
Response_P(PSTR("{")); // send values to MQTT & rules
|
||||
SEESAW_SOILJson(i);
|
||||
ResponseJsonEnd();
|
||||
MqttPublishTeleSensor();
|
||||
}
|
||||
#endif // SEESAW_SOIL_PUBLISH
|
||||
}
|
||||
}
|
||||
|
||||
void SEESAW_SOILShow(bool json) {
|
||||
char temperature[FLOATSZ];
|
||||
char sensor_name[sizeof(SeeSoilName) + 3];
|
||||
|
||||
for (uint32_t i = 0; i < SeeSoilCount; i++) {
|
||||
dtostrfd(SeeSoil[i].temperature, Settings.flag2.temperature_resolution, temperature);
|
||||
SEESAW_SOILName(i, sensor_name, sizeof(sensor_name));
|
||||
if (json) {
|
||||
ResponseAppend_P(PSTR(",")); // compose tele json
|
||||
SEESAW_SOILJson(i);
|
||||
if (0 == TasmotaGlobal.tele_period) {
|
||||
#ifdef USE_DOMOTICZ
|
||||
DomoticzTempHumPressureSensor(SeeSoil[i].temperature, CAP_TO_MOIST(SeeSoil[i].capacitance)*100, -42.0f);
|
||||
#endif // USE_DOMOTICZ
|
||||
#ifdef USE_KNX
|
||||
KnxSensor(KNX_TEMPERATURE, SeeSoil[i].temperature);
|
||||
KnxSensor(KNX_HUMIDITY, CAP_TO_MOIST(SeeSoil[i].capacitance) * 100);
|
||||
#endif // USE_KNX
|
||||
}
|
||||
#ifdef USE_WEBSERVER
|
||||
} else {
|
||||
#ifdef SEESAW_SOIL_RAW
|
||||
WSContentSend_PD(HTTP_SNS_ANALOG, sensor_name, 0, SeeSoil[i].capacitance); // dump raw value
|
||||
#endif // SEESAW_SOIL_RAW
|
||||
WSContentSend_PD(HTTP_SNS_MOISTURE, sensor_name,
|
||||
uint32_t (CAP_TO_MOIST(SeeSoil[i].capacitance)*100)); // web page formats as integer (%d) percent
|
||||
WSContentSend_PD(HTTP_SNS_TEMP, sensor_name, temperature, TempUnit());
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
} // for each sensor connected
|
||||
}
|
||||
|
||||
void SEESAW_SOILJson(int no) { // common json
|
||||
char temperature[FLOATSZ];
|
||||
char sensor_name[sizeof(SeeSoilName) + 3];
|
||||
|
||||
SEESAW_SOILName(no, sensor_name, sizeof(sensor_name));
|
||||
dtostrfd(SeeSoil[no].temperature, Settings.flag2.temperature_resolution, temperature);
|
||||
ResponseAppend_P(PSTR ("\"%s\":{\"" D_JSON_ID "\":\"%02X\",\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_MOISTURE "\":%u}"),
|
||||
sensor_name, SeeSoil[no].address, temperature, uint32_t (CAP_TO_MOIST(SeeSoil[no].capacitance)*100));
|
||||
}
|
||||
|
||||
void SEESAW_SOILName(int no, char *name, int len) // generates a sensor name
|
||||
{
|
||||
if (SeeSoilCount > 1) {
|
||||
snprintf_P(name, len, PSTR("%s%c%u"), SeeSoilName, IndexSeparator(), no + 1);
|
||||
}
|
||||
else {
|
||||
strlcpy(name, SeeSoilName, len);
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xsns81(uint8_t function)
|
||||
{
|
||||
if (!I2cEnabled(XI2C_56)) { return false; }
|
||||
bool result = false;
|
||||
|
||||
if (FUNC_INIT == function) {
|
||||
SEESAW_SOILDetect();
|
||||
}
|
||||
else if (SeeSoilCount){
|
||||
switch (function) {
|
||||
case FUNC_EVERY_SECOND:
|
||||
SEESAW_SOILEverySecond();
|
||||
break;
|
||||
case FUNC_JSON_APPEND:
|
||||
SEESAW_SOILShow(1);
|
||||
break;
|
||||
#ifdef USE_WEBSERVER
|
||||
case FUNC_WEB_SENSOR:
|
||||
SEESAW_SOILShow(0);
|
||||
break;
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_SEESAW_SOIL
|
||||
#endif // USE_I2C
|
Loading…
Reference in New Issue