.. note:: こんにちは、SunFounder Raspberry Pi & Arduino & ESP32 愛好家コミュニティ(Facebook)へようこそ!Raspberry Pi、Arduino、ESP32 の世界を仲間と一緒にさらに深めましょう。 **参加する理由** - **専門的なサポート**: 購入後の問題や技術的な課題を、コミュニティやチームの助けで解決できます。 - **学びと共有**: ヒントやチュートリアルを交換し、スキルを高められます。 - **特別な先行公開**: 新製品の発表やプレビューにいち早くアクセスできます。 - **特別割引**: 最新製品を会員限定割引で入手できます。 - **季節のイベントやプレゼント企画**: プレゼントや季節ごとのキャンペーンに参加できます。 👉 一緒に探求と創作を始めませんか?今すぐ [|link_sf_facebook|] をクリックして参加しましょう! .. _3.1.4_c_mcp3008: 3.1.4 スマートファン(MCP3008) ====================================== .. note:: .. image:: img/mcp3008_and_adc0834.jpg :width: 25% :align: left お持ちのキットの種類によって、**ADC0834** または **MCP3008** を確認し、対応するセクションに進んでください。 はじめに ----------------- このプロジェクトでは、モーター、ボタン、サーミスタを使って、手動+自動のスマートファンを作ります。風量は調整可能です。 必要な部品 ------------------------------ このプロジェクトには以下の部品を使用します。 .. image:: img/list2_Smart_Fan.png :align: center 回路図 ------------------------ ============ ======== ======== === T-Board 名 物理 wiringPi BCM SPICE0 Pin 24 10 8 SPIMOSI Pin 19 12 10 SPIMISO Pin 21 13 9 SPISCLK Pin 23 14 11 GPIO22 Pin 15 3 22 GPIO5 Pin 29 21 5 GPIO6 Pin 31 22 6 GPIO13 Pin 33 23 13 ============ ======== ======== === .. image:: img/schematic_3.1.4_smart_fan_mcp3008.png :align: center 実験手順 ----------------------------- **手順1:** 回路を組み立てます。 .. image:: img/july24_3.1.4_smart_fan_mcp3008.png :align: center .. note:: 電源モジュールにはキット付属の9V電池スナップを使用して9V電池を接続できます。電源モジュールのジャンパーキャップをブレッドボードの5V電源ラインに差し込みます。 .. image:: img/image118.jpeg :align: center **C言語ユーザー向け** ^^^^^^^^^^^^^^^^^^^^^^^^^ **手順2**: コードのフォルダに移動します。 .. code-block:: cd ~/davinci-kit-for-raspberry-pi/c/3.1.4-2/ **手順3**: コンパイルします。 .. code-block:: gcc 3.1.4_SmartFan.c -o SmartFan -lwiringPi -lm **手順4**: 実行ファイルを実行します。 .. code-block:: ./SmartFan プログラム実行後、ボタンを押すとファンが動きます。押すたびに風量レベルが1段階ずつ上下し、**0~4** の5段階で調整されます。レベル4の状態でさらに押すと風量0になり停止します。 温度が ±2℃ 変化すると、自動的に風量が1段階増減します。 .. note:: 実行してもうまく動作しない場合や、エラー「wiringPi.h: No such file or directory」が出る場合は、:ref:`install_wiringpi` を参照してください。 コード -------- .. code-block:: c #include #include #include #include #include #define SPI_CHANNEL 0 #define SPI_SPEED 1000000 #define MotorPin1 21 #define MotorPin2 22 #define MotorEnable 23 #define BtnPin 3 int read_ADC(int channel) { if (channel < 0 || channel > 7) return -1; unsigned char buffer[3]; buffer[0] = 1; // Start bit buffer[1] = (8 + channel) << 4; // Single-ended mode and channel buffer[2] = 0; wiringPiSPIDataRW(SPI_CHANNEL, buffer, 3); int result = ((buffer[1] & 3) << 8) | buffer[2]; return result; } int temperture() { int analogVal = read_ADC(0); double Vr = 3.3 * analogVal / 1023.0; // Use 3.3V as Vref for MCP3008 double Rt = 10000.0 * Vr / (3.3 - Vr); double temp = 1 / (((log(Rt / 10000.0)) / 3950.0) + (1 / (273.15 + 25.0))); double cel = temp - 273.15; double Fah = cel * 1.8 + 32; printf("Celsius: %.2f C Fahrenheit: %.2f F\n", cel, Fah); return (int)cel; } int motor(int level) { if (level == 0) { digitalWrite(MotorEnable, LOW); return 0; } if (level >= 4) { level = 4; } digitalWrite(MotorEnable, HIGH); softPwmWrite(MotorPin1, level * 25); return level; } void setup() { if (wiringPiSetup() == -1) { printf("wiringPi setup failed!\n"); return; } if (wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1) { printf("SPI setup failed!\n"); return; } softPwmCreate(MotorPin1, 0, 100); softPwmCreate(MotorPin2, 0, 100); pinMode(MotorEnable, OUTPUT); pinMode(BtnPin, INPUT); } int main(void) { setup(); int currentState, lastState = 0; int level = 0; int currentTemp, markTemp = 0; while (1) { currentState = digitalRead(BtnPin); currentTemp = temperture(); if (currentTemp <= 0) continue; if (currentState == 1 && lastState == 0) { level = (level + 1) % 5; markTemp = currentTemp; delay(500); } lastState = currentState; if (level != 0) { if (currentTemp - markTemp <= -2) { level = level - 1; markTemp = currentTemp; } if (currentTemp - markTemp >= 2) { level = level + 1; markTemp = currentTemp; } } level = motor(level); } return 0; } Code Explanation ---------------------- .. code-block:: c int read_ADC(int channel) { if (channel < 0 || channel > 7) return -1; unsigned char buffer[3]; buffer[0] = 1; // Start bit buffer[1] = (8 + channel) << 4; // Single-ended mode and channel buffer[2] = 0; wiringPiSPIDataRW(SPI_CHANNEL, buffer, 3); int result = ((buffer[1] & 3) << 8) | buffer[2]; return result; } この関数は、MCP3008の指定した入力から値を読むものです。3バイトのSPI信号を送り、0~1023の10bitの値を返します。 .. code-block:: c int temperture() { int analogVal = read_ADC(0); double Vr = 3.3 * analogVal / 1023.0; // Use 3.3V as Vref for MCP3008 double Rt = 10000.0 * Vr / (3.3 - Vr); double temp = 1 / (((log(Rt / 10000.0)) / 3950.0) + (1 / (273.15 + 25.0))); double cel = temp - 273.15; double Fah = cel * 1.8 + 32; printf("Celsius: %.2f C Fahrenheit: %.2f F\n", cel, Fah); return (int)cel; } ``temperture()`` 関数は、MCP3008を通じてサーミスタの値を読み、電圧・抵抗を計算してから、セルシウスと華氏の温度に変換します(シュタインハルト・ハート近似式)。 .. code-block:: c int motor(int level) { if (level == 0) { digitalWrite(MotorEnable, LOW); return 0; } if (level >= 4) { level = 4; } digitalWrite(MotorEnable, HIGH); softPwmWrite(MotorPin1, level * 25); return level; } ``motor()`` 関数はPWMで扇風機の回転を制御します。レベル0では停止し、レベル1~4ではそれぞれ25%ずつ増加します。 .. code-block:: c void setup() { if (wiringPiSetup() == -1) { printf("wiringPi setup failed!\n"); return; } if (wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1) { printf("SPI setup failed!\n"); return; } softPwmCreate(MotorPin1, 0, 100); softPwmCreate(MotorPin2, 0, 100); pinMode(MotorEnable, OUTPUT); pinMode(BtnPin, INPUT); } ``setup()`` 関数はWiringPiを初期化し、SPIを設定し、PWMとGPIOを準備します。 .. code-block:: c int main(void) { setup(); int currentState, lastState = 0; int level = 0; int currentTemp, markTemp = 0; while (1) { currentState = digitalRead(BtnPin); currentTemp = temperture(); if (currentTemp <= 0) continue; if (currentState == 1 && lastState == 0) { level = (level + 1) % 5; markTemp = currentTemp; delay(500); } lastState = currentState; if (level != 0) { if (currentTemp - markTemp <= -2) { level = level - 1; markTemp = currentTemp; } if (currentTemp - markTemp >= 2) { level = level + 1; markTemp = currentTemp; } } level = motor(level); } return 0; } ``main()`` 関数は以下を行います: 1. 常に釦入力と温度を確認します。 2. 釦を押すと風量が1段階上がり(0~4を循環)、その時点の温度を記録します。 3. 温度が ±2℃ 変化したら風量を自動調整します。 4. ``motor(level)`` を呼び出して出力を更新します。 **Pythonユーザー向け** ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ **手順2:** SPIインターフェースを有効化し、 ``spidev`` ライブラリをインストールします(詳細は :ref:`spi_configuration` を参照)。既に完了している場合はスキップ可能です。 **手順3**: コードのフォルダに移動します。 .. code-block:: cd ~/davinci-kit-for-raspberry-pi/python **手順4**: 実行します。 .. code-block:: sudo python3 3.1.4-2_SmartFan.py プログラム実行後、ボタンを押すとファンが動きます。押すたびに風量レベルが1段階ずつ上下し、 **0~4** の5段階で調整されます。レベル4の状態でさらに押すと風量0になり停止します。 温度が ±2℃ 変化すると、自動的に風量が1段階増減します。 コード -------- .. note:: You can **Modify/Reset/Copy/Run/Stop** the code below. But before that, you need to go to source code path like ``davinci-kit-for-raspberry-pi/python``. After modifying the code, you can run it directly to see the effect. .. raw:: html .. code-block:: python #!/usr/bin/env python3 import RPi.GPIO as GPIO import spidev import time import math # Pin configuration BTN_PIN = 22 # Button GPIO (physical pin 15) MOTOR_IN1 = 5 # Motor forward MOTOR_IN2 = 6 # Motor backward MOTOR_EN = 13 # PWM enable pin # GPIO setup GPIO.setmode(GPIO.BCM) GPIO.setup(BTN_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(MOTOR_IN1, GPIO.OUT) GPIO.setup(MOTOR_IN2, GPIO.OUT) GPIO.setup(MOTOR_EN, GPIO.OUT) # PWM setup for motor speed control pwm = GPIO.PWM(MOTOR_EN, 1000) # 1kHz frequency pwm.start(0) # Initialize SPI for MCP3008 spi = spidev.SpiDev() spi.open(0, 0) # Bus 0, CE0 spi.max_speed_hz = 1000000 # 1 MHz # Global variables level = 0 currentTemp = 0 markTemp = 0 def read_adc(channel): if channel < 0 or channel > 7: return -1 adc = spi.xfer2([1, (8 + channel) << 4, 0]) value = ((adc[1] & 0x03) << 8) | adc[2] return value def temperature(): analogVal = read_adc(0) Vr = 3.3 * analogVal / 1023.0 Rt = 10000.0 * Vr / (3.3 - Vr) tempK = 1.0 / (((math.log(Rt / 10000.0)) / 3950.0) + (1.0 / (273.15 + 25.0))) Cel = tempK - 273.15 return Cel def motor_run(level): if level == 0: GPIO.output(MOTOR_IN1, GPIO.LOW) GPIO.output(MOTOR_IN2, GPIO.LOW) pwm.ChangeDutyCycle(0) return 0 if level >= 4: level = 4 GPIO.output(MOTOR_IN1, GPIO.HIGH) GPIO.output(MOTOR_IN2, GPIO.LOW) pwm.ChangeDutyCycle(level * 25) # Map level (1–4) to 25%–100% return level def changeLevel(channel): global level, currentTemp, markTemp print("Button pressed") level = (level + 1) % 5 markTemp = currentTemp # Add event detection for button press GPIO.add_event_detect(BTN_PIN, GPIO.FALLING, callback=changeLevel, bouncetime=300) def main(): global level, currentTemp, markTemp markTemp = temperature() while True: currentTemp = temperature() if level != 0: if currentTemp - markTemp <= -2: level -= 1 markTemp = currentTemp elif currentTemp - markTemp >= 2: if level < 4: level += 1 markTemp = currentTemp level = motor_run(level) time.sleep(0.2) try: main() except KeyboardInterrupt: pass finally: pwm.stop() GPIO.cleanup() spi.close() コード解説 --------------------- #. 必要な道具を導入: - ``RPi.GPIO`` … GPIO制御(押し釦と回転機) - ``spidev`` … MCP3008との通信 - ``time`` … 待機処理 - ``math`` … 対数関数を用いた温度計算 .. code-block:: python #!/usr/bin/env python3 import RPi.GPIO as GPIO import spidev import time import math #. GPIOの端子を設定: - 押し釦を GPIO22 に接続(内部プルアップ有効) - 回転機の制御に GPIO5(正転), GPIO6(逆転), GPIO13(PWM有効)を使用 .. code-block:: python BTN_PIN = 22 MOTOR_IN1 = 5 MOTOR_IN2 = 6 MOTOR_EN = 13 GPIO.setmode(GPIO.BCM) GPIO.setup(BTN_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(MOTOR_IN1, GPIO.OUT) GPIO.setup(MOTOR_IN2, GPIO.OUT) GPIO.setup(MOTOR_EN, GPIO.OUT) pwm = GPIO.PWM(MOTOR_EN, 1000) pwm.start(0) #. MCP3008とのSPI通信を初期化(バス0, チップイネーブル0, 速度1 MHz): .. code-block:: python spi = spidev.SpiDev() spi.open(0, 0) spi.max_speed_hz = 1000000 #. ``read_adc()`` 関数を定義(指定したMCP3008の入力端子から10bit値(0–1023)を読み取る): .. code-block:: python def read_adc(channel): if channel < 0 or channel > 7: return -1 adc = spi.xfer2([1, (8 + channel) << 4, 0]) value = ((adc[1] & 0x03) << 8) | adc[2] return value #. ``temperature()`` 関数を定義: - アナログ電圧を抵抗値に換算 - シュタインハルト・ハート式を使ってセルシウス温度を計算 .. code-block:: python def temperature(): analogVal = read_adc(0) Vr = 3.3 * analogVal / 1023.0 Rt = 10000.0 * Vr / (3.3 - Vr) tempK = 1.0 / (((math.log(Rt / 10000.0)) / 3950.0) + (1.0 / (273.15 + 25.0))) Cel = tempK - 273.15 return Cel #. ``motor_run()`` 関数を定義: - レベル0のときは回転機を停止 - レベル1~4のときは前進回転、PWMデューティ比を25%~100%に対応 .. code-block:: python def motor_run(level): if level == 0: GPIO.output(MOTOR_IN1, GPIO.LOW) GPIO.output(MOTOR_IN2, GPIO.LOW) pwm.ChangeDutyCycle(0) return 0 if level >= 4: level = 4 GPIO.output(MOTOR_IN1, GPIO.HIGH) GPIO.output(MOTOR_IN2, GPIO.LOW) pwm.ChangeDutyCycle(level * 25) return level #. ``changeLevel()`` コールバックを定義(押し釦が押されたとき): - レベルを 0~4 の範囲で循環的に増加 - 現在の温度を基準温度として記録 .. code-block:: python def changeLevel(channel): global level, currentTemp, markTemp print("Button pressed") level = (level + 1) % 5 markTemp = currentTemp GPIO.add_event_detect(BTN_PIN, GPIO.FALLING, callback=changeLevel, bouncetime=300) #. ``main()`` ループを定義: - 記録した温度との変化を監視 - 温度が2℃以上下がればレベルを1段階下げる - 温度が2℃以上上がればレベルを1段階上げる - 0.2秒ごとに回転機の速度を調整 .. code-block:: python def main(): global level, currentTemp, markTemp markTemp = temperature() while True: currentTemp = temperature() if level != 0: if currentTemp - markTemp <= -2: level -= 1 markTemp = currentTemp elif currentTemp - markTemp >= 2: if level < 4: level += 1 markTemp = currentTemp level = motor_run(level) time.sleep(0.2) #. ``main()`` を実行し、Ctrl+Cで停止したときに必ず後始末を行う(回転機停止、GPIO解放、SPI終了): .. code-block:: python try: main() except KeyboardInterrupt: pass finally: pwm.stop() GPIO.cleanup() spi.close()