Skip to content

15. Interface and application programming

assignment

individual assignment:

write an application that interfaces a user with an input &/or output device that you made

group assignment:

compare as many tool options as possible

Proximity sensor Files

HTML file

CSS file

animation.js file

Piezo electric with my board

Folder.zip (.js, .css, .html, .ino files)

Gantt plan of the week

This week I plan to learn something about web graphic interfaces with SVG

Animate with SVG

I started with a SVG Animation tutorial

First thing I learn is to clean the SVG code :

Clean the SVG code

  • define the styles within the <style> tag (and not within the elements themselves)
  • Save as .svgsimple (svg 1.1)
  • clean the code with SVGOMG
  • give a class name to each element

1st test : move a circle

<svg viewBox="0 0 800 300">
<circle  class="circle" cx="400" cy="250" r="30"/>
<style>
    .circle {fill:#ff0;stroke:#000;stroke-width:.25;stroke-opacity:1;}

    .circle {
        animation-name: circleMoves;
        animation-duration: 6s;
        animation-iteration-count: infinite;
        animation-timing-function: ease-in;
        animation-timing-function: ease-out;
    }

    @keyframes circleMoves {
        from { transform: translateY(0px); }
        to   { transform: translateY(-150px); }
        }
</style>
</svg>

Then adding the function rotate, changing the origin, scale and changing the opacity :

.circle1 {
  animation-name: circleMoves1;
  animation-duration: 3s;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
  transform-origin: 530px 250px;
}
@keyframes circleMoves1 {
    0% { transform: translateY(0px); }
  20%   { transform: translateY(-150px); }
  90%   { transform: rotate(180deg); }
  100%  { transform: scale(0.5);
      opacity: 0;   }
    }

Adding randomness

To add a random number, I found the following documentation.

Useful sites

The random number has been generated with the following function in JavaScript. We get a random number (here from 15 to 50) and attribute it to the variable r in circle. And a delay of 4000ms has been added :

var circle = document.querySelector("#circle");
setInterval(function() {
  circle.setAttribute("r", getRandomInt(15, 50));
}, 4000);


function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1) + min);
}
adding javascript in markdown

In the file mkdocs.yml add the line : extra_javascript: - javascripts/extra.js

And add the js file in the folder docs/javascripts/extra.js

to change style attributes :circle.style.fill=generateRandomColor();

Then I added in the extra.js :

function randColor()  {
    return "#" + Math.floor(Math.random()*16777215).toString(16).padStart(6, '0').toUpperCase();
}
Difference between class="" and id=""

It seems that javascript functions only works with id="example"

To call an ID in CSS, use #example{} instead of .example{}

The difference is that the ID only refers to one single element while class can be used for several elements


Serial communication with p5.js

Code Arduino

To test how to communicate via serial on my computer, I used first my Arduino nano 33 ble sense. It’s useful to upload sketch fast and it has a lot of sensors to test different input signals.

I uploaded an example for PDMSerialPlotter sketch which allow to read the audio data from the Arduino and prints the samples in the serial monitor. I changed it to turn on the RGB led depending on the strength of the signal. The library used is the PDM.h (Pulse Density Modulation) which is similar to the PWM (Pulse Width Modulation).

Arduino sketch
/*
This example reads audio data from the on-board PDM microphones, and prints
out the samples to the Serial console. The Serial Plotter built into the
Arduino IDE can be used to plot the audio data (Tools -> Serial Plotter)


This example code is in the public domain.
*/

#include <PDM.h>

// buffer to read samples into, each sample is 16-bits
short sampleBuffer[256];

// number of samples read
volatile int samplesRead;

void setup() {
Serial.begin(9600);
while (!Serial);

// configure the data receive callback
PDM.onReceive(onPDMdata);

// optionally set the gain, defaults to 20
// PDM.setGain(30);

// initialize PDM with:
// - one channel (mono mode)
// - a 16 kHz sample rate
if (!PDM.begin(1, 16000)) {
Serial.println("Failed to start PDM!");
while (1);
}
}

void loop() {
// Wait for samples to be read
if (samplesRead) {

// Print samples to the serial monitor or plotter
for (int i = 0; i < samplesRead; i++) {
 Serial.println(sampleBuffer[i]);
 // check if the sound value is higher than 500
if (sampleBuffer[i]>=500){
  digitalWrite(LEDR,LOW);
  digitalWrite(LEDG,HIGH);
  digitalWrite(LEDB,HIGH);
}
// check if the sound value is higher than 250 and lower than 500
if (sampleBuffer[i]>=250 && sampleBuffer[i] < 500){
  digitalWrite(LEDB,LOW);
  digitalWrite(LEDR,HIGH);
  digitalWrite(LEDG,HIGH);
}
//check if the sound value is higher than 0 and lower than 250
if (sampleBuffer[i]>=0 && sampleBuffer[i] < 250){
  digitalWrite(LEDG,LOW);
  digitalWrite(LEDR,HIGH);
  digitalWrite(LEDB,HIGH);
}
delay(100);
}

// Clear the read count
samplesRead = 0;
}
}

/**
* Callback function to process the data from the PDM microphone.
* NOTE: This callback is executed as part of an ISR.
* Therefore using `Serial` to print messages inside this function isn't supported.
* */
void onPDMdata() {
// Query the number of available bytes
int bytesAvailable = PDM.available();

// Read into the sample buffer
PDM.read(sampleBuffer, bytesAvailable);

// 16-bit, 2 bytes per sample
samplesRead = bytesAvailable / 2;
}

Open p5.serialControl

We have to add the library p5.serialport because JavaScripts in a browser can’t interact directly with a serial port. The app p5.serialcontrol allows to use this library with a simple interface to open a serialport and read it with a .js file. (Another way would be to use Node.js to run it but the app seems the easiest to use).

Next step, I launched the application p5.serialControl and select the good port. When I tried to open the console, the application crashed. After testing several things, I could read the serial console when I put a delay of 100ms between each serial.println in the Arduino code.

Open an example in the browser

I then opened the Read and animate example. I modified first the port name in the sketch.js file.

The result displayed the value on the web page :

In this example the value would change abruptly from a low to a high value in fractions of seconds. So I made another test with the proximity sensor this time to have a more interesting visual experience :

Arduino code
/*
APDS-9960 - Proximity Sensor

This example reads proximity data from the on-board APDS-9960 sensor of the
Nano 33 BLE Sense and prints the proximity value to the Serial Monitor
every 100 ms.

The circuit:
- Arduino Nano 33 BLE Sense

This example code is in the public domain.
*/

#include <Arduino_APDS9960.h>

int ledState = LOW;

unsigned long previousMillis = 0;

const long intervalLong = 1000;
const long intervalMed = 500;
const long intervalShort = 100;

void setup() {
Serial.begin(9600);
while (!Serial);

if (!APDS.begin()) {
Serial.println("Error initializing APDS-9960 sensor!");
}

// set the LEDs pins as outputs
pinMode(LEDR, OUTPUT);
pinMode(LEDG, OUTPUT);
pinMode(LEDB, OUTPUT);

// turn all the LEDs off
digitalWrite(LEDR, HIGH);
digitalWrite(LEDG, HIGH);
digitalWrite(LEDB, HIGH);

}

void loop() {

unsigned long currentMillis = millis();

// check if a proximity reading is available-
if (APDS.proximityAvailable()) {
// read the proximity
// - 0   => close
// - 255 => far
// - -1  => error
int proximity = APDS.readProximity();

if (proximity > 150) {
      if (currentMillis - previousMillis >= intervalLong) {
        previousMillis = currentMillis;

        // if the LED is off turn it on and vice-versa:
        if (ledState == LOW) {
          ledState = HIGH;
        } else {
          ledState = LOW;
        }

        // set the green LED with the ledState of the variable and turn off the rest
        digitalWrite(LEDG, ledState);
        digitalWrite(LEDR, HIGH);
        digitalWrite(LEDB, HIGH);
      }
}
else if(proximity > 50 && proximity <= 150){
  if (currentMillis - previousMillis >= intervalMed) {
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
      } else {
        ledState = LOW;
      }

      // set the blue LED with the ledState of the variable and turn off the rest
      digitalWrite(LEDB, ledState);
      digitalWrite(LEDR, HIGH);
      digitalWrite(LEDG, HIGH);
    }
}
else {
  if (currentMillis - previousMillis >= intervalShort) {
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // set the blue LED with the ledState of the variable and turn off the rest
    digitalWrite(LEDR, ledState);
    digitalWrite(LEDB, HIGH);
    digitalWrite(LEDG, HIGH);
  }
}
  // print value to the Serial Monitor
  Serial.println(proximity);
}
delay(10);
}

