Marian Mihailescu
Published © Apache-2.0

Baby NAP (Night Activity Program)

Being a parent for the first time is a big challenge. With the Baby Night Activity Program and a lot of sensors, parents will have new help.

AdvancedFull instructions provided7,501
Baby NAP (Night Activity Program)

Things used in this project

Hardware components

Odroid C1
×1
Adafruit PN532 NFC/RFID controller breakout board
×1
Adafruit 13.56MHz RFID/NFC Bracelet
×2
Adafruit TSL2561 Digital Luminosity/Lux/Light Sensor Breakout
×1
PIR Motion Sensor (generic)
PIR Motion Sensor (generic)
×1
SparkFun Sound Detector
×1
Pressure Mat PM1
×1

Software apps and online services

AWS IoT
Amazon Web Services AWS IoT
AWS DynamoDB
Amazon Web Services AWS DynamoDB
AWS Lambda
Amazon Web Services AWS Lambda
AWS IAM
Amazon Web Services AWS IAM
AWS S3
Amazon Web Services AWS S3

Story

Read more

Schematics

Baby NAP fritzing Schematic

Baby NAP fritzing Schematic - Image

File missing, please reupload.

Code

Python Script to Upload Sensor Data

Python
#!/usr/bin/python
import paho.mqtt.client  as mqtt
import paho.mqtt.publish as publish
import time,json,ssl
import wiringpi2 as wpi
from tentacle_pi.TSL2561 import TSL2561
import nfc, sys, threading

def on_connect(mqttc, obj, flags, rc):
    if rc == 0:
        print "Connected to the AWS IoT service!"
    else :
        print("Error connecting to AWS IoT service! (Error code " + str(rc) + ": " + RESULT_CODES[rc] + ")")
        client.disconnect()

#Connect to AWS IoT
client = mqtt.Client(client_id="odroid-c1", protocol=mqtt.MQTTv311)
client.on_connect = on_connect
client.tls_set("certs/root-CA.crt", certfile="certs/certificate.pem.crt", keyfile="certs/private.pem.key", tls_version=ssl.PROTOCOL_SSLv23, ciphers=None)
client.tls_insecure_set(True)
client.connect("A32L40P6IYKK8W.iot.us-east-1.amazonaws.com", 8883, 60)
client.loop_start()

# GPIO pin setup
wpi.wiringPiSetup()
wpi.pinMode(2, 0) # PIR sensor on wpi GPIO2
wpi.pinMode(3, 0) # mat pressure sensor on wpi GPIO3
wpi.pinMode(21, 0) # sound sensor GATE on wpi GPIO21

# I2C TSL2561 setup
tsl = TSL2561(0x39,"/dev/i2c-1")
tsl.enable_autogain()
tsl.set_time(0x00)

# nfc functions
def connected(tag):
    global nfcid
    nfcid = str(tag)[12:]
    print "read successful"
    time.sleep(3)
    return False

def reader():
    while True:
        # we do a read every 5s
        timeout = lambda: time.time() - started > 5
        started = time.time()
        device = nfc.ContactlessFrontend('tty:S2:pn532')
        device.connect(rdwr={'on-connect': connected}, terminate=timeout)
        device.close()
        

nfcid = 0
thread = threading.Thread(target=reader)
thread.start()
# end nfc setup

count = 0
mlux = mpir = mmat = msound = mvolume = 0

while True:
    time.sleep(3)
    # read sensor data
    ts = int(time.time())
    lux = tsl.lux()
    pir = wpi.digitalRead(2)
    mat = wpi.digitalRead(3)
    sound = wpi.digitalRead(21)
    volume = wpi.analogRead(0)*255/2047 # 0-10=quiet, 10-30=moderate, 30-127=loud

    mom = 0
    dad = 0
    if mat == 0:
        if nfcid == 'F10B330F':
            mom = 1
        elif nfcid == '833BC4A2':
            dad = 1

    if lux > mlux:
        mlux = lux
    if pir > mpir:
        mpir = pir
    if mat < mmat:
        mmat = mat
    if sound > msound:
        msound = sound
    if volume > mvolume:
        mvolume = volume

    # send data to AWS
    if count == 0:
        msg = {'ts': ts, 'lux': mlux, 'pir': mpir, 'mat': mmat, 'sound': msound, 'volume': mvolume, 'mom': mom, 'dad': dad}
        print json.dumps(msg)
        client.publish('sensors', json.dumps(msg))
        mlux = mpir = mmat = msound = mvolume = 0
        if mmat == 1:
            nfcid = 0
