DESIGN III / IV / PROJECT NOTES
テスト進捗
テスト進捗
説明
最終的なプログラミングは、VS CodeとPLATFORMIOプラグインを使用して行いました。
環境内にウェブファイルをアップロードすることで、アクセス時にウェブページが表示され、ページ上からデバイスの制御も可能となります。
合計で35時間以上の試行錯誤を重ね、多数の方法を試したため、今回は最終的な成果のみを公開しています。
なお、カメラの起動には成功しませんでした。原因としては、ESP32-CAMのフラッシュメモリ容量不足が疑われます。環境内にウェブファイルをアップロードすることで、アクセス時にウェブページが表示され、ページ上からデバイスの制御も可能となります。
合計で35時間以上の試行錯誤を重ね、多数の方法を試したため、今回は最終的な成果のみを公開しています。
今後、技術的なスキルが向上した際には、あらためてカメラ機能の実装に挑戦する予定です。
cppファイル
#include
#include
#include
#include
// 超音波センサーのピン
#define TRIG1 13
#define ECHO1 12
#define TRIG2 15
#define ECHO2 14
#define TRIG3 4
#define ECHO3 16
const char* ssid = "ESP32-Radar";
const char* password = "12345678";
WebServer server(80);
bool radarActive = false;
int carWidth = 180;
// 超音波測定
long readDistanceCM(int trigPin, int echoPin) {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH, 30000);
return duration * 0.0343 / 2;
}
long filterDistance(long current, long previous) {
if (abs(current - previous) > 30) return previous;
return current;
}
// レスポンス: index.html
void handleRoot() {
File file = SPIFFS.open("/index.html", "r");
if (!file) {
server.send(500, "text/plain", "index.html not found");
return;
}
server.streamFile(file, "text/html");
file.close();
}
// レスポンス: main.js
void handleJS() {
File file = SPIFFS.open("/main.js", "r");
if (!file) {
server.send(404, "text/plain", "main.js not found");
return;
}
server.streamFile(file, "application/javascript");
file.close();
}
// レスポンス: style.css
void handleCSS() {
File file = SPIFFS.open("/style.css", "r");
if (!file) {
server.send(404, "text/plain", "style.css not found");
return;
}
server.streamFile(file, "text/css");
file.close();
}
// レスポンス: 距離データ(JSON)
void handleDistance() {
if (!radarActive) {
server.send(200, "application/json", "{\"status\":\"inactive\"}");
return;
}
static long prevD1 = 0, prevD2 = 0, prevD3 = 0;
long d1 = filterDistance(readDistanceCM(TRIG1, ECHO1), prevD1);
long d2 = filterDistance(readDistanceCM(TRIG2, ECHO2), prevD2);
long d3 = filterDistance(readDistanceCM(TRIG3, ECHO3), prevD3);
prevD1 = d1; prevD2 = d2; prevD3 = d3;
if (d1 < 0 || d1 > 500) d1 = -1;
if (d2 < 0 || d2 > 500) d2 = -1;
if (d3 < 0 || d3 > 500) d3 = -1;
String json = "{\"d1\":" + String(d1) + ",\"d2\":" + String(d2) + ",\"d3\":" + String(d3) + "}";
server.send(200, "application/json", json);
}
// レーダーON
void handleRadarStart() {
radarActive = true;
server.send(200, "text/plain", "Radar ON");
}
// レーダーOFF
void handleRadarStop() {
radarActive = false;
server.send(200, "text/plain", "Radar OFF");
}
// 車幅設定
void handleSetWidth() {
if (server.hasArg("width")) {
carWidth = server.arg("width").toInt();
carWidth = constrain(carWidth, 160, 200);
}
server.send(200, "text/plain", "幅: " + String(carWidth));
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("起動中...");
// SPIFFS
if (!SPIFFS.begin(true)) {
Serial.println("SPIFFSマウント失敗");
return;
}
// SPIFFS一覧
File root = SPIFFS.open("/");
File file = root.openNextFile();
while (file) {
Serial.printf(" %s (%d バイト)\n", file.name(), file.size());
file = root.openNextFile();
}
// 超音波初期化
pinMode(TRIG1, OUTPUT); pinMode(ECHO1, INPUT);
pinMode(TRIG2, OUTPUT); pinMode(ECHO2, INPUT);
pinMode(TRIG3, OUTPUT); pinMode(ECHO3, INPUT);
digitalWrite(TRIG1, LOW); digitalWrite(TRIG2, LOW); digitalWrite(TRIG3, LOW);
Serial.println("超音波センサー準備完了");
// WiFi
WiFi.softAP(ssid, password);
Serial.print("WiFi AP開始、IPアドレス: ");
Serial.println(WiFi.softAPIP());
// ハンドラ登録
server.on("/", handleRoot);
server.on("/index.html", handleRoot);
server.on("/main.js", handleJS);
server.on("/style.css", handleCSS);
server.on("/distance", handleDistance);
server.on("/radar/start", handleRadarStart);
server.on("/radar/stop", handleRadarStop);
server.on("/setwidth", handleSetWidth);
server.onNotFound([]() {
server.send(404, "text/plain", "未対応のURL");
});
server.begin();
Serial.println("HTTPサーバー起動完了");
}
void loop() {
server.handleClient();
}
jsファイル
#include
#include
#include
#include
// 超音波センサーのピン
#define TRIG1 13
#define ECHO1 12
#define TRIG2 15
#define ECHO2 14
#define TRIG3 4
#define ECHO3 16
const char* ssid = "ESP32-Radar";
const char* password = "12345678";
WebServer server(80);
bool radarActive = false;
int carWidth = 180;
// 超音波測定
long readDistanceCM(int trigPin, int echoPin) {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH, 30000);
return duration * 0.0343 / 2;
}
long filterDistance(long current, long previous) {
if (abs(current - previous) > 30) return previous;
return current;
}
// レスポンス: index.html
void handleRoot() {
File file = SPIFFS.open("/index.html", "r");
if (!file) {
server.send(500, "text/plain", "index.html not found");
return;
}
server.streamFile(file, "text/html");
file.close();
}
// レスポンス: main.js
void handleJS() {
File file = SPIFFS.open("/main.js", "r");
if (!file) {
server.send(404, "text/plain", "main.js not found");
return;
}
server.streamFile(file, "application/javascript");
file.close();
}
// レスポンス: style.css
void handleCSS() {
File file = SPIFFS.open("/style.css", "r");
if (!file) {
server.send(404, "text/plain", "style.css not found");
return;
}
server.streamFile(file, "text/css");
file.close();
}
// レスポンス: 距離データ(JSON)
void handleDistance() {
if (!radarActive) {
server.send(200, "application/json", "{\"status\":\"inactive\"}");
return;
}
static long prevD1 = 0, prevD2 = 0, prevD3 = 0;
long d1 = filterDistance(readDistanceCM(TRIG1, ECHO1), prevD1);
long d2 = filterDistance(readDistanceCM(TRIG2, ECHO2), prevD2);
long d3 = filterDistance(readDistanceCM(TRIG3, ECHO3), prevD3);
prevD1 = d1; prevD2 = d2; prevD3 = d3;
if (d1 < 0 || d1 > 500) d1 = -1;
if (d2 < 0 || d2 > 500) d2 = -1;
if (d3 < 0 || d3 > 500) d3 = -1;
String json = "{\"d1\":" + String(d1) + ",\"d2\":" + String(d2) + ",\"d3\":" + String(d3) + "}";
server.send(200, "application/json", json);
}
// レーダーON
void handleRadarStart() {
radarActive = true;
server.send(200, "text/plain", "Radar ON");
}
// レーダーOFF
void handleRadarStop() {
radarActive = false;
server.send(200, "text/plain", "Radar OFF");
}
// 車幅設定
void handleSetWidth() {
if (server.hasArg("width")) {
carWidth = server.arg("width").toInt();
carWidth = constrain(carWidth, 160, 200);
}
server.send(200, "text/plain", "幅: " + String(carWidth));
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("起動中...");
// SPIFFS
if (!SPIFFS.begin(true)) {
Serial.println("SPIFFSマウント失敗");
return;
}
// SPIFFS一覧
File root = SPIFFS.open("/");
File file = root.openNextFile();
while (file) {
Serial.printf(" %s (%d バイト)\n", file.name(), file.size());
file = root.openNextFile();
}
// 超音波初期化
pinMode(TRIG1, OUTPUT); pinMode(ECHO1, INPUT);
pinMode(TRIG2, OUTPUT); pinMode(ECHO2, INPUT);
pinMode(TRIG3, OUTPUT); pinMode(ECHO3, INPUT);
digitalWrite(TRIG1, LOW); digitalWrite(TRIG2, LOW); digitalWrite(TRIG3, LOW);
Serial.println("超音波センサー準備完了");
// WiFi
WiFi.softAP(ssid, password);
Serial.print("WiFi AP開始、IPアドレス: ");
Serial.println(WiFi.softAPIP());
// ハンドラ登録
server.on("/", handleRoot);
server.on("/index.html", handleRoot);
server.on("/main.js", handleJS);
server.on("/style.css", handleCSS);
server.on("/distance", handleDistance);
server.on("/radar/start", handleRadarStart);
server.on("/radar/stop", handleRadarStop);
server.on("/setwidth", handleSetWidth);
server.onNotFound([]() {
server.send(404, "text/plain", "未対応のURL");
});
server.begin();
Serial.println("HTTPサーバー起動完了");
}
void loop() {
server.handleClient();
}
iniファイル
[env:esp32cam]
platform = espressif32
board = esp32cam
framework = arduino
monitor_speed = 115200
upload_speed = 115200
upload_port = COM4
lib_deps =
bblanchon/ArduinoJson@^6.21.3
board_build.partitions = min_spiffs.csv
board_upload.flash_size = 4MB
board_upload.maximum_size = 4194304
board_build.filesystem = spiffs
upload_protocol = esptool
platform_packages = platformio/tool-mkspiffs@^2.230.0
monitor_filters = esp32_exception_decoder
htmlファイル
htmlファイルcssファイル
body {
background-color: #2c3e50;
color: #ecf0f1;
font-family: Arial, sans-serif;
padding: 20px;
margin: 0;
}
.container {
max-width: 600px;
margin: 0 auto;
background-color: #34495e;
border-radius: 10px;
padding: 20px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
h1 {
text-align: center;
color: #3498db;
margin-bottom: 20px;
}
#systemStatus {
background-color: #2c3e50;
padding: 15px;
border-radius: 8px;
text-align: center;
font-weight: bold;
margin-bottom: 20px;
}
.sensor-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
margin-bottom: 20px;
}
.sensor {
background-color: #27ae60; /* 安全距離:緑 */
border-radius: 8px;
padding: 15px;
text-align: center;
transition: background-color 0.3s;
}
.sensor-label {
font-weight: bold;
margin-bottom: 5px;
}
.distance {
font-size: 20px;
font-weight: bold;
}
.control-panel {
background-color: #2c3e50;
border-radius: 8px;
padding: 15px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
}
select {
width: 100%;
padding: 10px;
border-radius: 5px;
border: none;
background-color: #ecf0f1;
font-size: 16px;
}
.button-group {
display: flex;
gap: 10px;
}
.btn {
flex: 1;
padding: 10px;
border: none;
border-radius: 5px;
background-color: #3498db;
color: white;
font-size: 16px;
cursor: pointer;
}
#stopBtn {
background-color: #e74c3c;
}
/* 距離状態色分け */
.sensor-warning {
background-color: #f39c12; /* 警告距離:橙 */
}
.sensor-danger {
background-color: #e74c3c; /* 危険距離:赤 */
}
.sensor-safe {
background-color: #27ae60; /* 通常距離:緑 */
}
/* モバイル対応 */
@media (max-width: 600px) {
.sensor-grid {
grid-template-columns: 1fr;
}
.container {
padding: 10px;
}
}
csvファイル
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x1F0000,
spiffs, data, spiffs, 0x200000,0x1F0000,