Velxio上のRaspberry Piとは、キャンバス上に置かれたボードであり、その周囲に配線した回路に対してPythonスクリプトを実行するものです。コンパイルするものは何もありません。スクリプトを書いてRunを押すと、2つのエンジンのうちの1つがそれを実行します。
- 基本モードは、ブラウザタブ内のPythonインタプリタでスクリプトを実行します。数秒で起動し、ほとんどの場面で使うことになるモードです。
- Linuxモードは、Velxioのサーバー上で本物のLinuxゲストを起動し、それを同じ回路に接続します。シェル、ファイルシステム、そしてI2C、SPI、UART、GPIO用のカーネル自身のデバイスファイルが手に入ります。起動には約1分かかります。
この記事では、モードごとに1つずつ、2つのプロジェクトを構築し、その過程のすべてのクリックを紹介します。1つ目はRaspberry Pi 5上のGroveモジュールで作る防犯ステーションです。入力側にモーションセンサー、振動スイッチ、DHT11、出力側にブザー、2つのLED、ファン付きリレーを備えます。2つ目は3つのI2Cデバイスを接続したRaspberry Pi 4上のセンサー装置です。どちらのスクリプトもRaspberry Pi OSに同梱されているライブラリを使うので、実機のボードでもそのまま動作します。
始める前に、アクセスに関する2つの点を説明します。誰でもキャンバス上にRaspberry Piを配置し、その周囲に回路を配線できます。実行にはサインインしたアカウントが必要です。無料アカウントにはすべて、Raspberry Piファミリーでどちらのモードでも使える15分のトライアルセッションが3回付与され、有料プランならこの制限がなくなります。詳細は最後の方にある料金についてのセクションにあります。
同じスクリプトを実行する2つの方法
| 基本モード | Linuxモード | |
|---|---|---|
| スクリプトの実行場所 | ブラウザタブ内のPythonインタプリタ | Velxioのサーバー上でQEMU配下のAlpine Linuxゲスト |
| 起動時間 | 数秒 | 約1分 |
| GPIO | RPi.GPIO、gpiozero、Adafruit Blinka | 同じものに加え、/dev/gpiochip0とlibgpiodツール |
| I2C、SPI、UART、1-Wire | エミュレートされたデバイスファイル経由で本物のsmbus2、spidev、pyserial、w1thermsensor | カーネルの本物のデバイスノード経由で同じライブラリ |
| シェルとファイルシステム | なし | あり、Serial Monitor内で |
| サードパーティパッケージ | requirements.txtから | requirements.txtから、ゲスト起動前にオフラインで解決 |
| 最適な用途 | ほぼすべて | シェルツール、サブプロセス、ファイル、そしてチュートリアルがターミナルから行うようなあらゆる操作 |
エンジンは自動的に選択されます。subprocessのようにLinuxでしかできないことを要求するスクリプトはLinuxモードに送られ、ツールバーのチップがその理由を教えてくれます。自分でLinuxモードを強制することもでき、2つ目のプロジェクトではそうしています。
プロジェクト1 — Raspberry Pi 5上のGrove防犯ステーション
GroveモジュールはPiにセンサーを載せる最速の方法です。どのモジュールも同じ4ピンソケットを備えており、VelxioではそれぞれがSIG、VCC、GNDの3本のワイヤでヘッダーに接続されます。このステーションはPIR人感センサーと振動スイッチで部屋を監視し、DHT11で空気を読み取り、ブザー、「異常なし」を示す緑LED、「警報」を示す青LED、そして部屋が暖かくなるとファンを回すリレーで応答します。
ステップ1 — Raspberry Pi 5でプロジェクトを開始する
velxio.dev/editorを開きます。Start a new projectダイアログが自動的に開きます。Raspberry Piセクションまでスクロールし、Raspberry Pi 5カードをクリックします。Raspberry Pi 4Bカードも同じように動作します。この2つのボードは40ピンヘッダーとライブラリを共有しているため、以下の内容はどちらにも当てはまります。

ステップ2 — 部品を追加する
ツールバー右端の青いAddボタンをクリックします。部品カタログが開きます。

検索フィールドに名前を入力し、カードをクリックするとキャンバス上に部品が配置されます。GroveモジュールはすべてSeeedの名前で登録されているので、「Grove PIR」や「Grove relay」で見つかります。以下の8つの部品を1つずつ追加してください。
- Grove - PIR Motion Sensor
- Grove - Vibration Sensor (SW-420)
- Grove - Temperature & Humidity Sensor (DHT11)
- Grove - Piezo Buzzer
- Grove - Green LED
- Grove - Blue LED
- Grove - Relay
- DC Motor / Fan

部品はキャンバス上のどこにでもドラッグできます。Piを下に、モジュールをヘッダーの上に配置すると、ワイヤを短く保てます。
ステップ3 — 配線する
ワイヤを引くには、ピンをクリックしてから反対側のピンをクリックします。部品にカーソルを合わせるとピン名が表示されます。どのGroveモジュールも同じ3本のワイヤで接続します。SIGはGPIOへ、VCCは3V3へ、GNDはGNDへ。電源側の例外はリレーで、そのコイルは5Vを必要とします。
| モジュール | SIG | VCC | GND |
|---|---|---|---|
| PIR人感センサー | GPIO17 | 3V3 | GND |
| 振動センサー (SW-420) | GPIO27 | 3V3 | GND |
| DHT11 | GPIO4 | 3V3 | GND |
| ブザー | GPIO24 | 3V3 | GND |
| 緑LED | GPIO5 | 3V3 | GND |
| 青LED | GPIO6 | 3V3 | GND |
| リレー | GPIO23 | 5V | GND |
ファンには専用の信号線はありません。ベンチ上の負荷と同じように、リレーのスイッチ接点に接続します。電源はCOMに入り、コイルが通電している間だけNOから出てきます。
| 接続元 | 接続先 |
|---|---|
| Raspberry Pi 5V | リレー COM |
| リレー NO | ファン + |
| ファン - | Raspberry Pi GND |

ステップ4 — スクリプト
左側のファイルエクスプローラーには、短い点滅スクリプトを含むscript.pyというファイルがすでにあります。この名前をそのまま使ってください。基本モードは最初に見つけた.pyファイルを実行しますが、Linuxモードは特にscript.pyを実行するため、メインファイルがこの名前を保っているプロジェクトは両方のモードで動作します。

その内容を次のスクリプトに置き換えます。
"""Grove alarm station on a Raspberry Pi 5.
A PIR sensor and a vibration switch watch the room, a DHT11 reads the air,
and the actuators answer: a buzzer and a blue LED for the alarm, a green LED
while all is quiet, and a relay that runs a fan when the room gets warm.
Every module hangs off the header with a Grove cable, one signal pin each.
Wiring, BCM numbers (VCC and GND of each module to 3V3 and GND):
PIR motion sensor SIG GPIO17 HIGH while motion is seen
vibration SW-420 SIG GPIO27 LOW while shaking, HIGH at rest
DHT11 SIG GPIO4
buzzer SIG GPIO24
green LED GPIO5, blue LED GPIO6
relay SIG GPIO23, VCC on 5V; the contacts switch the fan:
5V to COM, NO to the fan's +, the fan's - to GND
"""
import time
import adafruit_dht
import board
from gpiozero import LED, Button, Buzzer, MotionSensor
FAN_ON_AT = 28 # degrees C
HOLD = 2.5 # seconds the alarm light stays on after the beeps
pir = MotionSensor(17)
shaker = Button(27, pull_up=True) # the SW-420 pulls SIG low while it vibrates
dht = adafruit_dht.DHT11(board.D4, use_pulseio=False)
buzzer = Buzzer(24)
quiet_led = LED(5)
alarm_led = LED(6)
fan = LED(23) # the relay coil: on() closes COM to NO
def alarm(reason, beeps, beep_time):
print(f"ALARM: {reason}")
quiet_led.off()
alarm_led.on()
buzzer.beep(on_time=beep_time, off_time=0.1, n=beeps, background=False)
time.sleep(HOLD)
alarm_led.off()
quiet_led.on()
quiet_led.on()
motion_seen = False
shaking = False
next_reading = 0
print("Station armed")
while True:
if pir.motion_detected and not motion_seen:
alarm("motion in the room", beeps=3, beep_time=0.15)
motion_seen = pir.motion_detected
if shaker.is_pressed and not shaking:
alarm("vibration on the door", beeps=1, beep_time=0.6)
shaking = shaker.is_pressed
if time.monotonic() >= next_reading:
next_reading = time.monotonic() + 2
try:
temperature, humidity = dht.temperature, dht.humidity
except RuntimeError as err: # a DHT11 misses a reading now and then
print(f"DHT11: {err}")
else:
fan.value = temperature >= FAN_ON_AT
print(f"{temperature:.0f} C {humidity:.0f} %RH fan {'on' if fan.value else 'off'}")
time.sleep(0.1)
このスクリプトには注目すべき点が3つあります。いずれも実機と同じ挙動です。
- SW-420はデータシートに記載されたとおりに配線されています。モジュールが静止している間はSIGがHIGHで、振動している間はLOWになります。プルアップ付きのgpiozeroの
Buttonはこれを振動中の「押下」として読み取るため、スクリプトに反転ロジックは不要です。 - リレーは
LEDとして駆動されています。gpiozeroにはリレークラスがありません。オンかオフかの出力デバイスがあれば、リレーコイルには十分です。 adafruit_dhtは読み取りに失敗するとRuntimeErrorを発生させ、DHT11は時折読み取りに失敗します。tryブロックは防御的な飾りではなく、このライブラリの想定された使い方です。
ステップ5 — 実行する
ツールバーの緑の三角形をクリックしてプロジェクトを実行します。下部にSerial Monitorが開き、エンジンが何をしているかを報告します。Pythonランタイムをロードし、GPIOライブラリをインストールし、[Velxio] ready in 6068 msのような行に続いて実行中のスクリプト名を出力します。それ以降、スクリプト内のすべてのprint()がそこに表示されます。

スクリプトはStation armedを出力し、その後2秒ごとに温度と湿度の行を出力します。キャンバス上のDHT11は31 Cから始まるので、リレーはすぐに閉じてファンが回り始めます。モーターには方向矢印とrpm単位の速度が表示され、リレー自身のインジケーターも点灯します。次にセンサーを操作してみましょう。
- PIRのドームをクリックするか、そのセンサーパネルのMotionを押します。SIGが5秒間HIGHになり、スクリプトは
ALARM: motion in the roomを出力し、青LEDが点灯してブザーが3回鳴ります。 - 振動センサーをクリックするか、そのパネルのVibrationを切り替えます。長いビープ音が1回鳴り、
ALARM: vibration on the doorと出力されます。 - DHT11の温度スライダーを28 C未満にドラッグします。次の読み取りでリレーが開き、ファンは惰性で停止します。

Runボタンの隣の赤い四角がスクリプトを停止します。これが基本モードのすべてです。スクリプトはタブ内で実行され、ピンはキャンバスに直接届き、Runから回路が反応するまでのループ全体が数秒で完了します。
プロジェクト2 — Raspberry Pi 4上のI2Cセンサーリグ、そしてLinuxモード
2つ目のプロジェクトでは、ヘッダーバスに3つのI2Cデバイスを接続し、本物のsmbus2を使ってそれらと通信します。2つのポテンショメーターを備えたGrove ADS1115アナログ-デジタルコンバーター、Grove SHT31温度・湿度センサー、そしてSSD1306 OLEDです。さらに、通常のGPIOに接続したボタンとLEDで完成します。同じ回路は、配線もコードも一行も変えることなくRaspberry Pi 5でも動作します。ヘッダーは同じだからです。
ADS1115が登場するのには、多くの人が引っかかる理由があります。Raspberry Piにはアナログ入力がありません。ポテンショメーターをGPIOに直接配線しても、実機でもVelxioでもハイかローしか読み取れないため、つまみはADCを経由します。
回路
Raspberry Pi 4Bカードで新しいプロジェクトを開始し、カタログから以下の部品を追加します。
- Grove - 4 Channel 16-bit ADC (ADS1115)
- Potentiometer、2つ
- Grove - Temperature&Humidity Sensor (SHT31)
- SSD1306 OLED (I2C, 4-pin)
- LED、Resistor 220 Ω、Pushbutton
次に配線します。3つのI2CデバイスはすべてGPIO2 (SDA) とGPIO3 (SCL) を共有します。
| 接続元 (Raspberry Pi 4) | 接続先 |
|---|---|
| GPIO2 (SDA) | ADS1115、SHT31、OLEDのSDA |
| GPIO3 (SCL) | ADS1115、SHT31、OLEDのSCL |
| 3V3 | ADS1115、SHT31、OLED、両ポテンショメーターのVCC |
| GND | ADS1115、SHT31、OLED、両ポテンショメーターのGND |
| GPIO27 | LEDアノード、LEDカソードから220 Ωを経てGND |
| GPIO22 | プッシュボタン、ボタンのもう一方をGNDへ |
| 接続元 | 接続先 |
|---|---|
| ポテンショメーター1 SIG | ADS1115 A0 |
| ポテンショメーター2 SIG | ADS1115 A1 |

