Back to Notebook Index

Week 12: Machine Week

This week was machine week!!

Planning

We met a couple of times to brainstorm what project we'd want to do, and to split out tasks once we had narrowed in on one.

We thought about a ton of different ideas, from a CNC-controlled Piano player to a CNC Cheese Hot-wire Carver.

We decided to go with an Apple Lathe, and set to work splitting it up into constituent components and designing the parts.

Software / Protocol Design

I first took on the task of making sure we could get a separate UI of our own design communicating with Squidworks.

Since we were doing at least cross-web-page-tab communication and possibly two ways, we went with WebSockets.

Cuttlefish already used WebSockets for its serial communication, but didn't have a built in facility for arbitrary data to and from a websocket server. There was a built-in websocketclient.js hunk that sounded promising, but it could only do a single stream of i/o and didn't contain logic for auto-spinning up the websocket server like the serialport connector does.

Working based off of the serialport.js hunk, I created:

  • hunks/comm/applejack.js - cuttlefish client hunk that connects to the apple-jack-server websocket and translates websocket packets to the corresponding inputs and outputs in cuttlefish-land
  • processes/apple-jack-server.js - websocket server process that facilitates addressed two-way communication between connected "ui" and "cuttlefish" clients
  • apple/ui-applejack.js - the UI's applejack (websocket) client
  • apple/ - folder for our UI creations (gets served by the same node express process as cuttlefish)
  • apple/socket-test.html - test page for applejack socket API

One interesting thing cuttlefish does that I followed was having the serialport-websocket clients spin up and shut-down their own corresponding websocket servers. The nice thing about this is that you don't need to worry about the IP / port of the websocket server -- it sends that along as part of an HTTP response.

This took a bit of fiddling to get right, as the main cuttlefish serialport-websocket could rely on always spinning up and always shutting down a server whenever it was created/destroyed — for our use case, I wanted it to be possible to open EITHER the cuttlefish hunk OR the standalone UI and still properly spin up the websocket server and connect to it.

Our apple-jack-server keeps a registry of currently connected clients and their type (UI or cuttlefish):


    let clientSocketsByType = {
      ui: [],
      cuttlefish: []
    };

    // ...

    if (data.type === 'identification') {
        clientSocketsByType[data.clientType].push(ws);
    }

    // ...

    ws.onclose = (evt) => {
        Object.keys(clientSocketsByType).forEach((type) => {
          clientSocketsByType[type] = clientSocketsByType[type].filter((item) => item !== ws);
        });
    };
        

I worked with the UI and squidworks/motor team to devise what data packets we'd want to go between the UI and cuttlefish.

To start, we did the flexible/clear approach of wrapping everything as JSON objects with relatively descriptive parameter names. If performance was found to to be insufficient for, we would move to a more compact performance-focused data format (e.g. protobufs or a format of our own devising)

apple-jack Socket Protocol

Top-level Packet Types

Client Identification:

{
  type: 'message',
  clientType: 'cuttlefish' or 'ui',
}

Messages:

{
  type: 'message',
  to: 'cuttlefish' or 'ui',
  message: [MESSAGE - see below]
}

[MESSAGE] contents:

UI to Cuttlefish

step message:

{
  messageType: 'step',
  x: 3.212,
  y: 5.2212,
  rotation: 12.5
}

distance sensor control message:

{
  messageType: 'distanceEnabled',
  enabled: true/false
}

stop message:

{
  messageType: 'stop',
}

Cuttlefish to UI

distance sensor control message:

{
  messageType: 'currentDistance',
  distance: 0.234234 (or default int?)
  x/y/z
}

steps completed

{
  messageType: 'stepsCompleted',
  steps: 123
}

Raspberry Pi

One of the neat things with the websocket UI interface is that we could do it remotely.

The Raspberry Pi didn't end up working out, but we got over a bunch of hurdles to get it fully working with the machine (it just wasn't fast enough to run all of Cuttlefish).

Aubrey installed Raspbian on an SD card for the device. The pi had a nice mounting spot on our router machine.