// 必要なライブラリをインクルード
#include // OLED用グラフィックライブラリ
#include // SSD1306 OLED用ライブラリ
#include // I2C通信ライブラリ
#include // WS2812用ライブラリ
#include "Adafruit_Keypad.h" // Adafruit製キーパッドライブラリ
// ピン設定
const int trigPin = 3; // 超音波センサーのTrigピン
const int echoPin = 2; // 超音波センサーのEchoピン
const int thermistorPin = A0; // サーミスタの接続ピン
const int motorIn1 = 7; // モーター制御ピン1
const int motorIn2 = 6; // モーター制御ピン2
const int objectLedPin = 5; // 単色LED制御ピン
#define LED_PIN 4 // NeoPixel LED制御ピン
#define LED_COUNT 8 // NeoPixel LEDの数
// OLEDディスプレイ設定
#define SCREEN_WIDTH 128 // OLEDの幅
#define SCREEN_HEIGHT 64 // OLEDの高さ
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1); // OLEDインスタンス作成
// NeoPixel LED設定
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800); // NeoPixelインスタンス作成
// キーパッド設定
const byte ROWS = 4; // 行数
const byte COLS = 4; // 列数
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {8, 9, 10, 11}; // キーパッドの行ピン
byte colPins[COLS] = {A1, A2, A3, 12}; // キーパッドの列ピン
// Adafruit_Keypadのインスタンス作成
Adafruit_Keypad keypad = Adafruit_Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// 動作設定
const float tempThreshold = 20.0; // 温度しきい値(℃)
const int objectDistanceThreshold = 20; // 距離しきい値(cm)
const byte MOTOR_PWM_MIN = 60; // モーター最低PWM
bool autoMode = true; // 自動モード(初期値:true)
byte manualWindLevel = 4; // 手動モード時の風力レベル(初期値:4)
// 初期化
void setup() {
Serial.begin(9600); // シリアル通信初期化
pinMode(trigPin, OUTPUT); // 超音波センサーのTrigピンを出力に設定
pinMode(echoPin, INPUT); // Echoピンを入力に設定
pinMode(motorIn1, OUTPUT); // モーター制御ピン1を出力に設定
pinMode(motorIn2, OUTPUT); // モーター制御ピン2を出力に設定
pinMode(objectLedPin, OUTPUT); // 単色LEDピンを出力に設定
// OLEDの初期化
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("OLED not found");
while (1); // OLEDが見つからない場合は停止
}
display.clearDisplay(); // OLED画面をクリア
display.display(); // OLEDにクリア状態を反映
strip.begin(); // NeoPixel LED初期化
strip.show(); // 全消灯
keypad.begin(); // Adafruitキーパッド初期化
}
// 超音波センサーによる距離の取得
long getDistance() {
digitalWrite(trigPin, LOW); // クリーンなパルスを送るためにLOWに設定
delayMicroseconds(2);
digitalWrite(trigPin, HIGH); // TrigピンをHIGHにして超音波を発信
delayMicroseconds(10);
digitalWrite(trigPin, LOW); // トリガーピンをLOWに戻す
long duration = pulseIn(echoPin, HIGH, 30000); // 最大30msまで待機
if (duration == 0) return 999; // タイムアウト時は999cm扱い
return duration * 0.034 / 2; // 距離の計算
}
// 温度の取得(摂氏)
float getTemperature() {
int analogValue = analogRead(thermistorPin);
const int beta = 3950; // サーミスタのB定数
const float resistance = 10000.0; // 10kΩ
float r = (1023.0 / analogValue - 1.0) * resistance; // サーミスタの抵抗値の計算
float tempK = 1.0 / (log(r / resistance) / beta + 1.0 / 298.15); // 温度の計算(ボルツマン定数)
return tempK - 273.15; // ケルビン → 摂氏
}
// 温度から風力レベル(0〜8)
byte calculateWindLevel(float temp) {
const float tempMin = 20.0; // 最低温度(20度以下は風力0)
const float tempMax = 36.0; // 最高温度(36度以上は風力8)
int level = map(temp * 10, (int)(tempMin * 10), (int)(tempMax * 10), 0, 8);
return constrain(level, 0, 8); // 0〜8に制限
}
// モーター回転速度を設定
void setMotorSpeed(byte level, long dist) {
if (level == 0) {
analogWrite(motorIn2, 0); // 停止
} else {
if (autoMode && dist > objectDistanceThreshold) {
analogWrite(motorIn2, 0); // 自動モードでは距離判定
} else {
byte pwm = map(level, 1, 8, MOTOR_PWM_MIN, 255);
analogWrite(motorIn2, pwm);
}
}
digitalWrite(motorIn1, LOW); // モーターの逆回転を防ぐため常にLOW
}
// モーターが回せるかどうか判定
bool canMotorRun(byte level, long dist) {
return (level >= 1 && dist <= objectDistanceThreshold);
}
// 風力レベルに応じた色(青→緑→赤)
uint32_t colorGradient(byte level) {
if (level == 0) return strip.Color(0, 0, 0); // 消灯
if (level >= 1 && level <= 4) {
// 青(0,0,255) → 緑(0,255,0)
float ratio = (level - 1) / 3.0;
byte r = 0;
byte g = 255 * ratio;
byte b = 255 * (1.0 - ratio);
return strip.Color(r, g, b);
} else if (level >= 5 && level <= 8) {
// 緑(0,255,0) → 赤(255,0,0)
float ratio = (level - 4) / 4.0;
byte r = 255 * ratio;
byte g = 255 * (1.0 - ratio);
byte b = 0;
return strip.Color(r, g, b);
}
return strip.Color(0, 0, 0); // 念のため
}
// NeoPixel LEDを更新
void updateWindLED(byte level, long dist) {
strip.clear(); // すべてのLEDを消灯
if (!autoMode) {
// 手動モードでは設定されたレベル分だけLEDを点灯
for (byte i = 0; i < level; i++) {
strip.setPixelColor(i, colorGradient(i + 1));
}
strip.show();
return;
}
if (!canMotorRun(level, dist)) {
// 自動モードでモーターが回らない条件ならLEDは消灯のまま
strip.show();
return;
}
for (byte i = 0; i < level; i++) {
strip.setPixelColor(i, colorGradient(i + 1)); // 風力レベルに応じた色
}
strip.show(); // LEDを更新
}
// 単色LEDの明るさ更新
void updateObjectLED(float temp, long dist) {
bool tempHigh = temp > tempThreshold; // 温度がしきい値を超えているか
bool objectNear = dist <= objectDistanceThreshold; // 物体が近いか
if (tempHigh && objectNear) {
analogWrite(objectLedPin, 255); // 高温かつ物体が近ければ明るさ最大
} else if (tempHigh || objectNear) {
analogWrite(objectLedPin, 128); // 温度が高いまたは物体が近い場合は明るさ半分
} else {
analogWrite(objectLedPin, 0); // それ以外は消灯
}
}
// OLED表示を更新
void updateDisplay(float temp, long dist, byte windLevel) {
display.clearDisplay(); // 画面をクリア
display.setTextSize(1); // フォントサイズを設定
display.setTextColor(SSD1306_WHITE); // 文字色を白に設定
display.setCursor(0, 0); // 画面の左上にカーソルを移動
// 温度、距離、風力レベル、モードを表示
display.print("Temp: ");
display.print(temp, 1);
display.println(" C");
display.print("Dist: ");
display.print(dist);
display.println(" cm");
display.print("Wind: ");
display.println(windLevel);
display.print("Mode: ");
display.println(autoMode ? "Auto" : "Manual");
display.display(); // 画面を更新
}
// メインループ
void loop() {
keypad.tick(); // キーパッドの状態更新
// キーパッドのイベントがあれば取得
while (keypad.available()) {
keypadEvent evt = keypad.read(); // ← ここを修正(keypad_event → keypadEvent)
if (evt.bit.EVENT == KEY_JUST_PRESSED) {
char key = evt.bit.KEY;
if (key == 'A') {
autoMode = !autoMode; // Aキーで自動・手動切り替え
} else if (!autoMode) {
if (key >= '1' && key <= '8') {
manualWindLevel = key - '0'; // 手動風力設定
} else if (key == '0') {
manualWindLevel = 0; // 手動風力停止
}
}
}
}
float temp = getTemperature(); // 温度取得
long dist = getDistance(); // 距離取得
byte windLevel = autoMode ? calculateWindLevel(temp) : manualWindLevel; // 風力決定
updateObjectLED(temp, dist); // 単色LED更新
updateWindLED(windLevel, dist); // NeoPixel更新
setMotorSpeed(windLevel, dist); // モーター制御
updateDisplay(temp, dist, windLevel); // OLED表示更新
delay(200); // 更新間隔
}