Smart Working Environment Monitoring with Nano 33 IoT Board

The tutorial shows how to integrate a IoT device into an oneM2M IoT Platform

BeginnerWork in progress520
Smart Working Environment Monitoring with Nano 33 IoT Board

Things used in this project

Hardware components

Arduino Nano 33 IoT
Arduino Nano 33 IoT
×1
DHT11 Temperature & Humidity Sensor (4 pins)
DHT11 Temperature & Humidity Sensor (4 pins)
×1
Gravity: Analog Carbon Monoxide Sensor (MQ7) For Arduino
DFRobot Gravity: Analog Carbon Monoxide Sensor (MQ7) For Arduino
×1
Gas Detection Sensor, Methane
Gas Detection Sensor, Methane
×1
Speaker, Piezo
Speaker, Piezo
×1
Proximity Sensor
Proximity Sensor
×1

Software apps and online services

Arduino IDE
Arduino IDE
Mobius platform
HTML
CSS
Java Script

Story

Read more

Code

Web Page

JavaScript
Java Script File
const path = 'http://192.168.56.1:7579/Mobius'

// ngrok      
// ngrok   uri        
//const path = 'http://b5bc-180-65-158-66.ngrok.io/Mobius'


// ### <ae> ###

function selectAe(ae_resource){

    console.log("<ae>  : " + ae_resource);

    let uri = path + '/' + ae_resource;

    fetch(uri, {
    method: 'GET',
    headers: {
        'Host':'<calculated when request is sent>',
        'User-Agent':'PostmanRuntime/7.28.4',
        'Accept': 'application/json',
        'Accept-Encoding':'gzip, deflate, br',
        'Connection':'keep-alive',
        'X-M2M-RI':'12345',
        'X-M2M-Origin':'admin',
        'Content-Type': 'application/json'
        }
    })
    .then((res) => {
        if (res.statusText == 'OK') {
            res.json().then(data => console.log(data));
        }
        else {
            alert(' .');
            console.error(res);
        }
    }).catch(err => console.error(err));
}


function createAe(ae_resource, ae_api){

    console.log("<ae>  : " + ae_resource);

    let uri = path;

    let item = {
        "m2m:ae" : {
              "rn": ae_resource,
              "api": ae_api,
              "rr": true
          }
      }

    fetch(uri, {
    method: 'POST',
    headers: {
        'Host':'<calculated when request is sent>',
        'User-Agent':'PostmanRuntime/7.28.4',
        'Accept': 'application/json',
        'Accept-Encoding':'gzip, deflate, br',
        'Connection':'keep-alive',
        'X-M2M-RI':'12345',
        'X-M2M-Origin':ae_resource,
        'Content-Type': 'application/json;ty=2',
        'Content-Length':'<calculated when request is sent>'
    },
    body: JSON.stringify(item)
})
    .then((res) => {
        if (res.statusText == 'OK' || res.statusText == 'Created') {
            res.json().then(data => console.log(data));
        }
        else {
            alert(' .');
            console.error(res);
        }
    }).catch(err => console.error(err));
}

function deleteAe(ae_resource){

    console.log("<ae>  : " + ae_resource);

    let uri = path + '/' + ae_resource;

    fetch(uri, {
    method: 'DELETE',
    headers: {
        'Host':'<calculated when request is sent>',
        'User-Agent':'PostmanRuntime/7.28.4',
        'Accept': 'application/json',
        'Accept-Encoding':'gzip, deflate, br',
        'Connection':'keep-alive',
        'X-M2M-RI':'12345',
        'X-M2M-Origin':ae_resource,
        'Content-Type': 'application/json;ty=2',
        'Content-Length':'<calculated when request is sent>'
    }
})
    .then((res) => {
        if (res.statusText == 'OK') {
            res.json().then(data => {
                console.log(data);
                location.reload();
            });
        }
        else {
            alert(' .');
            console.error(res);
        }
    }).catch(err => console.error(err));
}

// ### <cnt> ###

function selectCnt(ae_resource, cnt_resource){

    console.log("<cnt>  : " + ae_resource + '/' + cnt_resource);

    let uri = path + '/' + ae_resource + '/' + cnt_resource;

    fetch(uri, {
    method: 'GET',
    headers: {
        'Host':'<calculated when request is sent>',
        'User-Agent':'PostmanRuntime/7.28.4',
        'Accept': 'application/json',
        'Accept-Encoding':'gzip, deflate, br',
        'Connection':'keep-alive',
        'X-M2M-RI':'12345',
        'X-M2M-Origin':'admin',
        'Content-Type': 'application/json'
    }
})
    .then((res) => {
        if (res.statusText == 'OK') {
            res.json().then(data => console.log(data));
        }
        else {
            alert(' .');
            console.error(res);
        }
    }).catch(err => console.error(err));
}

function createCnt(ae_resource, cnt_resource, mni, con='no_cin'){      // <cnt>  , con   <cin> 

    console.log("<cnt>  : " + ae_resource + '/' + cnt_resource);

    let uri = path + '/' + ae_resource;

    let item = {
        "m2m:cnt" : {
          "rn": cnt_resource,
          "mni": mni
          }
      };

    fetch(uri, {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json; ty=3',
        'Content-Length':'<calculated when request is sent>',
        'Host':'<calculated when request is sent>',
        'User-Agent':'PostmanRuntime/7.28.4',
        'Accept': 'application/json',
        'Accept-Encoding':'gzip, deflate, br',
        'Connection':'keep-alive',
        'X-M2M-RI':'12345',
        'X-M2M-Origin': cnt_resource

    },
    body: JSON.stringify(item)
})
    .then((res) => {
        if (res.statusText == 'OK' || res.statusText == 'Created') {
            res.json().then(data => console.log(data));

            if(con != 'no_cin'){
                createCin(ae_resource, cnt_resource, con);
            }
        }
        else {
            alert(' .');
            console.error(res);
        }
    }).catch(err => console.error(err));
}

function deleteCnt(ae_resource, cnt_resource){

    console.log("<cnt>  : " + ae_resource + '/' + cnt_resource);

    let uri = path + '/' + ae_resource + '/' + cnt_resource;

    fetch(uri, {
    method: 'DELETE',
    headers: {
        'Host':'<calculated when request is sent>',
        'User-Agent':'PostmanRuntime/7.28.4',
        'Accept': 'application/json',
        'Accept-Encoding':'gzip, deflate, br',
        'Connection':'keep-alive',
        'X-M2M-RI':'12345',
        'X-M2M-Origin':cnt_resource,
        'Content-Type': 'application/json;ty=3',
        'Content-Length':'<calculated when request is sent>'
    }
})
    .then((res) => {
        if (res.statusText == 'OK') {
            res.json().then(data => console.log(data));
        }
        else {
            alert(' .');
            console.error(res);
        }
    }).catch(err => console.error(err));
}



// ### <grp> ###

async function selectGrpAsync(ae_resource, grp_resource) {

    console.log('selectGrpAsync() ');

    let uri = path + '/'+ae_resource+'/'+grp_resource+'/fopt';
    if(ae_resource == 'Zone'){
        uri = path + '/' + grp_resource + '/fopt';
    }
    let response = await fetch(uri, {
        method: 'GET',
        headers: {
            'Host':'<calculated when request is sent>',
            'User-Agent':'PostmanRuntime/7.28.4',
            'Accept': 'application/json',
            'Accept-Encoding':'gzip, deflate, br',
            'Connection':'keep-alive',
            'X-M2M-RI':'12345',
            'X-M2M-Origin':'admin',
            'Content-Type': 'application/json'
        }
    });
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return await response;
  }

function selectGrp(ae_resource, grp_resource, mode = 0){       // mode=0 : , mode='fopt' : fopt( )

    console.log("<grp>  : " + ae_resource + '/' + grp_resource + ' mode:' + mode);

    let uri = path + '/' + ae_resource + '/' + grp_resource;

    if(mode == 'fopt'){
        uri += '/fopt'
    }

    fetch(uri, {
    method: 'GET',
    headers: {
        'Host':'<calculated when request is sent>',
        'User-Agent':'PostmanRuntime/7.28.4',
        'Accept': 'application/json',
        'Accept-Encoding':'gzip, deflate, br',
        'Connection':'keep-alive',
        'X-M2M-RI':'12345',
        'X-M2M-Origin':'admin',
        'Content-Type': 'application/json'
    }
})
    .then((res) => {
        if (res.statusText == 'OK') {
            res.json().then(data => console.log(data));
        }
        else {
            alert(' .');
            console.error(res);
        }
    }).catch(err => console.error(err));
}

function createGrp(ae_resource, mode = 'biogrp'){     // mode='biogrp' :  , mode='entgrp' :  

    console.log("<grp>  : " + ae_resource + '/' + mode);

    let item = {};

    if (mode == 'biogrp'){
        item = {
            "m2m:grp" : {
                "rn": 'biogrp',
                "mid": [
                        "Mobius/"+ae_resource+"/location/la",
                        "Mobius/"+ae_resource+"/heartrate/la",
                        "Mobius/"+ae_resource+"/danger/la"
                ],
                "mnm":10
            }
        };
    }
    else if(mode == 'entgrp'){
        item = {
            "m2m:grp" : {
                "rn": 'entgrp',
                "mid": [
                        "Mobius/"+ae_resource+"/temperature/la",
                        "Mobius/"+ae_resource+"/humidity/la",
                        "Mobius/"+ae_resource+"/methane/la",
                        "Mobius/"+ae_resource+"/cmonoxide/la"
                ],
                "mnm":10
            }
        };
    }
    else if(mode == 'notegrp'){
        item = {
            "m2m:grp" : {
                "rn": 'notegrp',
                "mid": [
                        "Mobius/"+ae_resource+"/name/la",
                        "Mobius/"+ae_resource+"/hp/la",
                        "Mobius/"+ae_resource+"/location/la"
                ],
                "mnm":10
            }
        };
    }
    else{
        alert(" .");
        return;
    }

    let uri = path + '/' + ae_resource;

    fetch(uri, {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json;ty=9',
        'Content-Length':'<calculated when request is sent>',
        'Host':'<calculated when request is sent>',
        'User-Agent':'PostmanRuntime/7.28.4',
        'Accept': 'application/json',
        'Accept-Encoding':'gzip, deflate, br',
        'Connection':'keep-alive',
        'X-M2M-RI':'12345',
        'X-M2M-Origin': mode

    },
    body: JSON.stringify(item)
})
    .then((res) => {
        if (res.statusText == 'OK' || res.statusText == 'Created') {
            res.json().then(data => console.log(data));
        }
        else {
            alert(' .');
            console.error(res);
        }
    }).catch(err => console.error(err));
}

function deleteGrp(ae_resource, grp_resource){

    console.log("<grp>  : " + ae_resource + '/' + grp_resource);

    let uri = path + '/' + ae_resource + '/' + grp_resource;

    fetch(uri, {
    method: 'DELETE',
    headers: {
        'Host':'<calculated when request is sent>',
        'User-Agent':'PostmanRuntime/7.28.4',
        'Accept': 'application/json',
        'Accept-Encoding':'gzip, deflate, br',
        'Connection':'keep-alive',
        'X-M2M-RI':'12345',
        'X-M2M-Origin':grp_resource,
        'Content-Type': 'application/json;ty=9',
        'Content-Length':'<calculated when request is sent>'
    }
})
    .then((res) => {
        if (res.statusText == 'OK') {
            res.json().then(data => console.log(data));
        }
        else {
            alert(' .');
            console.error(res);
        }
    }).catch(err => console.error(err));
}


// ### <cin> ###

function selectCin(ae_resource, cnt_resource, mode=0){       // mode=0:  , mode='la':con

    console.log("<cin>  : " + ae_resource + '/' + cnt_resource);

    let uri = path + '/' + ae_resource + '/' + cnt_resource;

    if(mode == 'la'){
        uri += '/la'
    }
    else{
        uri += '?rcn=4&ty=4'
    }

    fetch(uri, {
    method: 'GET',
    headers: {
        'Host':'<calculated when request is sent>',
        'User-Agent':'PostmanRuntime/7.28.4',
        'Accept': 'application/json',
        'Accept-Encoding':'gzip, deflate, br',
        'Connection':'keep-alive',
        'X-M2M-RI':'12345',
        'X-M2M-Origin':'admin',
        'Content-Type': 'application/json;ty=4',
        'Content-Length':'<calculated when request is sent>'
    }
})
    .then((res) => {
        if (res.statusText == 'OK') {
            res.json().then(data => console.log(data));
        }
        else {
            alert(' .');
            console.error(res);
        }
    }).catch(err => console.error(err));
}

async function selectCinAsync(ae_resource, cnt_resource, mode=0) {

    console.log("<cin>  : " + ae_resource + '/' + cnt_resource);

    let uri = path + '/' + ae_resource + '/' + cnt_resource;

    if(mode == 'la'){
        uri += '/la'
    }
    else{
        //uri += '?rcn=4&ty=4&lim=15'           // lim=15     15     ..
        uri += '?rcn=4&ty=4'
    }
    let response = await fetch(uri, {
        method: 'GET',
        headers: {
            'Host':'<calculated when request is sent>',
            'User-Agent':'PostmanRuntime/7.28.4',
            'Accept': 'application/json',
            'Accept-Encoding':'gzip, deflate, br',
            'Connection':'keep-alive',
            'X-M2M-RI':'12345',
            'X-M2M-Origin':'admin',
            'Content-Type': 'application/json;ty=4',
            'Content-Length':'<calculated when request is sent>'
        }
    });
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return await response;
  }

function createCin(ae_resource, cnt_resource, con){

    console.log("<cin>  : " + ae_resource + '/' + cnt_resource + '/' + con);

    let uri = path + '/' + ae_resource + '/' + cnt_resource;

    let item = {
        "m2m:cin": {
            "con": con
        }
    };

    fetch(uri, {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json;ty=4',
        'Content-Length':'<calculated when request is sent>',
        'Host':'<calculated when request is sent>',
        'User-Agent':'PostmanRuntime/7.28.4',
        'Accept': 'application/json',
        'Accept-Encoding':'gzip, deflate, br',
        'Connection':'keep-alive',
        'X-M2M-RI':'12345',
        'X-M2M-Origin': cnt_resource

    },
    body: JSON.stringify(item)
})
    .then((res) => {
        if (res.statusText == 'OK' || res.statusText == 'Created') {
            res.json().then(data => console.log(data));
        }
        else {
            alert(' .');
            console.error(res);
        }
    }).catch(err => console.error(err));
}

async function createCinAsync(ae_resource, cnt_resource, con) {

    console.log("<cin>  : " + ae_resource + '/' + cnt_resource + '/' + con);

    let uri = path + '/' + ae_resource + '/' + cnt_resource;

    let item = {
        "m2m:cin": {
            "con": con
        }
    };

    let response = await fetch(uri, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json;ty=4',
            'Content-Length':'<calculated when request is sent>',
            'Host':'<calculated when request is sent>',
            'User-Agent':'PostmanRuntime/7.28.4',
            'Accept': 'application/json',
            'Accept-Encoding':'gzip, deflate, br',
            'Connection':'keep-alive',
            'X-M2M-RI':'12345',
            'X-M2M-Origin': cnt_resource

        },
        body: JSON.stringify(item)
    });
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return await response;
  }

Web Page

HTML
Index Page
<!DOCTYPE html>
<html lang="en">

<head>

  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="">
  <meta name="author" content="">

  <title>Smart Working Environment Monitoring Service</title>

  <!-- Bootstrap core CSS -->
  <link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">

  <!-- Custom fonts for this template -->
  <link href="vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">
  <link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>
  <link href='https://fonts.googleapis.com/css?family=Merriweather:400,300,300italic,400italic,700,700italic,900,900italic' rel='stylesheet' type='text/css'>

  <!-- Plugin CSS -->
  <link href="vendor/magnific-popup/magnific-popup.css" rel="stylesheet">

  <!-- Custom styles for this template -->
  <link href="css/creative.min.css" rel="stylesheet">

</head>

<style>
  body {
    padding-bottom: calc(10rem - 56px);
    background-image: url(img/header.jpg);
    background-size: cover;
  }
</style>

<body id="page-top">

  <!-- Navigation -->
  <nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
    <div class="container">
      <a class="navbar-brand js-scroll-trigger" href="index.html">Home</a>
      <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="navbarResponsive">
        <ul class="navbar-nav ml-auto">
          <li class="nav-item">
            <a class="nav-link js-scroll-trigger" href="registration.html">Worker Registration</a>
          </li>
          <li class="nav-item">
            <a class="nav-link js-scroll-trigger" href="users.html">Worker Inquire</a>
          </li>
          <li class="nav-item">
            <a class="nav-link js-scroll-trigger" href="zone-monitoring.html">Zone Moniroting</a>
          </li>
        </ul>
      </div>
    </div>
  </nav>

  <header class="masthead text-center text-white d-flex">
    <div class="container my-auto">
      <div class="row">
        <div class="col-lg-10 mx-auto">
          <h1 class="text-uppercase">
            <strong>Smart Working Environment Monitoring Service</strong>
          </h1>
          <hr>
        </div>
        <div class="col-lg-8 mx-auto">
          <p class="text-faded mb-5">Lee yelim / Shin jeongseop</p>
          <p class="text-faded" style="font-size: small">Mobi-Hero</p>
        </div>
      </div>
    </div>
  </header>

  <!-- Bootstrap core JavaScript -->
  <script src="vendor/jquery/jquery.min.js"></script>
  <script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>

  <!-- Plugin JavaScript -->
  <script src="vendor/jquery-easing/jquery.easing.min.js"></script>
  <script src="vendor/scrollreveal/scrollreveal.min.js"></script>
  <script src="vendor/magnific-popup/jquery.magnific-popup.min.js"></script>

  <!-- Custom scripts for this template -->
  <script src="js/creative.min.js"></script>

</body>

</html>

Web Page

HTML
Registration Page
<!DOCTYPE html>

<html lang="en" class="no-js">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="">
  <meta name="author" content="">

  <title>Smart Working Environment Monitoring Service</title>
  <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
  <link href="https://fonts.googleapis.com/css?family=Raleway:100,100i,200,200i,300,300i,400,400i,500,500i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">

  <link href="css/abc.css" rel="stylesheet">

  <!-- Bootstrap core CSS -->
  <link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">

  <!-- Plugin CSS -->
  <link href="vendor/magnific-popup/magnific-popup.css" rel="stylesheet">

  <!-- Custom styles for this template -->
  <link href="css/creative.min.css" rel="stylesheet">

  <!-- jquery -->
  <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>

  <!-- 모비우스 -->
  <script src="./mobi-hero-mobius.js"></script>

  <script>
    // <ae>worker 생성 시, 모든 cnt, grp 자동 생성하고 cnt 중 name과 hp는 cin까지 등록해줌
    function createWorker(workerId, name, hp){

        console.log("<ae>worker register : " + workerId +' ' + name + ' ' + ' ' + hp);

        let uri = path;

        let item = {
            "m2m:ae" : {
                  "rn": workerId,
                  "api": 'worker',
                  "rr": true
              }
          }

        fetch(uri, {
        method: 'POST',
        headers: {
            //'Host':'<calculated when request is sent>',
            //'User-Agent':'PostmanRuntime/7.28.4',
            //'Accept': 'application/json',
            //'Accept-Encoding':'gzip, deflate, br',
            //'Connection':'keep-alive',
            'X-M2M-RI':'12345',
            'X-M2M-Origin':workerId,
            'Content-Type': 'application/json;ty=2',
            'Content-Length':'100'
        },
        body: JSON.stringify(item)
    })
        .then((res) => {
            if (res.statusText == 'OK' || res.statusText == 'Created') {
                res.json().then(data => {
                    createCnt(workerId, 'name', 100, name);
                    createCnt(workerId, 'hp', 100, hp);

                    createCnt(workerId, 'location', 100, 'En');
                    createCnt(workerId, 'heartrate', 100);
                    createCnt(workerId, 'danger', 100, 0);

                    createCnt(workerId, 'temperature', 100, 0);
                    createCnt(workerId, 'humidity', 100, 0);
                    createCnt(workerId, 'methane', 100, 0);
                    createCnt(workerId, 'cmonoxide', 100, 0);

                    createCnt(workerId, 'buzzer', 100, "OFF");

                    createGrp(workerId, mode = 'biogrp');
                    createGrp(workerId, mode = 'entgrp');
                    createGrp(workerId, mode = 'notegrp');

                });

                alert("Registered.");
                $('#userid').val('');
                $('#username').val('');
                $('#userphone').val('');

            }
            else {
                alert('Error');
                console.error(res);
            }
        }).catch(err => console.error(err));
    }

  </script>
</head>

<body>

  <!-- Navigation -->
  <nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
    <div class="container">
      <a class="navbar-brand js-scroll-trigger" href="index.html">Home</a>
      <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="navbarResponsive">
        <ul class="navbar-nav ml-auto">
          <li class="nav-item">
            <a class="nav-link js-scroll-trigger" href="registration.html">Worker Registration</a>
          </li>
          <li class="nav-item">
            <a class="nav-link js-scroll-trigger" href="users.html">Worker Inquire</a>
          </li>
          <li class="nav-item">
            <a class="nav-link js-scroll-trigger" href="zone-monitoring.html">Zone Moniroting</a>
          </li>
        </ul>
      </div>
    </div>
  </nav>

  <div class="container">
    <header>
      <h1></h1> <br>
    </header>
    <section>
      <div id="container_demo">
        <a class="hiddenanchor" id="tologin"></a>

        <div id="wrapper">

          <div id="login" class="animate form">
            <form  action="#" target="iframe1">
              <h1>Worker Registration</h1>

              <p>
                <label for="userid" class="userid"> ID </label>
                <input id="userid" name="userid" type="text" />
              </p>

              <p>
                <label for="username" class="username"> Name </label>
                <input id="username" name="username" type="text" />
              </p>

              <p>
                <label for="userphone" class="userphone"> H.P </label>
                <input id="userphone" name="userphone" type="userphone" />
              </p>

              <p class="signin button">
                <input type="button" name="submit" value="Input" onclick="createWorker($('#userid').val(), $('#username').val(), $('#userphone').val())"/>
              </p>
              <p class="change_link">
                <a href="index.html">Back</a>
              </p>
            </form>
            <iframe name="iframe1" style="display:none"></iframe>   <!-- 페이지 이동 방지 -->
          </div>
        </div>
      </div>
    </section>
  </div>
</body>

</html>

Web Page

HTML
Users Page
<!DOCTYPE html>

<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="">
  <meta name="author" content="">

  <title>Smart Working Environment Monitoring Service</title>
  <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
  <link href="https://fonts.googleapis.com/css?family=Raleway:100,100i,200,200i,300,300i,400,400i,500,500i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">

  <link href="css/abc.css" rel="stylesheet">

  <!-- Bootstrap core CSS -->
  <link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">

  <!-- Plugin CSS -->
  <link href="vendor/magnific-popup/magnific-popup.css" rel="stylesheet">

  <!-- Custom styles for this template -->
  <link href="css/creative.min.css" rel="stylesheet">

  <!-- jquery -->
  <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>

  <!-- 모비우스 -->
  <script src="./mobi-hero-mobius.js"></script>


  <style>
    table {
      border-collapse: collapse;
      width: auto;
    }

    th,
    td {
      text-align: left;
      padding: 3px;
    }

    tr:nth-child(even) {
      background-color: #f2f2f2
    }

    th {
      background-color: #4CAF50;
      color: white;
    }
  </style>

<script>
  $(document).ready(function () {
    selectWorker();
  });

  function selectWorker(){

    console.log("worker Inquire : ");

    let uri = path + '?rcn=4&ty=2';

    fetch(uri, {
      method: 'GET',
      headers: {
          'Host':'<calculated when request is sent>',
          'User-Agent':'PostmanRuntime/7.28.4',
          'Accept': 'application/json',
          'Accept-Encoding':'gzip, deflate, br',
          'Connection':'keep-alive',
          'X-M2M-RI':'12345',
          'X-M2M-Origin':'admin',
          'Content-Type': 'application/json'
      }
      })
      .then((res) => {
          if (res.statusText == 'OK') {
              res.json().then(data => {
                  console.log(data);

                  $("#worker_table > tbody > tr").remove();

                  let list = data['m2m:rsp']['m2m:ae'];

                  let idx = 0;
                  for(let i=0; i<list.length; i++){
                      if(list[i]['api'] == 'worker'){

                          console.log(list[i]['rn']);

                          let table = $("#worker_table");

                          selectGrpAsync(list[i]['rn'], 'notegrp').then((res) => {
                              if (res.statusText == 'OK' || res.statusText == 'Created') {
                                  res.json().then(data => {
                                      idx +=1;

                                      let name = data["m2m:agr"]["m2m:rsp"][0]["pc"]["m2m:cin"]["con"];       // 이름
                                      let hp = data["m2m:agr"]["m2m:rsp"][1]["pc"]["m2m:cin"]["con"];         // 전화번호
                                      let loc = data["m2m:agr"]["m2m:rsp"][2]["pc"]["m2m:cin"]["con"];        // 출근 상태

                                      loc = loc=='En' ? '-' : 'On';

                                      let row = "<tr>"
                                          + "<td>" + idx + "</td>"
                                          + "<td>" + list[i]['rn'] + "</td>"
                                          + "<td>" + name + "</td>"
                                          + "<td>" + hp + "</td>"
                                          + "<td>" + loc + "</td>"
                                          //+ "<td><a href='list.html' onclick='clickListPage(this)'>조회</a></td>"
                                          //+ "<td><a href='monitoring.html' onclick='clickMonitoringPage(this)'>모니터링</a></td>"
                                          //+ "<td><a href='' onclick='clickDeleteWorker(this)'>삭제</a></td>"
                                          + "<td><button type='button' class='btn btn-dark btn-sm' onclick='clickListPage(this)'>Inquire</button></td>"
                                          + "<td><button type='button' class='btn btn-info btn-sm' onclick='clickMonitoringPage(this)'>Monitoring</button></td>"
                                          + "<td><button type='button' class='btn btn-primary btn-sm' onclick='clickDeleteWorker(this)'>Delete</button></td>"
                                          + "</tr>";

                                      table.append(row);
                                  });
                              }
                              else {
                                  alert('Error');
                                  console.error(res);
                              }
                          }).catch(err => console.error(err));
                      }
                  }
              });
          }
          else {
              alert('Error');
              console.error(res);
          }
      }).catch(err => console.error(err));
}

// 작업자조회 > 조회 버튼 클릭 이벤트
function clickListPage(id){
  let ae_resource = $(id).closest('tr').find("td:eq(1)").text();
  localStorage.setItem('list_item', ae_resource);

  location.href='list.html';
}

// 작업자조회 > 모니터링 버튼 클릭 이벤트
function clickMonitoringPage(id){
  let ae_resource = $(id).closest('tr').find("td:eq(1)").text();
  let name = $(id).closest('tr').find("td:eq(2)").text();
  localStorage.setItem('list_item', ae_resource);
  localStorage.setItem('list_item2', name);

  location.href='monitoring.html';
}

// 작업자조회 > 삭제 버튼 클릭 이벤트
function clickDeleteWorker(id){
  let ae_resource = $(id).closest('tr').find("td:eq(1)").text();

  let res = confirm("Worker '" + ae_resource + "' Delete?");
  if(res){
    deleteAe(ae_resource);
  }
}
</script>

</head>

<body>

  <!-- Navigation -->
  <nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
    <div class="container">
      <a class="navbar-brand js-scroll-trigger" href="index.html">Home</a>
      <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="navbarResponsive">
        <ul class="navbar-nav ml-auto">
          <li class="nav-item">
            <a class="nav-link js-scroll-trigger" href="registration.html">Worker Registration</a>
          </li>
          <li class="nav-item">
            <a class="nav-link js-scroll-trigger" href="users.html">Worker Inquire</a>
          </li>
          <li class="nav-item">
            <a class="nav-link js-scroll-trigger" href="zone-monitoring.html">Zone Moniroting</a>
          </li>
        </ul>
      </div>
    </div>
  </nav>

  <div class="container">

    <header>
      <h1></h1> <br><br><br>
    </header>
    <section>
      <div id="container_demo">
        <a class="hiddenanchor" id="toadmin_users"></a>

        <div id="wrapper2">

          <div id="login" class="animate form">

            <form>
              <h1>Worker Inquire</h1>

              <table border="2" align="center" id="worker_table">
                <thead>
                  <tr bgcolor="yellow" align="center">
                    <th> No </th>
                    <th> ID </th>
                    <th> Name </th>
                    <th> H.P </th>
                    <th> Go To Work </th>
                    <th> Inquire </th>
                    <th> Monitoring </th>
                    <th> Delete </th>
                  </tr>
                </thead>
                <tbody>

                </tbody>
              </table> <br><br>


              <p class="change_link2">

                <a href="index.html">Back</a>

              </p>
            </form>
          </div>
        </div>
      </div>
    </section>
  </div>
</body>

</html>

Web Page

HTML
worker's detailed inquiry page
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="">
  <meta name="author" content="">

  <title>Smart Working Environment Monitoring Service</title>

  <!-- Bootstrap core CSS -->
  <link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">

  <!-- Custom fonts for this template -->
  <link href="vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">
  <link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>
  <link href='https://fonts.googleapis.com/css?family=Merriweather:400,300,300italic,400italic,700,700italic,900,900italic' rel='stylesheet' type='text/css'>

  <!-- Plugin CSS -->
  <link href="vendor/magnific-popup/magnific-popup.css" rel="stylesheet">

  <!-- Custom styles for this template -->
  <link href="css/creative.min.css" rel="stylesheet">

  <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
  <link href="https://fonts.googleapis.com/css?family=Raleway:100,100i,200,200i,300,300i,400,400i,500,500i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">

  <link href="css/abc.css" rel="stylesheet">

  <!-- jquery -->
  <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>

  <!-- 모비우스 -->
  <script src="./mobi-hero-mobius.js"></script>

  <!-- 아이콘 -->
  <script src="https://use.fontawesome.com/releases/v5.2.0/js/all.js"></script>

  <style>
    .box{
      display: flex;
      height: 150px;
      width: 300px;
      justify-content: center;
      align-items: center;
      border: solid 1px gray;
      font-size:xx-large;
      font-weight: bold;
    }
  </style>

  <script>
    let name = hp = loc = danger = '';

    $(document).ready(function () {

      // 페이지 이동 시 넘어오는 ae_resource 값
      if(localStorage.getItem('list_item')){
        let ae_resource = localStorage.getItem('list_item');
        console.log(ae_resource);

        // notegrp 값 가져옴
        selectGrpAsync(ae_resource, 'notegrp').then((res) => {
        if (res.statusText == 'OK') {
          res.json().then(data => {

            // console.log("notegrp 응답");
            // console.log(data['m2m:agr']['m2m:rsp'][0]['pc']['m2m:cin']['con']);   // 이름
            // console.log(data['m2m:agr']['m2m:rsp'][1]['pc']['m2m:cin']['con']);   // 전화번호

            try{
              name = data['m2m:agr']['m2m:rsp'][0]['pc']['m2m:cin']['con'];
              hp = data['m2m:agr']['m2m:rsp'][1]['pc']['m2m:cin']['con'];
            } catch{
              console.log("notegrp catch!");
            }

            // 여기에 notegrp 값 활용 코드 작업
            $('#item1').text(ae_resource);
            $('#item2').text(name);
            $('#item3').text(hp);

          });
        }
      }).catch(err => console.error(err));

      // biogrp 값 가져옴
      selectGrpAsync(ae_resource, 'biogrp').then((res) => {
        if (res.statusText == 'OK') {
          res.json().then(data => {

            try{
              loc = data['m2m:agr']['m2m:rsp'][0]['pc']['m2m:cin']['con'];
              $("#box1").text(loc);

            }catch{
              console.log("biogrp catch!");
              $("#box1").text('');
              $("#box1").append('<i class="fas fa-exclamation-triangle"></i>')
            }

            try{
              danger = data['m2m:agr']['m2m:rsp'][2]['pc']['m2m:cin']['con'];
              if(danger == '0'){
                $("#box2").text("NORMAL");
                $("#box2").css("color","green");
              }
              else{
                $("#box2").text("FALL DOWN");
                $("#box2").css("color","red");
              }
            }catch{
              console.log("biogrp catch!");
              $("#box2").text('');
              $("#box2").append('<i class="fas fa-exclamation-triangle"></i>');
            }
          });
        }
      }).catch(err => console.error(err));


      }

    });
  </script>

</head>

<body>

  <!-- Navigation -->
  <nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
    <div class="container">
      <a class="navbar-brand js-scroll-trigger" href="index.html">Home</a>
      <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="navbarResponsive">
        <ul class="navbar-nav ml-auto">
          <li class="nav-item">
            <a class="nav-link js-scroll-trigger" href="registration.html">Worker Registration</a>
          </li>
          <li class="nav-item">
            <a class="nav-link js-scroll-trigger" href="users.html">Worker Inquire</a>
          </li>
          <li class="nav-item">
            <a class="nav-link js-scroll-trigger" href="zone-monitoring.html">Zone Moniroting</a>
          </li>
        </ul>
      </div>
    </div>
  </nav>

<section>
  <div class="container">
      <div id="container_demo">
        <a class="hiddenanchor" id="toadmin_users"></a>

        <div id="wrapper2">

          <div id="login" class="animate form">

            <form>
              <h1>Worker Information</h1>

              <table>
                <tr>
                  <td><b> ID </b></td>
                  <td style="padding: 0px 10px;"><b>:</b></td>
                  <td><span id="item1"></span></td>
                </tr>
                <tr>
                  <td><b> Name </b></td>
                  <td style="padding: 0px 10px;"><b>:</b></td>
                  <td><span id="item2"></span></td>
                </tr>
                <tr>
                  <td><b> H.P </b></td>
                  <td style="padding: 0px 10px;"><b>:</b></td>
                  <td><span id="item3"></span></td>
                </tr>
              </table>

              <br>

              <table style="width: 100%;">
                <tr>
                  <th><b> Location </b></th>
                  <th><b> Fall Down </b></th>
                </tr>
                <tr>
                  <td> <div class="box" id="box1"></div> </td>
                  <td> <div class="box" id="box2"></div> </td>
                </tr>
              </table>

              <br>

              <i class="exclamation-circle"></i>

              <br><br>

              <p class="change_link2">

                <a href="users.html">Back</a>

              </p>
            </form>
          </div>
        </div>
      </div>
    </section>
  </div>
</body>

</html>

Web Page

HTML
worker's monitoring page
<!DOCTYPE html>

<html lang="en" class="no-js">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="">
  <meta name="author" content="">

  <title>Smart Working Environment Monitoring Service</title>
  <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
  <link href="https://fonts.googleapis.com/css?family=Raleway:100,100i,200,200i,300,300i,400,400i,500,500i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">

  <link href="css/abc.css" rel="stylesheet">

  <!-- Bootstrap core CSS -->
  <link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">

  <!-- Plugin CSS -->
  <link href="vendor/magnific-popup/magnific-popup.css" rel="stylesheet">

  <!-- Custom styles for this template -->
  <link href="css/creative.min.css" rel="stylesheet">

  <!-- jquery -->
  <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>

  <!-- 모비우스 -->
  <script src="./mobi-hero-mobius.js"></script>

  <!-- 아이콘 -->
  <script src="https://use.fontawesome.com/releases/v5.2.0/js/all.js"></script>

  <!-- 차트 -->
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.bundle.min.js"></script>
	<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js"></script>

  <style>
    .box{
      display: flex;
      height: 150px;
      width: 220px;
      justify-content: center;
      align-items: center;
      border: solid 1px gray;
      font-size:xx-large;
      font-weight: bold;
    }

    #notiCard{
      position: fixed;
      bottom: 10px;
      right: 10px;


      padding: 5px;
      font-size: small;
      text-align: left;
    }
    .card-body{
      padding: 10px;
    }
    .card-header{
      padding: 5px;
    }

    #notiTable{
      width: 300px;
    }
  </style>

<script>
  let ae_resource;
  let name;

  $(document).ready(function () {
    if(localStorage.getItem('list_item')){
      ae_resource = localStorage.getItem('list_item');
      name = localStorage.getItem('list_item2');

      $('#worker').text(name+' ('+ae_resource+')');

      getBioData();
      startGetBioData();

      // 온도 그래프 그림
      drawTempChart(ae_resource);

      // 습도 그래프 그림
      drawHumiChart(ae_resource);

      // 메탄가스 그래프 그림
      drawMethChart(ae_resource);

      // 일산화탄소 그래프 그림
      drawCmonChart(ae_resource);
    }
  });

  startGetBioData = function() {
    playAlert = setInterval(function() {
      getBioData();
    }, 5000);
  };

  function getBioData(){
    // biogrp 조회
    selectGrpAsync(ae_resource, 'biogrp').then((res) => {
        if (res.statusText == 'OK') {

          let loc = heart = danger = '';

          res.json().then(data => {
            try{
              loc = data['m2m:agr']['m2m:rsp'][0]['pc']['m2m:cin']['con'];
              $("#box1").text(loc);

            }catch{
              console.log("biogrp > loc catch!");
              $("#box1").text('');
              $("#box1").append('<i class="fas fa-exclamation-triangle"></i>');
            }

            try{
              heart = data['m2m:agr']['m2m:rsp'][1]['pc']['m2m:cin']['con'];
              if ($("#box2").text() != heart){
                if(heart  < 65){
                  $('#td1').text("Worker heart rate is dangerous.");
                }
                else{
                  $('#td1').text('');
                }
              }
              $("#box2").text(heart);
            }catch{
              console.log("biogrp > heart catch!");
              $("#box2").text('');
              $("#box2").append('<i class="fas fa-exclamation-triangle"></i>');
              $('#td1').text('');
            }

            try{
              danger = data['m2m:agr']['m2m:rsp'][2]['pc']['m2m:cin']['con'];
              console.log("danger?");
              console.log(danger);
              if(danger == '0'){
                $("#box3").text("NORMAL");
                $("#box3").css("color","green");
                $('#td2').text('');
              }
              else{
                if($("#box3").text() != "FALL DOWN"){
                  $('#td2').text("A fall of the worker has been detected.");
                }
                $("#box3").text("FALL DOWN");
                $("#box3").css("color","red");
              }
            }catch{
              console.log("biogrp > heart catch!");
              $("#box3").text('');
              $("#box3").append('<i class="fas fa-exclamation-triangle"></i>');
              $('#td2').text('');
            }

            checkNoti();
          });
        }
        else {
          alert('ERROR');
          console.error(res);
        }
      }).catch(err => console.error(err));
  }

  // 알림 버튼 클릭 이벤트
  function clickAlertPage(){
    localStorage.setItem('list_item', ae_resource);
    location.href='alert.html';
  }

  function closeNoti(){
    $('#td1').text('');
    $('#td2').text('');
    $('#td3').text('');
    $('#td4').text('');
    $('#td5').text('');
    $('#td6').text('');
    $('#notiCard').hide();
  }

  function openNoti(){
    $('#notiCard').show();
  }

  function checkNoti(){
    if($('#td1').text() == '' && $('#td2').text() == '' && $('#td3').text() == '' && $('#td4').text() == '' && $('#td5').text() == '' && $('#td6').text() == ''){
      $('#notiCard').hide();
    }
    else{
      $('#notiCard').show();
    }
  }
  </script>

</head>

<body id="page-top">
  <!-- Navigation -->
  <nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
    <div class="container">
      <a class="navbar-brand js-scroll-trigger" href="index.html">Home</a>
      <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="navbarResponsive">
        <ul class="navbar-nav ml-auto">
          <li class="nav-item">
            <a class="nav-link js-scroll-trigger" href="registration.html">Worker Registration</a>
          </li>
          <li class="nav-item">
            <a class="nav-link js-scroll-trigger" href="users.html">Worker Inquire</a>
          </li>
          <li class="nav-item">
            <a class="nav-link js-scroll-trigger" href="zone-monitoring.html">Zone Moniroting</a>
          </li>
        </ul>
      </div>
    </div>
  </nav>

<div class="container">

  <section>
    <div id="container_demo">
      <a class="hiddenanchor" id="toadmin_users"></a>

      <div id="wrapper2">

        <div id="login" class="animate form">

          <form>
            <h1>Monitoring</h1>
            <div>
              <h2 id="worker">  </h2>
            </div>
              <br>
              <div>
                <table border="0" align="center">

                  <tr align="left" style="width: 100%;">
                    <th style="width: 30%; padding-right: 10px;"> <b>Location</b> </th>
                    <th style="width: 30%; padding-right: 10px"> <b>Heart Rate</b> </th>
                    <th style="width: 30%;"> <b>Fall Down</b> </th>
                  </tr>

                  <tr align="left" style="width: 100%;">
                    <td> <div class="box" id="box1"></div> </td>
                    <td> <div class="box" id="box2"></div> </td>
                    <td> <div class="box" id="box3"></div> </td>
                  </tr>
                </table>
            </div>
              <br>
              <br>


            <!--온도 그래프-->
            <div class="chart-container" style="position: relative; height:300px; width:100%;">
              <canvas id="tempChart"></canvas>
            </div>
            <br>

            <!--습도 그래프-->
            <div class="chart-container" style="position: relative; height:300px; width:100%;">
              <canvas id="humiChart"></canvas>
            </div>
            <br>

            <!--메탄가스 그래프-->
            <div class="chart-container" style="position: relative; height:300px; width:100%;">
              <canvas id="methChart"></canvas>
            </div>
            <br>

            <!--일산화탄소 그래프-->
            <div class="chart-container" style="position: relative; height:300px; width:100%;">
              <canvas id="cmonChart"></canvas>
            </div>
            <br>

            <br><br>

            <p class="change_link2">

              <a href="users.html">Back</a>

            </p>
          </form>
        </div>
      </div>
    </div>
  </section>

  <div class="card" id="notiCard">
    <div class="card-header">
					<button type="button" class="close" onclick="closeNoti()">
						<span aria-hidden="true">&times;</span>
					</button>
    </div>
    <div class="card-body">
      <table id="notiTable">
        <tbody>
          <tr>
            <td id="td1"></td>
          </tr>
          <tr>
            <td id="td2"></td>
          </tr>
          <tr>
            <td id="td3"></td>
          </tr>
          <tr>
            <td id="td4"></td>
          </tr>
          <tr>
            <td id="td5"></td>
          </tr>
          <tr>
            <td id="td6"></td>
          </tr>
          <tr style="text-align: right;">
            <td><button type='button' class='btn btn-primary btn-sm' onclick='clickAlert()'>알림</button></td>
          </tr>
        </tbody>
      </table>

    </div>
  </div>
</div>


<script>
  function clickAlert(){
    $('#td1').text('');
    $('#td2').text('');
    $('#td3').text('');
    $('#td4').text('');
    $('#td5').text('');
    $('#td6').text('');
    $('#notiCard').hide();
    createCin(ae_resource, 'buzzer', 'ON');
  }
</script>


<!-- 온도 그래프 관련 스크립트 -->
<script>

  startCheckTempChart = function() {
		playAlert = setInterval(function() {
			checkTempChart(ae_resource);
		}, 5000);
	};

  let tempConfig;
  let tempCtx;
	let tempChart;

  let lastTemp = 0;

  function drawTempChart(ae_resource){

    selectCinAsync(ae_resource, 'temperature').then((res) => {
      if (res.statusText == 'OK') {
      res.json().then(data => {

        let list = data['m2m:rsp']['m2m:cin'];
        lastTemp = list[0]['ct'];   // 온도 데이터 마지막 timestamp 값 저장

        let timeArr = [];
        let conArr = [];

        // 주의) 모비우스에 새로운 데이터가 앞에서부터 추가됨.
        let lastIdx = list.length > 15 ? 16 : list.length;
        for(let i=0; i<lastIdx; i++){
          let date = list[i]['ct'].substr(2,2)+'-'+list[i]['ct'].substr(4,2)+'-'+list[i]['ct'].substr(6,2)+' '+list[i]['ct'].substr(9,2)+':'+list[i]['ct'].substr(11,2)+':'+list[i]['ct'].substr(13,2);

          // 최신데이터가 앞에있어서 배열에 앞에서부터 추가하면 제대로 정렬됨
          timeArr.unshift(date);
          conArr.unshift(list[i]['con']);
        }

        tempCtx = document.getElementById('tempChart');

        tempConfig = {
          type: 'line',
          data: {
            labels: timeArr,
            datasets: [{
              label: 'temp',
              backgroundColor: 'rgba(255, 99, 132, 1)',
              borderColor: 'rgba(255, 99, 132, 0.8)',
              fill: false,
              data: conArr,
            }]
          },
          options: {
            maintainAspectRatio: false,
            legend: {
        	    display: false
            },
            title: {
              display : true, text: 'Temp Graph'
            },
            scales: {
              yAxes: [{
                scaleLabel: {
                  display: true,
                  labelString: 'Temp'
                }
              }]
            },
          }
        };

        //차트 그리기
        tempChart = new Chart(tempCtx, tempConfig);

        startCheckTempChart();

      });
      }
    }).catch(err => console.error(err));
  }

  // 데이터 select해서 그래프에 그려지지 않은 데이터 있으면 추가
  function checkTempChart(ae_resource){
    selectCinAsync(ae_resource, 'temperature').then((res) => {
      if (res.statusText == 'OK') {
        res.json().then(data => {

          let list = data['m2m:rsp']['m2m:cin'];

          if(list[0]['ct'] != lastTemp){
            lastTemp = list[0]['ct'];
            let date = list[0]['ct'].substr(2,2)+'-'+list[0]['ct'].substr(4,2)+'-'+list[0]['ct'].substr(6,2)+' '+list[0]['ct'].substr(9,2)+':'+list[0]['ct'].substr(11,2)+':'+list[0]['ct'].substr(13,2);
            insertTempChart(tempConfig, date, list[0]['con']);

            if(list[0]['con'] > 49){
              $('#td3').text("Worker temperature is dangerous.");
              checkNoti();
            }
            else{
              $('#td3').text('');
              checkNoti();
            }
          }

        });
      }
    }).catch(err => console.error(err));
  }

  function insertTempChart(tempConfig, label, data){

    tempConfig.data.labels.push(label);
    var dataset = tempConfig.data.datasets;
    dataset[0].data.push(data);

    // x축 15개 고정하기 위함
    if(tempConfig.data.labels.length > 15){
      tempConfig.data.labels.shift();
      dataset[0].data.shift();
    }

    tempChart.update();	//차트 업데이트
  }


</script>



<!-- 습도 그래프 관련 스크립트 -->
<script>

  startCheckHumiChart = function() {
		playAlert = setInterval(function() {
			checkHumiChart(ae_resource);
		}, 5000);
	};

  let humiConfig;
  let humiCtx;
	let humiChart;

  let lastHumi = 0;

  function drawHumiChart(ae_resource){
    selectCinAsync(ae_resource, 'humidity').then((res) => {
      if (res.statusText == 'OK') {
      res.json().then(data => {

        let list = data['m2m:rsp']['m2m:cin'];
        lastHumi = list[0]['ct'];   // 습도 데이터 마지막 timestamp 값 저장

        let timeArr = [];
        let conArr = [];

        // 주의) 모비우스에 새로운 데이터가 앞에서부터 추가됨.
        let lastIdx = list.length > 15 ? 16 : list.length;
        for(let i=0; i<lastIdx; i++){
          let date = list[i]['ct'].substr(2,2)+'-'+list[i]['ct'].substr(4,2)+'-'+list[i]['ct'].substr(6,2)+' '+list[i]['ct'].substr(9,2)+':'+list[i]['ct'].substr(11,2)+':'+list[i]['ct'].substr(13,2);

          // 최신데이터가 앞에있어서 배열에 앞에서부터 추가하면 제대로 정렬됨
          timeArr.unshift(date);
          conArr.unshift(list[i]['con']);
        }

        humiCtx = document.getElementById('humiChart');

        humiConfig = {
          type: 'line',
          data: {
            labels: timeArr,
            datasets: [{
              label: 'humi',
              backgroundColor: 'rgba(54, 162, 235, 1)',
              borderColor: 'rgba(54, 162, 235, 0.8)',
              fill: false,
              data: conArr,
            }]
          },
          options: {
            maintainAspectRatio: false,
            legend: {
        	    display: false
            },
            title: {
              display : true, text: 'Humidity Graph'
            },
            scales: {
              yAxes: [{
                scaleLabel: {
                  display: true,
                  labelString: 'Humidity'
                }
              }]
            },
          }
        };

        //차트 그리기
        humiChart = new Chart(humiCtx, humiConfig);

        startCheckHumiChart();

      });
      }
    }).catch(err => console.error(err));
  }

  // 데이터 select해서 그래프에 그려지지 않은 데이터 있으면 추가
  function checkHumiChart(ae_resource){
    selectCinAsync(ae_resource, 'humidity').then((res) => {
      if (res.statusText == 'OK') {
        res.json().then(data => {

          let list = data['m2m:rsp']['m2m:cin'];

          if(list[0]['ct'] != lastHumi){
            lastHumi = list[0]['ct'];
            let date = list[0]['ct'].substr(2,2)+'-'+list[0]['ct'].substr(4,2)+'-'+list[0]['ct'].substr(6,2)+' '+list[0]['ct'].substr(9,2)+':'+list[0]['ct'].substr(11,2)+':'+list[0]['ct'].substr(13,2);
            insertHumiChart(humiConfig, date, list[0]['con']);

            if(list[0]['con'] > 69){
              $('#td4').text("Worker humidity is dangerous.");
              checkNoti();
            }
            else{
              $('#td4').text('');
              checkNoti();
            }
          }

        });
      }
    }).catch(err => console.error(err));
  }

  function insertHumiChart(humiConfig, label, data){

    humiConfig.data.labels.push(label);
    var dataset = humiConfig.data.datasets;
    dataset[0].data.push(data);

    // x축 15개 고정하기 위함
    if(humiConfig.data.labels.length > 15){
      humiConfig.data.labels.shift();
      dataset[0].data.shift();
    }

    humiChart.update();	//차트 업데이트
  }

</script>


<!-- 메탄가스 그래프 관련 스크립트 -->
<script>

  startCheckMethChart = function() {
		playAlert = setInterval(function() {
			checkMethChart(ae_resource);
		}, 5000);
	};

  let methConfig;
  let methCtx;
	let methChart;

  let lastMeth = 0;

  function drawMethChart(ae_resource){
    selectCinAsync(ae_resource, 'methane').then((res) => {
      if (res.statusText == 'OK') {
      res.json().then(data => {

        let list = data['m2m:rsp']['m2m:cin'];
        lastMeth = list[0]['ct'];

        let timeArr = [];
        let conArr = [];

        // 주의) 모비우스에 새로운 데이터가 앞에서부터 추가됨.
        let lastIdx = list.length > 15 ? 16 : list.length;
        for(let i=0; i<lastIdx; i++){
          let date = list[i]['ct'].substr(2,2)+'-'+list[i]['ct'].substr(4,2)+'-'+list[i]['ct'].substr(6,2)+' '+list[i]['ct'].substr(9,2)+':'+list[i]['ct'].substr(11,2)+':'+list[i]['ct'].substr(13,2);

          // 최신데이터가 앞에있어서 배열에 앞에서부터 추가하면 제대로 정렬됨
          timeArr.unshift(date);
          conArr.unshift(list[i]['con']);
        }

        methCtx = document.getElementById('methChart');

        methConfig = {
          type: 'line',
          data: {
            labels: timeArr,
            datasets: [{
              label: 'meth',
              backgroundColor: 'rgba(255, 206, 86, 1)',
              borderColor: 'rgba(255, 206, 86, 0.8)',
              fill: false,
              data: conArr,
            }]
          },
          options: {
            maintainAspectRatio: false,
            legend: {
        	    display: false
            },
            title: {
              display : true, text: 'Methane Graph'
            },
            scales: {
              yAxes: [{
                scaleLabel: {
                  display: true,
                  labelString: 'Methane gas'
                }
              }]
            },
          }
        };

        //차트 그리기
        methChart = new Chart(methCtx, methConfig);

        startCheckMethChart();

      });
      }
    }).catch(err => console.error(err));
  }

  // 데이터 select해서 그래프에 그려지지 않은 데이터 있으면 추가
  function checkMethChart(ae_resource){
    selectCinAsync(ae_resource, 'methane').then((res) => {
      if (res.statusText == 'OK') {
        res.json().then(data => {

          let list = data['m2m:rsp']['m2m:cin'];

          if(list[0]['ct'] != lastMeth){
            lastMeth = list[0]['ct'];
            let date = list[0]['ct'].substr(2,2)+'-'+list[0]['ct'].substr(4,2)+'-'+list[0]['ct'].substr(6,2)+' '+list[0]['ct'].substr(9,2)+':'+list[0]['ct'].substr(11,2)+':'+list[0]['ct'].substr(13,2);
            insertMethChart(methConfig, date, list[0]['con']);

            if(list[0]['con'] > 49){
              $('#td5').text("Worker mehtane gas is dangerous.");
              checkNoti();
            }
            else{
              $('#td5').text('');
              checkNoti();
            }
          }

        });
      }
    }).catch(err => console.error(err));
  }

  function insertMethChart(methConfig, label, data){

    methConfig.data.labels.push(label);
    var dataset = methConfig.data.datasets;
    dataset[0].data.push(data);

    // x축 15개 고정하기 위함
    if(methConfig.data.labels.length > 15){
      methConfig.data.labels.shift();
      dataset[0].data.shift();
    }

    methChart.update();	//차트 업데이트
  }
</script>


<!-- 일산화탄소 그래프 관련 스크립트 -->
<script>

  startCheckCmonChart = function() {
		playAlert = setInterval(function() {
			checkCmonChart(ae_resource);
		}, 5000);
	};

  let cmonConfig;
  let cmonCtx;
	let cmonChart;

  let lastCmon = 0;

  function drawCmonChart(ae_resource){

    selectCinAsync(ae_resource, 'cmonoxide').then((res) => {
      if (res.statusText == 'OK') {
      res.json().then(data => {

        let list = data['m2m:rsp']['m2m:cin'];
        lastCmon = list[0]['ct'];

        let timeArr = [];
        let conArr = [];

        // 주의) 모비우스에 새로운 데이터가 앞에서부터 추가됨.
        let lastIdx = list.length > 15 ? 16 : list.length;
        for(let i=0; i<lastIdx; i++){
          let date = list[i]['ct'].substr(2,2)+'-'+list[i]['ct'].substr(4,2)+'-'+list[i]['ct'].substr(6,2)+' '+list[i]['ct'].substr(9,2)+':'+list[i]['ct'].substr(11,2)+':'+list[i]['ct'].substr(13,2);

          // 최신데이터가 앞에있어서 배열에 앞에서부터 추가하면 제대로 정렬됨
          timeArr.unshift(date);
          conArr.unshift(list[i]['con']);
        }

        cmonCtx = document.getElementById('cmonChart');

        cmonConfig = {
          type: 'line',
          data: {
            labels: timeArr,
            datasets: [{
              label: 'cmon',
              backgroundColor: 'rgba(75, 192, 192, 1)',
              borderColor: 'rgba(75, 192, 192, 0.8)',
              fill: false,
              data: conArr,
            }]
          },
          options: {
            maintainAspectRatio: false,
            legend: {
        	    display: false
            },
            title: {
              display : true, text: 'Cmonoxide Graph'
            },
            scales: {
              yAxes: [{
                scaleLabel: {
                  display: true,
                  labelString: 'Cmonoxide'
                }
              }]
            },
          }
        };

        //차트 그리기
        cmonChart = new Chart(cmonCtx, cmonConfig);

        startCheckCmonChart();

      });
      }
    }).catch(err => console.error(err));
  }

  // 데이터 select해서 그래프에 그려지지 않은 데이터 있으면 추가
  function checkCmonChart(ae_resource){
    selectCinAsync(ae_resource, 'cmonoxide').then((res) => {
      if (res.statusText == 'OK') {
        res.json().then(data => {

          let list = data['m2m:rsp']['m2m:cin'];

          if(list[0]['ct'] != lastCmon){
            lastCmon = list[0]['ct'];
            let date = list[0]['ct'].substr(2,2)+'-'+list[0]['ct'].substr(4,2)+'-'+list[0]['ct'].substr(6,2)+' '+list[0]['ct'].substr(9,2)+':'+list[0]['ct'].substr(11,2)+':'+list[0]['ct'].substr(13,2);
            insertCmonChart(cmonConfig, date, list[0]['con']);

            if(list[0]['con'] > 49){
              $('#td6').text("Worker cmonoxide is dangerous..");
              checkNoti();
            }
            else{
              $('#td6').text('');
              checkNoti();
            }
          }

        });
      }
    }).catch(err => console.error(err));
  }

  function insertCmonChart(cmonConfig, label, data){

    cmonConfig.data.labels.push(label);
    var dataset = cmonConfig.data.datasets;
    dataset[0].data.push(data);

    // x축 15개 고정하기 위함
    if(cmonConfig.data.labels.length > 15){
      cmonConfig.data.labels.shift();
      dataset[0].data.shift();
    }

    cmonChart.update();	//차트 업데이트
  }
</script>



</body>

<!-- Bootstrap core JavaScript -->
<script src="vendor/jquery/jquery.min.js"></script>
<script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>

<!-- Plugin JavaScript -->
<script src="vendor/jquery-easing/jquery.easing.min.js"></script>
<script src="vendor/scrollreveal/scrollreveal.min.js"></script>
<script src="vendor/magnific-popup/jquery.magnific-popup.min.js"></script>

<!-- Custom scripts for this template -->
<script src="js/creative.min.js"></script>

</body>

</html>

Web Page

HTML
Zone Monitoring Page
<!DOCTYPE html>

<html lang="en" class="no-js">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="">
  <meta name="author" content="">

  <title>Smart Working Environment Monitoring Service</title>
  <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
  <link href="https://fonts.googleapis.com/css?family=Raleway:100,100i,200,200i,300,300i,400,400i,500,500i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">

  <link href="css/abc.css" rel="stylesheet">

  <!-- Bootstrap core CSS -->
  <link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">

  <!-- Plugin CSS -->
  <link href="vendor/magnific-popup/magnific-popup.css" rel="stylesheet">

  <!-- Custom styles for this template -->
  <link href="css/creative.min.css" rel="stylesheet">

  <!-- jquery -->
  <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>

  <!-- 모비우스 -->
  <script src="./mobi-hero-mobius.js"></script>

  <!-- 아이콘 -->
  <script src="https://use.fontawesome.com/releases/v5.2.0/js/all.js"></script>

  <!-- 차트 -->
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.bundle.min.js"></script>
	<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js"></script>

  <style>
    .box{
      display: flex;
      height: 150px;
      width: 220px;
      justify-content: center;
      align-items: center;
      border: solid 1px gray;
      font-size:xx-large;
      font-weight: bold;
    }

    .placeBox{
      border: 1px solid black;
      width: 100%;
      height: 100px;
      margin-bottom: 20px;
      padding: 10px;
    }

    #notiCard{
      position: fixed;
      bottom: 10px;
      right: 10px;


      padding: 5px;
      font-size: small;
      text-align: left;
    }
    .card-body{
      padding: 10px;
    }
    .card-header{
      padding: 5px;
    }

    #notiTable{
      width: 300px;
    }
  </style>

</head>

<body id="page-top">
  <!-- Navigation -->
  <nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
    <div class="container">
      <a class="navbar-brand js-scroll-trigger" href="index.html">Home</a>
      <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="navbarResponsive">
        <ul class="navbar-nav ml-auto">
          <li class="nav-item">
            <a class="nav-link js-scroll-trigger" href="registration.html">Worker Registration</a>
          </li>
          <li class="nav-item">
            <a class="nav-link js-scroll-trigger" href="users.html">Worker Inquire</a>
          </li>
          <li class="nav-item">
            <a class="nav-link js-scroll-trigger" href="zone-monitoring.html">Zone Moniroting</a>
          </li>
        </ul>
      </div>
    </div>
  </nav>

<div class="container">

  <section>
    <div id="container_demo">
      <a class="hiddenanchor" id="toadmin_users"></a>

      <div id="wrapper2">

        <div id="login" class="animate form">

          <form>
            <h1>Zone Monitoring</h1>

            <img src="./img/drawing.png" width="100%" alt="" style="margin-bottom: 10px;">

            <div class="placeBox">
              <label style="font-weight: bolder;">A1</label>
              <label style="float: right">Number of people : <label id="total_a1">0</label></label>
              <br><br>
              <p id="list_a1"></p>
            </div>
            <div class="placeBox">
              <label style="font-weight: bolder;">A2</label>
              <label style="float: right">Number of people : <label id="total_a2">0</label></label>
              <br><br>
              <p id="list_a2"></p>
            </div>
            <div class="placeBox">
              <label style="font-weight: bolder;">A3</label>
              <label style="float: right">Number of people : <label id="total_a3">0</label></label>
              <br><br>
              <p id="list_a3"></p>
            </div>
            <div class="placeBox">
              <label style="font-weight: bolder;">A4</label>
              <label style="float: right">Number of people : <label id="total_a4">0</label></label>
              <br><br>
              <p id="list_a4"></p>
            </div>

            <br>
            <!--온도 그래프-->
            <div class="chart-container" style="position: relative; height:300px; width:100%; margin-bottom: 50px;">
              <canvas id="tempChart"></canvas>
            </div>
            <br>
            <br>
            <br>

            <!--습도 그래프-->
            <div class="chart-container" style="position: relative; height:300px; width:100%; margin-bottom: 50px;">
              <canvas id="humiChart"></canvas>
            </div>
            <br>
            <br>
            <br>

            <!--메탄가스 그래프-->
            <div class="chart-container" style="position: relative; height:300px; width:100%; margin-bottom: 50px;">
              <canvas id="methChart"></canvas>
            </div>
            <br>
            <br>
            <br>

            <!--일산화탄소 그래프-->
            <div class="chart-container" style="position: relative; height:300px; width:100%; margin-bottom: 50px;">
              <canvas id="cmonChart"></canvas>
            </div>
            <br>

            <br><br>


            <p class="change_link2">

              <a href="index.html">Back</a>

            </p>
          </form>
        </div>
      </div>
    </div>
  </section>

  <div class="card" id="notiCard">
    <div class="card-header">
					<button type="button" class="close" onclick="closeNoti()">
						<span aria-hidden="true">&times;</span>
					</button>
    </div>
    <div class="card-body">
      <table id="notiTable">
        <tbody>
          <tr>
            <td id="td1"></td>
          </tr>
          <tr>
            <td id="td2"></td>
          </tr>
          <tr>
            <td id="td3"></td>
          </tr>
          <tr>
            <td id="td4"></td>
          </tr>
        </tbody>
      </table>

    </div>
  </div>
</div>


<script>

  function closeNoti(){
    $('#notiCard').hide();
  }

  function openNoti(){
    $('#notiCard').show();
  }

  let ae_resource;
  let name;

  $(document).ready(function () {

    $('#notiCard').hide();

      displayZoneList();
      startDisplayZoneList();

      // 온도 그래프 그림
      drawZoneTempChart();

      // 습도 그래프 그림
      drawZoneHumiChart();

      // 메탄가스 그래프 그림
      drawZoneMethChart();

      // 일산화탄소 그래프 그림
      drawZoneCmonChart();

  });

  startDisplayZoneList = function() {
    playAlert = setInterval(function() {
      displayZoneList();
    }, 5000);
  };

  function displayZoneList(){
    console.log("worker list : ");

    let uri = path + '?rcn=4&ty=2';

    fetch(uri, {
      method: 'GET',
      headers: {
          'Host':'<calculated when request is sent>',
          'User-Agent':'PostmanRuntime/7.28.4',
          'Accept': 'application/json',
          'Accept-Encoding':'gzip, deflate, br',
          'Connection':'keep-alive',
          'X-M2M-RI':'12345',
          'X-M2M-Origin':'admin',
          'Content-Type': 'application/json'
      }
      })
      .then((res) => {
          if (res.statusText == 'OK') {
              res.json().then(data => {

                  let list = data['m2m:rsp']['m2m:ae'];

                  let a1_str = "";
                  let a2_str = "";
                  let a3_str = "";
                  let a4_str = "";

                  let a1_num = 0;
                  let a2_num = 0;
                  let a3_num = 0;
                  let a4_num = 0;

                  for(let i=0; i<list.length; i++){
                      if(list[i]['api'] == 'worker'){

                          selectGrpAsync(list[i]['rn'], 'notegrp').then((res) => {
                              if (res.statusText == 'OK' || res.statusText == 'Created') {
                                  res.json().then(data => {

                                      let name = data["m2m:agr"]["m2m:rsp"][0]["pc"]["m2m:cin"]["con"];       // 이름
                                      let hp = data["m2m:agr"]["m2m:rsp"][1]["pc"]["m2m:cin"]["con"];         // 전화번호
                                      let loc = data["m2m:agr"]["m2m:rsp"][2]["pc"]["m2m:cin"]["con"];        // 출근 상태

                                      console.log(list[i]['rn'] + ' : ' +name + ' ' + hp + ' ' + loc);

                                      if(loc == 'A1'){
                                        a1_num++;
                                        a1_str = a1_str + name + ' ';
                                      }
                                      else if(loc == 'A2'){
                                        a2_num++;
                                        a2_str = a2_str + name + ' ';
                                      }
                                      else if(loc == 'A3'){
                                        a3_num++;
                                        a3_str = a3_str + name + ' ';
                                      }
                                      else if(loc == 'A4'){
                                        a4_num++;
                                        a4_str = a4_str + name + ' ';
                                      }
                                      else{
                                      }

                                      // 구역별 인원 목록 작성
                                      $('#total_a1').text(a1_num);
                                      $('#list_a1').text(a1_str);

                                      $('#total_a2').text(a2_num);
                                      $('#list_a2').text(a2_str);

                                      $('#total_a3').text(a3_num);
                                      $('#list_a3').text(a3_str);

                                      $('#total_a4').text(a4_num);
                                      $('#list_a4').text(a4_str);

                                  });
                              }
                              else {
                                  alert('ERROR');
                                  console.error(res);
                              }
                          }).catch(err => console.error(err));
                      }
                  }
              });
          }
          else {
              alert('ERROR');
              console.error(res);
          }
      }).catch(err => console.error(err));
  }
  </script>


<!-- 구역별 온도 그래프 관련 -->
<script>
  let tList = [];

  let tempConfig;
  let tempCtx;
	let tempChart;

  startZoneCheckTempChart = function() {
    playAlert = setInterval(function() {
      checkZoneTempChart();
    }, 5000);
  };

  function drawZoneTempChart(){
    selectGrpAsync('Zone', 'ztempgrp').then((res) => {
        if (res.statusText == 'OK') {
          res.json().then(data => {

            tList.push(data['m2m:agr']['m2m:rsp'][0]['pc']['m2m:cin']['con']);     // A1 구역 온도 값
            tList.push(data['m2m:agr']['m2m:rsp'][1]['pc']['m2m:cin']['con']);     // A2 구역 온도 값
            tList.push(data['m2m:agr']['m2m:rsp'][2]['pc']['m2m:cin']['con']);     // A3 구역 온도 값
            tList.push(data['m2m:agr']['m2m:rsp'][3]['pc']['m2m:cin']['con']);     // A4 구역 온도 값

            console.log(tList);

            tempCtx = document.getElementById('tempChart').getContext('2d');
            tempConfig = {
                type: 'bar', // 차트의 형태
                data: { // 차트에 들어갈 데이터
                    labels: [
                        //x 축
                        'A1','A2','A3','A4'
                    ],
                    datasets: [
                        { //데이터
                            label: '', //차트 제목
                            fill: false, // line 형태일 때, 선 안쪽을 채우는지 안채우는지
                            data: tList,
                            backgroundColor: [
                                //색상
                                'rgba(255, 99, 132, 0.2)',
                                'rgba(54, 162, 235, 0.2)',
                                'rgba(255, 206, 86, 0.2)',
                                'rgba(75, 192, 192, 0.2)'
                            ],
                            borderColor: [
                                //경계선 색상
                                'rgba(255, 99, 132, 1)',
                                'rgba(54, 162, 235, 1)',
                                'rgba(255, 206, 86, 1)',
                                'rgba(75, 192, 192, 1)'
                            ],
                            borderWidth: 1 //경계선 굵기
                        }
                    ]
                },
                options: {
                  legend: {
                    display: false
                  },
                  title: {
                    display : true, text: 'Temperature graph by zone'
                  },
                    scales: {
                        yAxes: [
                            {
                                ticks: {
                                    beginAtZero: true
                                }
                            }
                        ]
                    }
                }
            };
            tempChart = new Chart(tempCtx, tempConfig);
            startZoneCheckTempChart();
          });
        }
      }).catch(err => console.error(err));
  }

  // 데이터 select해서 그래프에 그려지지 않은 데이터 있으면 추가
  function checkZoneTempChart(){
    selectGrpAsync('Zone', 'ztempgrp').then((res) => {
        if (res.statusText == 'OK') {
          res.json().then(data => {

            let newList = [];

            newList.push(data['m2m:agr']['m2m:rsp'][0]['pc']['m2m:cin']['con']);     // A1 구역 온도 값
            newList.push(data['m2m:agr']['m2m:rsp'][1]['pc']['m2m:cin']['con']);     // A2 구역 온도 값
            newList.push(data['m2m:agr']['m2m:rsp'][2]['pc']['m2m:cin']['con']);     // A3 구역 온도 값
            newList.push(data['m2m:agr']['m2m:rsp'][3]['pc']['m2m:cin']['con']);     // A4 구역 온도 값

            if(JSON.stringify(tList)!=JSON.stringify(newList)){

              console.log("temp data change detection");

              //데이터셋 수 만큼 반복
              var dataset = tempConfig.data.datasets;
              var data = dataset[0].data;
              for(var i=0 ; i < data.length ; i++){
                  data[i] = newList[i];
              }
              tempChart.update();	//차트 업데이트

              let dangerList = [];
              for(let i=0; i<4; i++){
                if(data[i] > 48){
                  let str = 'A'+(i+1);
                  dangerList.push(str);
                }
              }
              if(dangerList.length != 0){
                $('#notiCard').show();
                $('#td1').text(dangerList.toString()+' temperature is dangerous');
              }
              else{
                $('#td1').text('');
                if($('#td1').text() == '' && $('#td2').text()=='' && $('#td3').text()=='' && $('#td4').text()==''){
                  $('#notiCard').hide();
                }
              }
            }
          });
        }
      }).catch(err => console.error(err));
  }
</script>


<!-- 구역별 습도 그래프 관련 -->
<script>
  let hList = [];

  let humiConfig;
  let humiCtx;
	let humiChart;

  startZoneCheckHumiChart = function() {
    playAlert = setInterval(function() {
      checkZoneHumiChart();
    }, 5000);
  };

  function drawZoneHumiChart(){
    selectGrpAsync('Zone', 'zhumigrp').then((res) => {
        if (res.statusText == 'OK') {
          res.json().then(data => {

            hList.push(data['m2m:agr']['m2m:rsp'][0]['pc']['m2m:cin']['con']);     // A1 구역 온도 값
            hList.push(data['m2m:agr']['m2m:rsp'][1]['pc']['m2m:cin']['con']);     // A2 구역 온도 값
            hList.push(data['m2m:agr']['m2m:rsp'][2]['pc']['m2m:cin']['con']);     // A3 구역 온도 값
            hList.push(data['m2m:agr']['m2m:rsp'][3]['pc']['m2m:cin']['con']);     // A4 구역 온도 값

            console.log(hList);

            humiCtx = document.getElementById('humiChart').getContext('2d');
            humiConfig = {
                type: 'bar', // 차트의 형태
                data: { // 차트에 들어갈 데이터
                    labels: [
                        //x 축
                        'A1','A2','A3','A4'
                    ],
                    datasets: [
                        { //데이터
                            label: '', //차트 제목
                            fill: false, // line 형태일 때, 선 안쪽을 채우는지 안채우는지
                            data: hList,
                            backgroundColor: [
                                //색상
                                'rgba(255, 99, 132, 0.2)',
                                'rgba(54, 162, 235, 0.2)',
                                'rgba(255, 206, 86, 0.2)',
                                'rgba(75, 192, 192, 0.2)'
                            ],
                            borderColor: [
                                //경계선 색상
                                'rgba(255, 99, 132, 1)',
                                'rgba(54, 162, 235, 1)',
                                'rgba(255, 206, 86, 1)',
                                'rgba(75, 192, 192, 1)'
                            ],
                            borderWidth: 1 //경계선 굵기
                        }
                    ]
                },
                options: {
                  legend: {
                    display: false
                  },
                  title: {
                    display : true, text: 'Humidity graph by zone'
                  },
                    scales: {
                        yAxes: [
                            {
                                ticks: {
                                    beginAtZero: true
                                }
                            }
                        ]
                    }
                }
            };
            humiChart = new Chart(humiCtx, humiConfig);
            startZoneCheckHumiChart();
          });
        }
      }).catch(err => console.error(err));
  }

  // 데이터 select해서 그래프에 그려지지 않은 데이터 있으면 추가
  function checkZoneHumiChart(){
    selectGrpAsync('Zone', 'zhumigrp').then((res) => {
        if (res.statusText == 'OK') {
          res.json().then(data => {

            let newList = [];

            newList.push(data['m2m:agr']['m2m:rsp'][0]['pc']['m2m:cin']['con']);     // A1 구역 온도 값
            newList.push(data['m2m:agr']['m2m:rsp'][1]['pc']['m2m:cin']['con']);     // A2 구역 온도 값
            newList.push(data['m2m:agr']['m2m:rsp'][2]['pc']['m2m:cin']['con']);     // A3 구역 온도 값
            newList.push(data['m2m:agr']['m2m:rsp'][3]['pc']['m2m:cin']['con']);     // A4 구역 온도 값

            if(JSON.stringify(hList)!=JSON.stringify(newList)){

              console.log("humi data change detection");

              //데이터셋 수 만큼 반복
              var dataset = humiConfig.data.datasets;
              var data = dataset[0].data;
              for(var i=0 ; i < data.length ; i++){
                  data[i] = newList[i];
              }
              humiChart.update();	//차트 업데이트

              let dangerList = [];
              for(let i=0; i<4; i++){
                if(data[i] > 68){
                  let str = 'A'+(i+1);
                  dangerList.push(str);
                }
              }
              if(dangerList.length != 0){
                $('#notiCard').show();
                $('#td2').text(dangerList.toString()+' humidity is dangerous');
              }
              else{
                $('#td2').text('');
                if($('#td1').text() == '' && $('#td2').text()=='' && $('#td3').text()=='' && $('#td4').text()==''){
                  $('#notiCard').hide();
                }
              }
            }
          });
        }
      }).catch(err => console.error(err));
  }
</script>


<!-- 구역별 메탄가스 그래프 관련 -->
<script>
  let mList = [];

  let methConfig;
  let methCtx;
	let methChart;

  startZoneCheckMethChart = function() {
    playAlert = setInterval(function() {
      checkZoneMethChart();
    }, 5000);
  };

  function drawZoneMethChart(){
    selectGrpAsync('Zone', 'zmethgrp').then((res) => {
        if (res.statusText == 'OK') {
          res.json().then(data => {

            mList.push(data['m2m:agr']['m2m:rsp'][0]['pc']['m2m:cin']['con']);
            mList.push(data['m2m:agr']['m2m:rsp'][1]['pc']['m2m:cin']['con']);
            mList.push(data['m2m:agr']['m2m:rsp'][2]['pc']['m2m:cin']['con']);
            mList.push(data['m2m:agr']['m2m:rsp'][3]['pc']['m2m:cin']['con']);

            console.log(mList);

            methCtx = document.getElementById('methChart').getContext('2d');
            methConfig = {
                type: 'bar', // 차트의 형태
                data: { // 차트에 들어갈 데이터
                    labels: [
                        //x 축
                        'A1','A2','A3','A4'
                    ],
                    datasets: [
                        { //데이터
                            label: '', //차트 제목
                            fill: false, // line 형태일 때, 선 안쪽을 채우는지 안채우는지
                            data: mList,
                            backgroundColor: [
                                //색상
                                'rgba(255, 99, 132, 0.2)',
                                'rgba(54, 162, 235, 0.2)',
                                'rgba(255, 206, 86, 0.2)',
                                'rgba(75, 192, 192, 0.2)'
                            ],
                            borderColor: [
                                //경계선 색상
                                'rgba(255, 99, 132, 1)',
                                'rgba(54, 162, 235, 1)',
                                'rgba(255, 206, 86, 1)',
                                'rgba(75, 192, 192, 1)'
                            ],
                            borderWidth: 1 //경계선 굵기
                        }
                    ]
                },
                options: {
                  legend: {
                    display: false
                  },
                  title: {
                    display : true, text: 'Methane gas graph by zone'
                  },
                    scales: {
                        yAxes: [
                            {
                                ticks: {
                                    beginAtZero: true
                                }
                            }
                        ]
                    }
                }
            };
            methChart = new Chart(methCtx, methConfig);
            startZoneCheckMethChart();
          });
        }
      }).catch(err => console.error(err));
  }

  // 데이터 select해서 그래프에 그려지지 않은 데이터 있으면 추가
  function checkZoneMethChart(){
    selectGrpAsync('Zone', 'zmethgrp').then((res) => {
        if (res.statusText == 'OK') {
          res.json().then(data => {

            let newList = [];

            newList.push(data['m2m:agr']['m2m:rsp'][0]['pc']['m2m:cin']['con']);
            newList.push(data['m2m:agr']['m2m:rsp'][1]['pc']['m2m:cin']['con']);
            newList.push(data['m2m:agr']['m2m:rsp'][2]['pc']['m2m:cin']['con']);
            newList.push(data['m2m:agr']['m2m:rsp'][3]['pc']['m2m:cin']['con']);

            if(JSON.stringify(mList)!=JSON.stringify(newList)){

              console.log("meth data change detection");

              //데이터셋 수 만큼 반복
              var dataset = methConfig.data.datasets;
              var data = dataset[0].data;
              for(var i=0 ; i < data.length ; i++){
                  data[i] = newList[i];
              }
              methChart.update();	//차트 업데이트

              let dangerList = [];
              for(let i=0; i<4; i++){
                if(data[i] > 48){
                  let str = 'A'+(i+1);
                  dangerList.push(str);
                }
              }

              console.log("Methane Hazard Arrangement");
              console.log(dangerList);

              if(dangerList.length != 0){
                $('#notiCard').show();
                $('#td3').text(dangerList.toString()+' methane gas is dangerous.');
                console.log("methane if!");
              }
              else{
                $('#td3').text('');
                console.log("methane else!");
                console.log($('#td1').text() == '');
                console.log($('#td2').text() == '');
                console.log($('#td3').text() == '');
                console.log($('#td4').text() == '');
                if($('#td1').text() == '' && $('#td2').text()=='' && $('#td3').text()=='' && $('#td4').text()==''){
                  $('#notiCard').hide();
                  console.log("methane hide!");
                }
              }
            }
          });
        }
      }).catch(err => console.error(err));
  }
</script>


<!-- 구역별 일산화탄소 그래프 관련 -->
<script>
  let cList = [];

  let cmonConfig;
  let cmonCtx;
	let cmonChart;

  startZoneCheckCmonChart = function() {
    playAlert = setInterval(function() {
      checkZoneCmonChart();
    }, 5000);
  };

  function drawZoneCmonChart(){
    selectGrpAsync('Zone', 'zcmongrp').then((res) => {
        if (res.statusText == 'OK') {
          res.json().then(data => {

            cList.push(data['m2m:agr']['m2m:rsp'][0]['pc']['m2m:cin']['con']);
            cList.push(data['m2m:agr']['m2m:rsp'][1]['pc']['m2m:cin']['con']);
            cList.push(data['m2m:agr']['m2m:rsp'][2]['pc']['m2m:cin']['con']);
            cList.push(data['m2m:agr']['m2m:rsp'][3]['pc']['m2m:cin']['con']);

            cmonCtx = document.getElementById('cmonChart').getContext('2d');
            cmonConfig = {
                type: 'bar', // 차트의 형태
                data: { // 차트에 들어갈 데이터
                    labels: [
                        //x 축
                        'A1','A2','A3','A4'
                    ],
                    datasets: [
                        { //데이터
                            label: '', //차트 제목
                            fill: false, // line 형태일 때, 선 안쪽을 채우는지 안채우는지
                            data: cList,
                            backgroundColor: [
                                //색상
                                'rgba(255, 99, 132, 0.2)',
                                'rgba(54, 162, 235, 0.2)',
                                'rgba(255, 206, 86, 0.2)',
                                'rgba(75, 192, 192, 0.2)'
                            ],
                            borderColor: [
                                //경계선 색상
                                'rgba(255, 99, 132, 1)',
                                'rgba(54, 162, 235, 1)',
                                'rgba(255, 206, 86, 1)',
                                'rgba(75, 192, 192, 1)'
                            ],
                            borderWidth: 1 //경계선 굵기
                        }
                    ]
                },
                options: {
                  legend: {
                    display: false
                  },
                  title: {
                    display : true, text: 'Cmonoxide graph by zone'
                  },
                    scales: {
                        yAxes: [
                            {
                                ticks: {
                                    beginAtZero: true
                                }
                            }
                        ]
                    }
                }
            };
            cmonChart = new Chart(cmonCtx, cmonConfig);
            startZoneCheckCmonChart();
          });
        }
      }).catch(err => console.error(err));
  }

  // 데이터 select해서 그래프에 그려지지 않은 데이터 있으면 추가
  function checkZoneCmonChart(){
    selectGrpAsync('Zone', 'zcmongrp').then((res) => {
        if (res.statusText == 'OK') {
          res.json().then(data => {

            let newList = [];

            newList.push(data['m2m:agr']['m2m:rsp'][0]['pc']['m2m:cin']['con']);
            newList.push(data['m2m:agr']['m2m:rsp'][1]['pc']['m2m:cin']['con']);
            newList.push(data['m2m:agr']['m2m:rsp'][2]['pc']['m2m:cin']['con']);
            newList.push(data['m2m:agr']['m2m:rsp'][3]['pc']['m2m:cin']['con']);

            if(JSON.stringify(cList)!=JSON.stringify(newList)){

              console.log("cmon data change detection");

              //데이터셋 수 만큼 반복
              var dataset = cmonConfig.data.datasets;
              var data = dataset[0].data;
              for(var i=0 ; i < data.length ; i++){
                  data[i] = newList[i];
              }
              cmonChart.update();	//차트 업데이트

              let dangerList = [];
              for(let i=0; i<4; i++){
                if(data[i] > 48){
                  let str = 'A'+(i+1);
                  dangerList.push(str);
                }
              }

              console.log("Cmonoxide Hazard Arrangement");
              console.log(dangerList);

              if(dangerList.length != 0){
                $('#notiCard').show();
                $('#td4').text(dangerList.toString()+' Cmonoxide is dangerous');
                console.log("Cmonoxide if");
              }
              else{
                $('#td4').textCmonoxide
                console.log("일산화탄소 else");
                if($('#td1').text() == '' && $('#td2').text()=='' && $('#td3').text()=='' && $('#td4').text()==''){
                  $('#notiCard').hide();
                  console.log("Cmonoxide hide");
                }
              }
            }
          });
        }
      }).catch(err => console.error(err));
  }
</script>



</body>

<!-- Bootstrap core JavaScript -->
<script src="vendor/jquery/jquery.min.js"></script>
<script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>

<!-- Plugin JavaScript -->
<script src="vendor/jquery-easing/jquery.easing.min.js"></script>
<script src="vendor/scrollreveal/scrollreveal.min.js"></script>
<script src="vendor/magnific-popup/jquery.magnific-popup.min.js"></script>

<!-- Custom scripts for this template -->
<script src="js/creative.min.js"></script>

</body>

</html>

Web Page

HTML
virtual device page
<!doctype html>
<html lang="ko">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>가상 작업자</title>

    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">

    <!-- jquery -->
      <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>

      <!-- 모비우스 -->
      <script src="./mobi-hero-mobius.js"></script>

    <style>
      p.state {
        font-size: large;
        font-weight: bold;
      }
      input{
        display: inline-block;
      }
    </style>


    <script>
      $(document).ready(function() {

        $('#btn1').click(function(){
          if($('#btn1').text() == 'Go to work'){
            $('#btn1').removeClass('btn-secondary');
		        $('#btn1').addClass('btn-primary');
            $('#btn1').text('Off work');

            createCin('worker1', 'location', "A1");

            getWorker1();
            startWorker1();
          }
          else if($('#btn1').text() == 'Off work'){
            $('#btn1').removeClass('btn-primary');
		        $('#btn1').addClass('btn-secondary');
            $('#btn1').text('Go to work');

            // 임의로 처음 Go to work할 때만 Location 바뀌게 해놓음
            createCin('worker1', 'location', "En");

            stopWorker1();
          }
          else{ }
        });

        $('#btn2').click(function(){
          if($('#btn2').text() == 'Go to work'){
            $('#btn2').removeClass('btn-secondary');
		        $('#btn2').addClass('btn-primary');
            $('#btn2').text('Off work');

            createCin('worker2', 'location', "A2");

            getWorker2();
            startWorker2();
          }
          else if($('#btn2').text() == 'Off work'){
            $('#btn2').removeClass('btn-primary');
		        $('#btn2').addClass('btn-secondary');
            $('#btn2').text('Go to work');

            // 임의로 처음 Go to work할 때만 Location 바뀌게 해놓음
            createCin('worker2', 'location', "En");

            stopWorker2();
          }
          else{ }
        });

        $('#btn3').click(function(){
          if($('#btn3').text() == 'Go to work'){
            $('#btn3').removeClass('btn-secondary');
		        $('#btn3').addClass('btn-primary');
            $('#btn3').text('Off work');

            createCin('worker3', 'location', "A1");

            getWorker3();
            startWorker3();
          }
          else if($('#btn3').text() == 'Off work'){
            $('#btn3').removeClass('btn-primary');
		        $('#btn3').addClass('btn-secondary');
            $('#btn3').text('Go to work');

            // 임의로 처음 Go to work할 때만 Location 바뀌게 해놓음
            createCin('worker3', 'location', "En");

            stopWorker3();
          }
          else{ }
        });

        $('#btn4').click(function(){
          if($('#btn4').text() == 'Go to work'){
            $('#btn4').removeClass('btn-secondary');
		        $('#btn4').addClass('btn-primary');
            $('#btn4').text('Off work');

            createCin('worker4', 'location', "A2");

            getWorker4();
            startWorker4();
          }
          else if($('#btn4').text() == 'Off work'){
            $('#btn4').removeClass('btn-primary');
		        $('#btn4').addClass('btn-secondary');
            $('#btn4').text('Go to work');

            // 임의로 처음 Go to work할 때만 Location 바뀌게 해놓음
            createCin('worker4', 'location', "En");

            stopWorker4();
          }
          else{ }
        });


      });

      // 랜덤 값 생성 함수
      function createRandomNumber(min, max){
        let randNum = Math.floor(Math.random()*(max-min+1)) + min;
        return randNum;
      }

      // 작업자 랜덤 데이터 생성
      function createRandomWorkerDate() {
        let arr = ['A1', 'A2', 'A3', 'A4'];       // Off work할 때 location에 En 값 보내야 함
        let idx = createRandomNumber(0, 3);
        let location = arr[idx];
        let temperature = createRandomNumber(15, 50);
        let humidity = createRandomNumber(10, 70);
        let methane = createRandomNumber(1, 50);
        let cmonoxide = createRandomNumber(1, 50);
        let heartrate = createRandomNumber(70, 90);
        let danger = createRandomNumber(0, 10) < 10 ? 0 : 'fall';
        let state;
        if (temperature > 49 || humidity > 69 || methane > 49 || cmonoxide > 49 || heartrate < 71 || danger=='fall') {
          state = 'danger';
        }
        else{
          state = 0;
        }

        let list = {
          "temperature" : temperature,
          "humidity" : humidity,
          "methane" : methane,
          "cmonoxide" : cmonoxide,
          "heartrate" : heartrate,
          "location" : location,
          "danger" : danger,
          "state" : state
        };
        return list;
      }
    </script>

    <!-- 작업자1 관련 -->
    <script>
      startWorker1 = function() {
        playWorker1 = setInterval(function() {
          getWorker1();
        }, 3000);
      };

      stopWorker1 = function() {
        enterWorker1();
        clearInterval(playWorker1);
      };

      // 작업자1 Go to work : 랜덤값 생성하여 주기적으로 모비우스에 보냄
      function getWorker1(){
        let data = createRandomWorkerDate();
        console.log(data);
        $('#input1_loc').val("A1");
        $('#input1_heart').val(data["heartrate"]);
        $('#input1_danger').val(data["danger"]);
        $('#input1_temp').val(data["temperature"]);
        $('#input1_humi').val(data["humidity"]);
        $('#input1_meth').val(data["methane"]);
        $('#input1_cmon').val(data["cmonoxide"]);

        if(data["state"] == 0){
          $('#input1_state').text("SAFE");
          $('#input1_state').css("color","black");
        }
        else{
          $('#input1_state').text("DANGER");
          $('#input1_state').css("color","red");
        }

        //createCin('worker1', 'location', data["location"]);
        createCin('worker1', 'heartrate', data["heartrate"]);
        createCin('worker1', 'temperature', data["temperature"]);
        createCin('worker1', 'humidity', data["humidity"]);
        createCin('worker1', 'methane', data["methane"]);
        createCin('worker1', 'cmonoxide', data["cmonoxide"]);
        createCin('worker1', 'danger', data["danger"]);
      }


      // 작업자1 Off work
      function enterWorker1(){
        $('#input1_loc').val("En");
        $('#input1_heart').val('');
        $('#input1_danger').val('');
        $('#input1_temp').val('');
        $('#input1_humi').val('');
        $('#input1_meth').val('');
        $('#input1_cmon').val('');

        $('#input1_state').text("-");
        $('#input1_state').css("color","black");
      }
    </script>

    <!-- 작업자2 관련 -->
    <script>
      startWorker2 = function() {
        playWorker2 = setInterval(function() {
          getWorker2();
        }, 3000);
      };

      stopWorker2 = function() {
        enterWorker2();
        clearInterval(playWorker2);
      };

      // 작업자2 Go to work : 랜덤값 생성하여 주기적으로 모비우스에 보냄
      function getWorker2(){
        let data = createRandomWorkerDate();
        console.log(data);
        $('#input2_loc').val("A2");
        $('#input2_heart').val(data["heartrate"]);
        $('#input2_danger').val(data["danger"]);
        $('#input2_temp').val(data["temperature"]);
        $('#input2_humi').val(data["humidity"]);
        $('#input2_meth').val(data["methane"]);
        $('#input2_cmon').val(data["cmonoxide"]);

        if(data["state"] == 0){
          $('#input2_state').text("SAFE");
          $('#input2_state').css("color","black");
        }
        else{
          $('#input2_state').text("DANGER");
          $('#input2_state').css("color","red");
        }

        // createCin('worker2', 'location', data["location"]);
        createCin('worker2', 'heartrate', data["heartrate"]);
        createCin('worker2', 'temperature', data["temperature"]);
        createCin('worker2', 'humidity', data["humidity"]);
        createCin('worker2', 'methane', data["methane"]);
        createCin('worker2', 'cmonoxide', data["cmonoxide"]);
        createCin('worker2', 'danger', data["danger"]);
      }


      // 작업자2 Off work
      function enterWorker2(){
        $('#input2_loc').val("En");
        $('#input2_heart').val('');
        $('#input2_danger').val('');
        $('#input2_temp').val('');
        $('#input2_humi').val('');
        $('#input2_meth').val('');
        $('#input2_cmon').val('');

        $('#input2_state').text("-");
        $('#input2_state').css("color","black");
      }
    </script>


<!-- 작업자3 관련 -->
<script>
  startWorker3 = function() {
    playWorker3 = setInterval(function() {
      getWorker3();
    }, 3000);
  };

  stopWorker3 = function() {
    enterWorker3();
    clearInterval(playWorker3);
  };

  // 작업자3 Go to work : 랜덤값 생성하여 주기적으로 모비우스에 보냄
  function getWorker3(){
    let data = createRandomWorkerDate();
    console.log(data);
    $('#input3_loc').val("A4");
    $('#input3_heart').val(data["heartrate"]);
    $('#input3_danger').val(data["danger"]);
    $('#input3_temp').val(data["temperature"]);
    $('#input3_humi').val(data["humidity"]);
    $('#input3_meth').val(data["methane"]);
    $('#input3_cmon').val(data["cmonoxide"]);

    if(data["state"] == 0){
      $('#input3_state').text("SAFE");
      $('#input3_state').css("color","black");
    }
    else{
      $('#input3_state').text("DANGER");
      $('#input3_state').css("color","red");
    }

    // createCin('worker3', 'location', data["location"]);
    createCin('worker3', 'heartrate', data["heartrate"]);
    createCin('worker3', 'temperature', data["temperature"]);
    createCin('worker3', 'humidity', data["humidity"]);
    createCin('worker3', 'methane', data["methane"]);
    createCin('worker3', 'cmonoxide', data["cmonoxide"]);
    createCin('worker3', 'danger', data["danger"]);
  }


  // 작업자3 Off work
  function enterWorker3(){
    $('#input3_loc').val("En");
    $('#input3_heart').val('');
    $('#input3_danger').val('');
    $('#input3_temp').val('');
    $('#input3_humi').val('');
    $('#input3_meth').val('');
    $('#input3_cmon').val('');

    $('#input3_state').text("-");
    $('#input3_state').css("color","black");
  }
</script>

<!-- 작업자4 관련 -->
<script>
  startWorker4 = function() {
    playWorker4 = setInterval(function() {
      getWorker4();
    }, 3000);
  };

  stopWorker4 = function() {
    enterWorker4();
    clearInterval(playWorker4);
  };

  // 작업자4 Go to work : 랜덤값 생성하여 주기적으로 모비우스에 보냄
  function getWorker4(){
    let data = createRandomWorkerDate();
    console.log(data);
    $('#input4_loc').val("A2");
    $('#input4_heart').val(data["heartrate"]);
    $('#input4_danger').val(data["danger"]);
    $('#input4_temp').val(data["temperature"]);
    $('#input4_humi').val(data["humidity"]);
    $('#input4_meth').val(data["methane"]);
    $('#input4_cmon').val(data["cmonoxide"]);

    if(data["state"] == 0){
      $('#input4_state').text("SAFE");
      $('#input4_state').css("color","black");
    }
    else{
      $('#input4_state').text("DANGER");
      $('#input4_state').css("color","red");
    }

    // createCin('worker4', 'location', data["location"]);
    createCin('worker4', 'heartrate', data["heartrate"]);
    createCin('worker4', 'temperature', data["temperature"]);
    createCin('worker4', 'humidity', data["humidity"]);
    createCin('worker4', 'methane', data["methane"]);
    createCin('worker4', 'cmonoxide', data["cmonoxide"]);
    createCin('worker4', 'danger', data["danger"]);
  }


  // 작업자4 Off work
  function enterWorker4(){
    $('#input4_loc').val("En");
    $('#input4_heart').val('');
    $('#input4_danger').val('');
    $('#input4_temp').val('');
    $('#input4_humi').val('');
    $('#input4_meth').val('');
    $('#input4_cmon').val('');

    $('#input4_state').text("-");
    $('#input4_state').css("color","black");
  }
</script>
  </head>
  <body>
    <br>

    <div class="container">

        <div class="row">

            <div class="col-3">
              <div class="card">
                  <div class="card-header">
                    worker1
                    <button id="btn1" class="btn btn-secondary btn-sm"  style="float: right">Go to work</button>
                  </div>
                  <img src="img/worker2.png"alt="" />
                  <div class="card-body">
                  <table style="margin: 10px 0px; width: 100%; text-align: center;">
                    <tbody>
                      <tr>
                        <td><label for="input1_loc" class="form-label">Location</label></td>
                        <td><input type="text" class="form-control form-control-sm" id="input1_loc" placeholder="" style="width: 100px;" value="En"></td>
                      </tr>
                      <tr>
                        <td><label for="input1_heart" class="form-label">Heart Rate</label></td>
                        <td><input type="text" class="form-control form-control-sm" id="input1_heart" placeholder="" style="width: 100px;"></td>
                      </tr>
                      <tr>
                        <td><label for="input1_danger" class="form-label">DANGER</label></td>
                        <td><input type="text" class="form-control form-control-sm" id="input1_danger" placeholder="" style="width: 100px;"></td>
                      </tr>
                      <tr>
                        <td><label for="input1_temp" class="form-label">Temperature</label></td>
                        <td><input type="text" class="form-control form-control-sm" id="input1_temp" placeholder="" style="width: 100px;"></td>
                      </tr>
                      <tr>
                        <td><label for="input1_humi" class="form-label">Humidity</label></td>
                        <td><input type="text" class="form-control form-control-sm" id="input1_humi" placeholder="" style="width: 100px;"></td>
                      </tr>
                      <tr>
                        <td><label for="input1_meth" class="form-label">Methane</label></td>
                        <td><input type="text" class="form-control form-control-sm" id="input1_meth" placeholder="" style="width: 100px;"></td>
                      </tr>
                      <tr>
                        <td><label for="input1_cmon" class="form-label">Cmonoxide</label></td>
                        <td><input type="text" class="form-control form-control-sm" id="input1_cmon" placeholder="" style="width: 100px;"></td>
                      </tr>
                    </tbody>
                  </table>

                  <div style="text-align: center;"><p class="state" id="input1_state">-</p></div>
                  </div>
              </div>
              </div>

              <div class="col-3">
                <div class="card">
                    <div class="card-header">
                      worker2
                      <button id="btn2" class="btn btn-secondary btn-sm"  style="float: right">Go to work</button>
                    </div>
                    <img src="img/worker2.png"alt="" />
                    <div class="card-body">
                    <table style="margin: 10px 0px; width: 100%; text-align: center;">
                      <tbody>
                        <tr>
                          <td><label for="input2_loc" class="form-label">Location</label></td>
                          <td><input type="text" class="form-control form-control-sm" id="input2_loc" placeholder="" style="width: 100px;" value="En"></td>
                        </tr>
                        <tr>
                          <td><label for="input2_heart" class="form-label">Heart Rate</label></td>
                          <td><input type="text" class="form-control form-control-sm" id="input2_heart" placeholder="" style="width: 100px;"></td>
                        </tr>
                        <tr>
                          <td><label for="input2_danger" class="form-label">DANGER</label></td>
                          <td><input type="text" class="form-control form-control-sm" id="input2_danger" placeholder="" style="width: 100px;"></td>
                        </tr>
                        <tr>
                          <td><label for="input2_temp" class="form-label">Temperature</label></td>
                          <td><input type="text" class="form-control form-control-sm" id="input2_temp" placeholder="" style="width: 100px;"></td>
                        </tr>
                        <tr>
                          <td><label for="input2_humi" class="form-label">Humidity</label></td>
                          <td><input type="text" class="form-control form-control-sm" id="input2_humi" placeholder="" style="width: 100px;"></td>
                        </tr>
                        <tr>
                          <td><label for="input2_meth" class="form-label">Methane</label></td>
                          <td><input type="text" class="form-control form-control-sm" id="input2_meth" placeholder="" style="width: 100px;"></td>
                        </tr>
                        <tr>
                          <td><label for="input2_cmon" class="form-label">Cmonoxide</label></td>
                          <td><input type="text" class="form-control form-control-sm" id="input2_cmon" placeholder="" style="width: 100px;"></td>
                        </tr>
                      </tbody>
                    </table>

                    <div style="text-align: center;"><p class="state" id="input2_state">-</p></div>
                    </div>
                </div>
                </div>

                <div class="col-3">
                  <div class="card">
                      <div class="card-header">
                        worker3
                        <button id="btn3" class="btn btn-secondary btn-sm"  style="float: right">Go to work</button>
                      </div>
                      <img src="img/worker2.png"alt="" />
                      <div class="card-body">
                      <table style="margin: 10px 0px; width: 100%; text-align: center;">
                        <tbody>
                          <tr>
                            <td><label for="input3_loc" class="form-label">Location</label></td>
                            <td><input type="text" class="form-control form-control-sm" id="input3_loc" placeholder="" style="width: 100px;" value="En"></td>
                          </tr>
                          <tr>
                            <td><label for="input3_heart" class="form-label">Heart Rate</label></td>
                            <td><input type="text" class="form-control form-control-sm" id="input3_heart" placeholder="" style="width: 100px;"></td>
                          </tr>
                          <tr>
                            <td><label for="input3_danger" class="form-label">DANGER</label></td>
                            <td><input type="text" class="form-control form-control-sm" id="input3_danger" placeholder="" style="width: 100px;"></td>
                          </tr>
                          <tr>
                            <td><label for="input3_temp" class="form-label">Temperature</label></td>
                            <td><input type="text" class="form-control form-control-sm" id="input3_temp" placeholder="" style="width: 100px;"></td>
                          </tr>
                          <tr>
                            <td><label for="input3_humi" class="form-label">Humidity</label></td>
                            <td><input type="text" class="form-control form-control-sm" id="input3_humi" placeholder="" style="width: 100px;"></td>
                          </tr>
                          <tr>
                            <td><label for="input3_meth" class="form-label">Methane</label></td>
                            <td><input type="text" class="form-control form-control-sm" id="input3_meth" placeholder="" style="width: 100px;"></td>
                          </tr>
                          <tr>
                            <td><label for="input3_cmon" class="form-label">Cmonoxide</label></td>
                            <td><input type="text" class="form-control form-control-sm" id="input3_cmon" placeholder="" style="width: 100px;"></td>
                          </tr>
                        </tbody>
                      </table>

                      <div style="text-align: center;"><p class="state" id="input3_state">-</p></div>
                      </div>
                  </div>
                  </div>

                  <div class="col-3">
                    <div class="card">
                        <div class="card-header">
                          worker4
                          <button id="btn4" class="btn btn-secondary btn-sm"  style="float: right">Go to work</button>
                        </div>
                        <img src="img/worker2.png"alt="" />
                        <div class="card-body">
                        <table style="margin: 10px 0px; width: 100%; text-align: center;">
                          <tbody>
                            <tr>
                              <td><label for="input4_loc" class="form-label">Location</label></td>
                              <td><input type="text" class="form-control form-control-sm" id="input4_loc" placeholder="" style="width: 100px;" value="En"></td>
                            </tr>
                            <tr>
                              <td><label for="input4_heart" class="form-label">Heart Rate</label></td>
                              <td><input type="text" class="form-control form-control-sm" id="input4_heart" placeholder="" style="width: 100px;"></td>
                            </tr>
                            <tr>
                              <td><label for="input4_danger" class="form-label">DANGER</label></td>
                              <td><input type="text" class="form-control form-control-sm" id="input4_danger" placeholder="" style="width: 100px;"></td>
                            </tr>
                            <tr>
                              <td><label for="input4_temp" class="form-label">Temperature</label></td>
                              <td><input type="text" class="form-control form-control-sm" id="input4_temp" placeholder="" style="width: 100px;"></td>
                            </tr>
                            <tr>
                              <td><label for="input4_humi" class="form-label">Humidity</label></td>
                              <td><input type="text" class="form-control form-control-sm" id="input4_humi" placeholder="" style="width: 100px;"></td>
                            </tr>
                            <tr>
                              <td><label for="input4_meth" class="form-label">Methane</label></td>
                              <td><input type="text" class="form-control form-control-sm" id="input4_meth" placeholder="" style="width: 100px;"></td>
                            </tr>
                            <tr>
                              <td><label for="input4_cmon" class="form-label">Cmonoxide</label></td>
                              <td><input type="text" class="form-control form-control-sm" id="input4_cmon" placeholder="" style="width: 100px;"></td>
                            </tr>
                          </tbody>
                        </table>

                        <div style="text-align: center;"><p class="state" id="input4_state">-</p></div>
                        </div>
                    </div>
                    </div>

        </div>
        </div>


    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>
  </body>
</html>

Arduino Helmet

Arduino
Helmet.ino
#include <Arduino_LSM6DS3.h>

#include "DHT.h"
#define DHTPIN A6     // Digital pin connected to the DHT sensor
#define DHTTYPE DHT11   // DHT 11
DHT dht(DHTPIN, DHTTYPE);

// MQ-4 , MQ-7 + 
int Mq_4 = A2;   // MQ-4     
int Mq_7 = A3;   // MQ-7     

// 
#define mq4_d 30
#define mq7_d 30
#define t_d 45
#define h_d 60

//mq4 ppm
float RS_gas = 0;
float ratio = 0;
float sensorValue = 0;
float sensor_volt = 0;
float R0 = 7200.0;

//mq7 ppm
float RS_gas1 = 0;
float ratio1 = 0;
float sensorValue1 = 0;
float sensor_volt1 = 0;
float R01 = 7200.0;

unsigned long fallStartTime; //   
unsigned long nowTime; //   
boolean fallFirstState = true; //    
boolean gyro = false; 

int speaker = 2;  // 

//beacon
#include <ArduinoBLE.h>

void setup() {
  Serial.begin(115200);
  while (!Serial);
  
  pinMode(Mq_4 ,INPUT);   //   A2  
  pinMode(Mq_7 ,INPUT);   //   A3  
  pinMode(7, INPUT);     // 1
  pinMode(8, INPUT);     // 2
  pinMode(2, OUTPUT);     //

  dht.begin(); 
  if (!IMU.begin()) { //LSM6DS3 
    Serial.println("LSM6DS3 !");
    while (1);
  }

  if (!BLE.begin()) {
    Serial.println("starting BLE failed!");
    while (1);
  }
  Serial.println("BLE Central scan");

  BLE.scan();
}

String bleName = "";
char ble[100] = "None";

void Search_BLE() {
    BLEDevice peripheral = BLE.available();    

    if (peripheral) {
      //        
      if (peripheral.localName() == "En" || peripheral.localName() == "A1" || peripheral.localName() == "A2" || peripheral.localName() == "A3" || peripheral.localName() == "A4") {
        Serial.println("Discovered a peripheral");
        Serial.println("-----------------------");

        // print address
        Serial.print("localName: ");
        bleName = peripheral.localName();
        Serial.println(bleName);

        if(bleName == "En")
          bleName = "En";
        
        int bleLen = bleName.length();
        bleName.toCharArray(ble, bleLen+1);
        Serial.print("ble");
        Serial.println(ble);

    }
  }
}

float acc_x, acc_y, acc_z;
float acc_pit, acc_roll;
unsigned long rolltime = 0;

void danger_speaker(){
  int i=0;
  for( int i=0; i<=4; i++)
  {
    tone(2,900);
    delay(500);
    tone(2,800);
    delay(500);
  }
  noTone(2);
}

void loop() {
  //  
  nowTime = millis();

  //beacon  
  Search_BLE();

  float t = dht.readTemperature();
  int h = dht.readHumidity();
  int danger = 0;

   //mq4 ppm 
   sensorValue = analogRead(A2);
   sensor_volt = sensorValue/1024*5.0;
   RS_gas = (5.0-sensor_volt)/sensor_volt;
   ratio = RS_gas/R0; //Replace R0 with the value found using the sketch above
   float x = 1538.46 * ratio;
   int Mq4 = (float)pow(x,-1.709);

   //mq7 ppm  
   sensorValue1 = analogRead(A3);
   sensor_volt1 = sensorValue1/1024*5.0;
   RS_gas1 = (5.0 - sensor_volt1)/sensor_volt1;
   ratio1 = RS_gas1 / R01; //Replace R0 with the value found using the sketch above
   float x1 = 1538.46 * ratio1;
   int Mq7 = (float)pow(x1,-1.709);

  //
  if (IMU.accelerationAvailable()) {
    IMU.readAcceleration(acc_x, acc_y, acc_z);
    
  // 
    acc_pit = RAD_TO_DEG * atan(acc_x / sqrt(acc_y*acc_y + acc_z*acc_z)); // 
    acc_roll = RAD_TO_DEG * atan(acc_y / sqrt(acc_x*acc_x + acc_z*acc_z));//180/PI = 57.27755
  }

  String matter_str = "";  
  char matter[500]; 
  
  Search_BLE();

  //
  if(digitalRead(7)==0 && digitalRead(8)==0) {
    Serial.println("");  
   
   //  
    Serial.print("  :");
    Serial.println(acc_roll);

    //    
    if(acc_roll < 10) {
      if(fallFirstState) {
        fallStartTime = millis();
        fallFirstState = false;
      }

      if((nowTime - fallStartTime)/1000 > 5) {
        fallFirstState = true;
        
        gyro = true;
      }
    }
    else {
      fallFirstState = true;
    }

    if( Mq4 > mq4_d || Mq7 > mq7_d || t > t_d || h > h_d || gyro) {
     Serial.println("");
     //danger = -1;

     if( Mq4 > mq4_d ) {
        matter_str += "MQ4*";
     }
     if(Mq7 > mq7_d) {
      matter_str += "MQ7*";
     }
     if(t > t_d) {
      matter_str += "temp*";
     }
     if(h > h_d) {
      matter_str += "humi*";
     }
     if(gyro) {
      matter_str += "fall*";
      gyro = false;
     }
     
     int len = matter_str.length();
     matter_str.toCharArray(matter, len+1);

     Serial.print("temperature: ");
     Serial.println(t);
     Serial.print("humidity: ");
     Serial.println(h);
     Serial.print("Mq4: ");
     Serial.println(Mq4);
     Serial.print("Mq7: ");
     Serial.println(Mq7);
     Serial.print("workSection: ");
     Serial.println(ble);
     Serial.print("danger: ");
     Serial.println(matter);
     Serial.println();
     danger_speaker();
    }
    else {
      danger = 0;
      Serial.print("temperature: ");
     Serial.println(t);
     Serial.print("humidity: ");
     Serial.println(h);
     Serial.print("Mq4: ");
     Serial.println(Mq4);
     Serial.print("Mq7: ");
     Serial.println(Mq7);
     Serial.print("workSection: ");
     Serial.println(ble);
     Serial.print("danger: ");    
     Serial.println(danger);     
     Serial.println();  
     delay(1000);
     } 
  }

  //
  else {
    Serial.println("");
    String WearOrNot = "NoWear";
    char WON[50];
    int Wlen = WearOrNot.length();
    WearOrNot.toCharArray(WON, Wlen+1);
    Serial.print("WearOrNot: ");
    Serial.println(WON);
    Serial.println();
    }  
}

Credits

신정섭
1 project • 1 follower
LEEYELIM
1 project • 1 follower
Andreas Kraft
37 projects • 12 followers
IoT & connected home architect and developer. Ask me about oneM2M.
SeungMyeong Jeong
37 projects • 12 followers
Bob Flynn
32 projects • 16 followers
Miguel Angel Reina Ortega
38 projects • 7 followers
Laurent Velez
18 projects • 6 followers
Samir Medjiah
21 projects • 14 followers
Xavier Piednoir
26 projects • 6 followers
Wonbae Son
32 projects • 5 followers
안일엽
17 projects • 1 follower
khrho
0 projects • 0 followers

Comments