Hackster is hosting Hackster Holidays, Ep. 7: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Friday!Stream Hackster Holidays, Ep. 7 on Friday!
Peter MaSHIN AE HONG
Published © GPL3+

Walabot Sleep Quality Tracker

Track breathing during sleep using AI as an early detection system for sleep apnea and other sleep breathing disorders.

AdvancedWork in progress24 hours3,317

Things used in this project

Story

Read more

Schematics

Workflow

1) Walabot connects to IoT Device with Ubuntu
2) Upload information to the cloud
3) Alexa and other mobile device can extract information

Code

IoT Server Storage Code

JavaScript
This is the server code that stores data, currently we are just storing them into a data.txt file for sample data storage purpose, but it can be extended to mongodb and others
const express = require('express')
const path = require('path')
const PORT = process.env.PORT || 5000
var fs = require('fs');

var app = express()

// respond with "hello world" when a GET request is made to the homepage
app.get('/', function (req, res) {
	fs.readFile('data.txt', 'utf8', function readFileCallback(err, data){
	    if (err){
	        console.log(err);
	    } else {
	    obj = JSON.parse(data); //now it an object
	    res.send(JSON.stringify(obj));
	}});
})

app.get('/input', function (req, res) 
{	var fs = require('fs');
	var high = req.query.high;
	var medium = req.query.medium;
	fs.readFile('data.txt', 'utf8', function readFileCallback(err, data){
	    if (err){
	        console.log(err);
	    } else {
	    obj = JSON.parse(data); //now it an object
	    obj.high += parseInt(high);
	    obj.medium += parseInt(medium); //add some data
	    json = JSON.stringify(obj); //convert it back to json
	    fs.writeFile('data.txt', json, 'utf8', null); // write it back
	    res.send('success') 
	}});
})

app.listen(PORT, () => console.log(`Listening on ${ PORT }`))

Alexa lambda nodejs code

JavaScript
This is the code for Alexa on lambda side, copy/paste them into AWS Lambda
'use strict';
var http = require('https'); 

exports.handler = function (event, context) {
    try {
        console.log("event.session.application.applicationId=" + event.session.application.applicationId);

        /**
         * Uncomment this if statement and populate with your skill's application ID to
         * prevent someone else from configuring a skill that sends requests to this function.
         */
		 
     if (event.session.application.applicationId !== "amzn1.ask.skill.2af2edd4-3027-4b3d-aa4a-f9a974d82ee6") {
         context.fail("Invalid Application ID");
      }

        if (event.session.new) {
            onSessionStarted({requestId: event.request.requestId}, event.session);
        }

        if (event.request.type === "LaunchRequest") {
            onLaunch(event.request,
                event.session,
                function callback(sessionAttributes, speechletResponse) {
                    context.succeed(buildResponse(sessionAttributes, speechletResponse));
                });
        } else if (event.request.type === "IntentRequest") {
            onIntent(event.request,
                event.session,
                function callback(sessionAttributes, speechletResponse) {
                    context.succeed(buildResponse(sessionAttributes, speechletResponse));
                });
        } else if (event.request.type === "SessionEndedRequest") {
            onSessionEnded(event.request, event.session);
            context.succeed();
        }
    } catch (e) {
        context.fail("Exception: " + e);
    }
};

/**
 * Called when the session starts.
 */
function onSessionStarted(sessionStartedRequest, session) {
    console.log("onSessionStarted requestId=" + sessionStartedRequest.requestId
        + ", sessionId=" + session.sessionId);

    // add any session init logic here
}

/**
 * Called when the user invokes the skill without specifying what they want.
 */
function onLaunch(launchRequest, session, callback) {
    console.log("onLaunch requestId=" + launchRequest.requestId
        + ", sessionId=" + session.sessionId);

    var cardTitle = "Welcome to Sleep Tracker"
    var speechOutput = "Welcome to Sleep Tracker"
    callback(session.attributes,
        buildSpeechletResponse(cardTitle, speechOutput, "", true));
}

/**
 * Called when the user specifies an intent for this skill.
 */
function onIntent(intentRequest, session, callback) {
    console.log("onIntent requestId=" + intentRequest.requestId
        + ", sessionId=" + session.sessionId);

    var intent = intentRequest.intent,
        intentName = intentRequest.intent.name;

    // dispatch custom intents to handlers here
    if (intentName == 'SleepTrackIntent') {
        handleTrackRequest(intent, session, callback);
    }
    else {
        throw "Invalid intent";
    }
}

/**
 * Called when the user ends the session.
 * Is not called when the skill returns shouldEndSession=true.
 */
function onSessionEnded(sessionEndedRequest, session) {
    console.log("onSessionEnded requestId=" + sessionEndedRequest.requestId
        + ", sessionId=" + session.sessionId);

    // Add any cleanup logic here
}

function handleTrackRequest(intent, session, callback) {
    var url = "https://murmuring-bayou-68628.herokuapp.com/"; //you can use your own
                http.get(url, function(res){ 
                    res.setEncoding('utf8');
                    res.on('data', function (chunk) {
                        console.log('BODY: ' + chunk);
                        var chunk = JSON.parse(chunk);
                        if(chunk.medium < 100 && chunk.high < 100)
                        {                            
                            callback(session.attributes, buildSpeechletResponseWithoutCard("According to sleep tracker, you had a really good night sleep", "", "true"));
                        }
                        else if(chunk.medium > 100 && chunk.high < 100)
                        {                            
                            callback(session.attributes, buildSpeechletResponseWithoutCard("According to sleep tracker, it seems that you had a lot of movements during sleep, try some exercise will improve your sleep quality", "", "true"));
                        }
                        else if(chunk.high > 100)
                        {                 
                            callback(session.attributes, buildSpeechletResponseWithoutCard("According to sleep tracker, you didn't sleep much last night, try to go to bed earlier tonight", "", "true"));
                        }
                    })
                }).on('error', function (e) { 
                        callback(session.attributes, buildSpeechletResponseWithoutCard("There was a problem getting connected to your sleep tracker", "", "true"));
                })
    //callback(session.attributes, buildSpeechletResponseWithoutCard("test", "", "true"));
}

// ------- Helper functions to build responses -------

function buildSpeechletResponse(title, output, repromptText, shouldEndSession) {
    return {
        outputSpeech: {
            type: "PlainText",
            text: output
        },
        card: {
            type: "Simple",
            title: title,
            content: output
        },
        reprompt: {
            outputSpeech: {
                type: "PlainText",
                text: repromptText
            }
        },
        shouldEndSession: shouldEndSession
    };
}

function buildSpeechletResponseWithoutCard(output, repromptText, shouldEndSession) {
    return {
        outputSpeech: {
            type: "PlainText",
            text: output
        },
        reprompt: {
            outputSpeech: {
                type: "PlainText",
                text: repromptText
            }
        },
        shouldEndSession: shouldEndSession
    };
}

function buildResponse(sessionAttributes, speechletResponse) {
    return {
        version: "1.0",
        sessionAttributes: sessionAttributes,
        response: speechletResponse
    };
}

Walabot Python Code on Ubuntu

Python
This is the Ubuntu Python code, including the plot as well as server update.
#!/usr/bin/env python3
from __future__ import print_function # WalabotAPI works on both Python 2 an 3.
from sys import platform
from os import system
from imp import load_source
from os.path import join
import time, random
import math
from collections import deque
import urllib.request

modulePath = join('/usr', 'share', 'walabot', 'python', 'WalabotAPI.py')     


wlbt = load_source('WalabotAPI', modulePath)
wlbt.Init()
start = time.time()

class RealtimePlot:
    def __init__(self, axes, max_entries =100):
        self.axis_x = deque(maxlen=max_entries)
        self.axis_y = deque(maxlen=max_entries)
        self.axes = axes
        self.max_entries = max_entries
        
        self.lineplot, = axes.plot([], [], "ro-")
        self.axes.set_autoscaley_on(True)

    def add(self, x, y):
        self.axis_x.append(x)
        self.axis_y.append(y)
        self.lineplot.set_data(self.axis_x, self.axis_y)
        self.axes.set_xlim(self.axis_x[0], self.axis_x[-1] + 1e-15)
        self.axes.set_ylim(0, 0.2)
        self.axes.relim(); self.axes.autoscale_view() # rescale the y-axis

    def animate(self, figure, callback, interval = 50):
        import matplotlib.animation as animation
        def wrapper(frame_index):
            self.add(*callback(frame_index))
            self.axes.relim(); self.axes.autoscale_view() # rescale the y-axis
            return self.lineplot
        animation.FuncAnimation(figure, wrapper, interval=interval)

def main():
    from matplotlib import pyplot as plt

    # Walabot_SetArenaR - input parameters
    minInCm, maxInCm, resInCm = 30, 150, 1
    # Walabot_SetArenaTheta - input parameters
    minIndegrees, maxIndegrees, resIndegrees = -4, 4, 2
    # Walabot_SetArenaPhi - input parameters
    minPhiInDegrees, maxPhiInDegrees, resPhiInDegrees = -4, 4, 2
    # Configure Walabot database install location (for windows)
    wlbt.SetSettingsFolder()
    # 1) Connect : Establish communication with walabot.
    wlbt.ConnectAny()
    # 2) Configure: Set scan profile and arena
    # Set Profile - to Sensor-Narrow.
    wlbt.SetProfile(wlbt.PROF_SENSOR_NARROW)
    # Setup arena - specify it by Cartesian coordinates.
    wlbt.SetArenaR(minInCm, maxInCm, resInCm)
    # Sets polar range and resolution of arena (parameters in degrees).
    wlbt.SetArenaTheta(minIndegrees, maxIndegrees, resIndegrees)
    # Sets azimuth range and resolution of arena.(parameters in degrees).
    wlbt.SetArenaPhi(minPhiInDegrees, maxPhiInDegrees, resPhiInDegrees)
    # Dynamic-imaging filter for the specific frequencies typical of breathing
    wlbt.SetDynamicImageFilter(wlbt.FILTER_TYPE_DERIVATIVE)
    # 3) Start: Start the system in preparation for scanning.
    wlbt.Start()

    fig, axes = plt.subplots()
    display = RealtimePlot(axes)
    display.animate(fig, lambda frame_index: (time.time() - start, random.random() * 100))
    #plt.show()

    #fig, axes = plt.subplots()
    #display = RealtimePlot(axes)
    while True:
        appStatus, calibrationProcess = wlbt.GetStatus()
        # 5) Trigger: Scan(sense) according to profile and record signals
        # to be available for processing and retrieval.
        wlbt.Trigger()
        # 6) Get action: retrieve the last completed triggered recording
        energy = wlbt.GetImageEnergy()
        display.add(time.time() - start, energy * 100)
        #This is just for prototype purposes, we will gather the data in bulk and send them to the server in the future
        if energy * 100 > 0.05 and  energy * 100 <= 0.15:
        	urllib.request.urlopen("http://{your_server_link}/input?medium=1&high=0").read()
        elif energy * 100 > 0.15:
        	urllib.request.urlopen("http://{your_server_link}/medium=0&high=1").read()
        plt.pause(0.001)

if __name__ == "__main__": main()

Alexa Intent Schema

JSON
These are Alexa Intent and Sample Utterances, copy
Intent Schema:
{
  "intents": [
    {
      "intent": "SleepTrackIntent"
    },
    {
      "intent": "AMAZON.HelpIntent"
    }
  ]
}

Sample Utterances:
SleepTrackIntent How was my sleep
SleepTrackIntent How was my sleep tracking

Github repo for all the code

This is the github repo for all the code on this project, feel free to recreate entire project using it.

Credits

Peter Ma
49 projects • 394 followers
Prototype Hacker, Hackathon Goer, World Traveler, Ecological balancer, integrationist, technologist, futurist.
SHIN AE HONG
2 projects • 4 followers

Comments