ODYSSEY-X86J4105の拡張ピンヘッダを使ってみた

Seeed K.K.の中井です。

小型PCであるODYSSEY-X86J4105を入手したので、早速どんなものか使ってみました。

CPUはIntel Celeron J4105が載っており普通に使う分にはデスクトップPCとかわらずですが、 なんとこの基板にはSAMDが載っているのです!! Seeedらしいなぁと思いながらArduinoならGroveでしょ?と、使えそうなGroveを掘り起こしてつなげてみました。

f:id:mnakai:20210610165912p:plain

ODYSSEY-X86J4105

  • Intel® Celeron® J4105, Quad-Core 1.5-2.5GHZ
  • Dual-Band Frequency 2.4GHz/5GHz WiFi
  • Intel® UHD Graphics 600
  • Dual Gigabit Ethernet
  • Integrated Arduino Coprocessor ATSAMD21 ARM® Cortex®-M0+

汎用OSであるWindowsやLinuxのリアルタイム性能を補うためなのか、Seeedだからなのかはわかりませんが、Arduino CoreとしてATSAMD21G18が搭載されています。

f:id:mnakai:20210610165809p:plain

www.seeedstudio.com

現時点では製品ページにも記載があるとおり、後継機種がリリースされています。

Please be aware that the J4105 processor has been discontinued, and therefore we will stop manufacturing ODYSSEY - X86J4105 in the near future. So now we bring you the new ODYSSEY - X86J4125 with the J4125 processor which is more powerful than J4105.

28-Pin Arduino Pinoutを使ってみる

ODYSSEY-X86J4105の特徴であるArduino Core(ATSAMD21G18)に接続されている28-Pin Arduino PinoutにGroveモジュールをつなげて動作確認してみました。

Arduino IDEをインストール

Arduino Core(ATSAMD21G18)と接続される28-Pin Arduino Pinoutを制御するためには、なんらかのファームウェアを書き込む必要があります。 そこでArduinoの標準開発環境Arduino IDEをインストールし、ファームウェアの書き込みを行ってみました。

まずはArduino IDEのインストールから。公式Wikiに沿って実施しました。以降の作業はODESSEY-X86J4105で行っています。

※ OSはubuntu 20.04に変更してあります。OSのインストール方法はコチラにあります。

ODYSSEY-X86J4105:~$ cat /etc/issue.net
Ubuntu 20.04.2 LTS
ODYSSEY-X86J4105:~$ uname -a
Linux ODYSSEY-X86J4105 5.8.0-55-generic #62~20.04.1-Ubuntu SMP Wed Jun 2 08:55:04 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
1. Arduino IDEを公式サイトからダウンロード

f:id:mnakai:20210609141259j:plain

2. ターミナル(端末)を開いてアーカイブを展開し、/optに移動
ODYSSEY-X86J4105:~$ cd ダウンロード
ODYSSEY-X86J4105:~/ダウンロード$ ls arduino-*-linux64.tar.xz
arduino-1.8.15-linux64.tar.xz
ODYSSEY-X86J4105:~/ダウンロード$ tar xf arduino-1.8.15-linux64.tar.xz
ODYSSEY-X86J4105:~/ダウンロード$ sudo mv arduino-1.8.15 /opt
3. インストール
ODYSSEY-X86J4105:~/ダウンロード$ cd /opt/arduino-1.8.15/
ODYSSEY-X86J4105:/opt/arduino-1.8.15$ sudo ./install.sh
Adding desktop shortcut, menu item and file associations for Arduino IDE...


 done!
ODYSSEY-X86J4105:/opt/arduino-1.8.15$ ./arduino-linux-setup.sh $(whoami)

******* Add User to dialout,tty, uucp, plugdev groups *******

groupadd: グループ 'plugdev' は既に存在します
groupadd: グループ 'dialout' は既に存在します

******* Removing modem manager *******

パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下のパッケージが自動でインストールされましたが、もう必要とされていません:
  libfprint-2-tod1 libllvm10 libmbim-glib4 libmbim-proxy libqmi-glib5 libqmi-proxy usb-modeswitch usb-modeswitch-data
これを削除するには 'sudo apt autoremove' を利用してください。
以下のパッケージは「削除」されます:
  modemmanager
アップグレード: 0 個、新規インストール: 0 個、削除: 1 個、保留: 1 個。
この操作後に 3,740 kB のディスク容量が解放されます。
(データベースを読み込んでいます ... 現在 196351 個のファイルとディレクトリがインストールされています。)
modemmanager (1.12.8-1) を削除しています ...
Created symlink /run/systemd/system/ModemManager.service → /dev/null.
man-db (2.9.1-1) のトリガを処理しています ...
dbus (1.12.16-2ubuntu2.1) のトリガを処理しています ...
hicolor-icon-theme (0.17-2) のトリガを処理しています ...

Restarting udev


*********** Please Reboot your system ************

指示通りに再起動し、無事Arduino IDEがインストールされました。

f:id:mnakai:20210609161250j:plain

Arduino IDEでSeeeduino Zero用のボードパッケージをインストール

ODYSSEY-X86J4105のArduino Core(ATSAMD21G18)用のファームウェアをビルドする環境を整えます。 Arduino IDEで「ファイル > 環境設定」を開き、追加のボードマネージャのURL欄に次のURLを設定。

https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json

f:id:mnakai:20210609163409j:plain

続いて、「ツール > ボード > ボードマネージャ」を開き、Seeeduino Zeroを検索し、「Seeed SAMD Boards」をインストール。

f:id:mnakai:20210609164638j:plain

結構時間がかかりました。5分くらい?

Hello World、Lチカで動作確認

定番のHello World + Lチカで動作確認。 「ツール > ボード > Seeed SAMD (...) Boards > Seeeduino Zero」と、 「ツール > シリアルポート > /dev/ttyACM0」を選択し、下記のスケッチを用意して「スケッチ > マイコンボードに書き込む」。

f:id:mnakai:20210610101750j:plain

  • スケッチ
void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);
}
void loop() {
  Serial.println("Hello World!");
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000);
}

書き込みが終了すれば即実行されるので、「ツール > シリアルモニタ」でみると"Hello World!"が表示されました。

f:id:mnakai:20210610102234j:plain

Lチカの確認は、LED_BUILTIN = D13なので、Grove - Blue LED Buttonを下のように接続。無事Lチカ完了! ちなみにGrove - LED ButtonはGrove-4-pin-Female-Jumper-to-Grove-4-pin-Conversion-Cableを使いました。コレ非常に便利!

f:id:mnakai:20210610105300p:plain

www.seeedstudio.com

www.seeedstudio.com

Grove - OLED Display 1.12"を動かしてみる

ODYSSEY-X86J4105のような小型PCに常にディスプレイを接続しておくということは少ないと思います。 でもちょっと表示したいなぁと思うことがあるので、手元にあったGrove - OLED Display 1.12"をつないでみました。

www.seeedstudio.com

公式WikiをみるとU8g2ライブラリを使うそうなのでインストールして、スケッチもそのまま記載のサンプルを使いました。 下のように接続。

f:id:mnakai:20210610113356p:plain

表示できました!Arduinoそのままなので当たり前なのですがw

f:id:mnakai:20210610113734j:plain

40-Pin RPi GPIO側で同じことを試すが、、大変でした

40-Pin Raspberry Pi Compatible GPIOはLinuxで制御可能なピンヘッダとなっています。 Arduinoで試したようにサクッとOLEDが動くんだろうなと思い軽い気持ちでやり始めたのですが、、大変でした。

ファンクション設定

ODYSSEY-X86J4105の公式Wikiでは、各信号のデフォルト機能はファンクションモードと読み取り、i2c-toolsをインストールして認識するか確認。

Wikiより抜粋: The default pins on the ODYSSEY - X86J4105 are configured to function mode so for example the pin 3 is set to I2C at default instead of GPIO mode.

使ったのは、Pin 27 and 28 (I2C6_SDA and I2C6_SCL)です。

f:id:mnakai:20210610115510j:plain

I2Cの使い方というか、I2Cデバイスを認識したかどうかを確認する方法は、Wikiに記載があります。

