From dd36770ff6362ecba9ba2fbcc171bc399006659c Mon Sep 17 00:00:00 2001 From: truedo Date: Fri, 27 Sep 2024 14:39:42 +0900 Subject: [PATCH] =?UTF-8?q?=EB=A1=9C=EB=B3=B4=EB=A7=81=ED=81=AC=20?= =?UTF-8?q?=EC=A3=BC=EB=AF=B8=20=EB=AF=B8=EB=8B=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/modules/robolink_ZumiMini.js | 282 +++++++++++++++++++++++++++++ app/modules/robolink_ZumiMini.json | 34 ++++ app/modules/robolink_ZumiMini.png | Bin 0 -> 13707 bytes 3 files changed, 316 insertions(+) create mode 100644 app/modules/robolink_ZumiMini.js create mode 100644 app/modules/robolink_ZumiMini.json create mode 100644 app/modules/robolink_ZumiMini.png diff --git a/app/modules/robolink_ZumiMini.js b/app/modules/robolink_ZumiMini.js new file mode 100644 index 000000000..27997ada8 --- /dev/null +++ b/app/modules/robolink_ZumiMini.js @@ -0,0 +1,282 @@ +const BaseModule = require('./baseModule'); + +class ZumiMini extends BaseModule { + + constructor() { + super(); + + this.HEADER1 = 0; + this.HEADER2 = 1; + this.COM = 2; + this.INFO = 2; //for recevied packet(reporter) type + this.REQ = 3; + this.P_STAT = 4; + this.IR = 5; + this.FD = 10; + this.CD = 13; + this.AD = 16; + this.EULER = 19; + this.MD = 22; + this.USER = 23; + this.CRC_L = 7; + this.CRC_H = 8; + this.DELI1 = 9; + this.DELI2 = 10; + this.PACKET_LENGTH = 30; + + this.PARAM = 4; + this.SEND_PACKET_LENGTH = 11; + + this.DTYPE_FRONT_SENSOR = 1; + this.DTYPE_BOTTOM_SENSOR = 2; + this.DTYPE_FACE_DETECT = 3; + this.DTYPE_COLOR_DETECT = 4; + this.DTYPE_APRIL_DETECT = 5; + this.DTYPE_EULER = 6; + + this.zInterval = false; + this.counter = 0; + + this.foo = 0; + this.sp = null; + + //data to Entry + this.inputData = { + pInfo: 0, + pSize: 0, + pStat: 0, + irSensor: { + FL: 0, + FR: 0, + BL: 0, + BM: 0, + BR: 0, + }, + faceDetect:{ + DETECT: 0, + CX : 0, + CY: 0, + }, + colorDetect: { + COLOR: 0, + CX: 0, + CY: 0, + }, + aprilDetect:{ + ID: 0, + CX: 0, + CY: 0, + }, + euler:{ + ROLL: 0, + PITCH: 0, + YAW: 0, + }, + motionDetect: 0, + userDefined:{ + DETECT: 0, + CX: 0, + CY: 0, + } + }; + + //data from entry + this.dataFromEntry = { + com : 0, + req : 0, + speed: 0, + dir: 0, + dist: 0, + }; + + this.sPacket = new Array(this.SEND_PACKET_LENGTH); + for (var j = 0; j < this.SEND_PACKET_LENGTH; j++) { + this.sPacket[j] = 0x00; + } + + } + + init(handler, config) { + + } + + requestInitialData(sp) { + + return null; + } + + // 연결 후 초기에 수신받아서 정상연결인지를 확인해야하는 경우 사용합니다. + checkInitialData(data, config) { + + return true; + } + + // optional. 하드웨어에서 받은 데이터의 검증이 필요한 경우 사용합니다. + validateLocalData(data) { + + return true; + } + + // 엔트리로 전달할 데이터 + requestRemoteData(handler) { + //console.log('send hardware data to entry'); + handler.write("inputData", this.inputData); + } + + crc16_ccitt(buf) { + var crcTable = [ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 + ]; + + var crc = 0x0000; + var j, i; + for (i = 0; i < buf.length; i++) { + let c = buf[i]; + if (c > 255) { + throw new RangeError(); + } + j = (c ^ (crc >> 8)) & 0xFF; + crc = crcTable[j] ^ (crc << 8); + } + return ((crc ^ 0) & 0xFFFF); + } + + + // 엔트리에서 받은 데이터에 대한 처리 + handleRemoteData(handler) { + + //console.log('read data from entry'); + + Object.keys(this.dataFromEntry).forEach((port) => { + this.dataFromEntry[port] = handler.read(port); + }); + + this.sPacket[this.HEADER1] = 0x24; + this.sPacket[this.HEADER2] = 0x52; + this.sPacket[this.COM] = this.dataFromEntry.com; + this.sPacket[this.REQ] = this.dataFromEntry.req; + this.sPacket[this.PARAM] = this.dataFromEntry.speed; + this.sPacket[this.PARAM + 1] = this.dataFromEntry.dist; + this.sPacket[this.PARAM + 2] = this.dataFromEntry.dir; + + var crcBuff = this.sPacket.slice(2, this.SEND_PACKET_LENGTH - 4); + var crc16 = this.crc16_ccitt(crcBuff); + var crcL = crc16 & 0x00FF; + var crcH = (crc16 & 0xFF00) >> 8; + + this.sPacket[this.CRC_L] = crcL; + this.sPacket[this.CRC_H] = crcH; + this.sPacket[this.DELI1] = 0xFF; + this.sPacket[this.DELI2] = 0xFF; + + } + + + //하드웨어에 전달할 데이터 + requestLocalData() { + //console.log('send packet to hardware'); + + /* + if(this.zInterval == true) { + this.sPacket[this.HEADER1] = 0x24; + this.sPacket[this.HEADER2] = 0x52; + this.zInterval = false; + } + else { + this.sPacket[this.HEADER1] = 0x00; + this.sPacket[this.HEADER2] = 0x00; + } + */ + + //for (var j = 0; j < this.SEND_PACKET_LENGTH; j++) { + // process.stdout.write(" " + this.sPacket[j].toString(16)); + //} + //console.log(" "); + + return this.sPacket; + } + + // 하드웨어에서 온 데이터 처리 + handleLocalData(data) { + + console.log('receive packet from hardware'); + for (var j = 0; j < data.length; j++) { + process.stdout.write(" " + data[j].toString(16)); + // //process.stdout.write(" " + String.fromCharCode(data[j])); + } + console.log(" "); + + if ((data[this.HEADER1] == 0x24)&&(data[this.HEADER2] == 0x52)) { + + if (data.length < (this.PACKET_LENGTH - 4)) return; + + //if (data[this.PARAM] == 0x00) this.inputData.pStat = "READY"; + //else if (data[this.PARAM] == 0x01) this.inputData.pStat = "PROCESS"; + //else if (data[this.PARAM] == 0x02) this.inputData.pStat = "ACK"; + this.inputData.pInfo = data[this.INFO]; + this.inputData.pSize = data[this.P_SIZE]; + this.inputData.pStat = data[this.P_STAT]; + this.inputData.irSensor.FL = data[this.IR]; + this.inputData.irSensor.FR = data[this.IR + 1]; + this.inputData.irSensor.BL = data[this.IR + 2]; + this.inputData.irSensor.BM = data[this.IR + 3]; + this.inputData.irSensor.BR = data[this.IR + 4]; + this.inputData.faceDetect.DETECT = data[this.FD]; + this.inputData.faceDetect.CX = data[this.FD + 1]; + this.inputData.faceDetect.CY = data[this.FD + 2]; + this.inputData.colorDetect.COLOR = data[this.CD]; + this.inputData.colorDetect.CX = data[this.CD + 1]; + this.inputData.colorDetect.CY = data[this.CD + 2]; + this.inputData.aprilDetect.ID = data[this.AD]; + this.inputData.aprilDetect.CX = data[this.AD + 1]; + this.inputData.aprilDetect.CY = data[this.AD + 2]; + this.inputData.euler.ROLL = data[this.EULER]; + this.inputData.euler.PITCH = data[this.EULER + 1]; + this.inputData.euler.YAW = data[this.EULER + 2]; + this.inputData.motionDetect = data[this.MD]; + this.inputData.userDefined.DETECT = data[this.USER]; + this.inputData.userDefined.CX= data[this.USER + 1]; + this.inputData.userDefined.CY = data[this.USER + 2]; + + this.counter++; + if (this.counter > 255) this.counter = 0; + + //console.log("PSTAT:" + this.inputData.pStat + " FL:" + this.inputData.irSensor.FL + " FR: " + this.inputData.irSensor.FR); + //this.zInterval = true; + } + } + +} + +module.exports = new ZumiMini(); \ No newline at end of file diff --git a/app/modules/robolink_ZumiMini.json b/app/modules/robolink_ZumiMini.json new file mode 100644 index 000000000..df5911abd --- /dev/null +++ b/app/modules/robolink_ZumiMini.json @@ -0,0 +1,34 @@ +{ + "id": "4A0501", + "name": { + "en": "Robolink zumi mini", + "ko": "로보링크 주미 미니" + }, + "category": "robot", + "platform": ["win32"], + "icon": "robolink_ZumiMini.png", + "module": "robolink_ZumiMini.js", + "driver": { + "win32-ia32": "CH34x_install_Windows_v3_4/CH34x_install_Windows_v3_4.EXE", + "win32-x64": "CH34x_install_Windows_v3_4/CH34x_install_Windows_v3_4.EXE" + }, + "url": "https://www.robolink.co.kr/", + "email": "asteam@robolink.co.kr", + "reconnect": true, + "selectPort": true, + "hardware": { + "type": "serial", + "control": "master", + "scanType": "data", + "duration": 50, + "baudRate": 115200, + "parity": "none", + "dataBits": 8, + "stopBits": 1, + "byteDelimiter": [255, 255], + "vendor": "robolink", + "firmwarecheck": false, + "bufferSize": 1024, + "flowControl": "none" + } +} \ No newline at end of file diff --git a/app/modules/robolink_ZumiMini.png b/app/modules/robolink_ZumiMini.png new file mode 100644 index 0000000000000000000000000000000000000000..5c70b61b48664b6c9c373acbbedec2ebc7e7d28c GIT binary patch literal 13707 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 aF|zyd|IZn(-K2!hfsvC^lKd@U82CTe{OzFt literal 0 HcmV?d00001