Commit 2fa18a4e authored by Patrick's avatar Patrick

inital commit

parents
Red-Peg
-------
Arduino library for communication with the [red-peg shield](https://git.defproc.co.uk/ea/red-peg).
# Functions
To use the library, include it and create the constructor with a name that you
can remember.
```
#include <red_peg.h>
red_peg RP;
```
To use the library, call the `begin` function during `setup()`:
```
RP.begin();
```
This allows a new variable type that stores all the received data from the
red-peg slave controller: `t_SensorData`
And the functions available are:
* `t_SensorData returnedValue = RP.get(SENSOR_NAME);`
* `RP.sensorsOn()`
* `RP.sensorsOff()`
A set of helper functions will work on the `returnedValue` data to give more
useful results:
* `RP.print_data(returnedValue)`
* `RP.degC(returnedValue)`
* `RP.volts(returnedValue)`
* `RP.mA(returnedValue)`
* `RP.level(returnedValue, max_level)`
#include "Wire.h"
#include "MCP342x.h"
#include "Arduino.h"
// Assuming a 100kHz clock the address and config byte take 18 clock
// cycles, or 180 microseconds. Use a timeout of 250us to be safe.
const MCP342x::Channel MCP342x::channel1 = Channel(0x00);
const MCP342x::Channel MCP342x::channel2 = Channel(0x20);
const MCP342x::Channel MCP342x::channel3 = Channel(0x40);
const MCP342x::Channel MCP342x::channel4 = Channel(0x60);
const MCP342x::Mode MCP342x::oneShot = Mode(0x00);
const MCP342x::Mode MCP342x::continous = Mode(0x10);
const MCP342x::Resolution MCP342x::resolution12 = Resolution(0x00);
const MCP342x::Resolution MCP342x::resolution14 = Resolution(0x04);
const MCP342x::Resolution MCP342x::resolution16 = Resolution(0x08);
const MCP342x::Resolution MCP342x::resolution18 = Resolution(0x0c);
const MCP342x::Gain MCP342x::gain1 = Gain(0x00);
const MCP342x::Gain MCP342x::gain2 = Gain(0x01);
const MCP342x::Gain MCP342x::gain4 = Gain(0x02);
const MCP342x::Gain MCP342x::gain8 = Gain(0x03);
uint8_t MCP342x::generalCallReset(void)
{
Wire.beginTransmission(0x00);
Wire.write(0x06);
return Wire.endTransmission();
}
uint8_t MCP342x::generalCallLatch(void)
{
Wire.beginTransmission(0x00);
Wire.write(0x04);
return Wire.endTransmission();
}
uint8_t MCP342x::generalCallConversion(void)
{
Wire.beginTransmission(0x00);
Wire.write(0x08);
return Wire.endTransmission();
}
void MCP342x::normalise(long &result, Config config)
{
/* Resolution is 12, 14, 16,or 18; gain is 1, 2, 4, or 8. Shift
* least places necessary such that all possibilities can be
* accounted for:
*
* 18 - resolution + 3 - log2(gain)
*
* Largest shift is for resolution==12 and gain==1 (9 places)
* Smallest is for resolution==18 and gain==8 (0 places) This means
* that the lowest 21 bits of the long result are used and that up
* to 1024 results can be safely accumulated without
* underflow/overflow.
*/
result <<= (21 - int(config.getResolution()) - config.getGain().log2());
}
MCP342x::MCP342x(void) : address(0x68)
{
;
}
MCP342x::MCP342x(uint8_t add) : address(add)
{
;
}
bool MCP342x::autoprobe(const uint8_t *addressList, uint8_t len)
{
for (uint8_t i = 0; i < len; ++i) {
Wire.requestFrom(addressList[i], (uint8_t)1);
if (Wire.available()) {
address = addressList[i];
return true;
}
}
return false;
}
/** Initiate a conversion by writing to the configuration register
*/
MCP342x::error_t MCP342x::convert(Channel channel, Mode mode, Resolution resolution, Gain gain)
{
return convert(Config(channel, mode, resolution, gain));
}
MCP342x::error_t MCP342x::configure(const Config &config) const
{
Wire.beginTransmission(address);
Wire.write(config.val);
if (Wire.endTransmission())
return errorConfigureFailed;
else
return errorNone;
}
MCP342x::error_t MCP342x::convert(const Config &config) const
{
Wire.beginTransmission(address);
Wire.write(config.val | newConversionMask);
if (Wire.endTransmission())
return errorConvertFailed;
else
return errorNone;
}
MCP342x::error_t MCP342x::read(long &result, Config& status) const
{
// Read 4 bytes, the 4th byte will configuration. From that deduce
// if 18 bit conversion. If not use the 3rd byte, as that is the
// most appropriate configuration value (ready may have changed).
const uint8_t len = 4;
uint8_t buffer[len] = {};
Wire.requestFrom(address, len);
if (Wire.available() != len)
return errorReadFailed;
for (uint8_t i = 0; i < len; ++i)
buffer[i] = Wire.read();
uint8_t dataBytes;
if ((buffer[3] & 0x0c) == 0x0c) {
status = Config(buffer[3]); // 18 bit conversion
dataBytes = 3;
}
else {
status = Config(buffer[2]);
dataBytes = 2;
}
if ((status & notReadyMask) != 0)
return errorConversionNotReady;
long signBit = 0; // Location of sign bit
long signExtend = 0; // Bits to be set if sign is set
switch (int(status.getResolution())) {
case 12:
signBit = 0x800;
signExtend = 0xFFFFF000;
break;
case 14:
signBit = 0x2000;
signExtend = 0xFFFFC000;
break;
case 16:
signBit = 0x8000;
signExtend = 0xFFFF0000;
break;
case 18:
signBit = 0x20000;
signExtend = 0xFFFC0000;
break;
}
result = 0;
for (uint8_t i = 0; i < dataBytes; ++i) {
result <<= 8;
result |= (long)buffer[i];
}
// Fill/blank remaining bits
if ((result & signBit) != 0)
result |= signExtend; // Sign bit is set, sign-extend
return errorNone;
}
MCP342x::error_t MCP342x::convertAndRead(Channel channel, Mode mode, Resolution resolution, Gain gain, unsigned long timeout, long &result, Config &status)
{
error_t err = convert(channel, mode, resolution, gain);
if (err != errorNone)
return err;
unsigned long t = micros() + timeout;
unsigned long convTime = resolution.getConversionTime();
if (convTime > 16383) {
// Unreliable (see arduino reference), use delay() instead
convTime /= 1000;
delay(convTime);
}
else
delayMicroseconds(convTime);
do {
err = read(result, status);
if (!err && status.isReady())
return err;
} while (long(micros() - t) >= 0);
return errorReadTimeout;
}
int16_t MCP342x::getAdc(Channel the_channel) {
long adc_reading = 0;
uint16_t timeout = 10000;
MCP342x::Config status;
MCP342x::error_t err = convertAndRead(the_channel, MCP342x::oneShot, MCP342x::resolution16, MCP342x::gain1, timeout, adc_reading, status);
//if (err) {
//return (long)-err;
//} else {
// make sure the reading is positive (!)
//adc_reading = abs(adc_reading);
return (int16_t)adc_reading;
//return errNone;
//}
}
unsigned long MCP342x::Resolution::getConversionTime(void) const
{
switch ((int)(*this)) {
case 12:
return 4167; // 240 SPS
case 14:
return 16667; // 60 SPS
case 16:
return 66667; // 15 SPS
case 18:
return 266667; // 3.75 SPS
}
return 0; // Shouldn't happen
}
unsigned long MCP342x::Config::getConversionTime(void) const
{
return Resolution(val).getConversionTime();
// switch ((int)getResolution()) {
// case 12:
// return 4167; // 240 SPS
// case 14:
// return 16667; // 60 SPS
// case 16:
// return 66667; // 15 SPS
// case 18:
// return 266667; // 3.75 SPS
// }
// return 0; // Shouldn't happen
}
#ifndef MCP342X_h
#define MCP342X_h
#define MCP342X_VERSION "1.0.2"
#define MCP3428 0x6F
#define MCP_ADC1 MCP342x::channel1
#define MCP_ADC2 MCP342x::channel2
#define MCP_ADC3 MCP342x::channel3
#define MCP_ADC4 MCP342x::channel4
class MCP342x {
public:
class Config;
class Channel;
class Mode;
class Resolution;
class Gain;
static const Channel channel1;
static const Channel channel2;
static const Channel channel3;
static const Channel channel4;
static const Mode oneShot;
static const Mode continous;
static const Resolution resolution12;
static const Resolution resolution14;
static const Resolution resolution16;
static const Resolution resolution18;
static const Gain gain1;
static const Gain gain2;
static const Gain gain4;
static const Gain gain8;
static const uint8_t notReadyMask = 0x80;
static const uint8_t newConversionMask = 0x80;
static const uint8_t numChannels = 4;
static const uint8_t maxResolution = 18;
static const uint8_t maxGain = 8;
static const int writeTimeout_us = 250;
enum error_t {
errorNone,
errorConvertFailed,
errorReadFailed,
errorReadTimeout,
errorConversionNotReady,
errorConfigureFailed,
};
static uint8_t generalCallReset(void);
static uint8_t generalCallLatch(void);
static uint8_t generalCallConversion(void);
// Adjust result to account for gain and resolution settings
static void normalise(long &result, Config config);
MCP342x(void);
MCP342x(uint8_t address);
bool autoprobe(const uint8_t *addressList, uint8_t len);
/** Return the I2C address used for communicating with this device.
*/
uint8_t getAddress(void) const {
return address;
}
/** Configure the device. Useful only for generalCallConversion()
*/
error_t configure(const Config &config) const;
/** Instruct the MCP342x device to begin a conversion.
* @param channel The ADC channel, one of channel0, channel1,
* channel2 or channel3. Not all supported devices have 4
* channels.
* @param mode The conversion mode, oneShot or continous.
* @param resolution Number of bits in the result, one of res12,
* res14, res16 or res18. Not all devices support 18-bit resolution.
* @param gain The gain setting of the programmable gain amplifier,
* one of gain1, gain2, gain4 or gain8.
* @return Value indicating error (if any).
*/
error_t convert(Channel channel, Mode mode, Resolution resolution, Gain gain); error_t convert(const Config &config) const;
/** Read the sample value from the MCP342x device.
* @param result The signed result.
* @param config The contents of the configuration register.
* @return Value indicating error (if any).
*/
error_t read(long &result, uint8_t& config) const;
error_t read(long &result, Config& config) const;
/** Instruct the MCP342x device to begin a conversion and block
* until read completes or timed out.
* @param channel The ADC channel, one of channel0, channel1,
* channel2 or channel3. Not all supported devices have 4
* channels.
* @param mode The conversion mode, oneShotMode or continousMode.
* @param resolution Number of bits in the result, one of res12,
* res14, res16 or res18. Not all devices support 18-bit resolution.
* @param gain The gain setting of the programmable gain amplifier,
* one of gain1, gain2, gain4 or gain8.
* @param timeout The time out value in microseconds.
* @param result The signed result.
* @param config The contents of the configuration register.
* @return Value indicating error (if any).
*/
error_t convertAndRead(Channel channel, Mode mode, Resolution resolution, Gain gain, unsigned long timeout, long &result, Config &status);
/* DefProc added functions:
getAdc: read the ADC with 16bit, gain1, timeout 10 seconds
* @param the_channel one of: MCP_ADC(1,2,3,4)
* @return value (if positive) or error number (if negative)
*/
//error_t getAdc(Channel the_channel, long &adc_reading);
int16_t getAdc(Channel the_channel);
private:
uint8_t address;
// For easy readout need to know whether 18 bit mode was selected
// uint8_t config;
};
class MCP342x::Channel {
friend class MCP342x;
friend class MCP342x::Config;
public:
inline operator int(void) const {
return (val >> 5) + 1;
}
private:
inline Channel(uint8_t v) : val(v & 0x60) {
};
uint8_t val;
};
class MCP342x::Mode {
friend class MCP342x;
friend class MCP342x::Config;
public:
//inline operator int(void) const {
//return (val >> 1) + 12;
//}
private:
inline Mode(uint8_t v) : val(v & 0x10) {
};
uint8_t val;
};
class MCP342x::Resolution {
friend class MCP342x;
friend class MCP342x::Config;
public:
inline operator int(void) const {
return (val >> 1) + 12;
}
unsigned long getConversionTime(void) const;
private:
inline Resolution(uint8_t v) : val(v & 0x0c) {
};
uint8_t val;
};
class MCP342x::Gain {
friend class MCP342x;
friend class MCP342x::Config;
public:
inline operator int(void) const {
return (1 << val);
}
inline uint8_t log2(void) const {
return val;
}
private:
inline Gain(uint8_t v) : val(v & 0x03) {
};
uint8_t val;
};
class MCP342x::Config {
friend class MCP342x;
public:
inline Config(void) : val(0) {
};
inline Config(uint8_t v) : val(v) {
};
inline Config(Channel c, Mode m, Resolution r, Gain g) :
val(c.val | m.val | r.val | g.val) {
};
inline Config(uint8_t c, bool continuous, uint8_t r, uint8_t g) :
val((((c-1) & 3) << 5)
| (uint8_t)(continuous ? 0x10 : 0)
| ((((r-12) & 0x1e) << 1) & 0xc)) {
switch(g) {
case 2:
val |= 0x01;
break;
case 4:
val |= 0x02;
break;
case 8:
val |= 0x03;
break;
};
}
inline operator int(void) const {
return val;
}
inline Channel getChannel(void) const {
return Channel(val);
}
inline Resolution getResolution(void) const {
return Resolution(val);
}
inline Gain getGain(void) const {
return Gain(val);
}
inline bool isReady(void) const {
return !(val & notReadyMask);
}
unsigned long getConversionTime(void) const;
private:
uint8_t val;
};
#endif
// Code by JeeLabs http://news.jeelabs.org/code/
// Released to the public domain! Enjoy!
#include <Wire.h>
#include "RTClib.h"
#ifdef __AVR__
#include <avr/pgmspace.h>
#elif defined(ESP8266)
#include <pgmspace.h>
#elif defined(ARDUINO_ARCH_SAMD)
// nothing special needed
#elif defined(ARDUINO_SAM_DUE)
#define PROGMEM
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
#define Wire Wire1
#endif
#define DS1307_ADDRESS 0x68
#define DS1307_CONTROL 0x07
#define DS1307_NVRAM 0x08
#define SECONDS_PER_DAY 86400L
#define SECONDS_FROM_1970_TO_2000 946684800
#if (ARDUINO >= 100)
#include <Arduino.h> // capital A so it is error prone on case-sensitive filesystems
// Macro to deal with the difference in I2C write functions from old and new Arduino versions.
#define _I2C_WRITE write
#define _I2C_READ read
#else
#include <WProgram.h>
#define _I2C_WRITE send
#define _I2C_READ receive
#endif
////////////////////////////////////////////////////////////////////////////////
// utility code, some of this could be exposed in the DateTime API if needed
const uint8_t daysInMonth [] PROGMEM = { 31,28,31,30,31,30,31,31,30,31,30,31 };
// number of days since 2000/01/01, valid for 2001..2099
static uint16_t date2days(uint16_t y, uint8_t m, uint8_t d) {
if (y >= 2000)
y -= 2000;
uint16_t days = d;
for (uint8_t i = 1; i < m; ++i)
days += pgm_read_byte(daysInMonth + i - 1);
if (m > 2 && y % 4 == 0)
++days;
return days + 365 * y + (y + 3) / 4 - 1;
}
static long time2long(uint16_t days, uint8_t h, uint8_t m, uint8_t s) {
return ((days * 24L + h) * 60 + m) * 60 + s;
}
////////////////////////////////////////////////////////////////////////////////
// DateTime implementation - ignores time zones and DST changes
// NOTE: also ignores leap seconds, see http://en.wikipedia.org/wiki/Leap_second
DateTime::DateTime (uint32_t t) {
t -= SECONDS_FROM_1970_TO_2000; // bring to 2000 timestamp from 1970
ss = t % 60;
t /= 60;
mm = t % 60;
t /= 60;
hh = t % 24;
uint16_t days = t / 24;
uint8_t leap;
for (yOff = 0; ; ++yOff) {
leap = yOff % 4 == 0;
if (days < 365 + leap)
break;
days -= 365 + leap;
}
for (m = 1; ; ++m) {
uint8_t daysPerMonth = pgm_read_byte(daysInMonth + m - 1);
if (leap && m == 2)
++daysPerMonth;
if (days < daysPerMonth)
break;
days -= daysPerMonth;
}
d = days + 1;
}
DateTime::DateTime (uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t min, uint8_t sec) {
if (year >= 2000)
year -= 2000;
yOff = year;
m = month;
d = day;
hh = hour;
mm = min;
ss = sec;
}
DateTime::DateTime (const DateTime& copy):
yOff(copy.yOff),
m(copy.m),
d(copy.d),
hh(copy.hh),
mm(copy.mm),
ss(copy.ss)
{}
static uint8_t conv2d(const char* p) {
uint8_t v = 0;
if ('0' <= *p && *p <= '9')
v = *p - '0';
return 10 * v + *++p - '0';
}
// A convenient constructor for using "the compiler's time":
// DateTime now (__DATE__, __TIME__);
// NOTE: using F() would further reduce the RAM footprint, see below.
DateTime::DateTime (const char* date, const char* time) {
// sample input: date = "Dec 26 2009", time = "12:34:56"
yOff = conv2d(date + 9);
// Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
switch (date[0]) {
case 'J': m = date[1] == 'a' ? 1 : m = date[2] == 'n' ? 6 : 7; break;
case 'F': m = 2; break;
case 'A': m = date[2] == 'r' ? 4 : 8; break;
case 'M': m = date[2] == 'r' ? 3 : 5; break;
case 'S': m = 9; break;
case 'O': m = 10; break;
case 'N': m = 11; break;
case 'D': m = 12; break;
}
d = conv2d(date + 4);
hh = conv2d(time);
mm = conv2d(time + 3);
ss = conv2d(time + 6);
}
// A convenient constructor for using "the compiler's time":
// This version will save RAM by using PROGMEM to store it by using the F macro.
// DateTime now (F(__DATE__), F(__TIME__));
DateTime::DateTime (const __FlashStringHelper* date, const __FlashStringHelper* time) {
// sample input: date = "Dec 26 2009", time = "12:34:56"
char buff[11];
memcpy_P(buff, date, 11);
yOff = conv2d(buff + 9);
// Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
switch (buff[0]) {
case 'J': m = buff[1] == 'a' ? 1 : m = buff[2] == 'n' ? 6 : 7; break;
case 'F': m = 2; break;
case 'A': m = buff[2] == 'r' ? 4 : 8; break;
case 'M': m = buff[2] == 'r' ? 3 : 5; break;
case 'S': m = 9; break;
case 'O': m = 10; break;
case 'N': m = 11; break;
case 'D': m = 12; break;
}
d = conv2d(buff + 4);
memcpy_P(buff, time, 8);
hh = conv2d(buff);
mm = conv2d(buff + 3);
ss = conv2d(buff + 6);
}
uint8_t DateTime::dayOfTheWeek() const {
uint16_t day = date2days(yOff, m, d);
return (day + 6) % 7; // Jan 1, 2000 is a Saturday, i.e. returns 6
}
uint32_t DateTime::unixtime(void) const {
uint32_t t;
uint16_t days = date2days(yOff, m, d);
t = time2long(days, hh, mm, ss);
t += SECONDS_FROM_1970_TO_2000; // seconds from 1970 to 2000
return t;
}
long DateTime::secondstime(void) const {
long t;
uint16_t days = date2days(yOff, m, d);
t = time2long(days, hh, mm, ss);
return t;
}
DateTime DateTime::operator+(const TimeSpan& span) {
return DateTime(unixtime()+span.totalseconds());
}
DateTime DateTime::operator-(const TimeSpan& span) {
return DateTime(unixtime()-span.totalseconds());
}