Animate SVG with serial data

Looking into the previous example, we need to have the following files :

  • HTML file which contains the header to call the different file. It will also contain the SVG

  • p5.js file which I downloaded from Github

  • sketch.js file which contains the code to read the data coming from the serial port. It will also contain the script I wrote above to change the SVG.

  • p5.serialport.js which I downloaded from Github

  • A css file to apply the style to the SVG

Sketch.js

3 functions are needed to read serial port :

  • One for the setup :
function setup() {

  // make an instance of the SerialPort object
  serial = new p5.SerialPort();

  // Get a list the ports available
  // You should have a callback defined to see the results. See gotList, below:
  serial.list();

  // Assuming our Arduino is connected,  open the connection to it
  serial.open(portName);

  // When you get a list of serial ports that are available
  serial.on('list', gotList);

  // When you some data from the serial port
  serial.on('data', gotData);
}
  • One to retrieve the serial port :
// Got the list of ports
function gotList(thelist) {
  print("List of Serial Ports:");
  // theList is an array of their names
  for (let i = 0; i < thelist.length; i++) {
    // Display in the console
    print(i + " " + thelist[i]);
  }
}
  • And one to get the data from the serial port :
// Called when there is data available from the serial port
function gotData() {
  let currentString = serial.readLine();  // read the incoming data
  trim(currentString);                    // trim off trailing whitespace
  if (!currentString) return;             // if the incoming string is empty, do no more
  console.log(currentString);
  if (!isNaN(currentString)) {  // make sure the string is a number (i.e. NOT Not a Number (NaN))
    SunCXpos = currentString;   // save the currentString to use for the text position in draw()
  }
}

Finally here is a video to show the result :

Final video with my board

I finally made a test with my board made on the Output devices week and the following code. It includes the three functions mentioned above to read serial port, plus two functions to modify the property of the text and the circle :

DrumSVGanimation.js code
let portName = "COM17";
let dataSerial = 100;
let serial;

function setup() {

// make an instance of the SerialPort object
serial = new p5.SerialPort();

// Get a list the ports available
// You should have a callback defined to see the results. See gotList, below:
serial.list();

// Assuming our Arduino is connected,  open the connection to it
serial.open(portName);

// When you get a list of serial ports that are available
serial.on('list', gotList);

// When you some data from the serial port
serial.on('data', gotData);
}

// Got the list of ports
function gotList(thelist) {
print("List of Serial Ports:");
// theList is an array of their names
for (let i = 0; i < thelist.length; i++) {
// Display in the console
print(i + " " + thelist[i]);
}
}

// Called when there is data available from the serial port
function gotData() {
let currentString = serial.readLine();  // read the incoming data
trim(currentString);                    // trim off trailing whitespace
if (!currentString) return;             // if the incoming string is empty, do no more
console.log(currentString);
if (!isNaN(currentString)) {  // make sure the string is a number (i.e. NOT Not a Number (NaN))
dataSerial = currentString;   // save the currentString to use for the text position in draw()
}
}

var text = document.getElementById("value");        // change the text
setInterval(function() {
value.innerHTML=dataSerial;                             // change the text "value" to display dataSerial
value.setAttribute("y", +310 - +dataSerial/3.5);            // change the position y of the text
}, 100);

var circle = document.querySelector("#yellow");     
setInterval(function() {
yellow.setAttribute("cy", +305 - +dataSerial/3.5);      // change the position of the circle of id="yellow"
/*yellow.style.fill=testNum(dataSerial);*/              // change the color, calling the function testNum()
}, 10);

The HTML file contains the header which calls the different files needed :

<script language="javascript" type="text/javascript" src="p5.js"></script>
<script language="javascript" type="text/javascript" src="../../lib/p5.serialport.js"></script>
<script language="javascript" type="text/javascript" src="DrumSVGanimation.js"></script>
<link rel="stylesheet" href="DrumSVGanimation.css">

And here is a video with my board. You can download the files needed to do this on top of the page (You’ll need to download the p5.serialcontrol app and add the p5.serialport.js file in your folder):

Bluetooth communication with MIT app builder

Last week I made a test with the MIT app builder :


Last update: July 8, 2022