Skip to content

Commit

Permalink
Improv and settings page
Browse files Browse the repository at this point in the history
  • Loading branch information
jnthas committed Apr 1, 2023
1 parent 00b777f commit fbaaddc
Show file tree
Hide file tree
Showing 7 changed files with 328 additions and 162 deletions.
196 changes: 113 additions & 83 deletions firmware/lib/cw-commons/CWWebServer.h
Original file line number Diff line number Diff line change
@@ -1,104 +1,134 @@
#pragma once

#include "ImprovWiFiLibrary.h"
#define HTTP_GET_SIZE 3
#define HTTP_POST_SIZE 4

#include <WiFi.h>
#include <CWPreferences.h>
#include "IOManager.h"

#include "StatusController.h"
#include "SettingsWebPage.h"

WiFiServer server(80);
ImprovWiFi improvSerial(&Serial);

struct ClockwiseWebServer
{

char linebuf[80];
int charcount = 0;


static void onImprovWiFiErrorCb(improv::Error err)
String httpBuffer;

static ClockwiseWebServer *getInstance()
{
static ClockwiseWebServer base;
return &base;
}

void startWebServer()
{
server.begin();
StatusController::getInstance()->blink_led(100, 3);
}

void stopWebServer()
{
server.stop();
}

void handleHttpRequest()
{
WiFiClient client = server.available();
if (client)
{
server.stop();
IOManager::blink_led(2000, 3);
}

static void onImprovWiFiConnectedCb(std::string ssid, std::string password)
{
ClockwiseParams::getInstance()->load();
ClockwiseParams::getInstance()->wifiSsid = String(ssid.c_str());
ClockwiseParams::getInstance()->wifiPwd = String(password.c_str());
ClockwiseParams::getInstance()->save();

startCWWebServer();
}

static void startCWWebServer() {
server.begin();
IOManager::blink_led(100, 3);
}
StatusController::getInstance()->blink_led(100, 1);

void begin()
{
WiFi.mode(WIFI_STA);
WiFi.disconnect();

improvSerial.setDeviceInfo(improv::ChipFamily::CF_ESP32, "CW-20230324", "1.1.0", "Clockwise");
improvSerial.onImprovWiFiError(onImprovWiFiErrorCb);
improvSerial.onImprovWiFiConnected(onImprovWiFiConnectedCb);

ClockwiseParams::getInstance()->load();
if (improvSerial.tryConnectToWifi(ClockwiseParams::getInstance()->wifiSsid.c_str(), ClockwiseParams::getInstance()->wifiPwd.c_str())) {
startCWWebServer();
}

}

void handleHttpRequest()
{
while (client.connected())
{
if (client.available())
{
char c = client.read();
httpBuffer.concat(c);

improvSerial.handleSerial();
if (c == '\n')
{
//Serial.println(httpBuffer);

if (!improvSerial.isConnected())
return;
uint8_t method_pos = httpBuffer.indexOf(' ');
uint8_t path_pos = httpBuffer.indexOf(' ', method_pos + 1);

WiFiClient client = server.available();
String method = httpBuffer.substring(0, method_pos);
String path = httpBuffer.substring(method_pos + 1, path_pos);
String key = "";
String value = "";

if (client)
{
IOManager::blink_led(100, 1);
memset(linebuf, 0, sizeof(linebuf));
charcount = 0;
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected())
if (method == "POST")
{
if (client.available())
{
char c = client.read();
// read char by char HTTP request
linebuf[charcount] = c;
if (charcount < sizeof(linebuf) - 1)
charcount++;

if (c == '\n' && currentLineIsBlank)
{
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close"); // the connection will be closed after completion of the response
client.println();
client.println("<!DOCTYPE HTML><html><body>");
client.println("<h1 id=\"welcome\">Welcome!</h1>");
client.println("<p>This is a simple webpage served by your ESP32</p>");
client.println("</body></html>");
break;
}
}
key = path.substring(path.indexOf('?') + 1, path.indexOf('='));
value = path.substring(path.indexOf('=') + 1, ' ');
path = path.substring(0, path.indexOf('?'));
}
delay(1);
client.stop();

processRequest(client, method, path, key, value);
httpBuffer = "";
break;
}
}
}
delay(1);
client.stop();
}
}

void processRequest(WiFiClient client, String method, String path, String key, String value)
{

// Serial.println(method);
// Serial.println(path);
// Serial.println(key);
// Serial.println(value);

if (method == "GET" && path == "/") {
client.println("HTTP/1.0 200 OK");
client.println("Content-Type: text/html");
client.println();
client.println(SETTINGS_PAGE);
} else if (method == "GET" && path == "/get") {
getCurrentSettings(client);
} else if (method == "POST" && path == "/set") {
ClockwiseParams::getInstance()->load();

//a baby seal has died due this ifs
if (key == ClockwiseParams::getInstance()->PREF_DISPLAY_BRIGHT) {
ClockwiseParams::getInstance()->displayBright = value.toInt();
} else if (key == ClockwiseParams::getInstance()->PREF_SWAP_BLUE_GREEN) {
ClockwiseParams::getInstance()->swapBlueGreen = (value == "1");
} else if (key == ClockwiseParams::getInstance()->PREF_USE_24H_FORMAT) {
ClockwiseParams::getInstance()->use24hFormat = (value == "1");
} else if (key == ClockwiseParams::getInstance()->PREF_TIME_ZONE) {
ClockwiseParams::getInstance()->timeZone = value;
}
ClockwiseParams::getInstance()->save();
client.println("HTTP/1.0 204 No Content");
}
}


void getCurrentSettings(WiFiClient client) {
ClockwiseParams::getInstance()->load();

char response[256];
snprintf(response, sizeof(response), "{\"%s\":%d,\"%s\":%d,\"%s\":%d,\"%s\":\"%s\",\"%s\":\"%s\"}", \
ClockwiseParams::getInstance()->PREF_DISPLAY_BRIGHT,
ClockwiseParams::getInstance()->displayBright,
ClockwiseParams::getInstance()->PREF_SWAP_BLUE_GREEN,
ClockwiseParams::getInstance()->swapBlueGreen,
ClockwiseParams::getInstance()->PREF_USE_24H_FORMAT,
ClockwiseParams::getInstance()->use24hFormat,
ClockwiseParams::getInstance()->PREF_TIME_ZONE,
ClockwiseParams::getInstance()->timeZone.c_str(),
ClockwiseParams::getInstance()->PREF_WIFI_SSID,
ClockwiseParams::getInstance()->wifiSsid.c_str());

client.println("HTTP/1.0 200 OK");
client.println("Content-Type: application/json");
client.println();
client.println(response);
}


};
137 changes: 137 additions & 0 deletions firmware/lib/cw-commons/SettingsWebPage.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#pragma once

