This is a work in progress and I am mainly using it for my own learning...but here is the spec:
-
Low power.
-
Cheap.
-
Low Maintenance.
-
Multiple Sensor locations.
-
Different Sensors (Light, Temp, Voltage and possibly humidity or moisture later).
-
Simple to add new sensors.
-
I want a system for the greenhouse and other parts of the garden that monitors the temperature, light and maybe the humidity/moisture of various parts.
-
The data is to be sent to a raspberry Pi running a mySQL DB with a PHP front end.
-
Range 30-50m ish with clear LOS.
Here is what I have so far. I will add to this thread if there is any further interest! I am learning, so suggestions will always be taken...I may take time to understand them so may not implement them till I fully understand what they do!
The Transmitters
The transmitters I wanted to be cheap.
I decided on:
- ATtiny85 MCUs
- DS1820B Temp sensors in waterproof packaging (1m cable).
- 4F 5.5V supercap (Thanks China!)
- Diode (with 0.7V drop)
- 6V solar panel (Thanks China!)
- 433Mhz Tx (the cheap Chinese ones).
All in all, this makes them around £5 each to make. Most parts are about £1 when bought in a small batch/pack.
As far as coding, I have used watchdog and the Manchester Library.
It needs tidying and some modifying later for the different sensor types.
50degC is added to the temperature value, which is removed at the reciever end. This deals with "negative" values...so an signed int is not required (which the Manchester library only likes unsigned apparently).
/*
// A LOW POWER Tx SYSTEM - USING A SOLAR PANEL (6V) AND A SUPER CAP (5.5V)
// USE A DIODE TO STOP CURRENT BACK FLOW (ALSO Vdrop) to stop overcharging of the CAP)
//
// D1 - 433MHz Tx data line
// D2 = DS1820 Data line
WDT sleeps :
0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms, 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
Manchester Transmitter
Try different speeds using these constants, your maximum
possible speed will depend on various factors like transmitter
type, distance, microcontroller speed, ...
MAN_300 0
MAN_600 1
MAN_1200 2
MAN_2400 3
MAN_4800 4
MAN_9600 5
MAN_19200 6
MAN_38400 7
*/
#define TX_PIN 1
#define UNITID 9 // The identity of the unit. Used to ID the data when using multiple units.
#include <Manchester.h> //Protocol for sending data via 433Mhz Tx. Includes preambles and such.
#include <avr/sleep.h> // library for sleeping
#include <avr/wdt.h> //library for sleeping
#include <OneWire.h> //For the DS182B0 T Sensors
OneWire TemperatureSensor(2); // Dallas one wire data buss pin, a 4.7K resistor pullup is needed
//Some stuff for the watchdog timer. Sets some fuse bits.
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
byte num_sleeps = 0;
void setup() {
setup_watchdog(9); // approximately 8 seconds sleep
man.setupTransmit(TX_PIN, MAN_300);
}
void loop() {
while (num_sleeps < 20) {
system_sleep();
num_sleeps++;
}
num_sleeps = 0;
byte i;
byte data[12];
int16_t raw;
int celsius;
TemperatureSensor.reset(); // reset one wire buss
TemperatureSensor.skip(); // select only device
TemperatureSensor.write(0x44); // start conversion
delay(1000); // wait for the conversion
TemperatureSensor.reset();
TemperatureSensor.skip();
TemperatureSensor.write(0xBE); // Read Scratchpad
for ( i = 0; i < 9; i++) { // 9 bytes
data[i] = TemperatureSensor.read();
}
// Convert the data to actual temperature
raw = (data[1] << 8) | data[0];
celsius = ((int)raw / 16.0);
// Consruct the data packet for sending.
int byteT = (celsius+50) << 6;
byte byteUID = UNITID << 2;
byte byteDtype = 1;
uint16_t send_packet = byteT + byteUID + byteDtype;
// Transmit the packet.
man.transmit(send_packet);
// Get the capacitor voltage
int cap_volts = (int)readVcc();
int cap_volts_scaled= (cap_volts/10) << 6;
// Crate a packet for the super cap voltage level.
byteDtype=0;
cap_volts_scaled=cap_volts_scaled+byteUID+byteDtype;
//Send the super cap battery level (good for diagnostic...sensor placement issues?)
man.transmit(cap_volts_scaled);
delay(200);
}
void system_sleep() {
cbi(ADCSRA, ADEN); // switch Analog to Digitalconverter OFF
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
sleep_enable();
sleep_mode(); // System sleeps here
sleep_disable(); // System continues execution here when watchdog timed out
sbi(ADCSRA, ADEN); // switch Analog to Digitalconverter ON
}
void setup_watchdog(int ii) {
byte bb;
int ww;
if (ii > 9 ) ii = 9;
bb = ii & 7;
if (ii > 7) bb |= (1 << 5);
bb |= (1 << WDCE);
ww = bb;
MCUSR &= ~(1 << WDRF);
// start timed sequence
WDTCR |= (1 << WDCE) | (1 << WDE);
// set new watchdog timeout value
WDTCR = bb;
WDTCR |= _BV(WDIE);
}
ISR(WDT_vect) {
}
long readVcc() {
// Read 1.1V reference against AVcc
// set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = _BV(MUX3) | _BV(MUX2);
#else
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif
delay(2); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Start conversion
while (bit_is_set(ADCSRA,ADSC)); // measuring
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
uint8_t high = ADCH; // unlocks both
long result = (high<<8) | low;
result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
return result; // Vcc in millivolts
}
The Reciever
An Arduino Nano with the code below gets the data sent by the sensors and parses it in to something for the Raspberry Pi to pick up and deal with.
#include <Manchester.h>
/*
try different speeds using this constants, your maximum possible speed will
depend on various factors like transmitter type, distance, microcontroller speed, ...
MAN_300 0
MAN_600 1
MAN_1200 2
MAN_2400 3
MAN_4800 4
MAN_9600 5
MAN_19200 6
MAN_38400 7
*/
#define RX_PIN 2
void setup() {
man.setupReceive(RX_PIN, MAN_300);
man.beginReceive();
Serial.begin(9600);
}
void loop() {
if (man.receiveComplete()) {
uint16_t m = man.getMessage();
man.beginReceive(); //start listening for next message right after you retrieve the message
int rxdata = m >> 6;
int unit_ID = m << 10 >> 12;
int D_type = m << 14 >> 14;
if (D_type==0){ // If it is a cap volt packet
int capV = rxdata*10;
Serial.print(unit_ID);
Serial.print(",");
Serial.print(D_type);
Serial.print(",");
Serial.println(capV);
}
else if (D_type==1){ //if it is TempC data packet
int tempC = rxdata - 50;
Serial.print(unit_ID);
Serial.print(",");
Serial.print(D_type);
Serial.print(",");
Serial.println(tempC);
}
}
}
The code on the raspberry pi requires Python 3 and the pySerial library. Very easy to do. The code I have on it so far only prints what the arduino sends it via the USB serial and also saves it to a file.
import serial
ser=serial.Serial('/dev/ttyUSB0',9600)
f=open("data.txt","a")
while 1:
rxd = ser.readline()
print(rxd.decode("utf-8"))
f.write(rxd.decode("utf-8"))
