From b4fb2f1e7a74f9bfa3d5e296be13fd3dc8dcd71d Mon Sep 17 00:00:00 2001 From: Evgeny Kochetkov Date: Tue, 6 Feb 2018 16:34:30 +0300 Subject: [PATCH] test(xod-client-electron): func tests about saving o xodball --- .../test-func/0-fs.spec.js | 14 +- .../test-func/fixtures/welcome-to-xod.xodball | 2187 +++++++++++++++++ 2 files changed, 2200 insertions(+), 1 deletion(-) create mode 100644 packages/xod-client-electron/test-func/fixtures/welcome-to-xod.xodball diff --git a/packages/xod-client-electron/test-func/0-fs.spec.js b/packages/xod-client-electron/test-func/0-fs.spec.js index 9fceca81..4f38f345 100644 --- a/packages/xod-client-electron/test-func/0-fs.spec.js +++ b/packages/xod-client-electron/test-func/0-fs.spec.js @@ -39,10 +39,22 @@ describe('Test FS things', () => { } ); - // welcome project is readonly, let's save a "mutable" copy. + const saveAsXodballAndCheck = () => + ide.app.electron.ipcRenderer + .emit(TRIGGER_SAVE_AS, ide.wsPath('welcome-to-xod.xodball')) + .then(ide.page.waitUntilProjectSaved) + .then(() => { + const expectedXodball = fse.readFileSync(path.join(__dirname, './fixtures/welcome-to-xod.xodball'), 'utf8'); + const actualXodball = fse.readFileSync(ide.wsPath('welcome-to-xod.xodball'), 'utf8'); + assert.equal(actualXodball, expectedXodball); + }); + // !!! Ideally, we should simulate clicking 'file -> save', // check that a file dialog appears and enter desired path there. // But spectron can't handle file dialogs :( + it('saves welcome project to disk as xodball', saveAsXodballAndCheck); + it('saves welcome project to disk as xodball again and it remains the same', saveAsXodballAndCheck); + it('saves welcome project to disk', () => ide.app.electron.ipcRenderer .emit(TRIGGER_SAVE_AS, ide.wsPath('welcome-to-xod')) diff --git a/packages/xod-client-electron/test-func/fixtures/welcome-to-xod.xodball b/packages/xod-client-electron/test-func/fixtures/welcome-to-xod.xodball new file mode 100644 index 00000000..8162e90c --- /dev/null +++ b/packages/xod-client-electron/test-func/fixtures/welcome-to-xod.xodball @@ -0,0 +1,2187 @@ +{ + "authors": [ + "xod" + ], + "description": "A crash course tutorial baked as a project", + "license": "MIT", + "name": "welcome-to-xod", + "patches": { + "@/01-hello": { + "comments": { + "SyzEUkjK-": { + "content": "Welcome to XOD, Maker! \n\nIn XOD, we do not use text to code; we use visual objects instead.\n\nThis large gray area with boxes is your program. It's called a *patch*. In this patch, there is only one object of interest: the `led` node, a grey rectangle with two little green circles. You will be able to make complicated programs using this rectangles. \n\nFirst, try to move the rectangle. Its position on the workspace does not affect your program but can make it more readable. \n\nBTW, I am a comment. Even though I am here, I do not affect the patch because I am invisible to it. I am visible only to you. You can move me, too.\n\n## Instructions\n\n1. On the left-hand side, you will find a list of patches grouped by a project or library name. The list is called a *Project Browser*. The first item in it is `welcome-to-xod`. This is a special tutorial project you’re working with right now. Expand the project by clicking on it.\n\n2. As you can see, the tutorial consists of many patches. Right now, we are in patch `01-hello`. The next part of the tutorial is in patch `02-deploy`. Double-click it, and let's meet there!\n\nIf anything goes wrong or you have no idea what to do, we have [hints for every patch](https://goo.gl/bS7pmA) on the web.", + "id": "SyzEUkjK-", + "position": { + "x": 0, + "y": 0 + }, + "size": { + "height": 765, + "width": 306 + } + } + }, + "nodes": { + "SkjpJD4F-": { + "boundValues": { + "B1oqkTnIb": 11, + "HyYh1a3LZ": 0 + }, + "id": "SkjpJD4F-", + "position": { + "x": 340, + "y": 204 + }, + "type": "xod/common-hardware/led" + } + }, + "path": "@/01-hello" + }, + "@/02-deploy": { + "comments": { + "Hy8uMWjtZ": { + "content": "Good! Now, let’s upload your patch with the `led` node to the Arduino!\n\n## Instructions\n\n1. Assemble a circuit according to the [scheme](https://goo.gl/dRWtKS), and connect the Arduino to your computer.\n\n2. Upload your first program. To do this, select Deploy → Upload to Arduino from the main menu.\n\nTo upload programs to the Arduino, you will need to install the desktop version of XOD. The browser version does not have permission to access the USB ports. Although you’re still able to upload XOD programs even from the browser version if you have the original Arduino IDE installed ([learn how](https://goo.gl/dRWtKS)).\n\nIf you did it the right way, you will see the LED turn on. Great! Proceed to the next lesson, patch `03-inspector`.", + "id": "Hy8uMWjtZ", + "position": { + "x": -136, + "y": 0 + }, + "size": { + "height": 459, + "width": 306 + } + } + }, + "nodes": { + "SyiJgwVtb": { + "boundValues": { + "B1oqkTnIb": 11, + "HyYh1a3LZ": 1 + }, + "id": "SyiJgwVtb", + "position": { + "x": 204, + "y": 204 + }, + "type": "xod/common-hardware/led" + } + }, + "path": "@/02-deploy" + }, + "@/03-inspector": { + "comments": { + "H1nSnynt-": { + "content": "The LED is on because of the `led` node.\nA node is a visual representation of some physical device (such as the ports on an Arduino) or some function (adding, subtracting, or more complicated stuff).\n\nThe small colored circles on the nodes are called *pins*. Pins are divided into inputs and outputs. Inputs are always on the top side of the nodes, outputs on the bottom.\n\nThe `led` node is a simple LED-controlling node. It can control only one single-colored LED. The node has only input pins: `PORT` and `LUM`. \n\nPins are used to transfer data between nodes. Nodes process these values and take some action or give a result. In our case, the `led` node transfers the led brightnes value (set in `LUM`) to the Arduino port (set in `PORT`).\n\n## Instructions\n\n1. Click on the `led` node. You will see the Inspector under the list of projects. You can set the node parameters in the Inspector.\n\n2. In the Inspector, change the `LUM` value from 1 to 0.\n\n3. Upload the program to the Arduino. To do so, select Deploy → Upload to Arduino from the menu.\n\nThe LED will switch off after the upload because Arduino sent a value of 0 to the LED's pin. Physically speaking, it stopped delivering voltage to the pin.", + "id": "H1nSnynt-", + "position": { + "x": -68, + "y": 0 + }, + "size": { + "height": 765, + "width": 306 + } + } + }, + "nodes": { + "BykwlvEF-": { + "boundValues": { + "B1oqkTnIb": 11, + "HyYh1a3LZ": 1 + }, + "id": "BykwlvEF-", + "position": { + "x": 272, + "y": 204 + }, + "type": "xod/common-hardware/led" + } + }, + "path": "@/03-inspector" + }, + "@/04-pwm": { + "comments": { + "BkVzdWnKW": { + "content": "The `LUM` pin on the `led` node accepts values between 0 and 1. The 0 value represents no glow at all, while a value of 1 denotes full brightness.\n\nHowever, you can adjust brightness level by setting values *between* 0 and 1 to the `LUM` pin.\n\n## Instructions\n\n1. Set the `LUM` pin to 0.4. To do this, click on the led node and type in the value in the Inspector.\n\n2. Upload the patch program to the Arduino.\n\nNow your LED is not glowing as bright as before. Feel free to try other values between 0 and 1 before moving on to the next step.\n", + "id": "BkVzdWnKW", + "position": { + "x": -68, + "y": 0 + }, + "size": { + "height": 357, + "width": 306 + } + } + }, + "nodes": { + "BJ2tewEKb": { + "boundValues": { + "B1oqkTnIb": 11, + "HyYh1a3LZ": 0 + }, + "id": "BJ2tewEKb", + "position": { + "x": 272, + "y": 204 + }, + "type": "xod/common-hardware/led" + } + }, + "path": "@/04-pwm" + }, + "@/05-wiring": { + "comments": { + "BJ7AzfhK-": { + "content": "- `PORT` assigns the number of the Arduino’s digital port to be used.\n- `LUM` controls the brightness (value is between 0 and 1).", + "id": "BJ7AzfhK-", + "position": { + "x": 408, + "y": 204 + }, + "size": { + "height": 51, + "width": 510 + } + }, + "rkZofM3KW": { + "content": "Now you know what `LUM` pin does.\nHowever, an LED could be connected to any digital port on an Arduino. So you need to inform the node of the port number that you are about to use. You can do it with the `PORT` pin.\n\n## Instructions\n\n1. Reconnect the LED to digital port 10 on Arduino ([scheme](https://goo.gl/RwQRvD)).\n\n2. Change the `PORT` pin value to 10 and the `LUM` value to 1.\n\n3. Upload your patch to the Arduino.\n\nThe LED should turn on at Arduino's port 10.\n", + "id": "rkZofM3KW", + "position": { + "x": -34, + "y": 0 + }, + "size": { + "height": 357, + "width": 306 + } + } + }, + "nodes": { + "B1I7-DEYW": { + "boundValues": { + "B1oqkTnIb": 11, + "HyYh1a3LZ": 0.4 + }, + "id": "B1I7-DEYW", + "position": { + "x": 306, + "y": 204 + }, + "type": "xod/common-hardware/led" + } + }, + "path": "@/05-wiring" + }, + "@/06-adding-nodes": { + "comments": { + "Hy229X-hZ": { + "content": "Yet another way is the Quick Search feature:\n\n1. Press “I” key or *double-click* anywhere on the patch.\n2. Type what you are looking for.\n\nIt searches not only in nodes titles, but even in the nodes description, like a small and dumb built-in Google. ", + "id": "Hy229X-hZ", + "position": { + "x": 374, + "y": 408 + }, + "size": { + "height": 153, + "width": 442 + } + }, + "SJwr3fhFb": { + "content": "Now, let’s turn on two LEDs together! You will need another `led` node.\n\n## Instructions\n\n1. Assemble a circuit according to the [scheme](https://goo.gl/Ch43rm).\n2. In the node list (below the `welcome-to-xod`), you will find `xod/common-hardware` section. This is a *library* where you can find nodes to work with specific hardware.\n3. Find the `led` node. Note that nodes are arranged in alphabetical order.\n4. Hover the cursor over that node and click the burger button to access a context menu. There click “Place” to add the node onto a patch.\n5. Drag the new node to any slot. \n6. Now, assign the new values for the `PORT` and `LUM` pins to turn on the second LED.\n7. Upload the patch to the Arduino.\n\nBoth LEDs should turn on now.\n\nAlternatively, you may *drag* a node from the Project Browser instead of using the context menu. Try to add yet more `led` nodes to your project.", + "id": "SJwr3fhFb", + "position": { + "x": -34, + "y": 0 + }, + "size": { + "height": 561, + "width": 306 + } + } + }, + "nodes": { + "Hkg_WP4Kb": { + "boundValues": { + "B1oqkTnIb": 10, + "HyYh1a3LZ": 1 + }, + "id": "Hkg_WP4Kb", + "position": { + "x": 306, + "y": 204 + }, + "type": "xod/common-hardware/led" + } + }, + "path": "@/06-adding-nodes" + }, + "@/07-labels": { + "comments": { + "ry4xdQ2KZ": { + "content": "Now we have two led nodes. They have identical names but control different LEDs. To add some clarity, let’s rename them.\n\n## Instructions\n\n1. Click on any node. There is a field with a green flag in the Inspector; you can find it above the pins. Type any name for the node into this field.\n\n2. Do the same for the second node.\n\nNow the names of the nodes are different, and you can easily distinguish one from the other. However, the function and type of each node remains the same. This feature just makes your patch more readable.\n\nYou can always check out the node type in the Inspector.", + "id": "ry4xdQ2KZ", + "position": { + "x": -68, + "y": 0 + }, + "size": { + "height": 459, + "width": 306 + } + } + }, + "nodes": { + "H1l2-vNKW": { + "boundValues": { + "B1oqkTnIb": 11, + "HyYh1a3LZ": 1 + }, + "id": "H1l2-vNKW", + "position": { + "x": 272, + "y": 204 + }, + "type": "xod/common-hardware/led" + }, + "S1X2Wv4FW": { + "boundValues": { + "B1oqkTnIb": 10, + "HyYh1a3LZ": 1 + }, + "id": "S1X2Wv4FW", + "position": { + "x": 374, + "y": 204 + }, + "type": "xod/common-hardware/led" + } + }, + "path": "@/07-labels" + }, + "@/08-constants": { + "comments": { + "Byk34BnKZ": { + "content": "This is a `constant-number` node. \nIt sets `VAL` output to a number specified in the Inspector.\n", + "id": "Byk34BnKZ", + "position": { + "x": 442, + "y": 0 + }, + "size": { + "height": 51, + "width": 306 + } + }, + "HJfi4H2K-": { + "content": "Previously, we set the node values manually with the Inspector. However, the data can be transferred from the output pin of one node to the input pin of another node. This is the core idea of XOD programming.\n\n## Instructions\n\n1. Click on the `constant-number` node, and set the value of 1 for the `VAL` output in the Inspector.\n\n2. Connect the `VAL` pin with the `LUM` pin on the LED1 node. To do this, simply click on the `VAL` pin and then on the `LUM` pin. You will see a green line connecting the two pins. This line is called a *link*.\n\n3. Make another link between the `VAL` pin and `LUM` pin on the LED2 node.\n\n4. Upload the patch to the Arduino.\n\nThe both LEDs should be on. Now the values of the `LUM` pins of both `led` nodes are taken form the `VAL` pin of the `constant-number` node.\n", + "id": "HJfi4H2K-", + "position": { + "x": -34, + "y": 0 + }, + "size": { + "height": 459, + "width": 306 + } + } + }, + "nodes": { + "HknqGwVtb": { + "boundValues": { + "B1oqkTnIb": 10 + }, + "id": "HknqGwVtb", + "label": "LED1", + "position": { + "x": 306, + "y": 204 + }, + "type": "xod/common-hardware/led" + }, + "Syi5GPNFZ": { + "boundValues": { + "B1oqkTnIb": 11 + }, + "id": "Syi5GPNFZ", + "label": "LED2", + "position": { + "x": 408, + "y": 204 + }, + "type": "xod/common-hardware/led" + }, + "r1iTfwEFW": { + "boundValues": { + "B1x2RV3eZ": 0 + }, + "id": "r1iTfwEFW", + "position": { + "x": 374, + "y": 0 + }, + "type": "xod/core/constant-number" + } + }, + "path": "@/08-constants" + }, + "@/09-pot": { + "comments": { + "Sk8zxUaKW": { + "content": "Let’s try some more practice. Now, we will control the LEDs’ brightness with a potentiometer. All you need to do is to replace the `constant-number` node with a `pot` node and transfer the brightness value to LED1 and LED2 from this pot node, instead of the `constant-number`.\n\n## Instructions\n\n1. Delete the links between the pins. To do this, click on a link, then press Delete or Backspace key.\n\n2. Delete the `constant-number` node. Click on it and press Delete or Backspace.\n\n3. Find the `pot` node in the Project Browser inside the `xod/common-hardware` library.\n\n4. Connect a potentiometer to the Arduino according to the [scheme](https://goo.gl/ZQjdv7).\n\n5. Set the `PORT` pin value on the `pot` node to 0, bacause it’s connected to analog Arduino port A0. \n\n6. Link the `pot` node `VAL` pin to the `LUM` pins on the LED1 and LED2 nodes.\n\n7. Upload the patch to the Arduino.\n\nIf you turn the potentiometer knob, it will affect the brightness of the LEDs. Depending on the angle of the knob, the `pot` node returns a value from 0.0 to 1.0 to the `VAL` pin, and that value is transferred to the `LUM` pins of both LED nodes.", + "id": "Sk8zxUaKW", + "position": { + "x": -34, + "y": 0 + }, + "size": { + "height": 663, + "width": 306 + } + } + }, + "links": { + "Hktefo9sb": { + "id": "Hktefo9sb", + "input": { + "nodeId": "HyX57vEFZ", + "pinKey": "HyYh1a3LZ" + }, + "output": { + "nodeId": "SJnNBvNYZ", + "pinKey": "B1x2RV3eZ" + } + }, + "rkwlGoqjZ": { + "id": "rkwlGoqjZ", + "input": { + "nodeId": "rkWQ9mDEtW", + "pinKey": "HyYh1a3LZ" + }, + "output": { + "nodeId": "SJnNBvNYZ", + "pinKey": "B1x2RV3eZ" + } + } + }, + "nodes": { + "HyX57vEFZ": { + "boundValues": { + "B1oqkTnIb": 11 + }, + "id": "HyX57vEFZ", + "label": "LED2", + "position": { + "x": 408, + "y": 204 + }, + "type": "xod/common-hardware/led" + }, + "SJnNBvNYZ": { + "boundValues": { + "B1x2RV3eZ": 1 + }, + "id": "SJnNBvNYZ", + "position": { + "x": 374, + "y": 0 + }, + "type": "xod/core/constant-number" + }, + "rkWQ9mDEtW": { + "boundValues": { + "B1oqkTnIb": 10 + }, + "id": "rkWQ9mDEtW", + "label": "LED1", + "position": { + "x": 306, + "y": 204 + }, + "type": "xod/common-hardware/led" + } + }, + "path": "@/09-pot" + }, + "@/10-math": { + "comments": { + "BJj5fP6YZ": { + "content": "It’s a `multiply` node.\n- `X` and `Y` - numbers to multiply\n- `PROD` - product of the `X` and `Y`", + "id": "BJj5fP6YZ", + "position": { + "x": 510, + "y": 102 + }, + "size": { + "height": 153, + "width": 272 + } + }, + "Hkp_Mw6Kb": { + "content": "You probably noticed that there is a `UPD` input pin on the pot node and that it’s blue.\n\nThe pin’s color indicates the *type* of data that this pin is compatible with.\nGreen pins can take and return numerical values. Blue pins work with pulses, a special data type that we will return to later.\n\nLet’s try to do some math.\n\n## Instructions\n\n1. Delete the link between the `pot` node and LED2.\n\n2. Link the pot `VAL` pin to the `X` pin of the `multiply` node. Then, connect the `PROD` pin to the `LUM` pin on LED2.\n\n3. Upload the patch to the Arduino.\n\nTurn the potentiometer knob. The LED on port 11 will reach the maximum brightness with a half-turn of the knob. This happens because the `multiply` node multiplied the values of `X` and `Y`, and transferred the multiplication result to the `PROD` pin. As `Y` was set to 2 in the Inspector, the value of the `pot` node doubles before reaching the `LUM` pin of LED2.\n\nIn `xod/core`, you will find nodes for different mathematical and trigonometric calculations. Try other operators like `add`, `subtract`, `divide`.", + "id": "Hkp_Mw6Kb", + "position": { + "x": -34, + "y": 0 + }, + "size": { + "height": 663, + "width": 306 + } + } + }, + "links": { + "S1_ZaA13Z": { + "id": "S1_ZaA13Z", + "input": { + "nodeId": "SJihHwNKb", + "pinKey": "HyYh1a3LZ" + }, + "output": { + "nodeId": "SyclTC1hb", + "pinKey": "H1sM2A12-" + } + }, + "rkrZaAkn-": { + "id": "rkrZaAkn-", + "input": { + "nodeId": "r1V3SDEtW", + "pinKey": "HyYh1a3LZ" + }, + "output": { + "nodeId": "SyclTC1hb", + "pinKey": "H1sM2A12-" + } + } + }, + "nodes": { + "SJihHwNKb": { + "boundValues": { + "B1oqkTnIb": 11 + }, + "id": "SJihHwNKb", + "label": "LED2", + "position": { + "x": 408, + "y": 204 + }, + "type": "xod/common-hardware/led" + }, + "SyclTC1hb": { + "id": "SyclTC1hb", + "position": { + "x": 306, + "y": 0 + }, + "type": "xod/common-hardware/pot" + }, + "Synz8v4Yb": { + "boundValues": { + "SJ4zUC_BD1-": 2 + }, + "id": "Synz8v4Yb", + "position": { + "x": 408, + "y": 102 + }, + "type": "xod/core/multiply" + }, + "r1V3SDEtW": { + "boundValues": { + "B1oqkTnIb": 10 + }, + "id": "r1V3SDEtW", + "label": "LED1", + "position": { + "x": 306, + "y": 204 + }, + "type": "xod/common-hardware/led" + } + }, + "path": "@/10-math" + }, + "@/11-servo": { + "comments": { + "BJ52b_at-": { + "content": "Controlling servos in XOD is just as easy as controlling LEDs. There is a special node called `servo`. You will find it in `xod/common-hardware`.\n\n## Instructions\n\n1. Connect a servo to the Arduino as shown on the [scheme](https://goo.gl/Kqu3Ty).\n\n2. Link the `VAL` pin of the `pot` node to the `VAL` pin of the `servo` node.\n\n3. Upload the patch to the Arduino.\n\nDone! Turn the potentiometer knob, and watch the servo turn!\n", + "id": "BJ52b_at-", + "position": { + "x": -34, + "y": 0 + }, + "size": { + "height": 255, + "width": 306 + } + }, + "HkDByupFW": { + "content": "- `PORT` — digital port on the Arduino that the servo is connected to\n- `VAL` — a value ranging from 0 to 1; it turns the servo shaft from 0 to 180°\n", + "id": "HkDByupFW", + "position": { + "x": 408, + "y": 204 + }, + "size": { + "height": 153, + "width": 306 + } + } + }, + "nodes": { + "Syv38wNYW": { + "boundValues": { + "S1vGmu6Gb": 9 + }, + "id": "Syv38wNYW", + "position": { + "x": 306, + "y": 204 + }, + "type": "xod/common-hardware/servo" + }, + "rk_tTCk2Z": { + "id": "rk_tTCk2Z", + "position": { + "x": 306, + "y": 0 + }, + "type": "xod/common-hardware/pot" + } + }, + "path": "@/11-servo" + }, + "@/12-help": { + "comments": { + "B1bloO6YZ": { + "content": "Actually, we were lucky in the previous patch because the range of values from `pot` (0–1) matched the range accepted by the `servo` node. However, that doesn’t always happen. Often, value ranges on different nodes are incompatible, and we need to match them.\n\n## Instructions\n\n1. Find out the range of the `Tc` output pin on the thermometer node. To do so, click the node, and then click the book icon in the Inspector. You will see a help page. The other way to do it is to click on the node and then press the H key.\n\n2. Read the description. Every node’s description contains full information about the node including the input and output ranges and the types of data.\n\nNow you can figure out the node range by yourself; you just need to read the help page. Proceed to the next patch.", + "id": "B1bloO6YZ", + "position": { + "x": -68, + "y": 0 + }, + "size": { + "height": 459, + "width": 306 + } + }, + "H13O5OpYb": { + "content": "This node allows one to read data from TMP36 thermometers.\n- `PORT` — an analog Arduino port the thermometer is connected to.\n- `Tc` — the temperature value.", + "id": "H13O5OpYb", + "position": { + "x": 374, + "y": 0 + }, + "size": { + "height": 153, + "width": 340 + } + } + }, + "nodes": { + "Hy3tvDEKZ": { + "boundValues": { + "S1vGmu6Gb": 9 + }, + "id": "Hy3tvDEKZ", + "position": { + "x": 272, + "y": 204 + }, + "type": "xod/common-hardware/servo" + }, + "rkEKww4tW": { + "boundValues": { + "H1s0WI6MZ": 1 + }, + "id": "rkEKww4tW", + "position": { + "x": 272, + "y": 0 + }, + "type": "xod/common-hardware/thermometer-tmp36" + } + }, + "path": "@/12-help" + }, + "@/13-map": { + "comments": { + "rJ082K6KZ": { + "content": "- `X` — the input value that needs to be transformed\n- `Smin` — the lower bound of the input range\n- `Smax` — the upper bound of the input range\n- `Tmin` — the lower bound of the transformed value\n- `Tmax` — the upper bound of the transformed value\n- `Xm` — the mapped (transformed) value", + "id": "rJ082K6KZ", + "position": { + "x": 510, + "y": 102 + }, + "size": { + "height": 153, + "width": 374 + } + }, + "rJ1d3KptZ": { + "content": "If you have finished the previous experiment, you will have noticed that the thermometer node returns an output temperature value to the `Tc` pin in degrees Celsius. The `servo` node can work only with values ranging from 0 to 1 (0 is 0°, 1 is 180°).\n\nThe practical task is to make the servo rotate smoothly from 0 to 90°, reflecting a temperature change from 20°C to 50°C.\n\nYou can actually do this using a few math nodes, but we have a special node for such cases. This node is called the `map-range` node.\n\n## Instructions\n\n1. Connect a TMP36 thermometer to the Arduino as shown on the [scheme](https://goo.gl/gFxjpg).\n\n2. Link the `Tc` pin to the `X` pin on the `map-range` node.\n\n3. Define the input range. In our case, it will be numbers from 20 to 50. Open the Inspector for the `map-range`, and then set `Smin` to 20 and `Smax` to 50.\n\n4. Define the output range: set the `Tmin` to 0 and `Tmax` to 0.5.\n\n5. Link the `Xm` output to the `VAL` servo input.\n\nTry to heat the thermometer with a hot object (e.g. a paper knife heaten with a lightfire). At a temperature of 35°C (half the input range), the servo should rotate to 45°, which is half the output range.", + "id": "rJ1d3KptZ", + "position": { + "x": -34, + "y": 0 + }, + "size": { + "height": 663, + "width": 306 + } + } + }, + "nodes": { + "Bklh_vNFb": { + "boundValues": { + "H1s0WI6MZ": 1 + }, + "id": "Bklh_vNFb", + "position": { + "x": 306, + "y": 0 + }, + "type": "xod/common-hardware/thermometer-tmp36" + }, + "HkZpdP4tW": { + "boundValues": { + "HJCWLAdSwyW": 0, + "rkpbU0OrwyZ": 0, + "ry1z8CuBDy-": 0 + }, + "id": "HkZpdP4tW", + "position": { + "x": 306, + "y": 102 + }, + "type": "xod/core/map-range" + }, + "rkK3_PEYb": { + "boundValues": { + "S1vGmu6Gb": 9 + }, + "id": "rkK3_PEYb", + "position": { + "x": 306, + "y": 204 + }, + "type": "xod/common-hardware/servo" + } + }, + "path": "@/13-map" + }, + "@/14-map-adjust": { + "comments": { + "r1WPI5CKb": { + "content": "The `map-range` node will now linearly map the 20–50°C range to 0–90°.Let’s raise the sensitivity. Say, we want to map 15–30°C to 0–90° rotation.\n\nHowever, what will happen if the `X` pin of the `map-range` receives data that\nis outside the input range (10 or 42, for example)? The output value which is\na servo angle will fall outside the desired range too.\n\nFor such cases there is a sibling node called `map-clip-range`. It works the\nsame way, but any input that is out of range will be rounded to `Smin` or\n`Smax`. Thus, if `X` receives a value of 10, the `map-clip-range` node will accept it as 15, and 42 will be accepted as 30.\n\n## Instructions\n\n1. Replace the `map-range` node with a `map-clip-range` node. They both are located in `xod/core`.\n\n2. Set the input range (`Smin` and `Smax`) to 15–30.\n\n3. Set the output range (`Tmin` and `Tmax`) to 0–0.5.\n\n4. (Optional) If you prefer °F, you can easily translate the data from the thermometer with the `c-to-f` node from `xod/units`. The range will be 59–95°F.\n\n5. Upload.\n\nNow the servo is more sensitive to the changes of the temperature sensor.", + "id": "r1WPI5CKb", + "position": { + "x": -68, + "y": 0 + }, + "size": { + "height": 663, + "width": 306 + } + } + }, + "links": { + "BkFSFD4t-": { + "id": "BkFSFD4t-", + "input": { + "nodeId": "BJrNFP4tZ", + "pinKey": "r1sfQ_6fb" + }, + "output": { + "nodeId": "rkK7FPNFb", + "pinKey": "H12bIR_SPyZ" + } + }, + "HkvHKDNKW": { + "id": "HkvHKDNKW", + "input": { + "nodeId": "rkK7FPNFb", + "pinKey": "BJlzICOSv1-" + }, + "output": { + "nodeId": "B1WVFPEtZ", + "pinKey": "rkFgMITM-" + } + } + }, + "nodes": { + "B1WVFPEtZ": { + "boundValues": { + "H1s0WI6MZ": 1 + }, + "id": "B1WVFPEtZ", + "position": { + "x": 272, + "y": 0 + }, + "type": "xod/common-hardware/thermometer-tmp36" + }, + "BJrNFP4tZ": { + "boundValues": { + "S1vGmu6Gb": 9 + }, + "id": "BJrNFP4tZ", + "position": { + "x": 272, + "y": 204 + }, + "type": "xod/common-hardware/servo" + }, + "rkK7FPNFb": { + "boundValues": { + "HJCWLAdSwyW": 50, + "rkpbU0OrwyZ": 0.5, + "ry1z8CuBDy-": 20 + }, + "id": "rkK7FPNFb", + "position": { + "x": 272, + "y": 102 + }, + "type": "xod/core/map-range" + } + }, + "path": "@/14-map-adjust" + }, + "@/15-buttons": { + "comments": { + "H1IQRsAKW": { + "content": "- `PORT` — a digital port on the Arduino that the button is connected to\n- `PRS` — returns true value when the button is pressed and false otherwise", + "id": "H1IQRsAKW", + "position": { + "x": 374, + "y": 102 + }, + "size": { + "height": 51, + "width": 544 + } + }, + "r1BpRoRt-": { + "content": "So, buttons!\n\nWe have already added the `button` node from `xod/common-hardware`. The `button` node has a purple output pin called the `PRS`. This pin returns a new type of data: *boolean*.\n\nBoolean data can have either of two values: true or false. In our case, the `button` node returns a value of `false` when idle and `true` while the button is being pressed.\n\nGood news, in XOD boolean and number data types are compatible. Here are two rules of datacasting:\n\n* Boolean-to-number: if you send a boolean false to a numeric (green) input, it will be interpreted as a numeric 0; if you send a boolean true, it will be interpreted as a numeric 1.\n* Number-to-boolean: if you send any numeric value except 0 to a boolean (purple) input; it will be interpreted as true, and if you send 0, it will be interpreted as false.\n\n## Instructions\n\n1. Assemble the [circuit with a button and LED](https://goo.gl/euz896).\n\n2. Set the `PORT` value.\n\n3. Link the `PRS` pin to the `LUM` pin.\n\n4. Upload your patch.\n\nWhen you press the button the `button` node sets the `PRS` pin to `true`, the `led` node (`LUM` pin) interprets it as 1, and the LED turns on at full brightness.", + "id": "r1BpRoRt-", + "position": { + "x": -68, + "y": 0 + }, + "size": { + "height": 765, + "width": 306 + } + } + }, + "nodes": { + "S13FKPEFb": { + "boundValues": { + "B1oqkTnIb": 11 + }, + "id": "S13FKPEFb", + "position": { + "x": 272, + "y": 204 + }, + "type": "xod/common-hardware/led" + }, + "rkRFKDVF-": { + "boundValues": { + "ByNiWkt8Z": 13 + }, + "id": "rkRFKDVF-", + "position": { + "x": 272, + "y": 102 + }, + "type": "xod/common-hardware/button" + } + }, + "path": "@/15-buttons" + }, + "@/16-logic": { + "comments": { + "H1I643knZ": { + "content": "* `and`: returns `true` if both `A` *and* `B` are `true`.\n* `or`: returns `true` if A, B, or both are `true`.\n* `not` inverts the input value.\n\nExplore other logic nodes by yourself using node help (click a node and press H key).", + "id": "H1I643knZ", + "position": { + "x": 612, + "y": 204 + }, + "size": { + "height": 153, + "width": 442 + } + }, + "H1YyvTRt-": { + "content": "Boolean values are cool. You can use them to construct complex logic systems. In XOD, you will find a lot of nodes for this purpose. All of them are located in `xod/core`.\n\n## Instructions\n\n1. Assemble a [circuit with two buttons and a LED](https://goo.gl/ts9EdE).\n\n2. Upload the patch to the Arduino.\n\nThe `and` node returns `false` until both buttons are pressed. The not node inverts the value from the `and` node, so the `LUM` pin receives its true value (1). Thus, the LED turns on.\n\nPressing one of the buttons changes nothing. The LED will turn off only when the `LUM` pin receives false (0). This will happen only if the `not` node receives the true value, which is possible only if both buttons are pressed at the same time.", + "id": "H1YyvTRt-", + "position": { + "x": -34, + "y": 0 + }, + "size": { + "height": 459, + "width": 306 + } + } + }, + "links": { + "Hk_79PVtW": { + "id": "Hk_79PVtW", + "input": { + "nodeId": "Byp-5wEF-", + "pinKey": "r1bVLR_BPJW" + }, + "output": { + "nodeId": "ByVg5w4F-", + "pinKey": "BJ--G1tI-" + } + }, + "S1PX5DVtW": { + "id": "S1PX5DVtW", + "input": { + "nodeId": "Byp-5wEF-", + "pinKey": "SJMVU0urvkZ" + }, + "output": { + "nodeId": "ByHgcw4Yb", + "pinKey": "BJ--G1tI-" + } + }, + "S1oQqw4YW": { + "id": "S1oQqw4YW", + "input": { + "nodeId": "ByCkcvVYW", + "pinKey": "HyYh1a3LZ" + }, + "output": { + "nodeId": "BJ3z5DEtZ", + "pinKey": "r1if8ROSDJ-" + } + }, + "r1KXqPNYW": { + "id": "r1KXqPNYW", + "input": { + "nodeId": "BJ3z5DEtZ", + "pinKey": "ry3zLA_Bv1Z" + }, + "output": { + "nodeId": "Byp-5wEF-", + "pinKey": "B1gN80uHvk-" + } + } + }, + "nodes": { + "BJ3z5DEtZ": { + "id": "BJ3z5DEtZ", + "position": { + "x": 306, + "y": 204 + }, + "type": "xod/core/not" + }, + "ByCkcvVYW": { + "boundValues": { + "B1oqkTnIb": 11 + }, + "id": "ByCkcvVYW", + "position": { + "x": 306, + "y": 306 + }, + "type": "xod/common-hardware/led" + }, + "ByHgcw4Yb": { + "boundValues": { + "ByNiWkt8Z": 13 + }, + "id": "ByHgcw4Yb", + "position": { + "x": 306, + "y": 0 + }, + "type": "xod/common-hardware/button" + }, + "ByVg5w4F-": { + "boundValues": { + "ByNiWkt8Z": 12 + }, + "id": "ByVg5w4F-", + "position": { + "x": 408, + "y": 0 + }, + "type": "xod/common-hardware/button" + }, + "Byp-5wEF-": { + "id": "Byp-5wEF-", + "position": { + "x": 306, + "y": 102 + }, + "type": "xod/core/and" + }, + "H1xr5vNtZ": { + "id": "H1xr5vNtZ", + "position": { + "x": 408, + "y": 306 + }, + "type": "xod/core/nor" + }, + "r1U4cD4Yb": { + "id": "r1U4cD4Yb", + "position": { + "x": 408, + "y": 204 + }, + "type": "xod/core/or" + }, + "r1rHcPVYZ": { + "id": "r1rHcPVYZ", + "position": { + "x": 510, + "y": 306 + }, + "type": "xod/core/xor" + }, + "rkTBcPEFZ": { + "id": "rkTBcPEFZ", + "position": { + "x": 510, + "y": 204 + }, + "type": "xod/core/nand" + } + }, + "path": "@/16-logic" + }, + "@/17-ldr": { + "comments": { + "H1AsRpAtZ": { + "content": "Let’s introduce a new sensor to measure an ambient light level. A\nphotoresistor (aka light dependent resistor or LDR) would do a great job of\nmeasuring the parameter. However, there is a small problem: we do not have a\nphotoresistor node in XOD.\n\nYet, we have basic nodes for working with the digital and analog ports of the Arduino. A photoresistor is a pretty primitive device, and all we need to do is read its value from the analog port on the Arduino. To do so, we will use an `analog-input` node.\n\n## Instructions\n\n1. Assemble the [circuit with LDR and LED](https://goo.gl/2QczLg).\n\n2. Find the `analog-input` node in `xod/core` and add it to the patch.\n\n3. Read the description of the node on the help page. Pay attention to the range of values the node returns.\n\n4. Link the `VAL` output pin of the `analog-input` node to the `LUM` pin of the led node.\n\n5. Upload the patch to the Arduino.\n\nCover the photoresistor with your hand, and watch how the brightness of the LED changes.", + "id": "H1AsRpAtZ", + "position": { + "x": -68, + "y": 0 + }, + "size": { + "height": 561, + "width": 306 + } + } + }, + "nodes": { + "Hk0_5wNY-": { + "boundValues": { + "B1oqkTnIb": 11 + }, + "id": "Hk0_5wNY-", + "position": { + "x": 272, + "y": 204 + }, + "type": "xod/common-hardware/led" + } + }, + "path": "@/17-ldr" + }, + "@/18-comparisons": { + "comments": { + "B1XVTkyc-": { + "content": "- `LHS` and `RHS` are the input for the values to compare\n- `LT` returns `true` if LHS < RHS and `false` otherwise.", + "id": "B1XVTkyc-", + "position": { + "x": 374, + "y": 306 + }, + "size": { + "height": 51, + "width": 442 + } + }, + "BJBXpkJq-": { + "content": "- `LHS` and `RHS` are input pins for numeric values to compare\n- `GT` returns `true` if LHS > RHS and `false` otherwise.", + "id": "BJBXpkJq-", + "position": { + "x": 374, + "y": 102 + }, + "size": { + "height": 51, + "width": 476 + } + }, + "H1dIB11c-": { + "content": "Now for comparisons. XOD has `greater`-than and `less`-than nodes to do simple mathematical comparisons.\n\n## Instructions\n\n1. Link the `VAL` pin of the `analog-input` node to the `LHS` pin on the `greater` node.\n\n2. Set a value of 0.4 on the `RHS` pin. You can still do this in the Inspector. This is the value we will compare the photoresistor value with.\n\n3. Link the `GT` pin on the `greater` node to the `LUM` pin of the `led` node.\n\nNow, if the `analog-input` node (our photoresistor) returns a value greater than 0.4, the `greater` node will set the `GT` pin to “true,” and the LED will turn on.\n\nTry to set the comparison value manually using a potentiometer. Tips:\n1. Connect the potentiometer and add another `analog-input` node from `xod/core`. Define the `PORT` value for the Arduino port that the potentiometer is connected to.\n\n2. Link the output of this node to the `RHS` pin of the `greater` node.\n\nNow the threshold value is not fixed but is defined by the potentiometer.", + "id": "H1dIB11c-", + "position": { + "x": -68, + "y": 0 + }, + "size": { + "height": 663, + "width": 306 + } + } + }, + "nodes": { + "BJszoD4tZ": { + "boundValues": { + "B1oqkTnIb": 10 + }, + "id": "BJszoD4tZ", + "position": { + "x": 272, + "y": 204 + }, + "type": "xod/common-hardware/led" + }, + "BygDsvVFZ": { + "id": "BygDsvVFZ", + "position": { + "x": 272, + "y": 306 + }, + "type": "xod/core/less" + }, + "H1UNiwVt-": { + "boundValues": { + "BJuORNheZ": 2 + }, + "id": "H1UNiwVt-", + "position": { + "x": 272, + "y": 0 + }, + "type": "xod/core/analog-input" + }, + "S1THoPVFZ": { + "boundValues": { + "HJbACN3gb": 0 + }, + "id": "S1THoPVFZ", + "position": { + "x": 272, + "y": 102 + }, + "type": "xod/core/greater" + } + }, + "path": "@/18-comparisons" + }, + "@/19-if-else": { + "comments": { + "HyjM1O9oW": { + "content": "- `COND` - general input.\n- `T` - value passed to the `R` pin if `COND` receives true.\n- `F` - value passed to the `R` pin if `COND` receives false.\n- `R` - general output\n", + "id": "HyjM1O9oW", + "position": { + "x": 374, + "y": 102 + }, + "size": { + "height": 153, + "width": 442 + } + }, + "rJKFUe4cW": { + "content": "All the math comparison nodes return true or false.\n\nLet's imagine that you need to turn the servo to a certain angle, for instance to 45° when you receive true, and to 135° when you receive false. How can we make that work?\n\t\nThere is an `if-else` node in `xod/core`. The `COND` pin of this node checks the boolean input value. If `COND` receives true, the node sets the value from the `T` pin to the `R` pin. When it receives false, it sets the value from the `F` pin to the `R` pin.\n\n## Instructions\n\n1. [Assemble the circuit with a servo](https://goo.gl/5mqqAg).\n\n2. Define the value on the `RHS` pin of the `greater` node. This value will be compared to the value from the photoresistor (coming to the `LHS` pin).\n\n3. Define the values for the `T` and `F` pins on the `if-else` node. These values will be sent to the servo. Remember about the value range of the servo node: 45° of the servo will be 0.25, and 135° will be 0.75.\n\n4. Upload the patch to the Arduino.\n\nNow, if the comparison condition is true, the servo will turn to the angle set in the `T` pin of the `if-else` node or, otherwise, to the angle set in the `F` pin of the same node.", + "id": "rJKFUe4cW", + "position": { + "x": -102, + "y": -102 + }, + "size": { + "height": 663, + "width": 306 + } + } + }, + "links": { + "Hk5wdU9oW": { + "id": "Hk5wdU9oW", + "input": { + "nodeId": "By4w_LqoZ", + "pinKey": "r1sfQ_6fb" + }, + "output": { + "nodeId": "BywpJ149Z", + "pinKey": "S13xLCuHvkW" + } + }, + "SkhI7bE5Z": { + "id": "SkhI7bE5Z", + "input": { + "nodeId": "Bkxay1E5Z", + "pinKey": "rJg00Nhe-" + }, + "output": { + "nodeId": "H1pMkkVqZ", + "pinKey": "SyBtREhlW" + } + }, + "r1Z_QZEcb": { + "id": "r1Z_QZEcb", + "input": { + "nodeId": "BywpJ149Z", + "pinKey": "S1yZIA_rDJZ" + }, + "output": { + "nodeId": "Bkxay1E5Z", + "pinKey": "B19RYS3lW" + } + } + }, + "nodes": { + "Bkxay1E5Z": { + "boundValues": { + "HJbACN3gb": 0 + }, + "id": "Bkxay1E5Z", + "position": { + "x": 238, + "y": 0 + }, + "type": "xod/core/greater" + }, + "By4w_LqoZ": { + "boundValues": { + "S1vGmu6Gb": 9 + }, + "id": "By4w_LqoZ", + "position": { + "x": 238, + "y": 204 + }, + "type": "xod/common-hardware/servo" + }, + "BywpJ149Z": { + "boundValues": { + "r1AgIROHDJW": 0, + "ryTeUROHD1b": 0 + }, + "id": "BywpJ149Z", + "position": { + "x": 238, + "y": 102 + }, + "type": "xod/core/if-else" + }, + "H1pMkkVqZ": { + "boundValues": { + "BJuORNheZ": 2 + }, + "id": "H1pMkkVqZ", + "position": { + "x": 238, + "y": -102 + }, + "type": "xod/core/analog-input" + } + }, + "path": "@/19-if-else" + }, + "@/20-fade": { + "comments": { + "rkO6OLqiZ": { + "content": "Now, our servo can turn to the set angle at its maximum speed. High speed is not\nalways necessary and can damage the servo itself if the shaft hits an obstacle.\n\nLet’s improve our patch by making the servo move more smoothly. To achieve that, we'll need to set the `VAL` pin value along with a series of transitional values. There's a node for that, and it's called `fade`.\n\nThe `fade` node is in `xod/core`. This node sets a series of transitional values, so it will smoothen our servo motion.\n\nThe `TARG` pin waits for a target value.\n\nThe `RATE` pin is a number that defines amount of change for the transitional\nvalues per each second.\n\nThe output pin returns 0 at the start of the program. Then, it starts to move toward the `TARG` value by the `RATE` steps. The value of the output pin is saved, so if the `TARG` value changes, the output will start to move to this value from the last one returned.\n\n## Instructions\n\n1. Set the `RATE` value to 0.1.\n\n2. Upload the patch to the Arduino.\n\nNow, the servo will turn at a speed of 18° per second (0.1 = 18°).", + "id": "rkO6OLqiZ", + "position": { + "x": -34, + "y": 0 + }, + "size": { + "height": 663, + "width": 306 + } + } + }, + "links": { + "B1zhuL5jb": { + "id": "B1zhuL5jb", + "input": { + "nodeId": "B185dU5s-", + "pinKey": "HyYJqJFLZ" + }, + "output": { + "nodeId": "BkiYd85o-", + "pinKey": "S13xLCuHvkW" + } + }, + "SJqnuU9iZ": { + "id": "SJqnuU9iZ", + "input": { + "nodeId": "BkqfdIqob", + "pinKey": "rJg00Nhe-" + }, + "output": { + "nodeId": "rJyzdUqjZ", + "pinKey": "SyBtREhlW" + } + }, + "r1Bo_Uqj-": { + "id": "r1Bo_Uqj-", + "input": { + "nodeId": "B1ki_85jZ", + "pinKey": "r1sfQ_6fb" + }, + "output": { + "nodeId": "B185dU5s-", + "pinKey": "BJ0M5JKUW" + } + }, + "ryOhdLqs-": { + "id": "ryOhdLqs-", + "input": { + "nodeId": "BkiYd85o-", + "pinKey": "S1yZIA_rDJZ" + }, + "output": { + "nodeId": "BkqfdIqob", + "pinKey": "B19RYS3lW" + } + } + }, + "nodes": { + "B185dU5s-": { + "boundValues": { + "BkweckF8-": 0 + }, + "id": "B185dU5s-", + "position": { + "x": 306, + "y": 306 + }, + "type": "xod/core/fade" + }, + "B1ki_85jZ": { + "boundValues": { + "S1vGmu6Gb": 9 + }, + "id": "B1ki_85jZ", + "position": { + "x": 306, + "y": 408 + }, + "type": "xod/common-hardware/servo" + }, + "BkiYd85o-": { + "boundValues": { + "ryTeUROHD1b": 0 + }, + "id": "BkiYd85o-", + "position": { + "x": 306, + "y": 204 + }, + "type": "xod/core/if-else" + }, + "BkqfdIqob": { + "id": "BkqfdIqob", + "position": { + "x": 306, + "y": 102 + }, + "type": "xod/core/greater" + }, + "rJyzdUqjZ": { + "boundValues": { + "BJuORNheZ": 2 + }, + "id": "rJyzdUqjZ", + "position": { + "x": 306, + "y": 0 + }, + "type": "xod/core/analog-input" + } + }, + "path": "@/20-fade" + }, + "@/21-pulses": { + "comments": { + "r1ElrF2lM": { + "content": "In the tenth patch, we briefly mentioned the blue `UPD` input pin and the *pulse* data type.\n\nThis type is very different from all the others. All values ​​in the XOD are transferred to the links continuously, even if the values ​​haven't changed. The pulse data type is different. Imagine something like a flash. It’s basically a message to trigger something else. It does not transmit any data. Howerver, it is useful when you need to convey that an event has occurred or tell something else to work.\n\nFor example, say you only want an `analog-input` node to receive information from a board port at certain intervals, and not all the time. This frequency can be set with the help of pulses. Each time a pulse arrives on the `UPD` pin, the node reads the analog port and outputs the value to the `VAL` pin. This value will be stored there until it changes to another value.\n\n## Instructions\n\n1. Make a [circuit with a servo and pot](https://xod.io/docs/tutorial/21-pulses/?utm_source=ide&utm_medium=ide_comment&utm_campaign=tutorial_21).\n2. Open the Inspector for the `pot` node.\n3. Change the `UPD` value to `On boot`.\n4. Upload the patch to the Arduino.\n\nNow try to turn the potentiometer knob and press the reset button on the board itself. As you can see, the potentiometer updates now only once when the Arduino is turned on.", + "id": "r1ElrF2lM", + "position": { + "x": -34, + "y": 0 + }, + "size": { + "height": 663, + "width": 306 + } + }, + "r1TWu48HM": { + "content": "The behavior of a pulse pin can be set in the Inspector. For a pot the choice means:\n\n* `Never`: Never produce pulses, i.e. Do not take readings from the analog port at all.\n* `On boot`: Generate a pulse once at startup. We will update the state exactly once.\n* `Continuously`: Generate pulses constantly, i.e. take the readings from the analog port with the highest possible rate.\n\n", + "id": "r1TWu48HM", + "position": { + "x": 408, + "y": 204 + }, + "size": { + "height": 255, + "width": 340 + } + } + }, + "links": { + "SJADqz45Z": { + "id": "SJADqz45Z", + "input": { + "nodeId": "Skw0PzVcZ", + "pinKey": "r1sfQ_6fb" + }, + "output": { + "nodeId": "H1O2DMN5b", + "pinKey": "SyBtREhlW" + } + } + }, + "nodes": { + "H1O2DMN5b": { + "id": "H1O2DMN5b", + "label": "pot", + "position": { + "x": 306, + "y": 204 + }, + "type": "xod/core/analog-input" + }, + "Skw0PzVcZ": { + "boundValues": { + "S1vGmu6Gb": 9 + }, + "id": "Skw0PzVcZ", + "position": { + "x": 306, + "y": 408 + }, + "type": "xod/common-hardware/servo" + } + }, + "path": "@/21-pulses" + }, + "@/22-clock": { + "comments": { + "r1zzTtngM": { + "content": "Many tasks require you to set a specific frequency for the pulse generation.\n\nFor this, we need a new node `clock`. It can produce pulse signals at time intervals specified by the `IVAL` pin. This way, we can control the frequency of data collection from our sensors.\n\n## Instructions\n\n1. Set the `IVAL` pin in the `clock` node to `2`. This means that it will send a pulse every two seconds.\n\n2. Connect the `TICK` pin to the `UPD` pin on the pot node.\n\n3. Upload the patch to the Arduino.\n\nSlowly turn the potentiometer knob. You will see that the readings are being taken and the servo reacts to them every two seconds.", + "id": "r1zzTtngM", + "position": { + "x": -34, + "y": 102 + }, + "size": { + "height": 459, + "width": 306 + } + } + }, + "links": { + "B1fHXXNqb": { + "id": "B1fHXXNqb", + "input": { + "nodeId": "By-NXQVqW", + "pinKey": "r1sfQ_6fb" + }, + "output": { + "nodeId": "ByUpzmN5W", + "pinKey": "SyBtREhlW" + } + } + }, + "nodes": { + "By-NXQVqW": { + "boundValues": { + "S1vGmu6Gb": 9 + }, + "id": "By-NXQVqW", + "position": { + "x": 306, + "y": 510 + }, + "type": "xod/common-hardware/servo" + }, + "ByUpzmN5W": { + "id": "ByUpzmN5W", + "label": "pot", + "position": { + "x": 306, + "y": 408 + }, + "type": "xod/core/analog-input" + }, + "rk_NQmEqb": { + "boundValues": { + "B13SCNhl-": 2 + }, + "id": "rk_NQmEqb", + "position": { + "x": 306, + "y": 204 + }, + "type": "xod/core/clock" + } + }, + "path": "@/22-clock" + }, + "@/23-count": { + "comments": { + "SJsjJ53gz": { + "content": "Let’s make a funky watch hand. Now we have everything to do this.\n\nThe clock will be the servo. Using the `count` node, we will change its rotation from 0° to 180° in one minute.\n\n## Instructions\n\n1. In the `clock` node, set the value of `IVAL` to `1` second.\n\n2. In the `count` node, set `STEP` to `0.017`. This means that every time the pulse arrives on the `INC` pin (in our case, once per second), the node will increase the number on the output by 0.017. For the servo node, this is approximately 3°.\n\n3. Upload the patch to the Arduino.\n\nYou will see that the servo is “ticking” every second. In one minute, it will reach 180°.", + "id": "SJsjJ53gz", + "position": { + "x": -34, + "y": 0 + }, + "size": { + "height": 459, + "width": 306 + } + }, + "SyqK15nxf": { + "content": "The `count` node works as a simple counter. Each time it receives a pulse, it increases the number that it gives out, in increments of the `STEP` pin.", + "id": "SyqK15nxf", + "position": { + "x": 476, + "y": 204 + }, + "size": { + "height": 51, + "width": 408 + } + } + }, + "links": { + "r1VYXX4q-": { + "id": "r1VYXX4q-", + "input": { + "nodeId": "SkddQ749Z", + "pinKey": "HJAq-A_8-" + }, + "output": { + "nodeId": "r1xdmX4qW", + "pinKey": "HJU8CE2lW" + } + }, + "rksvMdSc-": { + "id": "rksvMdSc-", + "input": { + "nodeId": "Hy_vm7E9b", + "pinKey": "r1sfQ_6fb" + }, + "output": { + "nodeId": "SkddQ749Z", + "pinKey": "r1yhZRd8W" + } + } + }, + "nodes": { + "Hy_vm7E9b": { + "boundValues": { + "S1vGmu6Gb": 9 + }, + "id": "Hy_vm7E9b", + "position": { + "x": 306, + "y": 306 + }, + "type": "xod/common-hardware/servo" + }, + "SkddQ749Z": { + "id": "SkddQ749Z", + "position": { + "x": 306, + "y": 204 + }, + "type": "xod/core/count" + }, + "r1xdmX4qW": { + "id": "r1xdmX4qW", + "position": { + "x": 306, + "y": 102 + }, + "type": "xod/core/clock" + } + }, + "path": "@/23-count" + }, + "@/24-flip-flop": { + "comments": { + "Hy-l49nxf": { + "content": "You can control the behavior of many nodes with pulses. A very useful node is `flip-flop`. It acts like a virtual switch, whose states can be controlled by pulses.\n\nLet’s do a classic experiment: blink the LED.\n\n## Instructions\n\n1. Assemble a [circuit with the LED](https://xod.io/docs/tutorial/24-flip-flop/?utm_source=ide&utm_medium=ide_comment&utm_campaign=tutorial_24). Do not forget to specify the desired port in the `PORT` pin.\n2. Set the flashing frequency in seconds using `IVAL`.\n3. Upload the patch to the Arduino.\n\nThe LED will now turn on and off at the frequency you've chosen.\nTry to control the flashing speed of the LED with a potentiometer. To do this, connect the output of a pot node to the `IVAL` pin.", + "id": "Hy-l49nxf", + "position": { + "x": 0, + "y": 0 + }, + "size": { + "height": 459, + "width": 306 + } + }, + "SJVeV52ez": { + "content": "Each time a pulse arrives at the `TGL` pin, the node toggles the boolean value on the `MEM` pin from `false` to `true` and vice versa.", + "id": "SJVeV52ez", + "position": { + "x": 510, + "y": 204 + }, + "size": { + "height": 51, + "width": 408 + } + } + }, + "links": { + "BJcrQYS5W": { + "id": "BJcrQYS5W", + "input": { + "nodeId": "HyNvOmN5b", + "pinKey": "ryTIROHwkW" + }, + "output": { + "nodeId": "ByXLOXV9W", + "pinKey": "HJU8CE2lW" + } + }, + "Bk73s9rcb": { + "id": "Bk73s9rcb", + "input": { + "nodeId": "HyA_GKr9W", + "pinKey": "HyYh1a3LZ" + }, + "output": { + "nodeId": "HyNvOmN5b", + "pinKey": "HkyxURuSPyW" + } + } + }, + "nodes": { + "ByXLOXV9W": { + "boundValues": { + "B13SCNhl-": 1 + }, + "id": "ByXLOXV9W", + "position": { + "x": 340, + "y": 102 + }, + "type": "xod/core/clock" + }, + "HyA_GKr9W": { + "boundValues": { + "B1oqkTnIb": 11 + }, + "id": "HyA_GKr9W", + "position": { + "x": 340, + "y": 306 + }, + "type": "xod/common-hardware/led" + }, + "HyNvOmN5b": { + "id": "HyNvOmN5b", + "position": { + "x": 340, + "y": 204 + }, + "type": "xod/core/flip-flop" + } + }, + "path": "@/24-flip-flop" + }, + "@/25-multiple-timelines": { + "comments": { + "Bk9DtCS9b": { + "content": "The `count` node sends the accumulated rotation angle to the servo.", + "id": "Bk9DtCS9b", + "position": { + "x": 510, + "y": 408 + }, + "size": { + "height": 51, + "width": 510 + } + }, + "HkCIYRB9b": { + "content": "The `clock` node provides the pulse frequency for sending data to the servo.", + "id": "HkCIYRB9b", + "position": { + "x": 544, + "y": 306 + }, + "size": { + "height": 51, + "width": 476 + } + }, + "HyAXORSqb": { + "content": "This node sends a pulse to `flip-flop` once every 30 seconds, changing its value from `true` to `false` and back.", + "id": "HyAXORSqb", + "position": { + "x": 544, + "y": 0 + }, + "size": { + "height": 51, + "width": 340 + } + }, + "S14Ht0S9b": { + "content": "The `flip-flop` node changes the state on the `MEM` pin from 0 to 1 every 30 seconds. Fortunately, the boolean and number data types are compatible with each other — `true` and `false` are automatically converted to `1` and `0` for the `map-range` `X` input\n", + "id": "S14Ht0S9b", + "position": { + "x": 510, + "y": 102 + }, + "size": { + "height": 51, + "width": 612 + } + }, + "SJ7uYCrqb": { + "content": "The `servo` node receives a value from 0 to 1 in the `VAL` pin, which corresponds to the rotation of the shaft from 0° to 180°, respectively.", + "id": "SJ7uYCrqb", + "position": { + "x": 476, + "y": 510 + }, + "size": { + "height": 51, + "width": 544 + } + }, + "r1VItCB9b": { + "content": "The `map-range` node is used to set the rotation angle of the servo for one send from `count`.", + "id": "r1VItCB9b", + "position": { + "x": 578, + "y": 204 + }, + "size": { + "height": 51, + "width": 442 + } + }, + "ry1duHkZM": { + "content": "By using multiple independent pulse signals, we can create complex programs to control our devices. Here, we use two clock nodes to rotate the servo in one direction and then the other.\n\nThis patch is a little bigger than what we've seen so far. A good rule of thumb in XOD is to *read the patch from the bottom up*. That way you can see the cause and effect relationship created by the flow of the patch. First, you see the result and then what caused it!\n\nPay close attention to the `map-range` node. Instead of using it in the standard manner, we are simply using it to flip the sign of the output value.\n\n## Instructions\n\n1. Assemble the [servo drive circuit](https://xod.io/docs/tutorial/25-multiple-timelines/?utm_source=ide&utm_medium=ide_comment&utm_campaign=tutorial_25).\n2. Upload the patch to the Arduino.\n\nThe servo should now start ticking in one direction for 30 seconds and then in the other direction for 30 seconds.", + "id": "ry1duHkZM", + "position": { + "x": 34, + "y": 0 + }, + "size": { + "height": 459, + "width": 306 + } + } + }, + "links": { + "BJnyKhSq-": { + "id": "BJnyKhSq-", + "input": { + "nodeId": "SkEoF7Vq-", + "pinKey": "HJAq-A_8-" + }, + "output": { + "nodeId": "HkJcYQN5W", + "pinKey": "HJU8CE2lW" + } + }, + "BJtTd3S9-": { + "id": "BJtTd3S9-", + "input": { + "nodeId": "B1Epd2S9Z", + "pinKey": "BJlzICOSv1-" + }, + "output": { + "nodeId": "H1p5Y7E9-", + "pinKey": "HkyxURuSPyW" + } + }, + "SkYnOnr5b": { + "id": "SkYnOnr5b", + "input": { + "nodeId": "H1p5Y7E9-", + "pinKey": "ryTIROHwkW" + }, + "output": { + "nodeId": "SywtF745W", + "pinKey": "HJU8CE2lW" + } + }, + "SyakFhH9Z": { + "id": "SyakFhH9Z", + "input": { + "nodeId": "SkEoF7Vq-", + "pinKey": "HJvqZ0dLZ" + }, + "output": { + "nodeId": "B1Epd2S9Z", + "pinKey": "H12bIR_SPyZ" + } + }, + "rJlUaKrcW": { + "id": "rJlUaKrcW", + "input": { + "nodeId": "HyJKY74q-", + "pinKey": "r1sfQ_6fb" + }, + "output": { + "nodeId": "SkEoF7Vq-", + "pinKey": "r1yhZRd8W" + } + } + }, + "nodes": { + "B1Epd2S9Z": { + "boundValues": { + "HJCWLAdSwyW": 1, + "rJbGU0_Hv1Z": -0.017, + "rkpbU0OrwyZ": 0.017 + }, + "id": "B1Epd2S9Z", + "position": { + "x": 374, + "y": 204 + }, + "type": "xod/core/map-range" + }, + "H1p5Y7E9-": { + "id": "H1p5Y7E9-", + "position": { + "x": 374, + "y": 102 + }, + "type": "xod/core/flip-flop" + }, + "HkJcYQN5W": { + "boundValues": { + "B13SCNhl-": 1 + }, + "id": "HkJcYQN5W", + "label": "step-clock", + "position": { + "x": 408, + "y": 306 + }, + "type": "xod/core/clock" + }, + "HyJKY74q-": { + "boundValues": { + "S1vGmu6Gb": 9 + }, + "id": "HyJKY74q-", + "position": { + "x": 374, + "y": 510 + }, + "type": "xod/common-hardware/servo" + }, + "SkEoF7Vq-": { + "id": "SkEoF7Vq-", + "label": "count", + "position": { + "x": 374, + "y": 408 + }, + "type": "xod/core/count" + }, + "SywtF745W": { + "boundValues": { + "B13SCNhl-": 30 + }, + "id": "SywtF745W", + "label": "long-clock", + "position": { + "x": 374, + "y": 0 + }, + "type": "xod/core/clock" + } + }, + "path": "@/25-multiple-timelines" + }, + "@/26-lcd": { + "comments": { + "SkSPe8ybM": { + "content": "Brace yourself, because we are about to learn about the `text-lcd-16x2` node! If you haven’t already guessed, this node is used to control 16x2 LCD screens.\n\nNow, let’s concentrate on the two `constant-string` nodes. These nodes contain and transmit data of type *string*. Yellow is used to indicate string type pins and links. A string is just another name for a line of ordinary text. This sentence is a string!\n\n## Instructions\n\n1. Wire LCD and its contrast pot as shown on the [scheme](https://xod.io/docs/tutorial/26-lcd/?utm_source=ide&utm_medium=ide_comment&utm_campaign=tutorial_26). \n2. Link the `VAL` pins of the `constant-string` nodes to the `L1` and `L2` inputs on the screen node. `L1` and `L2` stand for the first and second line of the screen\n3. Upload the sketch to the Arduino.\n\nYou should now see “Hello world!” displayed on the screen.\n\nCool? Try to display your own message on the screen.", + "id": "SkSPe8ybM", + "position": { + "x": 102, + "y": 714 + }, + "size": { + "height": 561, + "width": 306 + } + } + }, + "nodes": { + "BJLI75r9Z": { + "boundValues": { + "B1TSE9tZ-": "Make", + "BJJqaX4Gb": 5, + "H1bLN9F-b": "the links!", + "HJysTXVMb": 3, + "S1nqa7NMZ": 4, + "SkBK6Q4fb": 11, + "rJlYT7EfW": 12, + "rkbjp7Ezb": 2 + }, + "id": "BJLI75r9Z", + "position": { + "x": 442, + "y": 918 + }, + "type": "xod/common-hardware/text-lcd-16x2" + }, + "By6RU3IqZ": { + "boundValues": { + "B1x2RV3eZ": "Hello" + }, + "id": "By6RU3IqZ", + "position": { + "x": 646, + "y": 816 + }, + "type": "xod/core/constant-string" + }, + "ryvyP3I9-": { + "boundValues": { + "B1x2RV3eZ": "world!" + }, + "id": "ryvyP3I9-", + "position": { + "x": 680, + "y": 816 + }, + "type": "xod/core/constant-string" + } + }, + "path": "@/26-lcd" + }, + "@/27-lcd-data": { + "comments": { + "BJEtTI1Wz": { + "content": "Note: the number data type is compatible with the string data type. Sensor values can be transmitted without additional conversions. They will be transmitted with an accuracy of two decimal places.", + "id": "BJEtTI1Wz", + "position": { + "x": 748, + "y": 1122 + }, + "size": { + "height": 153, + "width": 340 + } + }, + "BJj-p8kbf": { + "content": "Displaying static data on the screen is boring. We have more important things to see!\n\nLet’s display something useful. For this example, let’s show the time from the moment Arduino was started in seconds and the reading from the thermometer we learned about earlier.\n\n## Instructions\n\n1. Add a thermometer to your [circuit as shown](https://xod.io/docs/tutorial/27-lcd-data/?utm_source=ide&utm_medium=ide_comment&utm_campaign=tutorial_27).\n2. Upload the sketch to the Arduino.\n\nDone! Now you have a portable thermometer.\n\nTry to connect other sensors to reinforce the skills.", + "id": "BJj-p8kbf", + "position": { + "x": 102, + "y": 918 + }, + "size": { + "height": 357, + "width": 306 + } + } + }, + "links": { + "rJKQHvJZG": { + "id": "rJKQHvJZG", + "input": { + "nodeId": "BJFi3y8qZ", + "pinKey": "H1bLN9F-b" + }, + "output": { + "nodeId": "BJV7SDJbG", + "pinKey": "rkFgMITM-" + } + }, + "ryZ1akI9Z": { + "id": "ryZ1akI9Z", + "input": { + "nodeId": "BJFi3y8qZ", + "pinKey": "B1TSE9tZ-" + }, + "output": { + "nodeId": "HkvT31LqZ", + "pinKey": "BkEVI0uHwJb" + } + } + }, + "nodes": { + "BJFi3y8qZ": { + "boundValues": { + "BJJqaX4Gb": 5, + "HJysTXVMb": 3, + "S1nqa7NMZ": 4, + "SkBK6Q4fb": 11, + "rJlYT7EfW": 12, + "rkbjp7Ezb": 2 + }, + "id": "BJFi3y8qZ", + "position": { + "x": 442, + "y": 1122 + }, + "type": "xod/common-hardware/text-lcd-16x2" + }, + "BJV7SDJbG": { + "boundValues": { + "H1s0WI6MZ": 1 + }, + "id": "BJV7SDJbG", + "position": { + "x": 680, + "y": 1020 + }, + "type": "xod/common-hardware/thermometer-tmp36" + }, + "HkvT31LqZ": { + "id": "HkvT31LqZ", + "position": { + "x": 646, + "y": 1020 + }, + "type": "xod/core/system-time" + } + }, + "path": "@/27-lcd-data" + }, + "@/28-string-concat": { + "comments": { + "HyXbS17bf": { + "content": "Concat allows you to merge two strings into one. The new string will have the input to the `HEAD` pin placed in the beginning and the input to the `TAIL` pin placed at the end.", + "id": "HyXbS17bf", + "position": { + "x": 646, + "y": 714 + }, + "size": { + "height": 153, + "width": 204 + } + }, + "S1jgrJ7Wz": { + "content": "We have already learned how to display readings from our sensors. But if we have more than one sensor, we'll need a way to tell the readings apart.\n\nThe `concat` node will help us solve this problem.\n\n## Instructions\n\n1. Assemble the [circuit with a thermometer, photoresistor, and LCD](https://xod.io/docs/tutorial/28-string-concat/?utm_source=ide&utm_medium=ide_comment&utm_campaign=tutorial_28).\n2. Find the `constant-string` node in the library. Add one above each `concat`.\n3. Assign string values to each node using the Inspector. Try something like “Temp: ” and “Light: ” to keep things short. Put a space at the end so that when we combine it with another string they won't bunch up.\n4. Link the `VAL` pins of the `constant-string` nodes to the `HEAD` pins of the `concat` nodes. `HEAD` is the beginning of the line, it’s the first part.\n5. Link the outputs of the thermometer and photoresistor to the `TAIL` pins on the `concat` nodes. They will be the second part of the generated string.\n6. Upload the patch to the Arduino.\n\nNow, the readings from your sensors are displayed on the screen with a convenient label!\n\n## Try it yourself\n\nTry to display the measurement label before the reading and the units of measurement afterward. Use two more `concat` nodes for this. But remember, you only have 16 characters per line.\n\nTry to convert degrees Celsius to degrees Fahrenheit and label them accordingly. You will find a clue for this task in patch 14.\n", + "id": "S1jgrJ7Wz", + "position": { + "x": -102, + "y": 510 + }, + "size": { + "height": 867, + "width": 306 + } + } + }, + "links": { + "HJh3pyLcW": { + "id": "HJh3pyLcW", + "input": { + "nodeId": "SJHcpyUqb", + "pinKey": "B1TSE9tZ-" + }, + "output": { + "nodeId": "B1V26JIc-", + "pinKey": "rksccsp-W" + } + }, + "HyDkOuLcW": { + "id": "HyDkOuLcW", + "input": { + "nodeId": "SJHcpyUqb", + "pinKey": "H1bLN9F-b" + }, + "output": { + "nodeId": "S1ykdOLqb", + "pinKey": "rksccsp-W" + } + } + }, + "nodes": { + "B1V26JIc-": { + "id": "B1V26JIc-", + "position": { + "x": 340, + "y": 714 + }, + "type": "xod/core/concat" + }, + "By5oDu8qW": { + "boundValues": { + "BJuORNheZ": 0 + }, + "id": "By5oDu8qW", + "label": "Photoresistor", + "position": { + "x": 578, + "y": 612 + }, + "type": "xod/core/analog-input" + }, + "S1ykdOLqb": { + "id": "S1ykdOLqb", + "position": { + "x": 544, + "y": 714 + }, + "type": "xod/core/concat" + }, + "SJHcpyUqb": { + "id": "SJHcpyUqb", + "position": { + "x": 238, + "y": 816 + }, + "type": "xod/common-hardware/text-lcd-16x2" + }, + "r19sT1I9-": { + "boundValues": { + "H1s0WI6MZ": 1 + }, + "id": "r19sT1I9-", + "position": { + "x": 374, + "y": 612 + }, + "type": "xod/common-hardware/thermometer-tmp36" + } + }, + "path": "@/28-string-concat" + }, + "@/99-the-end": { + "comments": { + "Bk_ETwPHM": { + "content": "**Congratulations!**\n\nYou have completed the tutorial. Hope you enjoyed it. May we ask to rate it?\n\n[★★★★★ Excellent!](https://forum.xod.io/t/tutorial-feedback/457/?utm_source=ide&utm_medium=ide_comment&utm_campaign=tutorial_99&utm_term=5_stars)\n[★★★★ Very Good](https://forum.xod.io/t/tutorial-feedback/457/?utm_source=ide&utm_medium=ide_comment&utm_campaign=tutorial_99&utm_term=4_stars)\n[★★★ Okay](https://forum.xod.io/t/tutorial-feedback/457/?utm_source=ide&utm_medium=ide_comment&utm_campaign=tutorial_99&utm_term=3_stars)\n[★★ So-so](https://forum.xod.io/t/tutorial-feedback/457/?utm_source=ide&utm_medium=ide_comment&utm_campaign=tutorial_99&utm_term=2_stars)\n[★ Poor](https://forum.xod.io/t/tutorial-feedback/457/?utm_source=ide&utm_medium=ide_comment&utm_campaign=tutorial_99&utm_term=1_star)\n\nIf you would like to give more detailed feedback, we’ll happy to hear it on the rating page.", + "id": "Bk_ETwPHM", + "position": { + "x": 34, + "y": 0 + }, + "size": { + "height": 255, + "width": 442 + } + } + }, + "links": { + "r1nlTPvrG": { + "id": "r1nlTPvrG", + "input": { + "nodeId": "SJpThvwSf", + "pinKey": "H1v-80uHDyZ" + }, + "output": { + "nodeId": "Sy6n2DvHM", + "pinKey": "BkdbLAuSPyZ" + } + }, + "ryde6PwSG": { + "id": "ryde6PwSG", + "input": { + "nodeId": "H1D22DDBf", + "pinKey": "H1v-80uHDyZ" + }, + "output": { + "nodeId": "rJhi2DPBG", + "pinKey": "BkdbLAuSPyZ" + } + } + }, + "nodes": { + "B1AahPDBG": { + "id": "B1AahPDBG", + "position": { + "x": 170, + "y": 510 + }, + "type": "xod/core/ceil" + }, + "BJq6nPwHz": { + "id": "BJq6nPwHz", + "position": { + "x": 272, + "y": 510 + }, + "type": "xod/core/ceil" + }, + "ByP63PPHf": { + "id": "ByP63PPHf", + "position": { + "x": 204, + "y": 510 + }, + "type": "xod/core/ceil" + }, + "H1D22DDBf": { + "id": "H1D22DDBf", + "position": { + "x": 136, + "y": 510 + }, + "type": "xod/core/ceil" + }, + "HySThPDBM": { + "id": "HySThPDBM", + "position": { + "x": 238, + "y": 510 + }, + "type": "xod/core/ceil" + }, + "SJpThvwSf": { + "id": "SJpThvwSf", + "position": { + "x": 306, + "y": 510 + }, + "type": "xod/core/ceil" + }, + "Sy6n2DvHM": { + "id": "Sy6n2DvHM", + "position": { + "x": 374, + "y": 408 + }, + "type": "xod/core/ceil" + }, + "r1AmpwvHf": { + "id": "r1AmpwvHf", + "position": { + "x": 272, + "y": 306 + }, + "type": "xod/patch-nodes/output-number" + }, + "rJhi2DPBG": { + "id": "rJhi2DPBG", + "position": { + "x": 68, + "y": 408 + }, + "type": "xod/core/ceil" + }, + "rJnXTwvSM": { + "id": "rJnXTwvSM", + "position": { + "x": 170, + "y": 306 + }, + "type": "xod/patch-nodes/output-number" + } + }, + "path": "@/99-the-end" + } + }, + "version": "0.14.0" +}