注釈

こんにちは、SunFounderのRaspberry Pi & Arduino & ESP32愛好家コミュニティへようこそ!Facebook上でRaspberry Pi、Arduino、ESP32についてもっと深く掘り下げ、他の愛好家と交流しましょう。

参加する理由は?

  • エキスパートサポート:コミュニティやチームの助けを借りて、販売後の問題や技術的な課題を解決します。

  • 学び&共有:ヒントやチュートリアルを交換してスキルを向上させましょう。

  • 独占的なプレビュー:新製品の発表や先行プレビューに早期アクセスしましょう。

  • 特別割引:最新製品の独占割引をお楽しみください。

  • 祭りのプロモーションとギフト:ギフトや祝日のプロモーションに参加しましょう。

👉 私たちと一緒に探索し、創造する準備はできていますか?[ここ]をクリックして今すぐ参加しましょう!

3.1.5 電池残量表示器(MCP3008)

注釈

_images/mcp3008_and_adc0834.jpg

お持ちのキットの種類に応じて、 ADC0834MCP3008 を確認し、対応する節を参照してください。

はじめに

この課題では、電池の電圧を測定し、LEDバーグラフで残量を目に見える形で表示する装置を作ります。

警告

3.3Vを超える電池部品を使用しないでください。過負荷によりチップやRaspberry Piが損傷する可能性があります。

必要な部品

この課題には以下の部品を使います。

_images/list2_Battery_Indicator.png

回路図

T-Board 名

物理

wiringPi

BCM

SPICE0

Pin 24

10

8

SPIMOSI

Pin 19

12

10

SPIMISO

Pin 21

13

9

SPISCLK

Pin 23

14

11

GPIO25

Pin 22

6

25

GPIO12

Pin 32

26

12

GPIO16

Pin 36

27

16

GPIO20

Pin 38

28

20

GPIO21

Pin 40

29

21

GPIO5

Pin 29

21

5

GPIO6

Pin 31

22

6

GPIO13

Pin 33

23

13

GPIO19

Pin 35

24

19

GPIO26

Pin 37

25

26

_images/schematic_battery_indicator_mcp3008.png

実験手順

手順1: 回路を組み立てます。

_images/july24_3.1.5_battery_indicator_mcp3008.png

C言語利用者向け

手順2: コードのある場所に移動します。

cd ~/davinci-kit-for-raspberry-pi/c/3.1.5-2/

手順3: コードをコンパイルします。

gcc 3.1.5_BatteryIndicator.c -lwiringPi

手順4: 実行ファイルを実行します。

sudo ./a.out

プログラムが動作したら、MCP3008の3番ピンとGNDからそれぞれ導線を引き出し、電池の両極に接続します。LEDバーグラフ上の対応するLEDが点灯し、電圧レベル(測定範囲: 0–5V)が表示されます。

注釈

実行後に動作しない、あるいは「wiringPi.h: No such file or directory」と表示される場合は、wiringPi のインストールと確認 を参照してください。

コード

#include <wiringPi.h>
#include <wiringPiSPI.h>
#include <stdio.h>

#define SPI_CHANNEL 0
#define SPI_SPEED   1000000  // 1MHz
#define VREF        3.3

int pins[10] = {6, 26, 27, 28, 29, 21, 22, 23, 24, 25};

int read_ADC(int channel)
{
    if (channel < 0 || channel > 7) return -1;

    unsigned char buffer[3];
    buffer[0] = 1;  // 開始ビット
    buffer[1] = (8 + channel) << 4;  // 単端入力モード
    buffer[2] = 0;

    wiringPiSPIDataRW(SPI_CHANNEL, buffer, 3);

    int value = ((buffer[1] & 3) << 8) | buffer[2];
    return value;
}

void LedBarGraph(int value) {
    for (int i = 0; i < 10; i++) {
        if (i < value)
            digitalWrite(pins[i], HIGH);
        else
            digitalWrite(pins[i],LOW);
    }
}

