Interoperability Data-IoT: How to send and receive data and take control of your Arduino, from R
By: Saúl Buentello
Tags: Programming, Data Analysis, Data Science, Arduino, IoT

Interoperability Data-IoT: How to send and receive data and take control of your Arduino, from R

Photo: Receiving data from Arduino, in RStudio

What else could we talk about in common when we talk about R and Arduino, besides that they both share the letter R? Let’s start at the beginning, in case you are not familiar, Arduino is an open-source electronics platform based on easy to use hardware (Arduino board) and software (Arduino IDE). You can tell this popular card what to do if you have the correct type of data and a set of instructions to process the data and perform subsequent operations. The Arduino microcontroller is responsible for keeping all of your compiled code and executing the commands you specify. Arduino software, on the other hand, is the board’s IDE where the set of instructions governing the board is written. If this is your first time working with Arduino, I suggest you review this Getting Started to understand the minimum to continue with the examples in this article.

Before starting

Now, if you know R, you will know that an elegant language, well adapted to data analysis and data science. All that said, convergence can be achieved between the two: Data and IoT. This example is very simple, the objective of this article is to explore the interoperability between R and Arduino establishing a data flow between the two and having instructions in the microcontroller of the board, based on the received data. In this context, R will handle the data and everything associated with it, and send it to the Arduino. Arduino, on the other hand, will trigger peripherals based on the data it has received. How is this possible? Using the Arduino’s ability to be programmed directly through a serial port.

Before we begin, here is an overview of what will happen to demonstrate interoperability between R and Arduino:

First, you will send a series of data defining the brightness of 3 LEDs (in a range of 0% — 100%) from RStudio to the Arduino serial port.

An Arduino script will wait until serial data is available, extract the brightness values ​​for the 3 LEDs, assign them to analog values ​​(0–255), instruct the onboard microcontroller to write these values ​​to the LEDs, and then will send the mapped values ​​(0–255) to the Rstudio-Arduino serial interface.

Rstudio will read the values ​​sent from Arduino to the serial port. Then, you will use these values ​​(in a range from 0 to 255) to create a data set to turn a servo motor and pass these values ​​to the serial interface.

Once the Arduino detects that there is serial data again, it will read each value (0–255) on the serial interface, assign it to an angle rotation value (0 ° to 180 °), rotate the servo, and send the angle back to the serial interface.

Finally, you will read the angles of the motor and you will finish visualizing it with “ggplot” in Rstudio.

When all is done, this is what you want to achieve using R and Arduino, working together:

Video showing interoperability between Arduino and R

To move on, you will need the Arduino IDE and an Arduino (for this example I use Arduino UNO), a red LED, a green LED, a blue LED, a servo motor (SG90), and jumpers. The hardware components will be connected as I show you below:

Schematic interoperability project Arduino & R

For the RStudio part, we will need the “Tidyverse”, “Magrittr”, “Plotly” and “Serial” packages. Tidyverse is a collection of R packages designed for data science tasks like data visualization and handle. Serial allows to read and write binary and ASCII data in RS232 / RS422 / RS485 or any other virtual serial interface of the computer. Plotly will allow you to create interactive web graphics from “ggplot2” graphics. You can install them as follows:

# PACKAGE INSTALL install.packages(c("tidyverse", "serial", "plotly", "magrittr"))

Working with the serial interface

Starting a new script in R, you will load the previously mentioned libraries. To get a list of the serial interfaces available on your computer, you can simply use the listPorts() function.

# REQUIRED LIBRARIES library(tidyverse)

library(serial)

library(magrittr)

library(plotly) listPorts()

You will get an output like the following in the console. Most likely if you are working on Windows you will find more ports listed.

Output on console, ports enabled

Configuring a serial interface

Next, you will create a port object that you will call “myArduino”, which represents a serial client for communication with the USB serial port where your card is connected. But which port is where your Arduino UNO is connected? This information can be obtained by running the Arduino IDE by navigating to the Tools / Port menu to see the appropriate port.

Arduino IDE, port that Arduino UNO is connected to

In my case, the USB serial port it is connected to corresponds to “cu.usbmodem1421”. With this information, you can now create a serial interface connected to the board. This is accomplished by using the serialConnection() function to set the interface parameters.

# SERIAL CONNECTION myArduino <- serialConnection(

port = "cu.usbmodem1421",

mode = "9600,n,8,1" ,

buffering = "none",

newline = TRUE,

eof = "",

translation = "cr",

handshake = "none",

buffersize = 4096

)

Now that the interface is established, the next step is to initialize it and keep it open for later use, such as writing and reading data from it. Once the interface is initialized, the Arduino board flashes. This is because the board reboots once a serial port is opened to allow the bootloader to receive a new sketch.

# OPEN AND TESTING THE CONNECTION open(myArduino)

isOpen(myArduino)

Writing data from RStudio to the serial interface

Now you are ready to write some data to the serial interface. The values ​​that you will send to the interface will be in the range of 0 to 100, expressing the desired percentage of the brightness of the LED. Also, you will add R, G, and B characters to help the Arduino distinguish which value is written to which LED.

You must create a data set with 3 columns r, g, and b, and add a letter to its brightness values.

# MAKING RGB DATA n <- 30 arduinoInput <- tibble(

r = (sample(1:100, size = n, replace = T) %>%

paste('R', sep = '')),

g = (sample(1:100, size = n, replace = T) %>%

paste('G', sep = '')),

b = (sample(1:100, size = n, replace = T) %>%

paste('B', sep = ''))

) # A GLIMPSE OF ARDUINO INPUT glimpse(arduinoInput)

You can take an overview on the console.

Output console, overview of input

You can see that the values ​​of the LEDs that will be written to the serial interface are of character type, this is the type of data desired for serial communications.

You will use the write.serialConnection() function to write the LED values ​​to the serial port row by row.

# CLOSE THE OPEN CONNECTION AGAIN (BEST PRACTICE) close(myArduino)

open(myArduino) # GIVING TIME FOR THE BOARD TO RESET ONCE THE SERIAL INTERFACE IS INITIATED Sys.sleep(3) for (r in seq_len(n)){

Sys.sleep(0.25)

write.serialConnection(myArduino, paste(arduinoInput[r,], collapse = ''))

}

Pause! Let’s go to Arduino IDE and understand what happens

You are probably wondering, how will the LEDs light up with the character values? A script on Arduino will handle this. Upload the following sketch to your Arduino from the Arduino IDE.

// SETTING RGB LEDS



int rLed = 0;

int gLed = 0;

int bLed = 0;

int serv = 0; int redLed = 6; // RED LED ON PIN 6

int greenLed = 5; // GREEN LED ON PIN 5

int blueLed = 3; // BLUE LED ON PIN 3 void setup() {

Serial.begin(9600);



// SETTING PINS AS OUTPUT



pinMode(redLed, OUTPUT);

pinMode(greenLed, OUTPUT);

pinMode(blueLed, OUTPUT); void loop() {

if (Serial.available()){



// MAKING VARIABLE VISIBLE TO ONLY 1 FUNCTION

// CALL AND PRESERVE THEIR VALUE



static int t = 0;



char myvalue = Serial.read(); switch(myvalue){



// MYVALUE IS A VARIABLE WHOSE VALUE TO COMPARE WITH VARIOUS CASES



case '0'...'9':

t = t * 10 + myvalue - '0';

break;



case 'R':

{

rLed = map(t, 0, 100, 0, 255);

analogWrite(redLed, rLed);

Serial.println(rLed);

}

t = 0;

break;



case 'G':

{

gLed = map(t, 0, 100, 0, 255);

analogWrite(greenLed, gLed);

Serial.println(gLed);

}

t = 0;

break; case 'B':

{

bLed = map(t, 0, 100, 0, 255);

analogWrite(blueLed, bLed);

Serial.println(bLed);

}

t = 0;

break;

}

}

}

The main loop in Arduino waits until serial data is available, stores the data in the interface as a character vector (myvalue), and then execute it through a switch. In particular, the switch compares the value of a variable with the values ​​specified in each case. When a case is found whose value matches that of the variable, the code is executed.

For example, let’s say the character vector sent from RStudio is 94R44G22B. The first match case is case ‘0’… ‘9’: which is converted to an integer by subtracting the zero value character.

t, which represents the brightness of the LED in the range 0–100%, is first initialized to 0. The first value to be read will be 9. Consequently, the value of t becomes:

t = 0 * 10 + (‘9’ — ‘0’)

t = 9

The second value is a 4 and matches the first case. The new value of t becomes:

t = 9 * 10 + (‘4’ — ‘0’)

t = 94

The next value that is read is a letter ‘R’ that matches the case ‘R’. In this case, the value of t = 94 is reassigned to an analog value in the range 0–255 that can be used with analogWrite() functions.

If you wonder why it is necessary to reassign the values ​​that go to the analogWrite () function, it is because it is coming directly. If you only wanted to blink and perform LED ON and LED OFF, you would simply send a HIGH (5v) or a LOW (0v). But what if you want to generate a different voltage than 0v or 5v, to vary the brightness of the LED? Well, you can’t, unless you’re using a digital-to-analog converter (DAC) integrated circuit.

However, you can get pretty close to generating analog output values ​​using a trick called “pulse-width modulation” (PWM). The selected pins on each Arduino can use the analogWrite() function to generate PWM signals that can emulate a pure analog signal when used with certain peripherals. These pins are marked with a “~” on the card. On Arduino UNO, pins 3, 5, 6, 9, 10, and 11 are PWM pins.

