Skip to content

Instantly share code, notes, and snippets.

@kenci
Last active March 6, 2016 18:55
Show Gist options
  • Select an option

  • Save kenci/78840ac886fcffdf3e10 to your computer and use it in GitHub Desktop.

Select an option

Save kenci/78840ac886fcffdf3e10 to your computer and use it in GitHub Desktop.
MySensors
/*
PROJECT: MySensors / LiON charger board
PROGRAMMER: AWI
DATE: 28 april 2015/ last update: 11 may 2015 / BH1750 added: 5 September 2015
FILE: MS_Solar_2.ino
LICENSE: Public domain
Hardware: Ceech - ATmega328p board w/ ESP8266 and NRF24l01+ socket LTC4067 lithium battery charger
and MySensors 1.4
Temp & Humidity - HTU21
Barometer & Temp - BMP085
Light sensor - BH1750
On board EEPROM (I2C)
On board Li-On charger with multiple V/A measurements
Special:
program with Arduino Pro 3.3V 8Mhz
SUMMARY:
Reads on-board sensors and send to gateway /controller
Remarks:
On board EEPROM and MOSFET not used in this sketch
Fixed node-id
*/
#include <SPI.h>
#include <MySensor.h>
#include <Wire.h> // I2C
#include <BH1750FVI.h>
#define LTC4079_CHRG_PIN A7 //analog input A7 on ATmega 328 is /CHRG signal from LTC4067
#define batteryVoltage_PIN A0 //analog input A0 on ATmega328 is battery voltage ( /2)
#define solarVoltage_PIN A2 //analog input A2 is solar cell voltage (/ 2)
#define solarCurrent_PIN A6 //analog input A6 is input current ( I=V/Rclprog x 1000 )
const float VccMin = 1.0*3.5; // Minimum expected Vcc level, in Volts. Example for 1 rechargeable lithium-ion.
const float VccMax = 1.0*4.2; // Maximum expected Vcc level, in Volts.
#define NODE_ID 61
#define TEMPERATURE_CHILD_ID 2
#define BATT_CHILD_ID 10
#define SOLAR_CHILD_ID 11
#define LIGHT_CHILD_ID 6
BH1750FVI lightSensor; // define light sensor
#define LUX_THRESHOLD 5
MySensor gw; // Ceech board, 3.3v pin 7,8 in MySensors config (pin default 9,10)
unsigned long SLEEP_TIME = 60000; // 60 sec sleep time between reads (seconds * 1000 milliseconds)
float lastBattVoltage;
float lastSolarVoltage;
float lastSolarCurrent;
int lastBattPct = 0;
uint16_t lastLux = 0;
float VccReference = 3.3 ; // voltage reference for measurement, definitive init in setup
float vout = 0.0;
float vin = 0.0;
float R1 = 47000.0; // resistance of R1
float R2 = 10000.0; // resistance of R2
int value = 0;
int CHRG = A7;
MyMessage batteryVoltageMsg(BATT_CHILD_ID, V_VOLTAGE); // Battery voltage (V)
MyMessage solarVoltageMsg(SOLAR_CHILD_ID, V_VOLTAGE); // Solar voltage (V)
MyMessage solarCurrentMsg(SOLAR_CHILD_ID, V_CURRENT); // Solar current (A)
MyMessage luxMsg(LIGHT_CHILD_ID, V_LIGHT_LEVEL); // Light sensor (lux)
void setup()
{
gw.begin(NULL, NODE_ID); // fixed node 30
//gw.begin(NULL, NODE_ID, true); //Repeater nodes
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Lichtsensor Terrasse", "2.2");
gw.present(BATT_CHILD_ID, S_POWER); // Battery parameters
gw.present(SOLAR_CHILD_ID, S_POWER); // Solar parameters
gw.present(LIGHT_CHILD_ID, S_LIGHT_LEVEL); // Light sensor
// use VCC (3.3V) reference
analogReference(DEFAULT); // default external reference = 3.3v for Ceech board
VccReference = 3.354 ; // measured Vcc input (on board LDO)
Wire.begin(); // init I2C
while (lightSensor.begin() != true)
{
Serial.println(F(""));
Serial.println(F("ROHM BH1750FVI Ambient Light Sensor is not present"));
delay(1000);
}
}
void loop()
{
if(sendLight()) {
Serial.println("SENDING...");
sendVoltage();
}
Serial.println();
gw.sleep(SLEEP_TIME);
// By calling process() you route messages in the background
//gw.process();
}
void sendVoltage(void)
// battery and charging values
{
value = analogRead(solarVoltage_PIN);
vout = (value * VccReference) / 1024.0;
vin = vout / (R2/(R1+R2));
if (vin<0.09)
{
vin=0.0;
}
// get Battery Voltage & charge current
float batteryVoltage = ((float)analogRead(batteryVoltage_PIN)* VccReference/1024) * 2; // actual voltage is double
Serial.print("Batt: ");
Serial.print(batteryVoltage);
Serial.print("V ; ");
// get Solar Voltage & charge current
float solarVoltage = vin;
Serial.print("Solar: ");
Serial.print(solarVoltage);
Serial.print("V ; ");
// get Solar Current
float solarCurrent = ((float)analogRead(solarCurrent_PIN)* VccReference)/ 3.3; // current(mA) = V/Rclprog(kohm)
Serial.print(solarCurrent);
Serial.print(" mA; charge: ");
Serial.println((float)analogRead(A7)?"No":"Yes");
// send battery percentage for node
int battPct = 1 ;
if (batteryVoltage > VccMin){
battPct = 100.0*(batteryVoltage - VccMin)/(VccMax - VccMin);
}
Serial.print("BattPct: ");
Serial.print(battPct);
Serial.println("% ");
gw.send(batteryVoltageMsg.set(batteryVoltage, 3)); // Send (V)
gw.send(solarVoltageMsg.set(solarVoltage, 3)); // Send (V)
gw.send(solarCurrentMsg.set(solarCurrent, 3)); // Send (mA)
gw.sendBatteryLevel(battPct-4);
}
bool sendLight(void)
// Send light level - BH1750
{
lightSensor.setSensitivity(2);
uint16_t lux = lightSensor.readLightLevel();
//float diffLux = abs(lux-lastLux);
if (lux != lastLux) {
lastLux = lux;
gw.send(luxMsg.set(lux)); // Send
return true;
}
Serial.print("BH1750 lux: ");
Serial.print(lux);
Serial.println();
//Serial.print("Diff: ");
//Serial.print(diffLux);
return false;
}
/* Ceech board specifics for reference:
It provides power for the circuit and charges the backup single-cell lithium battery while greatly extends battery life. You can monitor the voltages and currents. It has suspend mode, which reduces current consumption to around 40μA. The power source is a small, 5V solar cell. Connections:
analog input A1 on ATmega 328 is /CHRG signal from LTC4067 (indicates fully charged)
analog input A0 on ATmega328 is battery voltage
analog input A2 is solar cell voltage
analog input A6 is input current ( I=V/Rclprog x 1000 )
analog input A7 is battery charge current ( I=V/Rprog x 1000 )
digital output D9 - drive it high to put LTC4067 in SUSPEND mode
All the voltages on analog inputs can be read with an analogRead() command in the Arduino IDE sketch. Those on inputs A0 an A2 represent direct values of the measured voltages divided by 2. The voltages on analog inputs A6 and A7 can be translated to currents. For example:
Let us say that the voltage on A7 is 0.12V. And the trimmer pot on PROG pin is set to 2.5kOhm. This means that the current into the battery equals to 0.12V/2500ohm x 1000, which is 48mA.
voltmeters on both battery and solar cell connections
They are connected to analog inputs A0 and A2 on the ATmega328p. The voltage dividers resistors are equal, so the measured voltage is double the shown voltage.
NRF24l01+ socket
with CE and CSN pins connected to digital pins 7 and 8 ( you use RF24 radio(7, 8); in Arduino code). There is a 4.7uF capacitor connected across Vin and GND of the port
*/
/* Sketch with Si7021 and battery monitoring.
by m26872, 20151109
*/
#include <MySensor.h>
#include <Wire.h>
#include <SI7021.h>
#include <SPI.h>
#include <RunningAverage.h>
#include <Vcc.h>
//#include <Bounce2.h>
#define DEBUG
#ifdef DEBUG
#define DEBUG_SERIAL(x) Serial.begin(x)
#define DEBUG_PRINT(x) Serial.print(x)
#define DEBUG_PRINTLN(x) Serial.println(x)
#else
#define DEBUG_SERIAL(x)
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#endif
#define NODE_ID 22 // <<<<<<<<<<<<<<<<<<<<<<<<<<< Enter Node_ID
#define SKETCH_INFO "TempReed SZ"
#define SKETCH_VERSION "3.0 06032016"
#define CHILD_ID_TEMP 0
#define CHILD_ID_HUM 1
//Reed
#define CHILD_ID_REED 2
#define REED_BUTTON_PIN 2 // Arduino Digital I/O pin for button/reed switch
// #define SLEEP_TIME 5000 // 15s for DEBUG
#define SLEEP_TIME 300000 // 5 min
#define FORCE_TRANSMIT_CYCLE 36 // 5min*12=1/hour, 5min*36=1/3hour
#define BATTERY_REPORT_CYCLE 2880 // Once per 5min => 12*24*7 = 2016 (one report/week)
//#define VMIN 1.70
//#define VMAX 3.24
#define HUMI_TRANSMIT_THRESHOLD 1.0 // THRESHOLD tells how much the value should have changed since last time it was transmitted.
#define TEMP_TRANSMIT_THRESHOLD 0.5
#define AVERAGES 2
const float vccMin = 1.70; // Minimum expected Vcc level, in Volts.
const float vccMax = 3.24;
const float vccCorrection = 3.35/3.31; // Measured Vcc by multimeter divided by reported Vcc
Vcc vcc(vccCorrection);
int batteryReportCounter = BATTERY_REPORT_CYCLE - 1; // to make it report the first time.
int measureCount = 0;
float lastTemperature = -100;
int lastHumidity = -100;
// Reed Switch
//Bounce debouncer = Bounce();
int oldValue=-1;
//
RunningAverage raHum(AVERAGES);
SI7021 humiditySensor;
MySensor gw;
MyMessage msgTemp(CHILD_ID_TEMP,V_TEMP); // Initialize temperature message
MyMessage msgHum(CHILD_ID_HUM,V_HUM);
MyMessage msgReed(CHILD_ID_REED,V_TRIPPED);
void setup() {
DEBUG_SERIAL(9600);
DEBUG_PRINTLN("Serial started");
DEBUG_PRINT("Voltage: ");
DEBUG_PRINT(vcc.Read_Volts());
DEBUG_PRINT("Percent: ");
DEBUG_PRINT(vcc.Read_Perc(vccMin, vccMax));
DEBUG_PRINTLN(" %");
/*
delay(500);
DEBUG_PRINT("Internal temp: ");
DEBUG_PRINT(GetInternalTemp()); // Probably not calibrated. Just to print something.
DEBUG_PRINTLN(" *C");
*/
delay(500); // Allow time for radio if power used as reset
gw.begin(NULL,NODE_ID);
gw.sendSketchInfo(SKETCH_INFO, SKETCH_VERSION);
gw.present(CHILD_ID_TEMP, S_TEMP); // Present sensor to controller
gw.present(CHILD_ID_HUM, S_HUM);
// Reed Switch
pinMode(REED_BUTTON_PIN,INPUT_PULLUP);
// After setting up the button, setup debouncer
//debouncer.attach(REED_BUTTON_PIN);
//debouncer.interval(5);
// Register binary input sensor to gw (they will be created as child devices)
// You can use S_DOOR, S_MOTION or S_LIGHT here depending on your usage.
// If S_LIGHT is used, remember to update variable type you send in. See "msg" above.
gw.present(CHILD_ID_REED, S_DOOR);
DEBUG_PRINT("Node and "); DEBUG_PRINTLN("2 children presented.");
raHum.clear();
}
void loop() {
// Process incoming messages (like config from server)
gw.process();
measureCount ++;
batteryReportCounter ++;
bool forceTransmit = false;
if (measureCount > FORCE_TRANSMIT_CYCLE) {
forceTransmit = true;
}
sendTempHumidityMeasurements(forceTransmit);
/*
// Read and print internal temp
float temperature0 = static_cast<float>(static_cast<int>((GetInternalTemp()+0.5) * 10.)) / 10.;
DEBUG_PRINT("Internal Temp: "); DEBUG_PRINT(temperature0); DEBUG_PRINTLN(" *C");
*/
// Check battery
if (batteryReportCounter >= BATTERY_REPORT_CYCLE) {
float batteryVolt = vcc.Read_Volts();
DEBUG_PRINT("Battery voltage: "); DEBUG_PRINT(batteryVolt); DEBUG_PRINTLN(" V");
uint8_t batteryPcnt = vcc.Read_Perc(vccMin, vccMax); //constrain(map(batteryVolt,VMIN,VMAX,0,100),0,255);
DEBUG_PRINT("Battery percent: "); DEBUG_PRINT(batteryPcnt); DEBUG_PRINTLN(" %");
gw.sendBatteryLevel(batteryPcnt);
batteryReportCounter = 0;
}
sendReedChange();
gw.sleep(REED_BUTTON_PIN-2, CHANGE, SLEEP_TIME);
}
void sendReedChange() {
//debouncer.update();
// Get the update value
//int value = debouncer.read();
int value = digitalRead(REED_BUTTON_PIN);
if (value != oldValue) {
// Send in the new value
DEBUG_PRINT("Reed Switch #1: "); DEBUG_PRINT(value==HIGH ? 1 : 0); DEBUG_PRINTLN("");
gw.send(msgReed.set(value==HIGH ? 0 : 1)); //NO
oldValue = value;
}
}
// function for reading Vcc by reading 1.1V reference against AVcc. Based from http://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/
// To calibrate reading replace 1125300L with scale_constant = internal1.1Ref * 1023 * 1000, where internal1.1Ref = 1.1 * Vcc1 (per voltmeter) / Vcc2 (per readVcc() function)
/*long readVcc() {
// set the reference to Vcc and the measurement to the internal 1.1V reference
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
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
}
// function for reading internal temp. From http://playground.arduino.cc/Main/InternalTemperatureSensor
double GetInternalTemp(void) { // (Both double and float are 4 byte in most arduino implementation)
unsigned int wADC;
double t;
// The internal temperature has to be used with the internal reference of 1.1V. Channel 8 can not be selected with the analogRead function yet.
ADMUX = (_BV(REFS1) | _BV(REFS0) | _BV(MUX3)); // Set the internal reference and mux.
ADCSRA |= _BV(ADEN); // enable the ADC
delay(20); // wait for voltages to become stable.
ADCSRA |= _BV(ADSC); // Start the ADC
while (bit_is_set(ADCSRA,ADSC)); // Detect end-of-conversion
wADC = ADCW; // Reading register "ADCW" takes care of how to read ADCL and ADCH.
t = (wADC - 88.0 ) / 1.0; // The default offset is 324.31.
return (t); // The returned temperature in degrees Celcius.
}
/*********************************************
* * Sends temperature and humidity from Si7021 sensor
* Parameters
* - force : Forces transmission of a value (even if it's the same as previous measurement)
*********************************************/
void sendTempHumidityMeasurements(bool force) {
bool tx = force;
si7021_env data = humiditySensor.getHumidityAndTemperature();
float temperature = data.celsiusHundredths / 100.0;
DEBUG_PRINT("T: ");DEBUG_PRINTLN(temperature);
float diffTemp = abs(lastTemperature - temperature);
DEBUG_PRINT(F("TempDiff :"));DEBUG_PRINTLN(diffTemp);
if (diffTemp > TEMP_TRANSMIT_THRESHOLD || tx) {
gw.send(msgTemp.set(temperature,1));
lastTemperature = temperature;
measureCount = 0;
DEBUG_PRINTLN("T sent!");
}
int humidity = data.humidityPercent;
DEBUG_PRINT("H: ");DEBUG_PRINTLN(humidity);
raHum.addValue(humidity);
humidity = raHum.getAverage(); // MA sample imply reasonable fast sample frequency
float diffHum = abs(lastHumidity - humidity);
DEBUG_PRINT(F("HumDiff :"));DEBUG_PRINTLN(diffHum);
if (diffHum > HUMI_TRANSMIT_THRESHOLD || tx) {
gw.send(msgHum.set(humidity));
lastHumidity = humidity;
measureCount = 0;
DEBUG_PRINTLN("H sent!");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment