Table of contents
The Universal Serial Bus (USB) port is found on most Arduino devices and also on the RFzero. In many ways it is used just like the Serial interface you may know from e.g. the Arduino Uno. But on the Arduino Zero and Arduino M0, Arduino Due etc. boards it is called SerialUSB instead.
Initializing the USB port
Initializing the USB port on the RFzero etc. is just like the classic Serial interface.
1 2 |
// Initialize the USB SerialUSB.begin(9600); // Set the SerialUSB port to 9600 Baud and 8N1 (default) |
However, there is one drawback of the USB embedded port on the RFzero, Arduino Zero, Arduino M0 etc. boards and that is that the embedded USB port takes up to two seconds to get started. So if you want to catch the data sent by the USB port you will have to halt the program for up to two seconds.
1 2 3 |
// Initialize the USB SerialUSB.begin(9600); // Set the SerialUSB port to 9600 Baud and 8N1 (default) wait(2000); // Wait for 2 s before continuing |
Another way is to wait for as long as it takes for the USB port to get ready.
1 2 3 |
// Initialize the USB SerialUSB.begin(9600); // Set the SerialUSB port to 9600 Baud and 8N1 (default) while (!SerialUSB); // Wait until USB is ready before continuing |
The drawback of waiting for the USB port to get ready is that the USB port HAS TO BE CONNECTED to a host. Otherwise it will wait forever.
The advice is to use the while (!SerialUSB) during development and testing but use the delay(2000) when the program is deployed for normal use.
Communicating via the USB port
Below is a simple “Hello World” example .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// RFzero include and object creation #include <RFzero.h> // MUST ALWAYS be included for RFzero void setup() { // Initialize the USB SerialUSB.begin(9600); // Set the SerialUSB port to 9600 Baud and 8N1 (default) while (!SerialUSB); // Wait for the USB port to be ready SerialUSB.println("Hello World - here is RFzero"); } void loop() { } |
Here is an example that used both the Serial5 and USB interfaces by communicating between the them.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// RFzero include and object creation #include <RFzero.h> // MUST ALWAYS be included for RFzero void setup() { // Initialize USB SerialUSB.begin(9600); // Set the serial USB port to 9600 Baud and 8N1 (default) while (!SerialUSB); // Wait for the USB port to be ready } void loop() { if (SerialUSB.available()) // Is there any USB data available? { char ch = (char) SerialUSB.read(); // Read the USB data SerialUSB.print(ch); // Echo the data to the USB port } } |
The above examples are rather simple but the data reception is included in the loop() function. But what if the program has to do something else? How about using an Interrupt Service Routine (ISR)? Well, ISR, USB and Arduino are really cumbersome. Fortunately, a standard Arduino function called yield() comes to rescue. In brief the yield() function is always called by the underlying Arduino environment during functions like delay() and delayMicroseconds(). So if you include it in your own slow loops and functions you will be home safe.
But what if you don’t want to process the received data before a terminating character is received, e.g. an exclamation mark (!). The below code is an example of how to do this using a buffer to store the USB data temporarily and also using the yield() function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
// RFzero include and object creation #include <RFzero.h> // MUST ALWAYS be included for RFzero // USB receiver woven into standard Arduino function. Always include in loops and slow functions void yield() { static char usbBuffer[100] = "\0"; // Initialize the buffer static int usbBufferCount = 0; // Empty buffer to first character is the 0 terminator bool resetBuffer = false; while (SerialUSB.available()) // Is there any SerialUSB data available? { char ch = (char) SerialUSB.read(); // Read the SerialUSB data // Check if buffer can overflow if (usbBufferCount < sizeof(usbBuffer) - 1) { // No overflow usbBuffer[usbBufferCount] = ch; // Add the new character to the end of the buffer usbBufferCount++; usbBuffer[usbBufferCount] = 0; // Set 0 terminating character } else resetBuffer = true; // Buffer overflow to reset buffer if (ch == '!') // Check if the character is a line feed { Serial5.print(usbBuffer); // Copy the data to Serial5 resetBuffer = true; // Buffer overflow to reset buffer } if (resetBuffer) { usbBuffer[0] = 0; usbBufferCount = 0; } } } void setup() { // Initialize USB SerialUSB.begin(9600); // Set the serial USB port to 9600 Baud and 8N1 (default) while (!SerialUSB); // Wait for the USB port to be ready // Initialize Serial5 Serial5.begin(9600); // Set the Serial5 port to 9600 Baud and 8N1 (default) } void loop() { if (Serial5.available()) // Is there any Serial5 data available? { char ch = (char) Serial5.read(); // Read the Serial5 data SerialUSB.print(ch); // Copy the data to Serial5 } yield(); // Include in loops and slow functions } |
The above example uses a buffer 100 bytes in size. If a larger amount of data is expected a ring buffer, with start and stop positions, that is large enough to hold the received data should be used instead.