New Combo ESP8266 + Arduino Pro Mini via i2c

This commit is contained in:
Maik Hofmann
2021-03-06 13:59:08 +01:00
parent 5fb027b629
commit 29918f0cb8
24 changed files with 1260 additions and 0 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,180 @@
#include <Arduino.h>
#include "EspSaveCrash.h"
#include <ESPAsyncWebServer.h>
#include <AsyncElegantOTA.h>
#include <AsyncJson.h>
#include <ArduinoJson.h>
#include <Wire.h>
#include "index_html.h"
#include "../../Arduino/src/i2cshare.h"
/* create this file and add your wlan credentials
const char* ssid = "MyWLANSID";
const char* password = "MYPASSWORD";
*/
#include "../../../../private/credentials.h"
// switch relay sync to the lamp
// e.g. the Wifi Relay Board U4648
#define USERELAY
// Relay Board parameters
#define ESP8266_GPIO2 2 // Blue LED.
#define ESP8266_GPIO4 4 // Relay control.
#define ESP8266_GPIO5 5 // Optocoupler input.
#define LED_PIN ESP8266_GPIO2
EspSaveCrash SaveCrash;
// the buffer to put the Crash log to
char *_debugOutputBuffer;
SHCIState lastState;
// webserver on port 80
AsyncWebServer server(80);
unsigned long nextStateCall = millis();
unsigned long lasti2cmsgtime = millis();
// switch GPIO4 und GPIO2 sync to the lamp
void onStatusChanged(){
//see https://ucexperiment.wordpress.com/2016/12/18/yunshan-esp8266-250v-15a-acdc-network-wifi-relay-module/
//Setting GPIO4 high, causes the relay to close the NO contact with
#ifdef USERELAY
bool lamp = lastState.valid && lastState.lampOn;
digitalWrite( ESP8266_GPIO4, lamp );
digitalWrite(LED_PIN, lamp);
#endif
}
// setup mcu
void setup(){
lastState.valid = false;
lastState.reserved = 0xAB;
// Setup I2C Master on UART Pins to use relayboard
//ESP 8266 PIN 1(TX0) and PIN 3 (RX0)
Wire.begin(1,3);
_debugOutputBuffer = (char *) calloc(2048, sizeof(char));
//setup wifi
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
WiFi.setAutoReconnect(true);
while (WiFi.status() != WL_CONNECTED) {
delay(100);
}
// setup http server
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
AsyncWebServerResponse *response = request->beginResponse_P( 200, "text/html", index_html,sizeof(index_html));
response->addHeader("Content-Encoding","deflate");
request->send(response);
});
server.on("/status", HTTP_GET, [](AsyncWebServerRequest *request){
AsyncResponseStream *response = request->beginResponseStream("application/json");
DynamicJsonDocument root(1024);
root["valid"] = lastState.valid;
root["doorstate"] = lastState.doorState;
root["doorposition"] = lastState.doorCurrentPosition;
root["doortarget"] = lastState.doorTargetPosition;
root["lamp"] = lastState.lampOn;
root["debug"] = lastState.reserved;
root["cc"] = lastState.cc;
root["lastresponse"] = millis()-lasti2cmsgtime;
serializeJson(root,*response);
request->send(response);
});
server.on("/command", HTTP_GET, [] (AsyncWebServerRequest *request) {
if (request->hasParam("action")) {
int actionid = request->getParam("action")->value().toInt();
switch (actionid){
case I2C_CMD_CLOSEDOOR:
case I2C_CMD_OPENDOOR:
case I2C_CMD_OPENDOORHALF:
case I2C_CMD_STOPDOOR:
case I2C_CMD_VENTPOS:
case I2C_CMD_TOGGLELAMP:
Wire.beginTransmission(I2CADDR);
Wire.write((unsigned char)actionid);
Wire.endTransmission();
break;
default:
break;
}
}
request->send(200, "text/plain", "OK");
});
server.on("/crashinfo", HTTP_GET, [] (AsyncWebServerRequest *request){
strcpy(_debugOutputBuffer, "");
SaveCrash.print(_debugOutputBuffer,2048);
if (request->hasParam("clear")) {
SaveCrash.clear();
}
request->send(200, "text/plain", _debugOutputBuffer);
});
server.on("/sysinfo", HTTP_GET, [] (AsyncWebServerRequest *request) {
char buffer[150];
rst_info* rinfo = ESP.getResetInfoPtr();
AsyncResponseStream *response = request->beginResponseStream("application/json");
DynamicJsonDocument root(1024);
root["freemem"] = ESP.getFreeHeap();
root["hostname"] = WiFi.hostname();
root["ip"] = WiFi.localIP().toString();
root["ssid"] = String(ssid);
root["wifistatus"] = WiFi.status();
root["resetreason"] =ESP.getResetReason();
root["errors"] = rinfo->exccause;
//The address of the last crash is printed, which is used to
sprintf(buffer, "epc1=0x%08x, epc2=0x%08x, epc3=0x%08x, excvaddr=0x%08x, depc=0x%08x, exccause=0x%x, reason=0x%x",
rinfo->epc1, rinfo->epc2, rinfo->epc3, rinfo->excvaddr, rinfo->depc, rinfo->exccause, rinfo->reason);
root["rstinfo"] = buffer;
serializeJson(root,*response);
request->send(response);
});
AsyncElegantOTA.begin(&server);
server.begin();
//setup relay board
#ifdef USERELAY
pinMode( ESP8266_GPIO4, OUTPUT ); // Relay control pin.
pinMode( ESP8266_GPIO5, INPUT_PULLUP ); // Input pin.
pinMode( LED_PIN, OUTPUT ); // ESP8266 module blue L
digitalWrite( ESP8266_GPIO4, 0 );
digitalWrite(LED_PIN,0);
#endif
}
void loop(){
if(nextStateCall < millis()){
Wire.requestFrom(I2CADDR, sizeof(SHCIState));
if(sizeof(SHCIState) != Wire.readBytes((unsigned char*) &lastState, sizeof(SHCIState))){
lasti2cmsgtime = millis();
onStatusChanged();
}
nextStateCall = millis()+200;
}
AsyncElegantOTA.loop();
}

