Commit 2fa18a4e authored by Patrick Fenner's avatar Patrick Fenner
Browse files

inital commit

parents
No related merge requests found
Showing with 1641 additions and 0 deletions
+1641 -0
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());
}
TimeSpan DateTime::operator-(const DateTime& right) {
return TimeSpan(unixtime()-right.unixtime());
}
////////////////////////////////////////////////////////////////////////////////
// TimeSpan implementation
TimeSpan::TimeSpan (int32_t seconds):
_seconds(seconds)
{}
TimeSpan::TimeSpan (int16_t days, int8_t hours, int8_t minutes, int8_t seconds):
_seconds((int32_t)days*86400L + (int32_t)hours*3600 + (int32_t)minutes*60 + seconds)
{}
TimeSpan::TimeSpan (const TimeSpan& copy):
_seconds(copy._seconds)
{}
TimeSpan TimeSpan::operator+(const TimeSpan& right) {
return TimeSpan(_seconds+right._seconds);
}
TimeSpan TimeSpan::operator-(const TimeSpan& right) {
return TimeSpan(_seconds-right._seconds);
}
////////////////////////////////////////////////////////////////////////////////
// RTC_DS1307 implementation
static uint8_t bcd2bin (uint8_t val) { return val - 6 * (val >> 4); }
static uint8_t bin2bcd (uint8_t val) { return val + 6 * (val / 10); }
boolean RTC_DS1307::begin(void) {
Wire.begin();
return true;
}
uint8_t RTC_DS1307::isrunning(void) {
Wire.beginTransmission(DS1307_ADDRESS);
Wire._I2C_WRITE((byte)0);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDRESS, 1);
uint8_t ss = Wire._I2C_READ();
return !(ss>>7);
}
void RTC_DS1307::adjust(const DateTime& dt) {
Wire.beginTransmission(DS1307_ADDRESS);
Wire._I2C_WRITE((byte)0); // start at location 0
Wire._I2C_WRITE(bin2bcd(dt.second()));
Wire._I2C_WRITE(bin2bcd(dt.minute()));
Wire._I2C_WRITE(bin2bcd(dt.hour()));
Wire._I2C_WRITE(bin2bcd(0));
Wire._I2C_WRITE(bin2bcd(dt.day()));
Wire._I2C_WRITE(bin2bcd(dt.month()));
Wire._I2C_WRITE(bin2bcd(dt.year() - 2000));
Wire.endTransmission();
}
DateTime RTC_DS1307::now() {
Wire.beginTransmission(DS1307_ADDRESS);
Wire._I2C_WRITE((byte)0);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDRESS, 7);
uint8_t ss = bcd2bin(Wire._I2C_READ() & 0x7F);
uint8_t mm = bcd2bin(Wire._I2C_READ());
uint8_t hh = bcd2bin(Wire._I2C_READ());
Wire._I2C_READ();
uint8_t d = bcd2bin(Wire._I2C_READ());
uint8_t m = bcd2bin(Wire._I2C_READ());
uint16_t y = bcd2bin(Wire._I2C_READ()) + 2000;
return DateTime (y, m, d, hh, mm, ss);
}
Ds1307SqwPinMode RTC_DS1307::readSqwPinMode() {
int mode;
Wire.beginTransmission(DS1307_ADDRESS);
Wire._I2C_WRITE(DS1307_CONTROL);
Wire.endTransmission();
Wire.requestFrom((uint8_t)DS1307_ADDRESS, (uint8_t)1);
mode = Wire._I2C_READ();
mode &= 0x93;
return static_cast<Ds1307SqwPinMode>(mode);
}
void RTC_DS1307::writeSqwPinMode(Ds1307SqwPinMode mode) {
Wire.beginTransmission(DS1307_ADDRESS);
Wire._I2C_WRITE(DS1307_CONTROL);
Wire._I2C_WRITE(mode);
Wire.endTransmission();
}
void RTC_DS1307::readnvram(uint8_t* buf, uint8_t size, uint8_t address) {
int addrByte = DS1307_NVRAM + address;
Wire.beginTransmission(DS1307_ADDRESS);
Wire._I2C_WRITE(addrByte);
Wire.endTransmission();
Wire.requestFrom((uint8_t) DS1307_ADDRESS, size);
for (uint8_t pos = 0; pos < size; ++pos) {
buf[pos] = Wire._I2C_READ();
}
}
void RTC_DS1307::writenvram(uint8_t address, uint8_t* buf, uint8_t size) {
int addrByte = DS1307_NVRAM + address;
Wire.beginTransmission(DS1307_ADDRESS);
Wire._I2C_WRITE(addrByte);
for (uint8_t pos = 0; pos < size; ++pos) {
Wire._I2C_WRITE(buf[pos]);
}
Wire.endTransmission();
}
uint8_t RTC_DS1307::readnvram(uint8_t address) {
uint8_t data;
readnvram(&data, 1, address);
return data;
}
void RTC_DS1307::writenvram(uint8_t address, uint8_t data) {
writenvram(address, &data, 1);
}
////////////////////////////////////////////////////////////////////////////////
// RTC_Millis implementation
long RTC_Millis::offset = 0;
void RTC_Millis::adjust(const DateTime& dt) {
offset = dt.unixtime() - millis() / 1000;
}
DateTime RTC_Millis::now() {
return (uint32_t)(offset + millis() / 1000);
}
////////////////////////////////////////////////////////////////////////////////
// Code by JeeLabs http://news.jeelabs.org/code/
// Released to the public domain! Enjoy!
#ifndef _RTCLIB_H_
#define _RTCLIB_H_
#include <Arduino.h>
class TimeSpan;
// Simple general-purpose date/time class (no TZ / DST / leap second handling!)
class DateTime {
public:
DateTime (uint32_t t =0);
DateTime (uint16_t year, uint8_t month, uint8_t day,
uint8_t hour =0, uint8_t min =0, uint8_t sec =0);
DateTime (const DateTime& copy);
DateTime (const char* date, const char* time);
DateTime (const __FlashStringHelper* date, const __FlashStringHelper* time);
uint16_t year() const { return 2000 + yOff; }
uint8_t month() const { return m; }
uint8_t day() const { return d; }
uint8_t hour() const { return hh; }
uint8_t minute() const { return mm; }
uint8_t second() const { return ss; }
uint8_t dayOfTheWeek() const;
// 32-bit times as seconds since 1/1/2000
long secondstime() const;
// 32-bit times as seconds since 1/1/1970
uint32_t unixtime(void) const;
DateTime operator+(const TimeSpan& span);
DateTime operator-(const TimeSpan& span);
TimeSpan operator-(const DateTime& right);
protected:
uint8_t yOff, m, d, hh, mm, ss;
};
// Timespan which can represent changes in time with seconds accuracy.
class TimeSpan {
public:
TimeSpan (int32_t seconds = 0);
TimeSpan (int16_t days, int8_t hours, int8_t minutes, int8_t seconds);
TimeSpan (const TimeSpan& copy);
int16_t days() const { return _seconds / 86400L; }
int8_t hours() const { return _seconds / 3600 % 24; }
int8_t minutes() const { return _seconds / 60 % 60; }
int8_t seconds() const { return _seconds % 60; }
int32_t totalseconds() const { return _seconds; }
TimeSpan operator+(const TimeSpan& right);
TimeSpan operator-(const TimeSpan& right);
protected:
int32_t _seconds;
};
// RTC based on the DS1307 chip connected via I2C and the Wire library
enum Ds1307SqwPinMode { OFF = 0x00, ON = 0x80, SquareWave1HZ = 0x10, SquareWave4kHz = 0x11, SquareWave8kHz = 0x12, SquareWave32kHz = 0x13 };
class RTC_DS1307 {
public:
boolean begin(void);
static void adjust(const DateTime& dt);
uint8_t isrunning(void);
static DateTime now();
static Ds1307SqwPinMode readSqwPinMode();
static void writeSqwPinMode(Ds1307SqwPinMode mode);
uint8_t readnvram(uint8_t address);
void readnvram(uint8_t* buf, uint8_t size, uint8_t address);
void writenvram(uint8_t address, uint8_t data);
void writenvram(uint8_t address, uint8_t* buf, uint8_t size);
};
// RTC using the internal millis() clock, has to be initialized before use
// NOTE: this clock won't be correct once the millis() timer rolls over (>49d?)
class RTC_Millis {
public:
static void begin(const DateTime& dt) { adjust(dt); }
static void adjust(const DateTime& dt);
static DateTime now();
protected:
static long offset;
};
#endif // _RTCLIB_H_
This diff is collapsed.
/* ======================== Arduino SDI-12 =================================
Arduino library for SDI-12 communications to a wide variety of environmental
sensors. This library provides a general software solution, without requiring
any additional hardware.
======================== Attribution & License =============================
Copyright (C) 2013 Stroud Water Research Center
Available at https://github.com/StroudCenter/Arduino-SDI-12
Authored initially in August 2013 by:
Kevin M. Smith (http://ethosengineering.org)
Inquiries: SDI12@ethosengineering.org
based on the SoftwareSerial library (formerly NewSoftSerial), authored by:
ladyada (http://ladyada.net)
Mikal Hart (http://www.arduiniana.org)
Paul Stoffregen (http://www.pjrc.com)
Garrett Mace (http://www.macetech.com)
Brett Hagman (http://www.roguerobotics.com/)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SDI12_h
#define SDI12_h
// Import Required Libraries
#include <avr/interrupt.h> // interrupt handling
#include <avr/parity.h> // optimized parity bit handling
#include <inttypes.h> // integer types library
#include <Arduino.h> // Arduino core library
class SDI12
{
private:
static SDI12 *_activeObject; // static pointer to active SDI12 instance
void setState(uint8_t state); // sets the state of the SDI12 objects
void wakeSensors(); // used to wake up the SDI12 bus
void writeChar(uint8_t out); // used to send a char out on the data line
void receiveChar(); // used by the ISR to grab a char from data line
public:
SDI12(uint8_t dataPin); // constructor
~SDI12(); // destructor
void begin(); // enable SDI-12 object
void end(); // disable SDI-12 object
void forceHold(); // sets line state to HOLDING
void sendCommand(String cmd); // sends the String cmd out on the data line
int available(); // returns the number of bytes available in buffer
int peek(); // reveals next byte in buffer without consuming
int read(); // returns next byte in the buffer (consumes)
void flush(); // clears the buffer
bool setActive(); // set this instance as the active SDI-12 instance
bool isActive(); // check if this instance is active
static inline void handleInterrupt(); // intermediary used by the ISR
};
#endif
#include <inttypes.h>
#include "Arduino.h"
#include "red_peg.h"
// master only functions
void red_peg::begin(uint8_t ss_pin, bool report_serial)
{
// set the slave select pin
_ss_pin = ss_pin;
pinMode(_ss_pin, OUTPUT);
digitalWrite(_ss_pin, HIGH);
SPI.begin();
SPI.setClockDivider(SPI_CLOCK_DIV8);
if (report_serial == true) {
Serial.begin(BAUD);
}
}
t_SensorData red_peg::ask(sensor_e request, uint8_t _y, uint8_t _m, uint8_t _d, uint8_t _hh, uint8_t _mm, uint8_t _ss, uint16_t _the_reading) //, float _the_data)
{
// create the temporary buffers for send/recieve
t_SensorData buffer;
uint8_t* outgoing = (uint8_t*)&buffer;
t_SensorData response;
uint8_t* incoming = (uint8_t*)&response;
// request a specific sensor's data
buffer.the_command = request;
// load the rest of the buffer with nothings
buffer.y= _y;
buffer.m = _m;
buffer.d = _d;
buffer.hh = _hh;
buffer.mm = _mm;
buffer.ss = _ss;
buffer.the_reading = _the_reading;
//buffer.the_data = _the_data;
// send the buffer and receive the incoming
digitalWrite(_ss_pin, LOW);
delay(TRANSFER_DELAY);
for (uint8_t i=0; i<size_t_SensorData; i++) {
incoming[i] = SPI.transfer(outgoing[i]);
delayMicroseconds(TRANSFER_DELAY);
}
digitalWrite(_ss_pin, HIGH);
// and reply with the return
return response;
}
t_SensorData red_peg::get(sensor_e request)
{
// wrapper to ask and check for a valid reply
// first, send a request but assume no valid reply
t_SensorData incoming = ask(request);
// then send some empty asks until we get a better return
// or we exceed the expected buffer length
delay(PROCESSING_DELAY*2);
//delay(100);
for (uint8_t i=0; i<BUFFER_LEN+1; i++) {
// send an empty get
incoming = ask();
if (incoming.the_sensor == request) {
return incoming;
}
delay(PROCESSING_DELAY);
}
// otherwise indicate an empty response
//Serial.print(F("no valid return"));
incoming.the_sensor = EMPTY;
return incoming;
}
void red_peg::print_data(t_SensorData data_record)
{
// helper function to write out the data to serial
Serial.print("0x");
Serial.print(data_record.start_byte, HEX);
Serial.print(F(","));
Serial.print(sensor_message(data_record.the_sensor));
Serial.write(',');
Serial.print(data_record.y);
Serial.write('-');
Serial.print(data_record.m);
Serial.write('-');
Serial.print(data_record.d);
Serial.write('T');
Serial.print(data_record.hh);
Serial.write(':');
Serial.print(data_record.mm);
Serial.write(':');
Serial.print(data_record.ss);
Serial.write('Z');
Serial.write(',');
Serial.print(data_record.the_reading);
//Serial.write(',');
//Serial.print(data_record.the_data);
Serial.print(",0x");
Serial.print(data_record.end_byte, HEX);
Serial.println();
}
char* red_peg::sensor_message(sensor_e request)
{
if (request == OK) {
return "OK";
} else if (request == AVAILABLE) {
return "AVAILABLE";
} else if (request == EMPTY) {
return "EMPTY";
} else if (request == RTC) {
return "RTC";
} else if (request == ADC1) {
return "ADC1";
} else if (request == ADC2) {
return "ADC2";
} else if (request == ADC3) {
return "ADC3";
} else if (request == SDI12) {
return "SDI12";
} else {
return "UNKNOWN";
}
}
void red_peg::sensorsOn()
{
// setup the power control pin
pinMode(SENSOR_ACTIVE_PIN, OUTPUT);
// turn it on
digitalWrite(SENSOR_ACTIVE_PIN, HIGH);
}
void red_peg::sensorsOff()
{
// turn it off
digitalWrite(SENSOR_ACTIVE_PIN, LOW);
// switch to low impedance
pinMode(SENSOR_ACTIVE_PIN, INPUT);
}
float red_peg::degC(t_SensorData data_record)
{
if (data_record.the_sensor == TMP) {
return float( ((data_record.the_reading * (2.048/32767.0)) - 0.5) * 100.0 );
} else {
return -1.0;
}
}
float red_peg::volts(t_SensorData data_record)
{
if (data_record.the_sensor == ADC1 || data_record.the_sensor == ADC3) {
return float( data_record.the_reading * (2.048/32767.0) );
} else if (data_record.the_sensor == ADC2) {
return float( data_record.the_reading * (5.0/32767.0) );
} else {
return -1.0;
}
}
float red_peg::mA(t_SensorData data_record)
{
if (data_record.the_sensor == MA4_20) {
return float( data_record.the_reading * 20.48 / 32787.0 );
} else {
return -1.0;
}
}
float red_peg::level(t_SensorData data_record, double max_level)
{
float _level = float(max_level);
return level(data_record, _level);
}
float red_peg::level(t_SensorData data_record, float max_level)
{
if (data_record.the_sensor == MA4_20) {
long zero_reading = 6515L;
// subtract the
long reading = long(data_record.the_reading) - 6515L;
float level = float(reading) * ( max_level / (26212.0));
return level;
} else {
return -max_level;
}
}
long red_peg::level(t_SensorData data_record, int max_level)
{
long _level = long(max_level);
return level(data_record, _level);
}
long red_peg::level(t_SensorData data_record, long max_level)
{
if (data_record.the_sensor == MA4_20) {
long zero_reading = 6515L;
// subtract the
long reading = long(data_record.the_reading) - 6515L;
long level = map(reading, 0, 26212, 0, max_level);
return level;
} else {
return -max_level;
}
}
float red_peg::distance(t_SensorData data_record)
{
return -1.0;
}
/* Red-Peg master and slave library
allows easier (smaller overhead) reading of red-peg
sensors by the attached microcontroller board, using
the onboard ATmega328 on the red-peg shield
*/
#ifndef red_peg_h
#define red_peg_h
#include <SPI.h>
#include <Wire.h>
// set the slave's buffer lengths
#define BUFFER_LEN 5
// set the red-peg filename format
#define FILENAME_DAT "RP%06u.dat"
// control pin definitions
#define SENSOR_ACTIVE_PIN 6
#define SD_SS 5
// set the default baud rate
#ifndef BAUD
#define BAUD 115200
#endif
#define START_BYTE 0xFF
#define END_BYTE 0x00
#define TRANSFER_DELAY 25 // µs spi inter-byte delay
#define PROCESSING_DELAY 100 // no. ms to allow for the slave to process the request
// possible inputs for the_command or the_sensor
enum sensor_e {EMPTY, AVAILABLE, OK, RTC, ADC1, ADC2, ADC3, SDI12};
// and some aliases relavent for the red-peg shield
#define MA4_20 ADC1
#define ANA ADC2
#define TMP ADC3
typedef struct{
uint8_t start_byte = START_BYTE;
union {
// aliases for the sensor/command variable
sensor_e the_sensor;
sensor_e sensor;
sensor_e the_command;
sensor_e command;
};
uint16_t y;
uint8_t m, d, hh, mm, ss; //the timestamp in DateTime-ready format
union {
// some aliases for the data reading
int16_t the_reading;
int16_t reading;
int16_t value;
int16_t integer;
};
//float the_data; // replaced with functions
uint8_t end_byte = END_BYTE;
} t_SensorData;
class red_peg
{
private:
volatile uint8_t _tx_buffer_head;
volatile uint8_t _tx_buffer_tail;
volatile uint8_t _rx_buffer_head;
volatile uint8_t _rx_buffer_tail;
uint8_t _ss_pin;
uint8_t _get_timeout = 4;
void printHex(byte* data, int data_len);
public:
const size_t size_t_SensorData = sizeof(t_SensorData);
// set up the red-peg library requirements (call during setup())
void begin(uint8_t ss_pin = 8, bool report_serial = true);
// use RP.ask(REQUEST) to retrieve the value from the requested sensor
t_SensorData ask(sensor_e request = EMPTY, uint8_t _y = END_BYTE, uint8_t _m = END_BYTE, uint8_t _d = END_BYTE, uint8_t _hh = END_BYTE, uint8_t _mm = END_BYTE, uint8_t _ss = END_BYTE, uint16_t _the_reading = END_BYTE); //, float _the_data = 0.0);
// use RP.get(REQUEST) to queue a request for a reading
// use RP.get(EMPTY) until you get the response (up to BUFFER_LEN+1 times)
t_SensorData get(sensor_e request);
// prints the data_record to Serial
void print_data(t_SensorData data_record);
// Serial.print the human readable version of request
char* sensor_message(sensor_e request); // return a human readable version of the enum
// sensor control
void sensorsOn(); // turn the external sensors on
void sensorsOff(); // turn the external sensors off
// Helper functions for calculating values
float degC(t_SensorData data_record); // TMP26 reading in degrees C
float volts(t_SensorData data_record); // Analog voltage reading for ADC1/2/3
float mA(t_SensorData data_record); // current conversion for 4-20mA sensor
// level sensor conversion using either integer or float
long level(t_SensorData data_record, int max_level);
long level(t_SensorData data_record, long max_level);
float level(t_SensorData data_record, double max_level);
float level(t_SensorData data_record, float max_level);
// TODO:
float distance(t_SensorData data_record); // Ultrasonic distance reading
};
#endif // red_peg_h
File added
#include <inttypes.h>
#include "Arduino.h"
#include "red_peg_slave.h"
// slave only functions
void red_peg_slave::begin(uint8_t ss_pin)
{
// set the slave select pin
_ss_pin = ss_pin;
pinMode(_ss_pin, INPUT);
// turn on SPI in slave mode
SPCR |= bit (SPE);
// prep the first byte in the SPI data record buffer
SPDR = (uint8_t)_data_buff[(_data_tail_index*size_t_SensorData)+_data_record_index];
// find out the current SS state
pinMode(SS_SLAVE_PIN, INPUT);
handle_interrupt();
// now turn on interrupts
SPI.attachInterrupt();
// make sure the buffers start empty
for (uint8_t i=0; i<sizeof(_command_buff); i++) {
_command_buff[i] = END_BYTE;
_data_buff[i] = END_BYTE;
}
}
void red_peg_slave::handle_interrupt()
{
// check which way the MISO pin should be set
uint8_t current_ss_state = digitalRead(SS_SLAVE_PIN);
if (current_ss_state != _ss_pin_state) {
if (current_ss_state == LOW) {
// activate MISO as output, only if our SS pin is low (active)
pinMode(MISO, OUTPUT);
_ss_pin_state = LOW;
new_command_line();
//Serial.println(F("SS LOW"));
} else {
pinMode(MISO, INPUT);
_ss_pin_state = HIGH;
//Serial.println(F("SS HIGH"));
}
}
}
void red_peg_slave::new_command_line()
{
// we've started a new line,
// so…
// set to the start of the next empty line in the command buffer
if ((_command_head_index+1)%BUFFER_LEN != _command_tail_index) {
_command_head_index = (_command_head_index+1) % BUFFER_LEN;
// or repeat the same line if there's no space in the buffer
}
// and set the record index to the beginning
_command_record_index = 0;
// if we're at the data buffer head
if (_data_tail_index != _data_head_index) {
// increment to the next line
_data_tail_index = (_data_tail_index+1) % BUFFER_LEN;
// we'll just repeat the head if there's no more data
}
// set the _data_record_index to zero
_data_record_index = 0;
// put the byte into the SPI buffer ready for transmission
//SPDR = byte(_data_buff[(_data_tail_index*size_t_SensorData)+_data_record_index]);
SPDR = _data_buff[(_data_tail_index*size_t_SensorData)+_data_record_index];
}
bool red_peg_slave::spiComplete()
{
uint8_t current_state = digitalRead(_ss_pin);
if(current_state == HIGH) {
return true;
} else {
return false;
}
}
int8_t red_peg_slave::available()
{
int8_t num = 0;
// take the item from the incoming buffer and parse for the action
uint8_t tail_position = _command_tail_index;
if (spiComplete()) {
while (tail_position != _command_head_index) {
tail_position = (tail_position + 1) % BUFFER_LEN;
num++;
}
}
return num;
}
t_SensorData red_peg_slave::read()
{
t_SensorData buffer;
if (_command_tail_index != _command_head_index) {
// make a peek
a_peek(&buffer);
// and then increment the buffer
_command_tail_index = (_command_tail_index + 1) % BUFFER_LEN;
// and return the result
}
return buffer;
}
t_SensorData red_peg_slave::peek()
{
t_SensorData buffer;
if (_command_tail_index != _command_head_index) {
// make a peek
a_peek(&buffer);
}
return buffer;
}
void red_peg_slave::a_peek(t_SensorData *sensor_data)
{
// take the next reading
uint8_t next_record = (_command_tail_index + 1) % BUFFER_LEN;
*sensor_data = _command_buffer[next_record];
}
int8_t red_peg_slave::write(t_SensorData return_data)
{
// increment the _data_buffer
uint8_t next_record = (_data_head_index + 1) % BUFFER_LEN;
// check there's space on the buffer to add that data
//TODO: this should probably be replaced by a memmove()
if (next_record != _data_tail_index) {
_data_buffer[next_record].the_sensor = return_data.the_sensor;
_data_buffer[next_record].y= return_data.y;
_data_buffer[next_record].m = return_data.m;
_data_buffer[next_record].d = return_data.d;
_data_buffer[next_record].hh = return_data.hh;
_data_buffer[next_record].mm = return_data.mm;
_data_buffer[next_record].ss = return_data.ss;
_data_buffer[next_record].the_reading = return_data.the_reading;
//_data_buffer[next_record].the_data = return_data.the_data;
_data_head_index = next_record;
return true; // ok
} else {
return false; // buffer is already full
}
}
void red_peg_slave::spi_stc_isr()
{
// we just received a byte
_command_buff[(_command_head_index*size_t_SensorData)+_command_record_index] = SPDR;
// increment the _command_record_index
_command_record_index++;
if (_command_record_index >= size_t_SensorData) {
// reset to beginning if there's too much data sent
_command_record_index = 0;
}
// increment the _data_record_index
_data_record_index = (_data_record_index+1) % size_t_SensorData;
if (_data_record_index >= size_t_SensorData) {
// we've rolled over to the next one, ignore the rest,
// the sender needs to toggle the SS for the next data line
SPDR = END_BYTE;
} else {
//SPDR = byte(_data_buff[(_data_tail_index*size_t_SensorData)+_data_record_index]);
SPDR = (uint8_t)_data_buff[(_data_tail_index*size_t_SensorData)+_data_record_index];
}
}
void red_peg_slave::print_data(t_SensorData data_record)
{
// helper function to write out the data to serial
Serial.print("0x");
Serial.print(data_record.start_byte, HEX);
Serial.write(',');
Serial.print(sensor_message(data_record.the_sensor));
Serial.write(',');
Serial.print(data_record.y);
Serial.write('-');
Serial.print(data_record.m);
Serial.write('-');
Serial.print(data_record.d);
Serial.write('T');
Serial.print(data_record.hh);
Serial.write(':');
Serial.print(data_record.mm);
Serial.write(':');
Serial.print(data_record.ss);
Serial.write('Z');
Serial.write(',');
Serial.print(data_record.the_reading);
//Serial.write(',');
//Serial.print(data_record.the_data);
Serial.print(",0x");
Serial.print(data_record.end_byte, HEX);
Serial.println();
}
char* red_peg_slave::sensor_message(sensor_e request)
{
if (request == OK) {
return "OK";
} else if (request == AVAILABLE) {
return "AVAILABLE";
} else if (request == EMPTY) {
return "EMPTY";
} else if (request == RTC) {
return "RTC";
} else if (request == ADC1) {
return "ADC1";
} else if (request == ADC2) {
return "ADC2";
} else if (request == ADC3) {
return "ADC3";
} else if (request == SDI12) {
return "SDI12";
} else {
return "UNKNOWN";
}
}
/* Red-Peg slave library
allows easier (smaller overhead) reading of red-peg
sensors by the attached microcontroller board, using
the onboard ATmega328 on the red-peg shield
*/
#ifndef red_peg_slave_h
#define red_peg_slave_h
#include "red_peg.h"
#include "RTClib.h"
#include "MCP342x.h"
// pin number connections for the slave micro
#define SS_SLAVE_PIN 10
#define SLAVE_SENSOR_ACTIVE_PIN 2
class red_peg_slave
{
private:
volatile uint8_t _command_head_index = 0; // up to BUFFER_LEN
volatile uint8_t _command_tail_index = 0; // up to BUFFER_LEN
volatile uint8_t _command_record_index = 0; // up to size_t_sensorData
volatile uint8_t _data_head_index = 0; // up to BUFFER_LEN
volatile uint8_t _data_tail_index = 0; // up to BUFFER_LEN
volatile uint8_t _data_record_index = 0; // up to size_t_sensorData
//volatile uint32_t _last_received_data = 0; // to know about new transmissions
volatile uint8_t _ss_pin_state = LOW;
uint8_t _ss_pin;
const size_t size_t_SensorData = sizeof(t_SensorData);
// need some buffers to hold the incoming/outgoing data
t_SensorData _command_buffer[BUFFER_LEN];
t_SensorData _data_buffer[BUFFER_LEN];
// and the uint8_t* equivalents for byte-by-byte shifting
uint8_t* _command_buff = (uint8_t*)(&_command_buffer);
uint8_t* _data_buff = (uint8_t*)(&_data_buffer);
void a_peek(t_SensorData *sensor_data);
void new_command_line();
public:
void handle_interrupt(); // reset the buffer positions on SPI SS pin activation
void begin(uint8_t ss_pin = SS_SLAVE_PIN);
void spi_stc_isr(); // for: ISR (SPI_STC_vect)
bool spiComplete(); // check SPI is inactive
int8_t available(); // return the number of available records
t_SensorData read(); // get the next item from the incomming buffer
t_SensorData peek(); // look at the next item in the incoming buffer
int8_t write(t_SensorData return_data); // put a dataset on the send buffer
void print_data(t_SensorData data_record); // serial.print the data record
char* sensor_message(sensor_e request); // return a human readable version of the enum
};
#endif // red_peg_slave_h
File added
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment