The arduino is a Uno costing around $30.00
The GPRS/GSM modem board is a SIM900 gprs/gsm shield from http://store.linksprite.com/, and cost $39.00.
The SIM card is a gophone card with pay as you go and I have a text plan that costs $10.00 per month for 1000 text messages.
I built the temperature and A/C power interfaces on an Arduino prototyping board. It connects to two 10K NTC temperature sensitive resistors for outside and inside temperature measurements. These connect to Arduino analog inputs 0 and 1. It connects via an opto isolator to a small cube-type 5V USB A/C power supply like those used for iPhone and MP3 charging, which supplies 5 V when A/C power is on and zero when off. This connects to Arduino digital input pin 3. A/C power is indicated by an led connected to pin 5.
The protoboard schematic is:
Power is supplied by a single USB cable that splits to provide power to the SIM900 GSM board, which requires more power than the Arduino board can supply, via a barrel connector and a standard USB B connector to Arduino.
Here are some photos, invarious stages of undress:
And code:
// Name
// tweet_house_status_sec_hp
//
// Description
//
// Sketch to measure two temperatures and report via SMS both periodically and
// when changes greater than some threshold are observed.
//
// Hardware needed:
//
// Arduino (Uno, professional, etc.)
// SIM900 gsm/gprs shield and activated sim card.
//
// Author: John Zouck
// Initial release date: November 24, 2013
//
#include <SIM900gsm.h>
#include <SoftwareSerial.h>
#include <gsm_timer_sec.h>
#include <thermistor.h>
#include <counts_last_period.h>
#include <Time.h>
#include <EEPROM.h>
static int house_power = 3; // House power reported on pin 3
static int power_led = 5; // Power led indicator is pin 5
static SoftwareSerial gprs(7,8);
#define TWEET_PERIOD_MINUTES 240 // 4 hours max per tweet
#define DELTA_TEMP 4.0 // delta temp to cause report: degrees F
#define TEMP_CHECK_TIME_SEC 2 // check temps and power every 2 seconds
#define TMIN_REPORT_TIME_MINUTES 1 // Report no more than once every 1 minutes.
#define SMS_CHECK_PERIOD_SECONDS 15
#define SMS_REPLY_TEST_DIVISOR 1
static gsm_timer_sec tcheck;
static gsm_timer_sec treport;
static gsm_timer_sec tchecksms;
// Last sampled temperature values.
static float temp0;
static float temp1;
// Calibraton factors, put in EEPROM.
static float delta_temp0 = 0.0;
static float delta_temp1 = 0.0;
static int hp_last = 0;
static counts_last_period clp( 30 * 60 * 1000); // Limit to 10 SMS per 30 minutes
static char *buildtstr( unsigned long t, char *str);
static void checksms( float t1, float t2, char *reply );
static char *build_report( float t1, float t2, int hp, char *str);
static int report_temps( char *str);
static int calibrate( char *sms, char *sender);
static int rcals( float *cf1, float *cf2);
static int wcals( float *cf1, float *cf2);
static char *build_cal_report( float ct1, float ct2, char *str);
// Noise reduction for analog inputs via averaging values
static int analog_average( int ch, int n ) {
int i;
int sum = 0;
for (i = 0; i < n; i++ ) {
sum += analogRead( ch );
delay (1);
}
return sum/n;
}
// The following #define is used so we can test without
// actually sending any messages.
#define GSM_SMS_ENABLE
void setup() {
char str[100];
unsigned long t;
int rply;
uint8_t tm[6]; // 0-5: yy mm dd hh mi ss
gprs.begin(9600); // Slow down output from modem so we can keep up...
Serial.begin(115200);
SIM900poweron( gprs);
Serial.println("Modem power on and sim card registered.");
// Set Time package time from Modem BBU clock once
// Use time package functions from now on so we
// don't have to query modem each time we want time.
// Get time and date from modem bbu clock once:
rply = SIM900date_sec( gprs, &t, str, 95, tm);
// Set time and date in software clock.
// Args are: hh, mi, ss, dd, mm, yy
// i.e. for 18:21:00 on November 23, 2013:
// setTime( 18, 21, 0, 23, 11, 13);
setTime( tm[3], tm[4], tm[5], tm[2], tm[1], tm[0]);
switch (timeStatus()) {
case timeSet:
Serial.print("Program tweet_house_status_sec started at time = ");
Serial.println( buildtstr(now(), str));
break;
case timeNotSet:
case timeNeedsSync:
default:
Serial.println("Time not synced.");
}
strcat( str, ": tweet_house_status_sec program startup.");
// Erase all SMS
//rply = SIM900eraseall( gprs, 0);
pinMode( house_power, INPUT);
pinMode( power_led, OUTPUT);
#ifdef GSM_SMS_ENABLE
rply = SIM900tweet( gprs, str );
#endif
// Read cal factors from EEPROM
rcals( &delta_temp0, &delta_temp1);
// Print cal factors
Serial.println("EEPROM cal factors 1 and 2: ");
Serial.println(delta_temp0, 4);
Serial.println(delta_temp1, 4);
tcheck.start(TEMP_CHECK_TIME_SEC); // seconds between temperature checks
treport.start( TWEET_PERIOD_MINUTES * 60 ); // report period if no delta temp detected.
tchecksms.start( SMS_CHECK_PERIOD_SECONDS/SMS_REPLY_TEST_DIVISOR); // Period to check for new SMS
}
void loop () {
float temp0_c;
float temp1_c;
int cnt;
static float lt0 = -50.0;
static float lt1 = -50.0;
char report_s[76];
int hp;
// Read house power pin
hp = digitalRead( house_power );
if ( hp == 0 ) {
digitalWrite( power_led, LOW);
} else {
digitalWrite( power_led, HIGH);
}
if ( tcheck.check()) {
temp0 = thermistor_tempf (analog_average(0, 10)); // get value
temp1 = thermistor_tempf (analog_average(1, 10)); // get value
temp0_c = delta_temp0 + temp0;
temp1_c = delta_temp1 + temp1;
build_report( temp0_c, temp1_c, hp, report_s);
if ( (abs( temp0_c - lt0 ) > DELTA_TEMP || abs( temp1_c - lt1) > DELTA_TEMP ||
treport.check() || hp != hp_last) && !clp.check( millis()) ) {
hp_last = hp;
Serial.println( report_s );
#ifdef GSM_SMS_ENABLE
report_temps( report_s);
#endif
lt0 = temp0_c;
lt1 = temp1_c;
treport.start( TWEET_PERIOD_MINUTES * 60);
}
tcheck.start( TEMP_CHECK_TIME_SEC );
}
if ( tchecksms.check() ) {
checksms(report_s);
tchecksms.start( SMS_CHECK_PERIOD_SECONDS / SMS_REPLY_TEST_DIVISOR);
}
}
// See if there are any SMS messages waiting. Reply with "reply"
// argument and erase SMS if so.
static void checksms( char *reply ) {
int cnt;
int rply;
int smslist[31];
char sms[200];
char calreport[70];
Serial.print("In checksms(), freeRam: ");
Serial.println( freeRam() );
cnt = SIM900getsmsixl( gprs, smslist, 32, 10000);
if ( cnt > 0 )
{
char sender[20];
for ( int i = 0; i < cnt; i++) {
SIM900getsms( gprs, smslist[i], sms, 190, 5000);
//Serial.println(sms);
SIM900getsender( sms, sender );
if ( calibrate( sms, sender) ) {
build_cal_report( delta_temp0, delta_temp1, calreport);
#ifdef GSM_SMS_ENABLE
SIM900sendsms( gprs, calreport, sender);
#endif
}
else {
// Reply, but not to twitter (40404)
if ( NULL == strstr( sender, "40404") ) {
Serial.print("Replying to sender: ");
Serial.println( sender);
#ifdef GSM_SMS_ENABLE
SIM900sendsms( gprs, reply, sender);
#endif
}
}
}
#ifdef GSM_SMS_ENABLE
SIM900eraseall( gprs, 0);
#endif
}
}
// Calibrate therms t1 and t2 by sending SMS with text "CALT1=xx.x" or
// "CALT2=yy.y" where xx.x and yy.y are actual temperatures, like 32.0 if
// therm is in ice bath at 32 degrees.
static int calibrate( char *sms, char *sender) {
float ct1;
char *calt1;
float ct2;
char *calt2;
int calrcvd = 0;
//Serial.println("calibrate() called.");
if ( NULL != (calt1 = strstr( sms, "CALT1=") )) {
//Serial.print("Got a CALT1= SMS to calibrate temperature T1 is: ");
ct1 = strtod( calt1 + 6, NULL);
//Serial.println(ct1);
delta_temp0 = ct1 - temp0;
// Write to EEPROM
wcals(&delta_temp0, &delta_temp1);
calrcvd = 1;
}
if ( NULL != (calt2 = strstr( sms, "CALT2=") )) {
//Serial.print("Got a CALT2= SMS to calibrate temperature T2 is: ");
ct2 = strtod( calt2 + 6, NULL);
//Serial.println(ct2);
delta_temp1 = ct2 - temp1;
// Write to EEPROM
wcals(&delta_temp0, &delta_temp1);
calrcvd = 1;
}
return calrcvd;
}
// Report temperatures via SMS
// Right now we just tweet to 40404. Later we might add sending
// SMS to a list of cell phones as well.
static int report_temps( char *str) {
int rply;
Serial.println("Tweeting: ");
Serial.println(str);
rply = SIM900tweet( gprs, str );
}
// Build string with leading time and date (current time) and
// temperatures t1 and t2, like:
//
// "11/24/2013 12:03:52 48.3 F and 33.6 F."
//
// Need min. of 46 characters space in char array str points to.
static char *build_report( float t1, float t2, int hp, char *str) {
char ts[15];
unsigned long t;
buildtstr( now(),str);
dtostrf( t1, 7, 1, ts);
strcat( str, ts );
strcat( str, " F and ");
dtostrf( t2, 7, 1, ts);
strcat( str, ts );
strcat( str, " F, Power: ");
if ( hp ) {
strcat( str, "ON");
} else {
strcat( str, "OFF");
}
return str;
}
// Build string with leading time and date (current time) and
// calibration constants.
// "11/24/2013 12:03:52 -1.2 F and 2.6 F."
//
// Need min. of 66 characters space in char array str points to.
static char *build_cal_report( float ct1, float ct2, char *str) {
char ts[15];
unsigned long t;
buildtstr( now(),str);
strcat( str, " Calibration consts: ");
dtostrf( ct1, 7, 1, ts);
strcat( str, ts );
strcat( str, " F and ");
dtostrf( ct2, 7, 1, ts);
strcat( str, ts );
strcat( str, " F.");
return str;
}
// Build time string from time t.
// buildtstr( now(), str) returns:
// "11/24/2013 12:03:52"
static char *buildtstr( unsigned long t, char *str) {
sprintf( str, "%02d/%02d/%02d %02d:%02d:%02d ", month(t),
day(t), year(t), hour(t), minute(t), second(t));
return str;
}
static int wcals( float *cf1, float *cf2) {
// write 4-byte floats to locations 0 and 4 in EEPROM
for (int i = 0; i < 4; i++) {
EEPROM.write(i, *((byte *) cf1 + i));
}
for (int i = 4; i < 8; i++) {
EEPROM.write(i, *((byte *) cf2 + i - 4));
}
}
static int rcals( float *cf1, float *cf2) {
// read 4-byte float 0.0 from locations 0 and 4 in EEPROM
for (int i = 0; i < 4; i++) {
*((byte *) cf1 + i) = EEPROM.read(i);
}
for (int i = 4; i < 8; i++) {
*((byte *) cf2 + i - 4) = EEPROM.read(i);
}
//Serial.println("EEPROM cal factors 1 and 2: ");
//Serial.println(*cf1, 4);
//Serial.println(*cf2, 4);
//Serial.println("Done!");
}




No comments:
Post a Comment