Projects

Making with Wio

3 easy to make gadgets with the Wio Terminal

Liam Davies

Issue 54, January 2022

This article includes additional downloadable resources.
Please log in to access.

Log in

We take a look at the Wio Terminal and how you can use it standalone or expand it with Arduino or Raspberry Pi hardware.

Over the years, we’ve had the opportunity to review and experiment with many gadgets in the Arduino ecosystem. The Wio Terminal from Seeed Studio is another one of these gadgets, however, it proves to be an entirely different beast.

Rather than be an individual module or control system, the Wio Terminal is a cleverly thought-out device that can operate either as a standalone IoT powerhouse or in conjunction with the hundreds of Grove and Raspberry Pi compatible modules.

What we find interesting about the Wio Terminal is, like the original Arduino ecosystem itself, caters to a very wide userbase. Those who are very new to the DIY electronics space will get just as much use out of the Wio Terminal as those who are looking to use it for advanced projects.

THE BROAD OVERVIEW

We put the Wio Terminal to use in three different ways.

First, we use the Wio Terminal standalone as a Quote Generator. This uses the inbuilt LCD screen and WiFi module to get text data of famous quotes. We display programming-related quotes, however, you can modify the project to suit your requirements.

Our second project is a game we have called Whack-A-LED. This uses the Wio’s inbuilt accelerometer, GPIO and LCD screen. Our program is a take of the carnival game Whack-A-Mole, except we’re trying to whack LEDs instead, of course.

Finally, we build a multi-channel data logger that can be switched between various data sources to log them to an SD card for future analysis. It also displays a time graph on the Wio Terminal’s screen. In our example, we use a DHT11 weather sensor and a four-channel gas sensor.

Tech Specs

The Wio Terminal has a very healthy selection of interface, sensor and display components.

The terminal itself is packaged in a neat plastic enclosure with no extruding connectors or delicate electronics. Along the top side is three digital pushbuttons which, like the directional D-pad underneath the screen, can be accessed through software to assign custom functionality to each.

The left side features a power slider that doubles as a reset switch. By sliding from the top position to the middle position, the Wio Terminal is turned on. However, by sliding it down further will quickly cut and reconnect power to the unit, resetting it. Sitting below this is a microSD card slot for datalogging and large files.

A very welcome addition to the Terminal is the use of the USB Type-C connector on the bottom, acting as both a power and data interface. There is a quickly diminishing list of reasons that manufacturers should be still using the outdated Micro and Mini USB connectors, so a fully reversible, durable and now largely universal port is a huge bonus.

Next-door neighbours to the Type-C port are two grove connectors, one of which is for accessing the I2C port and two GPIO pins D0 and D1.

Flipping the Terminal over, there is a 40-pin GPIO array compatible with the Raspberry Pi ecosystem. It can be slotted into most boards within the Raspberry Pi line-up, or external hardware can also be plugged in here.

It even has a set of magnets and mounting holes for slotting into projects without designing custom 3D-printed enclosures or holders. Handy!

But, as cheesy 1990s movies teach us, it’s what’s inside that counts. The Wio Terminal is rocking an ARM Cortex core running at 120Mhz, which is leaps and bounds ahead of the Arduino Uno, a hardy staple of the maker electronics world.

Sitting shotgun with the main microprocessor is the Realtek RTL8720DN WiFi / Bluetooth chip. This makes it effortless to integrate quick IoT functionality into your projects. This would be perfect for mini informational displays with little fussing about with wires or external modules.

While the technology described here isn’t new, this is the first time we’ve seen such a small and slick unit containing all of these features in one package. We think it’s a good way of experimenting with both basic and advanced coding depending on your skill level, while also not messing with wiring in modules as is typical with Arduino-based systems.

Getting Set Up

If you’ve used any of the Arduino series boards, using the Wio Terminal is a very similar process with a couple of changes.

To program it using the Arduino IDE, as we’ll be doing, you’ll need to head into your Arduino IDE and open the preferences window from the menu bar. From there, just pop the below URL into the ‘Additional Board Manager URLs’ box:

https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json

After that, head into the Tools > Boards > Board Manager menu and search for Wio Terminal. After installing the latest version, be sure to change the board selection from the Tools > Board menu and choose the correct COM port from the Tools > Port menu.

If you’ve used Arduino boards before, this shouldn’t be anything new, but if you are still getting the hang of programming, you may need to experiment with which COM port to use.

If your program doesn’t want to upload, try a different COM port and make sure that the Wio Terminal is plugged in with a USB-C data cable properly.

Depending on what sensors you’re using, you will need to install the appropriate libraries. Typically, you can find the information available on the Seeed wiki along with some handy examples to test the code with.

You can also use MicroPython to program the Wio Terminal if that’s more your thing. We haven’t investigated this in this article, but we have MicroPython guides available in DIYODE Issues 33 and 34 for further reading.

Project 1:

Daily Quote Screen

For our first project, we’re using both the inbuilt LCD screen and WiFi module to get text data of famous quotes. Since we’re all nerds at DIYODE, we’ve of course chosen to choose famous programming quotes. The centre button of the Wio Terminal will be used to load a new quote and display it on the screen.

WiFi is involved here because we’re using a simple Web API to gather data and display it live. Since it’s connecting to WiFi, we could connect it with virtually any other web interface and make it work.

If you’re new to programming, this code may appear daunting, but it’s really just our Wio Terminal pretending to be a computer sending a web request and reading the response. An API is just an ‘application programming interface’ and is a fancy way of saying it’ll be the source of our data.

We found Skolakola’s ‘Programming Quotes’ API from this big list of public APIs:

https://github.com/public-apis/public-apis

After installing the required WiFi libraries, we can open a new Arduino sketch and pop in the following initialization code. Unless your WiFi network so happens to be named “YOUR_WIFI_NAME” and has the password “YOUR_WIFI_PASSWORD”, you’ll want to change them to your home network details!

Parts Required:JaycarAltronicsPakronics
1 x Wio TerminalXC9190-SS102991299

Parts Required:

#include <ArduinoJson.h>
#include <rpcWiFi.h>
#include <HTTPClient.h>
#include "TFT_eSPI.h"
TFT_eSPI tft;
const char* ssid = "YOUR_WIFI_NAME";
const char* password =  "YOUR_WIFI_PASSWORD";
bool wasPressed = false; 
HTTPClient http;

There isn’t a ton of libraries we need to import here. We’re using the Arduino JSON, rpcWiFi and HTTPClient libraries to handle the internet connection and data, and the TFT_eSPI library to handle the screen on the Wio Terminal.

The ‘wasPressed’ variable will be used during the main loop to ensure we only display one new quote when the button is pressed, and not to continue looking for quotes when the button is held. This is typically referred to as state detection, and we’ll talk about this shortly.

void setup() {
  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(TFT_WHITE);
  tft.setTextSize(2);
  tft.setTextColor(TFT_BLACK);
  tft.drawString("Connecting...", 20, 20);
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.println("Connecting to WiFi..");
  }
  tft.fillScreen(TFT_WHITE);
  Serial.println("Connected to the WiFi network");
  Serial.print("IP Address: ");
  Serial.println (WiFi.localIP());
  pinMode(WIO_5S_PRESS, INPUT_PULLUP);
  getQuoteResponse();
}

Our setup code is verbose but should be fairly self-explanatory as we read through it. We’re starting the TFT screen and setting its rotation, background settings and a placeholder text while we wait for a connection to the WiFi.

Flashing Firmware

To make the WiFi and networking features work, you’ll need to reflash the WiFi firmware on the Wio Terminal. The official Seeed guide can be found here: https://wiki.seeedstudio.com/Wio-Terminal-Wi-Fi/ Note that if you haven’t already, you’ll need to also install Git.

It’s not as difficult as it sounds, and it only took us 10 minutes. If you’re wondering why the WiFi isn’t working on your Wio Terminal, there’s a good chance that this will fix the problem.

Also notice that there is a considerable number of calls to the Serial command, which essentially allows us to debug and inspect the functionality of the Wio Terminal by opening the Serial Monitor (Shortcut – Ctrl+Shift+M).

