注釈
こんにちは、SunFounder Raspberry Pi & Arduino & ESP32 愛好家コミュニティ(Facebook)へようこそ! Raspberry Pi、Arduino、ESP32 を仲間とともに深く学び、楽しみましょう。
参加する理由
専門的なサポート: 購入後の問題や技術的な課題を、コミュニティとチームがサポートします。
学びと共有: ヒントや作例を共有してスキルを高めます。
限定先行情報: 新製品発表やプレビューをいち早く入手できます。
特別割引: 最新製品の限定割引を受けられます。
季節イベントと景品: プレゼント企画や季節イベントに参加できます。
👉 ものづくりの世界を一緒に探検しませんか?[ここ] をクリックして今すぐ参加!
3.1.8 過熱モニター(MCP3008)
はじめに
工場などで回路の過熱を検知し、警報や自動停止を行う監視装置を作りたい場合があります。 このプロジェクトでは、サーミスタ、ジョイスティック、ブザー、LED、LCD を使い、しきい値を調整できる温度監視装置を作ります。
必要な部品
このプロジェクトで使用する部品は以下の通りです。
回路図
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 |
Pin15 |
3 |
22 |
GPIO23 |
Pin16 |
4 |
23 |
GPIO24 |
Pin18 |
5 |
24 |
SDA1 |
Pin 3 |
||
SCL1 |
Pin 5 |
実験手順
手順1: 回路を組み立てます。
手順2: コードのあるフォルダへ移動します。
cd ~/davinci-kit-for-raspberry-pi/c/3.1.8-2/
手順3: コードをコンパイルします。
gcc 3.1.8_OverheatMonitor.c -lm -lwiringPi
手順4: 実行します。
sudo ./a.out
実行すると、現在の温度と高温しきい値 40 が I2C LCD1602 に表示されます。 現在温度がしきい値を超えると、ブザーと LED が作動して警告します。
ジョイスティック はしきい値の調整に使用します。X軸またはY軸方向に倒すと、しきい値を上げ下げできます。 ジョイスティックを押すとしきい値を初期値にリセットします。
注釈
「wiringPi.h: No such file or directory」というエラーが出た場合は、wiringPi のインストールと確認 を参照してください。
「Unable to open I2C device: No such file or directory」というエラーが出た場合は、I²C 設定 を参照し、I2C を有効にして配線を確認してください。
コードと配線が正しいのに LCD が表示しない場合は、背面のボリュームを回してコントラストを調整してください。
コード
#include <wiringPi.h>
#include <stdio.h>
#include <wiringPiI2C.h>
#include <wiringPiSPI.h>
#include <string.h>
#include <math.h>
typedef unsigned char uchar;
typedef unsigned int uint;
#define Joy_BtnPin 3 // GPIO22 -> WiringPi 3
#define buzzPin 4 // GPIO23 -> WiringPi 4
#define LedPin 5 // GPIO24 -> WiringPi 5
#define SPI_CHANNEL 0
#define SPI_SPEED 1000000
int LCDAddr = 0x27;
int BLEN = 1;
int fd;
int upperTem = 40;
// Global variable to store the last joystick change
int lastJoystickChange = 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;
buffer[2] = 0;
wiringPiSPIDataRW(SPI_CHANNEL, buffer, 3);
return ((buffer[1] & 0x03) << 8) | buffer[2];
}
void write_word(int data){
int temp = data;
if (BLEN) temp |= 0x08;
else temp &= 0xF7;
wiringPiI2CWrite(fd, temp);
}
void send_command(int comm){
int buf = comm & 0xF0;
buf |= 0x04; write_word(buf); delay(2); buf &= 0xFB; write_word(buf);
buf = (comm & 0x0F) << 4;
buf |= 0x04; write_word(buf); delay(2); buf &= 0xFB; write_word(buf);
}
void send_data(int data){
int buf = data & 0xF0;
buf |= 0x05; write_word(buf); delay(2); buf &= 0xFB; write_word(buf);
buf = (data & 0x0F) << 4;
buf |= 0x05; write_word(buf); delay(2); buf &= 0xFB; write_word(buf);
}
void lcd_init(){
send_command(0x33); delay(5);
send_command(0x32); delay(5);
send_command(0x28); delay(5);
send_command(0x0C); delay(5);
send_command(0x01); wiringPiI2CWrite(fd, 0x08);
}
void lcd_clear(){
send_command(0x01);
}
void write_lcd(int x, int y, const char data[]){
int addr = 0x80 + 0x40 * y + x;
send_command(addr);
for (int i = 0; i < (int)strlen(data); i++)
send_data(data[i]);
}
int get_joystick_value(){
int x = read_ADC(1);
int y = read_ADC(2);
// Dead-band filtering to reduce small fluctuations
if (x > 900) return 1; // else if (x < 100) return -1; // else if (y > 900) return -10; // else if (y < 100) return 10; // else return 0;
}
void upper_tem_setting(){
write_lcd(0,0, "Upper Adjust:");
int change = get_joystick_value();
// Only respond to actual direction change
if (change != 0 && change != lastJoystickChange) {
upperTem += change;
lastJoystickChange = change;
}
else if (change == 0) {
// Allow next change after returning to center
lastJoystickChange = 0;
}
// Display current upperTem
char str[6];
snprintf(str, sizeof(str), "%d", upperTem);
write_lcd(0,1, str);
// Clear remaining LCD characters
write_lcd(strlen(str),1, " ");
delay(100);
}
double temperature(){
int raw = read_ADC(0);
double Vr = 3.3 * ((double)raw / 1023.0);
double Rt = 10000.0 * Vr / (3.3 - Vr);
double tempK = 1.0 / ((log(Rt/10000.0)/3950.0) + 1.0/(273.15+25.0));
return tempK - 273.15;
}
void monitoring_temp(){
char str[6];
double cel = temperature();
snprintf(str, sizeof(str), "%.2f", cel);
write_lcd(0,0, "Temp: ");
write_lcd(6,0, str);
snprintf(str, sizeof(str), "%d", upperTem);
write_lcd(0,1, "Upper: ");
write_lcd(7,1, str);
delay(100);
if (cel >= upperTem) {
digitalWrite(buzzPin, HIGH);
digitalWrite(LedPin, HIGH);
} else {
digitalWrite(buzzPin, LOW);
digitalWrite(LedPin, LOW);
}
}
void setup_all(){
fd = wiringPiI2CSetup(LCDAddr);
lcd_init();
if (wiringPiSetup() == -1 ||
wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1) {
printf("Setup failed!\n");
return;
}
pinMode(Joy_BtnPin, INPUT);
pullUpDnControl(Joy_BtnPin, PUD_UP);
pinMode(buzzPin, OUTPUT);
pinMode(LedPin, OUTPUT);
}
int main(void){
setup_all();
int lastBtnState = HIGH;
int stage = 0;
while (1) {
int curBtn = digitalRead(Joy_BtnPin);
// Switch mode when button changes from LOW to HIGH (button released)
if (curBtn == HIGH && lastBtnState == LOW) {
stage = (stage + 1) % 2;
lastJoystickChange = 0; // Clear debounce status
delay(100);
lcd_clear();
}
lastBtnState = curBtn;
if (stage == 1)
upper_tem_setting();
else
monitoring_temp();
}
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;
buffer[2] = 0;
wiringPiSPIDataRW(SPI_CHANNEL, buffer, 3);
return ((buffer[1] & 0x03) << 8) | buffer[2];
}
MCP3008 の指定チャンネル(CH0〜CH7)から SPI を使って 10ビットのアナログ値を読み取り、0〜1023 の整数で返します。
int get_joystick_value() {
int x = read_ADC(1);
int y = read_ADC(2);
if (x > 900) return 1; // Right
else if (x < 100) return -1; // Left
else if (y > 900) return -10; // Up
else if (y < 100) return 10; // Down
else return 0;
}
ジョイスティックの X 軸(CH1)と Y 軸(CH2)の値を読み取り、しきい値判定に基づき方向を数値で返します。
void upper_tem_setting() {
write_lcd(0,0, "Upper Adjust:");
int change = get_joystick_value();
if (change != 0 && change != lastJoystickChange) {
upperTem += change;
lastJoystickChange = change;
}
else if (change == 0) {
lastJoystickChange = 0;
}
char str[6];
snprintf(str, sizeof(str), "%d", upperTem);
write_lcd(0,1, str);
write_lcd(strlen(str),1, " ");
delay(100);
}
ジョイスティックで高温しきい値を調整します。方向を押し続けた場合の連続変更を防ぐ制御を行います。
double temperature() {
int raw = read_ADC(0);
double Vr = 3.3 * ((double)raw / 1023.0);
double Rt = 10000.0 * Vr / (3.3 - Vr);
double tempK = 1.0 / ((log(Rt/10000.0)/3950.0) + 1.0/(273.15+25.0));
return tempK - 273.15;
}
CH0 のサーミスタからアナログ値を読み取り、Steinhart–Hart の式を用いて摂氏温度に変換します。
void monitoring_temp() {
char str[6];
double cel = temperature();
snprintf(str, sizeof(str), "%.2f", cel);
write_lcd(0,0, "Temp: ");
write_lcd(6,0, str);
snprintf(str, sizeof(str), "%d", upperTem);
write_lcd(0,1, "Upper: ");
write_lcd(7,1, str);
delay(100);
if (cel >= upperTem) {
digitalWrite(buzzPin, HIGH);
digitalWrite(LedPin, HIGH);
} else {
digitalWrite(buzzPin, LOW);
digitalWrite(LedPin, LOW);
}
}
現在の温度と設定されたしきい値を LCD に表示し、温度がしきい値以上になるとブザーと LED を作動させます。
void setup_all() {
fd = wiringPiI2CSetup(LCDAddr);
lcd_init();
if (wiringPiSetup() == -1 || wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1) {
printf("Setup failed!\n");
return;
}
pinMode(Joy_BtnPin, INPUT);
pullUpDnControl(Joy_BtnPin, PUD_UP);
pinMode(buzzPin, OUTPUT);
pinMode(LedPin, OUTPUT);
}
LCD、SPI、GPIO の初期化を行い、ジョイスティックボタンにプルアップ設定をします。
int main(void) {
setup_all();
int lastBtnState = HIGH;
int stage = 0;
while (1) {
int curBtn = digitalRead(Joy_BtnPin);
if (curBtn == HIGH && lastBtnState == LOW) {
stage = (stage + 1) % 2;
lastJoystickChange = 0;
delay(100);
lcd_clear();
}
lastBtnState = curBtn;
if (stage == 1)
upper_tem_setting();
else
monitoring_temp();
}
return 0;
}
モード切替はジョイスティックボタンの押下・解放で行います。
モード0: 温度監視
モード1: しきい値調整