lot of cool stuff is working now :D

pull/1/head
Thomas Ballmann 4 years ago
parent 7ab6afc067
commit ee784749df

File diff suppressed because one or more lines are too long

@ -1,36 +0,0 @@
/**
* Mocking client-server processing
*/
const _settings = {
// connected wifi
"wifi_ssid": "",
// oparation mode
"device_mode": "active",
// Set rotation setting for display
// 0 thru 3 corresponding to 4 cardinal rotations
"device_rotation": 0,
// deep sleep timer
"cloud_refresh": 97
}
import axios from 'axios'
export default {
/**
* @param cb
* @returns {PromiseLike<any> | Promise<any>}
*/
getSettings(cb) {
return _settings
return axios
.get('/api/settings')
.then(response => cb(response.data))
}
}

@ -0,0 +1,80 @@
/**
* Mocking client-server processing
*/
// eslint-disable-next-line
const _settings = {
// connected wifi
"wifi_ssid": "",
// oparation mode
"device_mode": "active",
// Set rotation setting for display
// 0 thru 3 corresponding to 4 cardinal rotations
"device_rotation": 0,
// clound server endpoint
"cloud_server": ""
}
// eslint-disable-next-line
const _wifiScan = [{ "rssi": -59, "ssid": "xd-design.info", "bssid": "38:10:D5:34:80:1B", "channel": 11, "secure": 3 }, { "rssi": -75, "ssid": "FRITZ!Box 7430 JI", "bssid": "38:10:D5:5D:FE:7C", "channel": 1, "secure": 3 }, { "rssi": -87, "ssid": "Vodafone Hotspot", "bssid": "AA:0E:14:BD:50:ED", "channel": 1, "secure": 0 }, { "rssi": -88, "ssid": "WLAN-548426", "bssid": "E0:60:66:55:7F:C5", "channel": 1, "secure": 3 }, { "rssi": -89, "ssid": "Familie Kalinowski", "bssid": "C8:0E:14:BD:50:ED", "channel": 1, "secure": 3 }, { "rssi": -91, "ssid": "WLAN-507287", "bssid": "E0:60:66:48:6C:6B", "channel": 1, "secure": 3 }, { "rssi": -94, "ssid": "TP-LINK_7238", "bssid": "A4:2B:B0:D8:72:38", "channel": 3, "secure": 3 }]
import axios from 'axios'
export default {
/**
* @param cb
* @returns {PromiseLike<any> | Promise<any>}
*/
getSettings(cb) {
return axios
.get('/api/settings')
.then(response => cb(response.data))
},
/**
* @returns {IDBRequest<IDBValidKey> | Promise<void>}
*/
putSettings(settings, cb) {
return axios
.put('/api/settings', settings, {
headers: {
'Content-Type': 'application/json'
}
})
.then(response => cb(response.data))
},
/**
* scan for wifi in range
* @param {*} cb
*/
wifiScan(cb) {
return cb(_wifiScan)
// eslint-disable-next-line
return axios
.get('/api/wifi/scan')
.then(response => cb(response.data))
},
wifiConnect(ssid, password, cb) {
return axios
.put('/api/wifi/connect', {
ssid: ssid,
password: password
}, {
headers: {
'Content-Type': 'application/json'
}
})
.then(response => cb(response.data))
}
}

@ -8,5 +8,5 @@ Vue.config.productionTip = false
new Vue({
vuetify,
router,
render: h => h(App)
render: h => h(App)
}).$mount('#app')