ODYSSEY-X86J4105:~$ ls /sys/bus/pci/devices/*/i2c_designware.0/ | grep i2c
i2c-0
ODYSSEY-X86J4105:~$ sudo i2cdetect -r -y 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

ん、認識しない。。

念のためBIOSでファンクションモードになっているか確認。電源ON時にDELキーをタイミングよく押せば(何度か押す)BIOSのセットアップ画面に入ることができます。 「Advanced > HAT Configuration」を選択すると40-Pin GPIOのデフォルト設定が確認できます。

GPIO / I2C6 Selection        [GPIO]

案の定GPIO設定になっていたので、[I2C6]に設定変更。

GPIO / I2C6 Selection        [I2C6]

変更を保存して再起動させて確認。

んん、、まだ認識しない。。

Wikiの誤記?

色々確認してみた結果、Wikiの記述がおかしいのかもしれません。

i2c_designware.0 -> I2C channel on Pin 27 and 28 (I2C6_SDA and I2C6_SCL)

"i2c_designware.0"ではなくて"i2c_designware.2"なんじゃないかと。 Seeedフォーラムにもそれっぽい内容がチラホラ見つかります。

ODYSSEY-X86J4105:~$ ls /sys/bus/pci/devices/*/i2c_designware.2/ | grep i2c
i2c-3
ODYSSEY-X86J4105:~$ sudo i2cdetect -r -y 3
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

あと、I2Cのバスクロックを確認したところ、400kHzでした。

The Default I2C Speed for ODYSSEY - X86J4105 is 1MB/s, which cannot be configured.

OLED制御用のプログラム

そんなこんなで認識できたGrove - OLED Display 1.12"ですが、制御用のプログラムをゼロから作るのもしんどいのでgrove.pyから拝借して利用してみました。

ベースとなるプログラムは、sh1107g.pyです。 次のような変更を加えました。

--- ./grove.py/grove/display/sh1107g.py  2021-06-10 13:46:02.016179633 +0900
+++ ./sh1107g.py  2021-06-10 14:34:05.886605409 +0900
@@ -34,10 +34,8 @@
         time.sleep(3)
         oled.clear()
 '''
-from grove.display.base import *
-from upm.pyupm_lcd import *
-from grove.i2c import Bus
-import sys, mraa
+from smbus import SMBus
+import sys
 
 # sphinx autoapi required
 __all__ = ["SH1107G_SSD1327"]
@@ -141,7 +139,11 @@
         [0x00,0x02,0x05,0x05,0x02,0x00,0x00,0x00],
 ]
 
-class SH1107G_SSD1327(Display):
+BASE_PAGE_START_ADDR = 0xb0
+BASE_LOW_COLUMN_ADDR = 0x00
+BASE_HIGH_COLUMN_ADDR = 0x10
+
+class SH1107G_SSD1327(object):
     '''
     OLED Display 1.12"(v2) use chip SSD1327 or SH1107G.
 
@@ -155,19 +157,16 @@
     _PAGE_BYTES = 128
     _TOTAL_BYTES= _PAGE_CNT * _PAGE_BYTES
 
-    def __init__(self, address = 0x3C):
+    def __init__(self, bus = 2, address = 0x3C):
         super(SH1107G_SSD1327, self).__init__()
-        # self._bus = Bus()
-        self._bus = mraa.I2c(0)
+        self._bus = SMBus(bus)
         self._addr = address
-        self._bus.address(self._addr)
 
-        if self._bus.writeByte(0):
+        if self._bus.write_byte(self._addr, 0):
             print("Check if the OLED SH1107G/SSD1307 inserted, then try again")
             sys.exit(1)
  
-        # id = self._bus.read_byte_data(self._addr, SH1107G_SSD1327._REG_CMD)
-        id = self._bus.readReg(SH1107G_SSD1327._REG_CMD)
+        id = self._bus.read_byte_data(self._addr, SH1107G_SSD1327._REG_CMD)
         # print(" id = 0x{:2x}".format(id))
         self._sh1107 = (id & 0x3F) == 0x07
         if not self._sh1107:
@@ -199,8 +198,7 @@
 
     def _cmd(self, cmd):
         try:
