Chip Hippo ← Back to site

Arduino Integration

Chip Hippo can talk to a real Arduino over USB while the circuit runs. An Output hands a value from the breadboard to a sketch — a number to show on a real display, a bit to switch a real relay — and an Input hands a value from the sketch back onto the breadboard: a sensor reading, a key press, the answer to a question the circuit just asked.

The sketch side is written for you. Chip Hippo generates a header file for each board, declaring one function per Output and one small object per Input; you include it, call two functions from setup() and loop(), and fill in what each Output should do. The link underneath — framing, checksums, acknowledgements, retries — is the header's job and never yours.

No Arduino to hand? Every Chip Hippo has a built-in Mock connection that plays the board's part itself (see The Mock connection), so you can build and try an integration before a wire is connected.

What you need

Nothing here touches the network: a connection is a USB cable, and Chip Hippo opens the port only while the circuit is running.

Connections

A connection is a named Arduino: Display board, Sensor Nano. Open Settings (Cmd/Ctrl+,) and choose the Serial I/O tab. + beside the connection list creates one; give it a name and pick its Port from the list of what is plugged in right now (Re-scan after plugging a board in). Baud rate defaults to 115200, which suits every current board. Advanced holds the rest of the serial framing — data bits, parity, stop bits and flow control. Leave those alone unless you have a reason.

Apply saves the connection. If the port isn't plugged in at that moment you're told so, and the button becomes Apply anyway, which saves it regardless — useful when you're setting up before the board arrives. Open window… opens the connection's window, and the bin beside the list (click it twice) deletes the connection. An Output or Input still using it keeps its assignment — its Properties show the connection as (not on this computer) — and the circuit won't run until you choose another one there.

A connection whose port is missing, or which still Needs configuration, says so in the list, and its Port is marked in red.

The list always starts with the Mock, marked Built-in. It has nothing to configure and can't be removed, and its name is reserved: a connection of your own can't be called Mock.

Connections travel with the project

The connection's name and serial settings are saved in the project file, so a design opened on another computer still knows which board each Output talks to. The port is not — /dev/cu.usbmodem14101 on your Mac means nothing on someone else's PC — so a connection that arrives in a project is added to that computer's Settings marked Needs configuration, and the circuit won't run until a port has been chosen for it.

The Mock connection

The Mock is a pretend Arduino inside Chip Hippo. Choose it as an Output's or Input's Connection like any other, and press Run. There is no port to pick, no sketch to upload and no header to generate. It speaks the same protocol a real board does, byte for byte, so what works with the Mock works with a board.

When a run starts, the Mock's connection window opens beside the desk (without taking focus from it), with a send panel under its stream. You can also open it from Settings ▸ Serial I/O: pick Mock and press Open window…. It shows:

Faults, folded away at the bottom, make the Mock misbehave once, on purpose, so you can see how the link copes:

Fault What happens
Drop next ACK The Mock swallows its acknowledgement of the next Output. Chip Hippo waits, sends it again, and the circuit carries on — the value still arrives only once.
Corrupt next frame The next acknowledgement or Input the Mock sends is damaged in transit. Chip Hippo notices and asks for it again.
Ignore handshake The Mock doesn't answer on the next Run, which stops with No answer.
Wrong signature The Mock answers the next Run as a sketch built for a different design, which refuses to start.

A fault stays armed (its button highlighted) until it has happened. Click it again to disarm it. You can arm one while the circuit is stopped, ready for the next Run. What each fault causes shows up in the stream as a warning line (turn on Protocol to see the traffic around it too).

Outputs and Inputs

Add either from the parts palette's SIGNALS section: pick Output or Input, click anywhere on the desk, and choose how many pins it has — 1, 2, 4, 8 or 16. You can change that afterwards in its Properties.

Each one appears as a card on the right-hand edge of the desk, under the signal buttons: its direction arrow (→ Output, ← Input), its colour, its name and how many pins it has. Under the name sits a small numbered chip for every pin that isn't on the board yet, plus one more — the trigger, marked with an arrow. Drag a chip onto a breadboard hole to plant that pin there as a tag; drag a planted tag to move it, and press R while dragging (or with the tag selected) to turn it. Delete unplugs a selected tag back onto its card. The card's right-click menu offers Properties…, Remove All Tags and Delete Output / Delete Input; a tag's own menu adds Remove Tag and Add to analyzer.

A tag is plugged into its hole the way a wire end is — one hole, one lead — and deleting the breadboard under a tag sends it back to the card; the Output or Input itself is never lost with it.

Pins and fields

An element's pins are grouped into fields, and each field becomes one parameter of the Arduino function (for an Output) or one setter (for an Input). A field is a Bit (1 pin, a bool), a Byte (8 pins, a uint8_t) or a Word (16 pins, a uint16_t), up to 16 pins in all. The Properties dialog lists them in pin order with a name each; + Bit, + Byte and + Word add one, and the × removes one.