thread = threading.Thread(target=reader)
thread.start()
# end nfc setup

count = 0
mlux = mpir = mmat = msound = mvolume = 0

while True:
    time.sleep(3)
    # read sensor data
    ts = int(time.time())
    lux = tsl.lux()
    pir = wpi.digitalRead(2)
    mat = wpi.digitalRead(3)
    sound = wpi.digitalRead(21)
    volume = wpi.analogRead(0)*255/2047 # 0-10=quiet, 10-30=moderate, 30-127=loud

    mom = 0
    dad = 0
    if mat == 0:
        if nfcid == 'F10B330F':
            mom = 1
        elif nfcid == '833BC4A2':
            dad = 1

    if lux > mlux:
        mlux = lux
    if pir > mpir:
        mpir = pir
    if mat < mmat:
        mmat = mat
    if sound > msound:
        msound = sound
    if volume > mvolume:
        mvolume = volume

    # send data to AWS
    if count == 0:
        msg = {'ts': ts, 'lux': mlux, 'pir': mpir, 'mat': mmat, 'sound': msound, 'volume': mvolume, 'mom': mom, 'dad': dad}
        print json.dumps(msg)
        client.publish('sensors', json.dumps(msg))
        mlux = mpir = mmat = msound = mvolume = 0
        if mmat == 1:
            nfcid = 0
    count = (count + 1) % 20

index.html to build charts

HTML
<!DOCTYPE html>
<html>

<head>
  <title>Baby Night Activity Monitor</title>
  <link href='https://fonts.googleapis.com/css?family=Lato' rel='stylesheet' type='text/css'>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js"></script>
  <script src="https://code.highcharts.com/stock/highstock.js"></script>
  <script src="https://sdk.amazonaws.com/js/aws-sdk-2.2.33.min.js"></script>
</head>