View File

@ -0,0 +1 @@
python compress.py

View File

View File

@ -0,0 +1,35 @@
#pip install htmlmin
#or python -m pip install htmlmin
#pip install jsmin
#or python -m pip install jsmin
import gzip
import zlib
import htmlmin
from jsmin import jsmin
content = ""
with open('index.html','rt',encoding="utf-8") as f:
content=f.read()
content = htmlmin.minify(content, remove_comments=True, remove_empty_space=True, remove_all_empty_space=True, reduce_empty_attributes=True, reduce_boolean_attributes=False, remove_optional_attribute_quotes=True, convert_charrefs=True, keep_pre=False)
import re
regex = r"<script>(.+?)<\/script>"
content = re.sub(regex, lambda x: "<script>"+jsmin(x.group(1))+"</script>" ,content, 0, re.DOTALL)
result =""
for c in zlib.compress(content.encode("UTF-8"),9):
result= result + ("0x%02X" %c)
if len(result)> 0:
result=result + ","
with open('../index_html.h',"wt") as f:
f.write("const uint8_t index_html[] PROGMEM = {");
f.write(result.strip(","))
f.write("};");

View File

@ -0,0 +1,159 @@
<!DOCTYPE HTML><html>
<head>
<title>Garagentor Steuerung</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8">
<link rel="icon" href="data:,">
<style>
html {font-family: Arial; display: inline-block; text-align: center;}
h2 {font-size: 3.0rem;}
p {font-size: 3.0rem;}
body {max-width: 600px; margin:0px auto; padding-bottom: 25px;}
.switch {position: relative; display: inline-block; width: 80px; height: 48px}
.switch input {display: none}
.slider {position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; border-radius: 6px}
.slider:before {position: absolute; content: ""; height: 32px; width: 32px; left: 8px; bottom: 8px; background-color: #fff; -webkit-transition: .4s; transition: .4s; border-radius: 3px}
input:checked+.slider {background-color: #b30000}
input:checked+.slider:before {-webkit-transform: translateX(32px); -ms-transform: translateX(32px); transform: translateX(32px)}
.button {
border: 4px;
border-style: solid;
color: black;
width: 100px;
height: 100px;
text-align: center;
display: inline;
text-decoration: none;
display: inline-block;
font-weight: bold;
font-size: 20px;
margin: 4px 2px;
cursor: pointer;
border-radius: 50px;
}
.buttonred {border-color: #ED5961;}
.buttonyellow {border-color: #fcca00;}
.buttongreen {border-color: #48bd81;}
</style>
</head>
<body>
<h2>Garagentor</h2>
<canvas width="250px" height="250px" id="dc"></canvas>
<h3 id="status">warte auf Verbindung.</h3>
<hr/>
<button class="button buttonred" onclick="doCommand(1)">Auf</button> <button class="button buttonyellow" onclick="doCommand(2)">Stop</button> <button class="button buttongreen" onclick="doCommand(0)">Zu</button>
<hr/>
<h4>Licht</h4><label class="switch"><input type="checkbox" onchange="doCommand(5)" id="light"><span class="slider"></span></label>
<hr/>
<script>
doorpos = 10;
dir = -1;
isconnected = false;
var animation;
function startDoorAnimation() {
if(!animation){
animation = setInterval(Draw, 200);
}
}
function stopDoorAnimation() {
if(animation){
clearInterval(animation);
}
animation = null;
}
function Draw(){
var svg =document.getElementById("svg");
var dc = document.getElementById("dc");
var ctx = dc.getContext("2d");
width = dc.width;
height = dc.height;
ctx.clearRect(0, 0, width, height);
xa= [18,18,10,3,174,347,340,332,332,297,297,52,52,18];
ya= [327,114,117,100,22,100,117,114,327,327,112,112,327,327];
ctx.fillStyle = "black";
ctx.beginPath();
for (i =0;i<xa.length;i++){
if(i==0){
ctx.moveTo((xa[i]/350) * width , (ya[i]/350)*width);
}else{
ctx.lineTo((xa[i]/350) * width, (ya[i]/350)*width);
}
}
ctx.fill();
for(i=0;i<doorpos;i++){
ctx.fillRect((62/350) * width, ((120+21*i)/350) * width, (225/350) * width, (18/350) * width);
}
doorpos+=dir;
if(dir<0 && doorpos<=0){
dir=dir*-1;
}
if(dir>0 &&doorpos>=10){
dir=dir*-1;
}
}
function doCommand(action) {
if(!isconnected){return;}
var xhr = new XMLHttpRequest();
xhr.open("GET", "/command?action="+action, true);
xhr.send();
}
function setDoorState(state){
stopDoorAnimation();
if(state == 0) {doorpos = 10;dir = -1};
if(state == 1) {doorpos = 0; dir = 1};
if(state == 2) {doorpos = 5; dir = 1};
Draw();
if(state == 3) startDoorAnimation();
}
function getStatusText(id){
result = "Das Tor "
switch(id)
{
case 0x20: setDoorState(1); return result+"ist geöffnet.";
case 0x40: setDoorState(0); return result+"ist geschlossen.";
case 0x80: setDoorState(2); return result+"ist teilgeöffnet.";
case 0x00: setDoorState(2); return result+"ist teilgeöffnet.";
case 0x02: setDoorState(3); return result+"schließt.";
case 0x01: setDoorState(3); return result+"öffnet.";
case -1: setDoorState(0); return "keine Verbindung zum Tor.";
default: return "unbekanter Status: " + id;
}
}
function updateData(){
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var status = JSON.parse(this.responseText);
isconnected = status.valid;
document.getElementById("status").innerHTML = getStatusText(status.valid?status.doorstate:-1);
document.getElementById("light").checked = status.lamp & status.valid;
return;
}
isconnected= false;
};
xmlhttp.open("GET", "/status", true);
xmlhttp.send();
}
Draw();
updateData();
setInterval(updateData, 3000);
</script>
</body>
</html>

View File

@ -0,0 +1 @@
python -m http.server

View File

@ -0,0 +1,9 @@
{
"valid" : true,
"doorstate" : 1,
"doorposition" : 0,
"doortarget" : 0,
"lamp" : true,
"debug" : 0,
"lastresponse" : 0
}