The PWM output has an 8-bit value. In other words, you can write values ​​from 0 to ²⁸ — 1, or from 0 to 255. In the case of our LED circuit, mapping the output to 255 will result in full brightness, and 0 will result in the LED turns off. , with the brightness varying between these two values.

Going back to the switch, in case ‘R’, once an analog value is written to the LED, an instruction follows: Serial.println(rLed). This is the Arduino’s way of telling you to write that value to the serial port. After this is done, the value of t is set back to 0 and the following input characters are executed in subsequent cases.

All this said and done, now you can return to RStudio, to run the script you have so far and observe the behavior of the LEDs. Oh yes, magic!

Reading mapped values ​​in R

Now back to R issues, you can read the mapped values ​​sent to the serial port connection by Arduino thanks to read.serialConnection().

# READ MAPPED DATA SENT FROM MY ARDUINO dataFromArduino <- tibble(

capture.output(cat(read.serialConnection(myArduino,n=0)))

) # SELECT FIRST NINE ROWS, ASSIGN VALUES TO THEIR LEDS AND RENAME FIRST COLUMN dataFromArduino %>%

slice_head(n = 9)

Output in console, data sent from Arduino

Now you have the data reallocated at home️! Something interesting to note is that read.serialConnection() reads the entire buffer at once. In addition, the data has a long format since the reading is done per line.

Now you will need to manipulate your data contained in dataFromArduino.

# ASSIGN VALUES TO LEDS AND CHANGE COLUMN NAME dataFromArduino %<>%

tibble(ledNames = rep_along(seq_len(nrow(dataFromArduino)),

c('rMapped','gMapped','bMapped'))) %>%

rename("ledVal" = 1) %>%

group_by(ledNames) %>%



# ADD IDENTIFIERS REQUIRED BY PIVOT_WIDER FUNCTION AND CREATE NEW COLUMNS WITH 'LEDVAL' VALUES



mutate(row = row_number()) %>%

pivot_wider(names_from = ledNames, values_from = ledVal) %>%



# DROPPING 'ROW' COLUMN AND CONVERT ALL COLUMNS TO DATA TYPE INTEGER



select(-row) %>%

mutate_all(as.integer) dataFromArduino %>%

slice_head(n = 10)

You will be able to observe the mapped data on the console.

Output in console, mapped data

A data set that shows the initial values ​​of the LEDs sent from RStudio to the Arduino serial port and the mapped values ​​sent back, would communicate things much better.

# MERGE THE TWO DATA SETS, DROP NON NUMERICAL CHARACTERS (R,G,B), AND REORDER COLUMNS combinedData <- as_tibble(cbind(arduinoInput, dataFromArduino)) %>%

mutate(across(where(is.character), ~parse_number(.x)), across(where(is.double), as.integer)) %>%

select(c(1, 4, 2, 5, 3, 6)) combinedData %>%

slice_head(n = 10)

Now you will be able to see in the console as a result a single data set with the combined data.

Output in console, combined data

Last steps: The servo

So far, you’ve already sent data from RStudio to Arduino, remapped it, turned on some LEDs, and sent the remapped data to RStudio. Now you will create a new data set from the received reassigned values ​​(0–255), adding a termination character, and writing these values ​​to the Arduino serial port.

# CREATING NEW DATA SET THAT SELECTS VALUES IN ORDER: MAXIMUM OF RECEIVED LED VALUES, THEN MINIMUM, AND SO IS REPEATED rowMin <- tibble(inputMin = dataFromArduino %>% apply(1,min)) %>%



# SELECT EVEN ROWS



filter(row_number() %% 2 == 0) servoInput <- tibble(servoIn = dataFromArduino %>%

apply(1,max)) # REPLACE EVEN ROWS WITH A MINIMUM VALUE, AND APPENDING A TERMINATING CHARACTER servoInput[c(1:n)[c(F,T)],] <- rowMin servoInput %<>%

mutate(servoIn = servoIn %>%

paste('S', sep = ''))

And you will write the values ​​in the serial interface.

close(myArduino)

open(myArduino) Sys.sleep(1) for (r in seq_len(n)){

Sys.sleep(1)

write.serialConnection(myArduino, paste(servoInput[r,], collapse = ''))

}

Pause again! Let’s go to Arduino IDE and complete for the Servo

Time to complete your sketch in Arduino IDE. The main loop of the Arduino program waits until serial data is available, extracts the integer value, reassigns the value in the range 0–255 to a servo angle 0–179, and writes this value to the servo. Then the mapped angle value is printed on the serial interface. Add the bold highlighted parts to your sketch, corresponding to the Servo, and re-upload the updated sketch to your Arduino.

