SerialMonitor
every serial device on this Mac, live: the first USB one opens by itself, its lines scroll up the screen, a numeric line also fills a value bar, and after an unplug it reconnects on its own. The keys 1-9 open a specific device, and space writes back (SerialPort.availableDevices(), matching:)
- Run
swift run --package-path Examples Example-Integration-SerialMonitor- Imports
OllinSerial- Source
- Examples/Integration/SerialMonitor/Sketch.swift
import Foundation
import Ollin
import OllinSerial
/// Every serial device on this Mac, live, and whatever the open one prints.
/// With nothing plugged in it waits; plug a microcontroller in and the port
/// (matching "usb") opens by itself, every line the board prints scrolls up
/// the screen, and a line that is a plain number also fills the bar at the
/// bottom (the classic 0...1023 analog read). Unplugging is fine too: the
/// port waits and reconnects on its own when the board comes back.
///
/// Press 1-9 to open a specific device from the list, space to send "ping"
/// back to the board.
@main
final class SerialMonitor: Sketch {
var port: SerialPort!
var opened = "first device matching \"usb\""
var devices: [SerialDevice] = []
var lastScan = -10.0
var log: [String] = []
override func setup() {
port = SerialPort(matching: "usb", baudRate: 9600)
port.open()
}
override func draw() {
background(Color(white: 0.06))
// Rescan once a second, so plugging and unplugging shows up live.
if time - lastScan > 1 {
devices = SerialPort.availableDevices()
lastScan = time
}
// Drain this frame's lines into the scrolling log.
for line in port.lines() {
log.append(line)
if log.count > 16 { log.removeFirst(log.count - 16) }
}
let margin = 30 * scale
noStroke()
// The device list.
textSize(22 * scale)
fill(Color(white: 0.85))
drawText("Serial devices", margin, 50 * scale)
fill(Color(white: 0.55))
if devices.isEmpty {
drawText("none: plug a board in", margin, 86 * scale)
}
for (index, device) in devices.prefix(9).enumerated() {
drawText("\(index + 1) \(device.name) \(device.path)",
margin, (86 + Double(index) * 32) * scale)
}
// What is open, and the lines it prints.
let status = port.isOpen ? "open" : "waiting…"
fill(port.isOpen ? Color(red: 0.4, green: 0.9, blue: 0.5) : Color(white: 0.55))
drawText("\(opened): \(status) (1-9 opens, space sends \"ping\")",
margin, height * 0.42)
fill(Color(white: 0.75))
for (index, line) in log.enumerated() {
drawText(line, margin, height * 0.42 + (40 + Double(index) * 30) * scale)
}
// A numeric line also reads as a value; the bar is the latest one.
if let value = port.number() {
let level = min(max(value, 0), 1023) / 1023
let barY = height - 70 * scale
let barWidth = width - margin * 2
fill(Color(white: 0.18))
drawRect(margin, barY, barWidth, 26 * scale)
fill(CosinePalette.neon.color(at: 0.25 + level * 0.5))
drawRect(margin, barY, barWidth * level, 26 * scale)
}
}
override func keyPressed() {
if key == " " {
port.writeLine("ping")
return
}
guard let digit = key?.wholeNumberValue,
digit >= 1, digit <= min(devices.count, 9) else { return }
let device = devices[digit - 1]
port.close()
port = SerialPort(device: device, baudRate: 9600)
port.open()
opened = device.name
log.removeAll()
}
}