|
| 1 | +--- |
| 2 | +layout: page |
| 3 | +title: 브라우저로 연결하기에 하드웨어 등록 |
| 4 | +type: guide |
| 5 | +category: 'Entry HW' |
| 6 | +order: 6 |
| 7 | +--- |
| 8 | + |
| 9 | +## 브라우저로 연결하기란? |
| 10 | +'브라우저로 연결하기'이하 '하드웨어 웹연결'은 [web serial api](https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API)을 사용하여 브라우저와 하드웨어 기기를 직접 연결하는 기능입니다. |
| 11 | +기존의 엔트리 하드웨어는 "사용자 기기 <=> [Entry HW 프로그램](https://playentry.org/download/hardware) <=> [엔트리 만들기 웹 페이지](https://playentry.org/ws/new)" 의 구조를 가지고 있습니다. |
| 12 | +하드웨어 웹연결은 Entry HW 프로그램을 사용하지 않고 "사용자 기기 <=> 엔트리 만들기 웹 페이지"의 구조를 가집니다. |
| 13 | + |
| 14 | +사용자 입장에서는 사용이 간편하고, 개발자 입장에서도 [entry-js](https://github.com/entrylabs/entryjs)의 코드만 관리하면 된다는 장점이 있습니다. |
| 15 | +하지만, 펌웨어와 드라이버 제공이 불가능하기에 하드웨어 제조사에서 별도의 안내를 해야하고 실험적이 기능이기에 연결이 불안정할 수도 있습니다 |
| 16 | + |
| 17 | +<br> |
| 18 | + |
| 19 | +## 유의사항 |
| 20 | +- 반드시 **자사 하드웨어 관련 파일만 수정**을 부탁드립니다. hw_Lite.js 나 기타 공용파일들 수정은 반영이 어렵습니다. |
| 21 | +- 하드웨어 웹연결에서는 기본적으로 펌웨어를 제공하지 않습니다. WS의 '펌웨어 다운로드' 버튼은 .hex파일을 다운로드하여 사용자가 직접 펌웨어 업데이트가 가능한 기기를 위한 기능입니다. |
| 22 | +- 하드웨어 웹연결 기능은 개발 초기 단계로 추후 **제공 함수나 연결 라이프사이클이 변경될 수 있습니다**. 경우에 따라서는 제조사측 코드를 수정해야 할 수 있으므로 미리 유의 부탁드립니다. |
| 23 | + |
| 24 | +<br> |
| 25 | + |
| 26 | +## PR 파일 간략 설명 |
| 27 | + |
| 28 | + |
| 29 | +이 단원에서는 최종적으로 제조사가 entryjs에 PR해야할 파일 작성시 유의사항과 간략한 역할을 기술합니다. |
| 30 | +하드웨어 웹연결을 지원하기 위해선 아래 3종류의 파일이 필수적으로 추가되어야 합니다. 아래 3파일을 작성후 entryjs의 develop-hw로 PR부탁드립니다. |
| 31 | + |
| 32 | +### block_모듈명 _lite.js |
| 33 | +- 반드시 entryjs > src > playground > blocks > hardwareLite 하위에 위치해야합니다. |
| 34 | +- 파일명은 반드시 block_ 모듈명 _lite.js 이어야 합니다. |
| 35 | +- 이 파일에서 하드웨어 웹연결에 필요한 정보를 담고있는 모듈클래스를 정의하게 됩니다. |
| 36 | +- 기존 하드웨어 연결에서 entryjs의 block_모듈명.js와 entry-hw의 모듈명.js의 역할을 모두 가지고 있습니다. |
| 37 | + |
| 38 | +### metadata_모듈명 _lite.json |
| 39 | +- 반드시 entryjs > src > playground > blocks > hardwareLite 하위에 위치해야합니다. |
| 40 | +- 파일명은 반드시 metadata_ 모듈명 _lite.json 이어야 합니다. |
| 41 | +- 웹연결 모듈에 대한 메타데이터를 가지고 있습니다. |
| 42 | +- 이 파일의 moduleId와 block_ 모듈명 _lite.js 의 클래스 내 id는 반드시 일치해야 합니다.(포맷이 다르므로 다른 하드웨어 웹연결 파일들을 예시로 참고해 주세요.) |
| 43 | +- 이 파일의 title, description은 WS에서 '브라우저로 연결하기' 클릭시 나타나는 위의 이미지화면에서 모듈카드의 정보를 가지고 있습니다. |
| 44 | + |
| 45 | +### 모듈명.png |
| 46 | +- 반드시 entryjs > images > hw_lite 하위에 위치해야 합니다. |
| 47 | +- '브라우저로 연결하기' 클릭시 나타나는 위의 이미지화면에서 보여지는 이미지 파일입니다. |
| 48 | +- 파일명은 block_ 모듈명 _lite.js 에서 정의한 클래스 생성자의 this.imageName과 일치해야 합니다. |
| 49 | + |
| 50 | +<br> |
| 51 | + |
| 52 | +## block_ 모듈명 _lite.js 구조 설명 |
| 53 | +WS가 실행되었을경우, Entry.모듈클래스 를 추가하는 함수가 즉시 실행됩니다. |
| 54 | +이 파일에 정의되는 모듈클래스 구조와 역할은 다음과 같습니다. |
| 55 | + |
| 56 | +### 모듈클래스 |
| 57 | + |
| 58 | +``` javascript |
| 59 | +'use strict'; |
| 60 | + |
| 61 | +(function () { |
| 62 | + Entry.ArduinoLite = new (class ArduinoLite { |
| 63 | + constructor() { |
| 64 | + this.id = '010101'; // id는 6자리 모두 입력해야 합니다. |
| 65 | + this.name = 'ArduinoLite'; |
| 66 | + this.url = 'http://www.arduino.cc/'; |
| 67 | + this.imageName = 'arduinolite.png'; |
| 68 | + this.title = { |
| 69 | + ko: '아두이노 우노', |
| 70 | + en: 'Arduino Uno', |
| 71 | + }; |
| 72 | + this.duration = 32; // 엔트리js에서 기기와 통신하는 함수를 호출하는 duration 간격입니다. |
| 73 | + this.blockMenuBlocks = [ |
| 74 | + 'arduinolite_get_number_sensor_value', |
| 75 | + 'arduinolite_get_digital_value', |
| 76 | + ]; |
| 77 | + this.portData = { |
| 78 | + baudRate: 9600, |
| 79 | + duration: 32, // web serial api에서 기기와 통신하는 duration 간격입니다. |
| 80 | + dataBits: 8, |
| 81 | + parity: 'none', |
| 82 | + stopBits: 1, |
| 83 | + bufferSize: 512, |
| 84 | + constantServing: true, |
| 85 | + }; |
| 86 | + this.readablePorts = []; |
| 87 | + this.setZero(); |
| 88 | + } |
| 89 | + |
| 90 | + setZero() { |
| 91 | + this.port = new Array(14).fill(0); |
| 92 | + this.digitalValue = new Array(14).fill(0); |
| 93 | + this.remoteDigitalValue = new Array(14).fill(0); |
| 94 | + this.analogValue = new Array(6).fill(0); |
| 95 | + this.readablePorts = _range(0, 19); |
| 96 | + |
| 97 | + if (Entry.hwLite && Entry.hwLite.serial) { |
| 98 | + Entry.hwLite.serial.update(); |
| 99 | + } |
| 100 | + } |
| 101 | + |
| 102 | + // 디바이스에서 값을 읽어옵니다. |
| 103 | + handleLocalData(data) {} |
| 104 | + |
| 105 | + //디바이스에 값을 씁니다. |
| 106 | + requestLocalData() { |
| 107 | + const queryString = []; |
| 108 | + // ... |
| 109 | + return queryString; |
| 110 | + } |
| 111 | + |
| 112 | + setLanguage() { |
| 113 | + return { |
| 114 | + ko: { |
| 115 | + template: { |
| 116 | + arduinolite_text: '%1', |
| 117 | + arduinolite_get_sensor_number: '%1', |
| 118 | + arduinolite_get_port_number: '%1', |
| 119 | + }, |
| 120 | + Device: { |
| 121 | + arduinolite: '아두이노', |
| 122 | + }, |
| 123 | + Menus: { |
| 124 | + arduinolite: '아두이노', |
| 125 | + }, |
| 126 | + }, |
| 127 | + en: { |
| 128 | + template: { |
| 129 | + arduinolite_text: '%1', |
| 130 | + arduinolite_get_sensor_number: '%1', |
| 131 | + arduinolite_get_port_number: '%1', |
| 132 | + }, |
| 133 | + Device: { |
| 134 | + arduinolite: 'arduinolite', |
| 135 | + }, |
| 136 | + Menus: { |
| 137 | + arduinolite: 'ArduinoLite', |
| 138 | + }, |
| 139 | + }, |
| 140 | + }; |
| 141 | + } |
| 142 | + |
| 143 | + getBlocks() { |
| 144 | + return { |
| 145 | + arduinolite_get_sensor_number: { |
| 146 | + color: EntryStatic.colorSet.block.default.HARDWARE, |
| 147 | + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, |
| 148 | + skeleton: 'basic_string_field', |
| 149 | + statements: [], |
| 150 | + params: [ |
| 151 | + { |
| 152 | + type: 'Dropdown', |
| 153 | + options: [ |
| 154 | + ['0', 'A0'], |
| 155 | + ['1', 'A1'], |
| 156 | + ['2', 'A2'], |
| 157 | + ['3', 'A3'], |
| 158 | + ['4', 'A4'], |
| 159 | + ['5', 'A5'], |
| 160 | + ], |
| 161 | + value: 'A0', |
| 162 | + fontSize: 11, |
| 163 | + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, |
| 164 | + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, |
| 165 | + }, |
| 166 | + ], |
| 167 | + events: {}, |
| 168 | + def: { |
| 169 | + params: [null], |
| 170 | + }, |
| 171 | + paramsKeyMap: { |
| 172 | + PORT: 0, |
| 173 | + }, |
| 174 | + func(sprite, script) { |
| 175 | + return script.getStringField('PORT'); |
| 176 | + }, |
| 177 | + }, |
| 178 | + // ... |
| 179 | + }; |
| 180 | + } |
| 181 | + })(); |
| 182 | +})(); |
| 183 | + |
| 184 | +module.exports = Entry.ArduinoLite; |
| 185 | + |
| 186 | + |
| 187 | + |
| 188 | +``` |
| 189 | + |
| 190 | +- constructor |
| 191 | + - imageName : 샘플파일 이미지와 이름이 같아야 합니다 |
| 192 | + - portData : web-serial api를 사용해 브라우저로 기기와 통신하기위한 세팅값입니다. 연결이 불안정한경우가 아니라면 가급적 수정하지 않는것을 추천드립니다. |
| 193 | + - duration : 기기와 통신하는 간격입니다.(ms단위) |
| 194 | + - blockMenuBlocks : 블럭명세 정보입니다. |
| 195 | + |
| 196 | +- get monitorTemplate() : WS에서 하드웨어 연결시 좌측 ''오브젝트 추가하기'' 하단의 4번째 탭에 보여지는 센서 모니터링 툴을 사용하기 위한 함수입니다. 초기값 세팅 역할을 합니다. |
| 197 | + |
| 198 | +- getMonitorPort() : monitorTemplate()와 마찬가지로 모니터링 툴용 함수입니다. 실시간 값 갱신을 위한 함수입니다. |
| 199 | + |
| 200 | +- setZero() : 연결시작 및 연결해제시 기기상태를 초기화하기 위한 함수입니다. |
| 201 | + |
| 202 | +- handleLocalData(data) : 기기로부터 값을 읽어서 WS에 반영하는 함수입니다. (기기로부터 값 읽음) this.portData.constantServing일 경우 사용합니다. |
| 203 | + |
| 204 | +- requestLocalData() : WS의 블록동작의 명령을 기기에게 쓰는 함수입니다. (기기에 값 쓰기) this.portData.constantServing일 경우 사용합니다. |
| 205 | + |
| 206 | +- getBlocks() : 블럭 상세 정보 및 로직을 반환하는 함수입니다. |
| 207 | + |
| 208 | + |
| 209 | + |
| 210 | +<br> |
| 211 | + |
| 212 | +## 통신방법 |
| 213 | +디바이스와 통신하는 방법에는 아래 2가지가 있습니다. |
| 214 | + |
| 215 | +### 지속 통신 |
| 216 | +- 사용자 액션(블럭 실행)이 없어도 항상 지정된 duration간격만큼 지속통신하는 방법입니다. |
| 217 | +- block_ 모듈명 _lite.js 의 생성자에서 this.duration값을 지정하고, this.portData.constantServing 를 true로 세팅하게되면 handleLocalData(data)함수와 requestLocalData() 함수가 자동으로 실행되게 됩니다. |
| 218 | +- ex. [block_arduino_lite.js](https://github.com/entrylabs/entryjs/blob/develop/src/playground/blocks/hardwareLite/block_arduino_lite.js), [block_sensorboard_lite](https://github.com/entrylabs/entryjs/blob/develop/src/playground/blocks/hardwareLite/block_sensorboard_lite.js), [block_hamster_lite](https://github.com/entrylabs/entryjs/blob/develop/src/playground/blocks/hardwareLite/block_hamster_lite.js) |
| 219 | + |
| 220 | +### 단건 통신 |
| 221 | +- 사용자 액션(블럭 실행)이 있을때만 통신하는 방법입니다. |
| 222 | +- 필요할 때만 통신하므로 일반적으로 지속통신방법보다 부하가 적지만, '계속 반복하기' 블럭 안에서 하드웨어 블럭을 사용하는 것처럼(초당 60번 호출) 단기간에 많은 통신을 할 경우 문제가 발생할 수도 있습니다. |
| 223 | +- Entry.hwLite.serial.sendAsyncWithThrottle(기기에 입력할 값 : buffer | string, 리턴값 여부 | boolean)으로 호출할 수 있습니다. 첫번째 파라미터에는 기기에 입력할 버퍼, 두번째 파라미터를 false로 할 경우 기기로부터 받는 응답값을 받지 않습니다. 상세구조는 Entry.hwLite.serial.sendAsync() 함수를 확인 부탁드립니다. |
| 224 | +- ex. [block_microbit2_lite](https://github.com/entrylabs/entryjs/blob/develop/src/playground/blocks/hardwareLite/block_microbit2_lite.js) |
| 225 | + |
| 226 | +#### Entry.hwLite.serial.sendAsyncWithThrottle |
| 227 | +- Return : 기기로부터 리턴된 value값 |
| 228 | + |
| 229 | +| 파라미터 | 타입 | 선택적 | 설명 | |
| 230 | +| ---------- | ---------------- | ------ | ------------------------------------------------------------ | |
| 231 | +| data | Buffer \| string | | 기기에 송신할 데이터입니다. string 타입일경우 utf8로 인코딩되어 송신됩니다. | |
| 232 | +| isResetReq | boolean | ✔️ | 데이터를 송신한 이후에 기기로부터 응답을 받지 않고 함수를 종료합니다. | |
| 233 | +| callback | Function | ✔️ | 함수가 존재할경우, 송수신 완료 후 callback(value)값을 리턴합니다. | |
| 234 | + |
| 235 | + |
| 236 | +<br> |
| 237 | + |
| 238 | +## 기타 웹연결 관련 함수들 |
| 239 | +### Entry.playground.addHardwareLiteModule |
| 240 | +웹연결에 사용할 모듈을 선택하는 함수입니다. |
| 241 | +파라미터로 Entry.모듈명(ex. Entry.Neobot)을 넣으면 해당 하드웨어가 선택됩니다. |
| 242 | +실제 운영 엔트리WS에서는 '브라우저로 연결하기' => '팝업에서 모듈 선택 후 불러오기' 까지 진행하면 자동으로 이 함수가 실행되지만, entryjs만 사용한 개발환경에서는 위 팝업을 사용할 수 없기 때문에 직접 함수를 실행시켜주셔야 합니다. |
| 243 | + |
| 244 | + |
| 245 | + |
| 246 | +| 파라미터 | 타입 | 선택적 | 설명 | |
| 247 | +| -------- | ------------------------------------------------------------ | ------ | ------------------------------- | |
| 248 | +| module | [EntryHardwareBlockModule](https://github.com/entrylabs/entryjs/blob/edb5380602a0f035fb2b20eb9d2b7c8f1247f15d/types/index.d.ts#L180) | | 웹연결에 사용할 모듈객체입니다. | |
| 249 | + |
| 250 | + |
| 251 | + |
| 252 | +### Entry.hwLite.connect |
| 253 | +웹연결 연결실행 함수입니다. |
| 254 | + |
| 255 | +### Entry.hwLite.disconnect |
| 256 | +웹연결 연결해제 함수입니다. 가급적 직접 호출보다는 '연결 해제하기' 버튼을 사용해주세요. |
| 257 | + |
| 258 | +### Entry.hwLite.serial.handleConnectErrorInEngineRun |
| 259 | +연결중 기기가 멈추거나 화면이 멈추는 등, 강제종료가 필요한 상황에 사용하는 함수입니다. |
| 260 | + |
| 261 | +### Entry.hwLite.getConnectFailedMenu |
| 262 | +연결실패화면을 출력해야 할 때 사용합니다. |
| 263 | + |
| 264 | + |
| 265 | + |
| 266 | +<br> |
| 267 | + |
| 268 | +## 테스트하기 |
| 269 | +다음 2가지 방법으로 테스트하실수 있습니다. |
| 270 | + |
| 271 | +A. entryjs와 entry-tool을 함께 적용하고 계시다면 어려움없이 하드웨어 탭에서 '브라우저로 연결하기' > 모듈선택 > 포트선택으로 테스트하실수 있습니다. |
| 272 | +B. entryjs에서 yarn serve만으로 테스트하고 계시다면, 아래 순서로 진행해주세요 |
| 273 | +- 크롬 개발자도구에서 `Entry.playground.addHardwareLiteModule(Entry.모듈클래스명);`를 입력합니다. 사용자가 팝업창에서 해당모듈을 선택했을때 실행되는 동작입니다. |
| 274 | +- 크롬 개발자도구에서 `Entry.hwLite.connect();`를 입력합니다. 사용자가 연결하기 버튼을 클릭할때 실행되는 동작입니다. |
| 275 | +- 모듈이 연결된 포트를 선택하고 완료를 누르면 블록이 출력됩니다. |
| 276 | + |
| 277 | +혹시 연결이 정상적으로 진행되지 않는다면 Entry.모듈명과 Entry.HARDWARE_LITE_LIST['모듈ID'] 이 존재하는지 확인 부탁드립니다, 둘중 하나라도 없다면 정상동작하지 않습니다. 모듈의 name, id 등을 체크해주세요. |
| 278 | + |
0 commit comments