This guide is intended to help code contributors understand how relevant system components of Tessel 2 work, where to find code and design files, and accelerate the development process through technical transparency.
- T2 Github Repositories
- System Architecture
- Code Deploy Walkthrough
- Getting Questions Answered
- Links to more information
Quick guide to the Tessel stack (in order of complexity):
|User / Module Code||JS||(Many)|
|Tessel Library||Linux / JS / Comms||t2-firmware|
|OpenWRT||Linux / C / Shell / More||t2-firmware|
|SSH/GPIO Daemons||Linux / C||t2-firmware|
|Firmware||C / Comms||t2-firmware|
You can find all of the Tessel repositories on the organization page but this section provides some guidance over relevant Tessel 2-specific repositories.
The CLI is the primary method of interacting with Tessel for users and developers. It provides useful functions like listing available Tessels, deploying code, and setting wifi credentials on the device.
The primary processor of the Tessel 2 runs a very lightweight version of Linux called OpenWRT. OpenWRT provides all of the TCP/IP drivers, threading/schedule support, and runs Node, Rust or whatever other language you're using with Tessel 2. This repo contains all of the source files of the Tessel build of OpenWRT.
Tessel 2 features a SAMD21 co-processor that manages a handful of responsibilities like routing USB communication from the CLI and the realtime operations on the GPIO pins of the two module ports. This repo contains not only the firmware that drives this microcontroller but hardware-related scripts that define the interface to other languages (like tessel.js for Node scripts) so that the available hardware functionality doesn't get out of sync with what is exposed to the OpenWRT processor.
The VM is used primarily by Tessel developers who either don't have hardware available or want to develop on a faster computer. The repo provides the ability to create and run these Tessel virtual machines. You can use the CLI (see above) to interact with the running VM just as you would an actual Tessel 2. The VM has the limitation of not being able to set wifi credentials (it passes through the host network interface). It is not able to use 10-pin hardware modules but USB devices do get passed through.
The Tessel 2 compiler is another virtual machine with all the build tools needed to develop native add-ons to Node modules. These add-ons are built to perform faster or interact with lower level hardware than JS alone would provide. The compiler is being used, for example, to develop the audio-video Node module for webcams and recording audio from microphones.
These are the production schematic and assembly files for the Tessel 2 hardware. You can find information about the parts that are used and how they are all connected to each other. These files were created with KiCad, a completely open source electronics design tool.
This repo contains all of the KiCad part models for all Tessel hardware (not just Tessel 2). You will need this library in order to recreate the schematic and PCB of the hardware design files above.
- Ambient (Light & Sound) Module
- RFID/NFC Module
- Accelerometer Module
- Bluetooth Low Energy Module
- Climate (temperature & humidity) Module
- GPS Module
- Infrared Module
- Relay Module
- Servo Module
Tessel supports interfacing with USB devices. Here are libraries we support for Tessel to interoperate with USB modules, available from many vendors:
- Bluetooth Low Energy Dongles
- Cellular (3G/4G) Dongles (no Github module, it just works through the Node
httpmodule and similar for other languages)
- Audio Recording/Playback
- Video Cameras (specifically, webcams)
- Flash Storage Devices (no Github module, it just works through the Node
fsmodule and similar for other languages)
Below is a high level diagram of the hardware architecture:
As you can see from the diagram, the Atmel co-processor manages the USB connection and can communicate with the MediaTek through a SPI bus. The MediaTek can execute user scripts, and when it interprets hardware commands (like pulling a GPIO high or sending data over I2C), it packages it up and sends it over to the Atmel over a pre-defined protocol on that SPI bus.
The MediaTek is responsible for managing the two USB Host ports, the ethernet port, and the WiFi network interace directly from the OpenWRT Operating System.
Let's take a look at the software architecture of Tessel 2:
The USB Port on the Atmel actually has three interfaces to the to the MediaTek:
- A UART connection that acts a serial terminal to the MediaTek. You can use a program like
dtermto access the console through this connection.
- A direct connection to the SPI Flash bus of the MediaTek. This allows the Atmel to rewrite the OpenWRT image and helps prevent the Tessel from becoming bricked by any corruptions to OS.
- A SPI bus connection to the MediaTek for arbitrary data.
A good method of understanding all the components of the system is to go through an example of running a simple script on Tessel 2:
The user might have code like the example below to light up LED 0 on Tessel 2.
var tessel = require('tessel'); tessel.led.high();
First, the user will use the command line interface to deploy the script from the host computer:
t2 run index.js
The utf-8 encoding of each command is sent over the USB connection via a USB pipe. The USB pipe driver hands off the data to the general purpose SPI bridge (labeled as "SPI SLAVE SERCOMMx" in the diagram above) which transfers the command to the MediaTek.
On the MediaTek, the SPI Daemon is constantly waiting for data over the SPI bus and checks whether this data is from one of the module ports (for example, if an analog voltage was previously requested and was now returning the result) or from the USB port.
The shell script that starts up the SPI Daemon on boot also creates three unix domain sockets: one for Module Port A (
/var/run/tessel/port_a), one for Module Port B (
/var/run/tessel/port_b), and one for USB (
/var/run/tessel/usb). We'll get to the two module ports in a moment, but first, the data from the CLI is passed into the USB domain socket.
Waiting on the other end of the domain socket is the USB Daemon. This program has the thrilling task of accepting shell commands, initializing new threads to execute them, and routing stdin, stdout, and stderr of those threads back to the CLI.
So by this point, the CLI has executed a handful of bash commands that transferred the code onto the Tessel 2 and started a Node process to run it. The next question is how we actually do anything with the hardware.
var tessel = require('tessel') line of the user script. When Node executes this on the MediaTek, it finds the
tessel module in
/usr/lib/node/tessel.js. When this file is executed, it connects to a domain socket for each module port: two of the three domain sockets created by the SPI Daemon earlier! That means that whenever you run a command on a port like
tessel.led.high(), it's packaging that command into a array of bytes and sending it back over the SPI bus to the Atmel bridge firmware! That firmware then parses the command as pulling the specific GPIO high, and executes it. It can then return the result of that operation back through the SPI bus to the MediaTek SPI Daemon.
In the case of a LAN connection over WiFi or Ethernet, the procedure is much the same except all of the shell commands are executed over SSH. All of the module port communication between the SPI Daemon and the USB Bridge firmware is entirely the same. That's why the CLI is able to abstract out USB and LAN interfaces into a single
Connection object that simply accepts shell commands and returns a process object with the three streams (stdin, stdout, stderr) regardless of physical connection.
UCI and ubus are unique features of OpenWRT that essentially let you access system configuration programmatically and receive results in a JSON format. It's very handy for the command line interface. You can see that the CLI makes use of these features heavily, especially for wifi related settings.