int main(void)
{
    if (wiringPiSetup() == -1) {
        printf("setup wiringPi failed!\n");
        return 1;
    }

    if (wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1) {
        printf("SPI setup failed!\n");
        return 1;
    }

    for (int i = 0; i < 10; i++) {
        pinMode(pins[i], OUTPUT);
        digitalWrite(pins[i], HIGH);
    }

    while (1) {
        int analogVal = read_ADC(0);  // MCP3008 CH0
        if (analogVal < 0) continue;

        float voltage = analogVal * VREF / 1023.0;
        int level = analogVal * 10 / 1024;
        if (level > 10) level = 10;

        LedBarGraph(level);

        printf("ADC Value: %d\tVoltage: %.2f V\tLevel: %d\n", analogVal, voltage, level);

        delay(200);
    }

    return 0;
}

コード解説

int read_ADC(int channel)
{
    if (channel < 0 || channel > 7) return -1;

    unsigned char buffer[3];
    buffer[0] = 1;  // 開始ビット
    buffer[1] = (8 + channel) << 4;  // 単端入力モード, CH0~CH7
    buffer[2] = 0;

    wiringPiSPIDataRW(SPI_CHANNEL, buffer, 3);

    int value = ((buffer[1] & 3) << 8) | buffer[2];  // 10bitの結果を結合
    return value;
}

この関数は、SPIを介してMCP3008からアナログ値を読み取ります。 channel 引数で 8つの入力端子(CH0–CH7)のいずれかを選択します。 MCP3008は0~1023の範囲の10bitの値を返し、これはアナログ電圧を表します。

void LedBarGraph(int value) {
    for (int i = 0; i < 10; i++) {
        if (i < value)
            digitalWrite(pins[i], HIGH);  // LEDを点灯(アクティブHIGH配線を仮定)
        else
            digitalWrite(pins[i], LOW);   // LEDを消灯
    }
}

この関数は10個のLEDで構成されたバーグラフを制御します。 各LEDは電圧範囲の1/10を表します。 指定されたレベルまで順にLEDが点灯します。

注意: この実装は、LEDアノードがGPIO、カソードがGNDに接続されている(アクティブHIGH)の場合を想定しています。

int main(void)
{
    if (wiringPiSetup() == -1) {
        printf("setup wiringPi failed!\n");
        return 1;
    }

    if (wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1) {
        printf("SPI setup failed!\n");
        return 1;
    }

    for (int i = 0; i < 10; i++) {
        pinMode(pins[i], OUTPUT);
        digitalWrite(pins[i], HIGH);  // 初期状態で全LEDを点灯
    }

    while (1) {
        int analogVal = read_ADC(0);  // CH0の電圧を読み取る
        if (analogVal < 0) continue;

        float voltage = analogVal * VREF / 1023.0;
        int level = analogVal * 10 / 1024;  // 0~10段階に変換
        if (level > 10) level = 10;

        LedBarGraph(level);  // LEDでレベルを表示

        printf("ADC Value: %d\tVoltage: %.2f V\tLevel: %d\n", analogVal, voltage, level);

        delay(200);  // 更新速度: 5 Hz
    }

    return 0;
}

主な処理の流れ:

  • wiringPiとSPI通信を初期化する

  • 10個のLED制御用にGPIOを出力に設定する

  • MCP3008(CH0)でアナログ電圧を読み取る

  • 読み取った値を基準電圧 VREF = 3.3V で電圧に換算する

  • 電圧を0~10段階に割り当ててLEDバーグラフに表示する

  • シリアル画面にADC値・電圧(V単位)・LEDレベルを表示する

これにより、電池残量表示器または簡易電圧計として動作します。

Python利用者向け

手順2: SPIインターフェースを有効化し、 spidev ライブラリを導入します(詳細は SPI 設定 を参照)。すでに済んでいる場合は省略できます。

手順3: コードのある場所に移動します。

cd ~/davinci-kit-for-raspberry-pi/python

手順4: 実行ファイルを実行します。

sudo python3 3.1.5-2_BatteryIndicator.py

プログラムを実行したら、ADC0834の3番ピンとGNDからそれぞれ導線を引き出し、電池の両極に接続します。LEDバーグラフ上の対応するLEDが点灯し、電圧レベル(測定範囲: 0–5V)が表示されます。

警告

もし RuntimeError: Cannot determine SOC peripheral base address という誤りが出た場合は、「gpiozero」が動作しない場合。 を参照してください。

コード

注釈

以下のコードは 修正/リセット/コピー/実行/停止 が可能です。ただしその前に davinci-kit-for-raspberry-pi/python のようなソースコードの場所に移動しておく必要があります。修正後は直接実行して動作を確認できます。

#!/usr/bin/env python3

import RPi.GPIO as GPIO
import spidev
import time

# 左から右に並んだ10個のLEDに接続されたGPIOピン
led_pins = [25, 12, 16, 20, 21, 5, 6, 13, 19, 26]  # BCM番号

# GPIO設定
GPIO.setmode(GPIO.BCM)
for pin in led_pins:
    GPIO.setup(pin, GPIO.OUT)
    GPIO.output(pin, GPIO.LOW)

# SPI初期化
spi = spidev.SpiDev()
spi.open(0, 0)  # バス0, CE0
spi.max_speed_hz = 1000000  # 1 MHz

# MCP3008のチャネルから値を読み取る
def read_adc(channel):
    if channel < 0 or channel > 7:
        return -1
    r = spi.xfer2([1, (8 + channel) << 4, 0])
    value = ((r[1] & 0x03) << 8) | r[2]
    return value

# 値に応じてLEDバーグラフを点灯
def led_bar_graph(level):
    for i, pin in enumerate(led_pins):
        if i < level:
            GPIO.output(pin, GPIO.HIGH)
        else:
            GPIO.output(pin, GPIO.LOW)

# 主ループ
try:
    while True:
        analog_val = read_adc(0)  # MCP3008 CH0から読み取る
        level = int(analog_val * 10 / 1023)
        led_bar_graph(level)
        print(f"ADC: {analog_val}, Level: {level}")
        time.sleep(0.2)

except KeyboardInterrupt:
    pass

finally:
    for pin in led_pins:
        GPIO.output(pin, GPIO.LOW)
    GPIO.cleanup()
    spi.close()

コード解説

このプログラムは、MCP3008からアナログ電圧を読み取り、その結果をRaspberry Pi(BCMピン配置)で10個のLEDバーグラフに表示します。

  1. モジュールを導入

    • RPi.GPIO … Raspberry PiのGPIO制御

    • spidev … SPIを介してMCP3008と通信

    • time … 待機/スリープ処理

    #!/usr/bin/env python3
    
    import RPi.GPIO as GPIO
    import spidev
    import time
    
  2. GPIO LED設定

    10個のGPIOピンをLED制御用に定義。出力に設定し、初期状態はLOW(消灯)。

    led_pins = [25, 12, 16, 20, 21, 5, 6, 13, 19, 26]
    
    GPIO.setmode(GPIO.BCM)
    for pin in led_pins:
        GPIO.setup(pin, GPIO.OUT)
        GPIO.output(pin, GPIO.LOW)
    
  3. SPI初期化

    MCP3008と通信するためにバス0、CE0を使用。通信速度は1 MHzに設定。

    spi = spidev.SpiDev()
    spi.open(0, 0)
    spi.max_speed_hz = 1000000
    
  4. ADC読み取り関数

    MCP3008の指定チャネル(0–7)から値を読み取る。3バイトのSPI信号を送り、10bitの結果を取得。

    def read_adc(channel):
        if channel < 0 or channel > 7:
            return -1
        r = spi.xfer2([1, (8 + channel) << 4, 0])
        value = ((r[1] & 0x03) << 8) | r[2]
        return value
    
  5. LEDバーグラフ関数

    アナログ値に応じてLEDを点灯。例えばレベル7なら最初の7個が点灯し、残りは消灯。

    def led_bar_graph(level):
        for i, pin in enumerate(led_pins):
            if i < level:
                GPIO.output(pin, GPIO.HIGH)
            else:
                GPIO.output(pin, GPIO.LOW)
    
  6. 主ループ

    CH0からアナログ値を読み取り、0~10の範囲にスケーリングし、LEDに表示。シリアルにADC値とレベルを出力。

    try:
        while True:
            analog_val = read_adc(0)
            level = int(analog_val * 10 / 1023)
            led_bar_graph(level)
            print(f"ADC: {analog_val}, Level: {level}")
            time.sleep(0.2)
    
  7. 終了処理

    Ctrl+Cで停止したとき、すべてのLEDを消灯し、GPIOを解放し、SPIを閉じる。

    except KeyboardInterrupt:
        pass
    
    finally:
        for pin in led_pins:
            GPIO.output(pin, GPIO.LOW)
        GPIO.cleanup()
        spi.close()