Pebble watchface created during Rebble Hackathon #002.
This project has completely confiscated my life, darling. My best work, I must admit. Simple, elegant, yet bold. You will die.
App store:
I'm going to prepare in advance, like last time. Starting by setting up development environment. I'm not using premade VM. I prefer to have local setup. According to official Installing the Pebble SDK guide I'm supposed to follow article Writing a Pebble app in 2020. So I do.
But I'm already in trouble. I need Python2 and I can't install it the way it is done in the article. I'm running Debian 12 Bookworm now. This might be thought.
Ok, I have it. Here is what I did:
$ sudo ed /etc/apt/sources.list
a
deb http://archive.debian.org/debian/ stretch contrib main non-free
.
w
$ sudo apt update
$ sudo apt install python2.7
Then I completed the article with no problems. The pebble
command now works.
$ pebble --version
Pebble Tool v4.6-rc2 (active SDK: v4.3)
I created a "Hello World" watchface with white background layer, time and date printed using default fonts and setup for Clay config.
I remember Pebble development documentation being very helpful but
difficult to navigate. This is because it's divided into specific
sections and text search input does not work. So as usual it's just
better to grep the source code. I couldn't find the pebble.h
file
at first. I was searching in files downloaded when installing Pebble
SDK. But actually those are just tools that install the actual SDK.
By default Pebble SDK is places in ~/.pebble-sdk
and there I found
all header files.
Header files are nice but now we have full access to PebbleOS
source code. I can finally read implementations of pebble.h
API
functions which makes programming much easier. For example I can see
if passing a NULL pointer will cause trouble or it will be just
ignored. With that I don't have to add extra conditions.
Anyhow, I'm more than ready for Hackathon.
First day of hackathon. I will focus on design. Like last time I'm planning on testing ideas by making a simple watchface that display single BMP image. That way I can look at watchface on the watch and not only in Gimp which makes a huge difference.
Yes this time I'm not using Inkscape, we are not friends anymore </3
... done properly.
I was thinking about design for a month now. Initially I wanted to have 3 rows of big numbers. I liked the idea of 9 digits because I could display text PEBBLE, and 3 rows play very nice with the quick view popup as only the last bottom row disappear.
.-------. .-------. .-------.
| 1 2 | | P E | | 1 2 |
| 3 4 | | B B | |__3_4__|
| 5 6 | | L E | | popup |
'-------' '-------' '-------'
But no matter what I tried the design just didn't worked. The number where way to small, they where either stretched or a lot of the screen space was not used. This wasn't the look and feel I'm going for.
I want my font to feel bold and heavy. Then I realized that to make font feel bigger I need the contrast. I have to put big numbers next to very small font. With that the feeling of size will be emphasized.
It took me long time to figure out the pixel perfect grid that would fit 2 rows of big numbers that are build from repeated modular pieces of the same size, and with third row for smaller moonspace font that would fix perfectly from left to right and all margins between big and small font and screen borders are the same. So here is the recipe for my perfect grid:
- 5px margin from screen borders which is as minimal as it can be to avoid getting too close to screen rounded corners
- 5px of space between first, second and third rows of text
- 70px of height for first 2 rows
- 5x5px blocks are used to build big numbers
- 5px of space between big number lines
- 8px of height for last row
- 134px of width in each row which is perfect for small monospace font in third row making it possible to fix exactly 17 characters that are 6px wide and have 2px space between letters
It's perfect. I had similar design at some point that looked almost exactly the same. But spaces between number, margins and letters where very different. With this new grid fonts feel more solid. It's all about the feeling.
Don't think, feel! It is like a finger pointing away to the moon. Don't concentrate on the finger or you will miss all that heavenly glory. Do you understand? - Bruce Lee
I have first version of glyphs for both fonts. The main big font has only numbers and it's a proportional font. The small monospace font has uppercase letters, numbers and few special characters.
I'm not happy about few letters, mostly M
and W
but I want to do
some programming now.
Well, that was fast. I just implemented the most basic version of watchface using glyphs atlas. Both fonts works as expected.
Time for break. Here is a rough list of things to do next:
- Redesign number
0
, it's not heavy enough. - Settings, custom background and foreground color, data format etc.
- Background reflecting battery status.
- Refactor, use multiple layers, avoid numbers.
- Animations.
- Icon.
After using this watchface I realized that empty space on the left is problematic. It's there on purpose, give a space to breath and it's a result of decision to make main font non monospace. Proportional numbers are more readable. But I can't explain that to everyone who see this watchface. The design must defend itself and right now to regular user the left side looks strange. Everything is on the right side making it heavier. I need balance.
I tried to add some text using small font but even tho it's small it was too big for the little space that is on the left edge when clock shows time like 22:22. So I decided to try with another, even smaller font. And I love it. This is truly a challenge in typography. The tiny font still has some unique character and it's possible to read, but it's not there for everyday practical reasons. It's there to balance the design.
Now I'm satisfied with the composition. I can proceed to work on settings and refactor.
I've made few improvements to fonts glyphs. Code was heavily refactored. Watchface has now multiple layers. But most importantly I added configurations. User can choose colors, date formats and vibrations for Bluetooth disconnect event, Bluetooth connect event and on each hour.
Another addition is the second background color showing battery percent. I tried many design ideas. I rly liked my explorations of different background color just under the big numbers. But even tho I liked that in Gimp, it didn't look good at all on watch. So I ended up with most basic solution. I think that it looks excellent.
The battery level percent as background color was a short lived feature. No matter what color combination I tried I wasn't super happy about it. And the dithering on BW displays was messing with the small fonts. I tried to add background and border to those small fonts to solve this issue but then watchface looked super ugly.
With that, I decided to terminate this feature. But it will be back as text in either left or bottom text area.
I really like how the tiny font looks like. I took few better pictures of white on black color combination with custom texts.
It took a longer while but I made it. I had most done on day one. But as usual the finishing touches take longer than quick gains. Today I had to rewrite most of the code from bitmap rendering to framebuffer rendering to support dithering in my fonts. I needed that to support quick view (unobstructed / popup).
I also have all customisation in place. Custom content for side and bottom texts, colors and vibrations. I'm ready for firs release. I need screenshots for app store page.
First version is out. But this is not the end. There is a problem
with settings on Aplite pebbles. Nothing happens when user clicks the
submit button in Clay web form. I debugged this for hours but
found nothing. There is only a generic error log from pkjs
.
[11:23:40] pkjs> Failed to send config data!
[11:23:40] pkjs> {"data":{"transactionId":2}}
I found what print those logs in Clay source code. It's nothing special.
// Send settings to Pebble watchapp
Pebble.sendAppMessage(self.getSettings(e.response), function() {
console.log('Sent config data to Pebble');
}, function(error) {
console.log('Failed to send config data!');
console.log(JSON.stringify(error));
});
I tried to find implementation of Pebble.sendAppMessage
. I searched
Pebble SDK and Pebble OS. Nothing. Then I thought that there is
something strange in my Clay configuration. But after reducing it to
just a single toggle button and submit button I had the same results.
It works on all targets except for Aplite. Last thing that I did was
to search for few very popular watchfaces that use Clay. I read their
source code and found nothing. It's all the same.
At this point I don't know what else I could try. On Discord someone asked me about my Pebble build tools suggesting that this can make a difference. OFC it can. So maybe the last thing that I can try is to use some of the prepared dev environments like VM. I would hate to do that tho. I can also try to compile someones watchfaces and test them to verify my local setup. Eh...
Second "problem" is that the Brutal watchface don't support Diorite target, the Pebble Round. I rly tried to do a design for it earlier. But the main numbers don't even fit the screen. But 2 people already asked me about it. So I decided to give it a good second try. Now I have a complete design language. I could use tricks from Quick View layout in Pebble Round. We will see.
Last smallest issue is with the App Store images. They are not up to date and don't demonstrate watchface as well as they could. I will fix that first because I know how to do it.
BTW I liked the dithering pattern visible in Quick View so much that I found a way to include it in main view. I called it the "Shadow"! It's totally optional and it's strength can be adjusted.
In last devlog I talked about an issue with configuration page in Aplite platform. One might think that somewhere there was just a simple mistake that was difficult to spot. But not this time. This time I was going to suffer hours of debugging and another hours of rewriting font rendering ... again (-_- ).
I debugged this in so many way. I even got help from @NiVZ user. He helped confirm that there was nothing wrong with my local build tools. So eventually I created new super simple watchface with basic Clay configuration. And it worked with Aplite. So I kept adding code from Brutal source to that debug project until Clay configuration it was broken. Eventually I found it.
It was the resource bitmap image with font glyphs. It was just too fat. Took to much space. So now I was on quest to reduce size of it.
This is what I had at that time, the 256 x 256 px 1-bit bitmap image:
At first I thought that maybe it's enough to remove empty space and pack glyphs as tight as possible. I did that but it was not enough. Basic observation shown that big font is the problem. Single glyph from that font takes as much space as it is needed for all glyphs from small and tiny fonts together. But how can I reduce size of that font? I can't just compress it and then decompress later as it will still take the same heap memory. I also can't use vectors for it because it will not work with my custom dithering. What to do?
The big font is very modular. It's build from 5x5 px blocks that are mostly the same: full block, empty block and one diagonal with 3 orientations. Initially I wanted to encode information about those 3 blocks. But closer inspection shown that encoding itself would take almost the same space as current regular bitmap that is stored in 1-bit pixel format. And then I saw it. This font can be stored in bitmap as it was but 5 times smaller. Then when rendering I just have to scale it up 5 times. 45 degree edges looked tricky but information about where those edges are is still in this miniature version. I stored small tiles for each of those 45 degree edges and repainted them when glyphs are scaled.
This is how bitmap image looked after drawing big font 5 times smaller and rearranging glyphs to not waste any space:
Tiles for 45 deg edges are next to big number 4 and one full tile below small digit 9. With just those the big font is restored. I still have to reserve runtime memory that will hold one scaled up glyph from big font but it's not nearly as expensive as the original bitmap.
And OFC this is more heavy on CPU but still, thanks to all of that I was able to fix memory issue in Aplite and thanks to that the Clay configuration works.
No I can focus on some easy parts like battery charge percent. This
is interesting as I want to have it as flexible as possible. So it
would be the best to just extend syntax of Date format. Looks like
the strftime() already tool all alphabet letters. So I will use
different prefix. I'm going for #
as it is not even displayed in
the tiny font. It still can be used when it's fallowed by lowercase
letter. And fonts have only uppercase letters so it allows user to
use #
if they want in the bottom text.
#bottom_text -> #bottom_text
#Bottom_text -> 80ottom_text
Battery: #B -> Battery: 85
Battery: #B%% -> Battery: 85%
I can now add more custom values. I did the same thing with daily steps counter. I can add more like heart rate monitor but I think simple weather stuff would be better. Basic temperature would be great to have. I have never done this before. From my experience of using other watchfaces it's usually not so great. I could never trust it and I was always double checking the weather with second source. That's why I'm not convinced.
The right thing to do now would be to adapt the design to Pebble Time Round. I tried this before few times. Most recently 2 days ago. It's very difficult. Sure you can fit all numbers and text on round display. But it looks terrible. Too many sharp corners. And big font is build from 5x5 px blocks making it impossible to center. After doing some research I found that most watchfaces just center everything which makes sense. Fever designs tries to not do that. Looks like I'm forced to be creative again.