Pine64’s PineTime Smart Watch with charging cradle

Sneak Peek of PineTime Smart Watch… And why it’s perfect for teaching IoT

UPDATE: This code in this article has been archived in the pre-lvgl branch of pinetime-rust-mynewt. The pinetime-rust-mynewt firmware has been revamped to support Rust Watch Faces on LVGL. Check out the updates

I’m one of the lucky few who received the developer preview version of the PineTime Smart Watch by Pine64. (My official mission: Port Apache Mynewt OS and Embedded Rust to PineTime… Check out the Pine64 Store)

After coding on PineTime for a few days (in Rust), it suddenly struck me…

  • I felt really comfortable and productive coding the new gadget. Even though I have never coded a smart watch.
  • PineTime is a terrific tool for teaching IoT! And I have taught IoT with so many different gadgets.

Lemme elaborate…

Nordic nRF52832 Microcontroller inside PineTime (the big black square at top)

1️⃣ PineTime is the spiritual successor to BBC micro:bit (the educational gadget)!

Yes it’s blasphemy but if you think about it… BBC micro:bit is really a Nordic nRF51 in an oversized form factor that runs an odd 2.4 GHz wireless protocol. With PineTime we get a supercharged nRF52 and proper Bluetooth support, including Bluetooth Mesh! Imagine a classroom full of meshed PineTime gadgets…

2️⃣ PineTime has a touchscreen!

Millennials expect no less than a touchscreen for learning IoT, since they are used to touchy feely gadgets like Apple Watch and FitBit. How would you explain to a millennial how a touch controller works? Show them a PineTime! (Few of us have actually programmed a touch controller… Here’s the perfect opportunity!)

PineTime powered by its built-in lithium battery (ST-Link USB Programmer at the right)

3️⃣ PineTime has realistic battery life constraints!

I would give every IoT student a PineTime gadget… “Build me a watch face that has the longest battery life!” For the first time ever, we have a simple way to teach the real-world impact of firmware programming on battery life. And there are many options to explore: polling vs callbacks, coroutines vs multitasking, RTOS vs no RTOS, Deep Sleep vs Light Nap, WFI vs WFE, point-to-point vs mesh networks, …

4️⃣ PineTime reminds us that IoT can save lives!

I suffer from Severe Hypertension… I can really drop dead at any moment (probably because of Rust!) I discovered this by chance… I really wished my watch could tell me much earlier. PineTime has a simple Heart Rate Sensor, but it’s a good way to remind our future makers that they have the power to create a gadget that saves our lives.

[Watch the video on YouTube]

Coding and debugging PineTime with open-source Visual Studio Code

5️⃣ PineTime is powered by open source!

Coding with PineTime feels exactly like coding on STM32 Blue Pill… Just grab the $20 watch, build a simple SWD cradle (fun creativity exercise!), connect a $2 ST-Link dongle and hack away with the exact same stack of open source tools I used for STM32! VSCode, Cortex Debugger, GCC for Arm, Embedded Rust, OpenOCD, Apache Mynewt OS, NimBLE Bluetooth stack, … No better way to immerse our future makers into the world of Open Source Collaboration!

🛈 What is VSCode? Is it related to Visual Studio? How is Microsoft involved? Read this

6️⃣ PineTime was created for Embedded Rust!

With 512 KB of Flash ROM (built-in) and 64 KB of RAM, PineTime will handle most Rust Embedded crates just fine. (MicroPython and Embedded JavaScript may be tough to squeeze in.) Just watch how the embedded-graphics crate renders text and graphics so easily on PineTime! Rust on PineTime will create the same educational impact that graphical Logo programming gave us so many years ago. More about Rust in a while…

Gently prying open the PineTime Smart Watch with tweezers… Or “forceps” if you’re medically trained like me

7️⃣ PineTime was designed for millennials!

Some parts inside PineTime are so tiny that they hurt my eyes… But I bet the millennials will love it. It’s a gadget that fits naturally into delicate millennial hands… And begs to be pried open (gently) for deeper inspection. (Compare PineTime with the geriatric Arduino Uno and oversized BBC micro:bit)

EBYTE E73-TBB Development Boards with onboard Nordic nRF52 Microcontroller. From https://lupyuen.github.io/articles/bluetooth-mesh-with-nrf52-and-apache-mynewt

