目次 [ Contents ]
今回はu8glibを使って文字や数値を描画したいと思います。前回、前々回を踏まえた上でご覧ください。
配線
復習
まず前回の復習を踏まえて、可変抵抗器を使ったメータ表示のスケッチを書きます。
#include "U8glib.h" //U8GLIB_SSD1306_128X64 u8g(4, 5, 6, 7); // SPIの場合はこっちを生きに U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); #define VOL A0 #define VMAX 1013 int vol_val = 0; void setup() { pinMode(VOL, INPUT); u8g.setColorIndex(1); } void loop() { vol_val = 0.9 * vol_val + 0.1 * analogRead(VOL); byte val = map(vol_val, 0, VMAX, 0, 127); u8g.firstPage(); do { u8g.drawBox(0, 60, val, 3); } while (u8g.nextPage()); }
メータが邪魔にならないよう位置を下にしました。これをベースに解説していきます。
文字列を表示する
英語文字で文を表示します。
*日本語描画については触れません。他を当たってください。
前回も少し説明しましたが、u8glibで文字列をディスプレイに表示するには、
1. フォントを指定する
2. 座標を指定して文字列を投げる
フォント指定は変更しない限り継続されるので、一度設定すれば2の工程だけで済む場合もあります。グラフィックディスプレイでは、このようにこちらからフォントデータも用意して使う場合が多いようです。
とりあえず、前回「HelloWorld」にあった文字列の表示命令を追記します。
#include "U8glib.h" //U8GLIB_SSD1306_128X64 u8g(4, 5, 6, 7); U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); #define VOL A0 #define VMAX 1013 int vol_val = 0; void setup() { pinMode(VOL, INPUT); u8g.setColorIndex(1); } void loop() { vol_val = 0.9 * vol_val + 0.1 * analogRead(VOL); byte val = map(vol_val, 0, VMAX, 0, 127); u8g.firstPage(); do { u8g.setFont(u8g_font_unifont); u8g.drawStr( 0, 22, "Hello World!"); u8g.drawBox(0, 60, val, 3); } while (u8g.nextPage()); }
フォントを指定する
フォントを指定するには、
u8g.setFont(フォント名)
で指定できます。フォントはu8glibライブラリにあらかじめ用意されていて、setFontで呼び出していると、その分だけコンパイル時に実装されるようです。なのでフォントの種類を増やせば増やすだけスケッチ量に反映されます。
実は文字を扱うことで難しいのはフォント容量をどう節約するかにかかってきます。
ここで u8g_font_unifontの詳細データが見れますが、表記の一部を抜粋すると、
u8g_font_unifont
BBX Width 16, Height 16, Capital A 10
Font data size: 5551
Font data size、これはバイト表記と思われるので5.5KBということになります。ArduinoUNOが32KBなので、1つのフォントで全体の6分の1近い容量を使うことになります。
見た目を良くしたくてフォントを複数使おうとすると大変なことになるので、気をつけてください。
フォントリスト
ここにはu8glibで呼び出せるフォントのリストがあります。自分の環境に合ったフォントを探せますが、結構たくさんあるので、望みのものを見つけるのは大変かもしれません。また文字の級数で容量が大きく変わってくるので悩みます。
欲しいフォントの名前が見つかったら、その名前を関数内に書き込みます。
例えば、u8g_font_8x13r というフォントを使いたければ、
u8g.setFont(u8g_font_8x13r);
とするだけで使えるようになります。
名前最後にrがついているフォントが軽いし導入しやすいのと思うので、そこに集中して探すのがオススメです。
文字列を描く
文字列を描画するには、
u8g.drawStr(x座標, y座標, 文字列);
座標は図形の時と同じです。ただ、基本的に文字の軸は一番左下になるので、それを見越して配置します。
#include "U8glib.h" //U8GLIB_SSD1306_128X64 u8g(4, 5, 6, 7); U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); #define VOL A0 #define VMAX 1013 int vol_val = 0; void setup() { pinMode(VOL, INPUT); u8g.setColorIndex(1); } void loop() { vol_val = 0.9 * vol_val + 0.1 * analogRead(VOL); byte val = map(vol_val, 0, VMAX, 0, 127); u8g.firstPage(); do { u8g.setFont(u8g_font_8x13r); u8g.drawStr( 0, 20, "Hello World!"); u8g.drawBox(0, 60, val, 3); } while (u8g.nextPage()); }
これで文字列が表示できます。
もしフォントを変えて複数の字体で表示したければ、
u8g.setFont(u8g_font_unifont); u8g.drawStr( 0, 20, "Hello World!"); u8g.setFont(u8g_font_8x13r); u8g.drawStr( 64, 20, "Goodbye World!");
文字を表示する前にsetFontでフォントを指定します。
先ほども述べたとおり、一回でもsetFontされるフォントはスケッチに組み込まれてしまうので、使わなくてもお荷物になります。逆に、一度setFontすればそのまま設定が継続されるので、1つのフォントしか使わなければ void setup内でu8g.setFont()しちゃった方がいいです。
数値を表示する
文字列は比較的簡単に扱えました。でも、数値を表示したいとなると急に難易度が上がります。というのは数値をそのまま出力してくれる関数が見当たらないからです。
↑ありました。part4 に書いています。(16/10/06)
ということで、自力で数値をchar型配列に変換し出力する方法を説明します。
char型の説明のために一回u8glibを離れ、Serial.printについて話します。順を追って細かく説明していくので、分かっていたら飛ばしてください。
文字列について
ASCIIコード
Serial.printでは意識しなくても、文字列だろうと数値だろうと正常に表示してくれます。
void setup() { Serial.begin(9600); } char txt = '5'; byte val = 5; void loop() { Serial.println(txt); Serial.println(val); Serial.println(); delay(1000); }
txtはchar型、valはbyte型の整数です。そしてtxtには”(シングルクォーテーション)でくくった文字としての’5’を代入していますが、どちらも5が表示されます。当たり前のように見えるかもしれないですが、これは便宜を図って変換してくれているからこうなります。ためしに、
Serial.println((byte)txt);
txtを整数として表すと「53」と出るようになります。
これは’5’という文字がASCIIコードのリストでは53番目であるということを示しています。リストを見ると「文字」の列にある5の横は「10進法」の53になってますよね。
逆に、char型のtxtに数字の53を入れると「5」が文字として表示されます。
void setup() { Serial.begin(9600); } char txt = 53; void loop() { Serial.println(txt); delay(1000); }
ここで言いたいのは、char型というのはASCIIのリストに対応する数字を扱うためのもので、
「数」と、実際割り当てられている文字としての「数値」は違う
ということです。
これが分かると、「じゃ、そうやって変換すればu8glibでも数値を表せるんじゃないか」と思っちゃうんですが、もうひとつ問題があります。
u8g.draw.Str()関数で扱えるのは文字列だけ。
配列(array)とヌル文字
一応自分の中では「文字は1文字、文字列は複数の文字の団体」という棲み分けをしてます。
そして文字列は配列で扱えます。
通常の変数は1つで1データしか入らないのを、配列ではいくつかのデータをひとつのセットとして扱えます。端的に言えばグループ化です。
やり方は通常の変数宣言に [ ] をつけて何個の配列かを指定し、個々のデータを代入するだけです。こうすることで本来1文字づつ表現しないといけないものを1つの単語として扱えるようになります。
void setup() { Serial.begin(9600); } char txt [6] = {"Hello"}; void loop() { Serial.println(txt); delay(1000); }
最後のnullとはヌル文字、または終端文字と呼ばれているもので、char型の配列では必要不可欠な要素です。要は句読点です。“Hello”が5文字なのに[6]個作っているのは、ヌル文字用の箱を最後に置かなければいけないからです。
このように、個数と中に入るデータをきっちり最初に指定していれば、null文字は自動的に入ってくれます。逆に、null文字分を忘れて箱数が少ないと書き込みでエラーになりますので、気をつけてください。
また、配列変数は表記に [ ] で番号を指定すれば、その箱のデータだけを扱えます。例えば、
void setup() { Serial.begin(9600); } char txt [6] = {"Hello"}; void loop() { Serial.println(txt[1]); delay(1000); }
”Hello”の2番目の’e’だけ抜き出せたりします。
気をつけないといけないのは、箱の数は6個だけど箱のカウントは0から、っていうことです。よく間違えたり混乱するので、気をつけてください。
ということで、u8glibで数値を表示するための準備は整いました。
数値をChar型配列に変換して表示する
上記の話を踏まえ、まず可変抵抗器の値1桁分の数値を表示してみます。
#include "U8glib.h" //U8GLIB_SSD1306_128X64 u8g(4, 5, 6, 7); U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); #define VOL A0 #define VMAX 1013 int vol_val = 0; void setup() { pinMode(VOL, INPUT); u8g.setColorIndex(1); } void loop() { vol_val = 0.9 * vol_val + 0.1 * analogRead(VOL); char num [2] = {"0"}; num [0] = '0' + vol_val % 10; u8g.firstPage(); do { u8g.setFont(u8g_font_unifont); u8g.drawStr(95, 55, num); } while (u8g.nextPage()); }
文字列num[2]を用意し、0番の箱へvol_valの1の位の値を代入します。
この時ポイントなのが、まず、vol_valを10で割った余りだけにしていることです。これで1の位だけが抜き出せます。
次にその数値を’0’に足していることです。よく分からないかもしれないですが、’0’から’9’はASCIIコードの数字では順当に並んでいます。
例えばvol_valの値が5だとして、
‘0’ + 5 =
‘0’を数字ではなく数値に置き換えると、
48 + 5 = 53
ASCIIコードで確認すれば10進法の53は文字で数字の5。つまり1桁の数値なら文字の’0’に直接足してやれば、その分のシフトで事足ります。結果、Char型への変換になるという算段です。
後はこれを発展させて4桁分行えば、数値をディスプレイに表示できます。
#include "U8glib.h" //U8GLIB_SSD1306_128X64 u8g(4, 5, 6, 7); U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); #define VOL A0 #define VMAX 1013 int vol_val = 0; void setup() { pinMode(VOL, INPUT); u8g.setColorIndex(1); } void loop() { vol_val = 0.9 * vol_val + 0.1 * analogRead(VOL); char num [5] = {"0000"}; num [0] = '0' + (vol_val / 1000) % 10; num [1] = '0' + (vol_val / 100) % 10; num [2] = '0' + (vol_val / 10) % 10; num [3] = '0' + vol_val % 10; u8g.firstPage(); do { u8g.setFont(u8g_font_unifont); u8g.drawStr(95, 55, num); } while (u8g.nextPage()); }
vol_valのそれぞれの位を1桁に落としてnum配列にはめ込んでいくだけです。
これでようやく数値表示を達成できました。
まとめ : 諸々を簡素に表示する
最後に可変抵抗器、文字列、数値を織り交ぜて且つ、なるべく簡素化したスケッチを書いてみます。
#include "U8glib.h" //U8GLIB_SSD1306_128X64 u8g(4, 5, 6, 7); U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); #define VOL A0 #define VMAX 1013 int vol_val = 0; #define DEG 4 // 桁数 void setup() { pinMode(VOL, INPUT); u8g.setColorIndex(1); // ピクセルの塗りつぶし有効 u8g.setFont(u8g_font_unifont); // フォントの指定 } void loop() { vol_val = 0.9 * vol_val + 0.1 * analogRead(VOL); // VRの値取得 byte val = map(vol_val, 0, VMAX, 0, 127); // スクリーン上のゲージX位置 char num_array [DEG + 1]; // VR数値表示用の配列 bool space = true; // 数値より上の桁での0を判定 for (byte i = 0 ; i < DEG ; i++) // 桁分繰り返し { // 桁繰り下げ用の計算 long digit = 1; for (byte ii = 1 ; ii <= (DEG - 1 - i) ; ii++) digit *= 10; // 現在の桁を1の位まで落とし1桁の数値を取得 byte cur_val = (vol_val / digit) % 10; // 数値より上の0の見極め if ((vol_val == 0 && i == (DEG - 1)) || cur_val != 0) space = false; if (space) num_array [i] = ' '; // 数値より大きい0なら空白でうめる else num_array [i] = '0' + cur_val; // でなければ、数値を代入 } num_array [DEG] = '#include "U8glib.h" //U8GLIB_SSD1306_128X64 u8g(4, 5, 6, 7); U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); #define VOL A0 #define VMAX 1013 int vol_val = 0; #define DEG 4 // 桁数 void setup() { pinMode(VOL, INPUT); u8g.setColorIndex(1); // ピクセルの塗りつぶし有効 u8g.setFont(u8g_font_unifont); // フォントの指定 } void loop() { vol_val = 0.9 * vol_val + 0.1 * analogRead(VOL); // VRの値取得 byte val = map(vol_val, 0, VMAX, 0, 127); // スクリーン上のゲージX位置 char num_array [DEG + 1]; // VR数値表示用の配列 bool space = true; // 数値より上の桁での0を判定 for (byte i = 0 ; i < DEG ; i++) // 桁分繰り返し { // 桁繰り下げ用の計算 long digit = 1; for (byte ii = 1 ; ii <= (DEG - 1 - i) ; ii++) digit *= 10; // 現在の桁を1の位まで落とし1桁の数値を取得 byte cur_val = (vol_val / digit) % 10; // 数値より上の0の見極め if ((vol_val == 0 && i == (DEG - 1)) || cur_val != 0) space = false; if (space) num_array [i] = ' '; // 数値より大きい0なら空白でうめる else num_array [i] = '0' + cur_val; // でなければ、数値を代入 } num_array [DEG] = '\0'; // 最後にヌル文字を代入 u8g.firstPage(); do { u8g.drawStr(0, 22, "Hello World!"); // 文字列を表示 u8g.drawStr(95, 55, num_array); // 数値を表示 u8g.drawBox(0, 60, val, 3); // ゲージを表示 vol_val = 0.9 * vol_val + 0.1 * analogRead(VOL); // VRの値取得while対策用 } while (u8g.nextPage()); }'; // 最後にヌル文字を代入 u8g.firstPage(); do { u8g.drawStr(0, 22, "Hello World!"); // 文字列を表示 u8g.drawStr(95, 55, num_array); // 数値を表示 u8g.drawBox(0, 60, val, 3); // ゲージを表示 vol_val = 0.9 * vol_val + 0.1 * analogRead(VOL); // VRの値取得while対策用 } while (u8g.nextPage()); }
defineのDEGを変えれば4桁以上も表示できます。値より大きい位に’0’がつかないようにしてみました。良ければ自分に合った形に改良して使ってください。
これでu8glibでOLEDが何となく使えるようになったかと思います。
なんですが、u8glibには他にも関数がたくさんあります。次回は自分が使う機能を軸に紹介してみたいと思います。
Just wheat i need!!!!
Tanks… Good work for new to oled display
Thanks to comment. Why don’t you check other articles about Arduino.
https://jumbleat.com/about_arduino_articles/
Great help! Arigatoo! I’m stucked in Part 3. I am trying to make ethanol gauge for my car. The sensor outputs 0-5volts. 5 volt means %100 ethanol and 0 means no ethanol. The same sensor output sending pwm signals to show fuel temperature. I found a code here but cant modify for 8glib
https://codebender.cc/sketch:335619#Flex%20Fuel%20Sensor%20to%20OLED%20Sketch.ino
Thank you for comment.
Sorry to say, code of the url seems to be for “Adafruit” library. And I haven’t learn neither the library and how to use the sensor.
So, basically, I can’t help your project. But some rewrite of the code might be workable.
Watch Out! I changed sensor pin 10 to 2.
Good luck!
Thank you! Before the hertz stuff I’m really lost in here and tearing my hairs ;
vol_val = 0.9 * vol_val + 0.1 * analogRead(VOL);
byte val = map(vol_val, 0, VMAX, 0, 127);
Any touch here will make my gauge bar(u8g.drawBox, val) fail! I couldn’t tell that I really understood this code. Basically I’m trying to make the gauge screen (u8g.drawBox(val)) range to accept 0v to 5v and numbers(u8g.drawStr(num)) to 0-100 %
So I modify the code;
vol_val = 5.0 / 1024.0 * (analogRead (VOL) + 0.5) ;
u8g.drawBox(0, 44, val, 22);
but no go!
At first, to be sure, hertz(PWM) is not same as analog voltage. Be careful.
The analogRead() function returns 0-1024 value depends on its system voltage. So if you use 5V arduino, you don’t have to remap value. 5V is 1024, 0V is 0.
But, instead, you should remap the value to OLED pixel range.
For example, if I use 128×64 pixel OLED, screen size value goes 127×63. And,
u8g.drawBox(0, 43, analogRead (VOL) / 1024.0 * 127, 20);
Incidentally, I recommend you use “map()” function to be much easier.
In addition, “0.9 * vol_val + 0.1 * analogRead(VOL);” is very practical way to get stable value.
Check here,
https://jumbleat.com/2016/08/17/reduce_fluctuations/
Hi!
Okay I misunderstood how the sensor works. The sensor send PWM signal 50-150 hertz. So 50hz means %0 Alcohol and 150 hz means %100 alcohol. I get rid of some codes and now it works.
http://img.photobucket.com/albums/v124/cucuman/20170501_035320.jpg
Here is the code;
#include “U8glib.h”
U8GLIB_SH1106_128X64 u8g(13, 11, 10, 9, 8); // SW SPI Com: SCK = 13, MOSI = 11, CS = 10, A0 = 9
int Sensorpin = 2;
float pulsetime = 0;
int hertz = 0;
void setup() {
u8g.setColorIndex(1);
u8g.setFont(u8g_font_fub35n);
}
void loop() {
pulsetime = pulseIn(Sensorpin, HIGH) + pulseIn(Sensorpin, LOW);
hertz = int((1000000 / pulsetime) – 50);
u8g.firstPage();
do
{
u8g.drawBox(25,4,8,34); // dikey I
u8g.drawBox(31,4,15,6); // yatay 1
u8g.drawBox(31,18,15,6); // yatay 2
u8g.drawBox(31,32,15,6); // yatay 3
u8g.setPrintPos(49, 39);
u8g.print(hertz);
u8g.drawBox(0, 44, hertz, 22);
} while (u8g.nextPage());
delay(900);
}
But as you may see the bar graph doesn’t fit to screen. I’m trying to understand how u8g.drawBox(0, 44, hertz, 22); works on a screen range. Thank you for your great help
Ahh you have already told that and u8g.drawBox(0, 44, hertz / 100.0 * 127, 22); fixed it!
Be good project!