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.
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.
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!
Rendering text and graphics on PineTime with a few lines of Rust code. From https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/display.rs
6️⃣ PineTime was created for Embedded Rust!
KB of Flash ROM (built-in) and 64 KB of RAM, PineTime will handle most Rust Embedded crates just
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
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
Render User Interface Flow. Rust components are coloured brown.
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
crate makes it really easy to render raster fonts and various shapes with a few simple lines of Rust
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
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).
The Rust code above renders a circle and a line of text: “I AM A RUSTY BEACON”
using the standard
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
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
Respond to Touch Events Flow. Rust components are coloured brown.
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.
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:
a Touch Sensor Event with Mynewt and Rust should look like this:
Handling a Touch Sensor Event with Mynewt and
Rust should be similar to this Sensor Listener code. From
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
Receive and Transmit Sensor Data Flow. Rust components are coloured brown.
What about other sensors on the PineTime… the Accelerometer and the Heart Rate Sensor?
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:
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”
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”
lower left corner of Visual Studio Code, click the name of the branch and checkout the branch
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
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…
Build, flash and debug PineTime firmware with Visual Studio Code
PineTime firmware is still in active development by the open-source community (myself included). I’ll keep this article updated. Here are the other articles in the PineTime series…
If you have ideas how we can make PineTime more newbie-friendly for teaching and learning IoT, drop me a note!
Also check out my article… Do you think Visual Rust is suitable for PineTime? “Visual Embedded Rust Programming with Visual Studio Code”
Mynewt + Rust port of PineTime firmware (work in progress) may be found in this repository…
TinyGo version of PineTime firmware…
Zephyr OS version…
I streamed a Live Debugging session for RIOT-OS on PineTime, without any prior knowledge of RIOT-OS. Here are my findings…
Debug RIOT-OS on PineTime with VSCode
RIOT-OS with Rust…
Rust RTFM versions…
Nordic nRF52 Microcontroller
Hynitron CST816S Touch Controller
TianYiHeXin HRS3300 PPG Heart Rate Sensor
- HRS3300 PPG Heart Rate Sensor Design Guide in Chinese
- Reference Code for HRS3300 PPG Heart Rate Sensor
- Arduino Driver for HRS3300 PPG Heart Rate Sensor
Bosch BMA421 Accelerometer
- Datasheet for Bosch BMA423 Accelerometer (should be similar to BMA421)
Sitronix ST7789 LCD Display Controller
- Arduino Driver for ST7735 and ST7789 by Adafruit (to identify differences between ST7735 and ST7789)