Thanks! Haha! Boyā¦ Skip to the end if you donāt want to read this book.
The only reason Iām even here is because I bought a pair of those air quality monitors from China many years ago that was basically a PMS5003 with a small board/LCD mounted in a clear acrylic sandwich. I looked up the sensor and thought I could someday interface to it directly and get that data on the network and logged to my home automation system. I never got around to it. I never had the time. Recently, with COVID and always being indoors, I started to get concerned about CO2 levels and so I started to think more seriously about it. I never looked into Arduino before and thought I was going to use a Pi Pico W or something. Well, I had a friend who just happened to mention to me in an e-mail that he got an Air Gradient and upgrade kit. I looked it up.: open source, off-the-shelf sensors, etcā¦ it was exactly what I was looking to implement! Plus, I really liked having the injection molded case. I have a 3D printer, but I would spend a million years perfecting the casing design and STILL it would look 3D printed. And the real benefit, I thought, was that everything would just work and I wouldnāt have to spend any time building or debugging it. hahaā¦ boy has that not gone as plannedā¦ but Iām having fun debugging, so itās all good!
When I first got the kit up and running, the display bothered me because no one needed the temperature down to 1/100įµŹ° of a degreeā¦ Thatās needless precision without supporting accuracy. And CO2 looked weird. And I wanted to add in the NOx readings and needed to make some space to fit everything. So, I figured I would spend 5 minutes tops figuring out where the print statements were and just fix them the way I liked. Boy was I wrong.
For the single decimal place:
At first I thought of trying something like a C print statement ( %.1f
), or a truncation (I didnāt care about lack of rounding), but it took a bit of time to finally find the Arduino documentation on String()
located here: https://www.arduino.cc/reference/en/language/variables/data-types/stringobject/. I didnāt really understand why it was under Variables > Data types, but .
In the Syntax section, I noticed String(val, decimalPlaces)
and the parameter definition is given as:
decimalPlaces
: only if val is float or double. The desired decimal places.
So, just put in the number 1 there and it worked!
Degree symbol (Ā°):
Unicode: U+00B0
UTF-8: C2 B0
This, I thought was easy, just use the Ā° character, which you can literally paste in, but (my memory of this is a bit fuzzy) I think it didnāt work. I then learned about u8g2 and dug into the documentation. I tried a bunch of stuff like u8g2.enableUTF8Print();
chose a different font, and use the escape hex code of the UTF-8 code "\xC2B0"
to finally get it to work! But I will change it in a bit when I get to the subscript 2 below.
Subscript 2 (ā):
Unicode: U+2082
UTF-8: E2 82 82
Now, I feel like I was home freeā¦ the subscript 2 in COā was going to be the same way. If pasting the unicode character in the string didnāt work, I would do the same escape hex code trick with the UTF-8 codeā¦ but subscript 2 sits further down the unicode map and is a 3-byte UTF-8 character and so nothing I tried worked. I then started digging into the included fonts and looking at all of the glyph sets of all of the fonts only to discover that none of the fonts cover that remote area of the character mapā¦ The only character nearby that area is the Euro currency symbol sitting alone in a sea of blankness at U+20AC (UTF-8: E2 82 AC). So close!!
Well, I was left with no choice but to create my own font. I then looked into all of the font creation and conversion tools. Luckily, in my research, I found someone just two weeks earlier had created a web-based tool that can create custom fonts! No installation needed. It was meant for CJK characters that are missing, but it just used the GNU Unifont library to put into your own set of glyphs, so it would work. I put in my two characters (degree symbol and subscript 2) and clicked the āGenerate nowā button and I was presented with the font. I then saved that into a .h and did a #include at the top of my sketch, set u8g2 to the custom font, and walla! my print statements with the unicode strings just magically work!.
All of this is still a work-in-progress because before I finished, I started working on the IĀ²C stability issue and then the exception 0 issue. Part of the reason is that my hack-and-slash code to add in the TVOC/NOx sensor and mess with the display somehow made the reboots happen as frequently as every few minutes, whereas a full-stock FW seems to only reboot after many minutes or several hours.
TL;DR & Final Results:
Hereās the custom font generated by the web-tool:
/*
Fontname: -gnu-Unifont-Medium-R-Normal-Sans-16-160-75-75-c-80-iso10646-1
Copyright: Copyright (C) 1998-2019 Roman Czyborra, Paul Hardy, Qianqian Fang, Andrew Miller, Johnnie Weaver, David Corbett, et al. License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html> with the GNU Font Embedding Exception.
Glyphs: 99/57086
BBX Build Mode: 0
*/
const uint8_t u8g2_font_unifont_myfonts[1331] U8G2_FONT_SECTION("u8g2_font_unifont_myfonts") =
"c\0\3\2\5\5\4\5\6\20\20\0\376\12\376\13\377\1\233\3\63\5\11 \6\0\240G\1!\10A"
"\61DqH\4\42\10\205(F\221\271\5#\17F%D\325\323\60$Q\313\60D=\1$\22G%"
"D\27\16J\24I\341<FRe\20\63\0%\24G%D\243II)\211\224\64N\23)\211\222\222"
"\246\0&\22G%D\265\225\262J(&\221\226\210I&M\1'\7\201\60F\61\4(\14\203\355C"
"\225DI\324[\224\5)\15\203\351C\221EY\324K\224D\0*\15\347dDW\252\264mIS-"
"\3+\14\347dD\27\327\206!\213k\0,\11\202\254C\241$\12\0-\7$(E\61\4.\7B"
",D\61\4/\14F%D[\254\206iXM\1\60\22F%D\245EI\250M\211\22mb\22e"
"\22\0\61\13E)D\225II\330\247A\62\17F%D\63$\241\230fZXM\207\1\63\20F%"
"D\63$\241\230Fs*\212\311\220\0\64\20F%D\31jIT\311\222,\31\306\264\2\65\17F%"
"DqH\253\203\234\246b\62$\0\66\17F%D\65\205i:(\241c\62$\0\67\13F%Dq"
"-\246\305\264\11\70\20F%D\63$\241\61\31\222\320\61\31\22\0\71\16F%D\63$\241\61\31\324"
"\306h\2:\11\342lD\61\304C\0;\12\42\355C\61\304J\242\0<\11%)D\231u\355\0="
"\11\246\244Dq'\16\3>\11%%D\221v\353\10\77\17F%D\63$\241\230\206\325\34L#\0"
"@\22F%D\65eR\242$K\244DJ$-\361\20A\16F%D\245E-\241\70\14\242c\0"
"B\16F%D\61(\241qXB\307a\1C\16F%D\63$\241\265\243\230\14\11\0D\16F%"
"D\61DY\22\372-\31\42\0E\15F%DqH\253\203\222\266\16\3F\14F%DqH\253\203"
"\222v\5G\16F%D\63$\241\265\64\204\66e\11H\13F%D\21:\16\203\350\61I\13E)"
"D\61Ha\77\15\2J\16G%D\65\210qOY\224e\33\0K\21F%D\21jIT\311D"
"\61\311\242Z\22\6L\11F%D\221\366\327aM\15F%D\21\212\323\20-\36\35\3N\20F%"
"D\21n\233\22)\221\224H\211v\14O\14F%D\63$\241\77&C\2P\15F%D\61(\241"
"qX\322\256\0Q\26g\345C\63Da\22&a\22&a\22&\211\222H\322\220\13R\20F%D"
"\61(\241qX\242Z\222%\241\30S\15F%D\63$\241\331QL\206\4T\12G%Dq\310\342"
"\376\6U\13F%D\21\372\307dH\0V\21G%D\221Z\223,\312\242\254\22&i\234\1W\15"
"F%D\21zq\231\206h\24\3X\17F%D\21\212I\324&jQK(\6Y\16G%D\221"
"\252I\26e\225\64\356\6Z\13F%Dq-\366\232\16\3[\12\203\361C\61D\375\323\0\134\14F"
"%D\221\306\325\70\215\253\1]\12\203\345C\61\365OC\0^\11fdF\245EI\30_\7'\344"
"Cq\10`\7c\250F\221\25a\16\6%D\63$a\232\14\243MY\2b\16f%D\221\266,"
"\232\350\270)\13\0c\15\6%D\63$\241\332\61\31\22\0d\14f%D\333\262h\243\67e\11e"
"\17\6%D\63$\241\70\14j\61\31\22\0f\14e%D'\205\245A\12{\2g\23f\245C\233"
",Z\222%Y\264\245C\22\212\311\220\0h\14f%D\221\266,\232\350c\0i\13e)D\25\346"
"\210\330\247Aj\14\245\245CYG\304>J\221\4k\21f%D\221\266%Q%\23\223,\252%a"
"\0l\12e)D#\366O\203\0m\22\7%D\261(Q$ER$ER$ER\1n\13\6"
"%D\221,\232\350c\0o\14\6%D\63$\241\217\311\220\0p\16F\245C\221,\232\350\270)K"
"\232\2q\14F\245C\263h\243\67eI\13r\13\6%D\221,\232\250v\5s\15\6%D\63$"
"\241\354\230\14\11\0t\13E%D\25\226\6)\354*u\12\6%D\21\372MY\2v\14\6%D"
"\21\32\223\250\67Q\2w\21\7%D\221J\221\24I\221\24I\221T\261\0x\17\6%D\21\212I"
"\224\211Z\224\204b\0y\16F\245C\21zL\42KZ\31\22\0z\12\6%Dq\15{\35\6{"
"\16\244\251C\245da\26\25kQ\26\12|\7\301\261C\361A}\17\244\251C!fQ\26\226ja"
"\226H\0~\12g$F\243I\221\246\0\177#\20\242\203\221\364;I\347\244\223\16I\66%a\32%"
"C\222MI\230NC\62\354\234tN:)\351\7\200$\20\242\203\221\364;I\347\244\223\66i\231\222"
"(\211\242C\22\15a\224D\305(YtN:'\235\224\364\3\260\12\204\250E\243DR\242\0\0\0"
"\0\4\377\377 \202\15\345\344C\263da$e\341 \0";
That can go in itās own .h file (mine is named u8g2_font_107b527caaa4ad2133465f74776dcb44.h
) and then you will put in a #include
at the top of your sketch code. Or, alternatively, you can just paste in the font definition at the top of your sketch code as well.
For me, near the top of my script, I have:
#include "u8g2_font_107b527caaa4ad2133465f74776dcb44.h"
char spin = ' ';
unsigned int Hr, Min = 0;
Then, hereās my updateOLED(
) and updateOLED2()
functions:
void updateOLED() {
if (currentMillis - previousOled >= oledInterval) {
previousOled += oledInterval;
String ln4;
String ln1 = "PM:" + String(pm25) + " AQI:" + String(PM_TO_AQI_US(pm25));
String ln2 = "COā:" + String(Co2);
String ln3 = "VOC:" + String(TVOC) + " NOx:" + String(NOX);
if (inF) {
ln4 = String((temp* 9 / 5) + 32, 1) + "Ā°F " + String(hum)+"%RH";
} else {
ln4 = String(temp, 1) + "Ā°C RH:" + String(hum)+"%";
}
if (spin == '-') {
spin = '\\';
}
else if (spin == '\\') {
spin = '|';
}
else if (spin == '|') {
spin = '/';
}
else {
spin = '-';
}
Hr = (currentMillis/3600000);
Min = ((currentMillis/60000)-(Hr*60));
String ln5 = String( String(Hr) + "h" + String(Min) + "m " + spin);
updateOLED2(ln1, ln2, ln3, ln4, ln5);
}
}
void updateOLED2(String ln1, String ln2, String ln3, String ln4, String ln5) {
char buf[9];
u8g2.firstPage();
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_unifont_myfonts);
u8g2.drawUTF8(1, 10, ln1.c_str());
u8g2.drawUTF8(1, 25, ln2.c_str());
u8g2.drawUTF8(1, 40, ln3.c_str());
u8g2.drawUTF8(1, 55, ln4.c_str());
u8g2.setFont(u8g2_font_6x10_mf);
int ln4pos = u8g2.getDisplayWidth() - u8g2.getUTF8Width(ln5.c_str());
u8g2.drawUTF8(ln4pos, 64, ln5.c_str());
Serial.println(ln5);
} while ( u8g2.nextPage() );
}
Let me know if it doesnāt work. I may have missed something when extracting it out as code snippets and cleaning some of the commented-out stuff.