Pin 1 is the least significant bit of the element's value, pin 2 the next, and so on. A pin whose tag isn't planted, or whose net is floating or unknown, reads as 0.

Properties

Right-click a card (or one of its tags) and choose Properties… to edit:

Setting What it does
Name, Description The name is also the function (Output) or object (Input) name in the header, turned into a valid C++ identifier
Color The card's dot and its tags — any of the seven signal colours
Connection Which Arduino it talks to. The gear beside it opens Settings ▸ Serial I/O
Trigger Rising, Falling or Either — which transition of the trigger line counts
Trigger starts Low or High — what the trigger line is taken to have been before the run began, so a line that starts high can still fire (or not) on the first settle
Pins The fields, as above

How values move

Chip Hippo only ever looks at the board between settles — when every chip's output has stopped changing. A glitch inside one settle is never sent anywhere, and nothing from the Arduino ever lands on the board halfway through a ripple.

An Output fires on its trigger

When an Output's trigger line makes the chosen transition, Chip Hippo samples the Output's pins and sends the value. The circuit then waits — no clock edges, no further settles — until the Arduino's function has returned and the board has acknowledged it. So whatever the sketch does in response has already happened before the circuit moves on, and an Input the sketch sends from inside that function lands in the same step: the request/response shape (the circuit asks, the Arduino answers, the circuit carries on with the answer) works with no timing of your own.

An Output whose trigger tag isn't planted never fires.

An Input is live or triggered

An Input drives its pins at the strength of a chip output — two things disagreeing over one net is reported as a conflict, exactly as two chip outputs would be — but drives nothing at all until the Arduino has sent it a first value.

Generating the board's code

The Generate button in the desk tools pill opens a card listing every connection this desktop's Outputs and Inputs use, each with View files…, Copy and Save header…. Save ChipHippo.h into that board's sketch folder and include it:

#include "ChipHippo.h"

void setup() {
  ChipHippo.onConnect(sendInputs);  // optional — see below
  ChipHippo.begin();
}
void loop() { ChipHippo.poll(); }

// The Output "Display" arrives here: its name + In, its fields the parameters.
void DisplayIn(uint8_t value, bool dp) {
  ChipHippo.print("showing ");
  ChipHippo.println(value);
}

// The Input "Keypad" is sent from here: set its fields, then send it whole.
void keyPressed(uint8_t code) {
  ChipHippo.KeypadOut.setCode(code);
  ChipHippo.KeypadOut.setReady(true);
  ChipHippo.KeypadOut.send();
}

// Runs at the start of every run: give each Input its starting value.
void sendInputs() {
  ChipHippo.KeypadOut.setCode(0);
  ChipHippo.KeypadOut.setReady(false);
  ChipHippo.KeypadOut.send();
}

In the sketch, names read from the Arduino's side. An Output leaves the circuit and arrives at the Arduino, so the Output called Display is the function DisplayIn(); an Input is sent from the Arduino into the circuit, so the Input called Keypad is ChipHippo.KeypadOut. The two different endings also mean an Output and an Input can share a name. A name that already ends that way isn't doubled (an Output called DataIn stays DataIn). A name that still can't be used as it is (two Outputs with the same name, a field called delay) is changed, and the Generate card lists each change under its connection.

View files… shows the header beside ChipHippoExample.ino, the smallest sketch that uses it — one function per Output that logs what arrived, and every Input sent its starting value at the start of a run — each in its own tab, with line numbers and a Copy button. Closing it returns to the Generate card. The header's own opening comment points at the example too.

An Input's send() returns true once Chip Hippo has the value. It returns false when nobody is listening: at once before a run has greeted the sketch, and after one has stopped (the first time, after a second and a half of resends). ChipHippo.connected() says which state the link is in.

An Input puts nothing on its pins until its first value arrives, so a sketch should say where its Inputs stand as soon as a run begins. ChipHippo.onConnect(fn) is for exactly that: poll() runs fn at the start of every run. That matters on boards that don't restart when Chip Hippo opens the port — a Leonardo, a Micro, most boards with USB built into the chip — where the sketch simply carries on from one run into the next and connected() alone can't tell that a new one has begun.

The Mock needs no header. A desktop whose Outputs and Inputs are all on the Mock is told so, and the Mock never marks the Generate button out of date.

The header never contains your code, so saving over it after a change to the design is always safe. Three rules keep the link healthy:

Python boards (MicroPython and CircuitPython)

A board that runs Python — a Raspberry Pi Pico, an ESP32, an Arduino Nano ESP32 or Nano RP2040 Connect, most Adafruit boards — can take part too. Set its connection's Language to Python in Settings ▸ Serial I/O, and Generate writes chiphippo.py instead of a header: one module that runs on MicroPython and CircuitPython alike. (An Uno, Nano, Mega or Leonardo is too small for Python and stays C++.)

