Monday, February 27, 2017

Update from Farm-Team: Thinking about power #2: Reading the battery level and our first test results

Conclusion to our prior post: Thinking about power #1

Sensor node prototype #2


For our nodes we have decided to use:

  • Led-Acid battery for our main hub 
    • Overcharge tolerance 
    • Low self discharge 
    • Low maintenance 
    • Low price 
  • Li-Ion/NiCd battery for our sensor nodes 
    • Li-Ion due to the availability of cheap charge regulators (i.g. TP4056 <$1) 
    • Fast charge capabilities 
    • High number of (life) cycles 
    • Low price 

Useful links:

  • Very handy comparison of batteries: link
  • Very handy comparison of Li-Ion and NiMh batteries: link
  • Battery charging in relation to temperature: link


Battery charging:
We won't go into to much detail here. We recommend initially using a charge balancer to avoid damaging the battery. Furthermore, overcharging the battery or to quickly discharging battery may be dangerous (fire hazard). 




Choosing the right solar panel depends on the battery type and its charging advisory ("C"). 

We found these tutorials to be helpful: 1, 2 and 3.

Battery level (SOC):



After we researched how to measure battery load (level) we have realised that there is no real easy way to do this but to calculate and estimate a value.

Possible ways to do that:

  • From the voltage (in combination with the temperature) we can find out when the battery is full or is about to be empty. We need the temperature for this as it affects the battery efficiency and its output voltage
  • Or better: We can measure the current and find out how much is being consumed and estimate from the time the battery was full (voltage) the remaining battery life
  • Our main hub also measures light intensity so (as an example) on a cloudy day with battery level we can predict a power shortage and recommend to the user to manually recharge


Charge time in relation to temperature for NiCd

For the main hub we decided to measure current and voltage to determine the SOC. For the sensor nodes we will use the voltage and calculate and predict shortages using the light intensity measurements.
We found this and this tutorial to be very helpful.


    Shopping:

  • 16 channel analog digital Multiplexer Modul CD74HC4067
  • INA219 I2C Bi-directional DC Current Power Supply Sensor 


Sensor nodes

We got the analog multiplexer as the ESP8266 board supplies only one ADC input. We don't need 16 channels however we were not able to find a smaller module with the submission deadline of the challenge advancing. On the other hand, we could add another 14 hygrometer (moisture sensors) to get more readings like for a smaller area with smaller plants.

So set up the analog multiplexer we found this tutorial to be very helpful.
Sensor node final with voltage meter

Now that the multiplexer divides the 3.3 V to 1 V we had no need for the voltage divider we had set up before. How ever the battery voltage at full charge is 4.2 V and we have to get that down to 3.3 V. We used a 1,3 and 5 kOhm respectively. So we get about 1 V  reading on a fully charged battery and about 700mV on a half to almost empty battery level. We will be sending this value to the main hub.



