├── README.md ├── clients ├── scratch │ ├── projects │ │ ├── StarterProject_FR_v5.sb2 │ │ ├── Xi4S_Starter_Project.sb2 │ │ └── blink.sbx │ └── xi4s │ │ ├── xi4s.js │ │ └── xi4s_fr4.js └── snap! │ ├── blockDescriptors │ ├── analogRead.txt │ ├── analogWrite.txt │ ├── digitalRead.txt │ ├── digitalWrite.txt │ ├── infraredDistance.txt │ ├── moveContinuousServo.txt │ ├── moveStandardServo.txt │ ├── moveStepper.txt │ ├── noTone.txt │ ├── pinMode.txt │ ├── playTone.txt │ ├── setBoardIpAddress1.txt │ ├── setBoardIpAddress2.txt │ ├── setDebugLevel.txt │ ├── setPinMode.txt │ ├── setPins4DriverStepper.txt │ ├── setPins4WireStepper.txt │ ├── sonar.txt │ ├── stopServo.txt │ └── stopStepper.txt │ └── projects │ └── xi4snap_002.xml ├── documentation ├── Xi_Install_and_Usage_Guide.pdf ├── Xi_TranslationGuide.pdf ├── drawings │ ├── BBB_PiezoWiring.png │ ├── BBB_infraredWiring.png │ ├── BBBservoWiring.png │ ├── RPiServoWiring.png │ ├── RPiServoWiring2.png │ ├── StatusTable_31Dec14.png │ ├── XiLogo.png │ └── xi2.png ├── xi.ico ├── xi.png ├── xi.xpm └── xi3.png └── servers ├── xibone ├── xibone.sh └── xiboneInstall.sh ├── xiduino ├── linux │ ├── xiduino.sh │ ├── xiduino4Snap.sh │ └── xiduinoInstall.sh └── windows │ ├── xiduino.bat │ ├── xiduino4Snap.bat │ └── xiduinoInstall.bat ├── xipi ├── xipi.sh └── xipiInstall.sh └── xiserver └── xiserver.js /README.md: -------------------------------------------------------------------------------- 1 | # This repo is now archived. The xi concept has been incorporated into the [Banyan project](https://mryslab.github.io/python_banyan/) with [JavaScript support](https://github.com/MrYsLab/js-banyan). 2 | 3 | Xi 4 | ====== 5 | ## The Cross-Platform Interconnect 6 | 7 | ![ScreenShot](https://raw.github.com/MrYsLab/Xi/master/documentation/drawings/XiLogo.png) 8 | 9 | Xi allows you to simultaneously connect, monitor and control multiple Arduino, 10 | BeagleBone Black and Raspberry Pi boards using Scratch 2.0 or Snap! as a graphical user interface. 11 | 12 | Installation instructions are provided in the Installation and Usage Guide located in the documentation directory. 13 | 14 | French Block and Alert Translations provided by Seb Canet 15 | 16 | Translators Guide can be found in the Documentation Directory. 17 | 18 | ### Known Limitations Imposed By The Snap! Team: 19 | None 20 | 21 | ### Known Limitations Imposed By The Scratch Team: 22 | 23 | Currently Xi works only on Chrome for Ubuntu (and perhaps other linux flavors). After launching the Xi server for your board, go to: 24 | [http://scratchx.org/?url=http://MrYsLab.github.io/xi4s.js](http://scratchx.org/?url=http://MrYsLab.github.io/xi4s.js) 25 | 26 | This is the new Scratch Extension Web Site. After it launches, wait a moment and a dialog box will appear to allow 27 | you to continue with loading the extension. 28 | 29 | Accept the warning and the Xi blocks will load. 30 | 31 | You may see another warning stating there is an extension problem and that a plug-in needs to be loaded. Just click OK to dismiss 32 | this dialog box. Xi does not use the Scratch plugins, but uses the Xi servers. 33 | 34 | For a French translation use this URL: 35 | [http://scratchx.org/?url=http://MrYsLab.github.io/xi4s_fr4.js](http://scratchx.org/?url=http://MrYsLab.github.io/xi4s_fr4.js) 36 | 37 | ### Arduino/Device Wiring Information: 38 | 1. [HC-SR04 SONAR Distance Device](https://github.com/rwaldron/johnny-five/blob/master/docs/ping.md) 39 | 2. [Infrared Distance Sensor - GP2Y0A21YK0F -- Black Wire to ground, White wire to Analog Input Pin](https://www.adafruit.com/products/164) 40 | 3. [4 Wire Stepper Motor](https://learn.adafruit.com/adafruit-arduino-lesson-16-stepper-motors/breadboard-layout) 41 | 42 | ### BeageBone Black/Device Wiring Information: 43 | 1. [Infrared Distance Sensor](https://raw.github.com/MrYsLab/Xi/master/documentation/drawings/BBB_infraredWiring.png) 44 | 2. [Piezo (Tone) Actuator](https://raw.github.com/MrYsLab/Xi/master/documentation/drawings/BBB_PiezoWiring.png) 45 | 3. [Servo Motor (Standard and Continuous)](https://raw.github.com/MrYsLab/Xi/master/documentation/drawings/BBBservoWiring.png) 46 | 47 | 48 | NOTE: BeagleBone-IO package needs to be updated to the latest release for Servo support. To update the node modules 49 | to the latest versions, issue the following command while in the xibone directory: 50 | 51 | sudo npm update beaglebone-io 52 | 53 | 54 | ### Raspberry Pi/Device Wiring Information: 55 | 1. [Servo Motor (Standard and Continuous)](https://raw.github.com/MrYsLab/Xi/master/documentation/drawings/RPiServoWiring.png) 56 | 2. [Single Power Supply Servo Motor (Standard and Continuous)](https://raw.github.com/MrYsLab/Xi/master/documentation/drawings/RPiServoWiring2.png) 57 | 58 | NOTE: Raspberry-IO package needs to be updated to the latest release for Servo support. To update the node modules 59 | to the latest versions, issue the following command while in the xipi directory: 60 | 61 | sudo npm rm raspi-io 62 | sudo npm install raspi-io 63 | 64 | The pin numbering scheme for the Raspberry Pi follows the wiringPI pin numbering scheme. So for PWM or Servo, the Pin number to 65 | use in the Scratch Block would be 1, even though the physical pin number is 12. 66 | 67 | Please refer to the [Raspi-IO Pin Map](https://github.com/bryan-m-hughes/raspi-io/wiki). 68 | 69 | 70 | ### Current Board/Device Support Table 71 | ![ScreenShot](https://raw.github.com/MrYsLab/Xi/master/documentation/drawings/StatusTable_31Dec14.png) 72 | -------------------------------------------------------------------------------- /clients/scratch/projects/StarterProject_FR_v5.sb2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrYsLab/xi/2a5fbb47fce51498e115f94ae3d63d59b28937a1/clients/scratch/projects/StarterProject_FR_v5.sb2 -------------------------------------------------------------------------------- /clients/scratch/projects/Xi4S_Starter_Project.sb2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrYsLab/xi/2a5fbb47fce51498e115f94ae3d63d59b28937a1/clients/scratch/projects/Xi4S_Starter_Project.sb2 -------------------------------------------------------------------------------- /clients/scratch/projects/blink.sbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrYsLab/xi/2a5fbb47fce51498e115f94ae3d63d59b28937a1/clients/scratch/projects/blink.sbx -------------------------------------------------------------------------------- /clients/scratch/xi4s/xi4s.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4s * * * 4 | * 5 | ********************************************************************************************* 6 | * Created by afy on 8/24/14. 7 | * 8 | * This is the Xi Client for Scratch 9 | * 10 | * It follows the Scratch JavaScript Extension Spec 11 | * 12 | * http://llk.github.io/scratch-extension-docs/ 13 | * 14 | * Version v.002 15 | * Nov 7, 2014 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | new (function () { 37 | var ext = this; 38 | console.log('Xi4s v.003'); 39 | 40 | // 0 = no debug 41 | // 1 = low level debug 42 | // 2 = high - open the floodgates 43 | // Variable is set by user through a Scratch command block 44 | var debugLevel = 0; 45 | 46 | // a variable to set the color of the 'LED' indicator for the extension on the Scratch editor 47 | var boardStatus = 0; // 0:not ready(RED), 1:partially ready or warning(YELLOW), 2: fully ready(GREEN) 48 | 49 | // Board IP addresses and ports are set by the user with a Scratch command block, and the information 50 | // is stored in the WebSocket Array 51 | 52 | // WebSocket array 53 | // Each board will have an associated WebSocket instance used for communication 54 | // with the Xi client. webSocketsArray is an array of objects. Each object has a format of: 55 | // {id: BoardID, , ip: IPAddress, port: port, ws: WebSocketReference} 56 | 57 | var webSocketsArray = []; 58 | 59 | // Sensor Data Array 60 | // This is an array of objects that store the latest sensor or switch value updates received from all Xi servers. 61 | // The object format is: 62 | // key: generated by genReporterKey() 63 | // value: latest updated value 64 | // A key is used to uniquely identify each data entry with a specific sensor on a specific server 65 | var sensorDataArray = []; 66 | 67 | /********************* asynchronous messages from scratch ****************************************/ 68 | // Cleanup function when the extension is unloaded 69 | ext._shutdown = function () { 70 | //send a 'resetBoard' message to each board 71 | for (var index = 0; index < webSocketsArray.length; index++) { 72 | if (debugLevel >= 2) { 73 | console.log('Sending reset to board index ' + index); 74 | } 75 | webSocketsArray[index].ws.send('resetBoard'); 76 | } 77 | }; 78 | 79 | // Status reporting code - part of boilerplate provided by Scratch 80 | // Set the 'LED' on the Scratch Editor 81 | ext._getStatus = function () { 82 | return { 83 | status: boardStatus, 84 | msg: 'Ready' 85 | }; 86 | }; 87 | 88 | /*****************************************************************************************************/ 89 | /*********************************** Scratch Program Block Handlers, ******************************/ 90 | /*****************************************************************************************************/ 91 | 92 | // Associate a handler for each block described in the blocks section below 93 | 94 | /******************************* 95 | **** Command Block Handlers **** 96 | *******************************/ 97 | 98 | // Accepts IP Address and Port information for each board that the user adds 99 | // The associated scratch block is a 'wait' command block. 100 | // We don't want Scratch to continue until the socket is open bidirectionally. 101 | // When socket.onopen is called the callback is returned so that scratch can proceed processing 102 | ext.setBoard = function (boardID, ipAddress, port, callback) { 103 | var timeoutID; // need to set a timeout when a socket is created because we are using a 'wait' block 104 | 105 | 106 | if (debugLevel >= 1) 107 | console.log('setBoard: ' + boardID, ipAddress, port); 108 | 109 | // Check to make sure that this board was not entered previously 110 | for (var index = 0; index < webSocketsArray.length; index++) { 111 | if (webSocketsArray[index].id === boardID) { 112 | // allow user to reset the board to the same value - for stop and start 113 | if ((webSocketsArray[index].ip != ipAddress) || (webSocketsArray[index].port != port)) { 114 | createAlert(12, boardID); 115 | callback(); // release the scratch wait block 116 | return; // no need to go further 117 | } 118 | } 119 | } 120 | // This is a confirmed unique entry. Create a websocket for this board 121 | var socket = new WebSocket('ws://' + ipAddress + ':' + port); 122 | 123 | // add the entry including the websocket reference just created 124 | webSocketsArray.push({'id': boardID, 'ip': ipAddress, 'port': port, 'ws': socket}); 125 | 126 | // start the timer for a server reply - we wait for up to 2 seconds for the reply 127 | timeoutID = window.setTimeout(noServerAlert, 2000); 128 | 129 | 130 | // attach an onopen handler to this socket. This message is sent by a servers websocket 131 | socket.onopen = function (event) { 132 | window.clearTimeout(timeoutID); 133 | if (debugLevel >= 1) 134 | console.log('onopen message received'); 135 | // change the board status to green with the first board added, since we don't know ahead of time 136 | // how many boards are attached 137 | boardStatus = 2; 138 | socket.send('Xi4sOnline'); 139 | callback(); // tell scratch to proceed processing 140 | }; 141 | 142 | function noServerAlert() { 143 | createAlert(20, boardID); 144 | boardStatus = 0; 145 | } 146 | 147 | /********************** websocket 'onmessage' handler *************************************/ 148 | // 149 | // All messages sent from board's socket are handled here. 150 | // Attach an onmessage event handler to this socket. 151 | // Process messages received from the server associated with this socket. 152 | socket.onmessage = function (message) { 153 | if (debugLevel === 1) 154 | console.log('onmessage received: ' + message.data); 155 | 156 | // All message components are delimited with '/' character. 157 | // TODO: Should this be done with JSON? 158 | 159 | // Incoming messages are split into their component pieces and placed into a 'msg' array 160 | // msg[0] for each message is the message ID. 161 | var msg = message.data.split('/'); 162 | 163 | // process each message ID 164 | switch (msg[0]) { 165 | // dataUpdate - server data update data message 166 | case 'dataUpdate': 167 | var index = msg[1]; // unique value used as an index into sensorDataArray 168 | var data = msg[2]; // data value to be entered into sensorDataArray 169 | if (debugLevel >= 2) 170 | console.log('sensorData: index = ' + index + ' data = ' + data); 171 | // update the array with the new value 172 | sensorDataArray[index].value = data; 173 | break; 174 | 175 | /*************************************** 176 | ************** server detected errors 177 | ****************************************/ 178 | 179 | // server detected a problem in setting the mode of this pin 180 | case 'invalidSetMode': 181 | case 'invalidPinCommand': 182 | console.log('invalid alerts:' + 'index: ' + msg[1] + 'board: ' + msg[2] + 'pin: ' + msg[3]); 183 | createAlert(msg[1], msg[2], msg[3]); 184 | break; 185 | default: 186 | if (debugLevel >= 1) 187 | console.log('onmessage unknown message received'); 188 | } 189 | }; 190 | }; 191 | 192 | // Set the pin mode command block handler 193 | ext.pinMode = function (boardID, pin, mode) { 194 | if (debugLevel >= 1) 195 | console.log('Set Pin Mode - board: ' + boardID + ' Mode: ' + mode + ' Pin: ' + pin); 196 | // make sure the websocket for the board was previously established 197 | for (var index = 0; index < webSocketsArray.length; index++) { 198 | if (webSocketsArray[index].id === boardID) { 199 | // send message to server to create device(input devices) or set the pin mode (output device) 200 | var messageToServer; // message to be sent to server 201 | mode = extractMode(mode); 202 | 203 | // the mode is the value prescribed in block descriptor section 204 | switch (mode) { 205 | // set pin to digital input mode 206 | // msg: setDigitalIN - digital service input 207 | case 'Digital Input': 208 | if (debugLevel >= 1) 209 | console.log('pin mode digital input'); 210 | // build the message to send to the Xi Server 211 | // we use the length of the array as the index/device id 212 | messageToServer = 'setDigitalIN/' + boardID + '/' + pin + '/' + sensorDataArray.length; 213 | sendSetInputPinRequest(messageToServer, 'd', boardID, pin, index); 214 | break; 215 | // set pin to digital out 216 | // msg: setDigitalOUT 217 | case 'Digital Output': 218 | if (debugLevel >= 1) 219 | console.log('pin mode digital output'); 220 | messageToServer = 'setDigitalOUT/' + boardID + '/' + pin; 221 | if (debugLevel >= 2) 222 | console.log('pinMode Digital Out Msg to server: ' + messageToServer); 223 | webSocketsArray[index].ws.send(messageToServer); 224 | break; 225 | // set pin to analog in 226 | // msg: setAnalogIN 227 | case 'Analog Sensor Input': 228 | if (debugLevel >= 1) 229 | console.log('pin mode analog input'); 230 | // build the message to send to the Xi Server 231 | // we use the length of the array as the index/device id 232 | messageToServer = 'setAnalogIN/' + sensorDataArray.length + '/' + boardID + '/' + pin; 233 | sendSetInputPinRequest(messageToServer, 'a', boardID, pin, index); 234 | break; 235 | // set pin mode PWM 236 | // msg: setAnalogOUT 237 | case 'Analog (PWM) Output': 238 | if (debugLevel >= 1) 239 | console.log('pin mode PWM'); 240 | // send out the pwm mode message 241 | // the host tests if the pin is PWM and if not will send back an 'xp' exception message 242 | messageToServer = 'setAnalogOUT/' + boardID + '/' + pin; 243 | if (debugLevel >= 2) 244 | console.log('pinMode PWM Out Msg to server: ' + messageToServer); 245 | webSocketsArray[index].ws.send(messageToServer); 246 | break; 247 | case 'Standard Servo (PWM)': 248 | if (debugLevel >= 1) 249 | console.log('pin mode SERVO'); 250 | // send out the servo mode message 251 | messageToServer = 'setStandardServoMode/' + boardID + '/' + pin; 252 | if (debugLevel >= 2) 253 | console.log('pinMode Standard Servo Out Msg to server: ' + messageToServer); 254 | webSocketsArray[index].ws.send(messageToServer); 255 | break; 256 | case 'Continuous Servo (PWM)': 257 | if (debugLevel >= 1) 258 | console.log('pin mode SERVO'); 259 | // send out the servo mode message 260 | messageToServer = 'setContinuousServoMode/' + boardID + '/' + pin; 261 | if (debugLevel >= 2) 262 | console.log('pinMode ContinuousServo Out Msg to server: ' + messageToServer); 263 | webSocketsArray[index].ws.send(messageToServer); 264 | break; 265 | case 'SONAR Distance - (Digital In)': 266 | createAlert(13); 267 | messageToServer = 'setSonarMode/' + boardID + '/' + pin + '/' + sensorDataArray.length; 268 | if (debugLevel >= 2) 269 | console.log('pinMode Sonar Out Msg to server: ' + messageToServer); 270 | sendSetInputPinRequest(messageToServer, 'd', boardID, pin, index); 271 | break; 272 | case 'Infrared Distance (GP2Y0A21YK) - (Analog In)': 273 | messageToServer = 'setInfraRedDistanceMode/' + boardID + '/' + pin + '/' + sensorDataArray.length; 274 | if (debugLevel >= 2) 275 | console.log('pinMode infrared distance Out Msg to server: ' + messageToServer); 276 | sendSetInputPinRequest(messageToServer, 'a', boardID, pin, index); 277 | break; 278 | case 'Tone (Piezo)- (Digital Out)': 279 | if (debugLevel >= 1) 280 | console.log('pin mode Tone'); 281 | // send out the servo mode message 282 | messageToServer = 'setToneMode/' + boardID + '/' + pin; 283 | if (debugLevel >= 2) 284 | console.log('pinMode Tone Mode Out Msg to server: ' + messageToServer); 285 | webSocketsArray[index].ws.send(messageToServer); 286 | break; 287 | default: 288 | if (debugLevel >= 1) 289 | console.log('ext.pinMode: Unknown mode - ', +mode); 290 | } 291 | // just return from here after processing the command 292 | return; 293 | } 294 | } 295 | // board not yet established 296 | createAlert(14, boardID) 297 | }; 298 | 299 | 300 | // Digital output command block 301 | ext.digitalWrite = function (board, pin, value) { 302 | if (debugLevel >= 1) { 303 | console.log('digitalWrite Board: ' + board + ' Pin ' + pin + ' Value ' + value); 304 | } 305 | // strip index number off of message to determine value to send to server 306 | value = extractOffOn(value); 307 | var msg = 'digitalWrite/' + board + '/' + pin + '/' + value; 308 | sendCommand(msg, board, 'digitalWrite'); 309 | }; 310 | 311 | // PWM output (analog write) command block 312 | ext.analogWrite = function (board, pin, value) { 313 | var msg = 'analogWrite/' + board + '/' + pin + '/' + value; 314 | sendCommand(msg, board, 'analogWrite'); 315 | }; 316 | 317 | // set servo position to position in degrees 318 | ext.moveStandardServo = function (board, pin, degrees, inversion) { 319 | inversion = extractInversion(inversion); 320 | var msg = 'moveStandardServo/' + board + '/' + pin + '/' + degrees + '/' + inversion; 321 | sendCommand(msg, board, 'moveStandardServo'); 322 | }; 323 | 324 | // set servo position to position in degrees 325 | ext.moveContinuousServo = function (board, pin, direction, inversion, speed) { 326 | inversion = extractInversion(inversion); 327 | direction = extractDirection(direction); 328 | var msg = 'moveContinuousServo/' + board + '/' + pin + '/' + direction + '/' + inversion + '/' + speed; 329 | sendCommand(msg, board, 'moveContinuousServo'); 330 | }; 331 | 332 | // stop servo 333 | ext.stopServo = function (board, pin) { 334 | var msg = 'stopServo/' + board + '/' + pin; 335 | sendCommand(msg, board, 'stopServo'); 336 | }; 337 | 338 | // stop stepper 339 | ext.stopStepper = function (board, pin) { 340 | var msg = 'stopStepper/' + board + '/' + pin; 341 | sendCommand(msg, board, 'stopStepper'); 342 | }; 343 | 344 | ext.fourWireStepperPins = function (board, pinA, pinB, pinC, pinD, stepsPerRev) { 345 | 346 | createAlert(15); 347 | 348 | var pinArray = []; 349 | pinArray.push(pinA); 350 | pinArray.push(pinB); 351 | pinArray.push(pinC); 352 | pinArray.push(pinD); 353 | 354 | // check for 4 unique values 355 | var unique = pinArray.filter(onlyUnique); 356 | if (unique.length !== 4) { 357 | createAlert(16); 358 | return; 359 | } 360 | var msg = 'fourWireStepperPins/' + board + '/' + pinA + '/' + pinB + '/' + pinC + '/' + pinD + '/' + stepsPerRev; 361 | sendCommand(msg, board, 'fourWireStepperPins'); 362 | }; 363 | 364 | ext.stepperDriverPins = function (board, pinA, pinB, stepsPerRev) { 365 | 366 | createAlert(15); 367 | 368 | var pinArray = []; 369 | pinArray.push(pinA); 370 | pinArray.push(pinB); 371 | 372 | 373 | // check for 2 unique values 374 | var unique = pinArray.filter(onlyUnique); 375 | if (debugLevel >= 2) 376 | console.log('stepperDriverPins unique = ' + unique); 377 | 378 | if (debugLevel >= 2) 379 | console.log('stepperDriverPins unique length = ' + unique.length); 380 | 381 | if (unique.length !== 2) { 382 | createAlert(17); 383 | } 384 | var msg = 'stepperDriverPins/' + board + '/' + pinA + '/' + pinB + '/' + stepsPerRev; 385 | sendCommand(msg, board, 'stepperDriverPins'); 386 | }; 387 | 388 | ext.moveStepper = function (board, pin, rpm, direction, accel, decel, steps) { 389 | direction = extractDirection(direction); 390 | var msg = 'moveStepper/' + board + '/' + pin + '/' + rpm + '/' + direction + '/' + accel + '/' + decel + '/' + steps; 391 | sendCommand(msg, board, 'moveStepper'); 392 | }; 393 | 394 | // send command to play a tone 395 | ext.playTone = function (board, pin, frequency, duration) { 396 | var msg = 'playTone/' + board + '/' + pin + '/' + frequency + '/' + duration; 397 | sendCommand(msg, board, 'playTone'); 398 | }; 399 | 400 | // turn tone off 401 | ext.noTone = function (board, pin) { 402 | var msg = 'noTone/' + board + '/' + pin; 403 | sendCommand(msg, board, 'noTone'); 404 | }; 405 | 406 | // Set the debug level 407 | ext.setDebugLevel = function (level) { 408 | debugLevel = level; 409 | }; 410 | 411 | 412 | /******************************* 413 | **** Response Block Handlers **** 414 | *******************************/ 415 | 416 | // retrieve digital data from sensorDataArray 417 | ext.getDigitalInputData = function (board, pin) { 418 | if (debugLevel >= 1) 419 | console.log('Digital Input - board: ' + board + ' Pin: ' + pin); 420 | var key = genReporterKey(board, pin, 'd'); 421 | return retrieveReporterData(board, pin, key); 422 | }; 423 | 424 | // retrieve analog data data from sensorDataArray 425 | ext.getAnalogSensorData = function (board, pin) { 426 | if (debugLevel >= 1) 427 | console.log('Analog Sensor Input - board: ' + board + ' Pin: ' + pin); 428 | 429 | // generate a key for sensorDataArray 430 | var key = genReporterKey(board, pin, 'a'); 431 | return retrieveReporterData(board, pin, key); 432 | }; 433 | 434 | 435 | // retrieve sonar data 436 | ext.getSonarData = function (board, units, pin) { 437 | if (debugLevel >= 1) 438 | console.log('getSonarData - board: ' + board + 'Units' + units + ' Pin: ' + pin); 439 | 440 | // generate a key for sensorDataArray 441 | var key = genReporterKey(board, pin, 'd'); 442 | var distance = retrieveReporterData(board, pin, key); 443 | units = extractDistance(units); 444 | if (units === 'CM') { 445 | return (distance * 2.54).toFixed(2); 446 | } 447 | else { 448 | return distance; 449 | } 450 | }; 451 | 452 | // retrieve infrared distance data 453 | ext.getInfraredDistanceData = function (board, units, pin) { 454 | if (debugLevel >= 1) 455 | console.log('getInfraredDistanceData - board: ' + board + 'Units' + units + ' Pin: ' + pin); 456 | 457 | // generate a key for sensorDataArray 458 | var key = genReporterKey(board, pin, 'a'); 459 | var distance = retrieveReporterData(board, pin, key); 460 | units = extractDistance(units); 461 | if (units === 'CM') { 462 | return (distance * 2.54).toFixed(2); 463 | } 464 | else { 465 | return distance; 466 | } 467 | }; 468 | 469 | 470 | // helper functions 471 | 472 | //genReporterKey 473 | // Input: Board number 474 | // Pin number 475 | // Designator to differentiate between analog and digital - either 'a' or 'd' 476 | // 477 | // Returns the generated key 478 | function genReporterKey(boardNum, pinNum, designator) { 479 | if (debugLevel >= 1) 480 | console.log('genReporterKey returns: ' + boardNum + designator + pinNum); 481 | return boardNum + designator + pinNum; 482 | } 483 | 484 | // Using the supplied key, this function will retrieve the latest data from the sensorDataArray. 485 | function retrieveReporterData(board, pin, key) { 486 | if (debugLevel >= 1) { 487 | console.log('retrieveReporterData: board: ' + board + ' pin ' + pin + ' key ' + key); 488 | } 489 | // make sure that this is a unique key in the array 490 | for (var index = 0; index < sensorDataArray.length; index++) { 491 | if (sensorDataArray[index].key === key) { 492 | return sensorDataArray[index].value 493 | } 494 | } 495 | // did not find an entry in the array 496 | createAlert(18, board, pin); 497 | ext._shutdown(); 498 | } 499 | 500 | // This function will format a set input pin message (analog or digital) and send it to the server 501 | function sendSetInputPinRequest(msgToServer, analogOrDigital, board, pin, wsIndex) { 502 | var reporterArrayEntry = {key: null, data: -1}; // The entry we build to add to the sensorDataArray 503 | 504 | // generate a key so that we can use to retrieve the data from the reporterArrayEntry 505 | reporterArrayEntry.key = genReporterKey(board, pin, analogOrDigital); 506 | 507 | if (debugLevel >= 1) 508 | console.log('sendInputPinRequest generated key = ' + reporterArrayEntry.key); 509 | 510 | var found = false; 511 | // make sure that this is a unique key in the array 512 | for (var index = 0; index < sensorDataArray.length; index++) { 513 | if (sensorDataArray[index].key === reporterArrayEntry.key) { 514 | found = true; 515 | console.log("sendInputPinReq entry exists"); 516 | } 517 | } 518 | 519 | 520 | // it is unique so go ahead and add the record to the array 521 | if (found === false) { 522 | sensorDataArray.push(reporterArrayEntry); 523 | //} 524 | // now we can safely send the set pin message to the Xi Server to create the device 525 | if (debugLevel >= 1) 526 | console.log('sendInputPinRequest: msg = ' + msgToServer + ' index = ' + wsIndex); 527 | webSocketsArray[wsIndex].ws.send(msgToServer); 528 | } 529 | } 530 | 531 | // This function will check to see if a board has been established and if it has, will send a command 532 | // message to the server 533 | function sendCommand(msg, board, type) { 534 | if (debugLevel >= 1) { 535 | console.log('sendCommand: ' + msg + ' ' + board + ' ' + type); 536 | } 537 | for (var index = 0; index < webSocketsArray.length; index++) { 538 | if (webSocketsArray[index].id === board) { 539 | if (debugLevel >= 2) 540 | console.log('sendCommand: Message: ' + msg + ' board: ' + board); 541 | // send out message 542 | webSocketsArray[index].ws.send(msg); 543 | return; 544 | } 545 | } 546 | // board was not established 547 | createAlert(19, board); 548 | } 549 | 550 | // return unique values contained within an array 551 | function onlyUnique(value, index, self) { 552 | return self.indexOf(value) === index; 553 | } 554 | 555 | // strip off the text to accommodate translations 556 | function extractOffOn(value) { 557 | var offOn = value.split('.'); 558 | if (offOn[0] === '1') { 559 | return "Off" 560 | } 561 | else { 562 | return "On"; 563 | } 564 | } 565 | 566 | function extractDirection(direction) { 567 | var dirArray = direction.split('.'); 568 | if (dirArray[0] === '1') { 569 | return "CW"; 570 | } 571 | else { 572 | return "CCW"; 573 | } 574 | } 575 | 576 | function extractDistance(distance) { 577 | var distArray = distance.split('.'); 578 | if (distArray[0] === '1') { 579 | return "CM"; 580 | } 581 | else { 582 | return "Inches"; 583 | } 584 | } 585 | 586 | function extractInversion(inversion) { 587 | var invArray = inversion.split('.'); 588 | if (invArray[0] === '1') { 589 | return "False"; 590 | } 591 | else { 592 | return "True"; 593 | } 594 | } 595 | 596 | function extractMode(mode) { 597 | var modeArray = mode.split('.'); 598 | var serverMode = undefined; 599 | switch (modeArray[0]) { 600 | case '1': 601 | serverMode = 'Digital Input'; 602 | break; 603 | case '2': 604 | serverMode = 'Digital Output'; 605 | break; 606 | case '3': 607 | serverMode = 'Analog Sensor Input'; 608 | break; 609 | case '4': 610 | serverMode = 'Analog (PWM) Output'; 611 | break; 612 | case '5': 613 | serverMode = 'Standard Servo (PWM)'; 614 | break; 615 | case '6': 616 | serverMode = 'Continuous Servo (PWM)'; 617 | break; 618 | case '7': 619 | serverMode = 'Infrared Distance (GP2Y0A21YK) - (Analog In)'; 620 | break; 621 | case '8': 622 | serverMode = 'SONAR Distance - (Digital In)'; 623 | break; 624 | case '9': 625 | serverMode = 'Tone (Piezo)- (Digital Out)'; 626 | break; 627 | default: 628 | console.log("extract mode unknown mode = " + modeArray[0]); 629 | } 630 | return serverMode; 631 | } 632 | 633 | function createAlert(index, board, pin) { 634 | console.log('createAlert' + index, board, pin); 635 | var alertStrings = [ 636 | // 0 637 | "exceeds Maximum Number of Pins on Board.", 638 | // 1 639 | "does not support the requested mode.", 640 | //2 641 | "was not configured for digital write.", 642 | //3 643 | "was not configured for analog write.", 644 | //4 645 | "was not configured for TONE OUTPUT Control.", 646 | //5 647 | "was not configured for Servo Control.", 648 | //6 649 | "was not configured for Standard Servo Control.", 650 | //7 651 | "was not configured for Continuous Servo Control.", 652 | //8 653 | "was not configured for Stepper Control.", 654 | //9 655 | "this pin has already been assigned.", 656 | //10 657 | "Speed must be in the range of 0.0 to 1.0.", 658 | //11 659 | "does not support analog operation", 660 | //12 661 | "An IP entry already exists for this board.", 662 | //13 663 | "If you are using an Arduino, this feature requires a special version of StandardFirmata." + 664 | "See: https://github.com/rwaldron/johnny-five/wiki/Sonar for details.", 665 | //14 666 | "IP address must be set before a board is used", 667 | //15 668 | "If you are using an Arduino, this feature requires a special version of StandardFirmata." + 669 | "See: https://github.com/soundanalogous/AdvancedFirmata for details.", 670 | //16 671 | "The Four Pin Values Must Be Unique. Try Again!", 672 | //17 673 | "The Two Pin Values Must Be Unique. Try Again!", 674 | //18 675 | "Pin Mode was not set. ", 676 | //19 677 | "IP Address for this board was not set.", 678 | //20 679 | "Server not responding. Did you start XiServer for this board?" + 680 | "Please start the server, reload this page and try again" 681 | ]; 682 | 683 | var headerKeywords = { 684 | board: "Board: ", 685 | pin: "Pin: " 686 | }; 687 | 688 | var alertInfo = ""; 689 | if (board != undefined) { 690 | alertInfo = headerKeywords.board + board; 691 | } 692 | if (pin != undefined) { 693 | alertInfo = alertInfo + ' ' + headerKeywords.pin + pin; 694 | } 695 | alertInfo += " "; 696 | 697 | alert(alertInfo + alertStrings[index]); 698 | 699 | } 700 | 701 | 702 | // Block and block menu descriptions 703 | var descriptor = { 704 | blocks: [ 705 | ['w', 'Board %m.bdNum IPAddress/Port: %s : %s', 'setBoard', '1', 'localhost', '1234'], 706 | [' ', 'Board: %m.bdNum Set Pin %n as %m.pinMode', 'pinMode', '1', '2', '1. Digital Input'], 707 | [' ', 'Board: %m.bdNum Digital Write Pin %n = %m.onOff ', 'digitalWrite', '1', '2', '1. Off'], 708 | [' ', 'Board: %m.bdNum Analog Write(PWM) Pin %n = %n', 'analogWrite', '1', '3', '128'], 709 | [' ', 'Board: %m.bdNum Move Standard Servo On Pin %n To %n Degrees - Inverted %m.inversion', 710 | 'moveStandardServo', '1', '3', '90', '1. False'], 711 | [' ', 'Board: %m.bdNum Move Continuous Servo On Pin: %n Dir: %m.motorDirection Inverted %m.inversion Servo Speed (0.0 - 1.0) %n ', 712 | 'moveContinuousServo', '1', '3', '1. CW', '1. False', '.5'], 713 | [' ', 'Board: %m.bdNum Servo Stop! Pin: %n', 'stopServo', '1', '3'], 714 | [' ', 'Board: %m.bdNum Play Tone on Pin: %n HZ: %n MS: %n', 'playTone', '1', '3', '1000', '500'], 715 | [' ', 'Board: %m.bdNum Turn Tone Off For Pin: %n', 'noTone', '1', '3'], 716 | [' ', 'Set Debug Level %m.dbgLevel', 'setDebugLevel', '0'], 717 | ['r', 'Board: %m.bdNum Digital Input on Pin %n', 'getDigitalInputData', '1', '2'], 718 | ['r', 'Board: %m.bdNum Analog Sensor Input on Pin %n', 'getAnalogSensorData', '1', '2'], 719 | ['r', 'Board: %m.bdNum Infrared Distance %m.distance Pin %n', 'getInfraredDistanceData', '1', '1. CM', '2'], 720 | ['r', 'Board: %m.bdNum SONAR Distance %m.distance Pin %n', 'getSonarData', '1', '1. CM', '2'], 721 | [' ', 'Board: %m.bdNum Set Pins For 4 Wire Bipolar Stepper %n %n %n %n Steps Per Rev: %n', 'fourWireStepperPins', '1', '8', '9', '10', '11', '500'], 722 | [' ', 'Board: %m.bdNum Set Pins For Stepper Driver Board: Step %n Direction %n Steps Per Rev: %n', 'stepperDriverPins', '1', '8', '9', 500], 723 | [' ', 'Board: %m.bdNum Move Stepper On Pin %n RPM: %n Dir: %m.motorDirection Accel: %n Decel: %n # of Steps: %n', 724 | 'moveStepper', '1', '8', '180', '1. CW', '1600', '1600', '2000'], 725 | [' ', 'Board: %m.bdNum Stepper Stop! Pin: %n', 'stopStepper', '1', '8'] 726 | 727 | 728 | ], 729 | menus: { 730 | bdNum: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'], 731 | dbgLevel: ['0', '1', '2'], 732 | onOff: ['1. Off', '2. On'], 733 | pinMode: ['1. Digital Input', '2. Digital Output', '3. Analog Sensor Input', '4. Analog (PWM) Output', 734 | '5. Standard Servo (PWM)', '6. Continuous Servo (PWM)', '7. Infrared Distance (GP2Y0A21YK) - (Analog In)', 735 | '8. SONAR Distance - (Digital In)', '9. Tone (Piezo)- (Digital Out)'], 736 | motorDirection: ['1. CW', '2. CCW'], 737 | inversion: ['1. False', '2. True'], 738 | distance: ['1. CM', '2. Inches'] 739 | 740 | }, 741 | 742 | url: 'http://mryslab.blogspot.com/' 743 | }; 744 | 745 | 746 | // Register the extension 747 | ScratchExtensions.register('Xi4S_v_004_12Feb15', descriptor, ext); 748 | 749 | })(); 750 | -------------------------------------------------------------------------------- /clients/scratch/xi4s/xi4s_fr4.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4s * * * 4 | * 5 | ********************************************************************************************* 6 | * Created by afy on 8/24/14. 7 | * 8 | * This is the Xi Client for Scratch 9 | * 10 | * It follows the Scratch JavaScript Extension Spec 11 | * 12 | * http://llk.github.io/scratch-extension-docs/ 13 | * 14 | * Version v.002 15 | * Nov 7, 2014 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | new (function () { 37 | var ext = this; 38 | console.log('Xi4s v.004'); 39 | 40 | // 0 = no debug 41 | // 1 = low level debug 42 | // 2 = high - open the floodgates 43 | // Variable is set by user through a Scratch command block 44 | var debugLevel = 0; 45 | 46 | // a variable to set the color of the 'LED' indicator for the extension on the Scratch editor 47 | var boardStatus = 0; // 0:not ready(RED), 1:partially ready or warning(YELLOW), 2: fully ready(GREEN) 48 | 49 | // Board IP addresses and ports are set by the user with a Scratch command block, and the information 50 | // is stored in the WebSocket Array 51 | 52 | // WebSocket array 53 | // Each board will have an associated WebSocket instance used for communication 54 | // with the Xi client. webSocketsArray is an array of objects. Each object has a format of: 55 | // {id: BoardID, , ip: IPAddress, port: port, ws: WebSocketReference} 56 | 57 | var webSocketsArray = []; 58 | 59 | // Sensor Data Array 60 | // This is an array of objects that store the latest sensor or switch value updates received from all Xi servers. 61 | // The object format is: 62 | // key: generated by genReporterKey() 63 | // value: latest updated value 64 | // A key is used to uniquely identify each data entry with a specific sensor on a specific server 65 | var sensorDataArray = []; 66 | 67 | /********************* asynchronous messages from scratch ****************************************/ 68 | // Cleanup function when the extension is unloaded 69 | ext._shutdown = function () { 70 | //send a 'resetBoard' message to each board 71 | for (var index = 0; index < webSocketsArray.length; index++) { 72 | if (debugLevel >= 2) { 73 | console.log('Sending reset to board index ' + index); 74 | } 75 | webSocketsArray[index].ws.send('resetBoard'); 76 | } 77 | }; 78 | 79 | // Status reporting code - part of boilerplate provided by Scratch 80 | // Set the 'LED' on the Scratch Editor 81 | ext._getStatus = function () { 82 | return { 83 | status: boardStatus, 84 | msg: 'Ready' 85 | }; 86 | }; 87 | 88 | /*****************************************************************************************************/ 89 | /*********************************** Scratch Program Block Handlers, ******************************/ 90 | /*****************************************************************************************************/ 91 | 92 | // Associate a handler for each block described in the blocks section below 93 | 94 | /******************************* 95 | **** Command Block Handlers **** 96 | *******************************/ 97 | 98 | // Accepts IP Address and Port information for each board that the user adds 99 | // The associated scratch block is a 'wait' command block. 100 | // We don't want Scratch to continue until the socket is open bidirectionally. 101 | // When socket.onopen is called the callback is returned so that scratch can proceed processing 102 | ext.setBoard = function (boardID, ipAddress, port, callback) { 103 | var timeoutID; // need to set a timeout when a socket is created because we are using a 'wait' block 104 | 105 | 106 | if (debugLevel >= 1) 107 | console.log('setBoard: ' + boardID, ipAddress, port); 108 | 109 | // Check to make sure that this board was not entered previously 110 | for (var index = 0; index < webSocketsArray.length; index++) { 111 | if (webSocketsArray[index].id === boardID) { 112 | // allow user to reset the board to the same value - for stop and start 113 | if ((webSocketsArray[index].ip != ipAddress) || (webSocketsArray[index].port != port)) { 114 | createAlert(12, boardID); 115 | callback(); // release the scratch wait block 116 | return; // no need to go further 117 | } 118 | } 119 | } 120 | // This is a confirmed unique entry. Create a websocket for this board 121 | var socket = new WebSocket('ws://' + ipAddress + ':' + port); 122 | 123 | // add the entry including the websocket reference just created 124 | webSocketsArray.push({'id': boardID, 'ip': ipAddress, 'port': port, 'ws': socket}); 125 | 126 | // start the timer for a server reply - we wait for up to 2 seconds for the reply 127 | timeoutID = window.setTimeout(noServerAlert, 2000); 128 | 129 | 130 | // attach an onopen handler to this socket. This message is sent by a servers websocket 131 | socket.onopen = function (event) { 132 | window.clearTimeout(timeoutID); 133 | if (debugLevel >= 1) 134 | console.log('onopen message received'); 135 | // change the board status to green with the first board added, since we don't know ahead of time 136 | // how many boards are attached 137 | boardStatus = 2; 138 | socket.send('Xi4sOnline'); 139 | callback(); // tell scratch to proceed processing 140 | }; 141 | 142 | function noServerAlert() { 143 | createAlert(20, boardID); 144 | boardStatus = 0; 145 | } 146 | 147 | /********************** websocket 'onmessage' handler *************************************/ 148 | // 149 | // All messages sent from board's socket are handled here. 150 | // Attach an onmessage event handler to this socket. 151 | // Process messages received from the server associated with this socket. 152 | socket.onmessage = function (message) { 153 | if (debugLevel === 1) 154 | console.log('onmessage received: ' + message.data); 155 | 156 | // All message components are delimited with '/' character. 157 | // TODO: Should this be done with JSON? 158 | 159 | // Incoming messages are split into their component pieces and placed into a 'msg' array 160 | // msg[0] for each message is the message ID. 161 | var msg = message.data.split('/'); 162 | 163 | // process each message ID 164 | switch (msg[0]) { 165 | // dataUpdate - server data update data message 166 | case 'dataUpdate': 167 | var index = msg[1]; // unique value used as an index into sensorDataArray 168 | var data = msg[2]; // data value to be entered into sensorDataArray 169 | if (debugLevel >= 2) 170 | console.log('sensorData: index = ' + index + ' data = ' + data); 171 | // update the array with the new value 172 | sensorDataArray[index].value = data; 173 | break; 174 | 175 | /*************************************** 176 | ************** server detected errors 177 | ****************************************/ 178 | 179 | // server detected a problem in setting the mode of this pin 180 | case 'invalidSetMode': 181 | case 'invalidPinCommand': 182 | console.log('invalid alerts:' + 'index: ' + msg[1] + 'board: ' + msg[2] + 'pin: ' + msg[3]); 183 | createAlert(msg[1], msg[2], msg[3]); 184 | break; 185 | default: 186 | if (debugLevel >= 1) 187 | console.log('onmessage unknown message received'); 188 | } 189 | }; 190 | }; 191 | 192 | // Set the pin mode command block handler 193 | ext.pinMode = function (boardID, pin, mode) { 194 | if (debugLevel >= 1) 195 | console.log('Set Pin Mode - board: ' + boardID + ' Mode: ' + mode + ' Pin: ' + pin); 196 | // make sure the websocket for the board was previously established 197 | for (var index = 0; index < webSocketsArray.length; index++) { 198 | if (webSocketsArray[index].id === boardID) { 199 | // send message to server to create device(input devices) or set the pin mode (output device) 200 | var messageToServer; // message to be sent to server 201 | mode = extractMode(mode); 202 | 203 | // the mode is the value prescribed in block descriptor section 204 | switch (mode) { 205 | // set pin to digital input mode 206 | // msg: setDigitalIN - digital service input 207 | case 'Digital Input': 208 | if (debugLevel >= 1) 209 | console.log('pin mode digital input'); 210 | // build the message to send to the Xi Server 211 | // we use the length of the array as the index/device id 212 | messageToServer = 'setDigitalIN/' + boardID + '/' + pin + '/' + sensorDataArray.length; 213 | sendSetInputPinRequest(messageToServer, 'd', boardID, pin, index); 214 | break; 215 | // set pin to digital out 216 | // msg: setDigitalOUT 217 | case 'Digital Output': 218 | if (debugLevel >= 1) 219 | console.log('pin mode digital output'); 220 | messageToServer = 'setDigitalOUT/' + boardID + '/' + pin; 221 | if (debugLevel >= 2) 222 | console.log('pinMode Digital Out Msg to server: ' + messageToServer); 223 | webSocketsArray[index].ws.send(messageToServer); 224 | break; 225 | // set pin to analog in 226 | // msg: setAnalogIN 227 | case 'Analog Sensor Input': 228 | if (debugLevel >= 1) 229 | console.log('pin mode analog input'); 230 | // build the message to send to the Xi Server 231 | // we use the length of the array as the index/device id 232 | messageToServer = 'setAnalogIN/' + sensorDataArray.length + '/' + boardID + '/' + pin; 233 | sendSetInputPinRequest(messageToServer, 'a', boardID, pin, index); 234 | break; 235 | // set pin mode PWM 236 | // msg: setAnalogOUT 237 | case 'Analog (PWM) Output': 238 | if (debugLevel >= 1) 239 | console.log('pin mode PWM'); 240 | // send out the pwm mode message 241 | // the host tests if the pin is PWM and if not will send back an 'xp' exception message 242 | messageToServer = 'setAnalogOUT/' + boardID + '/' + pin; 243 | if (debugLevel >= 2) 244 | console.log('pinMode PWM Out Msg to server: ' + messageToServer); 245 | webSocketsArray[index].ws.send(messageToServer); 246 | break; 247 | case 'Standard Servo (PWM)': 248 | if (debugLevel >= 1) 249 | console.log('pin mode SERVO'); 250 | // send out the servo mode message 251 | messageToServer = 'setStandardServoMode/' + boardID + '/' + pin; 252 | if (debugLevel >= 2) 253 | console.log('pinMode Standard Servo Out Msg to server: ' + messageToServer); 254 | webSocketsArray[index].ws.send(messageToServer); 255 | break; 256 | case 'Continuous Servo (PWM)': 257 | if (debugLevel >= 1) 258 | console.log('pin mode SERVO'); 259 | // send out the servo mode message 260 | messageToServer = 'setContinuousServoMode/' + boardID + '/' + pin; 261 | if (debugLevel >= 2) 262 | console.log('pinMode ContinuousServo Out Msg to server: ' + messageToServer); 263 | webSocketsArray[index].ws.send(messageToServer); 264 | break; 265 | case 'SONAR Distance - (Digital In)': 266 | createAlert(13); 267 | messageToServer = 'setSonarMode/' + boardID + '/' + pin + '/' + sensorDataArray.length; 268 | if (debugLevel >= 2) 269 | console.log('pinMode Sonar Out Msg to server: ' + messageToServer); 270 | sendSetInputPinRequest(messageToServer, 'd', boardID, pin, index); 271 | break; 272 | case 'Infrared Distance (GP2Y0A21YK) - (Analog In)': 273 | messageToServer = 'setInfraRedDistanceMode/' + boardID + '/' + pin + '/' + sensorDataArray.length; 274 | if (debugLevel >= 2) 275 | console.log('pinMode infrared distance Out Msg to server: ' + messageToServer); 276 | sendSetInputPinRequest(messageToServer, 'a', boardID, pin, index); 277 | break; 278 | case 'Tone (Piezo)- (Digital Out)': 279 | if (debugLevel >= 1) 280 | console.log('pin mode Tone'); 281 | // send out the servo mode message 282 | messageToServer = 'setToneMode/' + boardID + '/' + pin; 283 | if (debugLevel >= 2) 284 | console.log('pinMode Tone Mode Out Msg to server: ' + messageToServer); 285 | webSocketsArray[index].ws.send(messageToServer); 286 | break; 287 | default: 288 | if (debugLevel >= 1) 289 | console.log('ext.pinMode: Unknown mode - ', +mode); 290 | } 291 | // just return from here after processing the command 292 | return; 293 | } 294 | } 295 | // board not yet established 296 | createAlert(14, boardID) 297 | }; 298 | 299 | 300 | // Digital output command block 301 | ext.digitalWrite = function (board, pin, value) { 302 | if (debugLevel >= 1) { 303 | console.log('digitalWrite Board: ' + board + ' Pin ' + pin + ' Value ' + value); 304 | } 305 | // strip index number off of message to determine value to send to server 306 | value = extractOffOn(value); 307 | var msg = 'digitalWrite/' + board + '/' + pin + '/' + value; 308 | sendCommand(msg, board, 'digitalWrite'); 309 | }; 310 | 311 | // PWM output (analog write) command block 312 | ext.analogWrite = function (board, pin, value) { 313 | var msg = 'analogWrite/' + board + '/' + pin + '/' + value; 314 | sendCommand(msg, board, 'analogWrite'); 315 | }; 316 | 317 | // set servo position to position in degrees 318 | ext.moveStandardServo = function (board, pin, degrees, inversion) { 319 | inversion = extractInversion(inversion); 320 | var msg = 'moveStandardServo/' + board + '/' + pin + '/' + degrees + '/' + inversion; 321 | sendCommand(msg, board, 'moveStandardServo'); 322 | }; 323 | 324 | // set servo position to position in degrees 325 | ext.moveContinuousServo = function (board, pin, direction, inversion, speed) { 326 | inversion = extractInversion(inversion); 327 | direction = extractDirection(direction); 328 | var msg = 'moveContinuousServo/' + board + '/' + pin + '/' + direction + '/' + inversion + '/' + speed; 329 | sendCommand(msg, board, 'moveContinuousServo'); 330 | }; 331 | 332 | // stop servo 333 | ext.stopServo = function (board, pin) { 334 | var msg = 'stopServo/' + board + '/' + pin; 335 | sendCommand(msg, board, 'stopServo'); 336 | }; 337 | 338 | // stop stepper 339 | ext.stopStepper = function (board, pin) { 340 | var msg = 'stopStepper/' + board + '/' + pin; 341 | sendCommand(msg, board, 'stopStepper'); 342 | }; 343 | 344 | ext.fourWireStepperPins = function (board, pinA, pinB, pinC, pinD, stepsPerRev) { 345 | 346 | createAlert(15); 347 | 348 | var pinArray = []; 349 | pinArray.push(pinA); 350 | pinArray.push(pinB); 351 | pinArray.push(pinC); 352 | pinArray.push(pinD); 353 | 354 | // check for 4 unique values 355 | var unique = pinArray.filter(onlyUnique); 356 | if (unique.length !== 4) { 357 | createAlert(16); 358 | return; 359 | } 360 | var msg = 'fourWireStepperPins/' + board + '/' + pinA + '/' + pinB + '/' + pinC + '/' + pinD + '/' + stepsPerRev; 361 | sendCommand(msg, board, 'fourWireStepperPins'); 362 | }; 363 | 364 | ext.stepperDriverPins = function (board, pinA, pinB, stepsPerRev) { 365 | 366 | createAlert(15); 367 | 368 | var pinArray = []; 369 | pinArray.push(pinA); 370 | pinArray.push(pinB); 371 | 372 | 373 | // check for 2 unique values 374 | var unique = pinArray.filter(onlyUnique); 375 | if (debugLevel >= 2) 376 | console.log('stepperDriverPins unique = ' + unique); 377 | 378 | if (debugLevel >= 2) 379 | console.log('stepperDriverPins unique length = ' + unique.length); 380 | 381 | if (unique.length !== 2) { 382 | createAlert(17); 383 | } 384 | var msg = 'stepperDriverPins/' + board + '/' + pinA + '/' + pinB + '/' + stepsPerRev; 385 | sendCommand(msg, board, 'stepperDriverPins'); 386 | }; 387 | 388 | ext.moveStepper = function (board, pin, rpm, direction, accel, decel, steps) { 389 | direction = extractDirection(direction); 390 | var msg = 'moveStepper/' + board + '/' + pin + '/' + rpm + '/' + direction + '/' + accel + '/' + decel + '/' + steps; 391 | sendCommand(msg, board, 'moveStepper'); 392 | }; 393 | 394 | // send command to play a tone 395 | ext.playTone = function (board, pin, frequency, duration) { 396 | var msg = 'playTone/' + board + '/' + pin + '/' + frequency + '/' + duration; 397 | sendCommand(msg, board, 'playTone'); 398 | }; 399 | 400 | // turn tone off 401 | ext.noTone = function (board, pin) { 402 | var msg = 'noTone/' + board + '/' + pin; 403 | sendCommand(msg, board, 'noTone'); 404 | }; 405 | 406 | // Set the debug level 407 | ext.setDebugLevel = function (level) { 408 | debugLevel = level; 409 | }; 410 | 411 | 412 | /******************************* 413 | **** Response Block Handlers **** 414 | *******************************/ 415 | 416 | // retrieve digital data from sensorDataArray 417 | ext.getDigitalInputData = function (board, pin) { 418 | if (debugLevel >= 1) 419 | console.log('Digital Input - board: ' + board + ' Pin: ' + pin); 420 | var key = genReporterKey(board, pin, 'd'); 421 | return retrieveReporterData(board, pin, key); 422 | }; 423 | 424 | // retrieve analog data data from sensorDataArray 425 | ext.getAnalogSensorData = function (board, pin) { 426 | if (debugLevel >= 1) 427 | console.log('Analog Sensor Input - board: ' + board + ' Pin: ' + pin); 428 | 429 | // generate a key for sensorDataArray 430 | var key = genReporterKey(board, pin, 'a'); 431 | return retrieveReporterData(board, pin, key); 432 | }; 433 | 434 | 435 | // retrieve sonar data 436 | ext.getSonarData = function (board, units, pin) { 437 | if (debugLevel >= 1) 438 | console.log('getSonarData - board: ' + board + 'Units' + units + ' Pin: ' + pin); 439 | 440 | // generate a key for sensorDataArray 441 | var key = genReporterKey(board, pin, 'd'); 442 | var distance = retrieveReporterData(board, pin, key); 443 | units = extractDistance(units); 444 | if (units === 'CM') { 445 | return (distance * 2.54).toFixed(2); 446 | } 447 | else { 448 | return distance; 449 | } 450 | }; 451 | 452 | // retrieve infrared distance data 453 | ext.getInfraredDistanceData = function (board, units, pin) { 454 | if (debugLevel >= 1) 455 | console.log('getInfraredDistanceData - board: ' + board + 'Units' + units + ' Pin: ' + pin); 456 | 457 | // generate a key for sensorDataArray 458 | var key = genReporterKey(board, pin, 'a'); 459 | var distance = retrieveReporterData(board, pin, key); 460 | units = extractDistance(units); 461 | if (units === 'CM') { 462 | return (distance * 2.54).toFixed(2); 463 | } 464 | else { 465 | return distance; 466 | } 467 | }; 468 | 469 | 470 | // helper functions 471 | 472 | //genReporterKey 473 | // Input: Board number 474 | // Pin number 475 | // Designator to differentiate between analog and digital - either 'a' or 'd' 476 | // 477 | // Returns the generated key 478 | function genReporterKey(boardNum, pinNum, designator) { 479 | if (debugLevel >= 1) 480 | console.log('genReporterKey returns: ' + boardNum + designator + pinNum); 481 | return boardNum + designator + pinNum; 482 | } 483 | 484 | // Using the supplied key, this function will retrieve the latest data from the sensorDataArray. 485 | function retrieveReporterData(board, pin, key) { 486 | if (debugLevel >= 1) { 487 | console.log('retrieveReporterData: board: ' + board + ' pin ' + pin + ' key ' + key); 488 | } 489 | // make sure that this is a unique key in the array 490 | for (var index = 0; index < sensorDataArray.length; index++) { 491 | if (sensorDataArray[index].key === key) { 492 | return sensorDataArray[index].value 493 | } 494 | } 495 | // did not find an entry in the array 496 | createAlert(18, board, pin); 497 | ext._shutdown(); 498 | } 499 | 500 | // This function will format a set input pin message (analog or digital) and send it to the server 501 | function sendSetInputPinRequest(msgToServer, analogOrDigital, board, pin, wsIndex) { 502 | var reporterArrayEntry = {key: null, data: -1}; // The entry we build to add to the sensorDataArray 503 | 504 | // generate a key so that we can use to retrieve the data from the reporterArrayEntry 505 | reporterArrayEntry.key = genReporterKey(board, pin, analogOrDigital); 506 | 507 | if (debugLevel >= 1) 508 | console.log('sendInputPinRequest generated key = ' + reporterArrayEntry.key); 509 | 510 | var found = false; 511 | // make sure that this is a unique key in the array 512 | for (var index = 0; index < sensorDataArray.length; index++) { 513 | if (sensorDataArray[index].key === reporterArrayEntry.key) { 514 | found = true; 515 | console.log("sendInputPinReq entry exists"); 516 | } 517 | } 518 | 519 | 520 | // it is unique so go ahead and add the record to the array 521 | if (found === false) { 522 | sensorDataArray.push(reporterArrayEntry); 523 | //} 524 | // now we can safely send the set pin message to the Xi Server to create the device 525 | if (debugLevel >= 1) 526 | console.log('sendInputPinRequest: msg = ' + msgToServer + ' index = ' + wsIndex); 527 | webSocketsArray[wsIndex].ws.send(msgToServer); 528 | } 529 | } 530 | 531 | // This function will check to see if a board has been established and if it has, will send a command 532 | // message to the server 533 | function sendCommand(msg, board, type) { 534 | if (debugLevel >= 1) { 535 | console.log('sendCommand: ' + msg + ' ' + board + ' ' + type); 536 | } 537 | for (var index = 0; index < webSocketsArray.length; index++) { 538 | if (webSocketsArray[index].id === board) { 539 | if (debugLevel >= 2) 540 | console.log('sendCommand: Message: ' + msg + ' board: ' + board); 541 | // send out message 542 | webSocketsArray[index].ws.send(msg); 543 | return; 544 | } 545 | } 546 | // board was not established 547 | createAlert(19, board); 548 | } 549 | 550 | // return unique values contained within an array 551 | function onlyUnique(value, index, self) { 552 | return self.indexOf(value) === index; 553 | } 554 | 555 | // strip off the text to accommodate translations 556 | function extractOffOn(value) { 557 | var offOn = value.split('.'); 558 | if (offOn[0] === '1') { 559 | return "Off" 560 | } 561 | else { 562 | return "On"; 563 | } 564 | } 565 | 566 | function extractDirection(direction) { 567 | var dirArray = direction.split('.'); 568 | if (dirArray[0] === '1') { 569 | return "CW"; 570 | } 571 | else { 572 | return "CCW"; 573 | } 574 | } 575 | 576 | function extractDistance(distance) { 577 | var distArray = distance.split('.'); 578 | if (distArray[0] === '1') { 579 | return "CM"; 580 | } 581 | else { 582 | return "Inches"; 583 | } 584 | } 585 | 586 | function extractInversion(inversion) { 587 | var invArray = inversion.split('.'); 588 | if (invArray[0] === '1') { 589 | return "False"; 590 | } 591 | else { 592 | return "True"; 593 | } 594 | } 595 | 596 | function extractMode(mode) { 597 | var modeArray = mode.split('.'); 598 | var serverMode = undefined; 599 | switch (modeArray[0]) { 600 | case '1': 601 | serverMode = 'Digital Input'; 602 | break; 603 | case '2': 604 | serverMode = 'Digital Output'; 605 | break; 606 | case '3': 607 | serverMode = 'Analog Sensor Input'; 608 | break; 609 | case '4': 610 | serverMode = 'Analog (PWM) Output'; 611 | break; 612 | case '5': 613 | serverMode = 'Standard Servo (PWM)'; 614 | break; 615 | case '6': 616 | serverMode = 'Continuous Servo (PWM)'; 617 | break; 618 | case '7': 619 | serverMode = 'Infrared Distance (GP2Y0A21YK) - (Analog In)'; 620 | break; 621 | case '8': 622 | serverMode = 'SONAR Distance - (Digital In)'; 623 | break; 624 | case '9': 625 | serverMode = 'Tone (Piezo)- (Digital Out)'; 626 | break; 627 | default: 628 | console.log("extract mode unknown mode = " + modeArray[0]); 629 | } 630 | return serverMode; 631 | } 632 | 633 | function createAlert(index, board, pin) { 634 | console.log('createAlert' + index, board, pin); 635 | var alertStrings = [ 636 | // 0 637 | "dépasse le nombre maximum de broches détecté sur la carte.", 638 | // 1 639 | "ne peut pas être configuré avec le mode voulu.", 640 | //2 641 | "n'est pas configurée comme une sortie Digital.", 642 | //3 643 | "n'est pas configurée comme une sortie Analogique.", 644 | //4 645 | "n'est pas configurée comme une sortie Son.", 646 | //5 647 | "n'est pas configurée pour commander des servo-moteurs.", 648 | //6 649 | "n'est pas configurée pour commander des servo-moteurs standards.", 650 | //7 651 | "n'est pas configurée pour commander des servo-moteurs à rotation continue.", 652 | //8 653 | "n'est pas configurée pour commander des moteurs pas-à-pas.", 654 | //9 655 | "cette broche est déjà configurée, différemment.", 656 | //10 657 | "La vitesse doit être réglée avec une valeur dans un intervalle de 0,0 à 1,0.", 658 | //11 659 | "ne peut pas effectuer d'opération de type analogique.", 660 | //12 661 | "Une adresse IP existe déjà pour cette carte.", 662 | //13 663 | "Si vous utilisez une carte Arduino, cette fonction nécessite une version spéciale du programme 'StandardFirmata'." + 664 | "Lire la page : https://github.com/rwaldron/johnny-five/wiki/Sonar pour les détails.", 665 | //14 666 | "Une adresse IP doit être fixée avant de pouvoir utiliser la carte.", 667 | //15 668 | "Si vous utilisez une carte Arduino, cette fonction nécessite une version spéciale du programme 'StandardFirmata'." + 669 | "Lire la page : https://github.com/soundanalogous/AdvancedFirmata pour les détails.", 670 | //16 671 | "Les 4 valeurs de numéro de broche doivent être uniques. Veuillez recommencer.", 672 | //17 673 | "Les 2 valeurs de numéro de broche doivent être uniques. Veuillez recommencer.", 674 | //18 675 | "Le mode de configuration de la broche n'a pas été défini. ", 676 | //19 677 | "L'adresse IP de la carte n'a pas encore été fixée.", 678 | //20 679 | "Le serveur ne répond pas. Avez-vous lancé un serveur XiServeur pour cette carte ?" + 680 | "Veuillez lancer un serveur Xi et recharger cette page pour essayer à nouveau." 681 | ]; 682 | 683 | var headerKeywords = { 684 | board: "Sur la carte n° ", 685 | pin: "la broche " 686 | }; 687 | 688 | var alertInfo = ""; 689 | if (board != undefined) { 690 | alertInfo = headerKeywords.board + board; 691 | } 692 | if (pin != undefined) { 693 | alertInfo = alertInfo + ' ' + headerKeywords.pin + pin; 694 | } 695 | alertInfo += " "; 696 | 697 | alert(alertInfo + alertStrings[index]); 698 | 699 | } 700 | 701 | 702 | // Block and block menu descriptions 703 | var descriptor = { 704 | blocks: [ 705 | ['w', "utiliser comme carte n° %m.bdNum celle à l'adresse IP / port %s : %s", 'setBoard', '1', 'localhost', '1234'], 706 | [' ', 'sur la carte n° %m.bdNum activer la broche Digital %n en mode %m.pinMode', 'pinMode', '1', '2', '1. entrée'], 707 | [' ', "sur la carte n° %m.bdNum mettre l'état logique de la broche Digital %n à %m.onOff ", 'digitalWrite', '1', '2', 'faux / bas'], 708 | [' ', 'sur la carte n° %m.bdNum écrire sur la broche PWM~ %n la valeur %n', 'analogWrite', '1', '3', '128'], 709 | [' ', 'sur la carte n° %m.bdNum orienter le servo-moteur sur la broche %n de %n degrés - inversé ? %m.inversion', 710 | 'moveStandardServo', '1', '3', '90', '1. faux'], 711 | [' ', 'sur la carte n° %m.bdNum faire tourner le servo-moteur continu sur la broche %n dans la direction %m.motorDirection - inversé ? %m.inversion, à la vitesse (0.0 - 1.0) %n ', 712 | 'moveContinuousServo', '1', '3', '1. avant', '1. faux', '.5'], 713 | [' ', 'sur la carte n° %m.bdNum arrêter le servo-moteur de la broche %n !', 'stopServo', '1', '3'], 714 | [' ', 'sur la carte n° %m.bdNum jouer un son sur la broche %n de fréquence (Hz) %n sur une durée (ms) de %n', 'playTone', '1', '3', '1000', '500'], 715 | [' ', 'sur la carte n° %m.bdNum arrêter le son de la broche %n !', 'noTone', '1', '3'], 716 | [' ', 'niveau de débogage : %m.dbgLevel', 'setDebugLevel', '0'], 717 | ['r', "sur la carte n° %m.bdNum l'état logique de la broche Digital %n", 'getDigitalInputData', '1', '2'], 718 | ['r', 'sur la carte n° %m.bdNum la valeur lue sur la broche analogique A %n', 'getAnalogSensorData', '1', '2'], 719 | ['r', 'sur la carte n° %m.bdNum la distance mesurée en %m.distance par le capteur Infra-Rouge de la broche %n', 'getInfraredDistanceData', '1', '1. cm', '2'], 720 | ['r', 'sur la carte n° %m.bdNum la distance mesurée en %m.distance par le Sonar de la broche %n', 'getSonarData', '1', '1. cm', '2'], 721 | [' ', 'sur la carte n° %m.bdNum activer pour un moteur pas-à-pas 4 fils les broches %n %n %n %n avec une rotation de %n pas par tour', 'fourWireStepperPins', '1', '8', '9', '10', '11', '500'], 722 | [' ', 'sur la carte n° %m.bdNum activer la carte de moteur pas-à-pas pour la broche %n, dans la direction %n à raison de %n pas par tour', 'stepperDriverPins', '1', '8', '9', 500], 723 | [' ', 'sur la carte n° %m.bdNum faire tourner le moteur pas-à-pas sur la broche %n : %n tr/mn, direction %m.motorDirection , accélération : %n , décélération : %n pour un nb de pas : %n', 724 | 'moveStepper', '1', '8', '180', '1. avant', '1600', '1600', '2000'], 725 | [' ', 'sur la carte n° %m.bdNum arrêter le moteur pas-à-pas de la broche %n !', 'stopStepper', '1', '8'] 726 | 727 | 728 | ], 729 | menus: { 730 | bdNum: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'], 731 | dbgLevel: ['0', '1', '2'], 732 | onOff: ['1. faux / bas', '2. vrai / haut'], 733 | pinMode: ['1. entrée', '2. sortie', '3. mesure analogique', '4. impulsions PWM~', 734 | '5. servo-moteur standard (sur PWM~)', '6. servo-moteur à rotation continue (sur PWM~)', '7. mesure distance par Infra-Rouge (entrée Analogique)', 735 | '8. mesure distance par Sonar (entrée Digital)', '9. sortie Son (sortie Digital)'], 736 | motorDirection: ['1. avant', '2. arrière'], 737 | inversion: ['1. faux', '2. vrai'], 738 | distance: ['1. cm', '2. pouces'] 739 | 740 | }, 741 | 742 | url: 'http://mryslab.blogspot.com/' 743 | }; 744 | 745 | 746 | // Register the extension 747 | ScratchExtensions.register('Xi4S_v_005_FR_14Feb15', descriptor, ext); 748 | 749 | })(); 750 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/analogRead.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * analogRead 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 9/21/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | // Using the supplied key, this function will retrieve the latest data from the sensorDataArray. 37 | function retrieveReporterData(board, pin, key) { 38 | // make sure that this is a unique key in the array 39 | for (var index = 0; index < window.xi_sensorDataArray.length; index++) { 40 | if (window.xi_sensorDataArray[index].key === key) { 41 | return window.xi_sensorDataArray[index].value 42 | } 43 | } 44 | // did not find an entry in the array 45 | alert('Did you set the pin mode for Board ' + board + ' Pin ' + pin + 46 | '? No entry for this block in database'); 47 | } 48 | 49 | 50 | 51 | 52 | //genReporterKey 53 | // Input: Board number 54 | // Pin number 55 | // Designator to differentiate between analog and digital - either 'a' or 'd' 56 | // 57 | // Returns the generated key 58 | function genReporterKey(boardNum, pinNum, designator) { 59 | if (window.xi_debugLevel >= 1) { 60 | console.log('genReporterKey returns: ' + boardNum + designator + pinNum); 61 | } 62 | return boardNum + designator + pinNum; 63 | } 64 | 65 | 66 | 67 | var rVal; 68 | 69 | if( window.xi_beenHereDoneThat === undefined) 70 | { 71 | alert("The first block of Xi script must be either a Set Debug Level or Set IPAddress block."); 72 | } 73 | 74 | if (window.xi_debugLevel >= 1) 75 | { 76 | console.log('Analog Input - board: ' + boardID + ' Pin: ' + pin); 77 | } 78 | 79 | 80 | var key = genReporterKey(boardID, pin, 'a'); 81 | rVal = retrieveReporterData(boardID, pin, key); 82 | if( rVal === undefined) { 83 | rVal = 0; 84 | } 85 | if( window.xi_debugLevel >= 1) { 86 | console.log("Analog Read returns: " + rVal) ; 87 | } 88 | return rVal; 89 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/analogWrite.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * analogWrite 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 9/21/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | 37 | function sendCommand(msg, board, type) { 38 | if (window.xi_debugLevel >= 1) { 39 | console.log('sendCommand: ' + msg + ' ' + board + ' ' + type); 40 | } 41 | for (var index = 0; index < window.xi_webSocketsArray.length; index++) { 42 | if (window.xi_webSocketsArray[index].id === board) { 43 | if (window.xi_debugLevel >= 2) 44 | console.log('sendCommand: Message: ' + msg + ' board: ' + board); 45 | // send out message 46 | window.xi_webSocketsArray[index].ws.send(msg); 47 | return; 48 | } 49 | } 50 | // board was not established 51 | alert(type + ' IP address for board ' + boardID + ' was not set'); 52 | } 53 | 54 | if( window.xi_beenHereDoneThat === undefined) 55 | { 56 | alert("The first block of Xi script must be either a Set Debug Level or Set IPAddress block."); 57 | } 58 | 59 | var msg = 'analogWrite/' + boardID + '/' + pin + '/' + value; 60 | sendCommand(msg, boardID, 'analogWrite'); 61 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/digitalRead.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * digitalRead 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 9/21/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | // Using the supplied key, this function will retrieve the latest data from the sensorDataArray. 37 | function retrieveReporterData(board, pin, key) { 38 | // make sure that this is a unique key in the array 39 | for (var index = 0; index < window.xi_sensorDataArray.length; index++) { 40 | if (window.xi_sensorDataArray[index].key === key) { 41 | return window.xi_sensorDataArray[index].value 42 | } 43 | } 44 | // did not find an entry in the array 45 | alert('Did you set the pin mode for Board ' + board + ' Pin ' + pin + 46 | '? No entry for this block in database'); 47 | } 48 | 49 | 50 | 51 | 52 | //genReporterKey 53 | // Input: Board number 54 | // Pin number 55 | // Designator to differentiate between analog and digital - either 'a' or 'd' 56 | // 57 | // Returns the generated key 58 | function genReporterKey(boardNum, pinNum, designator) { 59 | if (window.xi_debugLevel >= 1) { 60 | console.log('genReporterKey returns: ' + boardNum + designator + pinNum); 61 | } 62 | return boardNum + designator + pinNum; 63 | } 64 | 65 | 66 | 67 | var rVal; 68 | 69 | if( window.xi_beenHereDoneThat === undefined) 70 | { 71 | alert("The first block of Xi script must be either a Set Debug Level or Set IPAddress block."); 72 | } 73 | 74 | if (window.xi_debugLevel >= 1) 75 | { 76 | console.log('Digital Input - board: ' + boardID + ' Pin: ' + pin); 77 | } 78 | 79 | 80 | var key = genReporterKey(boardID, pin, 'd'); 81 | 82 | rVal = retrieveReporterData(boardID, pin, key); 83 | if( rVal === undefined) { 84 | rVal = 0; 85 | } 86 | if( window.xi_debugLevel >= 1) { 87 | console.log("Digital Read returns: " + rVal) ; 88 | } 89 | return rVal; 90 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/digitalWrite.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * digitalWrite 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 9/21/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | // This function will check to see if a board has been established and if it has, will send a command 37 | // message to the server 38 | function sendCommand(msg, board, type) { 39 | if (window.xi_debugLevel >= 1) { 40 | console.log('sendCommand: ' + msg + ' ' + board + ' ' + type); 41 | } 42 | for (var index = 0; index < window.xi_webSocketsArray.length; index++) { 43 | if (window.xi_webSocketsArray[index].id === board) { 44 | if (window.xi_debugLevel >= 2) 45 | console.log('sendCommand: Message: ' + msg + ' board: ' + board); 46 | // send out message 47 | window.xi_webSocketsArray[index].ws.send(msg); 48 | return; 49 | } 50 | } 51 | // board was not established 52 | alert(type + ' IP address for board ' + boardID + ' was not set'); 53 | } 54 | 55 | 56 | if( window.xi_beenHereDoneThat === undefined) 57 | { 58 | alert("The first block of Xi script must be either a Set Debug Level or Set IPAddress block."); 59 | } 60 | 61 | 62 | 63 | if (window.xi_debugLevel >= 1) { 64 | console.log('digitalWrite Board: ' + boardID + ' Pin ' + pin + ' Value ' + value); 65 | } 66 | var msg = 'digitalWrite/' + boardID + '/' + pin + '/' + value; 67 | sendCommand(msg, boardID, 'digitalWrite'); 68 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/infraredDistance.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * infrared distance read 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 11/7/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | // Using the supplied key, this function will retrieve the latest data from the sensorDataArray. 37 | function retrieveReporterData(boardID, pin, key) { 38 | // make sure that this is a unique key in the array 39 | for (var index = 0; index < window.xi_sensorDataArray.length; index++) { 40 | if (window.xi_sensorDataArray[index].key === key) { 41 | return window.xi_sensorDataArray[index].value 42 | } 43 | } 44 | // did not find an entry in the array 45 | alert('Did you set the pin mode for Board ' + boardID + ' Pin ' + pin + 46 | '? No entry for this block in database'); 47 | } 48 | 49 | 50 | 51 | 52 | //genReporterKey 53 | // Input: Board number 54 | // Pin number 55 | // Designator to differentiate between analog and digital - either 'a' or 'd' 56 | // 57 | // Returns the generated key 58 | function genReporterKey(boardID, pinNum, designator) { 59 | if (window.xi_debugLevel >= 1) { 60 | console.log('genReporterKey returns: ' + boardID + designator + pinNum); 61 | } 62 | return boardID + designator + pinNum; 63 | } 64 | 65 | 66 | 67 | var rVal; 68 | 69 | if( window.xi_beenHereDoneThat === undefined) 70 | { 71 | alert("The first block of Xi script must be either a Set Debug Level or Set IPAddress block."); 72 | } 73 | 74 | if (window.xi_debugLevel >= 1) 75 | { 76 | console.log('infrared distance Input - board: ' + boardID + ' Pin: ' + pin); 77 | } 78 | 79 | 80 | var key = genReporterKey(boardID, pin, 'a'); 81 | var distance = retrieveReporterData(boardID, pin, key); 82 | if (units === 'CM') { 83 | return (distance * 2.54).toFixed(2); 84 | } 85 | else { 86 | return distance; 87 | } 88 | 89 | if( window.xi_debugLevel >= 1) { 90 | console.log("infrared distance sensor Read returns: " + rVal) ; 91 | } 92 | return rVal; 93 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/moveContinuousServo.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * moveContinuousServo 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 9/21/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | 37 | function sendCommand(msg, board, type) { 38 | if (window.xi_debugLevel >= 1) { 39 | console.log('sendCommand: ' + msg + ' ' + board + ' ' + type); 40 | } 41 | for (var index = 0; index < window.xi_webSocketsArray.length; index++) { 42 | if (window.xi_webSocketsArray[index].id === board) { 43 | if (window.xi_debugLevel >= 2) 44 | console.log('sendCommand: Message: ' + msg + ' board: ' + boardID); 45 | // send out message 46 | window.xi_webSocketsArray[index].ws.send(msg); 47 | return; 48 | } 49 | } 50 | // board was not established 51 | alert(type + ' IP address for board ' + boardID + ' was not set'); 52 | } 53 | 54 | if( window.xi_beenHereDoneThat === undefined) 55 | { 56 | alert("The first block of Xi script must be either a Set Debug Level or Set IPAddress block."); 57 | } 58 | var msg = 'moveContinuousServo/' + boardID + '/' + pin + '/' + direction + '/' + inversion + '/' + speed; 59 | sendCommand(msg, boardID, 'moveContinuousServo'); 60 | 61 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/moveStandardServo.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * moveStandardServo 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 9/21/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | 37 | function sendCommand(msg, board, type) { 38 | if (window.xi_debugLevel >= 1) { 39 | console.log('sendCommand: ' + msg + ' ' + board + ' ' + type); 40 | } 41 | for (var index = 0; index < window.xi_webSocketsArray.length; index++) { 42 | if (window.xi_webSocketsArray[index].id === board) { 43 | if (window.xi_debugLevel >= 2) 44 | console.log('sendCommand: Message: ' + msg + ' board: ' + boardID); 45 | // send out message 46 | window.xi_webSocketsArray[index].ws.send(msg); 47 | return; 48 | } 49 | } 50 | // board was not established 51 | alert(type + ' IP address for board ' + boardID + ' was not set'); 52 | } 53 | 54 | if( window.xi_beenHereDoneThat === undefined) 55 | { 56 | alert("The first block of Xi script must be either a Set Debug Level or Set IPAddress block."); 57 | } 58 | 59 | var msg = 'moveStandardServo/' + boardID + '/' + pin + '/' + degrees + '/' + inversion; 60 | sendCommand(msg, boardID, 'moveStandardServo'); 61 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/moveStepper.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * move stepper 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 11/7/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | function sendCommand(msg, boardID, type) { 37 | if (window.xi_debugLevel >= 1) { 38 | console.log('sendCommand: ' + msg + ' ' + boardID + ' ' + type); 39 | } 40 | for (var index = 0; index < window.xi_webSocketsArray.length; index++) { 41 | if (window.xi_webSocketsArray[index].id === boardID) { 42 | if (window.xi_debugLevel >= 2) 43 | console.log('sendCommand: Message: ' + msg + ' board: ' + boardID); 44 | // send out message 45 | window.xi_webSocketsArray[index].ws.send(msg); 46 | return; 47 | } 48 | } 49 | // board was not established 50 | alert(type + ' IP address for board ' + boardID + ' was not set'); 51 | } 52 | 53 | if( window.xi_beenHereDoneThat === undefined) 54 | { 55 | alert("The first block of Xi script must be either a Set Debug Level or Set IPAddress block."); 56 | } 57 | 58 | var msg = 'moveStepper/' + boardID + '/' + pin + '/' + rpms + '/' + dir + '/' + acc + '/' + dec + '/' + stp; 59 | sendCommand(msg, boardID, 'moveStepper'); 60 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/noTone.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * stop tone 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 11/7/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | 37 | function sendCommand(msg, board, type) { 38 | if (window.xi_debugLevel >= 1) { 39 | console.log('sendCommand: ' + msg + ' ' + board + ' ' + type); 40 | } 41 | for (var index = 0; index < window.xi_webSocketsArray.length; index++) { 42 | if (window.xi_webSocketsArray[index].id === board) { 43 | if (window.xi_debugLevel >= 2) 44 | console.log('sendCommand: Message: ' + msg + ' board: ' + boardID); 45 | // send out message 46 | window.xi_webSocketsArray[index].ws.send(msg); 47 | return; 48 | } 49 | } 50 | // board was not established 51 | alert(type + ' IP address for board ' + boardID + ' was not set'); 52 | } 53 | 54 | if( window.xi_beenHereDoneThat === undefined) 55 | { 56 | alert("The first block of Xi script must be either a Set Debug Level or Set IPAddress block."); 57 | } 58 | var msg = 'noTone/' + boardID + '/' + pin ; 59 | sendCommand(msg, boardID, 'noTone'); 60 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/pinMode.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * setPinMode 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 9/21/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.002 15 | * Nov 7, 2014 16 | * 17 | * 18 | * @author: Alan Yorinks 19 | Copyright (c) 2014 Alan Yorinks All right reserved. 20 | 21 | This program is free software; you can redistribute it and/or 22 | modify it under the terms of the GNU General Public 23 | License as published by the Free Software Foundation; either 24 | version 3.0 of the License, or (at your option) any later version. 25 | 26 | This library is distributed in the hope that it will be useful, 27 | but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 29 | Lesser General Public License for more details. 30 | 31 | You should have received a copy of the GNU General Public 32 | License along with this library; if not, write to the Free Software 33 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 34 | * 35 | */ 36 | 37 | 38 | //genReporterKey 39 | // Input: Board number 40 | // Pin number 41 | // Designator to differentiate between analog and digital - either 'a' or 'd' 42 | // 43 | // Returns the generated key 44 | function genReporterKey(boardNum, pinNum, designator) { 45 | if (window.xi_debugLevel >= 1) 46 | console.log('genReporterKey returns: ' + boardNum + designator + pinNum); 47 | return boardNum + designator + pinNum; 48 | } 49 | 50 | function sendSetInputPinRequest(msgToServer, analogOrDigital, board, pin, wsIndex) { 51 | var reporterArrayEntry = {key: null, data: -1}; // The entry we build to add to the xi_sensorDataArray 52 | 53 | // generate a key so that we can use to retrieve the data from the reporterArrayEntry 54 | reporterArrayEntry.key = genReporterKey(board, pin, analogOrDigital); 55 | 56 | if (window.xi_debugLevel >= 1) 57 | console.log('sendInputPinRequest generated key = ' + reporterArrayEntry.key); 58 | 59 | var found = false; 60 | // make sure that this is a unique key in the array 61 | for (var index = 0; index < window.xi_sensorDataArray.length; index++) { 62 | if (window.xi_sensorDataArray[index].key === reporterArrayEntry.key) { 63 | found = true; 64 | console.log("sendInputPinReq entry exists"); 65 | } 66 | } 67 | 68 | 69 | // it is unique so go ahead and add the record to the array 70 | if (found === false) { 71 | window.xi_sensorDataArray.push(reporterArrayEntry); 72 | //} 73 | // now we can safely send the set pin message to the Xi Server to create the device 74 | if (window.xi_debugLevel >= 1) 75 | console.log('sendInputPinRequest: msg = ' + msgToServer + ' index = ' + wsIndex); 76 | window.xi_webSocketsArray[wsIndex].ws.send(msgToServer); 77 | } 78 | } 79 | 80 | 81 | if( window.xi_beenHereDoneThat === undefined) 82 | { 83 | alert("The first block of Xi script must be either a Set Debug Level or Set IPAddress block."); 84 | } 85 | 86 | 87 | // Set the pin mode command block handler 88 | 89 | if (window.xi_debugLevel >= 1) 90 | console.log('Set Pin Mode - board: ' + boardID + ' Mode: ' + mode + ' Pin: ' + pin); 91 | // make sure the websocket for the board was previously established 92 | for (var index = 0; index < window.xi_webSocketsArray.length; index++) { 93 | if (window.xi_webSocketsArray[index].id === boardID) { 94 | // send message to server to create device(input devices) or set the pin mode (output device) 95 | var messageToServer; // message to be sent to server 96 | 97 | // the mode is the value prescribed in block descriptor section 98 | switch (mode) { 99 | // set pin to digital input mode 100 | // msg: setDigitalIN - digital service input 101 | case 'Digital Input': 102 | if (window.xi_debugLevel >= 1) 103 | console.log('pin mode digital input'); 104 | // build the message to send to the Xi Server 105 | // we use the length of the array as the index/device id 106 | messageToServer = 'setDigitalIN/' + boardID + '/' + pin + '/' + window.xi_sensorDataArray.length; 107 | sendSetInputPinRequest(messageToServer, 'd', boardID, pin, index); 108 | break; 109 | // set pin to digital out 110 | // msg: setDigitalOUT 111 | case 'Digital Output': 112 | 113 | if (window.xi_debugLevel >= 1) 114 | console.log('pin mode digital output'); 115 | messageToServer = 'setDigitalOUT/' + boardID + '/' + pin; 116 | if (window.xi_debugLevel >= 2) 117 | console.log('pinMode Digital Out Msg to server: ' + messageToServer); 118 | window.xi_webSocketsArray[index].ws.send(messageToServer); 119 | break; 120 | // set pin to analog in 121 | // msg: setAnalogIN 122 | case 'Analog Sensor Input': 123 | if (window.xi_debugLevel >= 1) 124 | console.log('pin mode analog input'); 125 | // build the message to send to the Xi Server 126 | // we use the length of the array as the index/device id 127 | messageToServer = 'setAnalogIN/' + window.xi_sensorDataArray.length + '/' + boardID + '/' + pin; 128 | sendSetInputPinRequest(messageToServer, 'a', boardID, pin, index); 129 | break; 130 | // set pin mode PWM 131 | // msg: setAnalogOUT 132 | case 'Analog (PWM) Output': 133 | if (window.xi_debugLevel >= 1) 134 | console.log('pin mode PWM'); 135 | // send out the pwm mode message 136 | // the host tests if the pin is PWM and if not will send back an 'xp' message 137 | messageToServer = 'setAnalogOUT/' + boardID + '/' + pin; 138 | if (window.xi_debugLevel >= 2) 139 | console.log('pinMode PWM Out Msg to server: ' + messageToServer); 140 | window.xi_webSocketsArray[index].ws.send(messageToServer); 141 | break; 142 | // set pin mode Servo Standard 143 | // msg: setStandardServoMode 144 | case 'Standard Servo (PWM)': 145 | if (window.xi_debugLevel >= 1) 146 | console.log('pin mode Standard Servo'); 147 | messageToServer = 'setStandardServoMode/' + boardID + '/' + pin; 148 | if (window.xi_debugLevel >= 2) 149 | console.log('pinMode PWM Out Msg to server: ' + messageToServer); 150 | window.xi_webSocketsArray[index].ws.send(messageToServer); 151 | break; 152 | // set pin mode Servo Continous 153 | // msg: setContinuousServoMode 154 | case 'Continuous Servo (PWM)': 155 | if (window.xi_debugLevel >= 1) 156 | console.log('pin mode Continuous Servo'); 157 | messageToServer = 'setContinuousServoMode/' + boardID + '/' + pin; 158 | if (window.xi_debugLevel >= 2) 159 | console.log('pinMode PWM Out Msg to server: ' + messageToServer); 160 | window.xi_webSocketsArray[index].ws.send(messageToServer); 161 | break; 162 | // set pin mode Infrared distance 163 | // msg: setInfraRedDistanceMode 164 | case 'Infrared Distance (GP2Y0A21YK) - (Analog In)': 165 | if (window.xi_debugLevel >= 1) 166 | console.log('pin mode Infrared Distance'); 167 | messageToServer = 'setInfraRedDistanceMode/' + boardID + '/' + pin + '/' + window.xi_sensorDataArray.length; 168 | if (window.xi_debugLevel >= 2) 169 | console.log('pinMode PWM Out Msg to server: ' + messageToServer); 170 | sendSetInputPinRequest(messageToServer, 'a', boardID, pin, index); 171 | break; 172 | // set pin mode Sonar distance - hc-sr04 173 | // msg: setSonarMode 174 | case 'SONAR Distance - (Digital In)': 175 | alert('If you are using an Arduino, this feature requires a special version of StandardFirmata.' + 176 | '\n\rSee:\n\rhttps://github.com/rwaldron/johnny-five/wiki/Sonar\n\rfor details.'); 177 | if (window.xi_debugLevel >= 1) 178 | console.log('pin mode Sonar Distance'); 179 | messageToServer = 'setSonarMode/' + boardID + '/' + pin + '/' + window.xi_sensorDataArray.length; 180 | if (window.xi_debugLevel >= 2) 181 | console.log('pinMode PWM Out Msg to server: ' + messageToServer); 182 | sendSetInputPinRequest(messageToServer, 'd', boardID, pin, index); 183 | break; 184 | // set pin mode to tone 185 | // msg: setToneMode 186 | case 'Tone (Piezo) - (Digital Out)': 187 | if (window.xi_debugLevel >= 1) 188 | console.log('pin mode TONE'); 189 | // send out the tone mode message 190 | messageToServer = 'setToneMode/' + boardID + '/' + pin; 191 | if (window.xi_debugLevel >= 2) 192 | console.log('pinMode Tone Mode Out Msg to server: ' + messageToServer); 193 | window.xi_webSocketsArray[index].ws.send(messageToServer); 194 | break; 195 | default: 196 | if (window.xi_debugLevel >= 1) 197 | console.log('ext.pinMode: Unknown mode - ', +mode); 198 | } 199 | // just return from here after processing the command 200 | return; 201 | } 202 | } 203 | // board not yet established 204 | alert('Board ' + boardID + ' IP address must be set before a board is used'); 205 | 206 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/playTone.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * playTone 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 11/7/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | 37 | function sendCommand(msg, board, type) { 38 | if (window.xi_debugLevel >= 1) { 39 | console.log('sendCommand: ' + msg + ' ' + board + ' ' + type); 40 | } 41 | for (var index = 0; index < window.xi_webSocketsArray.length; index++) { 42 | if (window.xi_webSocketsArray[index].id === board) { 43 | if (window.xi_debugLevel >= 2) 44 | console.log('sendCommand: Message: ' + msg + ' board: ' + boardID); 45 | // send out message 46 | window.xi_webSocketsArray[index].ws.send(msg); 47 | return; 48 | } 49 | } 50 | // board was not established 51 | alert(type + ' IP address for board ' + boardID + ' was not set'); 52 | } 53 | 54 | if( window.xi_beenHereDoneThat === undefined) 55 | { 56 | alert("The first block of Xi script must be either a Set Debug Level or Set IPAddress block."); 57 | } 58 | var msg = 'playTone/' + boardID + '/' + pin + '/' + frequency + '/' + duration; 59 | sendCommand(msg, boardID, 'playTone'); 60 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/setBoardIpAddress1.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * setBoardIpAddress1 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 9/21/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | function noServerAlert() { 37 | alert('Server not responding. Did you start XiServer for board ' + 38 | boardID + '? Start the server, reload this page and try again'); 39 | } 40 | 41 | 42 | if (window.xi_beenHereDoneThat === undefined || window.xi_beenHereDoneThat === false) { 43 | if (window.xi_debugLevel >= 0) { 44 | console.log('Globals defined'); 45 | } 46 | // 0 = no debug 47 | // 1 = low level debug 48 | // 2 = high - open the floodgates 49 | // Variable is set by user through a Scratch command block 50 | window.xi_debugLevel = 0; 51 | 52 | // Board IP addresses and ports are set by the user with a Scratch command block, and the information 53 | // is stored in the WebSocket Array 54 | 55 | // WebSocket array 56 | // Each board will have an associated WebSocket instance used for communication 57 | // with the Xi client. webSocketsArray is an array of objects. Each object has a format of: 58 | // {id: BoardID, , ip: IPAddress, port: port, ws: WebSocketReference} 59 | 60 | window.xi_webSocketsArray = []; 61 | 62 | // Sensor Data Array 63 | // This is an array of objects that store the latest sensor or switch value updates received from all Xi servers. 64 | // The object format is: 65 | // key: generated by genReporterKey() 66 | // value: latest updated value 67 | // A key is used to uniquely identify each data entry with a specific sensor on a specific server 68 | window.xi_sensorDataArray = []; 69 | 70 | window.xi_beenHereDoneThat = true; 71 | } 72 | else { 73 | if (window.xi_debugLevel >= 0) { 74 | console.log('Globals previously defined'); 75 | } 76 | } 77 | 78 | /*****************************************************************************************************/ 79 | /*********************************** Scratch Program Block Handlers, ******************************/ 80 | /*****************************************************************************************************/ 81 | 82 | // Associate a handler for each block described in the blocks section below 83 | 84 | /******************************* 85 | **** Command Block Handlers **** 86 | *******************************/ 87 | 88 | // Accepts IP Address and Port information for each board that the user adds 89 | // The associated scratch block is a 'wait' command block. 90 | // We don't want Scratch to continue until the socket is open bidirectionally. 91 | // When socket.onopen is called the callback is returned so that scratch can proceed processing 92 | 93 | var timeoutID; // need to set a timeout when a socket is created because we are using a 'wait' block 94 | 95 | 96 | if (window.xi_debugLevel >= 1) 97 | console.log('setBoard: ' + boardID, ipAddress, port); 98 | 99 | // Check to make sure that this board was not entered previously 100 | for (var index = 0; index < window.xi_webSocketsArray.length; index++) { 101 | if (window.xi_webSocketsArray[index].id === boardID) { 102 | // allow user to reset the board to the same value - for stop and start 103 | if ((window.xi_webSocketsArray[index].ip === ipAddress) && (window.xi_webSocketsArray[index].port === port)) { 104 | //alert('An IP entry already exists for board ' + boardID + '!'); 105 | return; // no need to go further 106 | } 107 | } 108 | } 109 | 110 | 111 | // This is a confirmed unique entry. Create a websocket for this board 112 | var socket = new WebSocket('ws://' + ipAddress + ':' + port); 113 | 114 | 115 | 116 | // add the entry including the websocket reference just created 117 | window.xi_webSocketsArray.push({'id': boardID, 'ip': ipAddress, 'port': port, 'ws': socket}); 118 | 119 | if (window.xi_debugLevel >= 1) { 120 | console.log('Number of entries in webSocketsArray: ' + window.xi_webSocketsArray.length); 121 | } 122 | 123 | // start the timer for a server reply - we wait for up to 2 seconds for the reply 124 | timeoutID = window.setTimeout(noServerAlert, 2000); 125 | 126 | // attach an onopen handler to this socket. This message is sent by a servers websocket 127 | socket.onopen = function (event) { 128 | window.clearTimeout(timeoutID); 129 | 130 | if (window.xi_debugLevel >= 1) 131 | console.log('onopen message received'); 132 | // how many boards are attached 133 | 134 | socket.send('Xi4sOnline'); 135 | 136 | }; 137 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/setBoardIpAddress2.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * setBoardIpAddress2 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 9/21/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | var socket; 37 | 38 | // find the socket 39 | for (var index = 0; index < window.xi_webSocketsArray.length; index++) { 40 | if (window.xi_webSocketsArray[index].id === boardID) { 41 | // allow user to reset the board to the same value - for stop and start 42 | if ((window.xi_webSocketsArray[index].ip === ipAddress) && (window.xi_webSocketsArray[index].port === port)) { 43 | 44 | socket = window.xi_webSocketsArray[index].ws ; 45 | console.log("socket found") ; 46 | } 47 | } 48 | } 49 | 50 | /********************** websocket 'onmessage' handler *************************************/ 51 | // 52 | // All messages sent from board's socket are handled here. 53 | // Attach an onmessage event handler to this socket. 54 | // Process messages received from the server associated with this socket. 55 | socket.onmessage = function (message) { 56 | if (window.xi_debugLevel === 1) 57 | console.log('onmessage received: ' + message.data); 58 | 59 | // All message components are delimited with '/' character. 60 | // TODO: Should this be done with JSON? 61 | 62 | // Incoming messages are split into their component pieces and placed into a 'msg' array 63 | // msg[0] for each message is the message ID. 64 | var msg = message.data.split('/'); 65 | 66 | // process each message ID 67 | switch (msg[0]) { 68 | // dataUpdate - server data update data message 69 | case 'dataUpdate': 70 | var index = msg[1]; // unique value used as an index into xi_sensorDataArray 71 | var data = msg[2]; // data value to be entered into xi_sensorDataArray 72 | if (window.xi_debugLevel >= 2) 73 | console.log('sensorData: index = ' + index + ' data = ' + data); 74 | // update the array with the new value 75 | window.xi_sensorDataArray[index].value = data; 76 | break; 77 | 78 | /*************************************** 79 | ************** server detected errors 80 | ****************************************/ 81 | 82 | // server detected a problem in setting the mode of this pin 83 | case 'invalidSetMode': 84 | alert("Invalid Mode Is Being Attempted To Be Set For This Pin"); 85 | break; 86 | case 'invalidPinCommand': 87 | alert("This Pin Was Not Configured For This Mode"); 88 | break; 89 | default: 90 | if (window.xi_debugLevel >= 1) 91 | console.log('onmessage unknown message received'); 92 | } 93 | }; 94 | 95 | /********************** websocket 'onmessage' handler *************************************/ 96 | // 97 | // All messages sent from board's socket are handled here. 98 | // Attach an onmessage event handler to this socket. 99 | // Process messages received from the server associated with this socket. 100 | socket.onmessage = function (message) { 101 | if (window.xi_debugLevel === 1) 102 | console.log('onmessage received: ' + message.data); 103 | 104 | // All message components are delimited with '/' character. 105 | // TODO: Should this be done with JSON? 106 | 107 | // Incoming messages are split into their component pieces and placed into a 'msg' array 108 | // msg[0] for each message is the message ID. 109 | var msg = message.data.split('/'); 110 | 111 | // process each message ID 112 | switch (msg[0]) { 113 | // dataUpdate - server data update data message 114 | case 'dataUpdate': 115 | var index = msg[1]; // unique value used as an index into xi_sensorDataArray 116 | var data = msg[2]; // data value to be entered into xi_sensorDataArray 117 | if (window.xi_debugLevel >= 2) 118 | console.log('sensorData: index = ' + index + ' data = ' + data); 119 | // update the array with the new value 120 | window.xi_sensorDataArray[index].value = data; 121 | break; 122 | 123 | /*************************************** 124 | ************** server detected errors 125 | ****************************************/ 126 | 127 | // server detected a problem in setting the mode of this pin 128 | case 'invalidSetMode': 129 | alert("Invalid Mode Is Being Attempted To Be Set For This Pin"); 130 | break; 131 | case 'invalidPinCommand': 132 | alert("This Pin Was Not Configured For This Mode"); 133 | break; 134 | default: 135 | if (window.xi_debugLevel >= 1) 136 | console.log('onmessage unknown message received'); 137 | } 138 | }; 139 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/setDebugLevel.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * setDebugLevel 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 9/21/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | 37 | if (window.xi_beenHereDoneThat === undefined || window.xi_beenHereDoneThat === false) { 38 | if (window.xi_debugLevel >= 0) { 39 | console.log('Globals defined'); 40 | } 41 | 42 | // 0 = no debug 43 | // 1 = low level debug 44 | // 2 = high - open the floodgates 45 | // Variable is set by user through a Scratch command block 46 | window.xi_debugLevel = 0; 47 | 48 | // Board IP addresses and ports are set by the user with a Scratch command block, and the information 49 | // is stored in the WebSocket Array 50 | 51 | // WebSocket array 52 | // Each board will have an associated WebSocket instance used for communication 53 | // with the Xi client. webSocketsArray is an array of objects. Each object has a format of: 54 | // {id: BoardID, , ip: IPAddress, port: port, ws: WebSocketReference} 55 | 56 | window.xi_webSocketsArray = []; 57 | 58 | // Sensor Data Array 59 | // This is an array of objects that store the latest sensor or switch value updates received from all Xi servers. 60 | // The object format is: 61 | // key: generated by genReporterKey() 62 | // value: latest updated value 63 | // A key is used to uniquely identify each data entry with a specific sensor on a specific server 64 | window.xi_sensorDataArray = []; 65 | 66 | window.xi_beenHereDoneThat = true; 67 | 68 | } 69 | else { 70 | if (window.xi_debugLevel >= 0) { 71 | console.log("Globals Previously Defined"); 72 | } 73 | } 74 | 75 | window.xi_debugLevel = level; 76 | 77 | if (window.xi_debugLevel >= 0) { 78 | console.log('Debug Level Set to ' + window.xi_debugLevel); 79 | } 80 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/setPinMode.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * moveStandardServo 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 9/21/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | 37 | function sendCommand(msg, board, type) { 38 | if (window.xi_debugLevel >= 1) { 39 | console.log('sendCommand: ' + msg + ' ' + board + ' ' + type); 40 | } 41 | for (var index = 0; index < window.xi_webSocketsArray.length; index++) { 42 | if (window.xi_webSocketsArray[index].id === board) { 43 | if (window.xi_debugLevel >= 2) 44 | console.log('sendCommand: Message: ' + msg + ' board: ' + board); 45 | // send out message 46 | window.xi_webSocketsArray[index].ws.send(msg); 47 | return; 48 | } 49 | } 50 | // board was not established 51 | alert(type + ' IP address for board ' + boardID + ' was not set'); 52 | } 53 | 54 | if( window.xi_beenHereDoneThat === undefined) 55 | { 56 | alert("The first block of Xi script must be either a Set Debug Level or Set IPAddress block."); 57 | } 58 | 59 | var msg = 'moveStandardServo/' + board + '/' + pin + '/' + degrees + '/' + inversion; 60 | sendCommand(msg, board, 'moveStandardServo'); 61 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/setPins4DriverStepper.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * set pins for driver stepper 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 11/7/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | alert('If you are using an Arduino, this feature requires a special version of StandardFirmata.' + 37 | '\n\rSee:\n\rhttps://github.com/soundanalogous/AdvancedFirmata\n\rfor details.'); 38 | 39 | // return unique values contained within an array 40 | function onlyUnique(value, index, self) { 41 | return self.indexOf(value) === index; 42 | } 43 | 44 | 45 | function sendCommand(msg, boardID, type) { 46 | if (window.xi_debugLevel >= 1) { 47 | console.log('sendCommand: ' + msg + ' ' + boardID + ' ' + type); 48 | } 49 | for (var index = 0; index < window.xi_webSocketsArray.length; index++) { 50 | if (window.xi_webSocketsArray[index].id === boardID) { 51 | if (window.xi_debugLevel >= 2) 52 | console.log('sendCommand: Message: ' + msg + ' board: ' + boardID); 53 | // send out message 54 | window.xi_webSocketsArray[index].ws.send(msg); 55 | return; 56 | } 57 | } 58 | // board was not established 59 | alert(type + ' IP address for board ' + boardID + ' was not set'); 60 | } 61 | 62 | if( window.xi_beenHereDoneThat === undefined) 63 | { 64 | alert("The first block of Xi script must be either a Set Debug Level or Set IPAddress block."); 65 | } 66 | 67 | var pinArray = []; 68 | 69 | pinArray.push(pinA); 70 | pinArray.push(pinB); 71 | 72 | 73 | var unique = pinArray.filter(onlyUnique); 74 | if (window.xi_debugLevel >= 2) 75 | console.log('stepperDriverPins unique = ' + unique); 76 | 77 | if (window.xi_debugLevel >= 2) 78 | console.log('stepperDriverPins unique length = ' + unique.length); 79 | 80 | if (unique.length !== 2) { 81 | alert("The Two Pin Values Must Be Unique. Try Again!"); 82 | return; 83 | } 84 | var msg = 'stepperDriverPins/' + boardID + '/' + pinA + '/' + pinB + '/' + spr; 85 | sendCommand(msg, boardID, 'stepperDriverPins'); 86 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/setPins4WireStepper.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * set pins for 4 wire stepper 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 11/7/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | alert('If you are using an Arduino, this feature requires a special version of StandardFirmata.' + 37 | '\n\rSee:\n\rhttps://github.com/soundanalogous/AdvancedFirmata\n\rfor details.'); 38 | 39 | // return unique values contained within an array 40 | function onlyUnique(value, index, self) { 41 | return self.indexOf(value) === index; 42 | } 43 | 44 | 45 | function sendCommand(msg, boardID, type) { 46 | if (window.xi_debugLevel >= 1) { 47 | console.log('sendCommand: ' + msg + ' ' + boardID + ' ' + type); 48 | } 49 | for (var index = 0; index < window.xi_webSocketsArray.length; index++) { 50 | if (window.xi_webSocketsArray[index].id === boardID) { 51 | if (window.xi_debugLevel >= 2) 52 | console.log('sendCommand: Message: ' + msg + ' board: ' + boardID); 53 | // send out message 54 | window.xi_webSocketsArray[index].ws.send(msg); 55 | return; 56 | } 57 | } 58 | // board was not established 59 | alert(type + ' IP address for board ' + boardID + ' was not set'); 60 | } 61 | 62 | if( window.xi_beenHereDoneThat === undefined) 63 | { 64 | alert("The first block of Xi script must be either a Set Debug Level or Set IPAddress block."); 65 | } 66 | 67 | var pinArray = []; 68 | 69 | pinArray.push(pinA); 70 | pinArray.push(pinB); 71 | pinArray.push(pinC); 72 | pinArray.push(pinD); 73 | 74 | var unique = pinArray.filter(onlyUnique); 75 | if (unique.length !== 4) { 76 | alert("The Four Pin Values Must Be Unique. Try Again!"); 77 | return; 78 | } 79 | var msg = 'fourWireStepperPins/' + boardID + '/' + pinA + '/' + pinB + '/' + pinC + '/' + pinD + '/' + stepsPerRev; 80 | sendCommand(msg, boardID, 'fourWireStepperPins'); 81 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/sonar.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * Sonar 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 11/7/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | // Using the supplied key, this function will retrieve the latest data from the sensorDataArray. 37 | 38 | 39 | 40 | function retrieveReporterData(boardID, pin, key) { 41 | // make sure that this is a unique key in the array 42 | for (var index = 0; index < window.xi_sensorDataArray.length; index++) { 43 | if (window.xi_sensorDataArray[index].key === key) { 44 | return window.xi_sensorDataArray[index].value 45 | } 46 | } 47 | // did not find an entry in the array 48 | alert('Did you set the pin mode for Board ' + boardID + ' Pin ' + pin + 49 | '? No entry for this block in database'); 50 | } 51 | 52 | 53 | 54 | 55 | //genReporterKey 56 | // Input: Board number 57 | // Pin number 58 | // Designator to differentiate between analog and digital - either 'a' or 'd' 59 | // 60 | // Returns the generated key 61 | function genReporterKey(boardID, pinNum, designator) { 62 | if (window.xi_debugLevel >= 1) { 63 | console.log('genReporterKey returns: ' + boardID + designator + pinNum); 64 | } 65 | return boardID + designator + pinNum; 66 | } 67 | 68 | 69 | 70 | var rVal; 71 | 72 | if( window.xi_beenHereDoneThat === undefined) 73 | { 74 | alert("The first block of Xi script must be either a Set Debug Level or Set IPAddress block."); 75 | } 76 | 77 | if (window.xi_debugLevel >= 1) 78 | { 79 | console.log('infrared distance Input - board: ' + boardID + ' Pin: ' + pin); 80 | } 81 | 82 | 83 | var key = genReporterKey(boardID, pin, 'd'); 84 | var distance = retrieveReporterData(boardID, pin, key); 85 | if (units === 'CM') { 86 | return (distance * 2.54).toFixed(2); 87 | } 88 | else { 89 | return distance; 90 | } 91 | 92 | if( window.xi_debugLevel >= 1) { 93 | console.log("infrared distance sensor Read returns: " + rVal) ; 94 | } 95 | return rVal; 96 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/stopServo.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * stopServo 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 11/7/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | 37 | function sendCommand(msg, board, type) { 38 | if (window.xi_debugLevel >= 1) { 39 | console.log('sendCommand: ' + msg + ' ' + board + ' ' + type); 40 | } 41 | for (var index = 0; index < window.xi_webSocketsArray.length; index++) { 42 | if (window.xi_webSocketsArray[index].id === board) { 43 | if (window.xi_debugLevel >= 2) 44 | console.log('sendCommand: Message: ' + msg + ' board: ' + boardID); 45 | // send out message 46 | window.xi_webSocketsArray[index].ws.send(msg); 47 | return; 48 | } 49 | } 50 | // board was not established 51 | alert(type + ' IP address for board ' + boardID + ' was not set'); 52 | } 53 | 54 | if( window.xi_beenHereDoneThat === undefined) 55 | { 56 | alert("The first block of Xi script must be either a Set Debug Level or Set IPAddress block."); 57 | } 58 | var msg = 'stopServo/' + boardID + '/' + pin; 59 | sendCommand(msg, boardID, 'stopServo'); 60 | 61 | -------------------------------------------------------------------------------- /clients/snap!/blockDescriptors/stopStepper.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * * * * Xi4Snap * * * 4 | * 5 | * stop Stepper 6 | * 7 | * 8 | * 9 | ********************************************************************************************* 10 | * Created by afy on 11/7/14. 11 | * 12 | * This is the Xi Client for Snap! 13 | * 14 | * Version v.001 15 | * 16 | * 17 | * @author: Alan Yorinks 18 | Copyright (c) 2014 Alan Yorinks All right reserved. 19 | 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public 22 | License as published by the Free Software Foundation; either 23 | version 3.0 of the License, or (at your option) any later version. 24 | 25 | This library is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28 | Lesser General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public 31 | License along with this library; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 33 | * 34 | */ 35 | 36 | 37 | 38 | function sendCommand(msg, boardID, type) { 39 | if (window.xi_debugLevel >= 1) { 40 | console.log('sendCommand: ' + msg + ' ' + boardID + ' ' + type); 41 | } 42 | for (var index = 0; index < window.xi_webSocketsArray.length; index++) { 43 | if (window.xi_webSocketsArray[index].id === boardID) { 44 | if (window.xi_debugLevel >= 2) 45 | console.log('sendCommand: Message: ' + msg + ' board: ' + boardID); 46 | // send out message 47 | window.xi_webSocketsArray[index].ws.send(msg); 48 | return; 49 | } 50 | } 51 | // board was not established 52 | alert(type + ' IP address for board ' + boardID + ' was not set'); 53 | } 54 | 55 | if( window.xi_beenHereDoneThat === undefined) 56 | { 57 | alert("The first block of Xi script must be either a Set Debug Level or Set IPAddress block."); 58 | } 59 | 60 | 61 | var msg = 'stopStepper/' + boardID + '/' + pin; 62 | sendCommand(msg, boardID, 'stopStepper'); 63 | -------------------------------------------------------------------------------- /documentation/Xi_Install_and_Usage_Guide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrYsLab/xi/2a5fbb47fce51498e115f94ae3d63d59b28937a1/documentation/Xi_Install_and_Usage_Guide.pdf -------------------------------------------------------------------------------- /documentation/Xi_TranslationGuide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrYsLab/xi/2a5fbb47fce51498e115f94ae3d63d59b28937a1/documentation/Xi_TranslationGuide.pdf -------------------------------------------------------------------------------- /documentation/drawings/BBB_PiezoWiring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrYsLab/xi/2a5fbb47fce51498e115f94ae3d63d59b28937a1/documentation/drawings/BBB_PiezoWiring.png -------------------------------------------------------------------------------- /documentation/drawings/BBB_infraredWiring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrYsLab/xi/2a5fbb47fce51498e115f94ae3d63d59b28937a1/documentation/drawings/BBB_infraredWiring.png -------------------------------------------------------------------------------- /documentation/drawings/BBBservoWiring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrYsLab/xi/2a5fbb47fce51498e115f94ae3d63d59b28937a1/documentation/drawings/BBBservoWiring.png -------------------------------------------------------------------------------- /documentation/drawings/RPiServoWiring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrYsLab/xi/2a5fbb47fce51498e115f94ae3d63d59b28937a1/documentation/drawings/RPiServoWiring.png -------------------------------------------------------------------------------- /documentation/drawings/RPiServoWiring2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrYsLab/xi/2a5fbb47fce51498e115f94ae3d63d59b28937a1/documentation/drawings/RPiServoWiring2.png -------------------------------------------------------------------------------- /documentation/drawings/StatusTable_31Dec14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrYsLab/xi/2a5fbb47fce51498e115f94ae3d63d59b28937a1/documentation/drawings/StatusTable_31Dec14.png -------------------------------------------------------------------------------- /documentation/drawings/XiLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrYsLab/xi/2a5fbb47fce51498e115f94ae3d63d59b28937a1/documentation/drawings/XiLogo.png -------------------------------------------------------------------------------- /documentation/drawings/xi2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrYsLab/xi/2a5fbb47fce51498e115f94ae3d63d59b28937a1/documentation/drawings/xi2.png -------------------------------------------------------------------------------- /documentation/xi.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrYsLab/xi/2a5fbb47fce51498e115f94ae3d63d59b28937a1/documentation/xi.ico -------------------------------------------------------------------------------- /documentation/xi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrYsLab/xi/2a5fbb47fce51498e115f94ae3d63d59b28937a1/documentation/xi.png -------------------------------------------------------------------------------- /documentation/xi.xpm: -------------------------------------------------------------------------------- 1 | /* XPM */ 2 | static char *xi_xpm[] = { 3 | "131 83 239 2", 4 | "AA c #622222", 5 | "BA c #632323", 6 | "CA c #642424", 7 | "DA c #652525", 8 | "EA c #652841", 9 | "FA c #A7D7FF", 10 | "GA c #FFFFFF", 11 | "HA c #FFD987", 12 | "IA c #7B2925", 13 | "JA c #652A49", 14 | "KA c #B1DFFF", 15 | "LA c #FFE295", 16 | "MA c #802B25", 17 | "NA c #652C51", 18 | "OA c #BBE7FF", 19 | "PA c #FFEAA3", 20 | "QA c #862D25", 21 | "RA c #652E58", 22 | "SA c #C4EFFF", 23 | "TA c #FFF1B1", 24 | "UA c #8C2F25", 25 | "VA c #653060", 26 | "WA c #CDF6FF", 27 | "XA c #FFF9BF", 28 | "YA c #913125", 29 | "ZA c #653368", 30 | "aA c #D7FDFF", 31 | "bA c #FFFFCB", 32 | "cA c #983425", 33 | "dA c #662626", 34 | "eA c #673A75", 35 | "fA c #DDFFFF", 36 | "gA c #FFFFD3", 37 | "hA c #A23D29", 38 | "iA c #684183", 39 | "jA c #E3FFFF", 40 | "kA c #FEFBF8", 41 | "lA c #F8F2F0", 42 | "mA c #F2ECEC", 43 | "nA c #F2ECED", 44 | "oA c #F4F2F6", 45 | "pA c #FAFBFE", 46 | "qA c #FFFFDA", 47 | "rA c #AC452A", 48 | "sA c #6A4990", 49 | "tA c #E8FFFF", 50 | "uA c #FFFFFC", 51 | "vA c #DA7D4B", 52 | "wA c #73312B", 53 | "xA c #672626", 54 | "yA c #662628", 55 | "zA c #6A3139", 56 | "0A c #807AC9", 57 | "1A c #FCFFFF", 58 | "2A c #FFFFE2", 59 | "3A c #B64D2C", 60 | "4A c #6B519D", 61 | "5A c #EEFFFF", 62 | "6A c #AB452A", 63 | "7A c #684284", 64 | "8A c #FFFFEB", 65 | "9A c #BF552E", 66 | "+A c #6D58AB", 67 | "/A c #F3FFFF", 68 | "AB c #FFEBA6", 69 | "BB c #882F26", 70 | "CB c #662E54", 71 | "DB c #BEE9FF", 72 | "EB c #FFFFF2", 73 | "FB c #C95C30", 74 | "GB c #6E61B8", 75 | "HB c #F8FFFF", 76 | "IB c #FCC36B", 77 | "JB c #712726", 78 | "KB c #662634", 79 | "LB c #94C0F9", 80 | "MB c #FFFFFA", 81 | "NB c #D36532", 82 | "OB c #6F68C6", 83 | "PB c #FEFFFF", 84 | "QB c #E48644", 85 | "RB c #682626", 86 | "SB c #662629", 87 | "TB c #7983D7", 88 | "UB c #DB6F36", 89 | "VB c #672727", 90 | "WB c #6A3A53", 91 | "XB c #906464", 92 | "YB c #92635F", 93 | "ZB c #83372A", 94 | "aB c #69364E", 95 | "bB c #8E6364", 96 | "cB c #926461", 97 | "dB c #873C2C", 98 | "eB c #672F55", 99 | "fB c #BAD7ED", 100 | "gB c #F5F0E3", 101 | "hB c #CB6837", 102 | "iB c #682727", 103 | "jB c #672728", 104 | "kB c #7165B3", 105 | "lB c #EAEFF1", 106 | "mB c #F2D9A1", 107 | "nB c #882F27", 108 | "oB c #67305B", 109 | "pB c #C6F0FF", 110 | "qB c #F4AF5C", 111 | "rB c #6E2727", 112 | "sB c #67272F", 113 | "tB c #8AA9EC", 114 | "uB c #FFF2B3", 115 | "vB c #8D3127", 116 | "wB c #FFE194", 117 | "xB c #812D27", 118 | "yB c #672B48", 119 | "zB c #AFDCFF", 120 | "0B c #C48A81", 121 | "1B c #A68181", 122 | "2B c #A688A8", 123 | "3B c #682828", 124 | "4B c #68315B", 125 | "5B c #C7F0FF", 126 | "6B c #8E3228", 127 | "7B c #692929", 128 | "8B c #69325C", 129 | "9B c #FFF4B7", 130 | "+B c #923529", 131 | "/B c #69335E", 132 | "AC c #C8F0FF", 133 | "BC c #8F3329", 134 | "CC c #FBC46F", 135 | "DC c #752A29", 136 | "EC c #692A37", 137 | "FC c #97BFF8", 138 | "GC c #692A2F", 139 | "HC c #733F41", 140 | "IC c #7A4141", 141 | "JC c #793B31", 142 | "KC c #6B2929", 143 | "LC c #DF7C40", 144 | "MC c #69292B", 145 | "NC c #7878CD", 146 | "OC c #6E3B40", 147 | "PC c #7A3F38", 148 | "QC c #6D2A29", 149 | "RC c #69325B", 150 | "SC c #C5EEFF", 151 | "TC c #FFE399", 152 | "UC c #842F29", 153 | "VC c #692E4B", 154 | "WC c #B1DEFF", 155 | "XC c #8E3229", 156 | "YC c #693155", 157 | "ZC c #BDE8FF", 158 | "aC c #FFFDC8", 159 | "bC c #9B3A2A", 160 | "cC c #69366A", 161 | "dC c #D4FAFF", 162 | "eC c #893129", 163 | "fC c #692F4E", 164 | "gC c #B5E2FF", 165 | "hC c #FFFFE8", 166 | "iC c #BE5531", 167 | "jC c #6D5099", 168 | "kC c #ECFFFF", 169 | "lC c #FFE49B", 170 | "mC c #853029", 171 | "nC c #6A2A2A", 172 | "oC c #6A2E48", 173 | "pC c #AEDBFF", 174 | "qC c #DE793E", 175 | "rC c #6B2A2A", 176 | "sC c #6A2A2B", 177 | "tC c #7671C9", 178 | "uC c #FFDE90", 179 | "vC c #812F2A", 180 | "wC c #6A2C42", 181 | "xC c #A6D4FF", 182 | "yC c #F2AB5A", 183 | "zC c #702A2A", 184 | "0C c #6A2A32", 185 | "1C c #88A2E8", 186 | "2C c #FFD885", 187 | "3C c #7D2D2A", 188 | "4C c #6A2B3C", 189 | "5C c #9ECEFF", 190 | "6C c #FFD480", 191 | "7C c #7B2C2A", 192 | "8C c #6A2C3E", 193 | "9C c #A1CFFF", 194 | "+C c #FFD17A", 195 | "/C c #792C2A", 196 | "AD c #6A2A37", 197 | "BD c #97C6FC", 198 | "CD c #FFFFF4", 199 | "DD c #E9D0C7", 200 | "ED c #D4BCB9", 201 | "FD c #CCB5B4", 202 | "GD c #CAB4B4", 203 | "HD c #CBB5B7", 204 | "ID c #CEBCC1", 205 | "JD c #D8CFDE", 206 | "KD c #F6FFFF", 207 | "LD c #FFCB6F", 208 | "MD c #742A2A", 209 | "ND c #6A2A35", 210 | "OD c #92BBF7", 211 | "PD c #FBC268", 212 | "QD c #722A2A", 213 | "RD c #6A2A33", 214 | "SD c #8EB0F0", 215 | "TD c #F7B661", 216 | "UD c #712A2A", 217 | "VD c #8AA5E9", 218 | "WD c #F3AB5B", 219 | "XD c #6B2B2B", 220 | "YD c #6B2B31", 221 | "ZD c #869AE4", 222 | "aD c #EFA155", 223 | "bD c #702B2B", 224 | "cD c #6B2B2F", 225 | "dD c #818EDD", 226 | "eD c #EA9650", 227 | "fD c #6F2B2B", 228 | "gD c #6B2B2D", 229 | "hD c #7D83D6", 230 | "iD c #E68B49", 231 | "jD c #6E2B2B", 232 | "kD c #6B2B2C", 233 | "lD c #7877D0", 234 | "mD c #E18043", 235 | "nD c #6D2B2B", 236 | "oD c #746EC9", 237 | "pD c #DD753C", 238 | "qD c #6C2B2B", 239 | "rD c #6C2C2C", 240 | "sD c #6D2D2D", 241 | "tD c #6E2E2E", 242 | "uD c #6F2F2F", 243 | "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 244 | "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 245 | "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 246 | "BABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABA", 247 | "BABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABA", 248 | "BABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABA", 249 | "BABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABA", 250 | "BABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABA", 251 | "BABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABA", 252 | "CACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACA", 253 | "CACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACA", 254 | "CACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACA", 255 | "CACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACA", 256 | "CACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACA", 257 | "CACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACA", 258 | "DADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADAEAFAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAHAIADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADA", 259 | "DADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADAJAKAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGALAMADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADA", 260 | "DADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADANAOAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAPAQADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADA", 261 | "DADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADARASAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGATAUADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADA", 262 | "DADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADAVAWAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAXAYADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADA", 263 | "DADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADAZAaAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAbAcADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADA", 264 | "dAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAeAfAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAgAhAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdA", 265 | "dAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAiAjAGAGAGAGAGAkAlAmAmAmAmAmAmAmAmAmAmAmAmAmAmAmAmAmAnAoApAGAGAGAGAGAqArAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdA", 266 | "dAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAsAtAGAuAvAwAxAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAyAzA0A1AGA2A3AdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdA", 267 | "dAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdA4A5AGAqA6AdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdA7AjAGA8A9AdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdA", 268 | "dAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdA+A/AGAABBBdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdACBDBGAEBFBdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdA", 269 | "dAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAGBHBGAIBJBdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAKBLBGAMBNBdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdA", 270 | "dAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAOBPBGAQBRBdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdASBTBGAGAUBdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdAdA", 271 | "VBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBWBXBYBZBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBaBbBcBdBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVB", 272 | "VBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBeBfBgBhBiBVBVBVBVBVBVBVBVBVBVBVBVBjBkBlBmBnBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVB", 273 | "VBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBoBpBGAqBrBVBVBVBVBVBVBVBVBVBVBVBVBsBtBGAuBvBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVB", 274 | "VBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBoBpBGAwBxBVBVBVBVBVBVBVBVBVBVBVBVByBzBGAuBvBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVB", 275 | "VBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBoBpBGA2A0B1B1B1B1B1B1B1B1B1B1B1B1B2BtAGAuBvBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVB", 276 | "VBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBoBpBGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAuBvBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVBVB", 277 | "3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B4B5BGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAuB6B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B", 278 | "3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B4B5BGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAuB6B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B", 279 | "3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B4B5BGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAuB6B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B", 280 | "3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B4B5BGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAuB6B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B", 281 | "3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B4B5BGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAuB6B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B", 282 | "3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B4B5BGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAuB6B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B", 283 | "7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B8B5BGA9B+B7B7B7B7B7B7B7B7B7B7B7B7B/BACGAuBBC7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B", 284 | "7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B8B5BGACCDC7B7B7B7B7B7B7B7B7B7B7B7BECFCGAuBBC7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B", 285 | "7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7BGCHCICJCKC7B7B7B7B8B5BGALCKC7B7B7B7B7B7B7B7B7B7B7B7BMCNCPBuBBC7B7B7B7BMCOCICPCQC7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B", 286 | "7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7BRCSCGATCUC7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7BVCWCGATAXC7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B", 287 | "7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7BYCZCGAaCbC7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7BcCdCGAABeC7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B", 288 | "7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7BfCgCGAhCiC7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7BjCkCGAlCmC7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B", 289 | "nCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCoCpCGAGAqCrCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCsCtCPBGAuCvCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnC", 290 | "nCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCwCxCGAGAyCzCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnC0C1CGAGA2C3CnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnC", 291 | "nCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnC4C5CGAGA6C7CnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnC8C9CGAGA+C/CnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnC", 292 | "nCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCADBDGAGACDDDEDFDGDGDGDGDGDGDGDGDGDGDGDGDGDGDGDGDGDGDGDGDGDGDGDGDHDIDJDKDGAGALDMDnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnC", 293 | "nCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCNDODGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAPDQDnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnC", 294 | "nCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCRDSDGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGATDUDnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnC", 295 | "nCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnC0CVDGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAWDzCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnCnC", 296 | "XDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDYDZDGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAaDbDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXD", 297 | "XDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDcDdDGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAeDfDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXD", 298 | "XDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDgDhDGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAiDjDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXD", 299 | "XDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDkDlDGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAmDnDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXD", 300 | "XDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDoDPBGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGAGApDqDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXD", 301 | "XDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXD", 302 | "rDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrD", 303 | "rDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrD", 304 | "rDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrD", 305 | "rDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrD", 306 | "rDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrD", 307 | "rDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrDrD", 308 | "sDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsD", 309 | "sDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsD", 310 | "sDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsD", 311 | "sDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsD", 312 | "sDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsD", 313 | "sDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsDsD", 314 | "tDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtD", 315 | "tDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtD", 316 | "tDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtD", 317 | "tDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtD", 318 | "tDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtD", 319 | "tDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtD", 320 | "tDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtDtD", 321 | "uDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuD", 322 | "uDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuD", 323 | "uDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuD", 324 | "uDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuD", 325 | "uDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuDuD" 326 | }; 327 | -------------------------------------------------------------------------------- /documentation/xi3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrYsLab/xi/2a5fbb47fce51498e115f94ae3d63d59b28937a1/documentation/xi3.png -------------------------------------------------------------------------------- /servers/xibone/xibone.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "setting current data and time from ntp" 3 | sudo ntpdate -b pool.ntp.org 4 | echo "Starting XiBone server..." 5 | sudo node ~/xibone/xiserver.js bbb 6 | 7 | -------------------------------------------------------------------------------- /servers/xibone/xiboneInstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # XiBone install script 3 | 4 | # No need to install node.js - beaglebone black comes with it preloaded 5 | 6 | echo "Installing XiBone ..." 7 | echo "" 8 | echo "setting current data and time from ntp" 9 | sudo ntpdate -b pool.ntp.org 10 | 11 | echo "creating xibone directory" 12 | mkdir -p ~/xibone 13 | 14 | echo "change permissions on xibone.sh" 15 | sudo chmod ugo+x ./xibone.sh 16 | 17 | echo "copying xiduino.sh" 18 | cp ./xibone.sh ~/xibone/. 19 | 20 | echo "copying xiserver.js" 21 | cp ../xiserver/xiserver.js ~/xibone/. 22 | 23 | echo "loading node-gyp" 24 | sudo npm install --prefix ~/xibone node-gyp 25 | 26 | echo "loading johnny-five" 27 | sudo npm install --prefix ~/xibone johnny-five 28 | 29 | echo "loading beaglebone-io" 30 | 31 | sudo npm install --prefix ~/xibone beaglebone-io 32 | 33 | echo "loading websocket" 34 | 35 | sudo npm install --prefix ~/xibone websocket 36 | 37 | echo "installing winston" 38 | 39 | sudo npm install --prefix ~/xibone winston 40 | 41 | echo "installing open" 42 | sudo npm install --prefix ~/xibone open 43 | 44 | echo "!!!! Install Complete !!!" 45 | echo 46 | echo 47 | echo "To start XiBone: " 48 | echo " cd ~/xibone" 49 | echo " bash xibone.sh" 50 | echo 51 | echo "You may be prompted for sudo priveleges". 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /servers/xiduino/linux/xiduino.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Starting XiDuino server..." 3 | sudo node ~/xiduino/xiserver.js ard 4 | 5 | 6 | -------------------------------------------------------------------------------- /servers/xiduino/linux/xiduino4Snap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Starting XiDuino server..." 3 | sudo node ~/xiduino/xiserver.js ard http://snap.berkeley.edu/snapsource/snap.html 4 | 5 | 6 | -------------------------------------------------------------------------------- /servers/xiduino/linux/xiduinoInstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # XiDuino NPM modules install script 3 | 4 | echo "Installing XiDuino ..." 5 | echo 6 | echo "creating xiduino directory" 7 | mkdir -p ~/xiduino 8 | 9 | echo "change permissions on xiduino.sh" 10 | sudo chmod ugo+x ./xiduino.sh 11 | 12 | echo "copying xiduino.sh" 13 | cp ./xiduino.sh ~/xiduino/. 14 | 15 | echo "copying xiduino4Snap.sh" 16 | cp ./xiduino4Snap.sh ~/xiduino/. 17 | 18 | echo "copying xiserver.js" 19 | cp ../../xiserver/xiserver.js ~/xiduino/. 20 | 21 | echo "Installing npm modules for Xiduino" 22 | echo "loading node-gyp" 23 | npm install --prefix ~/xiduino node-gyp 24 | 25 | echo "loading johnny-five" 26 | npm install --prefix ~/xiduino johnny-five 27 | 28 | echo "loading open" 29 | npm install --prefix ~/xiduino open 30 | 31 | echo "loading websocket" 32 | 33 | npm install --prefix ~/xiduino websocket 34 | 35 | echo "!!!! Install Complete !!!" 36 | echo 37 | echo 38 | echo "To start XiDuino: " 39 | echo " cd ~/xiduino" 40 | echo " bash xiduino.sh" 41 | echo 42 | echo "You may be prompted for sudo priveleges". 43 | 44 | -------------------------------------------------------------------------------- /servers/xiduino/windows/xiduino.bat: -------------------------------------------------------------------------------- 1 | 2 | rem XiDuino for Windows start script 3 | 4 | node xiserver.js ard 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /servers/xiduino/windows/xiduino4Snap.bat: -------------------------------------------------------------------------------- 1 | 2 | rem XiDuino for Windows start script 3 | 4 | node xiserver.js ard http://snap.berkeley.edu/snapsource/snap.html 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /servers/xiduino/windows/xiduinoInstall.bat: -------------------------------------------------------------------------------- 1 | 2 | rem XiDuino for Windows install script 3 | 4 | echo on 5 | 6 | echo "creating a xi directory for the user" 7 | mkdir "%userprofile%"\Documents\xi 8 | 9 | echo "copying files to the xi directory" 10 | copy xiduino.bat "%userprofile%"\Documents\xi 11 | copy xiduino4Snap.bat "%userprofile%"\Documents\xi 12 | copy ..\..\xiserver\xiserver.js "%userprofile%"\Documents\xi 13 | copy ..\..\..\documentation\xi.ico "%userprofile%"\Documents\xi 14 | copy ..\..\..\clients\scratch\projects\Xi4S_Starter_Project.sb2 "%userprofile%"\Documents\xi 15 | copy ..\..\..\documentation\Xi_Install_and_Usage_Guide.pdf "%userprofile%"\Documents\xi 16 | 17 | echo "installing node.js modules" 18 | call npm install --prefix "%userprofile%"\Documents\xi node-gyp 19 | call npm install --prefix "%userprofile%"\Documents\xi johnny-five 20 | call npm install --prefix "%userprofile%"\Documents\xi websocket 21 | call npm install --prefix "%userprofile%"\Documents\xi open 22 | echo "!!!! Install Complete !!!" 23 | echo 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /servers/xipi/xipi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Starting XiPi server..." 4 | sudo node ~/xipi/xiserver.js rpi null 5 | 6 | -------------------------------------------------------------------------------- /servers/xipi/xipiInstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # XiPi install script 3 | echo "Installing XiPi ..." 4 | echo 5 | echo "creating xipi directory" 6 | mkdir -p ~/xipi 7 | 8 | echo "change permissions on xipi.sh" 9 | sudo chmod ugo+x ./xipi.sh 10 | 11 | echo "copying xipi.sh" 12 | cp ./xipi.sh ~/xipi/. 13 | 14 | echo "copying xiserver.js" 15 | cp ../xiserver/xiserver.js ~/xipi/. 16 | 17 | echo "Installing npm modules for XiPi" 18 | 19 | echo "loading node-gyp" 20 | npm install --prefix ~/xipi node-gyp 21 | 22 | echo "loading johnny-five" 23 | 24 | npm install --prefix ~/xipi johnny-five 25 | 26 | echo "loading raspi-io" 27 | 28 | npm install --prefix ~/xipi raspi-io 29 | 30 | echo "loading websocket" 31 | 32 | npm install --prefix ~/xipi websocket 33 | 34 | echo "loading open" 35 | npm install --prefix ~/xipi open 36 | 37 | echo "!!!! Install Complete !!!" 38 | echo 39 | echo 40 | echo "To start XiPi: " 41 | echo " cd ~/xipi" 42 | echo " bash xipi.sh" 43 | echo 44 | echo "You may be prompted for sudo priveleges". 45 | -------------------------------------------------------------------------------- /servers/xiserver/xiserver.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created on 8/24/14. 3 | * 4 | * Xi Server 5 | * 6 | * * Version: v.004 7 | * 8 | * * Updated 22 Nov 2014 9 | * 10 | * This file is common to all Xi Server types and is used to instantiate XiDuino, XiPi and XiBone servers 11 | * 12 | * Selecting the appropriate board type is done through a command line option 13 | * 14 | * Usage: 15 | * node xiserver.js [BOARD_ID] [URL] [DEBUG_LEVEL] [COM_PORT] [IP_PORT] 16 | * 17 | * BOARD_ID Options: 18 | * ard = arduino default 19 | * bbb = beaglebone black 20 | * rpi = raspberry pi 21 | * 22 | * URL: 23 | * Browser is launched to this URL when server is started 24 | * Default = "scratch.mit.edu" 25 | * To suppress launching, set this parameter to null 26 | * 27 | * DEBUG_LEVEL = 0 (no debug) 28 | * 3 (maximum debug) 29 | * 30 | *COM_PORT = force a specific com port to be used for arduino 31 | * eg: "/dev/ttyACM0" or "COM3" 32 | * 33 | * IP_PORT 34 | * IP port number 35 | * Default = 1234 36 | * 37 | * 38 | * 39 | * @author: Alan Yorinks 40 | Copyright (c) 2014 Alan Yorinks All right reserved. 41 | 42 | This program is free software; you can redistribute it and/or 43 | modify it under the terms of the GNU General Public 44 | License as published by the Free Software Foundation; either 45 | version 3.0 of the License, or (at your option) any later version. 46 | 47 | This library is distributed in the hope that it will be useful, 48 | but WITHOUT ANY WARRANTY; without even the implied warranty of 49 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 50 | Lesser General Public License for more details. 51 | 52 | You should have received a copy of the GNU General Public 53 | License along with this library; if not, write to the Free Software 54 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 55 | * 56 | */ 57 | 58 | 59 | 60 | // when running on linux, the command needs to be prefaced by sudo 61 | 62 | // pull in the required node packages and assign variables for the entities 63 | var WebSocketServer = require('websocket').server; 64 | var http = require('http'); 65 | var five = require('johnny-five'); 66 | 67 | 68 | var board; // a johnny-five 'board' 69 | 70 | var xiServerVersion = "XiServer version .004 22 Nov 2014"; 71 | 72 | var serverType = 'ard'; // set by user in command line invocation 73 | var urlAddr = 'http://scratchx.org/?url=http://MrYsLab.github.io/xi4s.js'; 74 | var debugLevel = 0; // set by user in command line invocation 75 | var comPort; // communications port for Arduino - allows user to select the com port 76 | var ipPort = 1234; // ip port number 77 | 78 | var servoArray = []; // array of servo devices added to the board 79 | var piezoArray = []; // array of piezo devices added to the board 80 | var stepperArray = []; // array of steppers, both 4 wire and driver 81 | 82 | 83 | // retrieve any command line parameters 84 | // Server Type - Arduino, BeagleBone Black, Raspberry Pi 85 | // URL Address - to open either Scratch or Snap! 86 | // Debug Level - send info to the console 87 | // Com Port - allows user to manually select the Serial Com port instead of using autodetect 88 | // IP Port - allows user to set IP Port. Used when multiple arduinos are connected to a single PC. 89 | 90 | switch (process.argv.length) { 91 | case 3: 92 | serverType = process.argv[2]; 93 | break; 94 | case 4: 95 | serverType = process.argv[2]; 96 | urlAddr = process.argv[3]; 97 | break; 98 | // all options provided 99 | case 5: 100 | serverType = process.argv[2]; 101 | urlAddr = process.argv[3]; 102 | debugLevel = process.argv[4]; 103 | break; 104 | case 6: 105 | serverType = process.argv[2]; 106 | urlAddr = process.argv[3]; 107 | debugLevel = process.argv[4]; 108 | comPort = process.argv[5]; 109 | break; 110 | case 7: 111 | serverType = process.argv[2]; 112 | urlAddr = process.argv[3]; 113 | debugLevel = process.argv[4]; 114 | comPort = process.argv[5]; 115 | ipPort = process.argv[6]; 116 | break; 117 | // no options provided 118 | case 2: 119 | default: 120 | //serverType = 'ard'; 121 | //debugLevel = 0; 122 | } 123 | 124 | console.log(xiServerVersion); 125 | // create the correct johnny-five board type 126 | switch (serverType) { 127 | case 'rpi': 128 | console.log('XiPi Server ...'); 129 | var raspi = require('raspi-io'); 130 | 131 | //noinspection JSCheckFunctionSignatures 132 | board = new five.Board({ 133 | io: new raspi() 134 | }); 135 | break; 136 | case 'bbb': 137 | console.log('XiBone Server ...'); 138 | var BeagleBone = require('beaglebone-io'); 139 | //noinspection JSCheckFunctionSignatures 140 | board = new five.Board({ 141 | io: new BeagleBone() 142 | }); 143 | break; 144 | case 'ard': 145 | default: 146 | serverType = 'ard'; 147 | console.log('XiDuino Server ...'); 148 | 149 | // for arduino servers the default web browser is automatically opened to the scratch web page 150 | if (urlAddr !== "null") { 151 | var open = require('open'); // this is the package that opens the browser 152 | open(urlAddr); 153 | } 154 | // user wants to select the com port manually 155 | if (comPort !== undefined) { 156 | //noinspection JSCheckFunctionSignatures 157 | board = new five.Board({port: comPort}); 158 | } 159 | // allow board to automatically find the com port 160 | else { 161 | //noinspection JSCheckFunctionSignatures 162 | board = new five.Board(); 163 | } 164 | } 165 | 166 | // when the board construction completes a 'ready' message is emitted - this is the "kickoff" point 167 | board.on('ready', function () { 168 | var connection; // WebSocket connection to client 169 | 170 | // create an http server that will be used to contain a WebSocket server 171 | var server = http.createServer(function (request, response) { 172 | // We are not processing any HTTP, so this is an empty function. 'server' is a wrapper for the 173 | // WebSocketServer we are going to create below. 174 | }); 175 | 176 | // Create an IP listener using the http server 177 | server.listen(ipPort, function () { 178 | if (debugLevel >= 1) { 179 | console.log('Webserver created and listening on port ' + ipPort); 180 | } 181 | }); 182 | 183 | // create the WebSocket Server and associate it with the httpServer 184 | var wsServer = new WebSocketServer({ 185 | httpServer: server 186 | }); 187 | 188 | // each time we create a new input device upon request from the client, it is added to the devices array 189 | var devices = []; 190 | 191 | // WebSocket server has been activated and a 'request' message has been received from client websocket 192 | wsServer.on('request', function (request) { 193 | // accept a connection request from Xi4S 194 | connection = request.accept(null, request.origin); // The server is now 'online' 195 | 196 | 197 | // Process Xi4S messages 198 | connection.on('message', function (message) { 199 | var pin; // the pin number contained within a message 200 | var index; // an index for the devices array 201 | var deviceID; // the device identifier contained within a message 202 | 203 | if (debugLevel >= 1) { 204 | console.log('message received: ' + message.utf8Data); 205 | } 206 | 207 | // Messages components are delimited by '/'. 208 | // A message is split into its components and placed in msg array. 209 | // The message ID is always the first element 210 | // TODO: explore using JSON instead of current delimitation scheme 211 | var msg = message.utf8Data.split('/'); 212 | 213 | 214 | // Process each message type received 215 | switch (msg[0]) { 216 | // handshake with client at start up of socket 217 | case 'Xi4sOnline': 218 | if (debugLevel >= 1) { 219 | console.log('Xi Server has a websocket to Xi4S'); 220 | } 221 | break; 222 | 223 | /********************************************************************************************** 224 | ********************* PIN MODE COMMAND HANDLERS ***************************************** 225 | **********************************************************************************************/ 226 | 227 | // set pin mode to analog in 228 | // create an an instance of an analog sensor and add it to the array 229 | case 'setAnalogIN': 230 | // get the pin number and add the 'analog' designator 231 | pin = msg[3]; 232 | if (debugLevel >= 1) { 233 | console.log("setAnalogIn", msg); 234 | } 235 | if (validateAnalogSetMode(msg[1], pin) === false) { 236 | break; 237 | } 238 | // specific to XiDuino - needs to see the 'A' designator 239 | if (serverType === 'ard') { 240 | pin = 'A' + pin; 241 | } 242 | index = devices.length; 243 | deviceID = msg[1]; 244 | 245 | addSensor(index, pin, deviceID); 246 | break; 247 | // set pin mode to digital in 248 | // create a switch object to handle a digital input pin and add it to the array 249 | case 'setDigitalIN': 250 | pin = msg[2]; 251 | if (validateDigitalSetMode(msg[1], pin, five.Pin.INPUT) === false) { 252 | break; 253 | } 254 | index = devices.length; 255 | deviceID = msg[3]; 256 | if (debugLevel >= 2) { 257 | console.log("adding switch", pin, deviceID); 258 | } 259 | addSwitch(index, pin, deviceID); 260 | break; 261 | // set up pin as PWM - we do not create device objects for output pins, but access them directly 262 | case 'setAnalogOUT': 263 | pin = msg[2]; 264 | // PWM is considered here to be a digital mode 265 | if (validateDigitalSetMode(msg[1], pin, five.Pin.PWM) !== false) { 266 | board.pinMode(pin, five.Pin.PWM); 267 | } 268 | break; 269 | // set up pin as digital output 270 | // we do not create device objects for output pins, but access them directly 271 | case 'setDigitalOUT': 272 | pin = msg[2]; 273 | // just set the pin (msg[2], to the output mode. 274 | if (validateDigitalSetMode(msg[1], pin, five.Pin.OUTPUT) !== false) { 275 | board.pinMode(pin, five.Pin.OUTPUT); 276 | } 277 | break; 278 | // This will set a pin to servo mode and add a standard servo device 279 | case 'setStandardServoMode': 280 | pin = msg[2]; 281 | if (debugLevel >= 3) { 282 | console.log('setServoMode current mode === ' + board.io.pins[pin].mode); 283 | console.log('pin = ' + pin); 284 | console.log(msg); 285 | } 286 | if (validateDigitalSetMode(msg[1], pin, five.Pin.SERVO) !== false) { 287 | board.pinMode(pin, five.Pin.SERVO); 288 | if (debugLevel >= 2) { 289 | console.log("adding servo", pin); 290 | } 291 | addServo(pin, 'standard', false); 292 | } 293 | break; 294 | // This will set a pin to Servo and add a continuous servo device 295 | case 'setContinuousServoMode': 296 | inverted = false; 297 | pin = msg[2]; 298 | if (debugLevel >= 3) { 299 | console.log('setServoMode current mode === ' + board.io.pins[pin].mode); 300 | console.log('pin = ' + pin); 301 | console.log(msg); 302 | } 303 | 304 | if (validateDigitalSetMode(msg[1], pin, five.Pin.SERVO) !== false) { 305 | board.pinMode(pin, five.Pin.SERVO); 306 | if (debugLevel >= 2) { 307 | console.log("adding servo", pin); 308 | } 309 | addServo(pin, 'continuous'); 310 | } 311 | break; 312 | // Set a pin for HC-SR04 type support and a ping device 313 | // Requires a replacement sketch for StandardFirmata 314 | // https://github.com/rwaldron/johnny-five/wiki/Ping#setup 315 | case 'setSonarMode': 316 | deviceID = msg[3]; 317 | pin = msg[2]; 318 | if (debugLevel >= 1) { 319 | console.log("setSonarMode", msg); 320 | } 321 | if (validateDigitalSetMode(msg[1], pin, five.Pin.INPUT) === false) { 322 | break; 323 | } 324 | index = devices.length; 325 | addPing(index, pin, deviceID); 326 | break; 327 | // set a pin for infrared distance sensing and add the device 328 | case 'setInfraRedDistanceMode': 329 | deviceID = msg[3]; 330 | pin = msg[2]; 331 | if (debugLevel >= 1) { 332 | console.log("setInfraRedDistanceMode", msg); 333 | } 334 | if (validateAnalogSetMode(msg[1], pin, five.Pin.ANALOG) === false) { 335 | break; 336 | } 337 | index = devices.length; 338 | addInfraRed(index, pin, deviceID); 339 | break; 340 | // set a pin to piezo tone mode and add the device 341 | case 'setToneMode': 342 | pin = msg[2]; 343 | if (validateDigitalSetMode(msg[1], pin, five.Pin.OUTPUT) !== false) { 344 | addPiezo(pin); 345 | } 346 | break; 347 | // The stepper motor support requires a replacement sketch for StandardFirmata 348 | // https://github.com/soundanalogous/AdvancedFirmata 349 | // set the pins for a 4 wire stepper and a 4 wire stepper device 350 | case 'fourWireStepperPins': 351 | var pinA = parseInt(msg[2]); 352 | var pinB = parseInt(msg[3]); 353 | var pinC = parseInt(msg[4]); 354 | var pinD = parseInt(msg[5]); 355 | var revSteps = parseInt(msg[6]); 356 | if (debugLevel >= 1) { 357 | console.log("fourWireStepperPin", msg); 358 | } 359 | if (validateDigitalSetMode(msg[1], pinA, five.Pin.OUTPUT) === false) { 360 | break; 361 | } 362 | if (validateDigitalSetMode(msg[1], pinB, five.Pin.OUTPUT) === false) { 363 | break; 364 | } 365 | if (validateDigitalSetMode(msg[1], pinC, five.Pin.OUTPUT) === false) { 366 | break; 367 | } 368 | if (validateDigitalSetMode(msg[1], pinD, five.Pin.OUTPUT) === false) { 369 | break; 370 | } 371 | addFourWireStepper(pinA, pinB, pinC, pinD, revSteps); 372 | break; 373 | // The stepper motor support requires a replacement sketch for StandardFirmata 374 | // https://github.com/soundanalogous/AdvancedFirmata 375 | // set the pins for a stepper driver board and a driver stepper device 376 | case 'stepperDriverPins': 377 | if (debugLevel >= 1) { 378 | console.log("stepperDriverPins", msg); 379 | } 380 | pinA = parseInt(msg[2]); 381 | pinB = parseInt(msg[3]); 382 | revSteps = parseInt(msg[4]); 383 | if (validateDigitalSetMode(msg[1], pinA, five.Pin.OUTPUT) === false) { 384 | break; 385 | } 386 | if (validateDigitalSetMode(msg[1], pinB, five.Pin.OUTPUT) === false) { 387 | break; 388 | } 389 | addDriverStepper(pinA, pinB, revSteps); 390 | break; 391 | 392 | /********************************************************************************************** 393 | ********************* COMMAND BLOCK HANDLERS ********************************** 394 | **********************************************************************************************/ 395 | 396 | // write out the digital value to the pin 397 | case 'digitalWrite': 398 | if (debugLevel >= 3) { 399 | console.log('digitalWrite: board ' + msg[1] + 'pin' + msg[2] + ' value ' + msg[3]); 400 | } 401 | //validate that this pin was initially set to correct mode 402 | if (board.io.pins[msg[2]].mode !== five.Pin.OUTPUT) { 403 | connection.send('invalidPinCommand/' + 2 + '/' + msg[1] + '/' + msg[2]); 404 | } 405 | else { 406 | if (msg[3] === 'Off') { 407 | if (debugLevel >= 3) { 408 | console.log(' pin off'); 409 | } 410 | board.digitalWrite(msg[2], 0); 411 | } 412 | else { 413 | if (debugLevel >= 3) { 414 | console.log(' pin on'); 415 | } 416 | board.digitalWrite(msg[2], 1); 417 | } 418 | } 419 | break; 420 | // write out the analog value to the PWM pin 421 | case 'analogWrite': 422 | pin = msg[2]; 423 | if (debugLevel >= 3) { 424 | console.log('analogWrite current mode === ' + board.io.pins[msg[2]].mode); 425 | console.log('pin = ' + pin); 426 | console.log(msg); 427 | } 428 | if (board.io.pins[msg[2]].mode !== five.Pin.PWM) { 429 | // send alert string 430 | connection.send('invalidPinCommand/' + 3 + '/' + msg[1] + '/' + pin); 431 | } 432 | else { 433 | board.analogWrite(msg[2], msg[3]); 434 | } 435 | break; 436 | // play a tone 437 | case 'playTone': 438 | pin = msg[2]; 439 | var frequency = msg[3]; 440 | var duration = msg[4]; 441 | 442 | if (debugLevel >= 3) { 443 | console.log('tone pin current mode === ' + board.io.pins[pin].mode); 444 | console.log('pin = ' + pin); 445 | console.log(msg); 446 | } 447 | if (board.io.pins[pin].mode !== five.Pin.OUTPUT) { 448 | // send alert string 449 | connection.send('invalidPinCommand/' + 4 + '/' + msg[1] + '/' + pin); 450 | break; 451 | } 452 | else { 453 | // retrieve piezo from array 454 | for (i = 0; i < piezoArray.length; i++) { 455 | if (piezoArray[i].pin === pin) { 456 | piezoArray[i].frequency(frequency, duration); 457 | return; 458 | } 459 | } 460 | if (debugLevel >= 3) { 461 | console.log('playTone - piezo not found for pin ' + pin); 462 | } 463 | } 464 | break; 465 | case 'noTone': 466 | pin = msg[2]; 467 | if (debugLevel >= 3) { 468 | console.log('notone for pin' + pin); 469 | } 470 | for (i = 0; i < piezoArray.length; i++) { 471 | if (piezoArray[i].pin === pin) { 472 | piezoArray[i].noTone(); 473 | return; 474 | } 475 | } 476 | if (debugLevel >= 3) { 477 | console.log('noTone - piezo not found for pin ' + pin); 478 | } 479 | break; 480 | // move the servo to position in degrees 481 | // message indices: 1= board, 2 = pin, 3 = degrees 482 | case 'moveStandardServo': 483 | pin = parseInt(msg[2]); 484 | var degrees = parseInt(msg[3]); 485 | var inverted = msg[4]; 486 | if (debugLevel >= 3) { 487 | console.log('servo current mode === ' + board.io.pins[pin].mode); 488 | console.log('pin = ' + pin); 489 | console.log(msg); 490 | } 491 | if (board.io.pins[pin].mode !== five.Pin.SERVO) { 492 | // send alert string 493 | connection.send('invalidPinCommand/' + 5 + '/' + msg[1] + '/' + pin); 494 | break; 495 | } 496 | else { 497 | // retrieve servo from array 498 | for (var i = 0; i < servoArray.length; i++) { 499 | if (servoArray[i].pin === pin) { 500 | if (servoArray[i].type !== "standard") { 501 | connection.send('invalidPinCommand/' + 6 + '/' + msg[1] + '/' + pin); 502 | return; 503 | } 504 | servoArray[i].isInverted = inverted !== 'False'; 505 | servoArray[i].to(degrees); 506 | return; 507 | } 508 | } 509 | if (debugLevel >= 3) { 510 | console.log('moveStandardServo - servo not found for pin ' + pin); 511 | } 512 | } 513 | break; 514 | // move a continuous servo in the given direction and speed 515 | case 'moveContinuousServo': 516 | 517 | pin = parseInt(msg[2]); 518 | var direction = msg[3]; 519 | inverted = msg[4]; 520 | var speed = parseFloat(msg[5]); 521 | speed = parseFloat(speed.toFixed(2)); 522 | if (debugLevel >= 3) { 523 | console.log('servo current mode === ' + board.io.pins[pin].mode); 524 | console.log('pin = ' + pin); 525 | console.log(msg); 526 | } 527 | if (board.io.pins[pin].mode !== five.Pin.SERVO) { 528 | // send alert string 529 | connection.send('invalidPinCommand/' + 5 + '/' + msg[1] + '/' + pin); 530 | break; 531 | } 532 | else { 533 | 534 | // retrieve servo from array 535 | for (i = 0; i < servoArray.length; i++) { 536 | if (servoArray[i].pin === pin) { 537 | if (servoArray[i].type !== "continuous") { 538 | connection.send('invalidPinCommand/' + 7 + '/' + msg[1] + '/' + pin); 539 | return; 540 | } 541 | 542 | if (speed >= 0.0 && speed <= 1.0) { 543 | servoArray[i].isInverted = inverted !== 'False'; 544 | if (direction === 'CW') { 545 | servoArray[i].cw(speed); 546 | } 547 | else { 548 | servoArray[i].ccw(speed); 549 | } 550 | return; 551 | } 552 | else { 553 | connection.send('invalidSetMode/Board ' + msg[1] + ' Pin ' + pin + 554 | ' Speed must be in the range of 0.0 to 1.0'); 555 | } 556 | return; 557 | } 558 | } 559 | if (debugLevel >= 3) { 560 | console.log('moveContinuousServo - servo not found for pin ' + pin); 561 | } 562 | } 563 | break; 564 | // stop servo motion - used for both standard and continuous 565 | case 'stopServo': 566 | pin = parseInt(msg[2]); 567 | if (debugLevel >= 3) { 568 | console.log('stopServo servo current mode === ' + board.io.pins[pin].mode); 569 | console.log('pin = ' + pin); 570 | console.log(msg); 571 | } 572 | if (board.io.pins[pin].mode !== five.Pin.SERVO) { 573 | // send alert string 574 | connection.send('invalidPinCommand/' + 5 + '/' + msg[1] + '/' + pin); 575 | break; 576 | } 577 | else { 578 | 579 | // retrieve servo from array 580 | for (i = 0; i < servoArray.length; i++) { 581 | if (servoArray[i].pin === pin) { 582 | servoArray[i].stop(); 583 | return; 584 | } 585 | } 586 | if (debugLevel >= 3) { 587 | console.log('stopServo - servo not found for pin ' + pin); 588 | } 589 | } 590 | break; 591 | // move a stepper motor - this works for both 4 wire and driver. 592 | case 'moveStepper': 593 | pin = parseInt(msg[2]); 594 | var rpms = parseInt(msg[3]); 595 | var dir = msg[4]; 596 | var acc = parseInt(msg[5]); 597 | var dec = parseInt(msg[6]); 598 | var stp = parseInt(msg[7]); 599 | 600 | 601 | if (debugLevel >= 3) { 602 | console.log('moveStepper mode '); 603 | console.log('pin = ' + pin); 604 | console.log(msg); 605 | } 606 | 607 | if (dir === 'CW') { 608 | dir = five.Stepper.DIRECTION.CW; 609 | } 610 | else { 611 | dir = five.Stepper.DIRECTION.CCW; 612 | } 613 | if (board.io.pins[pin].mode !== board.io.MODES.STEPPER) { 614 | // send alert string 615 | connection.send('invalidPinCommand/' + 8 + '/' + msg[1] + '/' + pin); 616 | break; 617 | } 618 | else { 619 | 620 | // retrieve servo from array 621 | for (i = 0; i < stepperArray.length; i++) { 622 | if (stepperArray[i].pins.step === pin) { 623 | stepperArray[i].step({ 624 | steps: stp, 625 | rpm: rpms, 626 | direction: dir, 627 | accel: acc, 628 | decel: dec 629 | }, function () { 630 | console.log("Done stepping!"); 631 | }); 632 | return; 633 | } 634 | 635 | } 636 | 637 | if (debugLevel >= 3) { 638 | console.log('moveStepper - stepper not found for pin ' + pin); 639 | } 640 | } 641 | break; 642 | 643 | // sets number of steps to 0 to stop stepper movement 644 | case 'stopStepper': 645 | pin = parseInt(msg[2]); 646 | 647 | if (debugLevel >= 3) { 648 | console.log('stopSStepper servo current mode === ' + board.io.pins[pin].mode); 649 | console.log('pin = ' + pin); 650 | console.log(msg); 651 | } 652 | if (board.io.pins[pin].mode !== board.io.MODES.STEPPER) { 653 | // send alert string 654 | connection.send('invalidPinCommand/' + 8 + '/' + msg[1] + '/' + pin); 655 | break; 656 | } 657 | else { 658 | 659 | // retrieve servo from array 660 | for (i = 0; i < stepperArray.length; i++) { 661 | if (stepperArray[i].pins.step === pin) { 662 | stepperArray[i].step({ 663 | steps: 0, 664 | direction: five.Stepper.DIRECTION.CW 665 | }, function () { 666 | console.log("stopped stepper pin " + pin); 667 | }); 668 | return; 669 | } 670 | } 671 | if (debugLevel >= 3) { 672 | console.log('stopStepper - stepper not found for pin ' + pin); 673 | } 674 | } 675 | 676 | break; 677 | 678 | 679 | case 'resetBoard': 680 | // reset the board 681 | if (debugLevel >= 2) { 682 | console.log('Client is shutting down'); 683 | } 684 | boardReset(); 685 | break; 686 | default: 687 | console.log('Xi Server unknown message received: ' + msg[0]); 688 | } 689 | }); 690 | connection.on('close', function (connection) { 691 | console.log('Client closed connection'); 692 | boardReset(); 693 | }); 694 | }); 695 | 696 | /**************************************************************************************************************** 697 | ********************************* Device Creation Functions **************************************************** 698 | ****************************************************************************************************************/ 699 | 700 | /*************************************************************************** 701 | ****************** INPUT Devices - Reporters ****************************** 702 | ***************************************************************************/ 703 | 704 | // dynamically add a sensor and sensor change listeners and their callback functions 705 | 706 | // This function dynamically adds an analog sensor to the devices array. 707 | // It adds a listener for the 'change' event. 708 | 709 | function addSensor(index, myPin, myId) { 710 | 711 | // create the device and add it to the array 712 | // index already calculated to current length of array, so entry will be added to end of array 713 | devices[index] = new five.Sensor({ 714 | pin: myPin, 715 | freq: 1, 716 | id: myId 717 | }); 718 | 719 | // event listener call back function for the sensor 720 | var sensorCallbackChange = function () { 721 | if (debugLevel >= 1) { 722 | console.log(' sensor callback ' + devices[index].id + ' value = ' + devices[index].value); 723 | } 724 | 725 | connection.send('dataUpdate/' + devices[index].id + '/' + Math.ceil(devices[index].value)); 726 | }; 727 | 728 | // add the 'change' listener to the device array 729 | devices[index].addListener('change', sensorCallbackChange); 730 | if (debugLevel >= 2) { 731 | console.log('addSensor: added change listener'); 732 | console.log('devices length = ' + devices.length + ' index = ' + index); 733 | } 734 | } 735 | 736 | // add an HC-SR04 type device 737 | function addPing(index, myPin, myId) { 738 | 739 | // create the device and add it to the array 740 | // index already calculated to current length of array, so entry will be added to end of array 741 | devices[index] = new five.Ping({ 742 | pin: myPin, 743 | id: myId 744 | }); 745 | // event listener call back function for the sensor 746 | var pingCallbackChange = function () { 747 | 748 | if (debugLevel >= 1) { 749 | console.log(' sensor callback inches ' + devices[index].id + ' value = ' + devices[index].inches); 750 | } 751 | connection.send('dataUpdate/' + devices[index].id + '/' + devices[index].inches.toFixed(2)); 752 | }; 753 | 754 | // add the 'change' listener to the device array 755 | devices[index].addListener('change', pingCallbackChange); 756 | if (debugLevel >= 2) { 757 | console.log('addPing: added change listener'); 758 | console.log('devices length = ' + devices.length + ' index = ' + index); 759 | } 760 | } 761 | 762 | // add an infrared distance sensor 763 | function addInfraRed(index, pin, myId) { 764 | 765 | // create the device and add it to the array 766 | // index already calculated to current length of array, so entry will be added to end of array 767 | if (serverType === 'ard') { 768 | pin = 'A' + pin; 769 | } 770 | devices[index] = new five.Distance({ 771 | pin: pin, 772 | device: 'GP2Y0A21YK', 773 | id: myId, 774 | freq: 75 775 | }); 776 | 777 | var infraredCallbackChange = function () { 778 | 779 | if (debugLevel >= 1) { 780 | console.log(' infrared callback inches ' + devices[index].id + ' value = ' + devices[index].inches); 781 | } 782 | connection.send('dataUpdate/' + devices[index].id + '/' + devices[index].inches.toFixed(2)); 783 | }; 784 | 785 | // add the 'change' listener to the device array 786 | devices[index].addListener('change', infraredCallbackChange); 787 | if (debugLevel >= 2) { 788 | console.log('addInfraRed: added change listener'); 789 | console.log('devices length = ' + devices.length + ' index = ' + index); 790 | } 791 | } 792 | 793 | // dynamically add a switch (digital input to the devices array) 794 | function addSwitch(index, myPin, myId) { 795 | devices[index] = new five.Switch({ 796 | pin: myPin, 797 | id: myId 798 | }); 799 | 800 | // event listener call back function for switch going to 'off' 801 | var switchOffCallbackChange = function () { 802 | if (debugLevel >= 1) { 803 | console.log(' switch callback ' + index, devices[index].id + ' off'); 804 | } 805 | connection.send('dataUpdate/' + devices[index].id + '/' + 0) 806 | }; 807 | 808 | // event listener call back function for switch going to 'on' 809 | var switchOnCallbackChange = function () { 810 | if (debugLevel >= 1) { 811 | console.log(' switch callback ' + index, devices[index].id + ' on'); 812 | } 813 | connection.send('dataUpdate/' + devices[index].id + '/' + 1) 814 | }; 815 | 816 | // add the open and closed listeners to devices in the array 817 | devices[index].addListener('open', switchOffCallbackChange); 818 | devices[index].addListener('close', switchOnCallbackChange); 819 | } 820 | 821 | // dynamically add a servo to the devices array 822 | function addServo(myPin, servoType) { 823 | var servo = new five.Servo({ 824 | pin: myPin, 825 | type: servoType 826 | }); 827 | 828 | servoArray.push(servo); 829 | } 830 | 831 | /*************************************************************************** 832 | ****************** OUTPUT Devices ****************************** 833 | ***************************************************************************/ 834 | 835 | // dynamically add a piezo device to the piezo array 836 | function addPiezo(pin) { 837 | var piezo = new five.Piezo({ 838 | pin: pin 839 | }); 840 | piezoArray.push(piezo); 841 | } 842 | 843 | function addFourWireStepper(pinA, pinB, pinC, pinD, revSteps) { 844 | revSteps = parseInt(revSteps); 845 | var stepper = new five.Stepper({ 846 | type: five.Stepper.TYPE.FOUR_WIRE, 847 | pins: [pinA, pinB, pinC, pinD], 848 | stepsPerRev: revSteps 849 | }); 850 | stepperArray.push(stepper); 851 | 852 | } 853 | 854 | function addDriverStepper(pinA, pinB, revSteps) { 855 | revSteps = parseInt(revSteps); 856 | var stepper = new five.Stepper({ 857 | type: five.Stepper.TYPE.DRIVER, 858 | pins: [pinA, pinB], 859 | stepsPerRev: revSteps 860 | }); 861 | stepperArray.push(stepper); 862 | } 863 | 864 | // place holder if we ever need to do anything 865 | function boardReset() { 866 | for (var i = 0; i < servoArray.length; i++) { 867 | servoArray[i].stop(); 868 | } 869 | for (i = 0; i < stepperArray.length; i++) { 870 | if (stepperArray[i].pins.motor1 === pin) { 871 | stepperArray[i].step({ 872 | steps: 0, 873 | direction: five.Stepper.DIRECTION.CW 874 | }, function () { 875 | console.log("stopped stepper pin " + pin); 876 | }); 877 | } 878 | } 879 | } 880 | 881 | /**************************************************************************************************** 882 | ************************ UTILITY FUNCTIONS ******************************************************** 883 | ****************************************************************************************************/ 884 | 885 | // Determine if a digital pin mode has already been set and if not, does this pin support the mode? 886 | // Returns true fs all tests pass, or false if any one fails. 887 | 888 | function validateDigitalSetMode(boardID, pin, mode) { 889 | 890 | if (debugLevel >= 1) { 891 | console.log('validateDigitalSetMode ' + pin + ' ' + mode); 892 | } 893 | 894 | // does board support pin number? 895 | if (board.io.pins.length < pin) { 896 | // send alert pin number exceeds number of pins on board 897 | connection.send('invalidSetMode/Board ' + boardID + ' Pin ' + pin + 898 | ' Exceeds Maximum Number of Pins on Board'); 899 | return false; 900 | } 901 | 902 | var currentMode = board.io.pins[pin].mode; // get the current mode of the pin 903 | 904 | switch (currentMode) { 905 | // boards are not consistent on how they indicate that a pin mode was not yet assigned 906 | // so check for any 907 | case 0: // rpi 908 | case undefined: // ard 909 | case null: //bbb 910 | break; 911 | default: 912 | // it was set before, but if it is the same just return true and continue on 913 | if (currentMode === mode) { 914 | return true 915 | } 916 | else { 917 | connection.send('invalidSetMode/' + 9 + '/' + boardID + '/' + pin); 918 | return false; 919 | } 920 | } 921 | 922 | if (debugLevel >= 3) { 923 | console.log('validateDigitalSetMode supported modes: ' + board.io.pins[pin].supportedModes); 924 | } 925 | 926 | 927 | // check to make sure that the pin actually supports the mode that the client is requesting 928 | for (var i = 0; i < board.io.pins[pin].supportedModes.length; i++) { 929 | if (debugLevel >= 3) { 930 | console.log('validateDigitalSetMode: mode = ' + mode); 931 | } 932 | 933 | if (board.io.pins[pin].supportedModes[i] == mode) { 934 | return true; 935 | } 936 | } 937 | 938 | // alert pin does not support mode 939 | connection.send('invalidSetMode/' + 1 + '/' + boardID + '/' + pin); 940 | return false; 941 | } 942 | 943 | // determine if a pin can be set analog mode (PWM) 944 | 945 | function validateAnalogSetMode(boardID, pin) { 946 | // check to see if the pin number is in the range of possible analog pins 947 | var pinMapped = false; 948 | var i; 949 | var analogPin; 950 | 951 | if (pin > board.io.analogPins.length) { 952 | connection.send('invalidSetMode/' + 0 + '/' + boardID + '/' + pin); 953 | return false; 954 | } 955 | 956 | // convert analog pin number to actual pin number 957 | for (i = 0; i < board.io.pins.length; i++) { 958 | if (board.io.pins[i].analogChannel == pin) { 959 | // we found a match 960 | analogPin = i; 961 | pinMapped = true; 962 | break; // get out of loop 963 | } 964 | } 965 | if (pinMapped === false) { 966 | connection.send('invalidSetMode/' + 11 + '/' + boardID + '/' + pin); 967 | return false; 968 | } 969 | 970 | // has pin been already been assigned? 971 | var currentMode = board.io.pins[analogPin].mode; 972 | if (currentMode != undefined || currentMode != null) { 973 | // send alert string 974 | connection.send('invalidSetMode/Board ' + boardID + ' Pin ' + analogPin + 975 | ' was previously assigned mode ' + currentMode); 976 | return false; 977 | } 978 | 979 | for (i = 0; i < board.io.pins[analogPin].supportedModes.length; i++) { 980 | if (board.io.pins[analogPin].supportedModes[i] === five.Pin.ANALOG) { 981 | return true; 982 | } 983 | } 984 | return false; 985 | 986 | } 987 | }); 988 | --------------------------------------------------------------------------------