This is a continuation of Setting up a WiFly RN-XV board so certain details are omitted.
In our last adventure we get a WiFly RN XV hooked up to a Teensy 3.1.
The code for that managed to auto-connect to a local wireless access point and grab an IP address using DHCP.
With that in place you could connect to the WiFly over your local network from, say, your laptop.
A next logical step would be to have the WiFly reach out to you.
The WiFly offers not just a full TCP/IP stack but assorted goodies for doing HTTP as well. You can have the device sending and receiving Web requests to interact with other devices if you like.
For this example, though, OSC over UPD will be used.
UDP is particularly useful when speed is more important than reliability. For example, a steady stream of rapidly changing location data. Or music commands. It’s not that reliability isn’t a worthwhile goal, it’s that it comes with a cost.
Network communications (e.g. E-amil, Web browsing) is typically done using TCP, which provides a means for checking if all the data arrived, and arrived intact. Often it’s fast enough, but the accuracy checks can introduce delays measurable in seconds. If you are sending E-mail then a few extra seconds is meaningless. If you are instructing a music program to trigger a note or jump to section of a composition then this overhead is unacceptable.
UDP also has the advantage of being computationally simpler than TCP. There’s less work required to create and process UDP, and it uses less bandwidth.
The code from the previous example needs a few changes to handle UDP. The initial set of commands can stay the same, but with a few additions.
First, the WiFly needs to be told that it will be using both TCP and UDP.
"set ip proto 3",
The value for this command is a bit mask. We get a value of 3 by way of 1|2
(that is, the bitwise OR
value of binary 00001 and 00010). This actually sets the WiFly to use both UDP (00001) and TCP (00010).
Next, the WiFly needs to know the remote IP address and port for the UDP messages.
You need to add two new String variables, remoteHost
and remotePort
, to the “settings.h” file so you can add these commands:
"set ip host " + remoteHost, // The remote host address
"set ip remote_port " + remotePort, // the remote host port
You can also set the port on which the WiFly will listen for incoming UDP messages, but this example doesn’t do that.
That takes care of configuration. Now the code needs a function to send an OSC message. But first, a trip to the library.
The previous code avoided using any libraries to help make clear what was going on. But it was hacky; for example, code for sending commands to the WiFly used a fixed delay to allow the command to be acknowledged rather than checking for a proper response.
One advantage of figuring out just what steps are needed is that it gave some insight in appreciating a decent library, knowing what it does, and knowing what might need changing in order to work with the Teensy and version 4.00 of the WiFly firmware.
This new example code makes use of two additional files, “RNVX.h” and “RNVX.cpp”. They are copied from the WiFly Robot project, though the original source is the Sparkfun WiFly Shield repo. You can find these (somewhat modified) files here.
That latter project includes many more files and feature. The two files here just handle some basic operations, such as entering and exiting command mode, sending data and commands to the WiFly, and reading data back from the device. It doesn’t change the code by very much but it makes command management much nicer, and faster (because there’s no need for fixed delays).
Here’s the code for the sketch:
The main item of interest is the method sendRawOsc()
, the contents pretty much straight-up stolen from Arduino + WiFly sends OSC.
An OSC message follows a simple format. You start with the address pattern. This is the thing that looks like a file path or Web page path (minus any domain name). This example is using /oscillator/4/frequency
.
Next comes the “type tag”. This example has one argument, a floating point number, so the type tag is simply ,f
. Note the leading comma. If there are no arguments the type tag is just the comma.
Finally, you put the argument values.
There are assorted rules about the format and number of bytes for the various OSC data types. For example, an OSC-string is a “sequence of non-null ASCII characters followed by a null, followed by 0-3 additional null characters to make the total number of bits a multiple of 32.”
An OSC “float32” is a 32-bit big-endian IEEE 754 floating point number, so four bytes are needed.
The OSC message /oscillator/4/frequency f 0.02
will fit nicely into 32 bytes, so sendRawOsc()
allocates a char array of 32 items, and it’s set to the OSC message with null bytes where needed to fill it out.
The last four bytes are then replaced with the values needed for the floating point value.
union u_tag
manages the extraction of those byte values. A union is a way to have single memory location represent the data in multiple ways. So, u.fval
is set to the float value; the individual bytes of that float are now available in the byte
array u.b[]
.
Once sendRawOsc()
has assembled that char array for the OSC message it just needs to push it off to the WiFly. Some methods were added to the original RNXV.cpp
code to help with a few things. Here, the new method sendChars()
is used.
This just iterates over the char array and prints each one to the WiFly UART (i.e. serial port), finally calling flush
. The size of the array is required so the code knows the iteration value.
You can see then that sending something over UDP is remarkably simple. Indeed, if you did not care about any message formatting you could pretty much just push arbitrary data over UDP. For example, if the receiver of the UDP message already assumed that any value it got was, say, a temperature reading then the Teensy/WiFly device could just write a series of floats or ints or whatever.
(Technically there is a format to UDP messages but this is handled by WiFly by virtue of using UDP.)
The example just repeats over loop()
calling sendRawOsc()
with a fixed delay. There’s some additional code there to track the time between calls. The reason is that while testing this code a simple Ruby script implementing an OSC server was used. There appeared to be a discrepancy between what the Arduino serial monitor was reporting and what the OSC server saw. Specifically, the server was occasionally missing an OSC message.
With time-stamps on both ends it appears that sendRawOsc()
is reliably called on schedule, so the OSC server is failing to receive or correctly arse the OSC message, or the WiFly is failing to send it despite sendRawOsc()
being called, or the message is getting lost in transit (certainly a possibility with UDP).
A next step might be to fire up Wireshark to see what is actually sent (or not).
So far, so good. The next step is set up a listening port on the WiFly and arrange for some interactive behavior.
If you have any questions or comments please leave them below.
PRRC (makers of Teensy boards) discussion on OSC
Stackoverflow – Difference between TCP and UDP?
Understanding the Android ‘Radio State Machine’ for better battery life
3G, 4G and Beyond: Bringing Networks, Devices and the Web Together
How power-hungry are various permutations of Ethernet on modern MacBook Pros?
Stackoverflow – When is it appropriate to use UDP instead of TCP?