Software Development Environment
There are a few software development choices supported by Seeed: the Arduino IDE and a couple of MicroPython variants - Adafruit CircuitPython and Seeed ArduPy. I've been using a different MicroPython variant, Sipeed MaixPy, to program RISC-V K210 boards and CircuitPython to interactively control sensors via the MCP2221, so I considered using MicroPython. CircuitPython is nice because it has many Adafruit sensor libraries and ArduPy is interesting because it combines MicroPython and the Arduino API. Using ArduPy is supposed to give you access to all of your current Arduino libraries although you need to write and compile C++ wrappers for any libraries that are not included in the base Arduino build. In the end I decided to stick with the standard Arduino IDE mainly because it is familiar and because the Seeed Grove Ecosystem has a complete set of Arduino sensor libraries.
Sensor Connection and setup
LCD 320x240 Display
The LCD display uses one of the 3 internal SPI interfaces (the other 2 are used for the WiFi and the SD card). There are touch control lines shown on the schematic but this might be for a future version upgrade as the current display does not have touch capability. The pin used to drive the backlight control FET is also PWM capable but a third party library is needed to drive it.
The libraries required to interface the LCD are the Seeed_Arduino_LCD library (TFT_eSPI) and the Adafruit_Zero_DMA library. These are included in the Seeed SAMD board package.
TSL2591 Lux Sensor
I am using the I2C1 bus - one of the 2 dedicated I2C interfaces. I2C1 is available on the bottom left Grove connector of the Wio Terminal and the bottom right connector of the battery chassis in addition to the 40 pin RPi connector. I am using the bottom left Grove connector for the light sensor. The Adafruit sensor breakout that I used is an obsolete version that uses a 0.1" header pattern for the pinout. The new ones have a Stemma QT (QWIC) connector. I have adapter cables from Stemma QT to Grove but in this case I just cut a Grove cable in half and wired it in directly. I used the other half of the cable to connect the NeoPixel Stick. I used the Adafruit Arduino library with the default I2C address of 0x29.
MS8607 Temperature, Humidity and Pressure Sensor
I'm using the second I2C1 Grove interface on the bottom right of the battery chassis for this sensor. Since this is a Grove module the connection was with a standard Grove cable. This sensor breakout is made by TEConnectivity and uses a MEMS sensor that is factory calibrated. I used the Adafruit Arduino library which is a version of the TEConnectivity library modified for the Arduino IDE. I was somewhat surprised that the sensor uses two I2C addresses - 0x76 for pressure/temperature and 0x40 for humidity.
NeoPixel Stick 8 x 5050
I am using Digital Pin D2 on the Grove connector on the upper right side of the battery chassis to drive the NeoPixel Stick. This stick uses RGB LEDs rather than the 4 color RGBW LEDs. Each LED is individually addressable and all LEDs are connected sequentially on a single wire interface, so I only needed one of the two signal pins on the Grove interface. I wired the board using the other half of the Grove cable that I cut for the light sensor interface. I used the Adafruit NeoPixel library to program it.
Interactive Sensor Program
So, I started with a program to use the 3 sensors together with the display.
- Display Temperature, humidity and pressure measurements from the MS8607
- Display Ambient light readings from the TSL2591
- Change the number and color of the LEDs on the NeoPixel Stick based on the temperature measurement
- Change the brightness of the LEDs based on the ambient light measurement
Wio_Terminal_MS8607_TSL2591_NeoPixel.ino
// Example testing sketch for various sensors on Wio Terminal // Written by ralphjy, public domain #include <Wire.h> #include <Adafruit_MS8607.h> #include <Adafruit_Sensor.h> #include <TFT_eSPI.h> // Hardware-specific library #include <Adafruit_NeoPixel.h> #include "Adafruit_TSL2591.h" #define LIGHTPIN A6 #define NEOPIXELPIN D2 #define NUMPIXELS 8 Adafruit_MS8607 ms8607; Adafruit_TSL2591 tsl = Adafruit_TSL2591(2591); // pass in a number for the sensor identifier (for your use later) TFT_eSPI tft = TFT_eSPI(); // Invoke custom library // Parameter 1 = number of pixels in strip // Parameter 2 = Arduino pin number (most are valid) // Parameter 3 = pixel type flags, add together as needed: // NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) // NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) // NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products) // NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2) // NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products) Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, NEOPIXELPIN, NEO_GRB + NEO_KHZ800); #if defined(ARDUINO_ARCH_AVR) #define debug Serial #elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) #define debug SerialUSB #else #define debug Serial #endif int ledBrightness = 50; void setup() { debug.begin(115200); debug.println("MS8607 test!"); pinMode(LIGHTPIN, INPUT); tft.init(); tft.setRotation(3); tft.fillScreen(TFT_BLACK); tft.setTextSize(1); tft.setTextColor(TFT_WHITE, TFT_BLACK); // Adding a background color erases previous text automatically // Try to initialize! if (!ms8607.begin()) { Serial.println("Failed to find MS8607 chip"); while (1) { delay(10); } } Serial.println("MS8607 Found!"); if (tsl.begin()) { Serial.println(F("Found a TSL2591 sensor")); } else { Serial.println(F("No sensor found ... check your wiring?")); while (1); } /* Display some basic information on this sensor */ displaySensorDetails(); /* Configure the sensor */ configureSensor(); strip.begin(); strip.setBrightness(ledBrightness); strip.show(); // Initialize all pixels to 'off' tft.setTextSize(2); tft.setTextDatum(TC_DATUM); tft.drawString("MS8607 Readings",160, 15, 1); tft.setTextDatum(TL_DATUM); } int ledNum = 0; int ledNumPrevious = 0; void loop() { float tempC, tempF, humid, press; sensors_event_t temp, pressure, humidity; ms8607.getEvent(&pressure, &temp, &humidity); Serial.print("Temperature: ");Serial.print(temp.temperature); Serial.println(" degrees C"); Serial.print("Pressure: ");Serial.print(pressure.pressure); Serial.println(" hPa"); Serial.print("Humidity: ");Serial.print(humidity.relative_humidity); Serial.println(" %rH"); Serial.println(""); tempC = temp.temperature; tempF = tempC * 9 / 5 + 32; humid = humidity.relative_humidity; press = pressure.pressure; uint16_t lightValue = tsl.getLuminosity(TSL2591_VISIBLE); int lightValueTrunc; if (lightValue > 1024) { lightValueTrunc = 1024; } else if (lightValue < 256) { lightValueTrunc = 256; } else { lightValueTrunc = (int) lightValue; } ledBrightness = lightValueTrunc / 32; if (tempC) { debug.print("Humidity: "); debug.print(humid); debug.print(" %\t"); debug.print("Temperature: "); debug.print(tempC); debug.println(" *C"); } else { debug.println("Failed to get temperature and humidity value."); } tft.setTextSize(2); tft.drawString("Temperature: ",20, 55, 1); tft.drawString("Humidity: ", 20, 100, 1); tft.drawString("Pressure: ", 20, 145, 1); tft.drawString("Ambient light: ", 20, 190, 1); tft.setTextDatum(TR_DATUM); tft.setTextPadding(tft.textWidth("999", 1)); tft.drawNumber(tempF, 255, 55, 1); tft.drawNumber(humid, 255, 100, 1); tft.setTextPadding(tft.textWidth("99999", 1)); tft.drawNumber(press, 255, 145, 1); tft.drawNumber(lightValue, 255, 190, 1); tft.setTextPadding(0); tft.setTextDatum(TL_DATUM); tft.drawString("F", 265, 55, 1); tft.drawString("%rH", 265, 100, 1); tft.drawString("hPa", 265, 145, 1); ledNum = (int) (tempF/10) - 3; if (ledNum <= 1){ ledNum = 1; } if (ledNum >= 8) { ledNum = 8; } if (ledNum != ledNumPrevious){ strip.clear(); } switch (ledNum) { case 1: strip.fill(strip.Color(0, 0, 255), 0, 1); break; case 2: strip.fill(strip.Color(0, 0, 255), 0, 2); break; case 3: strip.fill(strip.Color(0, 0, 255), 0, 3); break; case 4: strip.fill(strip.Color(0, 255, 0), 0, 4); break; case 5: strip.fill(strip.Color(0, 255, 0), 0, 5); break; case 6: strip.fill(strip.Color(255, 0, 0), 0, 6); break; case 7: strip.fill(strip.Color(255, 0, 0), 0, 7); break; case 8: strip.fill(strip.Color(255, 0, 0), 0, 8); break; default: strip.clear(); } strip.setBrightness(ledBrightness); strip.show(); delay(1500); } /**************************************************************************/ /* Displays some basic information on this sensor from the unified sensor API sensor_t type (see Adafruit_Sensor for more information) */ /**************************************************************************/ void displaySensorDetails(void) { sensor_t sensor; tsl.getSensor(&sensor); Serial.println(F("------------------------------------")); Serial.print (F("Sensor: ")); Serial.println(sensor.name); Serial.print (F("Driver Ver: ")); Serial.println(sensor.version); Serial.print (F("Unique ID: ")); Serial.println(sensor.sensor_id); Serial.print (F("Max Value: ")); Serial.print(sensor.max_value); Serial.println(F(" lux")); Serial.print (F("Min Value: ")); Serial.print(sensor.min_value); Serial.println(F(" lux")); Serial.print (F("Resolution: ")); Serial.print(sensor.resolution, 4); Serial.println(F(" lux")); Serial.println(F("------------------------------------")); Serial.println(F("")); delay(500); } /**************************************************************************/ /* Configures the gain and integration time for the TSL2591 */ /**************************************************************************/ void configureSensor(void) { // You can change the gain on the fly, to adapt to brighter/dimmer light situations //tsl.setGain(TSL2591_GAIN_LOW); // 1x gain (bright light) tsl.setGain(TSL2591_GAIN_MED); // 25x gain //tsl.setGain(TSL2591_GAIN_HIGH); // 428x gain // Changing the integration time gives you a longer time over which to sense light // longer timelines are slower, but are good in very low light situtations! //tsl.setTiming(TSL2591_INTEGRATIONTIME_100MS); // shortest integration time (bright light) // tsl.setTiming(TSL2591_INTEGRATIONTIME_200MS); tsl.setTiming(TSL2591_INTEGRATIONTIME_300MS); // tsl.setTiming(TSL2591_INTEGRATIONTIME_400MS); // tsl.setTiming(TSL2591_INTEGRATIONTIME_500MS); // tsl.setTiming(TSL2591_INTEGRATIONTIME_600MS); // longest integration time (dim light) /* Display the gain and integration time for reference sake */ Serial.println(F("------------------------------------")); Serial.print (F("Gain: ")); tsl2591Gain_t gain = tsl.getGain(); switch(gain) { case TSL2591_GAIN_LOW: Serial.println(F("1x (Low)")); break; case TSL2591_GAIN_MED: Serial.println(F("25x (Medium)")); break; case TSL2591_GAIN_HIGH: Serial.println(F("428x (High)")); break; case TSL2591_GAIN_MAX: Serial.println(F("9876x (Max)")); break; } Serial.print (F("Timing: ")); Serial.print((tsl.getTiming() + 1) * 100, DEC); Serial.println(F(" ms")); Serial.println(F("------------------------------------")); Serial.println(F("")); }
Sensor hookup to the Wio Terminal (running on battery in this picture)
Close-up of the sensor measurements on the LCD Display
Here is a quick video of the sensor interaction.
Color pattern (each LED represents 10 degrees F from 40 to 120 degrees F)
- Blue - below 70 degrees F
- Green - greater than or equal to 70 and less than 90
- Red - above 90 degrees F
Next challenge is to get keyword spotting working using TinyML.