# include <Servo.h> // SETTING SERVO AND RGB LEDS



int rLed = 0;

int gLed = 0;

int bLed = 0;

int serv = 0; int redLed = 6; // RED LED ON PIN 6

int greenLed = 5; // GREEN LED ON PIN 5

int blueLed = 3; // BLUE LED ON PIN 3

int theServo = 9; // SERVO ON PIN 9 Servo myServo; void setup() {

Serial.begin(9600);



// SETTING PINS AS OUTPUT



pinMode(redLed, OUTPUT);

pinMode(greenLed, OUTPUT);

pinMode(blueLed, OUTPUT); // ATTACHING SERVO OBJECT



myServo.attach(theServo);

} void loop() {

if (Serial.available()){



// MAKING VARIABLE VISIBLE TO ONLY 1 FUNCTION

// CALL AND PRESERVE THEIR VALUE



static int t = 0;



char myvalue = Serial.read(); switch(myvalue){



// MYVALUE IS A a VARIABLE WHOSE VALUE TO COMPARE WITH VARIOUS CASES



case '0'...'9':

t = t * 10 + myvalue - '0';

break;



case 'R':

{

rLed = map(t, 0, 100, 0, 255);

analogWrite(redLed, rLed);

Serial.println(rLed);

}

t = 0;

break;



case 'G':

{

gLed = map(t, 0, 100, 0, 255);

analogWrite(greenLed, gLed);

Serial.println(gLed);

}

t = 0;

break; case 'B':

{

bLed = map(t, 0, 100, 0, 255);

analogWrite(blueLed, bLed);

Serial.println(bLed);

}

t = 0;

break; case 'S':

{



// MAPPING ANALOGUE LED VALUE TO ANGLE FROM 0 TO 180 DEGREES



serv = map(t, 0, 255, 0, 179);

Serial.println(serv);

delay(5);

myServo.write (serv);

delay(150);

}

t = 0;

break;

}

}

}

Done this update, now you can go back to RStudio, run the script so far and observe the behavior of the Servo. Oh yes, one more time magic!

Servo data in R

Now, going back to RStudio, you can get what the Arduino gave you and do data wrangling while you do it.

# READ MAPPED ANGLES SENT FROM MY ARDUINO, RENAME FIRST COLUMN angleFromServo <- tibble(

capture.output(cat(read.serialConnection(myArduino,n=0)))) %>%

rename("servoAnglesMapped" = 1) %>%

mutate_all(as.integer) # SELECT FIRST TEN ROWS angleFromServo %>%

slice_head(n = 10)

You will be able to observe the mapped data on the console.

Output in console, mapped servo data

You can also now observe what you have sent versus what you have received, merging both data sets.

# WHAT WE SENT VS WHAT WE RECEIVED. MERGE THE TWO DATA SETS AND DROP NON NUMERIC CHARACTER 'S' combinedAngles <- as_tibble(

cbind(servoInput, angleFromServo)) %>%

mutate(across(where(is.character), ~parse_number(.x)),

across(where(is.double), as.integer)) combinedAngles %>%

slice_head(n = 10)

Output in console, data sent from servo vs data received

The servoIn column shows the data we sent to the Arduino, while the servoAnglesMapped represents what the Arduino wrote to the servo and returned to us.

Data visualization

As if that were not enough, you can now end with a data visualization, for example showing the variation made by the servo in each instance of writing angle data.

# PLOT VARIATION OF SERVO ANGLE theme_set(theme_light()) myPlot <- angleFromServo %>%

ggplot(mapping = aes(x = 1:nrow(angleFromServo), y = servoAnglesMapped)) +

geom_line() +

geom_smooth(se = F) +

labs(x = "Count", y = "Servo angle", title = "Servo angle variation at each count instance")+

theme(plot.title = element_text(hjust = 0.5)) ggplotly(myPlot)

You will get as a result a plot like the following in the RStudio viewer.

Servo angle variation plot at each count

In this article, you demonstrated the bi-directional flow of data between RStudio and Arduino. In each instance, the data you sent to the Arduino was transformed, triggered a peripheral, and then repeated. The data you got then went through some fixes to put it in the correct format that would be executed by the Arduino in subsequent operations. It is very interesting to play with Data and IoT, right?

I thank you for your kind reading. I hope this has brought you up to speed with both the Arduino and R and sparked a genuine interest in exploring the amazing things that can be done with these two beauties.

I share the plot generated with plotly a little more aesthetic: https://rpubs.com/cosmoduende/arduino-r

And here you can find the complete code, both the part programmed in R and the Arduino sketch: https://github.com/cosmoduende/r-arduino

I thank you for making it to the end, I wish you a very happy analysis, that you can put everything into practice, and that you are surprised and have as much fun as I am with the results!