<body style="font-family: 'Lato', sans-serif;">
<center>
  
  <br/>
  <h1>Baby Night Activity Monitor</h1>
  <br/>
  <!-- initial screen with login -->
  <div id="splash">
    <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/7/71/Child_in_crib_being_supervised_clip_art.svg/253px-Child_in_crib_being_supervised_clip_art.svg.png" width="253" height="240"></img>
    <br/><br/><br/>
    <div style="display:flex; align-items:center; justify-content: center;">
      You need to &nbsp;
      <a href="#" id="login">
        <img border="0" alt="Login with Amazon" src="https://images-na.ssl-images-amazon.com/images/G/01/lwa/btnLWA_drkgry_156x32.png" width="156" height="32" />
      </a>
      &nbsp; to access this application.
    </div>
    <br/><br/>
  </div>

  <!-- amazon login element -->
  <div id="amazon-root"></div>

  <!-- containers for charts -->
  <div id="main" style="display: none;">
    <div id="sound1" style="height: 398px; min-width: 600px; max-width: 960px;"></div><br/>
    <div id="mat1" style="height: 300px; min-width: 600px; max-width: 960px;"></div><br/>
    <div id="light1" style="height: 300px; min-width: 600px; max-width:   960px;"></div><br/>
    <div id="pir1" style="height: 300px; min-width: 600px; max-width: 960px;"></div><br/>
 </div>
  <!-- Javascript application -->
  <script type="text/javascript">
    // AWS credentials
    var clientId = 'amzn1.application-oa2-client.7b4ede4c6f32478d8dced3fa29ba7c30';
    var roleArn = 'arn:aws:iam::906035442422:role/aws_web_dynamoDB';
  
    // Highcharts theme defaults
    var chartTheme = {
      global: { useUTC: true },
      credits: { enabled: false },
      tooltip: { useHTML: true },
      chart: { zoomType: null, panning: false, alignTicks: true, backgroundColor: 'white', plotBackgroundColor: 'white', ignoreHiddenSeries: true, borderRadius: 0, borderWidth: 1, borderColor: 'black', spacingRight: 20, plotBorderWidth: 1, plotBorderColor: 'grey' },
      title: { style: { color: 'black', font: 'bold 14px "Trebuchet MS", Verdana, sans-serif', textTransform: 'capitalize'}, floating: false },
      xAxis: { title: { style: { color: '#333', font: '12px "Trebuchet MS", Verdana, sans-serif'} }, lineWidth: 0, lineColor: 'grey', gridLineWidth: 1, gridLineColor: '#ffb2b2', gridLineDashStyle: 'ShortDot', ordinal: false, labels: { overflow: 'justify' }, startOnTick: false, endOnTick: false
      },
      yAxis: { title: { style: { color: '#333', font: '12px "Trebuchet MS", Verdana, sans-serif'} }, lineWidth: 0, lineColor: 'grey', tickPixelInterval: 36, minorTickPixelInterval: 36, gridLineColor: '#ffb2b2', gridLineDashStyle: 'ShortDot', labels: { overflow: 'justify' }, startOnTick: true, endOnTick: true },
      legend: { enabled: true, backgroundColor: 'white', borderRadius: 0, borderColor: 'grey', itemStyle: { color: '#333', font: '12px "Trebuchet MS", Verdana, sans-serif'} }
    };

    // global database variable; 
    var db;
    // global chart variables
    var light1, mat1, pir1, sound1;

    // function to sort highcharts data array of arrays by timestamp (x)
    var bySeriesTimestamp = function(a, b){
      var ats = a[0];
      var bts = b[0]; 
      return ((ats < bts) ? -1 : ((ats > bts) ? 1 : 0));
    }

    // function that requests live data from AWS DynamoDB
    function requestData() {
      if (!db) return;
      // get current max timestamp from chart
      var ts_current = light1 && light1.xAxis && light1.xAxis[0].getExtremes().max ? light1.xAxis[0].getExtremes() : {max: 0};
      // request chart data from AWS, with timestamp > ts_current
      db.scan({TableName: 'sensors', FilterExpression: '#ts > :ts_current', ExpressionAttributeNames: {'#ts': 'device_ts'}, ExpressionAttributeValues: {':ts_current': {'S': String(ts_current.max)}}}, function(err, data) {
        if (err) {
          console.log(err, err.stack);
          return;
        } else {
          var light1data = []; //chart ? chart.series[0].data : [];
          var mat1data = []; //chart ? chart.series[0].data : [];
          var sound1data = []; //chart ? chart.series[0].data : [];
          var sound2data = []; //chart ? chart.series[0].data : [];
          var pir1data = []; //chart ? chart.series[0].data : [];
          console.log("update charts with " + data.Items.length + " items");
          _.each(data.Items, function(item) {
            payload = item.payload.M;
            point = [];
            point.push(Number(payload.ts.N)*1000);
            point.push(Number(payload.lux.N));
            light1data.push(point);
            point = [];
            point.push(Number(payload.ts.N)*1000);
            point.push(Number(payload.mat.N));
            mat1data.push(point);
            point = [];
            point.push(Number(payload.ts.N)*1000);
            point.push(Number(payload.sound.N));
            sound1data.push(point);
            point = [];
            point.push(Number(payload.ts.N)*1000);
            point.push(Number(payload.volume.N));
            sound2data.push(point);
            point = [];
            point.push(Number(payload.ts.N)*1000);
            point.push(Number(payload.pir.N));
            pir1data.push(point);
          });
          // highcharts required data to be sorted
          light1data.sort(bySeriesTimestamp);
          mat1data.sort(bySeriesTimestamp);
          sound1data.sort(bySeriesTimestamp);
          sound2data.sort(bySeriesTimestamp);
          pir1data.sort(bySeriesTimestamp);

          light1.hideLoading();
          mat1.hideLoading();
          sound1.hideLoading();
          pir1.hideLoading();

          var i = 0;
          for (i; i < light1data.length; i++) {
            // add data points in series, no redraw
            light1.series[0].addPoint(light1data[i], false);
            mat1.series[0].addPoint(mat1data[i], false);
            sound1.series[0].addPoint(sound1data[i], false);
            sound1.series[1].addPoint(sound2data[i], false);
            pir1.series[0].addPoint(pir1data[i], false);
          }
          // redraw chart
          light1.redraw();
          mat1.redraw();
          sound1.redraw();
          pir1.redraw();
          // run function again in 1 minute to request for newer data
          setTimeout(requestData, 60000); 
        }
      });
    }
  
    // main function, perform login and populate charts
    window.onAmazonLoginReady = function() {
      amazon.Login.setClientId(clientId);
      // callback function that populates charts after login
      document.getElementById('login').onclick = function() {
        amazon.Login.authorize({scope: 'profile'}, function(response) {
          if (!response.error) { // logged in
            AWS.config.credentials = new AWS.WebIdentityCredentials({
              RoleArn: roleArn,
              ProviderId: 'www.amazon.com',
              WebIdentityToken: response.access_token
            });
          
            // you are now logged in
            $("#splash").remove();
            $("#main").show();
            Highcharts.setOptions(chartTheme);
            
            // start using amazon services
            AWS.config.region = 'us-east-1';
            db = new AWS.DynamoDB();
            
            sound1el = $('#sound1').highcharts('StockChart', {
              chart: {
                zoomType: 'x'
              },
              title: {
                text: 'Sound Sensor'
              },
              xAxis: {
                title: { text: 'Date/Time Navigator (select range in all charts)', margin: 15 },
                events: {
                    afterSetExtremes: function (e) {
                      if (e.trigger == 'navigator' || e.trigger == 'zoom') {
                        light1.xAxis[0].setExtremes(e.min, e.max);
                        mat1.xAxis[0].setExtremes(e.min, e.max);
                        pir1.xAxis[0].setExtremes(e.min, e.max);
                      }
                    }
                }
              },
              yAxis: [{
                opposite: false,
                title: { text: 'Sound detected' }
              }, {
                opposite: true,
                title: {text: 'Sound volume'},
                plotBands: [{
                  from: 10,
                  to: 40,
                  color: '#fbe2a5'
                }, {
                  from: 40,
                  to: 127,
                  color: '#fbb7a5'
                }]
              }],
              rangeSelector: {
                  enabled: false
              },
              navigator: {
                enabled: true,
                baseSeries: -1,
                height: 30,
                margin: 10
              },
              series : [{
                type: 'area',
                color: '#f75c63',
                name : 'Sound',
                data : [],
                step: true
              }, {
                type: 'line',
                name : 'Volume',
                data : [],
                step: true,
                yAxis: 1
              }]
            });
            sound1 = sound1el.highcharts();
            sound1.showLoading();

            light1el = $('#light1').highcharts('StockChart', {
              chart: {
                defaultSeriesType: 'line'
              },
              title: {
                text: 'Light Sensor'
              },
              yAxis: {
                type: 'logarithmic',
                opposite: false,
                title: { text: 'Luminosity (lux)' },
                plotBands: [{
                  from: 0,
                  to: 40,
                  color: '#e3e3e3'
                }, {
                  from: 2000,
                  to: 100000,
                  color: '#fbe2a5'
                }]
              },
              rangeSelector: {
                  enabled: false
              },
              navigator: {
                enabled: false
              },
              scrollbar: {
                enabled: false
              },
              series : [{
                name : 'Luminosity',
                data : [],
                step: true
              }]
            });
            light1 = light1el.highcharts();
            light1.showLoading();

            mat1el = $('#mat1').highcharts('StockChart', {
              chart: {
                defaultSeriesType: 'line'
              },
              title: {
                text: 'Pressure Switch (Mat)'
              },
              yAxis: {
                opposite: false,
                title: { text: 'Baby in crib' }
              },
              rangeSelector: {
                  enabled: false
              },
              navigator: {
                enabled: false
              },
              scrollbar: {
                enabled: false
              },
              series : [{
                type: 'area',
                color: '#4f4f55',
                name : 'Pressure',
                data : [],
                step: true
              }]
            });
            mat1 = mat1el.highcharts();
            mat1.showLoading();

            pir1el = $('#pir1').highcharts('StockChart', {
              chart: {
                defaultSeriesType: 'line',
                events: {
                  load: requestData
                }
              },
              title: {
                text: 'Motion Sensor'
              },
              yAxis: {
                opposite: false,
                title: { text: 'Motion detected' }
              },
              rangeSelector: {
                  enabled: false
              },
              navigator: {
                enabled: false
              },
              scrollbar: {
                enabled: false
              },
              series : [{
                name : 'Motion',
                type: 'area',
                color: '#b6f3aa',
                data : [],
                step: true
              }]
            });
            pir1 = pir1el.highcharts();
            pir1.showLoading();


          } else {
            // error logging in to amazon
            console.log('There was a problem logging you in.');
          }
        });
      };
    };

    (function(d) {
      var a = d.createElement('script'); a.type = 'text/javascript';
      a.async = true; a.id = 'amazon-login-sdk';
      a.src = 'https://api-cdn.amazon.com/sdk/login1.js';
      d.getElementById('amazon-root').appendChild(a);
    })(document);
  </script>
</center>
</body>
</html>

Credits

Marian Mihailescu

Marian Mihailescu

1 project • 12 followers
Contact

Comments