Release 2016.9.7 / Update 2017.6.18

u8glibでOLEDを使う part 3

今回はu8glibを使って文字や数値を描画したいと思います。前回前々回を踏まえた上でご覧ください。

配線

前回と同じ配線で可変抵抗も接続します。下記はI2C。SPI接続の場合はこちらに可変抵抗を付け足してください。

simple_wiring_u8g_I2C_breadboard

復習

まず前回の復習を踏まえて、可変抵抗器を使ったメータ表示のスケッチを書きます。

#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_size_capa

見た目を良くしたくてフォントを複数使おうとすると大変なことになるので、気をつけてください。

フォントリスト

u8glibで使えるフォントの一覧

ここには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());
}
u8glib_axis
この角がx0、y20

これで文字列が表示できます。

もしフォントを変えて複数の字体で表示したければ、

    u8g.setFont(u8g_font_unifont);
    u8g.drawStr( 0, 20, "Hello World!");
    u8g.setFont(u8g_font_8x13r);
    u8g.drawStr( 64, 20, "Goodbye World!");

文字を表示する前にsetFontでフォントを指定します。

u8glib_hello_goodbye

先ほども述べたとおり、一回でも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);
}

u8glib_char_int_1

txtはchar型、valはbyte型の整数です。そしてtxtには”(シングルクォーテーション)でくくった文字としての’5’を代入していますが、どちらも5が表示されます。当たり前のように見えるかもしれないですが、これは便宜を図って変換してくれているからこうなります。ためしに、

  Serial.println((byte)txt);

txtを整数として表すと「53」と出るようになります。

u8glib_char_int_3

これは’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文字、文字列は複数の文字の団体」という棲み分けをしてます。

そして文字列は配列で扱えます。

u8glib_array_illust

通常の変数は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);
}

u8glib_char_int_2

”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());
}

u8glib_first_num

文字列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配列にはめ込んでいくだけです。

これでようやく数値表示を達成できました。

u8glib_hello_val

まとめ : 諸々を簡素に表示する

最後に可変抵抗器、文字列、数値を織り交ぜて且つ、なるべく簡素化したスケッチを書いてみます。

#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には他にも関数がたくさんあります。次回は自分が使う機能を軸に紹介してみたいと思います。

次回:part4
前回:part 2
前々回:Part 1

「u8glibでOLEDを使う part 3」への9件のフィードバック

    1. 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.

      #include 
      
      U8GLIB_SSD1306_128X64 u8g(13, 11, 10, 9, 8);  // SW SPI Com: SCK = 13, MOSI = 11, CS = 10, A0 = 9;
      
      int Sensorpin = 2;  // this is changed because of SPI pins
      float pulsetime = 0;
      int hertz = 0;
      
      char bar[12] = "-----------";
      char progress[12];
      
      
      void setup() {
        u8g.setColorIndex(1);  // pixel on
        u8g.setFont(u8g_font_8x13r);
      }
      
      
      void loop() {
      
        pulsetime = pulseIn(Sensorpin, HIGH) + pulseIn(Sensorpin, LOW);
        hertz = int((1000000 / pulsetime) - 50);
      
        //Serial.println(hertz);
      
        memset(progress, 0, sizeof(progress));
      
        u8g.firstPage();
        do
        {
          for (int i = 0; i < 11; i++)
          {
            strncpy (progress, bar, i);
      
            u8g.setPrintPos(4, 0);
            u8g.print(progress);
      
            u8g.setPrintPos(4, 25);
            if (hertz < 100) {
              u8g.print("E " + String(hertz));
            } else if (hertz < 10) {
              u8g.print("E 0" + String(hertz));
            } else {
              u8g.print("E" + String(hertz));
            }
          }
        } while (u8g.nextPage());
        delay(900);
      }
      

      Good luck!

  1. 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!

    1. 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/

  2. 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

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください