Ethan Fan
Created October 4, 2017 © MIT

Smart Health Band for Elderly

This smart health band can assist old people in their daily lives, leaving the family stress free! Monitor exercise and fall detection.

IntermediateFull instructions provided3 hours105

Things used in this project

Hardware components

STMicroelectronics ST SensorTile
×1

Software apps and online services

BlueMix
IBM BlueMix
Watson
IBM Watson
SMS Messaging API
Twilio SMS Messaging API

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Code

Node-Red JSON

JSON
[
    {
        "id": "94cfccb8.475e5",
        "type": "change",
        "z": "9a3b30e7.e7d3a",
        "name": "Extract Temperature",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.d.Temperature",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 545,
        "y": 150,
        "wires": [
            [
                "e5778a32.5b3d38"
            ]
        ]
    },
    {
        "id": "891ffcf0.7d87d",
        "type": "change",
        "z": "9a3b30e7.e7d3a",
        "name": "Extract Accelerometer X",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.d.X",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "X",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 555,
        "y": 210,
        "wires": [
            [
                "1ef3873b.1bb189"
            ]
        ]
    },
    {
        "id": "e96e920b.36bad",
        "type": "change",
        "z": "9a3b30e7.e7d3a",
        "name": "Extract Accelerometer Y",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.d.Y",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "Y",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 555,
        "y": 250,
        "wires": [
            [
                "1ef3873b.1bb189"
            ]
        ]
    },
    {
        "id": "48f968e3.d4d798",
        "type": "change",
        "z": "9a3b30e7.e7d3a",
        "name": "Extract Accelerometer Z",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.d.Z",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "Z",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 555,
        "y": 290,
        "wires": [
            [
                "1ef3873b.1bb189",
                "5558fb4d.8798a4",
                "8f91cae8.861868"
            ]
        ]
    },
    {
        "id": "4466822e.f8a0ec",
        "type": "switch",
        "z": "9a3b30e7.e7d3a",
        "name": "SensorTile Event Types",
        "property": "eventType",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "Temperature",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "Accelerometer",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "Mic Level",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "outputs": 3,
        "x": 255,
        "y": 250,
        "wires": [
            [
                "94cfccb8.475e5"
            ],
            [
                "891ffcf0.7d87d",
                "e96e920b.36bad",
                "48f968e3.d4d798"
            ],
            [
                "8b7c86dc.a54cd8"
            ]
        ]
    },
    {
        "id": "8b7c86dc.a54cd8",
        "type": "change",
        "z": "9a3b30e7.e7d3a",
        "name": "Extract Microphone level",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.d.Mic",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 555,
        "y": 350,
        "wires": [
            [
                "a5e994.ee09967",
                "a74e167f.401388"
            ]
        ]
    },
    {
        "id": "809bf588.62fbc8",
        "type": "ibmiot in",
        "z": "9a3b30e7.e7d3a",
        "authentication": "boundService",
        "apiKey": "",
        "inputType": "evt",
        "deviceId": "C07A2E305C4D",
        "applicationId": "",
        "deviceType": "+",
        "eventType": "+",
        "commandType": "",
        "format": "json",
        "name": "IBM IoT",
        "service": "registered",
        "allDevices": true,
        "allApplications": "",
        "allDeviceTypes": true,
        "allEvents": true,
        "allCommands": "",
        "allFormats": "",
        "qos": 0,
        "x": 55,
        "y": 167,
        "wires": [
            [
                "f383f7f1.72c348",
                "f3f6c29b.a57de"
            ]
        ]
    },
    {
        "id": "3e63e37d.0d0e3c",
        "type": "comment",
        "z": "9a3b30e7.e7d3a",
        "name": "WIoTP via API keys",
        "info": "Registered Device",
        "x": 101,
        "y": 119,
        "wires": []
    },
    {
        "id": "c03bb406.03f968",
        "type": "debug",
        "z": "9a3b30e7.e7d3a",
        "name": "",
        "active": false,
        "console": "false",
        "complete": "true",
        "x": 353.75000381469727,
        "y": 168.25000190734863,
        "wires": []
    },
    {
        "id": "98eac4d1.ae06d8",
        "type": "debug",
        "z": "9a3b30e7.e7d3a",
        "name": "",
        "active": true,
        "console": "false",
        "complete": "false",
        "x": 686.5277824401855,
        "y": 499.0278081893921,
        "wires": []
    },
    {
        "id": "f383f7f1.72c348",
        "type": "delay",
        "z": "9a3b30e7.e7d3a",
        "name": "",
        "pauseType": "rate",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": true,
        "x": 212.5,
        "y": 169.50000381469727,
        "wires": [
            [
                "4466822e.f8a0ec",
                "c03bb406.03f968"
            ]
        ]
    },
    {
        "id": "e5778a32.5b3d38",
        "type": "ui_chart",
        "z": "9a3b30e7.e7d3a",
        "name": "",
        "group": "a879ac54.d2c46",
        "order": 3,
        "width": 0,
        "height": 0,
        "label": "Temperature",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": "10",
        "removeOlderPoints": "",
        "removeOlderUnit": "60",
        "cutout": 0,
        "colors": [
            "#1f77b4",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "useOldStyle": true,
        "x": 775,
        "y": 150,
        "wires": [
            [],
            []
        ]
    },
    {
        "id": "1ef3873b.1bb189",
        "type": "ui_chart",
        "z": "9a3b30e7.e7d3a",
        "name": "",
        "group": "a879ac54.d2c46",
        "order": 4,
        "width": 0,
        "height": 0,
        "label": "Accelerometer",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": "10",
        "removeOlderPoints": "",
        "removeOlderUnit": "60",
        "cutout": 0,
        "colors": [
            "#1f77b4",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "useOldStyle": true,
        "x": 837.5000152587891,
        "y": 213.75000190734863,
        "wires": [
            [],
            []
        ]
    },
    {
        "id": "a5e994.ee09967",
        "type": "ui_chart",
        "z": "9a3b30e7.e7d3a",
        "name": "",
        "group": "a879ac54.d2c46",
        "order": 5,
        "width": 0,
        "height": 0,
        "label": "Microphone",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": "10",
        "removeOlderPoints": "",
        "removeOlderUnit": "60",
        "cutout": 0,
        "colors": [
            "#1f77b4",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "useOldStyle": true,
        "x": 775,
        "y": 350,
        "wires": [
            [],
            []
        ]
    },
    {
        "id": "a74e167f.401388",
        "type": "switch",
        "z": "9a3b30e7.e7d3a",
        "name": "",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "gte",
                "v": "50",
                "vt": "num"
            }
        ],
        "checkall": "true",
        "outputs": 1,
        "x": 758.2500076293945,
        "y": 385.0000057220459,
        "wires": [
            []
        ]
    },
    {
        "id": "39aec6e9.2e010a",
        "type": "switch",
        "z": "9a3b30e7.e7d3a",
        "name": "Shake",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "Shake",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "outputs": 1,
        "x": 668.3333549499512,
        "y": 451.4444227218628,
        "wires": [
            [
                "bb96af4.e54825"
            ]
        ]
    },
    {
        "id": "bb96af4.e54825",
        "type": "function",
        "z": "9a3b30e7.e7d3a",
        "name": "Send SMS",
        "func": "msg.url = 'https://rest.nexmo.com/sms/json';\nmsg.headers = {\n    'content-type': 'application/json',\n};\n\nvar data = {\"api_key\":\"_\",\n\"api_secret\":\"_\",\n\"to\":\"T Number\",\n\"from\":\"From Number\",\n\"text\":\"I am okay\"}; \nmsg.payload = JSON.stringify(data);\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 803.8888549804688,
        "y": 449.2221984863281,
        "wires": [
            [
                "f3ad53c0.7db21"
            ]
        ]
    },
    {
        "id": "f3ad53c0.7db21",
        "type": "http request",
        "z": "9a3b30e7.e7d3a",
        "name": "",
        "method": "POST",
        "ret": "obj",
        "url": "",
        "tls": "",
        "x": 961.2499542236328,
        "y": 449.4999752044678,
        "wires": [
            []
        ]
    },
    {
        "id": "d1f77a2d.d12df8",
        "type": "function",
        "z": "9a3b30e7.e7d3a",
        "name": "Extract Activity",
        "func": "switch(msg.payload.d.Activity) {\n    case 0:\n        msg.payload = \"NoActivity\";\n        break;\n    case 1:\n        msg.payload = \"Standing\";\n        break;\n    case 2:\n        msg.payload = \"Walking\";\n        break;\n    case 3:\n        msg.payload = \"FastWalking\";\n        break;\n    case 4:\n        msg.payload = \"Jogging\";\n        break;\n    case 5:\n        msg.payload = \"Biking\";\n        break;\n    case 6:\n        msg.payload = \"Drivin\";\n        break;\n    default:\n        msg.payload = \"Unkown\";\n}\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 508.12500762939453,
        "y": 578.2500076293945,
        "wires": [
            [
                "f7083456.1763b8"
            ]
        ]
    },
    {
        "id": "df7e29f3.924768",
        "type": "function",
        "z": "9a3b30e7.e7d3a",
        "name": "Extract Position",
        "func": "switch(msg.payload.d[\"Carry Position\"]) {\n    case 0:\n        msg.payload = \"Unkown\";\n        break;\n    case 1:\n        msg.payload = \"OnDesk\";\n        break;\n    case 2:\n        msg.payload = \"InHand\";\n        break;\n    case 3:\n        msg.payload = \"NearHead\";\n        break;\n    case 4:\n        msg.payload = \"ShirtPocket\";\n        break;\n    case 5:\n        msg.payload = \"TrousersPocket\";\n        break;\n    case 6:\n        msg.payload = \"ArmSwing\";\n        break;\n    default:\n        msg.payload = \"Unkown\";\n}\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 506.87500762939453,
        "y": 637.0000085830688,
        "wires": [
            [
                "31ea8505.6e16aa"
            ]
        ]
    },
    {
        "id": "280aaa43.c2b086",
        "type": "function",
        "z": "9a3b30e7.e7d3a",
        "name": "Extract Gesture",
        "func": "if (msg.payload.d[\"Mems Gesture\"] == 3){\n    msg.payload = \"Shake\";\n}\nreturn msg;\n\n",
        "outputs": 1,
        "noerr": 0,
        "x": 505,
        "y": 477,
        "wires": [
            [
                "39aec6e9.2e010a",
                "98eac4d1.ae06d8"
            ]
        ]
    },
    {
        "id": "f7083456.1763b8",
        "type": "ui_text",
        "z": "9a3b30e7.e7d3a",
        "group": "a879ac54.d2c46",
        "order": 1,
        "width": 0,
        "height": 0,
        "name": "",
        "label": "Activity",
        "format": "{{msg.payload}}",
        "layout": "row-spread",
        "x": 683.1250114440918,
        "y": 578.2500076293945,
        "wires": []
    },
    {
        "id": "31ea8505.6e16aa",
        "type": "ui_text",
        "z": "9a3b30e7.e7d3a",
        "group": "a879ac54.d2c46",
        "order": 2,
        "width": 0,
        "height": 0,
        "name": "",
        "label": "Carry Position",
        "format": "{{msg.payload}}",
        "layout": "row-spread",
        "x": 701.8750076293945,
        "y": 634.5000085830688,
        "wires": []
    },
    {
        "id": "42fae9c3.56d378",
        "type": "function",
        "z": "9a3b30e7.e7d3a",
        "name": "Send Emergency SMS",
        "func": "msg.url = 'https://rest.nexmo.com/sms/json';\nmsg.headers = {\n    'content-type': 'application/json',\n};\n\nvar data = {\"api_key\":\"_\",\n\"api_secret\":\"_\",\n\"to\":\"T Number\",\n\"from\":\"From Number\",\n\"text\":\"Emergency : Falling down\"}; \nmsg.payload = JSON.stringify(data);\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 985.0000152587891,
        "y": 277.00000286102295,
        "wires": [
            [
                "54ba7306.1d536c"
            ]
        ]
    },
    {
        "id": "54ba7306.1d536c",
        "type": "http request",
        "z": "9a3b30e7.e7d3a",
        "name": "",
        "method": "POST",
        "ret": "obj",
        "url": "",
        "tls": "",
        "x": 949.8610992431641,
        "y": 333.527774810791,
        "wires": [
            []
        ]
    },
    {
        "id": "5558fb4d.8798a4",
        "type": "switch",
        "z": "9a3b30e7.e7d3a",
        "name": "Fall Detection",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "gte",
                "v": "1500",
                "vt": "num"
            }
        ],
        "checkall": "true",
        "outputs": 1,
        "x": 780.0000152587891,
        "y": 278.25000286102295,
        "wires": [
            [
                "42fae9c3.56d378"
            ]
        ]
    },
    {
        "id": "8f91cae8.861868",
        "type": "debug",
        "z": "9a3b30e7.e7d3a",
        "name": "",
        "active": true,
        "console": "false",
        "complete": "false",
        "x": 893.1250114440918,
        "y": 240.75000190734863,
        "wires": []
    },
    {
        "id": "f3f6c29b.a57de",
        "type": "switch",
        "z": "9a3b30e7.e7d3a",
        "name": "SensorTile Event Types",
        "property": "eventType",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "Mems Gesture",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "Activity",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "Carry Position",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "outputs": 3,
        "x": 248.75,
        "y": 440.7500057220459,
        "wires": [
            [
                "280aaa43.c2b086"
            ],
            [
                "d1f77a2d.d12df8"
            ],
            [
                "df7e29f3.924768"
            ]
        ]
    },
    {
        "id": "a879ac54.d2c46",
        "type": "ui_group",
        "z": "",
        "name": "Elderly Monitor",
        "tab": "64da1951.2dc478",
        "disp": true,
        "width": "6"
    },
    {
        "id": "64da1951.2dc478",
        "type": "ui_tab",
        "z": "",
        "name": "Home",
        "icon": "dashboard",
        "order": 1
    }
]

Alexa Skill Kit Lamda Code

JavaScript
This lamda function is for Alexa and it calls IBM Watson IoT to get the latest status of the sensor
'use strict';

var https = require('https');
// https is a default part of Node.JS.  Read the developer doc:  https://nodejs.org/api/https.html
// try other APIs such as the current bitcoin price : https://btc-e.com/api/2/btc_usd/ticker  returns ticker.last

function httpsGet(callback) {


    // Update these options with the details of the web service you would like to call
    var options = {
        host: 'sensorelderly.mybluemix.net',
        path: '/status',
        method: 'GET',

        // if x509 certs are required:
        // key: fs.readFileSync('certs/my-key.pem'),
        // cert: fs.readFileSync('certs/my-cert.pem')
    };

    var req = https.request(options, res => {
        res.setEncoding('utf8');
        var returnData = "";

        res.on('data', chunk => {
            returnData = returnData + chunk;
        });

        res.on('end', () => {
            // we have now received the raw return data in the returnData variable.
            // We can see it in the log output via:
            console.log(JSON.stringify(returnData));
            // we may need to parse through it to extract the needed data

            var status = JSON.parse(returnData);

            callback(status);  // this will execute whatever function the caller defined, with one argument

        });

    });
    req.end();

}


// --------------- Helpers that build all of the responses -----------------------

function buildSpeechletResponse(title, output, repromptText, shouldEndSession) {
    return {
        outputSpeech: {
            type: 'PlainText',
            text: output,
        },
        card: {
            type: 'Simple',
            title: `SessionSpeechlet - ${title}`,
            content: `SessionSpeechlet - ${output}`,
        },
        reprompt: {
            outputSpeech: {
                type: 'PlainText',
                text: repromptText,
            },
        },
        shouldEndSession,
    };
}

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


// --------------- Functions that control the skill's behavior -----------------------

function getWelcomeResponse(callback) {
    // If we wanted to initialize the session to have some attributes we could add those here.
    const sessionAttributes = {};
    const cardTitle = 'Welcome';
    const speechOutput = 'Welcome to Elderly.';
    // If the user either does not reply to the welcome message or says something that is not
    // understood, they will be prompted again with this text.
    const repromptText = 'You can ask me the status of elderly';
    const shouldEndSession = false;

    callback(sessionAttributes,
        buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}

function handleSessionEndRequest(callback) {
    const cardTitle = 'Session Ended';
    const speechOutput = 'Thank you for using elderly';
    // Setting this to true ends the session and exits the skill.
    const shouldEndSession = true;

    callback({}, buildSpeechletResponse(cardTitle, speechOutput, null, shouldEndSession));
}


function getStatus(intent, session, callback) {
    const cardTitle = intent.name;
    let repromptText = '';
    let sessionAttributes = {};
    const shouldEndSession = true;
    let speechOutput = '';
    
    httpsGet((myResult) => {
        console.log("received : " + myResult);
        speechOutput = 'Elderly is currently ' + myResult.activity;
        callback(sessionAttributes,
            buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));

        }
    );
}



// --------------- Events -----------------------

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

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

    // Dispatch to your skill's launch.
    getWelcomeResponse(callback);
}

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

    const intent = intentRequest.intent;
    const intentName = intentRequest.intent.name;

    // Dispatch to your skill's intent handlers
    if (intentName === 'Status') {
        getStatus(intent, session, callback);
    } else if (intentName === 'AMAZON.HelpIntent') {
        getWelcomeResponse(callback);
    } else if (intentName === 'AMAZON.StopIntent' || intentName === 'AMAZON.CancelIntent') {
        handleSessionEndRequest(callback);
    } else {
        throw new Error('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 cleanup logic here
}


// --------------- Main handler -----------------------

// Route the incoming request based on type (LaunchRequest, IntentRequest,
// etc.) The JSON body of the request is provided in the event parameter.
exports.handler = (event, context, callback) => {
    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.echo-sdk-ams.app.[unique-value-here]') {
             callback('Invalid Application ID');
        }
        */

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

        if (event.request.type === 'LaunchRequest') {
            onLaunch(event.request,
                event.session,
                (sessionAttributes, speechletResponse) => {
                    callback(null, buildResponse(sessionAttributes, speechletResponse));
                });
        } else if (event.request.type === 'IntentRequest') {
            onIntent(event.request,
                event.session,
                (sessionAttributes, speechletResponse) => {
                    callback(null, buildResponse(sessionAttributes, speechletResponse));
                });
        } else if (event.request.type === 'SessionEndedRequest') {
            onSessionEnded(event.request, event.session);
            callback();
        }
    } catch (err) {
        callback(err);
    }
};

Credits

Ethan Fan

Ethan Fan

3 projects • 17 followers
Mobile, Watch, and Glass Developer

Comments