// ESP32版電動カーテン
// Base Code
#include <WiFi.h>
#include <WiFiClient.h>
#include <ArduinoOTA.h>
#include <ESP32WebServer.h>
#include <ESPmDNS.h>
#include <SPIFFS.h>
#include <Arduino.h>
#include <FS.h>
#include <WebSocketsServer.h>
#include <Stepper.h>
const char* path_root = "/index.html";
const char *ssid = "SSID";
const char *password = "PASSPHRASE";
#define BUFFER_SIZE 16384
uint8_t buf[BUFFER_SIZE];
ESP32WebServer server ( 80 );
WebSocketsServer webSocket(81); // WebSocketデフォルトポート 81
File fsUploadFile;
const char common_name[40] = "esp32_my_curtain";
const char *OTAName = common_name; // OTAサービス名
const char *mdnsName = common_name; // mDNS用ドメイン名
const int motorPin1 = 13; // Blue - 28BYJ48 pin 1
const int motorPin2 = 12; // Pink - 28BYJ48 pin 2
const int motorPin3 = 14; // Yellow - 28BYJ48 pin 3
const int motorPin4 = 27; // Orange - 28BYJ48 pin 4
// Red - 28BYJ48 pin 5 (VCC)
const int LimitSW = 26;
const int stepsPerRevolution = 200; // 1回転あたりのステップ数
int motorSpeed = 1200; // 数値が小さいほど高速度、大きいほどトルク大
int lookup[9] = {B01000, B01100, B00100, B00110, B00010, B00011, B00001, B01001, B00000};
Stepper myStepper(stepsPerRevolution, motorPin1, motorPin2, motorPin3, motorPin4);
void setup() {
delay(1000);
Serial.begin(115200);
delay(10);
Serial.println("\r\n");
//declare the motor pins as outputs
pinMode(motorPin1, OUTPUT);
pinMode(motorPin2, OUTPUT);
pinMode(motorPin3, OUTPUT);
pinMode(motorPin4, OUTPUT);
pinMode(LimitSW, INPUT_PULLUP);
startWiFi();
startOTA();
startSPIFFS();
startWebSocket();
startMDNS();
startServer();
}
void loop(){
webSocket.loop(); // constantly check for websocket events
server.handleClient(); // run the server
ArduinoOTA.handle(); // listen for OTA events
// ESP32用light-sleepモード設定(自動復帰?)
// esp_light_sleep_start();
}
/*__________________________________________________________SETUP_FUNCTIONS__________________________________________________________*/
void startWiFi() { // Start a Wi-Fi access point, and try to connect to some given access points. Then wait for either an AP or STA connection
// WiFi.softAP(ssid, password); // Start the access point
WiFi.mode(WIFI_STA); // Start the access point
WiFi.begin(ssid, password); // Start the access point
while ( WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("SSID \"");
Serial.print(ssid);
Serial.println("\" started\r\n");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.print("hostname : ");
// Serial.println(WiFi.hostname());
Serial.println("");
}
void startOTA() { // Start the OTA service
ArduinoOTA.setHostname(OTAName);
// ArduinoOTA.setPassword(OTAPassword);
ArduinoOTA.onStart([]() {
Serial.println("Start");
// turn off the LEDs
for (int i = 0; i < 6; i++) {
// digitalWrite(LED_BUILTIN, HIGH);
// digitalWrite(LED_BUILTIN, LOW);
}
});
ArduinoOTA.onEnd([]() {
Serial.println("\r\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
Serial.println("OTA ready\r\n");
}
//
// https://github.com/zhouhan0126/WebServer-esp32/blob/master/examples/FSBrowser/FSBrowser.ino
//
void listDir(fs::FS &fs, const char * dirname, uint8_t levels) {
// Serial.printf("Listing directory: %s\n", dirname);
File root = fs.open(dirname);
if (!root) {
Serial.println("Failed to open directory");
return;
}
if (!root.isDirectory()) {
Serial.println("Not a directory");
return;
}
File file = root.openNextFile();
while (file) {
if (file.isDirectory()) {
Serial.print(" DIR : ");
Serial.println(file.name());
if (levels) {
listDir(fs, file.name(), levels - 1);
}
} else {
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print(" SIZE: ");
Serial.println(file.size());
}
file = root.openNextFile();
}
}
void startSPIFFS() { // Start the SPIFFS and list all contents
SPIFFS.begin(); // Start the SPI Flash File System (SPIFFS)
Serial.println("SPIFFS started. Contents:");
{
listDir(SPIFFS, "/", 0);
}
Serial.printf("\n");
/*
if (!readHTML()) {
Serial.println("Read HTML error!!");
}
*/
/*
Serial.println("SPIFFS started. Contents:");
{
Dir dir = SPIFFS.openDir("/");
while (dir.next()) { // List the file system contents
String fileName = dir.fileName();
size_t fileSize = dir.fileSize();
Serial.printf("\tFS File: %s, size: %s\r\n", fileName.c_str(), formatBytes(fileSize).c_str());
}
Serial.printf("\n");
}
*/
}
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) { // When a WebSocket message is received
switch (type) {
case WStype_DISCONNECTED: // if the websocket is disconnected
Serial.printf("[%u] Disconnected!\n", num);
break;
case WStype_CONNECTED: { // if a new websocket connection is established
IPAddress ip = webSocket.remoteIP(num);
Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
// rainbow = false; // Turn rainbow off when a new connection is established
}
break;
case WStype_TEXT: // if new text data is received
Serial.printf("[%u] get Text: %s\n", num, payload);
Serial.print("payload[0] : ");
Serial.println(payload[0]);
if (payload[0] == '1')
{
Serial.print(" clockwise loop: " );
while (1) {
if(!digitalRead(LimitSW)) break;
counterclockwise();
delayMicroseconds(motorSpeed);
Serial.print(" ");
delay(1);
}
Serial.print("]");
setOutput(9);
}
else if (payload[0] == '2')
{
Serial.print(" counterclockwise loop: " );
while (1) {
if(!digitalRead(LimitSW)) break;
clockwise();
delayMicroseconds(motorSpeed);
Serial.print(" ");
delay(1);
}
Serial.print("]");
setOutput(9);
}
else if (payload[0] == '0')
{
setOutput(9);
}
break;
}
}
void handleFileUpload() { // upload a new file to the SPIFFS
HTTPUpload& upload = server.upload();
String path;
if (upload.status == UPLOAD_FILE_START) {
path = upload.filename;
if (!path.startsWith("/")) path = "/" + path;
if (!path.endsWith(".gz")) { // The file server always prefers a compressed version of a file
String pathWithGz = path + ".gz"; // So if an uploaded file is not compressed, the existing compressed
if (SPIFFS.exists(pathWithGz)) // version of that file must be deleted (if it exists)
SPIFFS.remove(pathWithGz);
}
Serial.print("handleFileUpload Name: "); Serial.println(path);
fsUploadFile = SPIFFS.open(path, "w"); // Open the file for writing in SPIFFS (create if it doesn't exist)
path = String();
} else if (upload.status == UPLOAD_FILE_WRITE) {
if (fsUploadFile)
fsUploadFile.write(upload.buf, upload.currentSize); // Write the received bytes to the file
} else if (upload.status == UPLOAD_FILE_END) {
if (fsUploadFile) { // If the file was successfully created
fsUploadFile.close(); // Close the file again
Serial.print("handleFileUpload Size: "); Serial.println(upload.totalSize);
server.sendHeader("Location", "/success.html"); // Redirect the client to the success page
server.send(303);
} else {
server.send(500, "text/plain", "500: couldn't create file");
}
}
}
void startWebSocket() { // Start a WebSocket server
webSocket.begin(); // start the websocket server
webSocket.onEvent(webSocketEvent); // if there's an incomming websocket message, go to function 'webSocketEvent'
Serial.println("WebSocket server started.");
}
void startMDNS() { // Start the mDNS responder
MDNS.begin(mdnsName); // start the multicast domain name server
Serial.print("mDNS responder started: http://");
Serial.print(mdnsName);
Serial.println(".local");
}
void startServer() { // Start a HTTP server with a file read handler and an upload handler
server.on("/", handleRoot);
server.on("/edit.html", HTTP_POST, []() { // If a POST request is sent to the /edit.html address,
server.send(200, "text/plain", "");
}, handleFileUpload); // go to 'handleFileUpload'
server.onNotFound(handleNotFound); // if someone requests any other file or page, go to function 'handleNotFound'
// and check if the file exists
server.begin(); // start the HTTP server
Serial.println("HTTP server started.");
}
/*__________________________________________________________Motor_FUNCTIONS__________________________________________________________*/
void counterclockwise() //反時計回り
{
for (int i = 0; i < 8; i++)
{
setOutput(i);
delayMicroseconds(motorSpeed);
}
}
void clockwise() //時計回り
{
for (int i = 7; i >= 0; i--)
{
setOutput(i);
delayMicroseconds(motorSpeed);
}
}
void setOutput(int out)
{
digitalWrite(motorPin1, bitRead(lookup[out], 0));
digitalWrite(motorPin2, bitRead(lookup[out], 1));
digitalWrite(motorPin3, bitRead(lookup[out], 2));
digitalWrite(motorPin4, bitRead(lookup[out], 3));
}
/*__________________________________________________________HELPER_FUNCTIONS__________________________________________________________*/
String formatBytes(size_t bytes) { // convert sizes in bytes to KB and MB
if (bytes < 1024) {
return String(bytes) + "B";
} else if (bytes < (1024 * 1024)) {
return String(bytes / 1024.0) + "KB";
} else if (bytes < (1024 * 1024 * 1024)) {
return String(bytes / 1024.0 / 1024.0) + "MB";
}
}
String getContentType(String filename) { // determine the filetype of a given filename, based on the extension
if (filename.endsWith(".html")) return "text/html";
else if (filename.endsWith(".css")) return "text/css";
else if (filename.endsWith(".json")) return "text/css";
else if (filename.endsWith(".js")) return "application/javascript";
else if (filename.endsWith(".ico")) return "image/x-icon";
else if (filename.endsWith(".png")) return "image/x-icon";
else if (filename.endsWith(".gz")) return "application/x-gzip";
return "text/plain";
}
/*__________________________________________________________SERVER_HANDLERS__________________________________________________________*/
void handleNotFound() { // if the requested file or page doesn't exist, return a 404 not found error
if (!handleFileRead(server.uri())) { // check if the file exists in the flash memory (SPIFFS), if so, send it
server.send(404, "text/plain", "404: File Not Found");
}
}
boolean readHTML() {
File htmlFile = SPIFFS.open(path_root, "r");
if (!htmlFile) {
Serial.println("Failed to open index.html");
return false;
}
size_t size = htmlFile.size();
if (size >= BUFFER_SIZE) {
Serial.print("File Size Error:");
Serial.println((int)size);
} else {
Serial.print("File Size OK:");
Serial.println((int)size);
}
// htmlFile.read(buf, size);
htmlFile.close();
return true;
}
bool handleFileRead(String path) { // send the right file to the client (if it exists)
Serial.println("handleFileRead: " + path);
if (path.endsWith("/")) path += "index.html"; // If a folder is requested, send the index file
String contentType = getContentType(path); // Get the MIME type
String pathWithGz = path + ".gz";
if (SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) { // If the file exists, either as a compressed archive, or normal
if (SPIFFS.exists(pathWithGz)) // If there's a compressed version available
path += ".gz"; // Use the compressed verion
File file = SPIFFS.open(path, "r"); // Open the file
size_t sent = server.streamFile(file, contentType); // Send it to the client
file.close(); // Close the file again
Serial.println(String("\tSent file: ") + path);
return true;
}
Serial.println(String("\tFile Not Found: ") + path); // If the file doesn't exist, return false
return false;
}
void handleRoot() {
Serial.println("Access");
char message[20];
String(server.arg(0)).toCharArray(message, 20);
server.send(200, "text/html", (char *)buf);
}