Files
esp8266-laser/src/esp8266-laser.ino

644 lines
16 KiB
C++

/*
https://github.com/0x1d/esp8266-laser
0x1d, FrYakaTKoP
*/
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <FS.h>
#include <SoftwareSerial.h>
#include <ArduinoJson.h>
/*
some hard coded config, change if needed
*/
// Set how long the esp should try to connect to a network before starting it's own AP
unsigned int wifiTimeout = 15000; //ms
// Hardcoded Soft AP network parameters
char *ssidAP = "laserAp";
IPAddress apIP(192, 168, 4, 1);
IPAddress netMsk(255, 255, 255, 0);
// this is sent to server if invalid request
const char *metaRefreshStr = "<head><meta http-equiv=\"refresh\" content=\"2; url=/index.html\" /></head><body><p>redirecting...</p></body>";
/* hostname for mDNS. Should work at least on windows. Try http://esplaser.local */
const char *myHostname = "esplaser";
// espsoftwareserial
// SoftwareSerial(int receivePin, int transmitPin, bool inverse_logic = false, unsigned int buffSize = 64);
SoftwareSerial laserSerial(14, 12, false, 256);
// Presets json buffer
const unsigned int maxPresets = 128; // 128 = 19496, 200 = 29864
// Preset cycleTime
unsigned int cycleTime = 5000; // ms
/*
End of hard coded config
*/
const unsigned int presetsBufferSize = maxPresets * JSON_ARRAY_SIZE(4) + JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(maxPresets) + 1000;
byte presets[maxPresets][4] = {1, 1, 1, 128};
unsigned int activePreset = 0;
unsigned int presetMode = 0;
String ssid = "";
String password = "";
int apMode = 1; // default value, will be overwriten with config from file
int _apMode = 0; // runtime apMode
// [0] laser, [1] m1, [2] m2, [3] m3
byte lmValues[4] = {0, 0, 0, 0};
unsigned long previousMillis = 0;
// Web server
ESP8266WebServer server(80);
bool hFileRead(String path) {
//Serial.println("handleFileRead: " + path);
if (path.endsWith("/")) path += "index.html";
String contentType = getContentType(path);
String pathWithGz = path + ".gz";
if (SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) {
if (SPIFFS.exists(pathWithGz))
path += ".gz";
File file = SPIFFS.open(path, "r");
size_t sent = server.streamFile(file, contentType);
file.close();
return true;
}
return false;
}
String getContentType(String filename) {
if (server.hasArg("download")) return "application/octet-stream";
else if (filename.endsWith(".htm")) return "text/html";
else if (filename.endsWith(".html")) return "text/html";
else if (filename.endsWith(".ini")) return "text/plain";
else if (filename.endsWith(".css")) return "text/css";
else if (filename.endsWith(".js")) return "application/javascript";
else if (filename.endsWith(".png")) return "image/png";
else if (filename.endsWith(".gif")) return "image/gif";
else if (filename.endsWith(".jpg")) return "image/jpeg";
else if (filename.endsWith(".ico")) return "image/x-icon";
else if (filename.endsWith(".xml")) return "text/xml";
else if (filename.endsWith(".pdf")) return "application/x-pdf";
else if (filename.endsWith(".zip")) return "application/x-zip";
else if (filename.endsWith(".gz")) return "application/x-gzip";
return "text/plain";
}
void hFileList() {
String responseBuffer = String();
StaticJsonBuffer<500> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
JsonArray& files = root.createNestedArray("files");
Dir dir = SPIFFS.openDir("/");
while (dir.next()) {
File entry = dir.openFile("r");
files.add(String(entry.name()));
entry.close();
}
root.prettyPrintTo(responseBuffer);
server.send(200, "text/json", responseBuffer);
jsonBuffer.clear();
responseBuffer = String();
}
bool hReadPresets()
{
// streamFile instead of rebuild json string
String path = "/presets.json";
String contentType = getContentType(path);
if (SPIFFS.exists(path)) {
File file = SPIFFS.open(path, "r");
size_t sent = server.streamFile(file, contentType);
file.close();
return true;
}
return false;
}
void hWritePreset()
{
presetMode = 0;
int error = 0;
int presetNr = 0;
bool apply = false;
StaticJsonBuffer<200> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
JsonObject& data = root.createNestedObject("preset");
if (server.hasArg("nr") ) {
presetNr = server.arg("nr").toInt();
data["nr"] = (String)presetNr;
}
else
{
error = 1;
}
if (server.hasArg("laser") && error == 0) {
presets[presetNr-1][0] = server.arg("laser").toInt();
data["laser"] = (String)presets[presetNr-1][0];
}
if (server.hasArg("motor1") && error == 0) {
presets[presetNr-1][1] = server.arg("motor1").toInt();
data["motor1"] = (String)presets[presetNr-1][1];
}
if (server.hasArg("motor2") && error == 0) {
presets[presetNr-1][2] = server.arg("motor2").toInt();
data["motor2"] = (String) presets[presetNr-1][2];
}
if (server.hasArg("motor3") && error == 0) {
presets[presetNr-1][3] = server.arg("motor3").toInt();
data["motor3"] = (String) presets[presetNr-1][3];
}
if (server.arg("apply") == "true" && error == 0)
{
apply = true;
data["apply"] = "true";
presetToLaser(presetNr-1);
}
error = savePresetFile() ? 0 : 1;
if(error != 0)
{
String responseBuffer = String();
root.prettyPrintTo(responseBuffer);
server.send(200, "text/json", responseBuffer);
responseBuffer = String();
}
else
{
server.send(400, "text/plain", "400 Bad Request");
}
jsonBuffer.clear();
}
void hReadSpiro()
{
String responseBuffer = String();
StaticJsonBuffer<200> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
JsonObject& data = root.createNestedObject("spirograph");
data["laser"] = (String)lmValues[0];
data["motor1"] = (String)lmValues[1];
data["motor2"] = (String)lmValues[2];
data["motor3"] = (String)lmValues[3];
root.prettyPrintTo(responseBuffer);
server.send(200, "text/json", responseBuffer);
jsonBuffer.clear();
responseBuffer = String();
}
void hWriteSpiro()
{
presetMode = 0;
String responseBuffer = String();
StaticJsonBuffer<200> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
JsonObject& data = root.createNestedObject("spirograph");
if (server.hasArg("laser") ) {
lmValues[0] = server.arg("laser").toInt();
data["laser"] = (String)lmValues[0];
String msg = "AT SLV ";
msg += (String)lmValues[0];
laserSerial.println(msg);
delay(20);
}
if (server.hasArg("motor1") ) {
lmValues[1] = server.arg("motor1").toInt();
data["motor1"] = (String)lmValues[1];
String msg = "AT SMS 1 ";
msg += (String)lmValues[1];
laserSerial.println(msg);
delay(20);
}
if (server.hasArg("motor2") ) {
lmValues[2] = server.arg("motor2").toInt();
data["motor2"] = (String)lmValues[2];
String msg = "AT SMS 2 ";
msg += (String)lmValues[2];
laserSerial.println(msg);
delay(20);
}
if (server.hasArg("motor3") ) {
lmValues[3] = server.arg("motor3").toInt();
data["motor3"] = (String)lmValues[3];
String msg = "AT SMS 3 ";
msg += (String)lmValues[3];
laserSerial.println(msg);
delay(20);
}
root.prettyPrintTo(responseBuffer);
server.send(200, "text/json", responseBuffer);
jsonBuffer.clear();
responseBuffer = String();
}
void hReadWifi()
{
// Serial.println("hReadWifi");
String responseBuffer = String();
StaticJsonBuffer<200> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
JsonObject& data = root.createNestedObject("wificonfig");
data["apMode"] = apMode;
data["SSID"] = ssid;
data["Password"] = "***"; //password;
//data["save"] = "false";
//data["apply"] = "false";
root.prettyPrintTo(responseBuffer);
//root.prettyPrintTo(Serial);
server.send(200, "text/json", responseBuffer);
jsonBuffer.clear();
responseBuffer = String();
}
void hWriteWifi()
{
// Serial.println("hWriteWifi");
String responseBuffer = String();
boolean save = false;
boolean apply = false;
StaticJsonBuffer<200> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
JsonObject& data = root.createNestedObject("wificonfig");
if (server.hasArg("apMode") ) {
apMode = server.arg("apMode").toInt();
data["apMode"] = apMode;
}
if (server.hasArg("SSID") ) {
ssid = server.arg("SSID");
data["SSID"] = ssid;
}
if (server.hasArg("password") ) {
password = server.arg("password");
data["Password"] = "***"; //password;
}
if (server.arg("save") == "true")
{
save = true;
data["save"] = "true";
}
if (server.arg("apply") == "true")
{
apply = true;
data["apply"] = "true";
}
root.prettyPrintTo(responseBuffer);
//root.prettyPrintTo(Serial);
server.send(200, "text/json", responseBuffer);
if (save) {
saveWifiConfig();
}
if (apply) {
setupWifi();
}
jsonBuffer.clear();
responseBuffer = String();
}
boolean readPresetsFile() {
Serial.println("Reading presets.json");
File configFile = SPIFFS.open("/presets.json", "r");
if (!configFile) {
Serial.println("Failed to open config file");
return false;
}
size_t size = configFile.size();
if (size > 10024) {
Serial.println("Config file size is too large");
return false;
}
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size]);
DynamicJsonBuffer presetsBuffer(presetsBufferSize);
configFile.readBytes(buf.get(), size);
JsonObject& root = presetsBuffer.parseObject(buf.get());
//root.prettyPrintTo(Serial);
JsonObject& sets = root["presets"];
for (JsonObject::iterator it = sets.begin(); it != sets.end(); ++it)
{
const char* keyStr = it->key;
unsigned int key = atoi(it->key);
// it->value contains the JsonVariant which can be casted as usual
const char* value = it->value;
JsonArray& values = sets[keyStr];
for (int x = 0; x < 4; x++)
{
presets[key-1][x] = values[x];
}
}
if (!root.success()) {
Serial.println("Failed to parse preset config file");
return false;
}
return true;
}
bool savePresetFile() {
DynamicJsonBuffer presetsBuffer(presetsBufferSize);
JsonObject& json = presetsBuffer.createObject();
JsonObject& sets = json.createNestedObject("presets");
for (int i = 0; i < maxPresets; i++)
{
JsonArray& set = sets.createNestedArray((String)i);
for (int x = 0; x < 4; x++)
{
set.add(presets[i+1][x]);
}
}
//json.prettyPrintTo(Serial);
File configFile = SPIFFS.open("/presets.json", "w");
if (!configFile) {
//Serial.println("Failed to open config file for writing");
return false;
}
json.printTo(configFile);
return true;
}
boolean readWifiConfig() {
Serial.println("Reading wifi.json");
File configFile = SPIFFS.open("/wifi.json", "r");
if (!configFile) {
Serial.println("Failed to open config file");
return false;
}
size_t size = configFile.size();
if (size > 1024) {
Serial.println("Config file size is too large");
return false;
}
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
StaticJsonBuffer<200> jsonBuffer;
JsonObject& json = jsonBuffer.parseObject(buf.get());
if (!json.success()) {
Serial.println("Failed to parse config file");
return false;
}
apMode = json["apMode"];
apMode = apMode;
const char* tSsid = json["SSID"];
const char* tPwd = json["Password"];
ssid = tSsid;
password = tPwd;
Serial.print("SSID: ");
Serial.println(ssid);
Serial.print("PW: ");
Serial.println("***");
//Serial.println(password);
return true;
}
bool saveWifiConfig() {
StaticJsonBuffer<200> jsonBuffer;
JsonObject& json = jsonBuffer.createObject();
json["apMode"] = apMode;
json["SSID"] = ssid;
json["Password"] = password;
File configFile = SPIFFS.open("/wifi.json", "w");
if (!configFile) {
//Serial.println("Failed to open config file for writing");
return false;
}
json.printTo(configFile);
return true;
}
void presetToLaser(int nr)
{
String msg = "AT SLV ";
msg += (String)presets[nr][0];
laserSerial.println(msg);
delay(20);
msg = "AT SMS 1 ";
msg += (String)presets[nr][1];
laserSerial.println(msg);
delay(20);
msg = "AT SMS 2 ";
msg += (String)presets[nr][2];
laserSerial.println(msg);
delay(20);
msg = "AT SMS 3 ";
msg += (String)presets[nr][3];
laserSerial.println(msg);
delay(20);
Serial.print("writenPresetToLaser: ");
Serial.println(nr);
}
void setupWifi()
{
WiFi.disconnect();
delay(100);
if (!connectWifi())
{
Serial.println("connect failed, start SoftAP");
_apMode = 1; // fallback to AP mode
connectWifi();
}
}
boolean connectWifi() {
Serial.print("connectWifi apMode=");
Serial.println(apMode);
if (_apMode == 0)
{
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid.c_str(), password.c_str());
previousMillis = millis();
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
if (millis() - previousMillis >= wifiTimeout) {
Serial.println("");
Serial.println("connection timedout");
return false;
}
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
return true;
}
else
{
Serial.printf("Starting SoftAP %s\n", ssidAP);
WiFi.mode(WIFI_AP);
WiFi.softAP(ssidAP);
Serial.print("SoftAP started! IP address: ");
Serial.println ( WiFi.softAPIP() );
return true;
}
}
void setup(void) {
laserSerial.begin(9600);
laserSerial.flush();
Serial.begin(115200);
Serial.print("\n");
SPIFFS.begin();
// {
// Dir dir = SPIFFS.openDir("/");
// while (dir.next()) {
// String fileName = dir.fileName();
// size_t fileSize = dir.fileSize();
// Serial.printf("FS File: %s\n", fileName.c_str());
// }
// Serial.printf("\n");
// }
if (!readWifiConfig())
{
Serial.println("reading wifi config failed, start SoftAP");
_apMode = 1;
}
delay(100);
setupWifi();
// loading presets
readPresetsFile();
// Setup MDNS responder
if (!MDNS.begin(myHostname)) {
Serial.println("Error setting up MDNS responder!");
} else {
Serial.println("mDNS responder started");
// Add service to MDNS-SD
MDNS.addService("http", "tcp", 80);
}
//SERVER INIT
//list directory
server.on("/files", HTTP_GET, hFileList);
server.on("/files", HTTP_POST, []() {
server.send(400, "text/plain", "400 Bad Request");
});
server.on("/wificonfig", HTTP_GET, hReadWifi);
server.on("/wificonfig", HTTP_POST, hWriteWifi);
server.on("/spirograph", HTTP_GET, hReadSpiro);
server.on("/spirograph", HTTP_POST, hWriteSpiro);
server.on("/presets", HTTP_GET, hReadPresets);
server.on("/presets", HTTP_POST, []() {
server.send(400, "text/plain", "400 Bad Request");
});
server.on("/preset", HTTP_GET, []() {
server.send(400, "text/plain", "400 Bad Request");
});
server.on("/preset", HTTP_POST, hWritePreset);
server.on("/presetMode", HTTP_GET, []() {
String json = "{";
json += "\"presetMode\":" + String(presetMode);
json += "}";
server.send(200, "text/json", json);
json = String();
});
server.on("/presetMode", HTTP_POST, []() {
if (server.hasArg("presetMode") ) {
presetMode = server.arg("presetMode").toInt();
}
String json = "{";
json += "\"presetMode\":" + String(presetMode);
json += "}";
server.send(200, "text/json", json);
json = String();
});
server.on("/heap", HTTP_GET, []() {
String json = "{";
json += "\"heap\":" + String(ESP.getFreeHeap());
json += "}";
server.send(200, "text/json", json);
json = String();
});
server.on("/heap", HTTP_POST, []() {
server.send(400, "text/plain", "400 Bad Request");
});
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
}
void handleNotFound() {
String uri = server.uri();
//Serial.print("unhandled uri:");
//Serial.println(uri);
if (uri.substring(0, 12) == "/wifi.json")
{
server.send(403, "text/plain", "Forbidden");
return;
}
if (!hFileRead(server.uri()))
{
server.send(404, "text/html", metaRefreshStr);
// server.send(302, "text/plain", "blub");
}
}
void loop(void) {
server.handleClient();
if (presetMode == 1)
{
if (millis() - previousMillis >= cycleTime)
{
presetToLaser(activePreset);
if (activePreset < 128)
{
activePreset++;
}
else
{
activePreset = 0;
}
previousMillis = millis();
}
}
}