고급 개발자로 가는 길

Java Script

[Node RED] Modbus Protocol JavaScript

다크엔지니어 2021. 4. 22. 22:08
반응형

이전에 키오스크 모드 웹어플리케이션 개발에 사용된 프로토콜이다.

바로 Modbus 프로토콜인데 워낙 임베디드 분야에서는 필수 프로토콜 이다.

RaspberryPi 보드 에서 Node RED 플랫폼 환경으로 Modbus Protocol 을 JavaScript 로 구현 한 것이다.

 

Modbus Protocol Module 을 사용하여 아래 처럼 구현 할 수 있다.

아래는 2개의 함수 노드 이다.

 

간략하게 설명하자면, 아래 환경을 구성하기 위해서는 Modbus Protocol 을 Read 하거나 Write

할 수 있는 Server 혹은 Client 가 존재해야 한다.

필자는 RaspberryPi 를 Client 로 사용하였으며, 다른 Board 와 Modbus Protocol 구성을 하여 데이터를

Read 해 사용하였다.

var arr, i;
var CNT_BRO = 20000;
var CNT_BRW = 20000;
var CNT_WRO = 65545;
var CNT_WRW = 65545;

if (global.get("MAIN_INIT1") === undefined) {
    node.warn("INIT GLOBAL1");
    global.set("MAIN_INIT1", 1);
    
    // memory map
    arr = new Array(CNT_BRO);
    for(i=0; i<CNT_BRO; i++)
        arr[i] = false;
        
    global.set('memorymap_bro1', arr);


    arr = new Array(CNT_BRW);
    for(i=0; i<CNT_BRW; i++)
        arr[i] = false;
        
    global.set('memorymap_brw1', arr);


    arr = new Array(CNT_WRO);
    for(i=0; i<CNT_WRO; i++)
        arr[i] = 0;
        
    global.set('memorymap_wro1', arr);


    arr = new Array(CNT_WRW);
    for(i=0; i<CNT_WRW; i++)
        arr[i] = 0;
        
    global.set('memorymap_wrw1', arr);
}

// function
context.global.word2short = function(x) {
    if(x > 32767)
    {
        x = x - 65536;
    }
    
    return x;
};


// io rw
msg_modbus = [
    {
        payload : {
            'fc': 1,
            'unitid': 1,
            'address': 0,
            'quantity': 40 //io
        }
    },
    {
        payload : {
            'fc': 2,
            'unitid': 1,
            'address': 0,
            'quantity': 40 //io
        }
    },
    {
        payload : {
            'fc': 3,
            'unitid': 1,
            'address': 29696,
            'quantity': 32 //axis
        }
    },
    {
        payload : {
            'fc': 3,
            'unitid': 1,
            'address': 29198,
            'quantity': 120 //사이클, 제품수량, 알람, 모델, 버전
        }
    },
    {
        payload : {
            'fc': 4,
            'unitid': 1,
            'address': 1024,
            'quantity': 20 // 전체 제품불량 및 취출수량
        }
    },
    {
        payload : {
            'fc': 3,
            'unitid': 1,
            'address': 15398,
            'quantity': 1 //자동종료:달성률
        }
    },
    {
        payload : {
            'fc': 3,
            'unitid': 1,
            'address': 4054,
            'quantity': 120 //ok
        }
    },
    {
        payload : {
            'fc': 4,
            'unitid': 1,
            'address': 24330,
            'quantity': 16 //통신
        }
    },
    {
        payload : {
            'fc': 1,
            'unitid': 1,
            'address': 6437,
            'quantity': 29 //모드
        }
    },
    {
        payload : {
            'fc': 1,
            'unitid': 1,
            'address': 13632,
            'quantity': 3 //불량, 종료OnOff
        }
    },
    {
        payload : {
            'fc': 3,
            'unitid': 1,
            'address': 4390,
            'quantity': 11 //속도
        }
    },
    {
        payload : {
            'fc': 3,
            'unitid': 1,
            'address': 11956,
            'quantity': 1 // 모델
        }
    },
    {
        payload : {
            'fc': 3,
            'unitid': 1,
            'address': 11741,
            'quantity': 12 // 파라미터
        }
    },
    {
        payload : {
            'fc': 3,
            'unitid': 1,
            'address': 12207,
            'quantity': 12 // 파라미터
        }
    },
    {
        payload : {
            'fc': 1,
            'unitid': 1,
            'address': 13568,
            'quantity': 2 //제품측,런너측
        }
    },
    {
        payload : {
            'fc': 1,
            'unitid': 1,
            'address': 2513,
            'quantity': 1 //total alarm
        }
    },
    {
        payload : {
            'fc': 3,
            'unitid': 1,
            'address': 20400,
            'quantity': 32 //smart function
        }
    },
    {
        payload : {
            'fc': 3,
            'unitid': 1,
            'address': 20600,
            'quantity': 3 //smart function channel use check
        }
    }
];

// 모드버스 쿼리는 MAIN타이머(56ms) 마다 18개를 순회하므로
// 쿼리수에 따라 interval 조정
var idx = 0;
var MODBUS_CNT = global.get("MODBUS_CNT1")||0;

global.set("MODBUS_CNT1", ++MODBUS_CNT);
idx = (MODBUS_CNT % msg_modbus.length);

return msg_modbus[idx];

Modbus 에는 Function Code 로 coil register 등이 존재하며, 그에따라 4개인 bit data 와 word date ,ro 와 rw 

로 나누어 메모리를 나누었다.

모든 데이터가 특정 점유 없이 동일한 우선순위로 가져가야 하므로 라운드 로빈 방식을 통해 

56ms 만큼 Query 한다.

 

 

var i;
var mbs = msg.modbusRequest;

if(msg.payload === "")
{
    var cnt = global.get('MODBUS_ERROR1')||0;
    global.set('MODBUS_ERROR1', ++cnt);
    
    node.status({fill:"red",shape:"dot",text:"error"});
    return null;
}
else
{
    global.set('MODBUS_ERROR1', 0);
}

// update memory map
if(mbs.fc == 1)
{
    var mmbrw = global.get('memorymap_brw1')||0;
    
    for(i=0; i<mbs.quantity; i++)
        mmbrw[mbs.address+i] = msg.payload.data[i];
}
else if(mbs.fc == 2)
{
    var mmbro = global.get('memorymap_bro1')||0;
    
    for(i=0; i<mbs.quantity; i++)
        mmbro[mbs.address+i] = msg.payload.data[i];
}
else if(mbs.fc == 3)
{
    var mmwrw = global.get('memorymap_wrw1')||0;
    
    for(i=0; i<mbs.quantity; i++)
        mmwrw[mbs.address+i] = msg.payload.data[i];
}
else if(mbs.fc == 4)
{
    var mmwro = global.get('memorymap_wro1')||0;
    
    for(i=0; i<mbs.quantity; i++)
        mmwro[mbs.address+i] = msg.payload.data[i];
}

node.status({fill:"green",shape:"dot",text:"good"});

return msg;​

이 부분은 Read 해온 데이터를 global var 로 write 하여 다른 시퀀스에서 데이터를 사용할 수 있게 한다.

반응형