#include <Arduino.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <SPIFFS.h>
#include "DHT.h"
#define LED_BUILTIN 2
#define IR_RECEIVE_PIN 15
#define IR_SEND_PIN 4
#define INDI_LED 17
#define DHT11_PIN 19
#define DHT11_PW 18
#define DHTTYPE DHT11
#define RAW_BUFFER_LENGTH 3000
#define RECORD_GAP_MICROS 25000
#define MARK_EXCESS_MICROS 20
#include <IRremote.hpp>
const char *ssid = "MyNetworkSSID";
const char *password = "mynetworkpassword";
WiFiServer server(5000);
DHT dht(DHT11_PIN, DHTTYPE);
const char *AUTO_IR_FILE = "/auto.ir";
const char *CONFIG_FILE = "/config.txt";
const uint32_t AUTO_SEND_DELAY_MS = 120000;
const float TEMP_WATCH_HIGH_MODE = 27.0;
const uint32_t TEMP_INTERVAL_NORMAL_MS = 60UL * 60UL * 1000UL;
const uint32_t TEMP_INTERVAL_HIGH_MS = 30UL * 60UL * 1000UL;
const uint32_t TEMP_RECHECK_DELAY_MS = 5UL * 60UL * 1000UL;
const uint32_t TEMP_WATCH_START_DELAY_MS = 5UL * 60UL * 1000UL;
float limitTemperature = 31.0;
enum IndicatorMode {
INDI_CONNECTING,
INDI_STANDBY,
INDI_LEARN,
INDI_SEND
};
volatile IndicatorMode indiMode = INDI_CONNECTING;
volatile uint32_t uptimeMinutes = 0;
volatile uint32_t lastIrSendMinute = 0;
String lastIrSendReason = "NONE";
bool hasLastIrSend = false;
int ipNo = 0;
bool serverStarted = false;
static uint16_t rawData[RAW_BUFFER_LENGTH];
static uint16_t rawLen = 0;
SemaphoreHandle_t irMutex;
SemaphoreHandle_t dhtMutex;
SemaphoreHandle_t statusMutex;
// =====================================================
// uptime分カウンタ
// =====================================================
void uptimeTask(void *args) {
while (true) {
vTaskDelay(pdMS_TO_TICKS(60000) );
uptimeMinutes++;
}
}
// =====================================================
// 最終IR送信記録
// =====================================================
void recordIrSendReason(const String &reason) {
if (statusMutex) xSemaphoreTake(statusMutex, portMAX_DELAY);
lastIrSendMinute = uptimeMinutes;
lastIrSendReason = reason;
hasLastIrSend = true;
if (statusMutex) xSemaphoreGive(statusMutex);
Serial.print("IR send reason recorded: ");
Serial.print(reason);
Serial.print(" at ");
Serial.print(lastIrSendMinute);
Serial.println(" min");
}
// =====================================================
// インジケータLED
// =====================================================
void indicatorTask(void *args) {
pinMode(INDI_LED, OUTPUT);
while (true) {
if (indiMode == INDI_LEARN) {
digitalWrite(INDI_LED, HIGH);
vTaskDelay(pdMS_TO_TICKS(80) );
digitalWrite(INDI_LED, LOW);
vTaskDelay(pdMS_TO_TICKS(80) );
continue;
}
if (indiMode == INDI_SEND) {
digitalWrite(INDI_LED, HIGH);
vTaskDelay(pdMS_TO_TICKS(50) );
digitalWrite(INDI_LED, LOW);
vTaskDelay(pdMS_TO_TICKS(50) );
continue;
}
if (WiFi.status() != WL_CONNECTED || ipNo <= 0) {
digitalWrite(INDI_LED, HIGH);
vTaskDelay(pdMS_TO_TICKS(100) );
digitalWrite(INDI_LED, LOW);
vTaskDelay(pdMS_TO_TICKS(100) );
continue;
}
digitalWrite(INDI_LED, LOW);
vTaskDelay(pdMS_TO_TICKS(3000) );
for (int i = 0; i < ipNo; i++) {
if (indiMode == INDI_LEARN || indiMode == INDI_SEND) break;
digitalWrite(INDI_LED, HIGH);
vTaskDelay(pdMS_TO_TICKS(250) );
digitalWrite(INDI_LED, LOW);
vTaskDelay(pdMS_TO_TICKS(250) );
}
}
}
// =====================================================
// 設定保存
// =====================================================
void saveConfig() {
File file = SPIFFS.open(CONFIG_FILE, FILE_WRITE);
if (!file) {
Serial.println("Config save failed");
return;
}
file.print("LIMIT=");
file.println(limitTemperature, 1);
file.close();
Serial.print("Config saved. LIMIT=");
Serial.println(limitTemperature, 1);
}
// =====================================================
// 設定読み込み
// =====================================================
void loadConfig() {
if (!SPIFFS.exists(CONFIG_FILE) ) {
Serial.println("No config file. Use default config.");
saveConfig();
return;
}
File file = SPIFFS.open(CONFIG_FILE, FILE_READ);
if (!file) {
Serial.println("Config open failed. Use current default.");
return;
}
while (file.available() ) {
String line = file.readStringUntil('\n');
line.trim();
if (line.startsWith("LIMIT=") ) {
float value = line.substring(6).toFloat();
if (value >= 0.0 && value <= 50.0) {
limitTemperature = value;
}
}
}
file.close();
Serial.print("Loaded LIMIT=");
Serial.println(limitTemperature, 1);
}
// =====================================================
// CSV RAWを配列へ変換
// =====================================================
bool parseRawCsv(const String &data) {
rawLen = 0;
int start = 0;
while (start < data.length() && rawLen < RAW_BUFFER_LENGTH) {
int comma = data.indexOf(',', start);
String token;
if (comma == -1) {
token = data.substring(start);
start = data.length();
} else {
token = data.substring(start, comma);
start = comma + 1;
}
token.trim();
if (token.length() == 0) continue;
uint32_t value = token.toInt();
if (value > 0 && value <= 65535) {
rawData[rawLen++] = (uint16_t)value;
}
}
Serial.print("Parsed RAW length: ");
Serial.println(rawLen);
return rawLen > 0;
}
// =====================================================
// RAW送信本体
// =====================================================
bool sendRawString(const String &payload) {
if (!parseRawCsv(payload) ) {
return false;
}
if (irMutex) xSemaphoreTake(irMutex, portMAX_DELAY);
indiMode = INDI_SEND;
Serial.print("Sending RAW length: ");
Serial.println(rawLen);
IrReceiver.stop();
delay(50);
IrSender.sendRaw(rawData, rawLen, 38);
delay(100);
IrReceiver.start();
indiMode = INDI_STANDBY;
if (irMutex) xSemaphoreGive(irMutex);
return true;
}
// =====================================================
// SPIFFSから自動送信用IRコードを読む
// =====================================================
bool loadAutoRaw(String &raw) {
raw = "";
if (!SPIFFS.exists(AUTO_IR_FILE) ) {
Serial.println("No auto IR file");
return false;
}
File file = SPIFFS.open(AUTO_IR_FILE, FILE_READ);
if (!file) {
Serial.println("Failed to open auto IR file");
return false;
}
raw = file.readString();
file.close();
raw.trim();
if (raw.startsWith("RAW:") ) {
raw = raw.substring(4);
raw.trim();
}
if (raw.length() == 0) {
Serial.println("Auto IR file is empty");
return false;
}
return true;
}
// =====================================================
// 冷房ON信号送信 + 10秒後再送
// =====================================================
void sendCoolingTwice(const String &reason) {
String raw;
if (!loadAutoRaw(raw) ) {
Serial.println("Cooling IR code not found");
return;
}
Serial.println("Sending cooling IR...");
bool ok1 = sendRawString(raw);
delay(10000);
Serial.println("Sending cooling IR again...");
bool ok2 = sendRawString(raw);
if (ok1 || ok2) {
Serial.println("Cooling IR sent");
recordIrSendReason(reason);
} else {
Serial.println("Cooling IR failed");
}
}
// =====================================================
// 自動送信タスク
// WiFi接続を待たずに120秒後に送信
// =====================================================
void autoSendTask(void *args) {
Serial.println("AutoSend task started");
if (!SPIFFS.exists(AUTO_IR_FILE) ) {
Serial.println("No auto IR file");
vTaskDelete(NULL);
return;
}
Serial.println("Auto IR file exists. Waiting 120 sec...");
vTaskDelay(pdMS_TO_TICKS(AUTO_SEND_DELAY_MS) );
sendCoolingTwice("BOOT_AUTO_SEND");
Serial.println("AutoSend task finished");
vTaskDelete(NULL);
}
// =====================================================
// DHT 温湿度
// =====================================================
String getTempHumidity() {
if (dhtMutex) xSemaphoreTake(dhtMutex, portMAX_DELAY);
digitalWrite(DHT11_PW, HIGH);
delay(1500);
float humidity = dht.readHumidity();
float temperature = dht.readTemperature();
digitalWrite(DHT11_PW, LOW);
if (dhtMutex) xSemaphoreGive(dhtMutex);
if (isnan(humidity) || isnan(temperature) ) {
return "ERROR:DHT_READ_FAILED";
}
return String( (int)temperature ) + ":" + String( (int)humidity );
}
// =====================================================
// 温度だけ取得
// =====================================================
float readTemperatureOnly() {
if (dhtMutex) xSemaphoreTake(dhtMutex, portMAX_DELAY);
digitalWrite(DHT11_PW, HIGH);
delay(1500);
float temperature = dht.readTemperature();
digitalWrite(DHT11_PW, LOW);
if (dhtMutex) xSemaphoreGive(dhtMutex);
if (isnan(temperature) ) {
Serial.println("Temperature read failed");
} else {
Serial.print("Temperature: ");
Serial.println(temperature);
}
return temperature;
}
// =====================================================
// 温度監視タスク
// =====================================================
void tempWatchTask(void *args) {
Serial.println("TempWatch task started");
Serial.println("TempWatch waits 5 min before first check...");
vTaskDelay(pdMS_TO_TICKS(TEMP_WATCH_START_DELAY_MS) );
uint32_t nextInterval = TEMP_INTERVAL_NORMAL_MS;
while (true) {
Serial.println("TempWatch: measuring...");
float temp = readTemperatureOnly();
if (isnan(temp) ) {
Serial.println("TempWatch: read failed. Next check in 30 min.");
vTaskDelay(pdMS_TO_TICKS(TEMP_INTERVAL_HIGH_MS) );
continue;
}
if (temp > TEMP_WATCH_HIGH_MODE) {
nextInterval = TEMP_INTERVAL_HIGH_MS;
Serial.println("TempWatch: over 27C. Next interval = 30 min.");
} else {
nextInterval = TEMP_INTERVAL_NORMAL_MS;
Serial.println("TempWatch: 27C or below. Next interval = 1 hour.");
}
if (limitTemperature > 0.0 && temp >= limitTemperature) {
Serial.print("TempWatch: limit detected. LIMIT=");
Serial.println(limitTemperature, 1);
Serial.println("TempWatch: recheck after 5 min.");
vTaskDelay(pdMS_TO_TICKS(TEMP_RECHECK_DELAY_MS) );
float recheckTemp = readTemperatureOnly();
if (limitTemperature > 0.0 &&
!isnan(recheckTemp) &&
recheckTemp >= limitTemperature) {
Serial.println("TempWatch: still over limit. Cooling ON.");
sendCoolingTwice("TEMP_LIMIT");
Serial.println("TempWatch: next check in 30 min.");
vTaskDelay(pdMS_TO_TICKS(TEMP_INTERVAL_HIGH_MS) );
continue;
}
Serial.println("TempWatch: recheck below limit. Next check in 30 min.");
vTaskDelay(pdMS_TO_TICKS(TEMP_INTERVAL_HIGH_MS) );
continue;
}
if (limitTemperature <= 0.0) {
Serial.println("TempWatch: LIMIT is OFF. Auto cooling disabled.");
}
vTaskDelay(pdMS_TO_TICKS(nextInterval) );
}
}
// =====================================================
// WiFi接続・再接続
// =====================================================
void connectToWiFi() {
if (WiFi.status() == WL_CONNECTED) {
return;
}
indiMode = INDI_CONNECTING;
serverStarted = false;
WiFi.disconnect(false);
delay(200);
WiFi.mode(WIFI_STA);
WiFi.setSleep(false);
WiFi.setAutoReconnect(true);
WiFi.persistent(false);
Serial.print("Connecting WiFi: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
int retry = 0;
while (WiFi.status() != WL_CONNECTED && retry < 40) {
delay(500);
Serial.print(".");
retry++;
}
Serial.println();
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi connect failed. Retry later.");
indiMode = INDI_CONNECTING;
return;
}
IPAddress ip = WiFi.localIP();
ipNo = ip[3];
Serial.print("WiFi connected. IP: ");
Serial.println(ip);
Serial.print("IP last number: ");
Serial.println(ipNo);
server.begin();
serverStarted = true;
Serial.println("TCP server started");
indiMode = INDI_STANDBY;
}
// =====================================================
// 1行受信
// =====================================================
bool readLine(WiFiClient &client, String &line, uint32_t timeoutMs = 30000) {
line = "";
uint32_t start = millis();
while (millis() - start < timeoutMs) {
while (client.available() ) {
char c = client.read();
if (c == '\r') continue;
if (c == '\n') return true;
line += c;
if (line.length() > 70000) {
Serial.println("ERROR: input too long");
return false;
}
}
delay(1);
}
return false;
}
// =====================================================
// IR学習
// =====================================================
void learnIR(WiFiClient &client) {
if (irMutex) xSemaphoreTake(irMutex, portMAX_DELAY);
indiMode = INDI_LEARN;
Serial.println("Waiting IR signal...");
uint32_t start = millis();
while (millis() - start < 10000) {
if (IrReceiver.decode() ) {
rawLen = IrReceiver.irparams.rawlen - 1;
if (rawLen > RAW_BUFFER_LENGTH) {
rawLen = RAW_BUFFER_LENGTH;
}
Serial.print("Received RAW length: ");
Serial.println(rawLen);
client.print("RAW:");
for (uint16_t i = 1; i <= rawLen; i++) {
uint32_t microsValue = IrReceiver.irparams.rawbuf[i] * MICROS_PER_TICK;
if (microsValue > 65535) {
microsValue = 65535;
}
client.print(microsValue);
if (i < rawLen) {
client.print(",");
}
}
client.print("\n");
IrReceiver.resume();
indiMode = INDI_STANDBY;
if (irMutex) xSemaphoreGive(irMutex);
return;
}
delay(1);
}
client.print("ERROR:IR_TIMEOUT\n");
indiMode = INDI_STANDBY;
if (irMutex) xSemaphoreGive(irMutex);
}
// =====================================================
// IR送信
// =====================================================
void sendIR(WiFiClient &client, const String &payload) {
bool ok = sendRawString(payload);
if (ok) {
recordIrSendReason("MANUAL_IR");
client.print("OK:SENT\n");
} else {
client.print("ERROR:RAW_PARSE_FAILED\n");
}
}
// =====================================================
// 自動送信用コード保存
// =====================================================
void autoSet(WiFiClient &client, const String &payload) {
String raw = payload;
raw.trim();
if (raw.startsWith("RAW:") ) {
raw = raw.substring(4);
raw.trim();
}
if (raw.length() == 0) {
client.print("ERROR:AUTOSET_EMPTY\n");
return;
}
File file = SPIFFS.open(AUTO_IR_FILE, FILE_WRITE);
if (!file) {
client.print("ERROR:SPIFFS_OPEN_FAILED\n");
return;
}
file.print(raw);
file.close();
client.print("OK:AUTOSET\n");
Serial.print("Auto IR saved. bytes=");
Serial.println(raw.length() );
}
// =====================================================
// 自動送信用コード削除
// =====================================================
void autoReset(WiFiClient &client) {
if (SPIFFS.exists(AUTO_IR_FILE) ) {
SPIFFS.remove(AUTO_IR_FILE);
client.print("OK:AUTORESET\n");
Serial.println("Auto IR deleted");
} else {
client.print("OK:NO_AUTO_FILE\n");
Serial.println("No auto IR file to delete");
}
}
// =====================================================
// LIMITSET
// 例: LIMITSET:31.5
// =====================================================
void limitSetCommand(WiFiClient &client, const String &payload) {
float value = payload.toFloat();
if (value < 0.0 || value > 50.0) {
client.print("ERROR:LIMIT_RANGE\n");
return;
}
limitTemperature = value;
saveConfig();
client.print("OK:LIMITSET:");
client.print(limitTemperature, 1);
client.print("\n");
Serial.print("LIMITSET=");
Serial.println(limitTemperature, 1);
}
// =====================================================
// LIMITOFF
// =====================================================
void limitOffCommand(WiFiClient &client) {
limitTemperature = 0.0;
saveConfig();
client.print("OK:LIMITOFF\n");
Serial.println("LIMIT OFF");
}
// =====================================================
// STATUS
// =====================================================
void statusCommand(WiFiClient &client) {
client.print("UPTIME_MIN:");
client.print(uptimeMinutes);
client.print("\n");
client.print("LIMIT_TEMP:");
client.print(limitTemperature, 1);
client.print("\n");
client.print("LIMIT_MODE:");
client.print(limitTemperature > 0.0 ? "ON" : "OFF");
client.print("\n");
client.print("AUTO_IR:");
client.print(SPIFFS.exists(AUTO_IR_FILE) ? "YES" : "NO");
client.print("\n");
client.print("WIFI:");
client.print(WiFi.status() == WL_CONNECTED ? "CONNECTED" : "DISCONNECTED");
client.print("\n");
if (WiFi.status() == WL_CONNECTED) {
client.print("IP:");
client.print(WiFi.localIP() );
client.print("\n");
}
if (statusMutex) xSemaphoreTake(statusMutex, portMAX_DELAY);
if (hasLastIrSend) {
uint32_t ago = uptimeMinutes - lastIrSendMinute;
client.print("LAST_IR_AGO_MIN:");
client.print(ago);
client.print("\n");
client.print("LAST_IR_REASON:");
client.print(lastIrSendReason);
client.print("\n");
client.print("LAST_IR_AT_MIN:");
client.print(lastIrSendMinute);
client.print("\n");
} else {
client.print("LAST_IR_AGO_MIN:NONE\n");
client.print("LAST_IR_REASON:NONE\n");
client.print("LAST_IR_AT_MIN:NONE\n");
}
if (statusMutex) xSemaphoreGive(statusMutex);
client.print("FREE_HEAP:");
client.print(ESP.getFreeHeap() );
client.print("\n");
client.print("OK:STATUS\n");
}
// =====================================================
// setup
// =====================================================
void setup() {
Serial.begin(115200);
delay(500);
Serial.println();
Serial.println("ESP32 IR Bridge starting...");
pinMode(INDI_LED, OUTPUT);
pinMode(DHT11_PW, OUTPUT);
digitalWrite(DHT11_PW, LOW);
dht.begin();
irMutex = xSemaphoreCreateMutex();
dhtMutex = xSemaphoreCreateMutex();
statusMutex = xSemaphoreCreateMutex();
if (!SPIFFS.begin(true) ) {
Serial.println("SPIFFS mount failed");
} else {
Serial.println("SPIFFS mounted");
}
loadConfig();
xTaskCreatePinnedToCore(
uptimeTask,
"uptimeTask",
2048,
NULL,
1,
NULL,
1
);
xTaskCreatePinnedToCore(
indicatorTask,
"indicatorTask",
2048,
NULL,
1,
NULL,
1
);
IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);
IrSender.begin();
xTaskCreatePinnedToCore(
autoSendTask,
"autoSendTask",
4096,
NULL,
1,
NULL,
1
);
xTaskCreatePinnedToCore(
tempWatchTask,
"tempWatchTask",
4096,
NULL,
1,
NULL,
1
);
connectToWiFi();
}
// =====================================================
// loop
// =====================================================
void loop() {
static uint32_t lastWiFiRetry = 0;
if (WiFi.status() != WL_CONNECTED) {
if (millis() - lastWiFiRetry > 10000) {
lastWiFiRetry = millis();
connectToWiFi();
}
delay(10);
return;
}
if (!serverStarted) {
server.begin();
serverStarted = true;
}
WiFiClient client = server.accept();
if (!client) {
delay(5);
return;
}
Serial.println("Client connected");
String line;
if (!readLine(client, line, 30000) ) {
client.print("ERROR:TIMEOUT\n");
client.stop();
Serial.println("Client timeout");
return;
}
line.trim();
Serial.print("Command: ");
Serial.println(line.substring(0, min( (int)line.length(), 80) ) );
if (line == "TMP") {
String tmp = getTempHumidity();
client.print(tmp);
client.print("\n");
}
else if (line == "LEARN") {
learnIR(client);
}
else if (line.startsWith("IR:") ) {
sendIR(client, line.substring(3) );
}
else if (line.startsWith("RAW:") ) {
sendIR(client, line.substring(4) );
}
else if (line.startsWith("AUTOSET:") ) {
autoSet(client, line.substring(8) );
}
else if (line == "AUTORESET") {
autoReset(client);
}
else if (line.startsWith("LIMITSET:") ) {
limitSetCommand(client, line.substring(9) );
}
else if (line == "LIMITOFF") {
limitOffCommand(client);
}
else if (line == "STATUS") {
statusCommand(client);
}
else {
client.print("ERROR:UNKNOWN_COMMAND\n");
}
client.stop();
Serial.println("Client disconnected");
}
ただ、今回使用した温度センサー(DHT11)はそこまで高精度ではなく、長期運用時の信頼性にも少し不安があるため、温度監視機能自体をON/OFFできるようにもしてあります。
#!/usr/bin/env python3
import argparse
import socket
import sys
from pathlib import Path
DEFAULT_HOST = "192.168.0.15"
DEFAULT_PORT = 500
TIMEOUT = 40
def send_command(host, port, message, timeout=TIMEOUT):
if not message.endswith("\n"):
message += "\n"
with socket.create_connection( (host, port), timeout=timeout ) as sock:
sock.settimeout(timeout)
sock.sendall(message.encode("utf-8") )
chunks = []
while True:
try:
data = sock.recv(4096)
except socket.timeout:
break
if not data:
break
chunks.append(data)
# STATUSは複数行なので OK:STATUS まで読む
text = b"".join(chunks)
if b"OK:STATUS\n" in text:
break
# それ以外は1行で終了
if b"\n" in data and not message.startswith("STATUS"):
break
return b"".join(chunks).decode("utf-8", errors="replace").strip()
def load_raw_file(file_path):
path = Path(file_path)
if not path.exists():
print(f"ファイルがありません: {file_path}")
sys.exit(1)
raw = path.read_text(encoding="utf-8").strip()
if raw.startswith("RAW:"):
raw = raw[4:].strip()
if not raw:
print("ファイル内のRAWデータが空です")
sys.exit(1)
return raw
def learn(args):
print("ESP32に学習要求を送信中...")
print("赤外線リモコンをESP32に向けて押してください。")
res = send_command(args.host, args.port, "LEARN", timeout=30)
if not res.startswith("RAW:"):
print("学習失敗:", res)
sys.exit(1)
raw = res[4:].strip()
Path(args.file).write_text(raw + "\n", encoding="utf-8")
print(f"保存しました: {args.file}")
print(f"データ長: {len(raw)} 文字")
def send_ir(args):
raw = load_raw_file(args.file)
print(f"送信中: {args.file}")
res = send_command(args.host, args.port, "IR:" + raw, timeout=40)
print("ESP32:", res)
if not res.startswith("OK"):
sys.exit(1)
def tmp(args):
res = send_command(args.host, args.port, "TMP", timeout=10)
print(res)
def autoset(args):
raw = load_raw_file(args.file)
print(f"自動送信用コードをESP32へ保存中: {args.file}")
res = send_command(args.host, args.port, "AUTOSET:" + raw, timeout=40)
print("ESP32:", res)
if not res.startswith("OK"):
sys.exit(1)
def autoreset(args):
print("ESP32内の自動送信用コードを削除中...")
res = send_command(args.host, args.port, "AUTORESET", timeout=20)
print("ESP32:", res)
if not res.startswith("OK"):
sys.exit(1)
def limitset(args):
value = args.temperature
if value < 0.0 or value > 50.0:
print("リミット温度は 0.0〜50.0 の範囲で指定してください")
sys.exit(1)
print(f"リミット温度を {value:.1f}℃ に設定中...")
res = send_command(args.host, args.port, f"LIMITSET:{value:.1f}", timeout=20)
print("ESP32:", res)
if not res.startswith("OK"):
sys.exit(1)
def limitoff(args):
print("温度リミット自動送信をOFFにします...")
res = send_command(args.host, args.port, "LIMITOFF", timeout=20)
print("ESP32:", res)
if not res.startswith("OK"):
sys.exit(1)
def status(args):
print("ESP32の状態を取得中...")
res = send_command(args.host, args.port, "STATUS", timeout=20)
print(res)
if "OK:STATUS" not in res:
sys.exit(1)
def main():
parser = argparse.ArgumentParser(
description="ESP32 IR Bridge controller"
)
parser.add_argument(
"--host",
default=DEFAULT_HOST,
help=f"ESP32のIPアドレス default={DEFAULT_HOST}"
)
parser.add_argument(
"--port",
type=int,
default=DEFAULT_PORT,
help=f"ESP32のポート default={DEFAULT_PORT}"
)
sub = parser.add_subparsers(dest="command", required=True)
p_learn = sub.add_parser("learn", help="赤外線コードを学習して保存")
p_learn.add_argument("file")
p_learn.set_defaults(func=learn)
p_send = sub.add_parser("send", help="保存済みコードを送信")
p_send.add_argument("file")
p_send.set_defaults(func=send_ir)
p_tmp = sub.add_parser("tmp", help="温湿度を取得")
p_tmp.set_defaults(func=tmp)
p_autoset = sub.add_parser("autoset", help="停電復帰後に自動送信するコードをESP32へ保存")
p_autoset.add_argument("file")
p_autoset.set_defaults(func=autoset)
p_autoreset = sub.add_parser("autoreset", help="ESP32内の自動送信用コードを削除")
p_autoreset.set_defaults(func=autoreset)
p_limitset = sub.add_parser("limitset", help="温度リミットを設定")
p_limitset.add_argument("temperature", type=float, help="例: 32.0")
p_limitset.set_defaults(func=limitset)
p_limitoff = sub.add_parser("limitoff", help="温度リミット自動送信をOFF")
p_limitoff.set_defaults(func=limitoff)
p_status = sub.add_parser("status", help="ESP32の状態を表示")
p_status.set_defaults(func=status)
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
main()