@ -3,6 +3,7 @@ import VueRouter from 'vue-router'
const Dashboard = () => import(/* webpackChunkName: "dashboard" */ '../views/Dashboard')
const Settings = () => import(/* webpackChunkName: "settings" */ '../views/Settings')
const Wifi = () => import(/* webpackChunkName: "wifi" */ '../views/Wifi')
const Sandbox = () => import(/* webpackChunkName: "sandbox" */ '../views/Sandbox')
@ -12,6 +13,7 @@ export default new VueRouter({
routes: [
{ path: '/', component: Dashboard },
{ path: '/settings', component: Settings },
{ path: '/wifi', component: Wifi },
{ path: '/sandbox', component: Sandbox },
{ path: '*', redirect: '/' }

@ -18,4 +18,4 @@
<style scoped>
</style>
</style>

@ -1,18 +1,88 @@
<template>
<v-container
fluid
grid-list-md
pa-2
>
settings...
</v-container>
<v-layout fluid fill-height>
<template v-if="isLoading">
<v-overlay :absolute="true" :value="true">
<v-progress-circular indeterminate size="64"></v-progress-circular>
</v-overlay>
</template>
<template v-if="true">
<v-container>
<v-snackbar
v-model="isSnackbar"
:timeout="3000"
color="success"
>
i8n:saved
</v-snackbar>
<v-card
class="mx-auto"
width="400"
>
<v-card-text>
<v-select
:items="deviceMode"
v-model="settings.device_mode"
label="i8n:Device mode"
placeholder=""
></v-select>
<v-text-field
label="i8n:Cloud server"
v-model="settings.cloud_server"
placeholder="i8n:https://"
></v-text-field>
<v-btn class="mr-4 primary" @click="onSave">i8n:save</v-btn>
</v-card-text>
</v-card>
</v-container>
</template>
</v-layout>
</template>
<script>
//import api from '../api/core'
import apiDevice from '../api/device'
export default {
name: "Settings",
data: () => ({
isLoading: true,
isSnackbar: false,
settings: {
device_mode: null,
cloud_server: null
},
deviceMode: [
{text: 'Active', value: 'active'},
{text: 'Passive', value: 'passive'}
]
}),
created () {
apiDevice.getSettings(settings => {
this.settings = settings
this.isLoading = false
})
},
methods: {
onSave () {
this.isLoading = true
apiDevice.putSettings(this.settings, (data) => {
console.log(data)
this.isLoading = false
this.isSnackbar = true
})
}
}
}
</script>

@ -0,0 +1,156 @@
<template>
<v-layout fluid fill-height>
<template v-if="isLoading">
<v-overlay :absolute="true" :value="true">
<v-progress-circular indeterminate size="64"></v-progress-circular>
</v-overlay>
</template>
<template v-if="true">
<v-container>
<v-snackbar
v-model="isSnackbar"
:timeout="3000"
color="success"
>
i8n:saved
</v-snackbar>
<v-dialog v-model="wifiConnectModal" max-width="400">
<v-card>
<v-card-title class="headline">
{{ wifiConnectSSID }}
</v-card-title>
<v-card-text>
<v-text-field
v-model="password"
:append-icon="show1 ? 'mdi-eye' : 'mdi-eye-off'"
:type="show1 ? 'text' : 'password'"
label="i8n:Password"
@click:append="show1 = !show1"
></v-text-field>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn text @click="wifiConnectModal = false">i8n:Cancel</v-btn>
<v-btn color="primary darken-1" text @click="onWifiConnect()">i8n:Connect</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-card
class="mx-auto"
max-width="300"
tile
>
<v-list >
<v-subheader>Wifi found</v-subheader>
<v-list-item-group v-model="wifiConnectSSID" color="primary">
<v-list-item
v-for="(wifi, i) in wifiAvailable"
:key="i"
:value="wifi.ssid"
@click.stop="wifiConnectModal = true"
>
<v-list-item-icon>
<v-icon v-text="getWifiIcon(wifi)"></v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title v-text="wifi.ssid"></v-list-item-title>
<v-list-item-subtitle v-text="wifi.bssid"></v-list-item-subtitle>
</v-list-item-content>
<v-list-item-avatar>
<v-avatar color="teal" size="24">
<span class="white--text headline caption">{{ wifi.channel }}</span>
</v-avatar>
</v-list-item-avatar>
</v-list-item>
</v-list-item-group>
</v-list>
</v-card>
</v-container>
</template>
</v-layout>
</template>
<script>
import apiDevice from '../api/device'
export default {
name: "Settings",
data: () => ({
isLoading: true,
isSnackbar: false,
wifiAvailable: [],
wifiConnectSSID: null,
wifiConnectModal: false,
show1: false,
}),
created () {
this.$vuetify.icons.values.signalWifi0 = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_0_bar/baseline.svg')}
this.$vuetify.icons.values.signalWifi1 = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_1_bar/baseline.svg')}
this.$vuetify.icons.values.signalWifi1Lock = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_1_bar_lock/baseline.svg')}
this.$vuetify.icons.values.signalWifi2 = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_2_bar/baseline.svg')}
this.$vuetify.icons.values.signalWifi2Lock = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_2_bar_lock/baseline.svg')}
this.$vuetify.icons.values.signalWifi3 = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_3_bar/baseline.svg')}
this.$vuetify.icons.values.signalWifi3Lock = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_3_bar_lock/baseline.svg')}
this.$vuetify.icons.values.signalWifi4 = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_4_bar/baseline.svg')}
this.$vuetify.icons.values.signalWifi4Lock = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_4_bar_lock/baseline.svg')}
apiDevice.wifiScan(list => {
this.wifiAvailable = list
this.isLoading = false
})
},
computed: {
},
methods: {
getWifiIcon(wifi) {
let icon = '$signalWifi'
// strength
if (wifi.rssi >= -67) {
icon += 4
}
else if (wifi.rssi >= -70) {
icon += 3
}
else if (wifi.rssi >= -80) {
icon += 2
}
else if (wifi.rssi >= -90) {
icon += 1
}
else {
icon += 0
}
// secure
if (wifi.secure !== 0 && wifi.rssi >= -90) {
icon += 'Lock'
}
return icon
},
onWifiConnect() {
// TODO
//this.isLoading = true
//alert("onWifiConnect")
//apiDevice.wifiConnect()
}
}
}
</script>
<style scoped>
</style>

@ -1,8 +1,17 @@
module.exports = {
"outputDir": "../data/dist",
"outputDir": "../data/dist",
"filenameHashing": false,
"productionSourceMap": false,
"transpileDependencies": [
"vuetify"
]
"transpileDependencies": [
"vuetify"
],
devServer: {
proxy: {
'^/api': {
target: 'http://192.168.178.65:80',
ws: true,
changeOrigin: true
},
}
}
}

@ -4,9 +4,7 @@
#include "AsyncJson.h"
#include "ArduinoJson.h"
#include "settings.h"
// https://techtutorialsx.com/2018/09/17/esp32-arduino-web-server-serving-external-css-file/
// https://docs.platformio.org/en/latest/platforms/espressif8266.html#uploading-files-to-file-system-spiffs
#include "device.h"
AsyncWebServer server(80);
@ -14,17 +12,18 @@ void setupSettingsGet();
void setupSettingsPost();
void setupWifiScan();
void setupWifiConnect();
void setupCurrentImage();
void setupApp()
{
Serial.println("setup configure");
if (!SPIFFS.begin()) {
if (!SPIFFS.begin())
{
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
// @see https://github.com/me-no-dev/ESPAsyncWebServer
// @see https://arduinojson.org/v6/assistant/
@ -32,27 +31,26 @@ void setupApp()
server
.serveStatic("/", SPIFFS, "/dist/")
.setDefaultFile("index.html")
.setCacheControl("max-age=600")
;
.setCacheControl("max-age=600");
setupSettingsGet();
setupSettingsPost();
setupWifiScan();
setupWifiConnect();
setupCurrentImage();
// TODO response
server.on("/test", HTTP_GET, [] (AsyncWebServerRequest *request) {
server.on("/stats", HTTP_GET, [](AsyncWebServerRequest *request) {
AsyncResponseStream *response = request->beginResponseStream("application/json");
DynamicJsonDocument root(1024);
root["heap"] = ESP.getFreeHeap();
root["ssid"] = WiFi.SSID();
root["wifi"] = WiFi.SSID();
root["sleep"] = 97;
serializeJson(root, *response);
request->send(response);
});
});
// CORS
//DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
@ -61,120 +59,141 @@ void setupApp()
Serial.println("setup configure - done");
}
void setupSettingsGet()
{
server.on("/api/settings", HTTP_GET, [] (AsyncWebServerRequest *request) {
server.on("/api/settings", HTTP_GET, [](AsyncWebServerRequest *request) {
AsyncResponseStream *response = request->beginResponseStream("application/json");
DynamicJsonDocument root(1024);
//root["heap"] = ESP.getFreeHeap();
// NVS.getString("wifi_ssid");
root["wifi_ssid"] = NVS.getString("wifi_ssid"); // WiFi.SSID();
root["device_mode"] = "active";
//root["wifi_ssid"] = NVS.getString("wifi_ssid");
root["device_mode"] = NVS.getString("device_mode");
root["device_rotation"] = 0;
root["cloud_refresh"] = 97; // aktueller sleep timer
root["cloud_server"] = NVS.getString("cloud_server");
serializeJson(root, *response);
request->send(response);
});
});
}
void setupSettingsPost()
{
AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/api/settings", [](AsyncWebServerRequest *request, JsonVariant &json) {
JsonObject jsonObj = json.to<JsonObject>();
Serial.println("/api/settings");
NVS.setString("wifi_ssid", jsonObj["wifi_ssid"], true);
/*
char hostname[64];
strlcpy(hostname, // <- destination
jsonObj["wifi_ssid"] | "example.com", // <- source
sizeof(hostname)); // <- destination's capacity
*/
Serial.println(NVS.getString("wifi_ssid"));
server.on("/api/settings", HTTP_PUT, [](AsyncWebServerRequest *request) { /* nothing and dont remove it */ }, NULL, [](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, data);
if (error) {
Serial.print(F("deserializeJson() failed with code "));
Serial.println(error.c_str());
// ...
});
server.addHandler(handler);
request->send(404, "text/plain", "");
}
else
{
if (doc.containsKey("device_mode")) {
NVS.setString("device_mode", doc["device_mode"]);
}
if (doc.containsKey("cloud_server")) {
NVS.setString("cloud_server", doc["cloud_server"]);
//Serial.println(doc["cloud_server"].as<char*>());
}
request->send(200, "application/ld+json; charset=utf-8", "{}");
} });
}
/**
* @todo
*/
void setupCurrentImage()
{
server.on("/current-image", HTTP_GET, [] (AsyncWebServerRequest *request) {
server.on("/current-image", HTTP_GET, [](AsyncWebServerRequest *request) {
Serial.println("/current-image");
request->send(SPIFFS, "/currentImage.bin", "image/x-bmp");
});
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/currentImage.bin", "image/vnd.wap.wbmp");
/*
server.on("/current-image2", HTTP_GET, [](AsyncWebServerRequest *request) {
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/currentImage.bin", "image/x-bmp"); // image/x-bmp | image/vnd.wap.wbmp
//response->addHeader("Content-Encoding", "gzip");
//response->addHeader("Content-Disposition", "inline; filename=\"image.wbmp\"");
request->send(response);
});
});
*/
}
/**
* @todo
*/
void setupWifiScan()
{
//First request will return 0 results unless you start scan from somewhere else (loop/setup)
//Do not request more often than 3-5 seconds
server.on("/api/wifi/scan", HTTP_GET, [](AsyncWebServerRequest *request){
server.on("/api/wifi/scan", HTTP_GET, [](AsyncWebServerRequest *request) {
String json = "[";
int n = WiFi.scanComplete();
if(n == -2){
if (n == -2)
{
WiFi.scanNetworks(true);
} else if(n){
for (int i = 0; i < n; ++i){
if(i) json += ",";
json += "{";
json += "\"rssi\":"+String(WiFi.RSSI(i));
json += ",\"ssid\":\""+WiFi.SSID(i)+"\"";
json += ",\"bssid\":\""+WiFi.BSSIDstr(i)+"\"";
json += ",\"channel\":"+String(WiFi.channel(i));
json += ",\"secure\":"+String(WiFi.encryptionType(i));
json += "}";
}
else if (n)
{
for (int i = 0; i < n; ++i)
{
if (i)
{
json += ",";
}
json += "{";
json += "\"rssi\":" + String(WiFi.RSSI(i));
json += ",\"ssid\":\"" + WiFi.SSID(i) + "\"";
json += ",\"bssid\":\"" + WiFi.BSSIDstr(i) + "\"";
json += ",\"channel\":" + String(WiFi.channel(i));
json += ",\"secure\":" + String(WiFi.encryptionType(i));
json += "}";
}
WiFi.scanDelete();
if(WiFi.scanComplete() == -2){
if (WiFi.scanComplete() == -2)
{
WiFi.scanNetworks(true);
}
}
json += "]";
request->send(200, "application/json", json);
json = String();
});
}
/**
* @todo
*/
void setupWifiConnect()
{
AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/api/wifi/connect", [](AsyncWebServerRequest *request, JsonVariant &json) {
//JsonObject jsonObj = json.to<JsonObject>();
// TODO save settings
server.on("/api/wifi/connect", HTTP_PUT, [](AsyncWebServerRequest *request) { /* nothing and dont remove it */ }, NULL, [](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
DynamicJsonDocument doc(1024);
Serial.println("/api/wifi/connect");
DeserializationError error = deserializeJson(doc, data);
if (error) {
Serial.print(F("deserializeJson() failed with code "));
Serial.println(error.c_str());
AsyncResponseStream *response = request->beginResponseStream("application/json");
DynamicJsonDocument root(1024);
root["status"] = true;
root["message"] = "";
serializeJson(root, *response);
request->send(response);
// ist ein restart wichtig?
//ESP.restart();
request->send(404, "text/plain", "");
}
else
{
if (doc.containsKey("user")) {
//NVS.setString("device_mode", doc["device_mode"]);
}
if (doc.containsKey("password")) {
//NVS.setString("cloud_server", doc["cloud_server"]);
//Serial.println(doc["cloud_server"].as<char*>());
}
});
server.addHandler(handler);
request->send(200, "application/ld+json; charset=utf-8", "{}");
} });
}

@ -7,15 +7,17 @@
#include "display.h"
#include "device.h"
// TODO SMART SIGN CONFIG ========
#define config_PullServer "http://paperdash.sonic.da-tom.com/gateway.php/" // pull server address
String config_UUID = "22805938-2280-8022-3822-385980225980"; // TODO
//#define config_PullServer "http://paperdash.sonic.da-tom.com/gateway.php/" // pull server address
String config_UUID = "22805938-2280-8022-3822-385980225980"; // TODO
//String config_PullServer;
//String config_UUID;
//#define config_PullServer = NVS.getString("cloud_server");
//String config_UUID = NVS.getString("cloud_server");
// SMART SIGN CONFIG ========
unsigned long requestInterval = 1000; // 1 sec
unsigned long previousTime = 0;
// runtime data
const char *setting_HeaderKeys[] = {
// update deep sleep interval
@ -23,21 +25,19 @@ const char *setting_HeaderKeys[] = {
// execute firmware update url
"UpdateFirmware"};
//#include <GxEPD2_BW.h>
//#define FRAME_BUFFERBUFFE_SIZE GxEPD2_750::WIDTH *GxEPD2_750::HEIGHT / 8
//PROGMEM unsigned char displayImageBuffer[FRAME_BUFFERBUFFE_SIZE];
HTTPClient http;
void pullData();
void requestCloud();
bool isCloudSetupComplete();
void updateInterval(unsigned long interval);
void setupCloud()
{
Serial.println("setup cloud");
// load settings
//config_PullServer = NVS.getString("cloud_gateway");
config_UUID = NVS.getString("cloud_uuid");
updateInterval(10);
SPIFFS.begin();
http.useHTTP10(true); // http1.1 chunked übertragung funktioniert irgendwie nicht
http.setTimeout(7000);
@ -48,7 +48,55 @@ void setupCloud()
void loopCloud()
{
pullData();
if (isCloudSetupComplete())
{
if (NVS.getString("device_mode") == "passive")
{
Serial.println("requestCloud()");
Serial.println("passive, after this, we go to deep sleep and startover here again");
}
else
{
unsigned long currentTime = millis();
if (currentTime - previousTime >= requestInterval)
{
/* Update the timing for the next time around */
previousTime = currentTime;
Serial.println("requestCloud()");
requestCloud();
}
}
}
}
bool isCloudSetupComplete()
{
return NVS.getString("cloud_server") != "" && config_UUID != "";
}
/**
* interval in seconds
*/
void updateInterval(unsigned long interval)
{
//Serial.print("updateInterval: ");
//Serial.println(interval);
// update config
Serial.println("###### config update");
Serial.println(" set deep sleep interval from: " + String(deviceGetSleepInterval()) + " to " + interval);
Serial.println("###### config update");
// active wait state
requestInterval = interval * 1000;
// passive deep sleep state
deviceSetSleepInterval(interval);
}
/**
@ -56,10 +104,11 @@ void loopCloud()
* 2. neues bild vom server laden und anzeigen sofern vorhanden
* @return bool true on new data to display
*/
void pullData()
void requestCloud()
{
String pullUrl = String(config_PullServer) + "/" + config_UUID; // + "?deep-sleep=" + String(config_DeepSleepInterval) + "&wakeup=" + getWakeupReason();
//String pullUrl = String(config_PullServer) + "/" + config_UUID; // + "?deep-sleep=" + String(config_DeepSleepInterval) + "&wakeup=" + getWakeupReason();
String pullUrl = NVS.getString("cloud_server") + "/" + config_UUID; // + "?deep-sleep=" + String(config_DeepSleepInterval) + "&wakeup=" + getWakeupReason();
Serial.println(pullUrl);
http.begin(pullUrl);
@ -75,17 +124,12 @@ void pullData()
if (false && DeepSleepInterval.toInt() == 0)
{
// disable deep sleep
Serial.println("###### deep sleep disabled");
deviceSetSleepInterval(0);
//Serial.println("###### deep sleep disabled");
//deviceSetSleepInterval(0);
}
else if (DeepSleepInterval.toInt() > 5 && DeepSleepInterval.toInt() != deviceGetSleepInterval())
{
// update config
Serial.println("###### config update");
Serial.println(" set deep sleep interval from: " + String(deviceGetSleepInterval()) + " to " + DeepSleepInterval);
Serial.println("###### config update");
deviceSetSleepInterval(DeepSleepInterval.toInt());
updateInterval(DeepSleepInterval.toInt());
}
// update to new firmware

@ -1,81 +1,85 @@
#include "device.h"
#include "settings.h"
#define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */
RTC_DATA_ATTR int bootCount = 0;
RTC_DATA_ATTR long config_DeepSleepInterval = 0;
RTC_DATA_ATTR long config_DeepSleepInterval = 10; // sec
unsigned long bootTime = 0;
// private methods
void sleepDevice();
String getWakeupReason();
bool isBootTimeOver();
/**
* setup deep sleep mode
*/
void setupDevice()
{
// increment boot number and print it every reboot
bootCount++;
// TODO muss in der cloud passieren? config wakeup timer
//deviceSetSleepInterval(300);
// increment boot number and print it every reboot
bootCount++;
bootTime = millis();
}
void loopDevice()
{
if (config_DeepSleepInterval > 0)
{
sleepDevice();
// device stop here
}
if (NVS.getString("device_mode") == "passive")
{
// TODO give user a time window to switch the mode to active
if (true) // isBootTimeOver()
{
sleepDevice();
// device stop here
}
}
}
void sleepDevice()
{
Serial.println("Going to sleep now");
Serial.flush();
Serial.println("Going to sleep now");
Serial.flush();
esp_sleep_enable_timer_wakeup(config_DeepSleepInterval * uS_TO_S_FACTOR);
esp_deep_sleep_start();
esp_deep_sleep_start();
}
void deviceSetSleepInterval(long interval)
{
config_DeepSleepInterval = interval;
}
long deviceGetSleepInterval()
{
return config_DeepSleepInterval;
}
String getWakeupReason()
bool isBootTimeOver()
{
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
//return String(wakeup_reason);
switch (wakeup_reason)
{
case 1:
return String("Wakeup caused by external signal using RTC_IO");
case 2:
return String("Wakeup caused by external signal using RTC_CNTL");
case 3:
return String("Wakeup caused by timer");
case 4:
return String("Wakeup caused by touchpad");
case 5:
return String("Wakeup caused by ULP program");
default:
return String("Wakeup was not caused by deep sleep: " + String(wakeup_reason));
}
return millis() - bootTime >= 60000;
}
return String("unkown");
String getWakeupReason()
{
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
//return String(wakeup_reason);
switch (wakeup_reason)
{
case 1:
return String("Wakeup caused by external signal using RTC_IO");
case 2:
return String("Wakeup caused by external signal using RTC_CNTL");
case 3:
return String("Wakeup caused by timer");
case 4:
return String("Wakeup caused by touchpad");
case 5:
return String("Wakeup caused by ULP program");
default:
return String("Wakeup was not caused by deep sleep: " + String(wakeup_reason));
}
return String("unkown");
}

@ -91,4 +91,4 @@ void printSplash()
display.setCursor(x, y);
display.print(Hello);
} while (display.nextPage());
}
}

@ -0,0 +1,298 @@
#include <Arduino.h>
#include <sys/stat.h>
typedef struct {
uint16_t x1;
uint16_t y1;
uint16_t x2;
uint16_t y2;
} dispWin_t;
dispWin_t dispWin = {
.x1 = 0,
.y1 = 0,
.x2 = 640, // DEFAULT_TFT_DISPLAY_WIDTH,
.y2 = 384, // DEFAULT_TFT_DISPLAY_HEIGHT,
};
uint8_t image_debug;
// @see https://github.com/Bodmer/TJpg_Decoder/blob/master/examples/SPIFFS_Jpg/SPIFFS_Jpg.ino
// @see https://github.com/loboris/ESP32_TFT_library/blob/aa21772f54a71887ec08b2e8bbaef9e304009891/components/tft/tft.c
void TFT_bmp_image(int x, int y, uint8_t scale, char *fname, uint8_t *imgbuf, int size)
{
FILE *fhndl = NULL;
struct stat sb;
int i, err=0;
int img_xsize, img_ysize, img_xstart, img_xlen, img_ystart, img_ylen;
int img_pos, img_pix_pos, scan_lines, rd_len;
uint8_t tmpc;
uint16_t wtemp;
uint32_t temp;
int disp_xstart, disp_xend, disp_ystart, disp_yend;
uint8_t buf[56];
char err_buf[64];
uint8_t *line_buf[2] = {NULL,NULL};
uint8_t lb_idx = 0;
uint8_t *scale_buf = NULL;
uint8_t scale_pix;
uint16_t co[3] = {0,0,0}; // RGB sum
uint8_t npix;
if (scale > 7) scale = 7;
scale_pix = scale+1; // scale factor ( 1~8 )
if (fname) {
// * File name is given, reading image from file
if (stat(fname, &sb) != 0) {
sprintf(err_buf, "opening file");
err = -1;
goto exit;
}
size = sb.st_size;
fhndl = fopen(fname, "r");
if (!fhndl) {
sprintf(err_buf, "opening file");
err = -2;
goto exit;
}
i = fread(buf, 1, 54, fhndl); // read header
}
else {
// * Reading image from buffer
if ((imgbuf) && (size > 54)) {
memcpy(buf, imgbuf, 54);
i = 54;
}
else i = 0;
}
sprintf(err_buf, "reading header");
if (i != 54) {err = -3; goto exit;}
// ** Check image header and get image properties
if ((buf[0] != 'B') || (buf[1] != 'M')) {err=-4; goto exit;} // accept only images with 'BM' id
memcpy(&temp, buf+2, 4); // file size
if (temp != size) {err=-5; goto exit;}
memcpy(&img_pos, buf+10, 4); // start of pixel data
memcpy(&temp, buf+14, 4); // BMP header size
if (temp != 40) {err=-6; goto exit;}
memcpy(&wtemp, buf+26, 2); // the number of color planes
if (wtemp != 1) {err=-7; goto exit;}
memcpy(&wtemp, buf+28, 2); // the number of bits per pixel
if (wtemp != 24) {err=-8; goto exit;}
memcpy(&temp, buf+30, 4); // the compression method being used
if (temp != 0) {err=-9; goto exit;}
memcpy(&img_xsize, buf+18, 4); // the bitmap width in pixels
memcpy(&img_ysize, buf+22, 4); // the bitmap height in pixels
// * scale image dimensions
/*
img_xlen = img_xsize / scale_pix; // image display horizontal size
img_ylen = img_ysize / scale_pix; // image display vertical size
if (x == CENTER) x = ((dispWin.x2 - dispWin.x1 + 1 - img_xlen) / 2) + dispWin.x1;
else if (x == RIGHT) x = dispWin.x2 + 1 - img_xlen;
if (y == CENTER) y = ((dispWin.y2 - dispWin.y1 + 1 - img_ylen) / 2) + dispWin.y1;
else if (y == BOTTOM) y = dispWin.y2 + 1 - img_ylen;
*/
if ((x < ((dispWin.x2 + 1) * -1)) || (x > (dispWin.x2 + 1)) || (y < ((dispWin.y2 + 1) * -1)) || (y > (dispWin.y2 + 1))) {
sprintf(err_buf, "out of display area (%d,%d", x, y);
err = -10;
goto exit;
}
// ** set display and image areas
if (x < dispWin.x1) {
disp_xstart = dispWin.x1;
img_xstart = -x; // image pixel line X offset
img_xlen += x;
}
else {
disp_xstart = x;
img_xstart = 0;
}
if (y < dispWin.y1) {
disp_ystart = dispWin.y1;
img_ystart = -y; // image pixel line Y offset
img_ylen += y;
}
else {
disp_ystart = y;
img_ystart = 0;
}
disp_xend = disp_xstart + img_xlen - 1;
disp_yend = disp_ystart + img_ylen - 1;
if (disp_xend > dispWin.x2) {
disp_xend = dispWin.x2;
img_xlen = disp_xend - disp_xstart + 1;
}
if (disp_yend > dispWin.y2) {
disp_yend = dispWin.y2;
img_ylen = disp_yend - disp_ystart + 1;
}
if ((img_xlen < 8) || (img_ylen < 8) || (img_xstart >= (img_xsize-2)) || ((img_ysize - img_ystart) < 2)) {
sprintf(err_buf, "image too small");
err = -11;
goto exit;
}
// ** Allocate memory for 2 lines of image pixels
line_buf[0] = static_cast<uint8_t *>(heap_caps_malloc(img_xsize*3, MALLOC_CAP_DMA));
if (line_buf[0] == NULL) {
sprintf(err_buf, "allocating line buffer #1");
err=-12;
goto exit;
}
line_buf[1] = static_cast<uint8_t *>(heap_caps_malloc(img_xsize*3, MALLOC_CAP_DMA));
if (line_buf[1] == NULL) {
sprintf(err_buf, "allocating line buffer #2");
err=-13;
goto exit;
}
if (scale) {
// Allocate memory for scale buffer
rd_len = img_xlen * 3 * scale_pix;
scale_buf = static_cast<uint8_t *>(malloc(rd_len*scale_pix));
if (scale_buf == NULL) {
sprintf(err_buf, "allocating scale buffer");
err=-14;
goto exit;
}
}
else rd_len = img_xlen * 3;
// ** ***************************************************** **
// ** BMP images are stored in file from LAST to FIRST line **
// ** ***************************************************** **
/* Used variables:
img_xsize horizontal image size in pixels
img_ysize number of image lines
img_xlen image display horizontal scaled size in pixels
img_ylen image display vertical scaled size in pixels
img_xstart first pixel in line to be displayed
img_ystart first image line to be displayed
img_xlen number of pixels in image line to be displayed, starting with 'img_xstart'
img_ylen number of lines in image to be displayed, starting with 'img_ystart'
rd_len length of color data which are read from image line in bytes
*/
// Set position in image to the first color data (beginning of the LAST line)
img_pos += (img_ystart * (img_xsize*3));
if (fhndl) {
if (fseek(fhndl, img_pos, SEEK_SET) != 0) {
sprintf(err_buf, "file seek at %d", img_pos);
err = -15;
goto exit;
}
}
if (image_debug) printf("BMP: image size: (%d,%d) scale: %d disp size: (%d,%d) img xofs: %d img yofs: %d at: %d,%d; line buf: 2* %d scale buf: %d\r\n",
img_xsize, img_ysize, scale_pix, img_xlen, img_ylen, img_xstart, img_ystart, disp_xstart, disp_ystart, img_xsize*3, ((scale) ? (rd_len*scale_pix) : 0));
// * Select the display
//disp_select();
while ((disp_yend >= disp_ystart) && ((img_pos + (img_xsize*3)) <= size)) {
if (img_pos > size) {
sprintf(err_buf, "EOF reached: %d > %d", img_pos, size);
err = -16;
goto exit1;
}
if (scale == 0) {
// Read the line of color data into color buffer
if (fhndl) {
i = fread(line_buf[lb_idx], 1, img_xsize*3, fhndl); // read line from file
if (i != (img_xsize*3)) {
sprintf(err_buf, "file read at %d (%d<>%d)", img_pos, i, img_xsize*3);
err = -16;
goto exit1;
}
}
else memcpy(line_buf[lb_idx], imgbuf+img_pos, img_xsize*3);
if (img_xstart > 0) memmove(line_buf[lb_idx], line_buf[lb_idx]+(img_xstart*3), rd_len);
// Convert colors BGR-888 (BMP) -> RGB-888 (DISPLAY) ===
for (i=0; i < rd_len; i += 3) {
tmpc = line_buf[lb_idx][i+2] & 0xfc; // save R
line_buf[lb_idx][i+2] = line_buf[lb_idx][i] & 0xfc; // B -> R
line_buf[lb_idx][i] = tmpc; // R -> B
line_buf[lb_idx][i+1] &= 0xfc; // G
}
img_pos += (img_xsize*3);
}
else {
// scale image, read 'scale_pix' lines and find the average color
for (scan_lines=0; scan_lines<scale_pix; scan_lines++) {
if (img_pos > size) break;
if (fhndl) {
i = fread(line_buf[lb_idx], 1, img_xsize*3, fhndl); // read line from file
if (i != (img_xsize*3)) {
sprintf(err_buf, "file read at %d (%d<>%d)", img_pos, i, img_xsize*3);
err = -17;
goto exit1;
}
}
else memcpy(line_buf[lb_idx], imgbuf+img_pos, img_xsize*3);
img_pos += (img_xsize*3);
// copy only data which are displayed to scale buffer
memcpy(scale_buf + (rd_len * scan_lines), line_buf[lb_idx]+img_xstart, rd_len);
}
// Populate display line buffer
for (int n=0;n<(img_xlen*3);n += 3) {
memset(co, 0, sizeof(co)); // initialize color sum
npix = 0; // initialize number of pixels in scale rectangle
// sum all pixels in scale rectangle
for (int sc_line=0; sc_line<scan_lines; sc_line++) {
// Get colors position in scale buffer
img_pix_pos = (rd_len * sc_line) + (n * scale_pix);
for (int sc_col=0; sc_col<scale_pix; sc_col++) {
co[0] += scale_buf[img_pix_pos];
co[1] += scale_buf[img_pix_pos + 1];
co[2] += scale_buf[img_pix_pos + 2];
npix++;
}
}
// Place the average in display buffer, convert BGR-888 (BMP) -> RGB-888 (DISPLAY)
line_buf[lb_idx][n+2] = (uint8_t)(co[0] / npix); // B
line_buf[lb_idx][n+1] = (uint8_t)(co[1] / npix); // G
line_buf[lb_idx][n] = (uint8_t)(co[2] / npix); // R
}
}
//wait_trans_finish(1);
//send_data(disp_xstart, disp_yend, disp_xend, disp_yend, img_xlen, (color_t *)line_buf[lb_idx]);
lb_idx = (lb_idx + 1) & 1; // change buffer
disp_yend--;
}
err = 0;
exit1:
//disp_deselect();
exit:
if (scale_buf) free(scale_buf);
if (line_buf[0]) free(line_buf[0]);
if (line_buf[1]) free(line_buf[1]);
if (fhndl) fclose(fhndl);
if ((err) && (image_debug)) printf("Error: %d [%s]\r\n", err, err_buf);
//return err;
}

@ -29,7 +29,7 @@ void setup()
if (wlan_isConnected())
{
//setupCloud();
setupCloud();
}
setupApp();
@ -44,7 +44,7 @@ void loop()
if (wlan_isConnected())
{
//loopCloud();
loopCloud();
}
loopDevice();

Loading…
Cancel
Save