Arduino

Wifi 스마트 콘센트 2 + 라즈베리파이

[혜안] 2017. 12. 9. 22:59
728x90

코어기능 개발에는 30분이 안걸렸다.

2017/12/03 - [Arduino] - Wifi 스마트 콘센트 1


이제 기존의 스마트폰 앱에서 호출 가능한 구조로 손을 봐야 한다. 아마 300분은 걸릴 듯...


방법은 이렇다.

1. 기존에 스마트 콘센트를 제어하던 앱 위젯에 Wifi형 스마트 콘센트를 추가한다. 

2017/04/18 - [Raspberry pi] - 4편 스마트 콘센트? Smart Plug! + 앱위젯

2. 그러기 위해서 라즈베리파이에서 아두이노를 제어하도록 기능을 통합한다.

     (아두이노가 서버, 라즈베리파이가 클라이언트이다.)

3. 그러기 위해서 아두이노의 제어 로직을 수정해야 한다.



1. 아두이노의 제어로직 부터 수정하자.


json 포멧으로 정의하고, SWITCH 가 입력되면 현재 상태를 toggle 하도록 구현한다.

아래는 일부소스이다. 

1편 (2017/12/03 - [Arduino] - Wifi 스마트 콘센트 1) 소스에서 아래 내용만 변경되었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void loop() {
  // Check if a client has connected
  WiFiClient client = server.available();
  if (!client) {
    return;
  }
 
  // Wait until the client sends some data
  Serial.println("new client");
  while(!client.available()){
    delay(1);
  }
 
  // Read the first line of the request
  String request = client.readStringUntil('\r');
  Serial.println(request);
  client.flush();
 
  int value = digitalRead(SWITCH);
  if(request.indexOf("/SWITCH"!= -1) {
    digitalWrite(SWITCH, !value);
    value = digitalRead(SWITCH);
  }
  
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: application/json");
  client.println("");
  if(value == HIGH)
    client.println("{\"result\":\"on\"}");
  else
    client.println("{\"result\":\"off\"}");
  
  delay(1);
  Serial.println("Client disonnected");
  Serial.println("");
}
cs


시험!

잘 동작한다.



2. 이제, 라즈베리파이에서 아두이노를 호출하도록 기능을 추가하자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
var wifiplug_state = false;
 
var tog_wifiplug = {host : '192.168.0.11', path: '/SWITCH', method: 'GET', timeout:1000};
var stat_wifiplug = {host : '192.168.0.11', path: '/', method: 'GET', timeout:1000};
 
function stat_wifi(do_response) {
    var req = http.request(stat_wifiplug, function(response){
        var data = '';
        response.on('data', function(chunk) {
            data += chunk;
        });
        response.on('end', function() {
            var obj = JSON.parse(data);
            l("wifiplug " + obj.result);
            if(obj.result == "on")
                wifiplug_state = true;
            else
                wifiplug_state = false;
            if(do_response)
                send_push();
        });
    });
    
    req.on('error', function(err) {
        e('problem with request: ' + err.message);
    });
    req.end();
}
 
function toggle_wifi(res) {
    var req = http.request(tog_wifiplug, function(response){
        //handleResponse(res);
        var data = '';
        response.on('data', function(chunk) {
            data += chunk;
        });
        response.on('end', function() {
            var obj = JSON.parse(data);
            l("wifiplug " + obj.result);
            if(obj.result == "on")
                wifiplug_state = true;
            else
                wifiplug_state = false;
 
            send_push();
            res.send([{"result":"ok"}]);
        });
    });
    
    req.on('error', function(err) { 
        e('problem with request: ' + err.message);
        res.send([{"result":"fail"}]);
    });
    req.end();
}
 
app.get('/wifiplugtoggle', function(req, res) {    
    toggle_wifi(res);    
});
cs


설명을 좀 달자면, 

var tog_wifiplug = {host : '192.168.0.11', path: '/SWITCH', method: 'GET', timeout:1000};
var stat_wifiplug = {host : '192.168.0.11', path: '/', method: 'GET', timeout:1000};

tog_wifiplug : 스마트 콘센트 on/off toggle 을 위한 옵션값

stat_wifiplug: 현재의 스마트 콘센트 on/off 상태를 조회하기위한 옵션값

 → http request를 할때에 호출주소, 경로, 방법, 타임아웃 시간 등을 미리 정의하여 호출할 수 있다.

timeout은 msec 단위이며, 1초로 주어야 아두이노가 응답이 없을때에 답답하다는 느낌이 들지 않는다.


        response.on('data', function(chunk) {
            data += chunk;
        });

request를 한 후에 응답이 오는 chunk를 data 에 쌓아 놓는다.

응답은 한번에 모두 오지 않기 때문에, chunk를 바로 해석하려고 하면 아무리 짧은 응답도 간헐적으로 오류가 발생한다.


       response.on('end', function() {
     var obj = JSON.parse(data);
            l("wifiplug " + obj.result);
            if(obj.result == "on")
                wifiplug_state = true;
            else
                wifiplug_state = false;
 
            send_push();
            res.send([{"result":"ok"}]);
        });

응답이 종료되면 chunk를 쌓아노은 data 를 json으로 파싱한다.

그러면 아두이노에서 응답한 {"result":"on"} 과 같은 json data가 파싱되어 on 또는 off 라는 문자열 결과값을 가져올 수 있다.

그리고 가져온 데이터는 gcm push로 등록된 스마트폰에 전송한다.

스마트폰으로의 http response는 오류가 발생하지 않는 한 "ok" 이다.


    req.on('error', function(err) {
        e('problem with request: ' + err.message);
       res.send([{"result":"fail"}]);
    });

만약 아두이노에 호출하는 중 오류가 발생하면, 스마트폰으로 보내는 http response는 "fail"이다.


시험!!

역시 잘 동작한다.

만약 아두이노가 꺼져있으면?

역시 잘 동작한다. 그런데 1초만에 응답이 오는 것 같지 않다... 이건 좀 확인이 필요할 듯....


3. 스마트폰 앱을 수정하자.

워낙에 땜빵코드가 많아서 기능을 추가하려면 구조수정이 필요했다.

뭐 이런 과정이, 크게보면 애자일한게 아닐까? ㅋㅋ


우선 기존 스마트 콘센트는 lan plug로, wifi 스마트 콘센트는 wifi plug로 명명했다.

그리고, gcm push를 통해 받는 상태값이 하나가 아니고 두개가 된다. lan plug와 wifi plug.


좋은 소프트웨어 구조라면,

당연히 현재의 콘센트 개수를 받아오고,

개수대로  화면에 표시해주며, 

각 콘센트의 on/off 상태를 구분하여 표현해주도록 구성해야 하겠지만.... ㅋㅋ

일단 콘센트는 항상 두 개라는 가정하에, 두 개의 콘센트의 상태를 각각 표시해주고, 

앱 위젯도 lan plug와 wifi plug를 각각 배치할 수 있도록만 수정해보자. 

이것만해도 앱 수정범위가 아두이노와 라즈베리파이 보다 많다.

나머지는 다음에 애자일하게.... ㅋㅋ


일단 Main Activity에 wifi plug  버튼을 추가한다.

그리고 클릭 시 wifiplugtoggle을 호출하도록 한다.

그다음, 수신되는 gcm push에서 두 개의 콘센트 값을 각각 받아 Main Activity 버튼 상태를 변경한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
 
            if(intent.getAction().equals(context.getResources().getString(R.string.ACTION_PUSH))) {
                ToggleButton btnTog1 = (ToggleButton)findViewById(R.id.btnTog);
                if(intent.getStringExtra("state0").equals("on")){
                    btnTog1.setChecked(true);
                } else {
                    btnTog1.setChecked(false);
                }
                ToggleButton btnTog2 = (ToggleButton)findViewById(R.id.btnTog2);
                if(intent.getStringExtra("state1").equals("on")){
                    btnTog2.setChecked(true);
                } else {
                    btnTog2.setChecked(false);
                }
            }
        }
    };
cs


이번엔, 앱위젯...

앱위젯을 lan plug용과 wifi plug용으로 구분하려면,

1. 앱위젯을 추가로 만들거나, 

2. 기존 앱위젯에 설정을 추가해야 한다.

제어하는 plug만 바뀌는데 새 위젯 클래스를 만드는것 보다는 설정 추가가 더 낫다는 판단.

앱위젯을 화면에 배치하는 순간 설정화면이 떠서 lan plug와 wifi plug를 선택하도록 하면 된다.


방법은... 다음 포스트에...



728x90

'Arduino' 카테고리의 다른 글

Wifi 스마트 콘센트 4 + 터치센서  (0) 2017.12.12
Wifi 스마트 콘센트 3 + 앱 위젯  (0) 2017.12.10
Wifi 스마트 콘센트 1  (0) 2017.12.03
로봇 청소기 분해하기  (7) 2017.06.25
NodeMCU 환경구성  (0) 2017.05.15