Would like to show my first prototype of a battery operated Lux sensor. I have choosen to use a step up convertor to have a stable Vcc of 3.3V for the Pro Mini and the RFM69 radio. Using the battery power direct has drawbacks, the logic levels are fluctuating, the BOD detection of the Pro Mini is set to 2.7 V so I can not go to the minimum of 1V per cell. Using the step-up also has a drawback, it consumes a bit of power for itself. But it makes it possible to go down to the 1V per cell.
Doing the measurements and calculations it should work for about 17 months (still theoretical !) on two AA alkaline batteries. The sensor sends in a lux value every 5 minutes, only when the value has changed. It also send the battery level, 0% is 2.0V, 100% is 3.0V. The prototype also sends the actual battery voltage but this is only for testing. This setup is running for a week now.
It's still "under construction".
// Change baudrate to 9600 in c:\Users\Roland\Documents\Arduino\libraries\MySensors\MyConfig.h
#include <MySigningNone.h>
#include <MyTransportRFM69.h>
#include <MyHwATMega328.h>
#include <MySigningAtsha204Soft.h>
#include <MySigningAtsha204.h>
#include <MySensor.h>
#include <SPI.h>
#include <Wire.h>
#include <SFE_TSL2561.h>
#define MY_DEBUG
#define Led 3
#define MeasurePoint1 4
#define MeasurePoint2 5
int BATTERY_SENSE_PIN = A0; // select the input pin for the battery sense point
// Create an SFE_TSL2561 object, here called "light":
SFE_TSL2561 light;
boolean gain; // Gain setting, 0 = X1, 1 = X16;
unsigned int ms; // Integration ("shutter") time in milliseconds
float Vref = 3.324; // Vref in mV
int VMIN = 2000; // Battery monitor low level in mV
int VMAX = 3000; // Battery monitor high level in mV
int batteryPcnt;
int LastbatteryPcnt = 0;
int batterymV;
float batteryV;
int MeasureCount = 50;
// Define the child ID's
#define CHILD_ID_LIGHT 0
#define CHILD_ID_VOLTMETER 1
#define CHILD_ID_STATUS 2
#ifdef MY_DEBUG
unsigned long SLEEP_TIME = 60000; // Sleep time between reads (in milliseconds) 1 minute for testing
#else
unsigned long SLEEP_TIME = 300000; // Sleep time between reads (in milliseconds) 5 minutes
#endif
MyTransportRFM69 transport;
// Hardware profile
MyHwATMega328 hw;
// Construct MySensors library (signer needed if MY_SIGNING_FEATURE is turned on in MyConfig.h)
// To use LEDs blinking, uncomment WITH_LEDS_BLINKING in MyConfig.h
#ifdef WITH_LEDS_BLINKING
MySensor gw(transport, hw /*, signer*/, RADIO_RX_LED_PIN, RADIO_TX_LED_PIN, RADIO_ERROR_LED_PIN);
#else
MySensor gw(transport, hw /*, signer*/);
#endif
// V_LIGHT_LEVEL should only be used for uncalibrated light level 0-100%.
// If your controller supports the new V_LEVEL variable, use this instead for
// transmitting LUX light level.
// MyMessage luxmsg(CHILD_ID_LIGHT, V_LIGHT_LEVEL);
MyMessage luxmsg(CHILD_ID_LIGHT, V_LEVEL);
MyMessage voltmsg(CHILD_ID_VOLTMETER, V_VOLTAGE);
MyMessage statusmsg(CHILD_ID_STATUS, V_STATUS);
uint16_t lastlux;
uint16_t mlux;
double lux; // Resulting lux value
boolean good; // True if neither sensor is saturated
void setup()
{
#ifdef MY_DEBUG
pinMode(MeasurePoint1, OUTPUT);
pinMode(MeasurePoint2, OUTPUT);
pinMode(Led, OUTPUT);
digitalWrite(MeasurePoint1,LOW);
digitalWrite(MeasurePoint2,LOW);
digitalWrite(Led,LOW);
#endif
analogReference(DEFAULT);
gw.begin();
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("BattLuxSensor", "1.4");
// Register all sensors to gateway (they will be created as child devices)
gw.present(CHILD_ID_LIGHT, S_LIGHT_LEVEL);
gw.present(CHILD_ID_VOLTMETER, S_MULTIMETER);
gw.present(CHILD_ID_STATUS, S_BINARY);
// Initialize the SFE_TSL2561 library
// You can pass nothing to light.begin() for the default I2C address (0x39),
// or use one of the following presets if you have changed
// the ADDR jumper on the board:
// TSL2561_ADDR_0 address with '0' shorted on board (0x29)
// TSL2561_ADDR default address (0x39)
// TSL2561_ADDR_1 address with '1' shorted on board (0x49)
light.begin();
// Get factory ID from sensor:
// (Just for debug, you don't need to do this to operate the sensor)
unsigned char ID;
#ifdef MY_DEBUG
if (light.getID(ID))
{
Serial.print("Found TSL2561 light sensor at ID: 0X");
Serial.println(ID,HEX);
}
// Most library commands will return true if communications was successful,
// and false if there was a problem. You can ignore this returned value,
// or check whether a command worked correctly and retrieve an error code:
else
{
byte error = light.getError();
Serial.println("TSL2561 light sensor not found");
printError(error);
}
#endif
// The light sensor has a default integration time of 402ms,
// and a default gain of low (1X).
// If you would like to change either of these, you can
// do so using the setTiming() command.
// If gain = false (0), device is set to low gain (1X)
// If gain = high (1), device is set to high gain (16X)
gain = 0;
// If time = 0, integration will be 13.7ms
// If time = 1, integration will be 101ms
// If time = 2, integration will be 402ms
// If time = 3, use manual start / stop to perform your own integration
unsigned char time = 1;
// setTiming() will set the third parameter (ms) to the
// requested integration time in ms (this will be useful later):
#ifdef MY_DEBUG
Serial.println("Set timing and gain TSL2561");
#endif
light.setTiming(gain,time,ms);
}
void loop()
{
#ifdef MY_DEBUG
digitalWrite(MeasurePoint1,HIGH);
#endif
// To start taking measurements, power up the sensor:
#ifdef MY_DEBUG
Serial.println("Powerup TSL2561");
#endif
light.setPowerUp();
//light.manualStart(); // Do manual start
delay(410); // Wait integration time
//light.manualStop(); // Do manual stop
// Once integration is complete, we'll retrieve the data.
// There are two light sensors on the device, one for visible light
// and one for infrared. Both sensors are needed for lux calculations.
// Retrieve the data from the device:
unsigned int data0, data1;
if (light.getData(data0,data1))
{
// getData() returned true, communication was successful
// To calculate lux, pass all your settings and readings
// to the getLux() function.
// The getLux() function will return 1 if the calculation
// was successful, or 0 if one or both of the sensors was
// saturated (too much light). If this happens, you can
// reduce the integration time and/or gain.
// For more information see the hookup guide at: https://learn.sparkfun.com/tutorials/getting-started-with-the-tsl2561-luminosity-sensor
// Perform lux calculation:
good = light.getLux(gain,ms,data0,data1,lux);
}
else
{
// getData() returned false because of an I2C error, inform the user.
byte error = light.getError();
#ifdef MY_DEBUG
printError(error);
#endif
}
#ifdef MY_DEBUG
Serial.print("LUX Measurement return flag : ");
if (good)
{
Serial.println("Good");
}
else
{
Serial.println("Bad");
}
#endif
// Convert lux to an integer
mlux = int(lux); // Set Lux value to integer
#ifdef MY_DEBUG
Serial.print("Measured LUX value: ");
Serial.println(mlux);
Serial.println("Powerdown TSL2561");
#endif
light.setPowerDown(); // Power down the sensor
// Get the battery Voltage
// Do 50 measurements and average the result
long Average = 0;
for(int i = 0; i < MeasureCount; i++) {
Average = Average + analogRead(BATTERY_SENSE_PIN);
}
int sensorValue = Average / MeasureCount;
batteryV = sensorValue * (Vref / 1024.0);
batterymV = int (1000 * batteryV);
if (batterymV > 3000) batterymV = 3000;
if (batterymV < 2000) batterymV = 2000;
batteryPcnt = map(batterymV, VMIN, VMAX, 0, 100);
#ifdef MY_DEBUG
Serial.print("ADC value: ");
Serial.println(sensorValue);
Serial.print("Battery Voltage: ");
Serial.print(batteryV,2);
Serial.println(" V");
Serial.print("Battery percent: ");
Serial.print(batteryPcnt);
Serial.println(" %");
#endif
if ((batteryPcnt != LastbatteryPcnt) || (mlux != lastlux)) {
#ifdef MY_DEBUG
digitalWrite(MeasurePoint2,HIGH);
digitalWrite(Led,HIGH);
#endif
// Send in status (good or bad)
gw.send(statusmsg.set(good));
// Send in the Lux value
gw.send(luxmsg.set(mlux));
// Send in the battery level percentage
gw.sendBatteryLevel(batteryPcnt);
// Send in the battery voltage
gw.send(voltmsg.set(batteryV, 2)); // Set wants volts and how many decimals (2 in our case)
LastbatteryPcnt = batteryPcnt;
lastlux = mlux;
#ifdef MY_DEBUG
digitalWrite(MeasurePoint2,LOW);
digitalWrite(Led,LOW);
#endif
}
#ifdef MY_DEBUG
digitalWrite(MeasurePoint1,LOW);
#endif
gw.sleep(SLEEP_TIME);
}
// ===========================================================
#ifdef MY_DEBUG
void printError(byte error)
// If there's an I2C error, this function will
// print out an explanation.
{
Serial.print("I2C error: ");
Serial.print(error,DEC);
Serial.print(", ");
switch(error)
{
case 0:
Serial.println("success");
break;
case 1:
Serial.println("data too long for transmit buffer");
break;
case 2:
Serial.println("received NACK on address (disconnected?)");
break;
case 3:
Serial.println("received NACK on data");
break;
case 4:
Serial.println("other error");
break;
default:
Serial.println("unknown error");
}
}
#endif