[Node-red] Relay node 제작

2022. 12. 5. 11:52개발/Node-red

2022.11.21 - [기록/개발 노트] - [Node-red] Schedule node 수정 - 4

 

[Node-red] Schedule node 수정 - 4

2022.11.21 - [기록/개발 노트] - [Node-red] Custom node 수정 - 3 [Node-red] Custom node 수정 - 3 마지막 수정 후 보완할 점들 mqtt 통신 불안정 mqtt 통신 불안정에 따른 Node-red 서버 중지 ( cmd 에서 자주 node-red 종료

iruk.tistory.com

Node-red 에서 새로운 custom node를 만들어 보았다.

위 링크에서 만들었던 schedule node는

시간 데이터를 입력받아 타이머 기능을 한다.

 

지정 시간이 되면 MQTT프로토콜을 사용해

ESP32에 원격으로 데이터를 전송해 릴레이가 동작한다. 

 

새로 만든 custom node는

단순히 JSON 데이터를 입력받아서

MQTT 로 ESP32를 제어한다.

 

schedule node 와 별개로

시간 데이터 없이 단순 동작을 구현한다.


.js 파일

node.on('input', function(msg) {
	var pLoad = msg.payload;

	var msg_toPublish = '{\"mac\" : ' + "\"" + String(config.id) + "\",\"type\" : 100,\"outNo\" : " 
	+ String(pLoad.outNo) + ",\"value\" : " + String(pLoad.value) + "}";

	console.log(msg.payload);
	//var pLoad = JSON.parse(msg.payload);
	if(pLoad.outNo != undefined && pLoad.value != undefined){
	client.publish(inTopic, msg_toPublish);
	}

	//client.publish(inTopic, msg.payload);
	//node.send(msg);
});

custom node에 msg가 입력됐을 때 실행 함수다.

입력된 msg에 outNo, value Key가 입력됐을 때만

msg_toPublish 문자열 데이터를 MQTT로 전송한다.

var mqtt    = require('mqtt');
var nodeContext = this.context();
var outTopic=  String(config.id) + "-out";
var inTopic =  String(config.id) + "-in";

MQTT의 Topic은 ESP32의 고유번호를 사용하도록 했다.

위와 같이 ESP32 겉면에

C049EF34003C 같이 고유번호가 있다.

Arduino 소스로 확인해본 결과

  uint8_t macH[6]="";
  WiFi.macAddress(macH);
  sprintf(mac,"%02x%02x%02x%02x%02x%02x%c",macH[0], macH[1], macH[2], macH[3], macH[4], macH[5],0);
  sMac=mac;
  clientName=mac;
  Serial.println(mac);

WiFi.macAddress(macH) 로 추출한 값과

ESP32의 겉면에 적혀있는 값이 같았다.

 

따라서 사용자들로 하여금

각자 사용하는 ESP32의 번호는 겹치지 않으므로

본인 칩을 확인해서 node에 입력하면

그 값에 따라서 MQTT topic이 각각 부여된다.

위와 같이 node 설정에서 칩 겉면의 값을 입력하면

사용자 모두의 MQTT topic이 고유한 값으로 설정된다.


Arduino 처리

//outNo1 켜기 function
msg.payload={
    "outNo":1,
    "value":1
}
return msg;
// outNo0 끄기 function
msg.payload={
    "outNo":0,
    "value":0
}
return msg;

위와 같이 node-red flow를 구성했다.

// 아두이노 데이터 수신할 때 callback 처리문 
  deserializeJson(doc,payload);
  root = doc.as<JsonObject>();
  int md_value = root["outNo"];
  int payload_val = root["value"];
  Serial.print("payload_val : ");
  Serial.println(payload_val);
  switch (md_value){
    //heat = 0, cool = 1, exhaust = 2, led = 3
    case 0 :
        on_off(payload_val, heat_pin);
        Serial.println("case 0");
        break;

    case 1 :
        on_off(payload_val, cool_pin);
        Serial.println("case 1");
        break;

    case 2 :
        on_off(payload_val, exha_pin);
        Serial.println("case 2");
        break;

    default :
        on_off(payload_val, led_pin);     
        Serial.println("last case");
  }

outNo 와 value Key값을 반환해서 각각의 처리를 실행한다.


다른 방식의 동작 추가

위 까지의 동작은

outNo, value 두가지 key를 사용했다.

즉 사용자가 on/off 하고 싶은 릴레이의 동작 데이터를

하나 하나 기입해줘야 했다.

 

번거로운 점이 있으므로 모든 릴레이의 동작 데이터를

하나의 msg로 전송하도록 하는 방식을 생각했다.

 

// 노드레드 function 코드
msg.payload={
    "outNo0":0,
    "outNo1": 0,
    "outNo2": 0,
    "outNo3": 0
}
return msg;

위와 같이 outNo0 ~ outNo3 데이터를 한 번에 전송해서

사용자가 보다 편리하게 제어할 수 있도록 한다.


.js 파일 수정

node.on('input', function(msg) {
	var pLoad = msg.payload;

	var msg_toPublish = '{\"mac\" : ' + "\"" + String(config.id) + "\",\"type\" : 100,\"outNo\" : " 
	+ String(pLoad.outNo) + ",\"value\" : " + String(pLoad.value) + "}";
	
    	//-----추가-------
	var msg_toPublish2 = '{\"mac\" : ' + "\"" + String(config.id) + "\",\"type\" : 100,\"outNo0\" : " 
	+ String(pLoad.outNo0) + ",\"outNo1\" : " + String(pLoad.outNo1) + ",\"outNo2\" : " + String(pLoad.outNo2) 
	+ ",\"outNo3\" : " + String(pLoad.outNo3) + "}"; 
	//-----추가-------
    
	console.log(msg.payload);
	//var pLoad = JSON.parse(msg.payload);
	if(pLoad.outNo != undefined && pLoad.value != undefined){
	client.publish(inTopic, msg_toPublish);
	}
    
    	//-----추가-------
	else if(pLoad.outNo0 != undefined && pLoad.outNo1 != undefined && pLoad.outNo2 != undefined && pLoad.outNo3 != undefined){
	client.publish(inTopic,msg_toPublish2);
	}
    	//-----추가-------

	//client.publish(inTopic, msg.payload);
	//node.send(msg);
});

outNo0 outNo1 outNo2 outNo3 값이 동시에 존재할때만 

동작이 구현되도록 했다.

아두이노의 처리문도 수정해서 동작한다.


아두이노 처리문 수정

  int md_value = root["outNo"]; // 구분해서 들어오는 데이터
  int payload_val = root["value"]; 
  Serial.print("payload_val : ");
  Serial.println(payload_val);
  
  int out0 = root["outNo0"];  // 한 줄로 들어오는 데이터
  int out1 = root["outNo1"];
  int out2 = root["outNo2"];
  int out3 = root["outNo3"];
  if(root["outNo0"] && root["outNo1"] && root["outNo2"] && root["outNo3"]){
    Serial.println("test");
  }

기존에 사용하던 outNo, value 두 가지 key를 제외하고

4가지 key를 추가했다.

outNo0 outNo1 outNo2 outNo3 데이터를 반환해서

처리 동작을 구현한다.

 

전체 데이터가 수신됐을 때

시리얼 모니터에 'test'를 출력하도록 한다.


오류

분명 전체 데이터가 한 번에 들어왔는데

아두이노 에서는 outNo, value 가 들어왔을 경우로 인식한다.

  int md_value = root["outNo"]; // 구분해서 들어오는 데이터
  int payload_val = root["value"]; 
  
  switch (md_value){
    //heat = 0, cool = 1, exhaust = 2, led = 3
    case 0 :
        on_off(payload_val, heat_pin);
        Serial.println("case 0");
        break;

위 처리로 인식해서 case 0 이 출력되는 상태다.

  int md_value = root["outNo"]; // 구분해서 들어오는 데이터
  int payload_val = root["value"]; 
  Serial.print("payload_val : ");
  Serial.println(payload_val);
  
  int out0 = root["outNo0"];  // 한 줄로 들어오는 데이터
  int out1 = root["outNo1"];
  int out2 = root["outNo2"];
  int out3 = root["outNo3"];
  if(root["outNo0"] && root["outNo1"] && root["outNo2"] && root["outNo3"]){
    Serial.println("test");
  }

 

root["outNo"] 와 root["outNo0"] 에서 충돌이 나는건가 싶어서

아두이노와 노드의 소스를 수정했다.


오류 후 수정

// node 소스 수정
node.on('input', function(msg) {
	var pLoad = msg.payload;
	
	var msg_toPublish = '{\"mac\" : ' + "\"" + String(config.id) + "\",\"type\" : 100,\"outNo\" : " 
	+ String(pLoad.outNo) + ",\"value\" : " + String(pLoad.value) + "}";

	var msg_toPublish2 = '{\"mac\" : ' + "\"" + String(config.id) + "\",\"type\" : 100,\"No0\" : " 
	+ String(pLoad.outNo0) + ",\"No1\" : " + String(pLoad.outNo1) + ",\"No2\" : " + String(pLoad.outNo2) 
	+ ",\"No3\" : " + String(pLoad.outNo3) + "}"; 

	console.log(msg.payload);
	//var pLoad = JSON.parse(msg.payload);
	if(pLoad.outNo != undefined && pLoad.value != undefined){
	client.publish(inTopic, msg_toPublish);
	}
	else if(pLoad.outNo0 != undefined && pLoad.outNo1 != undefined && pLoad.outNo2 != undefined && pLoad.outNo3 != undefined){
	client.publish(inTopic,msg_toPublish2);
	}

	//client.publish(inTopic, msg.payload);
	//node.send(msg);
});
  // 아두이노 처리문 수정
  int out0 = root["No0"];  // 한 줄로 들어오는 데이터
  int out1 = root["No1"];
  int out2 = root["No2"];
  int out3 = root["No3"];
  if(root["No0"] && root["No1"] && root["No2"] && root["No3"]){
    Serial.println("test");
  }

node 에서 msg를 인식하고, MQTT 데이터를 송신할 때

key를 outNo0 ~ outNo3 에서 No0~No3 로 수정했다.

 

그에 따라서 아두이노 처리문도 같이 수정했다.

데이터는 정상적으로 수신된다.

하지만 'test'가 출력되지 않는다.

No0 ~ No3 이 존재하므로 'test'가 출력돼야 하는데

이유를 모르겠다. 다른 방식으로 수정해보았다.

오류 후 수정 - 2

  if(root["No0"] && root["No1"] && root["No2"] && root["No3"]){
    Serial.println("test");
  }

  else if(root["outNo"] && root["value"]){
  int md_value = root["outNo"];
  int payload_val = root["value"];
  
  switch (md_value){
    //heat = 0, cool = 1, exhaust = 2, led = 3
    case 0 :
        on_off(payload_val, heat_pin);
        Serial.println("case 0");
        break;

    case 1 :
        on_off(payload_val, cool_pin);
        Serial.println("case 1");
        break;

    case 2 :
        on_off(payload_val, exha_pin);
        Serial.println("case 2");
        break;

    default :
        on_off(payload_val, led_pin);     
        Serial.println("last case");
    }
  }

위와 같이 switch 문도 else if 문에 포함시켜

outNo 와 value가 수신됐을 때만 동작하도록 했다.

case 1 이 출력되긴 하지만

on 동작일때만 출력된다. 심지어 릴레이가 off되지가 않는다.

 

소스에서의 오류는 아닌 것 같은데

일단 소스부터 계속 수정해보고

 

아니면 MQTT 프로토콜의 최대 크기 초과일수도 있겠다.

'개발 > Node-red' 카테고리의 다른 글

[Node-red] Schedule 노드 ( mqtt x ) 제작  (0) 2022.12.09
[Node-red] Relay node 제작 - 2  (0) 2022.12.07
[Node-red] Custom node 만드는 방법  (0) 2022.12.02
[Node-red] Custom node 배포  (0) 2022.12.01
[Node-red] Schedule node 수정 - 4  (0) 2022.11.21