スクリプト
これをscript.pyとして保存します。まず3つのアドレスそれぞれに応答があるか問い合わせ、その後ループします。2つのADCチャンネル、温度と湿度、LEDへのボタン状態、そしてluma.oledを通じたOLEDへの測定値表示です。
"""Sensor rig check for a Raspberry Pi 4.
Probes the three I2C devices, then loops: two knobs through the ADS1115,
temperature and humidity from the SHT31, the readings on the OLED, and a
button that lights the LED. Everything here is what Raspberry Pi OS ships:
smbus2 for the bus, gpiozero for the LED and the button, luma.oled for
the display.
Wiring, on the 40-pin header:
I2C : ADS1115, SHT31 and OLED on GPIO2 (SDA) and GPIO3 (SCL), 3V3, GND
ADC : one potentiometer on ADS1115 A0, another on A1
GPIO : LED through 220 ohm on GPIO27, button between GPIO22 and GND
"""
import time
import smbus2
from gpiozero import LED, Button
from luma.core.interface.serial import i2c
from luma.core.render import canvas
from luma.oled.device import ssd1306
ADS1115, SHT31, OLED = 0x48, 0x44, 0x3C
NAMES = {ADS1115: "ADS1115", SHT31: "SHT31", OLED: "SSD1306 OLED"}
# ADS1115 config register, field by field (datasheet, table 8)
ADS_START = 0x8000 # OS: begin one conversion
ADS_MUX_SINGLE = 0x4000 # AINx against GND; the channel goes in bits 12 and 13
ADS_PGA_4V = 0x0200 # +/-4.096 V full scale
ADS_ONE_SHOT = 0x0100 # power down between conversions
ADS_128_SPS = 0x0080 # 128 samples per second
ADS_NO_COMP = 0x0003 # comparator off
ADS_LSB_VOLTS = 4.096 / 32768
bus = smbus2.SMBus(1)
led = LED(27)
button = Button(22, pull_up=True)
def present(address):
"""True when a device acknowledges its address on bus 1."""
try:
bus.write_quick(address)
return True
except OSError:
return False
def read_ads1115(channel):
"""One single-shot conversion on AIN0..AIN3, returned in volts."""
config = (ADS_START | ADS_MUX_SINGLE | (channel << 12)
| ADS_PGA_4V | ADS_ONE_SHOT | ADS_128_SPS | ADS_NO_COMP)
bus.write_i2c_block_data(ADS1115, 0x01, [config >> 8, config & 0xFF])
time.sleep(0.01)
hi, lo = bus.read_i2c_block_data(ADS1115, 0x00, 2)
raw = (hi << 8) | lo
if raw & 0x8000:
raw -= 0x10000
return raw * ADS_LSB_VOLTS
def read_sht31():
"""Single shot, high repeatability, no clock stretching (command 0x2400)."""
bus.i2c_rdwr(smbus2.i2c_msg.write(SHT31, [0x24, 0x00]))
time.sleep(0.02)
reply = smbus2.i2c_msg.read(SHT31, 6)
bus.i2c_rdwr(reply)
d = list(reply)
temperature = -45 + 175 * ((d[0] << 8) | d[1]) / 65535
humidity = 100 * ((d[3] << 8) | d[4]) / 65535
return temperature, humidity
print("I2C bus 1:")
for address, name in NAMES.items():
print(f" 0x{address:02X} {name}: {'found' if present(address) else 'missing'}")
print()
oled = ssd1306(i2c(port=1, address=OLED))
while True:
knob_a = read_ads1115(0)
knob_b = read_ads1115(1)
temperature, humidity = read_sht31()
pressed = button.is_pressed
led.value = pressed
print(f"A0 {knob_a:5.3f} V A1 {knob_b:5.3f} V {temperature:4.1f} C {humidity:3.0f} %RH"
+ (" button pressed" if pressed else ""))
with canvas(oled) as draw:
draw.text((0, 0), "Pi 4 sensor rig", fill="white")
draw.text((0, 16), f"A0 {knob_a:.2f}V A1 {knob_b:.2f}V", fill="white")
draw.text((0, 32), f"{temperature:.1f} C {humidity:.0f} %RH", fill="white")
draw.text((0, 48), "button " + ("down" if pressed else "up"), fill="white")
time.sleep(2)
注目に値する詳細が3つあります。ADS1115は、データシートがコンフィグレジスタを定義する方法に従ってフィールドごとに設定されているため、各定数をそのドキュメントの表8と照合できます。SHT31はi2c_rdwrで読み取られます。その測定コマンドはレジスタアドレスのない2バイトであり、通常のSMBus呼び出しでは表現できないからです。そしてluma.oledは、Raspberry Piのチュートリアルがpipでインストールするのと同じディスプレイライブラリです。ここではsmbus2やgpiozeroと同様、プリインストールされています。
まず基本モードで実行する
Runを押します。プローブが3つのチップを見つけ、ループが測定値を出力し、OLEDがそれを表示します。ポテンショメーターを回すとA0またはA1の電圧が追従し、SHT31をクリックすると報告される温度と湿度を設定でき、プッシュボタンを押し続けるとLEDが点灯します。

ここまではすべてブラウザ上で実行されました。次のステップでは、まったく同じプロジェクトをLinuxマシン上で実行します。
Linuxモードに切り替える
- シリアルモニターが開いていない場合は、ツールバーのSerialをクリックします。
- シリアルモニターのタイトルバー右側にあるLinux terminalをクリックします。

- Advanced Linux modeというダイアログが、これから起こることを説明します。プロジェクトが再起動し、起動には約1分かかり、すべてがブラウザよりも遅く動作します。Start Linux modeをクリックします。

ゲストはシリアルモニター内で起動します。シリアルモニターは本物のターミナルになっています。起動中、ツールバーのボードセレクターの隣にあるチップはLinuxと表示され、そのツールチップにはこのプロジェクトでLinuxモードが有効であることが示されます。

プロンプトが表示されると、Velxioはscript.pyをゲストのホームディレクトリにコピーして起動します。静かなサーバーでは、プロンプトは約20秒後に表示され、最初の測定値はボタンをクリックしてから約1分後に現れます。出力は以前と同じで、OLEDは同じluma.oled呼び出しによって駆動されますが、今度はカーネルの/dev/i2c-1を通じてゲスト内部からです。

シェルを使う
これがLinuxモードの目的です。ターミナル内をクリックし、Ctrl+Cを押してスクリプトを停止すると、Alpine Linuxシステムのrootプロンプトにいます。試すべきいくつかの行を以下に示します。
cat /etc/os-release | head -2
ls /dev/i2c* /dev/spidev* /dev/gpiochip*
gpioset gpiochip0 27=1
ls -la /home/pi
2行目はヘッダーバスをデバイスノードとして一覧表示します。/dev/i2c-0、/dev/i2c-1、/dev/spidev0.0、/dev/spidev0.1、そして/dev/gpiochip0です。これらはRaspberry Pi上と同じシステムコールに応答するため、それらを自ら開くライブラリ、Cプログラム、あるいは独自のioctlコードがキャンバス上の部品と通信できます。
3行目は、Raspberry Pi OS Bookwormが同梱するlibgpiodツールを使い、シェルからピンを駆動する現代的な方法です。Enterを押した瞬間、キャンバス上のGPIO27のLEDが点灯します。ゲストはlibgpiodバージョン1を搭載しているため、上記のようにチップは位置引数であり、--chipを使うバージョン2の記法は理解されません。

スクリプトを手動で再度実行するには、python3 script.pyと入力します。ブラウザエンジンに戻るには、同じタイトルバーのExit Linux terminalをクリックします。この選択はプロジェクトとともに保存されません。明日開き直してRunを押すと、基本モードに戻ります。