void loop() {
  if(!digitalRead(WIO_5S_PRESS)) {
    if(!wasPressed) {
      wasPressed = true;
      getQuoteResponse();
    }
  } else {
    wasPressed = false;
  }
}
void getQuoteResponse() {
    const uint16_t port = 80; // Default port
    const char* host = 
"http://programming-quotes-api.herokuapp.com";  
    // Target Server IP Address
    Serial.print("Connecting to ");
    Serial.println(host);
    WiFiClient client;
    http.begin(client, 
"http://programming-quotes-api.
herokuapp.com/quotes/random");
    http.GET();
    DynamicJsonDocument doc(2048);
    deserializeJson(doc, http.getStream());
    String author = doc["author"].as<String>();
    String quote  = doc["en"].as<String>();

This is where the real heavy lifting happens! In our loop function, we’re using that ‘wasPressed’ variable mentioned before to respond only when the button is pressed, and not continuously held.

The getQuoteResponse() function is where the request actually happens, which consists of opening our URL (feel free to visit the URL shown, it will show a random programming quote in your browser), and loading it from a JSON format. We won’t go into JSON formats and the specifics of it in this project, but essentially its a field and value-based system where attributes are given names. Our response usually comes in this format:

{"id":"5a6ce86f2af929789500e824","author":"Ken Thompson","en":"One of my most productive days was throwing away 1,000 lines of code."}

In this case, if we refer to the field “author”, it’s value is “Ken Thompson”. That’s why in our code, we can refer to fields to get their values.

 int len = 23;
  tft.fillScreen(TFT_WHITE);
  ft.setTextSize(2);
  tft.setTextColor(TFT_BLACK);
  for(int i = 0; i < 10; i++) {
    tft.drawString(quote.
substring(len*i,len*(i+1)),20,20+i*18);
  }
  tft.setTextColor(TFT_BLUE);     
  tft.drawString(" - " + author, 20, 210);
  http.end();
}

Finally, we can actually draw the quote text on the Wio Terminal’s screen! This isn’t that tricky, except for that weird for loop with the numbers in it. The purpose of this is to provide some basic text wrapping.

Text wrapping is the process of bringing text fields down to the next line on the screen if it’s too long – which is often the case with quotes. The LCD library does have this function built-in, but it wasn’t cooperating for us, so we wrote it ourselves!

Essentially, we’re taking ‘chunks’ out of the text with the substring function and writing each to one line of the Wio Terminal’s LCD screen. The ‘len’ variable describes the number of characters on each line. If the function is confusing, just change some values and observe the effects!

We’re all done! Now just hit the upload button, ensuring that the Wio Terminal is switched on. After a couple of seconds of letting it connect to our WiFi…

…and boom! It’s all working. Inspiring programming quotes at the press of a button. Obviously, this isn’t the most practical program ever – but it’s a good starting program to experiment with the Wio Terminal and to demonstrate its capabilities with precisely zero external wiring required.

Project 2:

Whack-a-LED

Programming quotes, as exciting as they are, get a little boring after a while. Let’s make a minigame with the Wio’s inbuilt accelerometer, GPIO and LCD screen.

Our program will be like the classic carnival game Whack-A-Mole, except here we’re trying to whack LEDs instead, of course. 9 ‘Holes’ fit comfortably on the Wio Terminal’s screen, so we can simply use the inbuilt accelerometer to determine which square the Wio is currently tilted towards.

To demonstrate that GPIO control is easy to accomplish with the Wio Terminal, we’re also going to be adding a simple green and red LED to show if a player was successful or not after pressing the centre button of the directional pad to signify a ‘whack’.

#include"LIS3DHTR.h"
LIS3DHTR<TwoWire> lis;
#include"TFT_eSPI.h"
TFT_eSPI tft;
void setup() {
  Serial.begin(115200);
  lis.begin(Wire1);
  lis.setOutputDataRate(LIS3DHTR_DATARATE_25HZ); //Data output rate
  lis.setFullScaleRange(LIS3DHTR_RANGE_2G); //Scale range set to 2g
  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(0x2d40);
  pinMode(WIO_5S_PRESS, INPUT_PULLUP);
  pinMode(D0, OUTPUT);
  pinMode(D1, OUTPUT);
}
int lastX = 0;
int lastY = 0;
float x_values, y_values, z_values;
bool isOnTarget = false;
int LEDpos = 0;
unsigned long lastChangeTime = 0;
const int LEDwaitTime = 2000;

Like our first program, this code includes quite a bit of setup code to make everything talk together nicely. The LIS3DHTR library (and its associated calls to ‘lis’) is the inbuilt accelerometer, which can be used to calculate the current orientation and angular velocity (i.e. how fast the Wio Terminal is turning). We’re using that in the following section to find the current ‘tilt’ of the device. As usual, we’re starting up the screen and setting up the GPIO to handle the external LEDs on D0 and D1. We’ll check that out at the end!

Note that you can make the game easier or harder by changing the ‘LEDwaitTime’ variable to reflect how long each LED should wait before it disappears.

void loop() {
  x_values = lis.getAccelerationX();
  y_values = lis.getAccelerationY();
  if(lastChangeTime + LEDwaitTime < millis()) {
    digitalWrite(D0, HIGH);
    delay(300);
    digitalWrite(D0, LOW);
    generateRandomLED();
  }
  drawHoles();
  if (digitalRead(WIO_5S_PRESS) == LOW) {
    if(isOnTarget) {
      Serial.println("Hit!");
      digitalWrite(D1, HIGH);
      delay(300);
      digitalWrite(D1, LOW);
    } else {
      Serial.println("Miss!");
      digitalWrite(D0, HIGH);
      delay(300);
      digitalWrite(D0, LOW);
    }
    generateRandomLED();
  }
}

Much of this code will look familiar to those who have dabbled with Arduinos, as we are mostly dealing with game timing and logic here. It’s mostly a bunch of IF statements that check that delays are respected during game play. Notice, however, the calls to ‘lis.getAccelerationX()’ – this is the Wio Terminal’s inbuilt accelerometer.

Besides the initialization required in the setup function, there isn’t any fancy code required to get an accurate reading of the Wio’s physical movement! Handy!

void drawLED(int x, int y) {
  tft.fillCircle(x,y,12,TFT_RED);
  tft.fillRect(x-12,y,25,19,TFT_RED);
  tft.fillRect(x-8,y+24,4,10,TFT_LIGHTGREY);
  tft.fillRect(x+5,y+24,4,10,TFT_LIGHTGREY);
  tft.fillRect(x-14,y+19,29,6,0xa800);
  tft.fillRoundRect(x+7,y-1,3,10,2,TFT_WHITE);
}

This chunk of code is responsible for drawing an LED with primitive shapes. While it may look like a mess of coordinates and numbers, it simply draws shapes to make up an LED – including the top plastic dome, the legs and the white reflection on the LED body.

While we haven’t looked into using it, there is also functionality within the eSPI_TFT library to draw sprites. This means that small images can be loaded onto the Wio which can then be displayed through code.

ADDITIONAL Parts Required:JaycarAltronicsPakronics
1 x 5mm Red LED*ZD1690Z0800ADA299
1 x 5mm Green LED*ZD0170Z0801ADA298
2 x 220Ω Resistors*RR0556R0542SS110990043
1 x Grove to Male Header Adapter*XC9192-SS110990210
1 x BreadboardPB8820P1002PAKR-A0066
Solid Core Wire / Jumper WiresWC6027P1014ASS110990049

ADDITIONAL Parts Required:

* Quantity shown, may be sold in packs.

Breadboard Circuit

Before we can play our Whack-a-LED game though, we need to build a simple circuit for two LEDs – one green LED for a ‘hit’ and one red LED for a ‘miss’. We used a small breadboard and a grove to four-pin header adapter. This can also be made yourself if you have a soldering iron and heatshrink handy!

We inserted the two green and red LEDs spaced about an inch and half between each other and added two 220Ω resistors.

The anode of each LED also needs to be connected to Ground (the blue line) and each cathode to the two data pins of the Grove connector. This means that by turning on pins D0 and D1, we can turn on the two status LEDs.

Finally, make sure that the Grove connector is plugged into the right port – the left port is for I2C accessories as we’ll be using in the next section.

And we’re done! Once the code is uploaded, enjoy our brand-new game Whack-a-LED! It’s actually pretty fun and the cartoon-style sprites look pretty good on the TFT display. Every time we miss a hole (or don’t hit it in time), the red LED lights up. If we’re successful, the green LED lights up. Awesome!

Project 3:

Mini Environment Data Logger

ADDITIONAL Parts Required:JaycarAltronicsPakronics
Multichannel Gas Sensor--SS101020820
DHT11 Temperature and Humidity Sensor--SS101020011
microSD CardXC4989DA0365ADA1294

ADDITIONAL Parts Required:

For our final project, we’re building a multi-channel data logger that can be switched between various data sources to log them to an SD card for future analysis. This could be imported into an application like Microsoft Excel later for graphing.

It’ll also display a time graph on the Wio Terminal. We’re sprinkling in some juicy data to analyse by using a DHT11 weather sensor and a four-channel gas sensor!

IMPORTANT NOTE

You may notice in our photos and experimentation that the gas concentrations sourced from the sensor unit have very high readings. The Carbon Monoxide readings, for example, were frequently reading above 200ppm – CO alarms are typically required to activate at around 70ppm. Unless there is something seriously wrong with our workshop, the gas readings are incorrect.

From our understanding, this is due to the fact that the gas sensor must preheat for some time before reasonably accurate measurements can be sourced. If the module has been in storage for some time, a preheat time of 72 hours is required according to the Seeed Wiki. Regardless, this sensor (and any other low-cost electronic module) should never be used as the sole detector device where there is concern about critical safety.

Graphing

While the Wio Terminal doesn’t include any inbuilt graphics libraries for graphing or data displays, there are a number of libraries available for this purpose. We’re using the Seeed Line Chart library for display of data.

To switch between each data display, we’re putting the directional pad on the Wio Terminal to allow the user to switch between data channels. Pressing right increments the current data view to the next sensor, while pressing left decrements. Recording the data to the SD card (discussed shortly) can also be started by pressing in the button, and saved by pressing it again.

SD Card

The Wio Terminal’s inbuilt microSD card slot is a great way to get data logging functionality into any project. Since we have direct access to the data being written to the SD card, any file format can be written provided we know its structure.

Recording data to the microSD card slot on the Wio Terminal is easier than you may expect. We’re going to be creating a CSV (Comma Separated Value) data format, which is a very simple system for writing and storing data readable by many spreadsheet software.

It simply consists of a header, comprised of the data ‘name’ separated by commas, followed by rows of the data in the same format. For example:

TIME, VALUE
0,15.23
1,144.32
2,14.87

There are three modes that can be used to interact with the microSD card – READ, WRITE and APPEND. READ simply puts the file into a read-only mode – perfect for reading images without modifying the content. WRITE allows the Wio Terminal to completely overwrite the file, while APPEND automatically continues writing to the file without overwriting old content. We’re using READ and WRITE in our code to automatically fill out the header and insert new data content.

Let’s get to implementing this within our Arduino code!

#include"seeed_line_chart.h" //include the library
#include <SPI.h>
#include <Seeed_FS.h>
#include "SD/Seeed_SD.h"
#include "DHT.h"
#include <Multichannel_Gas_GMXXX.h>
#include <Wire.h>

Above we’ve imported all the libraries required for this project. There isn’t a required order for this, but it’s still important that they are added otherwise the code won’t upload. You may also need to head to the Library Manager in the Arduino IDE to download these libraries if you haven’t got them installed.

TFT_eSPI tft;
File myFile;
#define max_size 50 //maximum size of data
#define DHTTYPE DHT11
#define DHTPIN 0
#define FILENAME "data.csv"
DHT dht(DHTPIN, DHTTYPE);
doubles data; //Initilising a doubles type to store data
TFT_eSprite spr = TFT_eSprite(&tft);  // Sprite 
GAS_GMXXX<TwoWire> gas;
int current_graph = 0;
int max_graphs = 6;
bool last_left = false;
bool last_right = false;
bool last_press = false;
bool sd_card_open = false;
short currentColor;

The above code is a bit of a big chunk, but all it does is declare and initialize the variables we’ll be using for our data logger. In the first half, we’re initializing the variables we need to run the sensors, and in the second half, some variables are created to help track the state of the data logger. Note that if you have more sensors to use, simply change the max_graphs variable to suit the number of data points you have. In our case, we have the four gas levels from the gas sensor, temperature and humidity – so six total.

void setup() {
    tft.begin();
    tft.setRotation(3);
    spr.createSprite(TFT_HEIGHT,TFT_WIDTH);
    gas.begin(Wire, 0x08); // use the hardware I2C
    
    Serial.begin(115200);
    dht.begin();
    pinMode(WIO_5S_LEFT, INPUT_PULLUP);
    pinMode(WIO_5S_RIGHT, INPUT_PULLUP);
    pinMode(WIO_5S_PRESS, INPUT_PULLUP);
    if (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI)) {
      Serial.println("initialization failed!");
      while (1);
    }
}

