Commit 2310b102 authored by Nelso Jost's avatar Nelso Jost

NEW: RTC DS1307 support

parent 94479f04
.venv/
ino/src
ino/lib
*.pyc
*.swp
*.csv
......
......@@ -14,7 +14,7 @@ help:
@ echo ""
@ echo " make upload -- Send the compiled sketch to the board"
@ echo ""
@ echo " make bu -- build & upload"
@ echo " make bus -- build & upload & start serial picocom"
@ echo ""
@ echo " make serial -- Starts a picocom serial communication"
@ echo ""
......@@ -44,17 +44,17 @@ clean:
rm -rf .build src/*
build:
rm -rf src/*
mkdir -p src/ lib/
cp -rf ../meteorolog/. src/
${VENV}/bin/ino build
upload:
${VENV}/bin/ino upload
bu: build upload
bus: build upload serial
serial:
${VENV}/bin/ino serial -- -c
${VENV}/bin/ino serial -- -c -r -l
list-models:
${VENV}/bin/ino list-models
......
......@@ -115,7 +115,7 @@ class Meteorologger:
self.API_POST_URL = self._getCFG('SERVER', 'API_POST_URL',
expected_type=str)
self.SENSORS_CSV_LINE = self.CSV_SEP.join(
self.SENSORS_CSV_LINE = 'read' + self.CSV_SEP + self.CSV_SEP.join(
[d['nickname'] for d in self._getCFG('SENSORS',
expected_type=list)])
......
......@@ -7,7 +7,7 @@ SERVER:
LOGGER:
# time between readings attempts (cycles of the logger execution)
#
READING_INTERVAL: {days: 0, hours:0, minutes: 0, seconds: 10}
READING_INTERVAL: {days: 0, hours: 0, minutes: 0, seconds: 10}
# format of the datetime column (see Python docs on datetime module)
#
......
// 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>
#define WIRE Wire
#else
#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::dayOfWeek() 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(days*86400L + hours*3600 + 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); }
uint8_t RTC_DS1307::begin(void) {
return 1;
}
uint8_t RTC_DS1307::isrunning(void) {
WIRE.beginTransmission(DS1307_ADDRESS);
WIRE._I2C_WRITE(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(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._I2C_WRITE(0);
WIRE.endTransmission();
}
DateTime RTC_DS1307::now() {
WIRE.beginTransmission(DS1307_ADDRESS);
WIRE._I2C_WRITE(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_
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 dayOfWeek() 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:
static uint8_t 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_
/*------------------------------------------------------------------------------
* Author: Nelso G. Jost (nelsojost@gmail.com)
*
* License: BEERWARE (http://en.wikipedia.org/wiki/Beerware)
*
* Purpose: Testing board comunication.
* License: GPLv2
* Purpose: Root file for Arduino IDE projects.
*----------------------------------------------------------------------------*/
#include <Wire.h>
#include "mysensors.h"
......@@ -22,6 +20,6 @@ void loop()
if (Serial.available())
{
stream_line = Serial.readString();
Serial.println(parse_line_sensorlist(stream_line));
Serial.println(execute_command(stream_line));
}
}
......@@ -18,7 +18,6 @@ String read_LDR()
// https://github.com/adafruit/DHT-sensor-library
#include "DHT.h"
#define DHT22_PIN 4 // digital
#define DHTTYPE DHT22
DHT dht(DHT22_PIN, DHTTYPE);
......@@ -36,13 +35,7 @@ String read_DHT22_AH()
// === BMP085 SETUP ===============================================
// https://github.com/adafruit/Adafruit-BMP085-Library
/*------------------------------------------------------------------------------
* AUTO-GENERATED CODE FROM CTA-EMM-WEB
* EDIT ONLY IF YOU KNOW WHAT YOU ARE DOING
*----------------------------------------------------------------------------*/
#include "mysensors.h"
#include "Adafruit_BMP085.h"
Adafruit_BMP085 bmp;
bool is_bmp085_connected=false;
......@@ -55,8 +48,78 @@ String read_BMP085_PRESSURE()
}
// ----------------------------------------------------------------
// === RTC_DS1307 SETUP ===========================================
// https://github.com/adafruit/RTClib
RTC_DS1307 rtc;
String read_RTC_DS1307()
{
if (rtc.isrunning())
return get_datetime_str(rtc.now());
else
return String("<RTC_DS1307_not_found>");
}
String get_datetime_str(DateTime dt)
{
String result="";
result += String(dt.year(), DEC);
result += String("-");
result += String(dt.month(), DEC);
result += String("-");
result += String(dt.day(), DEC);
result += String(" ");
result += String(dt.hour(), DEC);
result += String(":");
result += String(dt.minute(), DEC);
result += String(":");
result += String(dt.second(), DEC);
return result;
}
// Expects something like "2015,6,28,13,13,10"
String set_time_from_csv(String s)
{
DateTime dt;
int i_month = s.indexOf(CSV_SEP);
int i_day = s.indexOf(CSV_SEP, i_month + 1);
int i_hour = s.indexOf(CSV_SEP, i_day + 1);
int i_minute = s.indexOf(CSV_SEP, i_hour + 1);
int i_second = s.indexOf(CSV_SEP, i_minute + 1);
String year = s.substring(0, i_month);
String month = s.substring(i_month + 1, i_day);
String day = s.substring(i_day + 1, i_hour);
String hour = s.substring(i_hour + 1, i_minute);
String minute = s.substring(i_minute + 1, i_second);
String second = s.substring(i_second + 1);
dt = DateTime(year.toInt(), month.toInt(), day.toInt(), \
hour.toInt(), minute.toInt(), second.toInt());
rtc.adjust(dt);
return String("done: ") + get_datetime_str(dt);
}
// ----------------------------------------------------------------
void mysensors_setup()
{
dht.begin();
is_bmp085_connected = bmp.begin();
#ifdef AVR
Wire.begin();
#else
Wire1.begin(); // Shield I2C pins connect to alt I2C bus on Arduino Due
#endif
rtc.begin();
// adjust rtc from compile date time
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
......@@ -4,15 +4,24 @@
#include "Arduino.h"
String read_LDR();
String read_BMP085_PRESSURE();
#include "DHT.h"
String read_DHT22_TEMP();
String read_DHT22_AH();
#include "Adafruit_BMP085.h"
String read_BMP085_PRESSURE();
#include "RTClib.h"
String read_RTC_DS1307();
String get_datetime_str(DateTime dt);
String set_time_from_csv(String s);
void mysensors_setup();
#define __SENSOR_COUNT 4
#define __LIST_SENSOR_NICKNAMES {"LDR", "BMP085_PRESSURE", "DHT22_TEMP", "DHT22_AH"}
#define __LIST_SENSOR_SHORT_NICKS {"l", "p", "t", "ah"}
#define __FP_ARRAY_SENSOR_READ {&read_LDR, &read_BMP085_PRESSURE, &read_DHT22_TEMP, &read_DHT22_AH}
#define __SENSOR_COUNT 5
#define __LIST_SENSOR_NICKNAMES {"LDR", "BMP085_PRESSURE", "DHT22_TEMP", "DHT22_AH", "RTC_DS1307"}
#define __LIST_SENSOR_SHORT_NICKS {"l", "p", "t", "ah", "dt"}
#define __FP_ARRAY_SENSOR_READ {&read_LDR, &read_BMP085_PRESSURE, &read_DHT22_TEMP, &read_DHT22_AH, &read_RTC_DS1307}
#endif
/*------------------------------------------------------------------------------
* Author: Nelso G. Jost (nelsojost@gmail.com)
*
* License: BEERWARE (http://en.wikipedia.org/wiki/Beerware)
*
* Purpose: General utilities, for instance, not present on the Wiring lib.
*----------------------------------------------------------------------------*/
......@@ -50,6 +48,26 @@ float read_ground_humidity(int analog_pin_read, int digital_pin_vcc)
return (us1+us2+us3+us4+us5)/5;
}
String execute_command(String s)
{
String command = s.substring(0, s.indexOf(CSV_SEP));
String args = s.substring(s.indexOf(CSV_SEP) + 1);
if (command == "read" && args.length() > 0)
{
return parse_line_sensorlist(s.substring(s.indexOf(CSV_SEP) + 1));
}
else if (command == "setrtc" && args.length() > 0)
{
return set_time_from_csv(args);
}
else
{
return String("<invalid_command:'") + s + String("'>");
}
}
String parse_line_sensorlist(String line)
{
int s=0, e=0; // start,end string indexes
......@@ -74,7 +92,7 @@ String read_sensor_by_nickname(String nickname)
{
for (int i=0; i < __SENSOR_COUNT; i++)
{
if ((nickname.length() < 3 && nickname == sensor_short_nicks[i]) ||
if ((nickname.length() < 4 && nickname == sensor_short_nicks[i]) ||
nickname == sensor_nicknames[i]) return FPArrayReadSensor[i]();
}
return String("<invalid_nickname:") + nickname + String(">");
......
......@@ -29,6 +29,8 @@ float read_ground_humidity(int analog_pin_read, int digital_pin_vcc);
#define CSV_SEP ','
String execute_command(String s);
/* Given a string with a comma-separated list of sensor nicknames, for each
* nickname in the list calls the right sensor reading function and then
* returns a new comma-separated list of reading values (same ordering).
......
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