Skip to content

Commit

Permalink
fixup! feat(docs): add guide "Using Arduino-libraries in XOD"
Browse files Browse the repository at this point in the history
  • Loading branch information
brusherru committed Sep 11, 2018
1 parent 6e1d99a commit d38125d
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 271 deletions.
166 changes: 81 additions & 85 deletions docs/guide/wrapping-arduino-libraries/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
title: Wrapping Arduino Libraries
title: Wrapping Class-based Arduino Libraries
version: 1.0.0
---

# Wrapping Arduino Libraries
# Wrapping Class-based Arduino Libraries
Many libraries already have been written for Arduino and they can be included
into XOD to use the full power of the existing ecosystem.

Expand All @@ -16,7 +16,7 @@ Imagine that we want our Arduino board to turn the built-in led on or off when
we bring an NFC tag, such as travel pass, to it.

Consider that we have no XOD library to work with RFID/NFC scanner PN532
([xod-dev/pn532-nfc](/libs/xod-dev/pn532-nfc)), and try to make it on your own.
([xod-dev/pn532-nfc](/libs/xod-dev/pn532-nfc)), and try to make it on our own.
In order not to go into the details of working with tags, we will take the
the [Adafruit-PN532](https://github.com/adafruit/Adafruit-PN532) Arduino library
and just wrap it in XOD nodes.
Expand All @@ -29,7 +29,7 @@ this node, one for each hardware module.
download it, saving users from manual install.
3. Create action nodes for the new type that correspond to the functions
and methods provided by the third-party C++ library.
4. Create a quick-start node that reads an NFC tag to solve most common purposes.
4. Create a quick-start node that reads an NFC tag to serve most common purposes.
5. Create few example patches.

## Create device and require library
Expand Down Expand Up @@ -77,7 +77,7 @@ master branches of GitHub repositories. If you want to require library from
another branch or edited — make a fork and require it.
</div>

This is a special line tells XOD, that this node need a third-party library.
This is a special line tells XOD, that this node needs a third-party library.
At the first compilation XOD will check its presence in your workspace and if it
does not find a library, it will offer to download and install it just by
clicking on the button in the IDE.
Expand Down Expand Up @@ -113,9 +113,9 @@ using Type = Adafruit_PN532*;
{{ GENERATED_CODE }}

void evaluate(Context ctx) {
// It should be evaluated only once on the first (setting up) transaction
// to avoid memory leaks
if (!isSettingUp()) return;
// It should be evaluated only once on the first (setup) transaction
if (!isSettingUp())
return;

auto state = getState(ctx);
auto irq = getValue<input_IRQ>(ctx);
Expand All @@ -128,36 +128,36 @@ void evaluate(Context ctx) {
}
```
We included the library and created an instance of the class. It remains to
initialize the NFC scanner and start working with it. To do this, we need to
create action nodes.
We included the library and created the instance of the class.
Let's create action nodes to initialize the NFC scanner and start working with it.
## Action nodes
To work with the instance of a class in XOD we need to wrap method calls in
nodes, and pass the class inside as a just created custom type. Thus, any action
node for this library will contain input of type `pn532-device`.
Actually, we deal not with just a custom type, but we deal with the class, that
do some side-effects (communicating with hardware module) and this actions could
be an asynchronous (it means our program won't halt and wait until some
side-effect is done). Therefore, for each such action, we have to add some
`pulse` terminals: one to run action and two to notify about a completion
(successful or failed).
For our task we have to create two action nodes:
1. `init` — to initialize hardware module
nodes. To access the instance itself we pass it the custom type value we defined
earlier. Thus, any action node for this library will contain an input of type `pn532-device`.
The methods can invoke some side-effects (communication with the hardware module)
and be asynchronous, that is our program won't halt and wait until some
side-effect are done. Therefore, for each such action node, we have to add some
`pulse` terminals: one to run the action and two to notify about completion,
either successful or failed.
For our task we create two action nodes:
1. `init` — to initialize the hardware module
2. `pair-tag` — to detect an NFC tag and read its UID
<div class="ui segment note">
<span class="ui ribbon label">Note</span>
Pay attention, that names of action nodes begin with verbs. By convention,
names of any nodes which do instructions "what to do" in imperative style
must begin with a verb (`init`, `read-byte`, `pair-tag`, `write-line` and etc).
</div>
### Create node `init`
Let's start by creating a node that initializes the NFC scanner.
Let's create a node that initializes the NFC scanner.
![Create init patch](./init.patch.png)
Expand All @@ -174,7 +174,7 @@ void evaluate(Context ctx) {
if (!isInputDirty<input_INIT>(ctx))
return;
// Get our custom type, that is a pointer on the `Adafruit_PN532` class instance
// Get a pointer to the `Adafruit_PN532` class instance
auto nfc = getValue<input_DEV>(ctx);
// Initialize RFID/NFC module
Expand All @@ -188,8 +188,8 @@ void evaluate(Context ctx) {
return;
}
// Set the max number of retry attempts to read from a card
// This prevents us from waiting forever for a card, which is
// Set the max number of retry attempts to read from a tag
// This prevents us from waiting forever for a tag, which is
// the default behavior of the PN532.
nfc->setPassiveActivationRetries(1);
Expand All @@ -201,28 +201,27 @@ void evaluate(Context ctx) {
}
```

Now we can initialize RFID/NFC module.
On the next step we'll prepare everything for reading an UID and compare it with
the UID of our NFC tag. And then create a node that will read an UID.
Now we can initialize the RFID/NFC module.

<div class="ui segment note">
<span class="ui ribbon label">Note</span>

Do not forget to give [descriptions](/docs/guide/documenting-nodes/) to
terminals and patches.

</div>

### Storing and comparing UIDs
The UID is a set of bytes, which may be sized from 4 to 10 bytes for different
types of cards and different types of UID (Single size UID, Double size UID,
RUID, NUID, FNUID) by the specification ISO14443A.
For reliability, let's pack this value into another custom data type that we
have to create now.
For reliability, let's pack this value into another custom data type.

Create a new patch `nfc-uid`:
![Create nfc-uid type](./nfc-uid.patch.png)

Note that here we have 7 inputs of type `byte` so that we can manually set the
UID of an NFC tag. This will be useful when we'll compare the UID of the
UID of an NFC tag. This will be useful when we'll compare a UID of the
detected tag and the UID of our activation tag.

Let's write C++ code:
Expand All @@ -231,7 +230,7 @@ Let's write C++ code:
struct State {
};

// Declare out custom type as a struct,
// Declare custom type as a struct
// in which we will store an array of bytes
struct Type {
uint8_t items[7];
Expand All @@ -255,23 +254,28 @@ void evaluate(Context ctx) {
emitValue<output_OUT>(ctx, uid);
}
```
In the likeness, we can make another node, which unfolds the UID into a set of
<div class="ui segment note">
<span class="ui ribbon label">Note</span>
Likewise, we can make another node, which unfolds the UID into a set of
bytes with seven outputs from the bottom (`unpack-nfc-uid` is a good name for it).
This can be useful if we want to compare, for example, specific bytes after
reading the UID. For example, Double size UID contains the UID manufacturer at
This can be useful if we want to compare specific bytes after
reading the UID. For example, Double size UID contains a manufacturer ID at
the zero position.
For our task is important to compare the UID of detected tag with the UID of our
tag (we don't want anyone to turn our led on and off, right?)
</div>
For our task it is important to compare a UID of the detected tag with the UID
of our tag: we don't want anyone to turn our led on and off, right?
Let's create a new node `equal(nfc-uid)`. Note that we defined our data type
`nfc-uid` in the parentheses. It will be a
[specialization](/docs/guide/creating-generics/#specialization-patches) node for
comparing UIDs. In that way, any User can use familiar to him `xod/core/equal`
node even to compare UIDs.
`nfc-uid` in the parentheses. It is a
[specialization](/docs/guide/creating-generics/#specialization-patches) node, so
any xoder can use the familiar `xod/core/equal` node even to compare UIDs.
![Equal node for nfc-uid type](./equal-nfc-uid.patch.png)
To compare two byte array we'll use the built-in function `memcmp`:
To compare two byte arrays we use the standard function `memcmp`:
```cpp
struct State {
Expand All @@ -291,24 +295,17 @@ void evaluate(Context ctx) {
}
```

Let's check that everything is working and at the same time create a
demonstration patch: `example-uid-equals`.

<div class="ui segment note">
<span class="ui ribbon label">Note</span>
Creating example patches within the library is a good practice. It allows you
to immediately check the workability of the library, check its usability and
in the future will be a great example of how to use the library.
</div>
Let's check that everything works. Create a demonstration patch
`example-uid-equals`.

![Example for equal node](./example-uid-eq.patch.png)

Now we can create, store and compare UIDs. It's time to read the UID from our
Now we can create, store, and compare UIDs. It's time to read the UID from our
NFC tag.

### Create a node `pair-tag`
### Create node `pair-tag`

To interact with NFC tags we have to detect it first and read its UID. That is
To interact with an NFC tag we should detect it first and read its UID. That is
why the node is not called `read-uid`, but `pair-tag`.

![Pair-tag node](./pair-tag.patch.png)
Expand All @@ -332,8 +329,8 @@ void evaluate(Context ctx) {
// Create a variable to store length of the UID
uint8_t uidLength;

// Replace UID with zeros
memset(uid.items, 0, sizeof(uid));
// Fill UID with zeroes
memset(uid.items, 0, sizeof(uid.items));
// Detect the tag and read the UID
bool res = nfc->readPassiveTargetID(PN532_MIFARE_ISO14443A, uid.items, &uidLength);

Expand All @@ -348,50 +345,48 @@ void evaluate(Context ctx) {
Everything is ready to read the UID of the tag. But in fact, the library from
Adafruit allows us to read not only the UID of the tag but also to read and
write data to the tag. And if you're wrapping on a library, we recommend
wrapping around at least the most common methods to avoid having multiple
wrappers over the same library that only have a limited set of features.
write data to the tag. And if you're wrapping a library, we recommend
wrapping at least the most common methods to avoid having multiple
wrappers of the same library that only have a limited set of features.
Try to make `read-page` and `write-page` nodes yourself using the
Try to make `read-page` and `write-page` nodes by yourself using the
`mifareultralight_ReadPage` and `mifareultralight_WritePage` methods from the
Adafruit library. And make an example patch on which count the number of times
the tag is touched the NFC scanner, keeping this counter on the tag (you will
need the MIFARE Ultralight labels to work with these methods).
Adafruit library. And make an example patch to count the number of times
the tag has touched the NFC scanner, keeping this counter on the tag. You will
need the MIFARE Ultralight labels to work with these methods.
## Quick start node
After you have done the previous steps, it may seem that this step is optional
because you already know how the NFC scanner works, you know how to get the
desired data to make your idea work.
But let's imagine that this library was done by someone else, and you just want
We could stop right here. But let's make yet another thing: a quick start node.
Imagine, this library was done by someone else and you just want
to use it. Will it be handy to use a chain of nodes `pn532-device`, `init`,
`read-uid`, just to know is there any NFC tag near the module?
Let's simplify the life of both ourselves and the library's consumers, and write
a simple `nfc-scanner` that solves most simple tasks with just one node.
Such nodes are called quick start nodes.
Let's simplify our lives and write an `nfc-scanner` node that solves most common
tasks with just one node. Such nodes are called *quick start nodes*.
![Quick start node "nfc-scanner"](./nfc-scanner.patch.png)
*Footnotes:*
*[1] `flip-flop` is used to trigger `INIT` only once*
*[2] open the `gate` to trigger `READ` on every next trigger of the `SCAN`*
1. `flip-flop` is used to trigger `INIT` only once;
2. Open the `gate` to trigger `READ` on each subsequent pulse on the `SCAN`.
It's time to do our `example-nfc-scanner`:
It's time to make our `example-nfc-scanner`:
![nfc-scanner example](./example-nfc-scanner.patch.png)
Well done!
<div class="ui segment note">
<span class="ui ribbon label">Note</span>
Now you can [add project description and share it](/docs/guide/creating-libraries/#setting-metadata)
with the community.
Creating example patches within the library is a good practice. It allows you
to immediately check the workability and usability of the library.
In the future users will thank you for examples.
</div>
Well done!
## Summary
1. To use a third-party library just specify a special pragma-instruction
1. To use a third-party library specify a special pragma-instruction
in the code:
`#pragma XOD require "https://github.com/some/library"`
2. In addition to specifying where to get the library, do not forget to include
Expand All @@ -404,6 +399,7 @@ it in the code:
```
3. When you wrapping methods in nodes use verbs in their names
(`pair-tag`, `write-page`).
5. Create simple patch nodes (quick start nodes) to quickly solve common
problems (`nfc-scanner`, `nfc-writer`)
6. [Share](/docs/guide/creating-libraries/) your wrappers over C++ libraries.
4. Create quick start nodes to solve common problems (`nfc-scanner`, `nfc-writer`)

Now you can [add project description and share it](/docs/guide/creating-libraries/)
with the community.
Loading

0 comments on commit d38125d

Please sign in to comment.