#include <Arduino.h>



typedef struct SettingsCards {
const char* title;
const char* description;
const char* formInput;
const char* icon;
const char* propName;
} SettingsCard;

const char SETTINGS_PAGE[] PROGMEM = R""""(
<!DOCTYPE html>
<html>
<title>Clockwise Settings</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="shortcut icon" type="image/x-icon"
href="https://github.com/jnthas/clockwise/raw/gh-pages/static/images/favicon.png">
<body>
<div class="w3-container">
<img src="https://github.com/jnthas/clockwise/raw/gh-pages/static/images/clockwise_logo.png" alt="Logo"
class="w3-image w3-padding" style="width:50%;max-width:600px">
</div>

<div class="w3-panel w3-black">
<h3>.:: Settings Page</h3>
</div>
<div class="w3-row-padding w3-padding">
<div id="base" class="w3-col s3 m3 s12" style="display: none;">
<div class="w3-card-4 w3-margin-bottom">
<header class="w3-container w3-blue-gray">
<h3 id="title">{{TITLE}}</h3>
</header>
<div class="w3-container">
<p style="min-height: 45px;" id="description">{{DESCRIPTION}}</p>
<hr>
<div class="w3-row w3-section">
<div class="w3-col" style="width:50px"><i id="icon" class="w3-xxlarge w3-text-dark-grey fa"></i></div>
<div class="w3-rest" id="formInput">
{{FORM_INPUT}}
</div>
</div>
</div>
<button id="cardButton" class="w3-button w3-block w3-light-blue">Save</button>
</div>
</div>
</div>
<script>
function createCards(settings) {
console.log(settings);
const cards = [
{
title:"Display Bright",
description:"0 = dark (display off) / 255 = super bright | Value: <strong><output id='rangevalue'>" + settings.displayBright + "</output></strong>",
formInput:"<input class='w3-input w3-border' type='range' min='0' max='255' value='" + settings.displayBright + "' class='slider' id='bright' oninput='rangevalue.value=value'>",
icon:"fa-adjust",
save:"updatePreference('displayBright', bright.value)",
property:"displayBright"
},
{
title:"Use 24h format?",
description:"Changes the hour format to show 20:00 instead of 8:00PM",
formInput:"<input class='w3-check' type='checkbox' id='use24h' " + (settings.use24hFormat == 1 ? "checked" : "") + "><label for='use24h'> Yep</label>",
icon:"fa-calendar",
save:"updatePreference('use24hFormat', Number(use24h.checked))",
property:"use24hFormat"
},
{
title:"Swap Blue/Green pins?",
description:"Swap Blue and Green pins because the panel is RBG instead of RGB",
formInput:"<input class='w3-check' type='checkbox' id='swapBG' " + (settings.swapBlueGreen == 1 ? "checked" : "") + "><label for='swapBG'> Yep</label>",
icon:"fa-random",
save:"updatePreference('swapBlueGreen', Number(swapBG.checked))",
property:"swapBlueGreen"
},
{
title:"Timezone",
description:"Consult your TZ identifier <a href='https://en.wikipedia.org/wiki/List_of_tz_database_time_zones'>here.</a> Examples: America/Sao_Paulo, Europe/Lisbon",
formInput:"<input id='tz' class='w3-input' name='tz' type='text' placeholder='Timezone' value='"+ settings.timeZone +"''>",
icon:"fa-clock-o",
save:"updatePreference('timeZone', tz.value)",
property:"timeZone"
}
];

var base = document.querySelector('#base');
cards.forEach(c => {

var clone = base.cloneNode(true);
clone.id = c.property;
clone.removeAttribute("style");

Array.prototype.slice.call(clone.getElementsByTagName('*')).forEach(e => {
e.id = e.id + "-" + c.property;
});

base.before(clone);
document.getElementById("title-" + c.property).innerHTML = c.title
document.getElementById("description-" + c.property).innerHTML = c.description
document.getElementById("formInput-" + c.property).innerHTML = c.formInput
document.getElementById("icon-" + c.property).classList.add(c.icon);
document.getElementById("cardButton-" + c.property).setAttribute("onclick", c.save);
})
}

function updatePreference(key, value) {
const xhr = new XMLHttpRequest();
xhr.open('POST', '/set?' + key + '=' + value);
xhr.send();
}

function begin() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
createCards(JSON.parse(this.responseText));
}
};
xmlhttp.open("GET", "/get", true);
xmlhttp.send();
}

//Local
//createCards({"displayBright":30,"swapBlueGreen":1,"use24hFormat":0,"timeZone":"Europe/Lisbon","wifiSsid":"test"});

//Embedded
begin();

</script>
</body>
</html>
)"""";
Original file line number Diff line number Diff line change
Expand Up @@ -549,8 +549,14 @@ const uint16_t epd_bitmap_qrcode[] PROGMEM = {
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff};

struct IOManager
struct StatusController
{

static StatusController* getInstance() {
static StatusController base;
return &base;
}

void clockwiseLogo()
{
Locator::getDisplay()->drawRGBBitmap(1, 1, epd_bitmap_clockwise64, 63, 21);
Expand Down Expand Up @@ -588,7 +594,7 @@ struct IOManager
Locator::getDisplay()->print(buf);
}

static void blink_led(int d, int times)
void blink_led(int d, int times)
{
for (int j = 0; j < times; j++)
{
Expand Down
Loading

0 comments on commit fbaaddc

Please sign in to comment.