ARDUINO ↔ P5.JS

Week 08 - 10/26/2022

Serial IMU Output to p5.js | p5.webserial

p5.webserial is more direct and easier to use, however it only works in Chrome or Edge. One of the advantages is that it does not need a third-party application (such as p5.serialcontrol from Week07’s lab) to communicate with Arduino.

In the first part of the lab, I focused on reading the Internal Motion Unit (IMU), which is a nano 33 Iot built-in sensor and translating to p5.js in order to determine its heading, pitch, and roll.

const int buttonPin = 2;      // digital input
 
void setup() {
  // configure the serial connection:
  Serial.begin(9600);
  // configure the digital input:
  pinMode(buttonPin, INPUT);
}
 
void loop() {
  // read the first analog sensor:
  int sensorValue = analogRead(A0);
  // print the results:
  Serial.print(sensorValue);
  Serial.print(",");
 
  // read the second analog sensor:
  sensorValue = analogRead(A1);
  // print the results:
  Serial.print(sensorValue);
  Serial.print(",");
 
  // read the button:
  sensorValue = digitalRead(buttonPin);
  // print the results:
  Serial.println(sensorValue);
}

Arduino:

// 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 outData; // for outgoing data

// variables for the circle to be drawn:
let locH, locV;
let circleColor = 255;

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); // black background
  fill(circleColor); // fill depends on the button
  ellipse(locH, locV, 50, 50); // draw the circle
}

// 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() {
  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();
}

// read any incoming data as a byte:
function serialEvent() {
  // read a string from the serial port
  // until you get carriage return and newline:
  var inString = serial.readStringUntil("\r\n");
  //check to see that there's actually a string there:
  if (inString) {
    // split the string on the commas:
    var sensors = split(inString, ",");
    if (sensors.length > 2) {
      // if there are three elements
      // element 0 is the locH:
      locH = map(sensors[0], 0, 1023, 0, width);
      // element 1 is the locV:
      locV = map(sensors[1], 0, 1023, 0, height);
      // element 2 is the button:
      circleColor = 255 - sensors[2] * 255;
    }
  }
}

// pop up an alert if there's a port error:
function portError(err) {
  alert("Serial port error: " + err);
}

// 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");
}

p5.js:


The result went well. I think the most difficult part is to fully understand how Serial Handshaking works. I am looking forward to applying it to some real-time motion projects in the future.


Two-Way Serial Communication

This part did not go well. Initially I tried to connect the breadboard and at least write part of the code in Arduino, since I think I understand the principle and the steps. However, I failed — it did not work at all. I did need to consult the lab page.

For some reason, after my failure, when I used the exact code from the lab page, the result was not correct either. The code worked, but the outcome was not as the same as it should be. I did not even reach the Handshaking part — I am still trying to debug it.


When the board is put flat on the table, the circle would not show at all. When I tip it over even for a little bit, the circle shows in the corner. According to the code, it should not work like that. It might be a connection problem.

When the problem is solved, I would like to integrate it in one of my p5.js sketches, in which each potentiometer will be able to control either x or y axis on the canvas. The sketch then will be more interactive than being controlled by a mouse.

UPDATE: PROBLEM SOLVED. It was the map.

video 1

video 2

figure 1