Nearly there! This is our setup function that, as usual, starts up the LCD screen. It also initializes the left, right and press (i.e. push in) assignments for the directional pad on the Wio Terminal. These can be used like standard GPIO the same way as the Whack-A-LED project.

Note that we’re skipping over some code for the ‘loop’ section below. There is quite a bit of code that controls the SD card reading and writing, if you’re interested in the specifics or just want to upload it directly to the Wio Terminal, you can find it in the project files section.

void loop() {
    handleButtons();
    spr.fillSprite(TFT_WHITE);
    if (data.size() == max_size) {
        data.pop();//this is used to remove the first read variable
    }
    int val;
  switch(current_graph) {
      case 0:
        val = gas.getGM102B();
        header.value("NO2 Gas (ppm)");
        currentColor = TFT_RED;
        break;
      case 1:
        //…. Other data points ….
    data.push(val); //read variables and store in data
    header.height(header.font_height() * 2);
    header.draw(); //Header height is the twice the height of the font
    /// (code omitted – initializes graph) 
    spr.pushSprite(0, 0);
    if(sd_card_open) {
      myFile.print((String)((float)millis()/1000));
      myFile.print(',');
      myFile.println(val);
      tft.setTextSize(2);
      tft.setTextColor(TFT_RED);  
      tft.drawString("Recording...", 100, 215);
    }
    delay(50);
}

And voila, we’re done! There is obviously a number of changes that could be made to this program depending on your requirements, but we feel this is a good start for a basic data logger with visual feedback in the form of a graph. You’ll need to plug the Multichannel Gas sensor into the left Grove port on the Wio Terminal, and the DHT11 weather sensor into the right port.

Once it’s uploaded, data should immediately appear to scroll across the display. To start or stop recording, just hit the centre button. If a different data stream is desired, move the directional pad left or right to switch data channels.

After you’ve stopped recording, just pop out the SD card in your computer and open the ‘data.csv’ file in any spreadsheet program. Graphs and analysis can be immediately done on the data!

Where To From Here?

With the time we’ve had experimenting with the Wio Terminal and its many compatible sensors, we’re confident that a vast variety of DIY projects can be accomplished with minimal effort. There isn’t a lot of hardware to configure, since the Wio Terminal may already have the module inbuilt, or a compatible grove module can be used with a single cable connection. It’s one of the first polished all-in-one development packages we’ve seen, convenient for both beginners and the experienced alike.

There are several inbuilt sensors and features within the Wio Terminal we didn’t get the time to try, including the inbuilt Infrared LED (to act as a TV remote), the buzzer, light sensor and the microphone.

We hope the projects we’ve shown here inspire you to make something cool with the Wio Terminal! It’s a little powerhouse that can be used with the vast majority of existing Arduino modules, so you’ll have no trouble mixing with electronics you already have. Show us your creations at @diyodemag!