Week 11: Serial Communication

Serial Communication:

This week we learnt that serial communication is a way for devices, like an Arduino microcontroller and a computer, to exchange data. It allows the Arduino to send data to the computer or for the computer to send data to the Arduino.

One way to establish serial communication between an Arduino and a computer is to use p5.js, a JavaScript library for creating interactive graphics and visualizations. To use serial communication with p5.js, we will need to use the p5.serialport library, which is a p5.js library that provides a wrapper around the popular serialport library for Node.js.

Once we have set up the p5.serialport library and created a p5.Serial object, we can use the serial.read() function to read data from the serial port and the serial.write() function to write data to the serial port. We can also use the serial.on() function to set up event listeners for different events, like when data is received or when the serial port is opened or closed.

Exercise 1:

In class we practiced serial communication using a potentiometer example. We modified the potentiometer example demonstrated in class to create a ball that bounces horizontally across the screen at a speed that is determined by the value of the potentiometer. In our Arduino code, we used serial communication to send the value of the potentiometer to the p5.js sketch, which was then used to control the velocity of the bouncing ball:

Arduino Code:

void loop() {
// put your main code here, to run repeatedly:
 int potentiomenter = analogRead(A0); 
 int mappedPot = map(potentiomenter, 0, 1023, 0, 255); 
 Serial.write(mappedPot); 
delay(100);

n our p5.js sketch, we created a ball object using a Circle class with properties for the x and y positions and a move() method. The move() method only updates the x position of the ball, which is calculated using the value of the potentiometer that was read from the Arduino using serial communication. The y position of the ball remains constant along the horizontal axis at the height/2 position. The p5js code for this exercise:

// variable to hold an instance of the p5.webserial library:
const serial = new p5.WebSerial();
 
// HTML button object:
let portButton;
let inData;                   // for incoming serial data
let outByte = 0;              // for outgoing data
 
function setup() {
  createCanvas(400, 300);          // make the canvas
  // check to see if serial is available:
  if (!navigator.serial) {
    alert("WebSerial is not supported in this browser. Try Chrome or MS Edge.");
  }
  // if serial is available, add connect/disconnect listeners:
  navigator.serial.addEventListener("connect", portConnect);
  navigator.serial.addEventListener("disconnect", portDisconnect);
  // check for any ports that are available:
  serial.getPorts();
  // if there's no port chosen, choose one:
  serial.on("noport", makePortButton);
  // open whatever port is available:
  serial.on("portavailable", openPort);
  // handle serial errors:
  serial.on("requesterror", portError);
  // handle any incoming serial data:
  serial.on("data", serialEvent);
  serial.on("close", makePortButton);
}
 
function draw() {
  
   background(0);
   fill(255);
   text("sensor value: " + inData, 30, 50);
 
}
// if there's no port selected, 
// make a port select button appear:
function makePortButton() {
  // create and position a port chooser button:
  portButton = createButton("choose port");
  portButton.position(10, 10);
  // give the port button a mousepressed handler:
  portButton.mousePressed(choosePort);
}
 
// make the port selector window appear:
function choosePort() {
  if (portButton) portButton.show();
  serial.requestPort();
}
 
// open the selected port, and make the port 
// button invisible:
function openPort() {
  // wait for the serial.open promise to return,
  // then call the initiateSerial function
  serial.open().then(initiateSerial);
 
  // once the port opens, let the user know:
  function initiateSerial() {
    console.log("port open");
  }
  // hide the port button once a port is chosen:
  if (portButton) portButton.hide();
}
 
// pop up an alert if there's a port error:
function portError(err) {
  alert("Serial port error: " + err);
}
// read any incoming data as a string
// (assumes a newline at the end of it):
function serialEvent() {
  inData = Number(serial.read());
  console.log(inData);
}
 
// try to connect if a new serial port 
// gets added (i.e. plugged in via USB):
function portConnect() {
  console.log("port connected");
  serial.getPorts();
}
 
// if a port is disconnected:
function portDisconnect() {
  serial.close();
  console.log("port disconnected");
}
 
function closePort() {
  serial.close();
}

Exercise 2:

In our sketch, we created an interactive visual that represents a sun rising and falling in the sky and linked it to the brightness of an LED. When the up arrow key is pressed, the sun “rises” in the sky and the background color changes to a bright magenta. When the down arrow key is pressed, the sun “falls” and the background color deepens to a dark blue. We achieved this effect by using the y position of the sun, which was created using the Circle class from the previous exercise, to control the RGB values of the background color.

Arduino Code:

void setup() {
  Serial.begin(9600);
  pinMode(2, OUTPUT);
  pinMode(5, OUTPUT);
  while (Serial.available() <= 0) {
    Serial.println("0,0"); // send a starting message
    delay(200);      
  }
}
void loop() {
  while (Serial.available() > 0) {
    // read the incoming byte:
    int inByte = Serial.read();
    analogWrite(5,inByte);
    Serial.println();
  }
}

p5js Code:

let serialPort; // variable to hold an instance of the serialport library
let portName = "COM3"; // fill in your serial port name here
let currentXPos=0;
let currentYPos=240;
let switchStatus=0;
let value;
function setup() {
createCanvas(640, 480);
serialPort = new p5.SerialPort(); // make a new instance of the serialport library
serialPort.on("list", printListOfPorts); // set a callback function for the serialport list event
serialPort.on("connected", serverConnected); // callback for connecting to the server
serialPort.on("open", portOpened); // callback for the port opening
serialPort.on("data", serialDataReceived); // callback for when new data arrives
serialPort.on("error", serialError); // callback for errors
serialPort.on("close", portClosed); // callback for the port closing
serialPort.list(); // list the serial ports
serialPort.open(portName); // open a serial port
}
function draw() {
background(255);
value = map(mouseX, 0, width, 0, 255);
}
// get the list of ports:
function printListOfPorts(portList) {
// portList is an array of serial port names
for (let i = 0; i < portList.length; i++) {
// Display the list the console:
print(i + " " + portList[i]);
}
}
function serverConnected() {
print("Connected to server.");
}
function portOpened() {
print("The serial port opened.");
}
function serialDataReceived() {
// read a string from the serial port
// until you get carriage return and newline:
let incomingString = serialPort.readLine();
serialPort.write(value);
}
function serialError(err) {
print("Something went wrong with the serial port. " + err);
}
function portClosed() {
print("The serial port closed.");
}

Exercise 3:

For the final assignment, we had to use bidirectional serial communication, which basically involved controlling a p5js sketch with an Arduino and changing the p5js sketch to modify the arduino. We made the ball move on the Arduino by using a distance sensor to modify the wind speed in p5js. Depending on the ball’s velocity, we programmed the p5js sketch so that an LED will turn on each time the ball bounces.

Arduino Code:

Arduino code
void setup() {
  Serial.begin(9600);
  pinMode(2, OUTPUT);
  while (Serial.available() <= 0) {
    Serial.println("0"); // send a starting message
    delay(300);              // wait 1/3 second
  }
}
void loop() {
  while (Serial.available() > 0) {
    // read the incoming byte:
    int inByte = Serial.read();
    analogWrite(2, inByte);
    int WindPower = analogRead(A0);
    Serial.println(WindPower);
    
  }
}

p5js Code:

let serialPort; // variable to hold an instance of the serialport library
let portName = "COM7"; // replace with your serial port name
let speed;
let weight;
let location;
let acceleration;
let wind;
let resistance = 0.99;
let mass = 50;
let windVal;

function setup() {
createCanvas(640, 360);
noFill();
location = createVector(width/2, 0);
speed = createVector(0,0);
acceleration = createVector(0,0);
weight = createVector(0, 0.5*mass);
wind = createVector(0,0);

serialPort = new p5.SerialPort(); // create a new instance of the serialport library
serialPort.on("list", printPortList); // set a callback function for the serialport list event
serialPort.on("connected", serverConnected); // callback for connecting to the server
serialPort.on("open", portOpen); // callback for the port opening
serialPort.on("data", serialEvent); // callback for when new data arrives
serialPort.on("error", serialError); // callback for errors
serialPort.on("close", portClose); // callback for the port closing
serialPort.list(); // list the serial ports
serialPort.open(portName); // open a serial port
}

// get the list of ports:
function printPortList(portList) {
// portList is an array of serial port names
for (let i = 0; i < portList.length; i++) {
// Display the list the console:
print(i + " " + portList[i]);
}
}

function serverConnected() {
print("Connected to server.");
}

function portOpen() {
print("The serial port opened.");
}

function serialEvent() {
// read a string from the serial port until a carriage return and newline are received
let inString = serialPort.readLine();
print(inString);
if (inString.length > 0){
windVal = map(inString, 0, 1023, -3, 3);
}
}

function serialError(err) {
print("There was an error with the serial port: " + err);
}

function portClose() {
print("The serial port closed.");
}

function draw() {
background(255);

wind.x = windVal;

applyForce(wind);
applyForce(weight);
speed.add(acceleration);
speed.mult(resistance);
location.add(speed);
acceleration.mult(0);
ellipse(location.x,location.y,mass,mass);

if (location.y > height-mass/2 - 25){
serialPort.write(255);
}
else serialPort.write(0);

if (location.y > height-mass/2) {
speed.y *= -0.9; // A little dampening when hitting the bottom
location.y = height-mass/2;

 

Overall, using serial communication with p5.js allows us to create interactive graphics and visualizations that can communicate with and control devices like the Arduino.

 

 

Leave a Reply