8️⃣ PineTime begins a lifelong IoT learning journey…

PineTime has a number of interesting built-in sensors and actuators (on I2C and SPI) to keep learners busy for a while: Colour Touchscreen, Heart Rate Sensor, Accelerometer, Vibrator, Power Management, Bluetooth, … But what if they wish to explore more?

Then just pick up one of the many affordable nRF52 development boards! They will be so happy to see the same code and the same suite of open source tools running on any nRF52 board. (Unlike the Arduino Uno and BBC micro:bit, which turned out to be disappointing deadends)

Here’s my plea to Pine64 (I hope others will join me)… Please keep PineTime open and hackable… For the sake of IoT Education!

Proposed PineTime Smart Watch Firmware

PineTime is extremely hackable (in its current raw form), so you’re free to run any RTOS (FreeRTOS, Zephyr, Mynewt, mbed)… Or none at all (Bare Metal Embedded Rust)!

But as an uptight, severely hypertensive former enterprise architect… I expect no less than this firmware stack (even if it means stacking the kueh lapis by myself!) Because it makes teaching IoT more modular and less crash-prone…

Proposed Firmware Stack for PineTime Smart Watch. Rust components are coloured brown.

The PineTime Smart Watch Application is coded in Rust… Because Rust has memory safety features that prevent the application from crashing due to bad pointers. And yet it compiles into Arm machine code, running as efficiently as C. (Given a choice to teach Embedded C or Embedded Rust, I would most certainly choose Rust, because it’s modern and friendlier!)

The Rust Application has three jobs…

1️⃣ Render the user interface text and graphics to the LCD display

2️⃣ Respond to touch events that are generated whenever we touch the touchscreen

3️⃣ Receive and transmit sensor data over Bluetooth

Here’s the main() function implemented in the Rust Application…

PineTime’s main() function in Rust. From https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/lib.rs#L52-L94

Some of the blocks are still missing (like the touchscreen driver), so if you’re keen to help out, drop me a note!

Let’s inspect the three flows in detail…

Render User Interface on PineTime Smart Watch

A Smart Watch Application needs a decent user interface with text and graphics. Coding that in low-level C would be tedious. Fortunately we got Rust and the embedded-graphics crate to help us. The embedded-graphics crate makes it really easy to render raster fonts and various shapes with a few simple lines of Rust code.

Render text and graphics to PineTime display with Rust. From https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/display.rs#L33-L80

The embedded-graphics crate is designed to render its bitmap directly into a Display Controller Driver that complies with the Rust Embedded Framework (which uses specific types and constructs to access bare-metal hardware on a microcontroller).

We’re now using the standard st7735_lcd Rust Embedded Driver. It needs some tweaking to make it 100% compatible with PineTime’s ST7789 display controller… But for now it seems to be rendering the display fairly well without any modification.

The st7735_lcd driver assumes that the Rust Embedded HAL (Hardware Adaptation Layer) is available for our microcontroller… That’s how the driver accesses the SPI port and GPIO pins connected to the display controller. But we can’t allow Rust to access our hardware directly… Everything must go through Mynewt OS!

The solution is simple… We created a thin Rust Embedded HAL that maps to the Mynewt APIs for SPI and GPIO. And it works!

Rust Embedded HAL for PineTime on Mynewt OS. From https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/mynewt_hal.rs

Now we have a working demo of the screen rendering controlled by Rust. The colours seem OK even though we are using a ST7735 driver for PineTime’s ST7789 display controller.

Respond to Touch Events on PineTime Smart Watch

We now have a simple way to render watch faces in Rust… Yay! But a Smart Watch isn’t really Smart unless it can respond to our tapping and prodding on the touchscreen. And it gets complicated because of “Interrupts” or “中断” in Chinese (learnt that from the Hynitron docs).

How do we check if the screen has been touched? We could poll the Touch Controller over I2C every second. But that would consume too much power. And it would feel laggy.

The right solution is to monitor for Interrupts generated by the Touch Controller. The Touch Controller switches the Interrupt Pin (just a regular GPIO Pin) into the Low state whenever a touch is detected.

Interrupt Pin (IRQ) goes low on just one touch. From http://www.hynitron.com/upload/1408075960.pdf

Mynewt OS provides a GPIO API that transforms this Hardware Interrupt into a Software Event that we may handle in our C code. That’s how we create a Touch Controller Driver that responds to touch.

Calling the Mynewt API to transform a Hardware Interrupt into a Software Event. From https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/touch_sensor.rs

How shall we escalate the Touch Event up through the firmware layers to the Rust Application (so that it can update the display)?

Mynewt OS provides a Sensor Framework enables sensors to raise events to the application. So a Temperature Sensor could use this Sensor Framework to alert the application of any variations in temperature. We shall use Mynewt’s Sensor Framework to escalate Touch Events the same way.

This works only when we have a Mynewt Sensor Driver for our Touch Controller… Which I’m still coding in C. I have coded similar drivers previously… Here’s my Mynewt Sensor Driver for the Nordic low-power transceiver nRF24L01 that watches for the Incoming Packet Interrupt and transforms it into a Sensor Data Event for the application: github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/master/libs/nrf24l01

Handling a Touch Sensor Event with Mynewt and Rust should look like this: github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/app_sensor.rs

Handling a Touch Sensor Event with Mynewt and Rust should be similar to this Sensor Listener code. From github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/app_sensor.rs

The Touch Sensor Driver is now being implemented… As I figure out how the Hynitron CST816S I2C commands work. Find out more in this article…

Receive and Transmit Sensor Data on PineTime Smart Watch

What about other sensors on the PineTime… the Accelerometer and the Heart Rate Sensor?

Our Rust Application can poll them via the Mynewt Sensor Framework. Mynewt provides a Sensor Driver in C for the BMA2xx Accelerometers that we may customise to derive the BMA421 Sensor Driver for PineTime. Mynewt doesn’t have a Heart Rate Sensor Driver but it should be straightforward to create a new one, like the GPS Sensor Driver that I have created: github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/pinetime/libs/gps_l70r

What shall we do with the polled Sensor Data?

For some types of Sensor Data we may choose to transmit them over the Bluetooth network that’s supported by the PineTime hardware. The Nordic nRF52 microcontroller includes a 2.4 GHz transceiver radio that may be programmed with a Bluetooth software stack so that the PineTime behaves like a standard Bluetooth gadget.

Most nRF52 developers would probably use Nordic SoftDevice. This is the standard firmware provided by Nordic Semiconductor that implements the Bluetooth LE functions.

Nordic SoftDevice Architecture. From https://infocenter.nordicsemi.com/topic/struct_nrf52/struct/nrf52_softdevices.html

The firmware runs as a base system layer underneath our application code and RTOS.

SoftDevice reserves some hardware resources for itself, like the radio transceiver, some timers and some ROM+RAM.

The remaining resources would be available for our application, which would call the SoftDevice API to perform Bluetooth LE functions and receive notifications.

What if we wish to experiment with the Bluetooth LE implementation… Trace it to see how it works, tweak it to improve it, or even roll out a new Bluetooth LE protocol?

SoftDevice is clearly not meant for experimentation… Apache NimBLE is perfect for that! Apache NimBLE is an open-source Bluetooth LE stack that completely replaces SoftDevice. It’s designed to run with the Apache Mynewt embedded OS, so NimBLE feels like a typical Mynewt task.

Apache NimBLE is the Bluetooth LE implementation that we’re adopting for PineTime Smart Watch.

Our PineTime code now includes a fully-functional iBeacon Transmitter, to verify that our PineTime works wirelessly indeed. Let’s probe deeper to uncover the full power of PineTime wireless networking…

PineTime running as an iBeacon. Check this article for iBeacon testing instructions: https://lupyuen.github.io/articles/coding-nrf52-with-rust-and-apache-mynewt-on-visual-studio-code

Bluetooth Mesh and other networking options for PineTime Smart Watch

I’m particularly excited that PineTime supports Bluetooth Mesh networking. Suppose I’m out at Taipei’s crowded Shihlin Night Market 士林夜市 with my family (and my dog)… I need to make sure that we all stay close. My son may not always be close to me, but as long as he’s near Grandma or his siblings, I’m happy.

Bluetooth Mesh of PineTime Smart Watches to make sure that the family stays together (even the dog)

