Close

Source code!

A project log for WiFi Logger

Simple ESP8266 based data logger, sending data to the cloud over WiFi

patrick-van-oosterwijckPatrick Van Oosterwijck 09/09/2016 at 17:070 Comments

So here's the code for my WiFi Logger, using JavaScript with Espruino running on the ESP8266. I created Espruino modules to log to InitialState and Losant and pushed them upstream. Gordon merged my pull request but it seems the modules did not make it to the website yet (I don't know how often he regenerates it from Github). For now, you can find them in the EspruinoDocs repo.

I made the code in such a way that you can choose which cloud service the data is sent to by setting the cloudService variable. I cleared my credentials from the code for obvious reasons, you'll need to get your own. :)

var cloudService = 'initialstate';

if (cloudService === 'initialstate') {
  initialState = require('InitialState');

  initialStateBucket = {
      bucketId: 'your bucket ID',
      accessId: 'your access ID'
  };
}

if (cloudService === 'losant') {
  var losantDeviceId = 'your device ID';
  var losantDeviceAuth = {
    key: 'your access key',
    secret: 'your access secret'
  };

  losant = require('Losant').setup(losantDeviceId, losantDeviceAuth);
}


I2C1.setup({scl: NodeMCU.D4, sda: NodeMCU.D3});

var bme = require('BME280').connect(I2C1);
var ads = require('ADS1X15').connect(I2C1);
var wifi = require('Wifi');
var esp = require('ESP8266');


function getMedianEnvData(number, callback) {
  function sorter(a, b) { return 10e6 * (a - b); }
  var num = number;
  var temp = [];
  var press = [];
  var hum = [];
  var timer = setInterval(function () {
    bme.readRawData();
    temp.push(bme.calibration_T(bme.temp_raw) / 100.0);
    press.push(bme.calibration_P(bme.pres_raw) / 100.0);
    hum.push(bme.calibration_H(bme.hum_raw) / 1024.0);
    if (--num === 0) {
      clearInterval(timer);
      temp.sort(sorter);
      press.sort(sorter);
      hum.sort(sorter);
      callback({
        temperature: temp[number >> 1],
        pressure: press[number >> 1],
        humidity: hum[number >> 1]
      });
    }
  }, 1500);
}

function getChargerData(callback) {
  var data = {};
  ads.setGain(4096);
  ads.getADCVoltage(0, function (val) {
    data.inputVoltage = val * 208.2 / 8.2;
    ads.getADCVoltage(1, function (val) {
      data.batteryVoltage = val;
      ads.getADCVoltage(3, function (val) {
        data.outputVoltage = val;
        ads.getADCVoltage([2,3], function (val) {
          data.chargeCurrent = val / 0.27;
          callback(data);
        });
      });
    });
  });
}

var timeout = 20;

function sendData(envdata, chargerdata) {
  if (wifi.getDetails().status !== 'connected') {
    timeout--;
    if (timeout === 0) {
      console.log('No WiFi connection');
      esp.deepSleep(60000000);
    } else {
      setTimeout(sendData, 1000, envdata, chargerdata);
    }
  } else {
    if (cloudService === 'initialstate') {
      initialState.sendEventsInsecure(initialStateBucket, envdata,
                                      chargerdata, function (err) {
        if (!err) console.log('Sent to InitialState');
        esp.deepSleep(60000000);
      });
    }
    if (cloudService === 'losant') {
      losant.updateDeviceData({
        temp: envdata.temperature,
        humidity: envdata.humidity,
        pressure: envdata.pressure,
        voltage_solar: chargerdata.inputVoltage,
        voltage_bat: chargerdata.batteryVoltage,
        current_charge: chargerdata.chargeCurrent
      }, function (err) {
        if (!err) console.log('Sent to Losant');
        esp.deepSleep(60000000);
      });
    }
  }
}

E.on('init', function() {
  getMedianEnvData(5, function (envdata) {
    console.log(envdata);
    getChargerData(function (chargerdata) {
      console.log(chargerdata);
      sendData(envdata, chargerdata);
    });
  });
});
Some of this stuff is of course very specific to my application, but it gives you the gist. After you load this code with the Espruino IDE, you need to type save() in the terminal to save it to flash. It makes use of the deep sleep timer to save power. For this to work, GPIO16 needs to be connected to the RESET pin. Every 60 seconds, the device will wake up, take readings, send them to the cloud and go back to sleep.

I had issues with crappy readings from the BME280 that would cause a signal with significant spikes. That is the reason I implemented the filtering to get the median value. It's possible this is not necessary if everything is integrated on a nice board instead of a mess connected together with fly wires. :)

Below is a screen shot of environmental data taken overnight in my office. As you can tell, it gets hot in my office (I have Bitcoin miners running). You can tell the air conditioning was turned off from 10 PM to 6 AM. You can tell when I closed my office and when I came back in the morning. It also shows nicely how the relative humidity changes with temperature, while the absolute humidity must have stayed pretty much the same.

Now I have to get all of this outside with a solar panel. :)

Discussions