Skip to content

week14. 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

Group assignment

For more information about group projects, please see the group project page on the FabLab Kannai website.
Group assignment page is here

node.js + socket.io + serialport + XIAO RP2040 app

This week, I will modify the mock part of the node.js implemented in this week's Group assignment page to enable communication with the XIAO RP2040. This app generates random values on the XIAO RP2040 only while the button is pressed and displays them as the size of a circle in the browser. This app was inspired by the documentation from Fab Academy 2021's Nadieh.

app1.jpg

JavaScript

JavaScript is a programming language used to add dynamic behavior and user interface features to web pages. It is typically used in conjunction with HTML and CSS. It is currently the most important language in web development. For more details, please refer to the following link.
https://developer.mozilla.org/en-US/docs/Web/JavaScript

Node.js

Node.js is a revolutionary technology that overturns the conventional wisdom that “JavaScript = web pages” and enables the development of web apps, IoT, CLI, and APIs all using a single language. In other words, it is a technology that brought JavaScript to the server side. For more details, please refer to the following link.
https://nodejs.org/en/

Installing Node.js

The environment setup (macOS only) is as follows.

brew install node

npm install

Start the server.

node index.js

Node.js app code

The directory structure of a Node.js application is as follows.

realtime-sensor-visualization/
├── node_modules/             # Dependency packages installed with npm
├── public/                   # Public folder for placing client-side HTML and JS
│   └── index.html            # UI displayed in the browser
├── index.js                  # Node.js main application file
├── package.json              # Project settings and list of dependent modules
├── package-lock.json         # Record accurate dependency versions

The code for index.js, the main application file for Node.js, is as follows. This code is a server application that uses Node.js to receive data from a serial port and deliver it to a web browser in real time.

const express = require("express");
const http = require("http");
const socketIo = require("socket.io");
const { SerialPort } = require("serialport");
const { ReadlineParser } = require("@serialport/parser-readline");

const app = express();
const server = http.createServer(app);
const io = socketIo(server);

app.use(express.static("public"));

// Serial port settings
const port = new SerialPort({ path: "/dev/cu.usbmodem1101", baudRate: 9600 });
const parser = port.pipe(new ReadlineParser({ delimiter: "\n" }));

// Sending data to the client(public/index.html)
parser.on("data", (data) => {
  console.log("Sensor:", data);
  io.emit("sensorData", data.trim());
});

server.listen(3000, () => {
  console.log("Server running at http://localhost:3000");
});

The code for public/index.html is as follows. This code is a simple web UI that visually displays sensor data received in real time from the Node.js server (sensorData event) as circles of varying sizes.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Sensor Visualizer</title>
  <script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
  <script src="https://d3js.org/d3.v7.min.js"></script>
  <style>
    body {
      font-family: sans-serif;
      text-align: center;
      background-color: #f0f0f0;
    }
    svg {
      margin-top: 50px;
    }
  </style>
</head>
<body>
  <h1>📡 Real-time sensor visualization</h1>
  <p id="value">Waiting for data...</p>

  <svg width="400" height="400">
    <circle id="sensorCircle" cx="200" cy="200" r="10" fill="steelblue"></circle>
  </svg>

  <script>
    const socket = io();

    socket.on("sensorData", (data) => {
      const value = parseInt(data);
      document.getElementById("value").textContent = `Sensor Value: ${value}`;

      const radius = d3.scaleLinear()
        .domain([0, 1023])  // sensor value range
        .range([10, 150]);  // radius range

      d3.select("#sensorCircle")
        .transition()
        .duration(200)
        .attr("r", radius(value));
    });
  </script>
</body>
</html>

XIAO RP2040 code

The XIAO RP2040 code is as follows. This code is a simple infinite loop that generates a random number between 0 and 1023 every second while the button is pressed and displays it on the standard output (print). It emulates the process of acquiring values from a sensor.

# main.py
import time
import random
from machine import Pin

# Pin settings
button = Pin(4, Pin.IN, Pin.PULL_UP)    # Tact switch: pull-up resistor enabled
led = Pin(3, Pin.OUT)                   # LED: Output mode

while True:
    if button.value() == 0:             # When the switch is pressed (LOW)
        led.value(1)                    # Turn on the LED
        value = random.randint(0, 1023) # Generate random numbers between 0 and 1023
        print(value)
    else:
        led.value(0)                    # Turn off the LED

app video

The video below shows an app running on XIAO RP2040 that generates random values and displays them as circles of varying sizes in a browser.

Afterthoughts

  • Using simple code, we were able to receive sensor values from the XIAO RP2040 in real time from a Node.js server (sensorData event) and visualize them as circles of different sizes!

  • If we had more time, we would have liked to try visualization using a different JS library.