Assuming everyone wears a PineTime Smart Watch (even my dog), all we need to implement this proximity safety app is a Bluetooth Mesh. It allows messages to be forwarded from gadget to gadget, peer to peer. And if one of the gadgets has internet access, the messages may be transmitted to the cloud as well. Here’s an example of a Mynewt network driver that routes local network messages (from nRF24L01) to the cloud (via ESP8266)

I have written a tutorial on Bluetooth Mesh networking with nRF52 and the NimBLE stack. It’s easy to port this to the PineTime Smart Watch. Here’s my question for you…

What kind of networking would you need on the PineTime Smart Watch? Would you need Localised Messaging, Cloud Messaging, or both?

I’ll design the NimBLE stack and the Rust messaging API accordingly when I hear from you!

Programming the PineTime Smart Watch

PineTime is now available for purchase by general public! The source code may be found in this repository, which includes prebuilt firmware image for PineTime…

Check this article for updated instructions to build and flash PineTime firmware…

Build SWD Cradle

To program the PineTime Smart Watch via SWD (Serial Wire Debug), we need to construct a sturdy cradle that exposes the four pins of the SWD port. Make sure that the watch button and the touchscreen are accessible.

I created my SWD cradle with Single Core Wire (22 AWG), $2 clear box cover from Daiso, Blu Tack and sticky tape.

SWD Cradle constructed from Single Core Wire (22 AWG), $2 clear box cover from Daiso, Blu Tack and sticky tape

Also connect the 5V Charging Pad on PineTime to the 5V Pin of ST-Link. This ensures sufficient power to all components on PineTime. And the battery gets charged properly too.

I used a folded loop of Single Core Wire (22 AWG) like this…

Connecting the 5V Charging Pad on PineTime to the 5V Pin of ST-Link with a folded loop of Single Core Wire (22 AWG)

Remove nRF52 Flash Protection

We’ll be flashing and debugging PineTime with a $2 generic ST-Link V2 USB dongle. ST-Link doesn’t implement all SWD functions, just the minimal set of high-level functions needed for flashing and debugging. PineTime is shipped with its flash ROM protected against tampering, and the ST-Link can’t be used to remove the nRF52 Flash Protection.

Fortunately there’s a way to remove the flash protection with a Raspberry Pi. (This only needs to be done once.) Just follow the instructions in the section “Advanced Topic: Remove nRF52 Flash Protection” at the end of the article Coding nRF52 with Rust and Apache Mynewt on Visual Studio Code

The RESET pin is not needed when connecting PineTime to Raspberry Pi.

Removing PineTime flash protection with a Raspberry Pi

Install Source Code and Development Tools

Follow the instructions in the article Install Apache Mynewt and Embedded Rust for nRF52 and Visual Studio Code on Windows and macOS”.

Stop just before the section “Build The Firmware”

At the lower left corner of Visual Studio Code, click the name of the branch and checkout the branch origin/pinetime.

This upgrades the downloaded code to the new PineTime source code.

In the PineTime branch, the Mynewt OS Board Support Package has been updated with the correct pins used for the SPI and I2C ports.

Pins used for SPI and I2C ports on PineTime. From https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/hw/bsp/nrf52/syscfg.yml#L36-L50

Connect ST-Link to SWD Port

Connect the four pins of the PineTime SWD Port to ST-Link:
SWCLK, SWDIO, 3.3V, GND

Connect ST-Link to the USB port of your computer.

UPDATE: Connect the 5V pin to charge the battery as well

ST-Link connected to PineTime’s SWD Port

Build, Flash and Debug Firmware

Refer to the instructions in the article Install Apache Mynewt and Embedded Rust for nRF52 and Visual Studio Code on Windows and macOS

Continue from the section “Build The Firmware” till the end of the article.

We should be able to build, flash and debug the PineTime firmware like this…

[Watch on YouTube]

Build, flash and debug PineTime firmware with Visual Studio Code

References

Chat with the PineTime Community and me (lupyuen#4063) on Discord!

Mynewt + Rust port of PineTime firmware (work in progress) may be found in this repository…

TinyGo version of PineTime firmware…

Zephyr OS version…

FreeRTOS version…

RIOT-OS version…

I streamed a Live Debugging session for RIOT-OS on PineTime, without any prior knowledge of RIOT-OS. Here are my findings…