from chiphippo import link


@link.display_in  # the Output "Display" arrives here
def display_in(value, dp):
    link.print("showing", value)


@link.on_connect  # runs at the start of every run
def send_inputs():
    link.keypad_out.set_code(0)
    link.keypad_out.set_ready(False)
    link.keypad_out.send()


link.begin()
while True:
    link.poll()

It is the header said in Python: the same names in Python's own style (display_in, keypad_out.set_code), an Output handled by decorating a function with @link.<its name>_in, and link.print() for the log. The Generate card offers Save module… instead of Save header…, and View files… shows the module beside main.py (MicroPython) and code.py with boot.py (CircuitPython). Copy chiphippo.py and the program for your board onto it.

Where the link runs is the one real difference between the two:

A function that raises an exception is reported in the connection window and the circuit carries on — on a board there is nowhere else it could be seen. Most Python boards don't restart when Chip Hippo opens the port, so give your Inputs their starting values from an @link.on_connect function, as above. link.begin() can also be handed a port you have opened yourself — a machine.UART or busio.UART at the connection's baud rate.

Keeping the header current

Every header carries a design hash of what it was generated from — the connection's settings and the names, fields and order of its Outputs and Inputs. Change any of those and the header is out of date: the Generate button shows a dot, and the Generate card says which header needs doing again. Positions of tags on the board, colours and descriptions don't change the hash. A header saved by an older Chip Hippo whose generated code has since changed shows as out of date too, so that saving it again picks the change up.

A run checks something narrower: the layout the sketch was built for — which Outputs and Inputs there are, in what order, and how each one's pins are split into fields. Names aren't part of it, so a design you've only renamed things in still runs while you get round to regenerating. But a sketch built for a different layout would hand values to the wrong functions or parameters, so that stops the run before it starts, asking you to generate the header again and re-upload the sketch. A sketch built by a version of Chip Hippo that speaks a different protocol is refused the same way.

Running with an Arduino

Press Run as usual. Before anything simulates, Chip Hippo checks that every Output and Input in use has a connection, that each connection is configured, and that its port is plugged in — and if not, says which and offers to open the right Properties or Settings. It then opens each port and greets the sketch, asking again every quarter of a second for up to five seconds; many boards reset when the port opens, so this is also the time a sketch has to boot. A board that never answers stops the run with the port named, and so does a sketch built for a different layout (above).

While it runs:

Stop closes every port, so the Arduino IDE can have it back to upload a new sketch — there's no need to quit Chip Hippo between uploads.

The connection window

Each connection has a window showing everything that happens on it, in one stream: what its sketch printed through ChipHippo, and the values and protocol frames going back and forth. Because it is one timeline, cause and effect read in order — a value arrives, then the line the sketch printed in response.

Open one by clicking the lamps, with Open Connection Window on an Output's or Input's right-click menu (it works while the circuit runs), or by picking the connection in Settings ▸ Serial I/O and pressing Open window….

Every line starts with a mark saying what it is:

Mark Line Example
(none) Text the sketch printed got 41
→ A value sent to the board (an Output) → OUTPUT Display value=0x3F dp=1
← A value from the board (an Input) ← INBOUND Keypad code=0x07 ready=1
· Protocol traffic: the handshake, acknowledgements, the port opening and closing · ← ACK seq 12
! Something went wrong, and what the link did about it ! No ACK for seq 12 — resending (attempt 2/3)

Arrows are always from Chip Hippo's side: → leaves Chip Hippo, ← arrives. Hover over a value's line to see the exact bytes it travelled as. A value that had to be sent again appears once, with the resend as a warning line beneath it.

The footer holds the controls:

A new Run starts the window afresh. After Stop everything stays, so a failure can be read at leisure — even after closing and reopening the window. It keeps the last 2,000 lines, follows new lines only while you're scrolled to the bottom, and its text can be selected and copied. Each connection's window remembers where it was and how it was set up.

Under the hood

For the curious, or for anyone writing their own firmware: the link is a small framed protocol over the serial port, version 1. Every frame is 0x7E · TYPE · SEQ · LEN · PAYLOAD · CRC, with 0x7E and 0x7D escaped everywhere after the start byte and a CRC-16/CCITT-FALSE (polynomial 0x1021, initial value 0xFFFF) over the unescaped TYPE to PAYLOAD. A value's payload is the element's index, its width in bits, and the value as two bytes, little-endian.

At the start of every run Chip Hippo sends a HELLO carrying the protocol version, the layout signature (a CRC-32 of the layout described above) and a fresh session number, and the sketch answers with its own version and signature. Outputs and Inputs are then acknowledged and resent on a timeout or a NAK. A resend is recognised by its sequence number, so a function never runs twice for one value, and an Output is acknowledged only once its function has returned. Log text travels in unacknowledged chunks that never split a character.