各モードで動作するもの
以下の表は、ドキュメントのRaspberry Piページの要約版であり、実際の製品に対してモジュールごとに検証したものです。
| モジュール | 基本モード | Linuxモード |
|---|---|---|
RPi.GPIO、gpiozero | プリインストール済み | プリインストール済み |
smbus2、spidev | プリインストール済み、本物のライブラリ | プリインストール済み、カーネルノード経由の本物のライブラリ |
serial(pyserial) | PiのUARTパスのみ | 実際のtty上で動作する本物のpyserial、/dev/serial0 |
adafruit_dht、Adafruit_DHT | プリインストール済み | プリインストール済み |
w1thermsensor、1-Wire sysfsツリー | プリインストール済み | requirements.txt経由。sysfsツリーは存在する |
luma.oled、RPLCD、ST7789、Adafruit Blinka | プリインストール済み | プリインストール済み |
gpiodとgpio*ツール | ブラウザ内にキャラクタデバイスなし | プリインストール済み |
cv2(OpenCV) | あり | ゲスト用ビルドなし |
picamera2 | あり、ウェブカメラ経由 | なし。rpicam-jpegとその仲間を使う |
requests、urllib | 許可リスト付きの外向きプロキシ経由 | ゲスト内にネットワークなし |
| PyPI上のその他すべて | requirements.txt経由 | requirements.txt経由、PyPIにmusl aarch64ホイールがある場合 |
試用セッションを無駄にしないために、どちらのモードでもできないことを先に挙げておきます。
- GPIOのアナログ入力。 PiにはADCがありません。2つ目のプロジェクトのように、センサーとボードの間にADS1115またはMCP3008を挟んでください。
pigpio。 これは/dev/gpiomem経由でペリフェラルレジスタをマップしますが、ここではそのマッピングを提供していません。RPi.GPIO、gpiozero、またはLinuxモードではlibgpiodを使ってください。apt、raspi-config、デスクトップ。 ゲストはRaspberry Pi OSではなくAlpine Linuxです。Python 3とpipはありますが、パッケージマネージャーとPiファームウェアツールはありません。- ゲスト内部からのネットワーク。 ゲスト内の何もインターネットやPyPIに到達できません。
requirements.txtに記載されたパッケージは起動前に解決され、プロンプトでインポート可能になります。 - 大規模な機械学習スタック。 PyTorchとTensorFlowは説明付きで拒否されます。
2つの習慣が時間を節約します。メインファイルをscript.pyと名付ければ、両モードが同じファイルを実行します。そしてサードパーティのパッケージを隣のrequirements.txtに書いておきましょう。スクリプトが不足しているパッケージをインポートすると、実行コンソールがその行を提示し、ボタンがそれを書き込んでくれます。
コストについて
アカウントなしでRaspberry Piの回路を配線することは誰でもできます。実行には制限があります。
- 無料アカウントでは、Raspberry Piファミリーで15分の試用セッションを3回利用できます。セッションはどちらのモードでもRunを押したときに開き、それが終了するまでのすべての実行は無料です。サインアウト状態では、ツールバーに最初の実行前にサインインを促すチップが表示されます。
- 有料プランであれば制限がなくなります。最も安いNo AIは月額4ドルまたは年額36ドルで、Raspberry PiとSTM32ボードを無制限の実行時間で含みます。MakerとProも同様に含みます。現在の価格は料金ページにあります。

試用セッションは両エンジンで同じなので、基本モードだけで済むスクリプトはそこに留めるべきです。1分ではなく数秒で起動し、LinuxゲストはLinuxにしかできないことのためにあります。
次に進む先
- Piのギャラリー例:Pi 4のボタンとLED、Pi 5のPIRモーションアラーム、smbus2経由のMPU6050、そしてPi 4ですべてのバスを同時に。
- Groveカタログ:Groveモジュールの記事では、キャンバス上でモジュールがどのように見えるか、そしてどのようにエミュレートされるかを解説しています。
- ドキュメント:完全な互換性表を備えたRaspberry Piファミリーページ、そしてRaspberry Pi 4とRaspberry Pi 5のピン配置。
まとめ
2つのプロジェクト、2つのエンジン。GroveアラームステーションはRaspberry Pi 5の基本モードで、空のキャンバスから数分で反応する回路へと至り、Raspberry Piのチュートリアルで使われるようなgpiozeroとAdafruit DHTのコードを使用しました。センサー装置はまずRaspberry Pi 4の基本モードで動作し、次にLinuxモードで、同じスクリプトがカーネルの/dev/i2c-1を通じてADS1115、SHT31、OLEDと通信し、プロンプトで入力したgpiosetコマンドがキャンバス上のLEDを点灯させました。基本モードはRaspberry Piのチュートリアルが求めるもののほとんどをカバーし、Linuxモードはターミナル、ファイル、システムツールのために存在し、その代償は1分の起動時間です。