Here is our final code for our sensor node:

 /*  
  *   
  * Smartwatering: Eclipse IoT Challenge  
  * Sensor node  
  *   
  */  
 #include <ESP8266WiFi.h>  
 #include <PubSubClient.h>  
 //Digital pins for Analog multiplexer  
 #define AMUXSEL0 14   // AMUX Selector 0  
 #define AMUXSEL1 12   // AMUX Selector 1  
 #define AMUXSEL2 13   // AMUX Selector 2  
 WiFiClient espClient;  
 PubSubClient client(espClient);  
 long lastMsg = 0;  
 char msg[50];  
 int moisture, battery;  
 int sensorValue = 0;  
 int sensorPin = A0; //ADC pin  
 boolean testmode = true;   
 // Update these with values suitable for your network.  
 const char* ssid = "FarmHub";  
 const char* password = "xxxxxxxxxxxxx";  
 const char* mqtt_server = "192.168.1.1";  
 void setup() {  
  pinMode(AMUXSEL0 , OUTPUT); //Pin 14 for Channel 0 and 1 of the Analog multiplexer  
  Serial.begin(9600);  
  setup_wifi();  
  client.setServer(mqtt_server, 1883);  
  client.setCallback(callback);  
  if (!client.connected()) {  
   reconnect();  
   while (!client.connected()) {  
      Serial.print(".");  
      delay(500);  
    }  
   }  
  client.publish("channel21", "Node connected");  
  sensorValue = analogRead(sensorPin);  
  Serial.println("Moisture voltage: " + String(sensorValue));  
  sensorValue = constrain (sensorValue, 240, 1024);  
  moisture = map (sensorValue, 240, 1024, 100, 0);  
  snprintf (msg, 75, "%ld", moisture);  
  Serial.print("Publish message: ");  
  Serial.println(msg);  
  client.publish("moisture", msg);  
  //Setting pin 14 on high to switch from channel 0 to 1  
  digitalWrite(AMUXSEL0, HIGH);  
  delay(500);  
  sensorValue = analogRead(sensorPin);  
  Serial.println("Battery voltage: " + String(sensorValue));  
  battery = map (sensorValue, 0, 1024, 100, 0);  
  snprintf (msg, 75, "%ld", battery);  
  Serial.print("Publish battery message: ");  
  Serial.println(msg);  
  client.publish("battery", msg);  
  delay(500);  
  //Setting pin 14 on low to switch from channel 1 to 0  
  digitalWrite(AMUXSEL0, LOW);  
  delay(500);  
  client.publish("channel21", "Node disconnected");  
  client.disconnect();     
  WiFi.disconnect();  
  //Go to sleep for about 30 minutes  
  ESP.deepSleep(2000 * 1000000);  
  delay(100);  
 }  
 void setup_wifi() {  
  delay(10);  
  // We start by connecting to a WiFi network  
  Serial.println();  
  Serial.print("Connecting to ");  
  Serial.println(ssid);  
  WiFi.begin(ssid, password);  
  while (WiFi.status() != WL_CONNECTED) {  
   delay(500);  
   Serial.print(".");  
  }  
  Serial.println("");  
  Serial.println("WiFi connected");  
  Serial.println("IP address: ");  
  Serial.println(WiFi.localIP());  
 }  
 void callback(char* topic, byte* payload, unsigned int length) {  
  Serial.print("Message arrived [");  
  Serial.print(topic);  
  Serial.print("] ");  
  for (int i = 0; i < length; i++) {  
   Serial.print((char)payload[i]);  
  }  
  Serial.println();  
  // Switch on the LED if an 1 was received as first character  
  if ((char)payload[0] == '1') {  
   // digitalWrite(BUILTIN_LED, LOW);  // Turn the LED on (Note that LOW is the voltage level  
   // but actually the LED is on; this is because  
   // it is acive low on the ESP-01)  
  } else {  
   //digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH  
  }  
 }  
 void reconnect() {  
  // Loop until we're reconnected  
  while (!client.connected()) {  
   Serial.print("Attempting MQTT connection...");  
   // Attempt to connect  
   if (client.connect("ESP8266Client")) {  
    Serial.println("connected");  
    // Once connected, publish an announcement...  
    client.publish("channel21", "Node connected");  
    // ... and resubscribe  
    client.subscribe("channel21");  
   } else {  
    Serial.print("failed, rc=");  
    Serial.print(client.state());  
    Serial.println(" try again in 5 seconds");  
    // Wait 5 seconds before retrying  
    delay(5000);  
   }  
  }  
 }  
 void loop() {  
 }  






Main hub

Connecting the INA219 was easy as it has an I2C interface and we already had a I2C multiplexer with two sensors connected to it. Now we are getting the following additional measurements:
  •   shunt voltage
  •   bus voltage
  •   current
And we can calculate the  load voltage = bus voltage + (shunt voltage / 1000)

Here is our final code:

 /*  
  *   
  * Smartwatering: Eclipse IoT Challenge  
  * Main hub  
  *   
  */  
 //Libraries  
 #include <DHT.h>  
 #include <BH1750.h>  
 #include <Adafruit_BMP085.h>  
 #include <NanoESP.h>  
 #include <SoftwareSerial.h>  
 #include <Adafruit_INA219.h>  
 #include <Wire.h>  
 //I2C Mux Addr  
 #define TCAADDR 0x70  
 //nanoespdebug  
 #define DEBUG false  
 //Constants  
 #define DHTPIN 2   // what pin we're connected to  
 #define DHTTYPE DHT22  // DHT 22 (AM2302)  
 DHT dht(DHTPIN, DHTTYPE); //// Initialize DHT sensor for normal 16mhz Arduino  
 BH1750 lightMeter;  
 Adafruit_BMP085 bmp;  
 Adafruit_INA219 ina219;  
 NanoESP nanoesp = NanoESP();  
 int sensorValue = 0;  
 int rain = 0;  
 int sensorPin = A0;  
 //int voltage, current;  
 void tcaselect(uint8_t i) {  
  if (i > 7) return;  
  Wire.beginTransmission(TCAADDR);  
  Wire.write(1 << i);  
  Wire.endTransmission();   
 }  
 void setup()   
 {  
  Serial.begin(19200);  
  nanoesp.init(DEBUG);  
  /* Initialise the 1st sensor */  
  tcaselect(2);  
  lightMeter.begin();  
  delay(500);  
  /* Initialise the 2nd sensor */  
  tcaselect(4);  
  ina219.begin();  
  delay(500);  
  /* Initialise the 3rd sensor etc*/  
  tcaselect(6);  
  if(!bmp.begin())  
  {  
   /* There was a problem detecting the BMP ... check your connections */  
   Serial.println("Ooops, no BMP detected ... Check your wiring!");  
   while(1);  
  }  
  /* Dht22 init */  
   dht.begin();  
 }  
 void loop()   
 {  
  //Sending esp board to deep sleep to save power  
  nanoesp.sendCom("AT+GSLP=1000000");  
  //Going through sensors to get values, tca select is used to multiplex i2c  
  tcaselect(2);  
  displaySensorDetailsBH();  
  delay(500);  
  tcaselect(6);  
  displaySensorDetailsBMP();  
  delay(500);  
  displaySensorDetailsDHT();  
  delay(500);  
  displaySensorDetailsRain();  
  delay(500);  
  tcaselect(4);  
  displaySensorDetailsPower();  
  delay(1000000);  
 }  
 void displaySensorDetailsBH() {  
  uint16_t lux = lightMeter.readLightLevel();  
  Serial.print("{\"d\":{");  
  Serial.print("\"");  
  Serial.print("light");  
  Serial.print("\" : ");  
  Serial.print(lux);  
  Serial.print(",");  
 }  
 void displaySensorDetailsBMP() {  
  Serial.print("\"");  
  Serial.print("temperature1");  
  Serial.print("\" : ");  
  Serial.print(bmp.readTemperature());  
  Serial.print(", \"");  
  Serial.print("pressure");  
  Serial.print("\" : ");  
  Serial.print(bmp.readPressure());  
  Serial.print(", \"");  
  // Calculate altitude assuming 'standard' barometric  
  // pressure of 1013.25 millibar = 101325 Pascal  
  Serial.print("altitude");  
  Serial.print("\" : ");  
  Serial.print(bmp.readAltitude());  
  Serial.print(", \"");  
  Serial.print("calculatedpressureatsealevel");  
  Serial.print("\" : ");  
  Serial.print(bmp.readSealevelPressure());  
  // you can get a more precise measurement of altitude  
  // if you know the current sea level pressure which will  
  // vary with weather and such. If it is 1015 millibars  
  // that is equal to 101500 Pascals.  
  Serial.print(", \"");  
  Serial.print("realaltitude");  
  Serial.print("\" : ");  
  Serial.print(bmp.readAltitude(101500));  
  Serial.print(",");  
 }  
 void displaySensorDetailsDHT() {  
   float h = dht.readHumidity(); //Luftfeuchte auslesen  
   float t = dht.readTemperature(); //Temperatur auslesen  
   // Prüfen ob eine gültige Zahl zurückgegeben wird. Wenn NaN (not a number) zurückgegeben wird, dann Fehler ausgeben.  
   if (isnan(t) || isnan(h))   
   {  
   Serial.println("DHT22 konnte nicht ausgelesen werden");  
   }   
   else  
   {  
   Serial.print("\"");  
   Serial.print("humidity");  
   Serial.print("\" : ");  
   Serial.print(h);  
   Serial.print(", ");  
   Serial.print("\"");  
   Serial.print("temperature2");  
   Serial.print("\" : ");  
   Serial.print(t);  
   Serial.print(",");  
  }  
 }  
 void displaySensorDetailsRain() {  
  sensorValue = analogRead(sensorPin);  
  rain = map (sensorValue, 250, 1024, 100, 0);  
  Serial.print("\"");  
  Serial.print("rain");  
  Serial.print("\" : ");  
  Serial.print(rain);  
 }  
  void displaySensorDetailsPower() {  
  float shuntvoltage = 0;  
  float busvoltage = 0;  
  float current_mA = 0;  
  float loadvoltage = 0;  
  shuntvoltage = ina219.getShuntVoltage_mV();  
  busvoltage = ina219.getBusVoltage_V();  
  current_mA = ina219.getCurrent_mA();  
  loadvoltage = busvoltage + (shuntvoltage / 1000);  
  Serial.print(",\"busVoltage \": "); Serial.print(busvoltage);   
  Serial.print(",\"shuntVoltage \": "); Serial.print(shuntvoltage);   
  Serial.print(",\"loadVoltage\" : "); Serial.print(loadvoltage);   
  Serial.print(",\"current\" : "); Serial.print(current_mA);   
  Serial.print("}}");  
  Serial.println();  
 }  








1 comment: