Amid the global COVID-19 pandemic, the proportion of non-face-to-face education has increased significantly in Korea through social distancing. Online classes have a definite effect on preventing infectious diseases, but inequality is arising because children in childhood (6 to 13 years old) cannot receive as high-quality education as offline. In addition, due to the inability to engage in any hands-on activities, there is a limit to the physical and emotional development of growing children. However, the need for creative thinking expression or cooperative learning to solve children's development is increasing. We thought about how to solve these problems and make children experience safe hands-on activities. Therefore, We came to think that there was a need for educational teaching tools that all children could use equally regardless of environmental factors. In addition, We felt that there was a need for an educational system that helps personal experiences develop naturally into cooperation and communication skills. In addition, non-face-to-face classes are difficult for teachers to check the individual progress of students. So, we thought there was also a need for a platform that could easily check students' progress in real-time under the leadership of teachers. Finally, we came to think of EduFarm, a teaching tool for educational plant experience activities.
The teacher (admin) needed a web page to check the students' learning and give them feedback accordingly.
Therefore, we created a web that can be linked to hardware (plant) and apps (students) using Django.
Web compositionMain Page
- Students observation log (diary)
- Point List
- Market
Sub
- Check the student log & write feedback.
- Check the product list & add and delete the product.
- Product sales & auction system.
- Check the list of point items and give points to students.
- Check the student's points and usage list.
- Check the students' requests.
Main Page
The main page is configured to connect to the student observation log, point List, and marketplace.
Other additional functions that help the administrator are organized in the right list on the right.
The configuration and function of the web are as follows.
- Check the student's observation log and provide feedback and points accordingly.
- Properly managing the products.
- Market open and close.
- Check and send product inventory and product buyers through the market.
- Check and send the student's points.
1. Check the student's observation log and provide feedback and points accordingly
When clicking on the student name in the first image, update the student's observation log every day and show it to the teacher. This diary is taken from Mobius [record] cnt, and the [record] has the following data.
The web takes the id, image, title, text, date, and water of the [record], stores them, and makes and shows each student's observation journal as follows to the teacher.
After creating an observation diary with the data saved through the [record], it was configured to receive feedback from the teacher. The feedback is posted to [feedback] cnt of mobius.
After receiving the feedback, the check changes to 'o' to help the teacher with his work.
2. Properly managing the products
Properly manage the products to be launched in the market according to students' market activities.
3. Market open and close
This page allows you to open a marketplace for items owned by teachers.
[Market Open]
When the market open, the item list is posted as mobius [market_teacher] cnt.
Based on this, it is possible to check the item list in the app.
[Market Close]
After closing the market, read [auction]cnt containing student's purchase requests.
In addition, Products are provided sequentially according to the quantity to students who paid the largest points for each product.
[code]
4. Check and send product inventory and product buyers through the market.
Market closing results are sent to [market_access] and [user_control], respectively.
- market_access: Purchased products, owners, and used points.
- user_control : The products each student owns.
Also, the web shows the results of the successful bid for the teacher.
5. Check and send the student's points.
Show student's currently and usage points at a glance after or at any time the market closes.
Additional page
1. point list all
This page shows the criteria for providing points.
Based on this standard, students are given points and market activities are possible.
2. Requirements
This page shows the student's needs.
Students can grow plants and learn new things through remote online classes. They can engage in plant-related economic activities and receive economic education through the app.
We have created an app so that students can check their plant conditions easily and quickly and record their feelings while growing plants.
Apps we created can be connected to hardware and web pages through Mobius server.
App descriptionThe app has 6 main functions
- Showing own points
- Showing real-time data of user's plant
- Uploading plant diary (journals)
- Showing own diary and Showing teacher's feedback.
- Buying the hidden items through auction.
- Actuating IoT plant system through own item
- Showing user's own items
1. User
This tab is the User tab. Users can see 'My Profile' that showing own name and own point.
It refreshes shown data when the user tab is getting started or clicking the button.
In [havepoint] cnt, cumulative point inquiry is possible for each student.
2. PlantUsers can see their own plant's condition and can remote actions on/off.
2-1 Showing own plant's condition
The values are in [soil], [light], [temp] cnt of Mobius server.
2-2 Remote actions
What kind of actions are in there?
- Automatic water supply
- Automatic Ventilation motor operation
- Remote mood light
- Automatic Rotating pot via the light
(For more detail, go to theHardware part.)
The actuating button can be added by buying Items in a market.(in the Item tab)
The presence or absence of the button is retrieved in real-time from the list of approved items on the Mobius server. [user_access] - [Username] cnt.
This structure allows the button to disappear from the app when the expiration date has passed or the teacher cancels the approval of the item.
Automatic water supply & Automatic Ventilation fan
the button of the water supply & ventilation fan is a toggle type.the state of the toggle buttons depends on-the value of [act_water]cnt and [act_motor]cnt.
LED SettingBut If you click the LED button, a new page opens with three buttons to control the LED's R, G, B.
The buttons of colors depend on the value of [act_led] cnt.So, there may be a slight delay in seeing the color change in the app.
And this is the example code part of the led setting.
/*If you click one of the three buttons, A thread that decorates the button color according to the value retrieved from the server is executed and also postAct() is excuted.
And getState() is that Gets the con value of cin from the path according to the given string.*/
public class LEDsetActivity extends AppCompatActivity {
ImageView red;
ImageView green;
ImageView blue;
ImageView btn_back;
LED led = new LED(0,0,0);
String result;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ledset);
red = findViewById(R.id.red);
green = findViewById(R.id.green);
blue = findViewById(R.id.blue);
btn_back = findViewById(R.id.back);
check();//Initialize the led object with the value in the server.
View.OnClickListener back = new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
};
btn_back.setOnClickListener(back);
View.OnClickListener tabr = new View.OnClickListener() {
@Override
public void onClick(View view) {
//0이면 1로, 1이면 0으로 toggle
led.setR((led.getR()!= 0)? 0:1);
/* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
red.setImageDrawable(getDrawable(R.drawable.circle_off));
}*/
System.out.println("led="+led.getRGB());
postAct();
check();
}
};
red.setOnClickListener(tabr);
View.OnClickListener tabg = new View.OnClickListener() {
@Override
public void onClick(View view) {
led.setG((led.getG()!= 0)? 0:1);
System.out.println("led="+led.getRGB());
postAct();
check();
check();
}
};
green.setOnClickListener(tabg);
View.OnClickListener tabb = new View.OnClickListener() {
@Override
public void onClick(View view) {
led.setB((led.getB()!= 0)? 0:1);
System.out.println("led="+led.getRGB());
postAct();
check();
check();
}
};
blue.setOnClickListener(tabb);
}
public void postAct(){//Transmits the actuating state set by the app to the server.
new Thread(){
@Override
public void run() {
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/vnd.onem2m-res+json; ty=4");
RequestBody body = RequestBody.create(mediaType,
"{\n" +
" \"m2m:cin\": {\n" +
" \"con\": \""+led.getRGB()+"\"\n" +
" }\n" +
"}");
Request request = new Request.Builder()
.url("http://203.253.128.161:7579/Mobius/AduFarm/act_led")
.method("POST", body)
.addHeader("Accept", "application/json")
.addHeader("X-M2M-RI", "12345")
.addHeader("X-M2M-Origin", "{{aei}}")
.addHeader("Content-Type", "application/vnd.onem2m-res+json; ty=4")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, final Response response) throws IOException {
if (!response.isSuccessful()) {
Log.i("tag", "LED set failed");
} else {
Log.i("tag", "LED set success");
final String responseData = response.body().string();
// Error when changing UI in sub-thread.
// set the main thread UI.
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
if (!response.isSuccessful()) {
// 응답 실패
Toast toast = Toast.makeText(LEDsetActivity.this,"LED failed", Toast.LENGTH_LONG);
toast.show();
}
System.out.println("응답" + responseData);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
});
try {
Response response = client.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
public void check(){//Check the status of the flowerpot and the operation of the item.
Thread thread = new Thread() {
public String getState(String path){//Get the con value of cin from the path according to the given string.
OkHttpClient client = new OkHttpClient().newBuilder().build();
Request request = new Request.Builder()
.url("http://203.253.128.161:7579/Mobius/AduFarm/" + path + "?fu=2&la=1&ty=4&rcn=4")
.method("GET", null)
.addHeader("Accept", "application/json")
.addHeader("X-M2M-RI", "12345")
.addHeader("X-M2M-Origin", "SOrigin")
.build();
Response response = null;
try {
response = client.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
//String result = response.body().string();
String con = null;
JSONObject obj = null;
try {
obj = new JSONObject(response.body().string());
} catch (JSONException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(obj);
JSONObject o1 = null;
try {
o1 = obj.getJSONObject("m2m:rsp");
} catch (JSONException e) {
e.printStackTrace();
}
System.out.println(o1);
JSONArray a1 = null;
try {
a1 = o1.getJSONArray("m2m:cin");
} catch (JSONException e) {
e.printStackTrace();
}
System.out.println(a1);
try {
o1 = a1.getJSONObject(0);
} catch (JSONException e) {
e.printStackTrace();
}
System.out.println(o1);
try {
con = o1.getString("con");
} catch (JSONException e) {
e.printStackTrace();
}
System.out.println(con);
return con;
}
@Override public void run () {
result = getState("act_led");
if(result!=null) {
int r, g, b;
r = Integer.parseInt(result.substring(0, 1));
g = Integer.parseInt(result.substring(1, 2));
b = Integer.parseInt(result.substring(2));
System.out.println("현재 삼색은" + r+ g+b);
runOnUiThread(new Runnable() {
// Error when changing UI in sub-thread.
// set the main thread UI.
@Override
public void run() {
red = findViewById(R.id.red);
green = findViewById(R.id.green);
blue = findViewById(R.id.blue);
if (r == 1) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
red.setImageDrawable(getDrawable(R.drawable.circle_red));
}
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
red.setImageDrawable(getDrawable(R.drawable.circle_off));
}
}
if (g == 1) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
green.setImageDrawable(getDrawable(R.drawable.circle_green));
}
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
green.setImageDrawable(getDrawable(R.drawable.circle_off));
}
}
if (b == 1) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
blue.setImageDrawable(getDrawable(R.drawable.circle_blue));
}
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
blue.setImageDrawable(getDrawable(R.drawable.circle_off));
}
}
}
});
}
}//run
};
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3. Diary
This is the Diary tab. It shows previously created journals in a feed format and can add by clicking the plus button at the bottom right.
This is the journal writing page that appears when you click the plus button.There is a Text view that automatically loads the current date.
And there are 4 states of the user's plant.(In this part, information is loaded only once, not in real-time, for stable application execution.)
If you click the Camera button, you can add your picture of your own plant.
And the users can write the title and contents of the journal.
Lastly, If click the SEND button, all the displayed information is uploaded to the Mobius server in JSON format.
The image file entered by the user is encoded as a byte array in bitmap format and the byte array is encoded in Base64.
(You can test like this in https://codebeautify.org/base64-to-image-converter)
So, you can see the Image on the Plant page, on the Diary page, and on the web.
(In 0:36 ~ is waiting for the teacher's feedback.)
4-1. Item
Next is the item tab. On this page, you can check the items you have purchased so far. You can also check your winning bid price when the auction began at the time. You must click the cart button in the top bar to go to the market page.
Items are used when we control the IoT plant system.
4-2. Plant Market
This page is a market page. You can purchase items posted by your teacher through the Mobius server on the web.
There is a fixed quantity that can be purchased by item.
Items sold in the market can be purchased through points accumulated when students grow plants with great care.
If you click the 'Buy' button, you can apply for the purchase through the 'dialog'.
Users can get items in the order of the highest price.
However, users can only purchase within their own points.
When you acquire the item, it is automatically uploaded to the item page.
If the information is sent well, it is created under the auction cnt on the Mobius server.
We created a function to automatically return the pot according to the values of the four CDS sensors attached to the pot. Plants can be prevented from growing in one direction by shade by this function.
1. Circuit
First, we connected the motor using Raspberry Pi and L298N motor driver.At this time, we used a gearbox to increase the gear ratio of the motor.
Next, Put the motor and wheel in the hole at the bottom of the 3d printer and place the plant on it.
Third, connect the CDS sensor using Raspberry Pi and MCP3008.We should make analog data to digital data ourselves by using MCP3008 because Raspberry Pi doesn't have analog input itself.
Data detected by each sensor is transmitted to the Mobius server and can be checked in the application. Each data is transmitted in the form of an array.
2. TheResult(CDSValue)ofTas communication with Mobius & Raspberry pi
- Transferring value from Raspberry Pi to Mobius server via Tas.
Each four sensor values are transmitted in the type of array. This array is sent to the 'light' container of the Mobius server.
From n-cube Thyme, we can post data at Moius.
By this resource, We can check the direction where the light comes and we can make Plants can receive light equally by rotating the Pot.
3.TheVideo of rotates pot via CDS light.
This is the video that shows a rotating pot. In the video, you can see that the pot rotates well depending on the degree to which light is obscured by the sensor. The console window at the bottom left is the data reception value( and Mobius Ncube execution(right).
3) Remote mood light.Our work works indoors. Therefore, plants cannot be given enough sunlight. So we created a function to illuminate plants with red and blue light, which is good for plant growth. In addition, RGB LEDs are used to make it possible to function as indoor mood lights.
1. Circuit
first, we connected 3-color LED with Raspberry Pi.Red, Green, Blue lights are connected with GPIO 13, 19, 16 each.A combination of red, green, and blue LEDs can produce a total of 7 combinations of light. (Red, green, blue, purple, cyan, yellow, white)
2.TheResult(CDSValue)ofTas communication with Mobius & Raspberry pi
It determines the color of light output through a 3-bit binary number transmitted from the led app. For example, When the value "110" rises to the container of the Mobius server to which LED is subscribed, LED emits purple light.
3.TheVideo of remoteMoodLight
We can see Led get changed by APP. This app is also connected with the Mobius server, so when the user touches the icons, the value of state will send to the server. And LED gets changed because it subscribed to the container which the app posts the data.
4) Plant State check & TreatWe check the condition of the pot through the temperature and humidity sensor and the soil humidity sensor. And the value is stored on the server, and the results can be checked through the app. And users can automatically water or run the function of creating wind through the app.
1. Circuit
For the Water pump and Fan, We connected with Motor Driver to control each power. We used the soil sensor's output pin at the mcp3008 channel 5 because we should find out the level of soil humidity. And as the humid sensor(dht11), We connected with Raspberry pi GPIO 04.
2. TheResultofTas communication with Mobius & Raspberry pi
- TheResult(Fan)ofTas communication with Mobius & Raspberry pi
- TheResult(humidity)ofTas communication with Mobius & Raspberry pi
- TheResult(soilhumidity)ofTas communication with Mobius & Raspberry pi
- TheResult(watermotor)ofTas communication with Mobius & Raspberry pi
- soil humidity sensor: When the soil is dry, the sensor value rises above 1000. but when we give water to the soil, the value gets under 600. So we can know whether the user watered it or not.
- temp sensor: We receive two values from the sensor. Each is temperature and humidity. Using this data, you can know the environment outside the pot.
- water pump & fan: we made them subscribe to each cnt. so when they got '1', they get to work. else if they got '0', it stopped.
3.TheResult video
(1) Automatic Water Supply
(2) Automatic Ventilation Motor Operation
(3) Check between Humid & soil Humid sensor and app
Codes of Hw1) ncube-thyme-node-js / conf.js
/**
* Created by Il Yeup, Ahn in KETI on 2017-02-23.
*/
/**
* Copyright (c) 2018, OCEAN
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
var ip = require("ip");
var conf = {};
var cse = {};
var ae = {};
var cnt_arr = [];
var sub_arr = [];
var acp = {};
conf.useprotocol = 'http'; // select one for 'http' or 'mqtt' or 'coap' or 'ws'
// build cse
cse.host = '203.253.128.161';
cse.port = '7579';
cse.name = 'Mobius';
cse.id = '/Mobius2';
cse.mqttport = '1883';
cse.wsport = '7577';
// build ae
ae.name = 'AduFarm';
ae.id = 'SPLIKNC0doF';
ae.parent = '/' + cse.name;
ae.appid = 'measure_co2';
ae.port = '9727';
ae.bodytype = 'json'; // select 'json' or 'xml' or 'cbor'
ae.tasport = '3105';
// build cnt
var count = 0;
//Sensing
cnt_arr[count] = {};
cnt_arr[count].parent = '/' + cse.name + '/' + ae.name;
cnt_arr[count++].name = 'temp';
cnt_arr[count] = {};
cnt_arr[count].parent = '/' + cse.name + '/' + ae.name;
cnt_arr[count++].name = 'light';
cnt_arr[count] = {};
cnt_arr[count].parent = '/' + cse.name + '/' + ae.name;
cnt_arr[count++].name = 'soil';
//Actuating
//[3]
cnt_arr[count] = {};
cnt_arr[count].parent = '/' + cse.name + '/' + ae.name;
cnt_arr[count++].name = 'act_led';
//4
cnt_arr[count] = {};
cnt_arr[count].parent = '/' + cse.name + '/' + ae.name;
cnt_arr[count++].name = 'act_water';
//5
cnt_arr[count] = {};
cnt_arr[count].parent = '/' + cse.name + '/' + ae.name;
cnt_arr[count++].name = 'act_motor';
//6
cnt_arr[count] = {};
cnt_arr[count].parent = '/' + cse.name + '/' + ae.name;
cnt_arr[count++].name = 'act_wheel';
//soil_result / 7
cnt_arr[count] = {};
cnt_arr[count].parent = '/' + cse.name + '/' + ae.name;
cnt_arr[count++].name = 'Soil_result';
// build sub
count = 0;
//sub_arr[count] = {};
//sub_arr[count].parent = '/' + cse.name + '/' + ae.name + '/' + cnt_arr[1].name;
//sub_arr[count].name = 'sub-ctrl';
//sub_arr[count++].nu = 'mqtt://' + cse.host + '/' + ae.id;
// --------
//led
sub_arr[count] = {};
sub_arr[count].parent = '/' + cse.name + '/' + ae.name + '/' + cnt_arr[3].name;
sub_arr[count].name = 'sub';
sub_arr[count++].nu = 'mqtt://' + cse.host + '/' + ae.id + '?ct=' + ae.bodytype; // mqtt
//water
sub_arr[count] = {};
sub_arr[count].parent = '/' + cse.name + '/' + ae.name + '/' + cnt_arr[4].name;
sub_arr[count].name = 'sub';
sub_arr[count++].nu = 'mqtt://' + cse.host + '/' + ae.id + '?ct=' + ae.bodytype;
//motor
sub_arr[count] = {};
sub_arr[count].parent = '/' + cse.name + '/' + ae.name + '/' + cnt_arr[5].name;
sub_arr[count].name = 'sub';
sub_arr[count++].nu = 'mqtt://' + cse.host + '/' + ae.id + '?ct=' + ae.bodytype;
//wheel
sub_arr[count] = {};
sub_arr[count].parent = '/' + cse.name + '/' + ae.name + '/' + cnt_arr[6].name;
sub_arr[count].name = 'sub';
sub_arr[count++].nu = 'mqtt://' + cse.host + '/' + ae.id + '?ct=' + ae.bodytype;
//sub_arr[count++].nu = 'http://' + ip.address() + ':' + ae.port + '/noti?ct=json'; // http
//sub_arr[count++].nu = 'Mobius/'+ae.name; // mqtt
// --------
/*// --------
sub_arr[count] = {};
sub_arr[count].parent = '/' + cse.name + '/' + ae.name + '/' + cnt_arr[1].name;
sub_arr[count].name = 'sub2';
//sub_arr[count++].nu = 'http://' + ip.address() + ':' + ae.port + '/noti?ct=json'; // http
//sub_arr[count++].nu = 'mqtt://' + cse.host + '/' + ae.id + '?rcn=9&ct=' + ae.bodytype; // mqtt
sub_arr[count++].nu = 'mqtt://' + cse.host + '/' + ae.id + '?ct=json'; // mqtt
// -------- */
// build acp: not complete
acp.parent = '/' + cse.name + '/' + ae.name;
acp.name = 'acp-' + ae.name;
acp.id = ae.id;
conf.usesecure = 'disable';
if(conf.usesecure === 'enable') {
cse.mqttport = '8883';
}
conf.cse = cse;
conf.ae = ae;
conf.cnt = cnt_arr;
conf.sub = sub_arr;
conf.acp = acp;
module.exports = conf;
2) ncube-thyme-node-js / thyme.js
/**
* Copyright (c) 2018, OCEAN, KETI
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Created by Il Yeup, Ahn in KETI on 2016-08-19.
*/
var fs = require('fs');
var shortid = require('shortid');
global.resp_mqtt_ri_arr = [];
global.resp_mqtt_path_arr = {};
global.socket_q = {};
global.conf = require('./conf.js');
//fs.writeFileSync('aei.json', JSON.stringify(conf, null, 4), 'utf-8');
//global.sh_state = 'rtvae';
global.sh_state = 'crtae';
global.wdt = require('./wdt');
global.mqtt_client = null;
// AE core
if(conf.useprotocol === 'mqtt') {
require('./mqtt_app');
}
else if(conf.useprotocol === 'coap') {
require('./coap_app');
}
else if(conf.useprotocol === 'ws') {
require('./ws_app');
}
else {
require('./http_app');
}
3) tas_led / app.js
/*
* - use tas_sample created by I.-Y. Ahn, KETI
*/
var net = require('net');
var util = require('util');
var fs = require('fs');
var xml2js = require('xml2js');
var cp = require('child_process');
var wdt = require('./wdt');
var ledm = require('./ledm');
var useparentport = '';
var useparenthostname = '';
var upload_arr = [];
var download_arr = [];
var conf = {};
fs.readFile('conf.xml', 'utf-8', function (err, data) {
if (err) {
console.log("FATAL An error occurred trying to read in the file: " + err);
console.log("error : set to default for configuration")
}
else {
var parser = new xml2js.Parser({explicitArray: false});
parser.parseString(data, function (err, result) {
if (err) {
console.log("Parsing An error occurred trying to read in the file: " + err);
console.log("error : set to default for configuration")
}
else {
var jsonString = JSON.stringify(result);
conf = JSON.parse(jsonString)['m2m:conf'];
useparenthostname = conf.tas.parenthostname;
useparentport = conf.tas.parentport;
if (conf.upload != null) {
if (conf.upload['ctname'] != null) {
upload_arr[0] = conf.upload;
}
else {
upload_arr = conf.upload;
}
}
if (conf.download != null) {
if (conf.download['ctname'] != null) {
download_arr[0] = conf.download;
}
else {
download_arr = conf.download;
}
}
}
});
}
});
var tas_state = 'init';
var upload_client = null;
var t_count = 0;
var tas_download_count = 0;
function on_receive(data) {
if (tas_state == 'connect' || tas_state == 'reconnect' || tas_state == 'upload') {
var data_arr = data.toString().split('<EOF>');
if (data_arr.length >= 2) {
for (var i = 0; i < data_arr.length - 1; i++) {
var line = data_arr[i];
var sink_str = util.format('%s', line.toString());
var sink_obj = JSON.parse(sink_str);
if (sink_obj.ctname == null || sink_obj.con == null) {
console.log('Received: data format mismatch');
}
else {
if (sink_obj.con == 'hello') {
console.log('Received: ' + line);
if (++tas_download_count >= download_arr.length) {
tas_state = 'upload';
}
}
else {
for (var j = 0; j < upload_arr.length; j++) {
if (upload_arr[j].ctname == sink_obj.ctname) {
console.log('ACK : ' + line + ' <----');
break;
}
}
for (j = 0; j < download_arr.length; j++) {
if (download_arr[j].ctname == sink_obj.ctname) {
g_down_buf = JSON.stringify({id: download_arr[i].id, con: sink_obj.con});
console.log(g_down_buf + ' <----');
control_led(sink_obj.con);
break;
}
}
}
}
}
}
}
}
function control_led(comm_num) {
var parent_process = cp.fork("ledm.js", [comm_num]);
parent_process.on('close', function (code) {
});
}
function tas_watchdog() {
if (tas_state == 'init') {
upload_client = new net.Socket();
upload_client.on('data', on_receive);
upload_client.on('error', function(err) {
console.log(err);
tas_state = 'reconnect';
});
upload_client.on('close', function() {
console.log('Connection closed');
upload_client.destroy();
tas_state = 'reconnect';
});
if (upload_client) {
console.log('tas init ok');
tas_state = 'init_thing';
}
}
else if (tas_state == 'init_thing') {
// init things
control_led('0');
tas_state = 'connect';
}
else if (tas_state == 'connect' || tas_state == 'reconnect') {
upload_client.connect(useparentport, useparenthostname, function() {
console.log('upload Connected');
tas_download_count = 0;
for (var i = 0; i < download_arr.length; i++) {
console.log('download Connected - ' + download_arr[i].ctname + ' hello');
var cin = {ctname: download_arr[i].ctname, con: 'hello'};
upload_client.write(JSON.stringify(cin) + '<EOF>');
}
if (tas_download_count >= download_arr.length) {
tas_state = 'upload';
}
});
}
}
// Every 3 seconds, check if the TAS is not working
wdt.set_wdt(require('shortid').generate(), 3, tas_watchdog);
4) tas_led / ledm.js
const Gpio = require('onoff').Gpio;
const pinRed = 13; // GPIO17 (pin11): red
const pinGreen = 19; // GPIO18 (pin12): green
const pinBlue = 26; // GPIO27 (pin13): blue
function turnOnLed() {
const ledRed = new Gpio(pinRed, 'out');
const ledBlue = new Gpio(pinBlue,'out');
const ledGreen = new Gpio(pinGreen,'out');
console.log('light on!');
ledRed.writeSync(1);
ledBlue.writeSync(1);
ledGreen.writeSync(1);
}
function turnOnPurple(){
const ledRed = new Gpio(pinRed,'out');
const ledBlue = new Gpio(pinBlue,'out');
const ledGreen = new Gpio(pinGreen,'out');
console.log('Purple on!');
ledRed.writeSync(1);
ledBlue.writeSync(1);
ledGreen.writeSync(0);
}
function turnOnBlue(){
const ledRed = new Gpio(pinRed,'out');
const ledBlue = new Gpio(pinBlue,'out');
const ledGreen = new Gpio(pinGreen,'out');
console.log('blue on!');
ledRed.writeSync(0);
ledBlue.writeSync(1);
ledGreen.writeSync(0);
}
function turnOnRed(){
const ledRed = new Gpio(pinRed,'out');
const ledBlue = new Gpio(pinBlue,'out');
const ledGreen = new Gpio(pinGreen,'out');
console.log('red on!');
ledRed.writeSync(1);
ledBlue.writeSync(0);
ledGreen.writeSync(0);
}
function turnOnGreen(){
const ledRed = new Gpio(pinRed,'out');
const ledBlue = new Gpio(pinBlue,'out');
const ledGreen = new Gpio(pinGreen,'out');
console.log('green on!');
ledRed.writeSync(0);
ledBlue.writeSync(0);
ledGreen.writeSync(1);
}
function turnOffAll() {
const ledRed = new Gpio(pinRed, 'out');
const ledGreen = new Gpio(pinGreen, 'out');
const ledBlue = new Gpio(pinBlue, 'out');
console.log('All lights off!');
ledRed.writeSync(0);
ledGreen.writeSync(0);
ledBlue.writeSync(0);
}
function turnonCyan(){
const ledRed = new Gpio(pinRed,'out');
const ledBlue = new Gpio(pinBlue,'out');
const ledGreen = new Gpio(pinGreen,'out');
console.log('cyan on!');
ledRed.writeSync(0);
ledBlue.writeSync(1);
ledGreen.writeSync(1);
}
function turnonYellow(){
const ledRed = new Gpio(pinRed,'out');
const ledBlue = new Gpio(pinBlue,'out');
const ledGreen = new Gpio(pinGreen,'out');
console.log('cyan on!');
ledRed.writeSync(1);
ledBlue.writeSync(0);
ledGreen.writeSync(1);
}
switch (process.argv[2]) {
case '000':
turnOffAll(); break;
case "100":
turnOnRed(); break;
case '010':
turnOnGreen(); break;
case '001':
turnOnBlue(); break;
case '110':
turnOnPurple();break;
case '011':
turnonCyan();break;
case '101':
turnonYellow();break;
case '111':
turnOnLed();break;
default:
console.log('Sorry, wrong command!');
}
5) tas_led / conf.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<m2m:conf xmlns:m2m="http://www.onem2m.org/xml/protocols"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<tas>
<parenthostname>localhost</parenthostname>
<parentport>3105</parentport>
</tas>
<download>
<ctname>act_led</ctname>
<id>led#1</id>
</download>
</m2m:conf>
MobiusStatusThis is what the final Mobius monitor looks like.However, we have to do need to separate them by function.
It could be Web - App & HW - App.
ConclusionThe biggest advantage will be that children can grow and observe plants themselves, write diaries, and purchase new items at home through "EduFarm." Regardless of environmental factors, it can be raised anytime, anywhere, students can easily handle teaching aids by linking apps and the web, and it is possible for instructors to check students' progress in real-time.
Comments