-            # self._bus.write_byte_data(self._addr,
-            self._bus.writeReg(
+            self._bus.write_byte_data(self._addr,
                                     SH1107G_SSD1327._REG_CMD, cmd)
         except IOError:
             print("*** Check if OLED module inserted ***")
@@ -217,9 +215,8 @@
         for i in range(length):
             data[i + 1] = datas[i]
         try:
-            self._bus.write(data)
-            # self._bus.write_i2c_block_data(self._addr,
-            #                       SH1107G_SSD1327._REG_DATA, datas)
+            self._bus.write_i2c_block_data(self._addr,
+                                    SH1107G_SSD1327._REG_DATA, datas)
         except IOError:
             print("*** Check if OLED module inserted ***")
             sys.exit(1)
@@ -348,15 +345,17 @@
 def main():
     import time
 
-    oled = SH1107G_SSD1327()
+    args = sys.argv
+    if len(args) > 1:
+        bus = int(args[1])
+    else:
+        bus = 2
+
+    oled = SH1107G_SSD1327(bus = bus)
     rows, cols = oled.size()
     print("OLED model: {}".format(oled.name))
     print("OLED type : {} x {}".format(cols, rows))
 
-    oled.backlight(False)
-    time.sleep(1)
-
-    oled.backlight(True)
     oled.setCursor(0, 0)
     oled.write("hello world!")
     oled.setCursor(0, cols - 1)

下のように実行。ようやくOLEDに表示されました。

ODYSSEY-X86J4105:~$ ls /sys/bus/pci/devices/*/i2c_designware.2/ | grep i2c
i2c-3
ODYSSEY-X86J4105:~$ sudo python3 ./sh1107g.py 3
OLED model: SH1107G
OLED type : 16 x 16

I2Cをユーザーモードで使用できるようにする

Linux標準状態ではI2Cの制御にはroot権限が必要なのですが、上のように毎回sudoで実行するのも面倒。 そんなときには下記のようにi2cグループにユーザーを追加すると楽になります。

ODYSSEY-X86J4105:~$ sudo usermod -a -G i2c $(whoami)

コマンド実行後は再起動しておきます。

無線LANの情報を表示

Linux側でGrove - OLED Display 1.12"に表示できるようになったので、無線LANの情報を表示してみました。 無線LANのインターフェース名は、wlo2のようです。

ODYSSEY-X86J4105:~$ ip -4 addr show wlo2
4: wlo2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    altname wlp0s12f0
    inet 192.168.2.113/24 brd 192.168.2.255 scope global dynamic noprefixroute wlo2
       valid_lft 73278sec preferred_lft 73278sec
ODYSSEY-X86J4105:~$ iw dev wlo2 link
Connected to a4:12:42:xx:xx:xx (on wlo2)
        SSID: MY-NETWORK-SSID
        freq: 5180
        RX: 4291661 bytes (45523 packets)
        TX: 279039590 bytes (44902 packets)
        signal: -59 dBm
        rx bitrate: 526.6 MBit/s VHT-MCS 6 80MHz VHT-NSS 2
        tx bitrate: 866.7 MBit/s VHT-MCS 9 80MHz short GI VHT-NSS 2

        bss flags:      short-slot-time
        dtim period:    1
        beacon int:     100

f:id:mnakai:20210610161134j:plain

一応、signalの値によってアンテナ本数を変えています (SSIDの横、写真では潰れて見辛いのですが)

Groveはどちらの拡張ヘッダで使うべきか

28-Pin Arduino Pinoutと40-Pin Raspberry Pi Compatible GPIOの両方でGroveモジュールを使ってみましたが、 Grove - OLED Display 1.12"に関してだけ言えば、断然Arduino側で使ったほうが簡単でした。 Linux - Arduino間はシリアル通信できるので、Linuxから表示したい文字列を送り、それを表示させるというような使い方が一番簡単そうに思いました。

どのデバイスをどのように使うかでそれぞれメリット・デメリットがあるようですので、 これを考えながら作ってみるのも面白味のひとつなんじゃないかなと思いました。

変更履歴

日付 変更者 変更内容
2021/6/10 mnakai 作成