Updated build system and added page to select host mode or auto detection

This commit is contained in:
Philip Smart
2026-05-27 22:41:00 +01:00
parent 8a90b8fec8
commit 75a7bb2518
25 changed files with 864 additions and 228 deletions

View File

@@ -9,3 +9,17 @@ project(main)
# Create the default filesystem with files loaded from the webserver directory.
littlefs_create_partition_image(filesys webfs)
idf_build_get_property(build_dir BUILD_DIR)
idf_build_get_property(elf_name EXECUTABLE_NAME GENERATOR_EXPRESSION)
# Post-build: version management, filepack creation, and release packaging.
add_custom_command (OUTPUT ${CMAKE_SOURCE_DIR}/updated
DEPENDS "${build_dir}/.bin_timestamp"
COMMAND bash -c "${CMAKE_SOURCE_DIR}/backup_version.sh"
COMMAND bash -c "${CMAKE_SOURCE_DIR}/update_version.sh"
COMMAND bash -c "${CMAKE_SOURCE_DIR}/make_filepack.sh"
COMMAND bash -c "${CMAKE_SOURCE_DIR}/make_release.sh"
)
add_custom_target(version ALL DEPENDS ${CMAKE_SOURCE_DIR}/updated)

19
backup_version.sh Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/bash
PRJDIR=$(dirname ${PWD})
ROOTDIR=/srv/dvlp/Projects
OLDVERSION=$(cat ${PRJDIR}/version.txt)
NEWVERSION=$(perl -e "$(echo "print $(cat ${PRJDIR}/version.txt)+0.01")")
echo "Backing up version (${OLDVERSION}) to ${PRJDIR}/versions/sharpkey_${OLDVERSION}_$(date +'%y%m%d%H%M').tar.gz..."
cd ${PRJDIR}
tar -czf ${PRJDIR}/versions/sharpkey_${OLDVERSION}_$(date +'%y%m%d%H%M').tar.gz --exclude=build \
backup_version.sh build_webfs.sh CMakeLists.txt license.txt \
main make_filepack.sh make_release.sh \
sharpkey_partition_table.csv sharpkey_version.txt \
sdkconfig update_version.sh version.txt webfs
if [[ $? != 0 ]]; then
echo "Backup failure!"
cd ${PRJDIR}
exit 1
fi

View File

@@ -4,21 +4,22 @@ SRCDIR=`pwd`/webserver
WEBFSDIR=`pwd`/webfs
echo "Building into:$WEBFSDIR from $SRCDIR..."
mkdir -p ${WEBFSDIR}/css
mkdir -p ${WEBFSDIR}/js
mkdir -p ${WEBFSDIR}/font-awesome
mkdir -p ${WEBFSDIR}/font-awesome/css
mkdir -p ${WEBFSDIR}/font-awesome/fonts
mkdir -p ${WEBFSDIR}/images
mkdir -p webfs/css
mkdir -p webfs/js
mkdir -p webfs/font-awesome
mkdir -p webfs/font-awesome/css
mkdir -p webfs/font-awesome/fonts
mkdir -p webfs/images
(cd ${SRCDIR}/;
cp favicon.ico ${WEBFSDIR}/
cp version.txt ${WEBFSDIR}/
cp webfs_version.txt ${WEBFSDIR}/
cp index.html ${WEBFSDIR}/
cp keymap.html ${WEBFSDIR}/keymap.html
cp mouse.html ${WEBFSDIR}/mouse.html
cp ota.html ${WEBFSDIR}/ota.html
cp wifimanager.html ${WEBFSDIR}/wifimanager.html
cp hostconfig.html ${WEBFSDIR}/hostconfig.html
(cd ${SRCDIR}/css;

1
filepack_version.txt Normal file
View File

@@ -0,0 +1 @@
1.04

View File

@@ -886,30 +886,48 @@ void HID::init(const char *className, enum HID_DEVICE_TYPES deviceType)
ps2Keyboard = new PS2KeyAdvanced();
ps2Keyboard->begin(CONFIG_PS2_HW_DATAPIN, CONFIG_PS2_HW_CLKPIN);
// If no PS/2 keyboard detected then default to Bluetooth.
if(checkPS2Keyboard() == false)
// PS/2 keyboards need 300-750ms after power-on to complete their BAT (Basic Assurance Test).
// Retry detection several times to avoid a race condition where the ESP32 boots faster than
// the keyboard and incorrectly falls back to Bluetooth.
{
// Remove the PS/2 keyboard object, free up memory and disable the interrupts.
ESP_LOGW(INITTAG, "PS2 keyboard not available.");
delete ps2Keyboard;
hidCtrl.hidDevice = HID_DEVICE_BT_KEYBOARD;
// Instantiate Bluetooth HID object.
ESP_LOGW(INITTAG, "Initialise Bluetooth keyboard.");
btHID = new BTHID();
btHID->setup(btPairingHandler);
sw->setBTPairingEventCallback(&HID::btStartPairing, this);
bool ps2Found = false;
for(int retry = 0; retry < 10 && !ps2Found; retry++)
{
if(checkPS2Keyboard() == true)
{
ps2Found = true;
} else
{
ESP_LOGW(INITTAG, "PS2 keyboard not detected, retry %d/10...", retry + 1);
vTaskDelay(100);
}
}
// Setup a mouse callback as it is possible to receive mouse data when the primary input method is a keyboard. This data can be used by a registered
// mouse interface to provide dual services to a host.
btHID->setMouseDataCallback(&HID::mouseReceiveData, this);
// If no PS/2 keyboard detected after retries then default to Bluetooth.
if(!ps2Found)
{
// Remove the PS/2 keyboard object, free up memory and disable the interrupts.
ESP_LOGW(INITTAG, "PS2 keyboard not available.");
delete ps2Keyboard;
hidCtrl.hidDevice = HID_DEVICE_BT_KEYBOARD;
hidCtrl.deviceType = HID_DEVICE_TYPE_BLUETOOTH;
hidCtrl.hidDevice = HID_DEVICE_BLUETOOTH;
} else
{
hidCtrl.deviceType = HID_DEVICE_TYPE_KEYBOARD;
hidCtrl.hidDevice = HID_DEVICE_PS2_KEYBOARD;
// Instantiate Bluetooth HID object.
ESP_LOGW(INITTAG, "Initialise Bluetooth keyboard.");
btHID = new BTHID();
btHID->setup(btPairingHandler);
sw->setBTPairingEventCallback(&HID::btStartPairing, this);
// Setup a mouse callback as it is possible to receive mouse data when the primary input method is a keyboard. This data can be used by a registered
// mouse interface to provide dual services to a host.
btHID->setMouseDataCallback(&HID::mouseReceiveData, this);
hidCtrl.deviceType = HID_DEVICE_TYPE_BLUETOOTH;
hidCtrl.hidDevice = HID_DEVICE_BLUETOOTH;
} else
{
hidCtrl.deviceType = HID_DEVICE_TYPE_KEYBOARD;
hidCtrl.hidDevice = HID_DEVICE_PS2_KEYBOARD;
}
}
break;
}

View File

@@ -128,6 +128,9 @@ struct SharpKeyConfig {
struct {
uint8_t bootMode; // Flag to indicate the mode SharpKey should boot into.
// 0 = Interface, 1 = WiFi (configured), 2 = WiFi (default).
uint32_t hostMode; // Host machine override. 0 = Auto (detect from hardware),
// 2500 = MZ-2500, 2800 = MZ-2800, 1 = X1, 68000 = X68000,
// 5600 = MZ-5600/6500, 9801 = PC-9801, 2 = Mouse.
} params;
} sharpKeyConfig;
@@ -485,8 +488,15 @@ void startWiFi(NVS &nvs, LED *led, bool defaultMode, uint32_t ifMode)
// Method to determine which host the SharpKey is connected to. This is done by examining the host I/O for tell tale signals
// or inputs wired in a fixed combination.
//
uint32_t getHostType(bool eFuseInvalid, t_EFUSE sharpkeyEfuses)
uint32_t getHostType(bool eFuseInvalid, t_EFUSE sharpkeyEfuses, uint32_t hostMode)
{
// If a host mode has been set via the web interface (stored in NVS), use it directly.
if(hostMode != 0)
{
ESP_LOGW(MAINTAG, "Host mode set via config: %d", hostMode);
return(hostMode);
}
// Locals.
//
uint32_t RTSNI_MASK = (1 << (CONFIG_HOST_RTSNI - 32));
@@ -905,6 +915,7 @@ void setup(NVS &nvs)
{
ESP_LOGW(SETUPTAG, "SharpKey configuration set to default, no valid config found in NVS.");
sharpKeyConfig.params.bootMode = 0;
sharpKeyConfig.params.hostMode = 0;
// Persist the data for next time.
if(nvs.persistData(SHARPKEY_NAME, &sharpKeyConfig, sizeof(struct SharpKeyConfig)) == false)
@@ -919,7 +930,7 @@ void setup(NVS &nvs)
}
// Get the host type SharpKey is connected with.
ifMode = getHostType(eFuseInvalid, sharpkeyEfuses);
ifMode = getHostType(eFuseInvalid, sharpkeyEfuses, sharpKeyConfig.params.hostMode);
// If bootMode is for Wifi, start it. This has to be seperate due to a conflict with Bluetooth and WiFi which shares the same antenna.
// Code is written to allow co-existence but it doesnt work so well in this project.

View File

@@ -57,6 +57,8 @@
#include <filesystem>
#include <vector>
#include <algorithm>
#include <memory>
#include <unistd.h>
#include <map>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
@@ -78,6 +80,16 @@
#include "esp_littlefs.h"
#include "WiFi.h"
// External reference to the SharpKey global configuration stored in NVS.
struct SharpKeyConfig {
struct {
uint8_t bootMode;
uint32_t hostMode;
} params;
};
extern struct SharpKeyConfig sharpKeyConfig;
#define SHARPKEY_NAME "SharpKey"
// FreeRTOS event group to signal when we are connected
static EventGroupHandle_t s_wifi_event_group;
@@ -558,6 +570,16 @@ esp_err_t WiFi::sendMouseRadioChoice(httpd_req_t *req, const char *option)
//
esp_err_t WiFi::expandVarsAndSend(httpd_req_t *req, std::string str)
{
// Fast path: template variables are always prefixed "%SK_".
// A line with no "%SK_" cannot contain any template variable, so skip the
// expensive pairs build (which includes flash partition enumeration via
// esp_partition_find / esp_ota_get_partition_description) and send directly.
if(str.find("%SK_") == std::string::npos)
{
str += "\n";
return httpd_resp_send_chunk(req, str.c_str(), str.length());
}
// Locals.
//
bool largeMacroDetected = false;
@@ -590,6 +612,14 @@ esp_err_t WiFi::expandVarsAndSend(httpd_req_t *req, std::string str)
keyValue.name = "%SK_CURRENTGW%"; keyValue.value = (wifiCtrl.run.wifiMode == WIFI_CONFIG_AP ? wifiCtrl.ap.gateway : wifiCtrl.client.gateway); pairs.push_back(keyValue);
keyValue.name = "%SK_CURRENTIF%"; keyValue.value = keyIf->ifName().append(" "); pairs.push_back(keyValue);
keyValue.name = "%SK_SECONDIF%"; keyValue.value = (mouseIf != NULL ? mouseIf->ifName().append(" ") : ""); pairs.push_back(keyValue);
keyValue.name = "%SK_HOSTMODE_AUTO%"; keyValue.value = (sharpKeyConfig.params.hostMode == 0 ? "checked" : ""); pairs.push_back(keyValue);
keyValue.name = "%SK_HOSTMODE_MZ2500%"; keyValue.value = (sharpKeyConfig.params.hostMode == 2500 ? "checked" : ""); pairs.push_back(keyValue);
keyValue.name = "%SK_HOSTMODE_MZ2800%"; keyValue.value = (sharpKeyConfig.params.hostMode == 2800 ? "checked" : ""); pairs.push_back(keyValue);
keyValue.name = "%SK_HOSTMODE_X1%"; keyValue.value = (sharpKeyConfig.params.hostMode == 1 ? "checked" : ""); pairs.push_back(keyValue);
keyValue.name = "%SK_HOSTMODE_X68000%"; keyValue.value = (sharpKeyConfig.params.hostMode == 68000 ? "checked" : ""); pairs.push_back(keyValue);
keyValue.name = "%SK_HOSTMODE_MZ5600%"; keyValue.value = (sharpKeyConfig.params.hostMode == 5600 ? "checked" : ""); pairs.push_back(keyValue);
keyValue.name = "%SK_HOSTMODE_PC9801%"; keyValue.value = (sharpKeyConfig.params.hostMode == 9801 ? "checked" : ""); pairs.push_back(keyValue);
keyValue.name = "%SK_HOSTMODE_MOUSE%"; keyValue.value = (sharpKeyConfig.params.hostMode == 2 ? "checked" : ""); pairs.push_back(keyValue);
keyValue.name = "%SK_REBOOTBUTTON%"; keyValue.value = (wifiCtrl.run.rebootButton == true ? "block" : "none"); pairs.push_back(keyValue);
keyValue.name = "%SK_ERRMSG%"; keyValue.value = wifiCtrl.run.errorMsg; pairs.push_back(keyValue);
keyValue.name = "%SK_PRODNAME%"; keyValue.value = (wifiCtrl.run.versionList->elements > 1 ? wifiCtrl.run.versionList->item[0]->object : "Unknown"); pairs.push_back(keyValue);
@@ -778,39 +808,54 @@ esp_err_t WiFi::expandAndSendFile(httpd_req_t *req, const char *basePath, std::s
std::string line;
std::ifstream inFile;
esp_err_t result = ESP_OK;
// Build the FQFN for reading.
std::string fqfn = basePath; fqfn += "/"; fqfn += fileName;
// Ensure the content type is set correctly.
setContentTypeFromFileType(req, fileName);
// Read the file into an input stream, read a line, expand it and r and then store into a string buffer to be returned to caller.
// Read the entire file in one filesystem operation — dramatically faster than
// getline() per line (each getline triggers a separate fread, ~2-3ms per call;
// a 300-line file would cost 600-900ms in filesystem I/O alone).
inFile.open(fqfn.c_str());
while(result == ESP_OK && std::getline(inFile, line))
if(!inFile.is_open())
{
// Call method to output line after expanding, in-situ, any macros into variable values.
if((result=expandVarsAndSend(req, line)) != ESP_OK)
{
// Abort sending file.
httpd_resp_sendstr_chunk(req, NULL);
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "File does not exist");
return(ESP_FAIL);
}
// Respond with 500 Internal Server Error.
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
break;
std::string contents((std::istreambuf_iterator<char>(inFile)), std::istreambuf_iterator<char>());
inFile.close();
if(contents.find("%SK_") == std::string::npos)
{
// No template variables — send entire file as a single HTTP chunk.
result = httpd_resp_send_chunk(req, contents.c_str(), contents.length());
if(result == ESP_OK)
result = httpd_resp_send_chunk(req, NULL, 0);
}
else
{
// Template variables present — process line by line from the in-memory
// string (no filesystem I/O per line).
std::istringstream stream(contents);
while(result == ESP_OK && std::getline(stream, line))
{
if((result = expandVarsAndSend(req, line)) != ESP_OK)
{
httpd_resp_sendstr_chunk(req, NULL);
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
break;
}
}
if(result == ESP_OK)
{
result = httpd_resp_send_chunk(req, NULL, 0);
}
}
// Successful, end the response with a NULL string.
if(result == ESP_OK)
{
result = httpd_resp_send_chunk(req, NULL, 0);
}
// Tidy up for exit.
inFile.close();
// Return result code.
return(result);
}
@@ -997,65 +1042,47 @@ esp_err_t WiFi::defaultFileHandler(httpd_req_t *req)
struct stat file_stat;
char *buf;
int bufLen;
esp_err_t result = ESP_OK;
std::string gzipFile = "";
std::string disposition = "";
// Per-request local variables — the shared wifiCtrl.session struct must NOT be
// used here because multiple requests are handled concurrently by esp_http_server.
// Using the shared session caused a race condition where parallel requests
// overwrote each other's filePath/fileName/gzip fields, leaving some responses
// stuck forever in "pending" state.
std::string localFilePath;
std::string localFileName;
bool localGzip = false;
// Retrieve pointer to object in order to access data.
WiFi* pThis = (WiFi*)req->user_ctx;
// Get required Header values for processing.
bufLen = httpd_req_get_hdr_value_len(req, "Host") + 1;
if(bufLen > 1)
{
buf = new char[bufLen];
// Copy null terminated value string into buffer
if(httpd_req_get_hdr_value_str(req, "Host", buf, bufLen) == ESP_OK)
{
// Assign to control structure for later use.
pThis->wifiCtrl.session.host = buf;
}
// Free up memory to complete.
delete buf;
}
// Close connection after each file response. The server is single-threaded —
// while sending a large file (e.g. 98KB font), all other requests are blocked.
// With keep-alive, the browser queues subsequent requests on existing connections
// that may be stalled behind a large transfer, leaving them "pending" forever.
// With Connection:close, the browser opens fresh connections for each resource.
httpd_resp_set_hdr(req, "Connection", "close");
// Get encoding methods.
bufLen = httpd_req_get_hdr_value_len(req, "Accept-Encoding") + 1;
if(bufLen > 1)
{
buf = new char[bufLen];
// Set flags to indicate allowed encoding methods.
if(httpd_req_get_hdr_value_str(req, "Accept-Encoding", buf, bufLen) == ESP_OK)
{
pThis->wifiCtrl.session.gzip = (strstr(buf, "gzip") != NULL ? true : false);
pThis->wifiCtrl.session.deflate = (strstr(buf, "deflate") != NULL ? true : false);
localGzip = (strstr(buf, "gzip") != NULL ? true : false);
}
// Free up memory to complete.
delete buf;
}
// Get and store the URL query string.
bufLen = httpd_req_get_url_query_len(req) + 1;
if (bufLen > 1)
// Look for a filename in the URI and construct the file path returning both.
if(pThis->getPathFromURI(localFilePath, localFileName, pThis->wifiCtrl.run.basePath, req->uri) == ESP_FAIL)
{
buf = new char[bufLen];
if (httpd_req_get_url_query_str(req, buf, bufLen) == ESP_OK)
{
pThis->wifiCtrl.session.queryStr = buf;
ESP_LOGI(WIFITAG, "Found URL query => %s", pThis->wifiCtrl.session.queryStr.c_str());
}
// Free up memory to complete.
delete buf;
}
// Look for a filename in the URI and construct the file path returning both. If filename isnt valid, respond with 500 Internal Server Error and exit.
if(pThis->getPathFromURI(pThis->wifiCtrl.session.filePath, pThis->wifiCtrl.session.fileName, pThis->wifiCtrl.run.basePath, req->uri) == ESP_FAIL)
{
// Check for root URL.
if(strlen(req->uri) == 1 && req->uri[0] == '/')
{
pThis->wifiCtrl.session.fileName = "/";
localFileName = "/";
} else
{
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename invalid");
@@ -1063,18 +1090,17 @@ esp_err_t WiFi::defaultFileHandler(httpd_req_t *req)
}
}
// See if the provided name matches a static handler, such as root, if so execute response directly.
if(pThis->wifiCtrl.session.fileName.compare("/") == 0 || pThis->wifiCtrl.session.fileName.compare("index.html") == 0 || pThis->wifiCtrl.session.fileName.compare("index.htm") == 0)
// See if the provided name matches a static handler, such as root.
if(localFileName.compare("/") == 0 || localFileName.compare("index.html") == 0 || localFileName.compare("index.htm") == 0)
{
// Open the given file, read and expand macros and send to open connection.
return pThis->expandAndSendFile(req, pThis->wifiCtrl.run.basePath, "index.html");
}
// Is this a macro to specify keymap file? Keymap file name changes depending on runmode so adjust filename accordingly.
if(pThis->wifiCtrl.session.fileName.compare("keymap") == 0)
// Is this a macro to specify keymap file?
if(localFileName.compare("keymap") == 0)
{
pThis->wifiCtrl.session.fileName = std::regex_replace(pThis->wifiCtrl.session.fileName, std::regex("keymap"), pThis->keyIf->getKeyMapFileName());
pThis->wifiCtrl.session.filePath = std::regex_replace(pThis->wifiCtrl.session.filePath, std::regex("keymap"), pThis->keyIf->getKeyMapFileName());
localFileName = std::regex_replace(localFileName, std::regex("keymap"), pThis->keyIf->getKeyMapFileName());
localFilePath = std::regex_replace(localFilePath, std::regex("keymap"), pThis->keyIf->getKeyMapFileName());
disposition = "attachment; filename=" + pThis->keyIf->getKeyMapFileName();
if(httpd_resp_set_hdr(req, "Content-Disposition", disposition.c_str()) != ESP_OK)
{
@@ -1084,23 +1110,17 @@ esp_err_t WiFi::defaultFileHandler(httpd_req_t *req)
}
// Get details of the file, throw error 404 - File Not Found on error.
if(stat(pThis->wifiCtrl.session.filePath.c_str(), &file_stat) == -1)
if(stat(localFilePath.c_str(), &file_stat) == -1)
{
// Prepare gzip version, size remains 0 if normal file is found.
if(pThis->wifiCtrl.session.gzip)
gzipFile = pThis->wifiCtrl.session.filePath + ".gz";
if(localGzip)
gzipFile = localFilePath + ".gz";
// Check to see if the file is compressed. Tag on .gz and retry, if success then set encoding content and carry on as normal.
//
if(pThis->wifiCtrl.session.gzip == true && stat(gzipFile.c_str(), &file_stat) == -1)
if(localGzip == true && stat(gzipFile.c_str(), &file_stat) == -1)
{
// Respond with 404 Not Found.
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "File does not exist");
return(ESP_FAIL);
}
// Set the content encoding to gzip to comply with specs.
// WARNING: Do not gzip html or library css files as they get parsed and expanded.
if(httpd_resp_set_hdr(req, "Content-Encoding", "gzip") != ESP_OK)
{
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Set content encoding to gzip failed");
@@ -1108,67 +1128,57 @@ esp_err_t WiFi::defaultFileHandler(httpd_req_t *req)
}
}
// If the file is HTML, JS or CSS then process externally as we need to subsitute embedded variables as required. Note the guard around static evaluation, ie. gzipFile.size. This is to cater for gzipped html, js or css as we cannot
// parse and expand, it is served 'as is'.
if((pThis->isFileExt(pThis->wifiCtrl.session.fileName, ".html") || (pThis->isFileExt(pThis->wifiCtrl.session.fileName, ".js") && !pThis->isFileExt(pThis->wifiCtrl.session.fileName, ".min.js")) || (pThis->isFileExt(pThis->wifiCtrl.session.fileName, ".css") && !pThis->isFileExt(pThis->wifiCtrl.session.fileName, ".min.css"))) && gzipFile.size() == 0)
// If the file is HTML, JS or CSS then process externally as we need to substitute embedded variables.
// Guard around gzipFile.size: gzipped html/js/css cannot be parsed, served 'as is'.
if((pThis->isFileExt(localFileName, ".html") || (pThis->isFileExt(localFileName, ".js") && !pThis->isFileExt(localFileName, ".min.js")) || (pThis->isFileExt(localFileName, ".css") && !pThis->isFileExt(localFileName, ".min.css"))) && gzipFile.size() == 0)
{
// Open the given file, read and expand macros and send to open connection.
pThis->expandAndSendFile(req, pThis->wifiCtrl.run.basePath, pThis->wifiCtrl.session.fileName);
result = pThis->expandAndSendFile(req, pThis->wifiCtrl.run.basePath, localFileName);
} else
{
// Try to open the file, we performed a stat so is does exist but perhaps a FAT corruption occurred?.
fd = fopen(gzipFile.size() > 0 ? gzipFile.c_str() : pThis->wifiCtrl.session.filePath.c_str(), "r");
fd = fopen(gzipFile.size() > 0 ? gzipFile.c_str() : localFilePath.c_str(), "r");
if(!fd)
{
// Respond with 500 Internal Server Error
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file");
return(ESP_FAIL);
}
ESP_LOGI(WIFITAG, "Sending %sfile : %s (%ld bytes)...", gzipFile.size() > 0 ? "gzip " : " ", pThis->wifiCtrl.session.fileName.c_str(), file_stat.st_size);
pThis->setContentTypeFromFileType(req, pThis->wifiCtrl.session.fileName);
ESP_LOGI(WIFITAG, "Sending %sfile : %s (%ld bytes)...", gzipFile.size() > 0 ? "gzip " : " ", localFileName.c_str(), file_stat.st_size);
pThis->setContentTypeFromFileType(req, localFileName);
// Use smart pointer to prevent leaks on error paths.
std::unique_ptr<char[]> chunk(new (std::nothrow) char[MAX_CHUNK_SIZE]);
if(chunk == nullptr)
{
ESP_LOGE(WIFITAG, "Memory exhausted in defaultFileHandler — closing socket");
fclose(fd);
int sockFd = httpd_req_to_sockfd(req);
if(sockFd != -1) close(sockFd);
return(ESP_FAIL);
}
// Allocate a buffer for chunking the file. The file could be binary, so unlike the HTML/CSS handler, strings cant be used
// thus we read chunks according to our buffer size and send accordingly.
char *chunk = new char[MAX_CHUNK_SIZE];
size_t chunksize;
do {
// Read file in chunks into the temporary buffer.
chunksize = fread(chunk, 1, MAX_CHUNK_SIZE, fd);
if (chunksize > 0)
chunksize = fread(chunk.get(), 1, MAX_CHUNK_SIZE, fd);
if(chunksize > 0)
{
// Send the buffer contents as HTTP response chunk.
if(httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK)
if(httpd_resp_send_chunk(req, chunk.get(), chunksize) != ESP_OK)
{
// Release memory and close files, error!!
fclose(fd);
delete chunk;
// Abort sending file.
httpd_resp_sendstr_chunk(req, NULL);
// Respond with 500 Internal Server Error.
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
return(ESP_FAIL);
}
result = ESP_FAIL;
httpd_resp_send_chunk(req, NULL, 0);
int sockFd = httpd_req_to_sockfd(req);
if(sockFd != -1) close(sockFd);
}
}
} while(chunksize != 0 && result == ESP_OK);
// Keep looping till the whole file is sent.
} while (chunksize != 0);
// Release memory to complete.
delete chunk;
// Close file after sending complete.
fclose(fd);
ESP_LOGI(WIFITAG, "File sending complete");
}
// Respond with an empty chunk to signal HTTP response completion.
httpd_resp_send_chunk(req, NULL, 0);
return ESP_OK;
if(result == ESP_OK)
httpd_resp_send_chunk(req, NULL, 0);
return result;
}
// Handler to send data sets. The handler is triggered on the /data URI and subpaths define the data to be sent.
@@ -2088,6 +2098,46 @@ esp_err_t WiFi::mouseDataPOSTHandler(httpd_req_t *req, std::vector<t_kvPair> pai
return(dataError == false ? ESP_OK : ESP_FAIL);
}
// /data/hostconfig POST handler. Process host machine selection and store in NVS.
//
esp_err_t WiFi::hostConfigDataPOSTHandler(httpd_req_t *req, std::vector<t_kvPair> pairs, std::string& resp)
{
// Locals.
//
bool dataError = false;
uint32_t newHostMode = 0;
resp = "";
for(auto pair : pairs)
{
if(pair.name.compare("hostmode") == 0)
{
newHostMode = (uint32_t)atoi(pair.value.c_str());
// Validate the value.
if(newHostMode != 0 && newHostMode != 2500 && newHostMode != 2800 && newHostMode != 1 &&
newHostMode != 68000 && newHostMode != 5600 && newHostMode != 9801 && newHostMode != 2)
{
resp.append("Invalid host mode value: " + pair.value);
dataError = true;
}
}
}
if(!dataError)
{
sharpKeyConfig.params.hostMode = newHostMode;
if(nvs->persistData(SHARPKEY_NAME, &sharpKeyConfig, sizeof(struct SharpKeyConfig)) == false ||
nvs->commitData() == false)
{
resp.append("Save config to NVS RAM failed, retry, if 2nd attempt fails, power cycle the interface.");
dataError = true;
}
}
return(dataError == false ? ESP_OK : ESP_FAIL);
}
// /data POST handler. Process the request and call service as required.
//
esp_err_t WiFi::defaultDataPOSTHandler(httpd_req_t *req)
@@ -2132,6 +2182,14 @@ esp_err_t WiFi::defaultDataPOSTHandler(httpd_req_t *req)
resp = "Data values accepted. Press 'Reboot' to restart interface with new values.";
}
}
if(uriStr.compare("hostconfig") == 0)
{
if((ret = pThis->hostConfigDataPOSTHandler(req, pairs, resp)) == ESP_OK)
{
pThis->wifiCtrl.run.rebootButton = true;
resp = "Host configuration saved. Press 'Reboot' to restart interface with new host selection.";
}
}
} else
{
resp = "<p>No values in POST, check browser!</p>";
@@ -2193,10 +2251,19 @@ bool WiFi::startWebserver(void)
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
// Tweak default settings.
config.stack_size = 10240;
// Pages request multiple files simultaneously (CSS, JS, fonts, images).
// The default 7 sockets causes LRU purge to kill active transfers, leaving
// resources stuck in "pending" state forever. 20 sockets accommodates
// parallel requests with spare slots. Aggressive recv_wait_timeout recycles
// idle keep-alive connections; send_wait_timeout prevents stale connections
// from blocking fresh ones.
config.stack_size = 16384;
config.uri_match_fn = httpd_uri_match_wildcard;
config.lru_purge_enable = true;
config.max_uri_handlers = 12;
config.max_uri_handlers = 14;
config.max_open_sockets = 7;
config.recv_wait_timeout = 1;
config.send_wait_timeout = 15;
// Setup the required paths and descriptors then register them with the server.
const httpd_uri_t dataPOST = {

View File

@@ -262,6 +262,7 @@
esp_err_t wifiDataPOSTHandler(httpd_req_t *req, std::vector<t_kvPair> pairs, std::string& resp);
esp_err_t mouseDataPOSTHandler(httpd_req_t *req, std::vector<t_kvPair> pairs, std::string& resp);
esp_err_t hostConfigDataPOSTHandler(httpd_req_t *req, std::vector<t_kvPair> pairs, std::string& resp);
static esp_err_t defaultDataPOSTHandler(httpd_req_t *req);
static esp_err_t defaultDataGETHandler(httpd_req_t *req);
IRAM_ATTR static esp_err_t otaFirmwareUpdatePOSTHandler(httpd_req_t *req);

98
make_filepack.sh Executable file
View File

@@ -0,0 +1,98 @@
#!/bin/bash
# PRJDIR hardcoded as CMake may not be in correct directory.
ROOTDIR=/srv/dvlp/Projects
PRJDIR=${PWD}
if [[ "${PWD}" != "${ROOTDIR}/SharpKey" ]]; then
PRJDIR=$(dirname ${PWD})
fi
#if [[ "${PRJDIR}" != "${ROOTDIR}/SharpKey" ]] && [[ "${PRJDIR}" != "/project" ]]; then
# echo "Wrong run directory (${PRJDIR})! Should be <project>/SharpKey"
# exit -1
#fi
SRCDIR=${PRJDIR}/webserver
WEBFSDIR=${PRJDIR}/webfs
OLDWEBFSVERSION=$(cat ${SRCDIR}/webfs_version.txt)
NEWWEBFSVERSION=$(perl -e "$(echo "print $(cat ${SRCDIR}/webfs_version.txt)+0.01")")
OLDFILEPACKVERSION=$(cat ${PRJDIR}/filepack_version.txt)
NEWFILEPACKVERSION=$(perl -e "$(echo "print $(cat ${PRJDIR}/filepack_version.txt)+0.01")")
ISNEWER=$(find ${SRCDIR} -newer ${SRCDIR}/webfs_version.txt)
if [[ ${ISNEWER} != "" ]]; then
echo "Building into:$WEBFSDIR from $SRCDIR..."
mkdir -p ${WEBFSDIR}/
rm -fr ${WEBFSDIR}/*
mkdir -p ${WEBFSDIR}/css
mkdir -p ${WEBFSDIR}/js
mkdir -p ${WEBFSDIR}/font-awesome
mkdir -p ${WEBFSDIR}/font-awesome/css
mkdir -p ${WEBFSDIR}/font-awesome/fonts
mkdir -p ${WEBFSDIR}/images
echo ${NEWWEBFSVERSION} > ${SRCDIR}/webfs_version.txt
echo "Old WebFS Version:${OLDWEBFSVERSION} -> New Version:${NEWWEBFSVERSION}"
(cd ${SRCDIR}/;
cp favicon.ico ${WEBFSDIR}/
cp webfs_version.txt ${WEBFSDIR}/
cp index.html ${WEBFSDIR}/
cp keymap.html ${WEBFSDIR}/keymap.html
cp mouse.html ${WEBFSDIR}/mouse.html
cp ota.html ${WEBFSDIR}/ota.html
cp wifimanager.html ${WEBFSDIR}/wifimanager.html
cp hostconfig.html ${WEBFSDIR}/hostconfig.html
(cd ${SRCDIR}/css;
cp bootstrap.min.css.gz ${WEBFSDIR}/css/
gzip -c jquery.edittable.min.css > ${WEBFSDIR}/css/jquery.edittable.min.css.gz
gzip -c sb-admin.css > ${WEBFSDIR}/css/sb-admin.css.gz
gzip -c sharpkey.css > ${WEBFSDIR}/css/sharpkey.css.gz
gzip -c style.css > ${WEBFSDIR}/css/style.css.gz
gzip -c styles.css > ${WEBFSDIR}/css/styles.css.gz
)
(cd ${SRCDIR}/font-awesome
)
(cd ${SRCDIR}/font-awesome/css
gzip -c font-awesome.css > ${WEBFSDIR}/font-awesome/css/font-awesome.min.css.gz
)
(cd ${SRCDIR}/font-awesome/fonts
gzip -c fontawesome-webfont.woff > ${WEBFSDIR}/font-awesome/fonts/fontawesome-webfont.woff.gz
)
(cd ${SRCDIR}/images;
)
(cd ${SRCDIR}/js;
cp 140medley.min.js ${WEBFSDIR}/js/
cp bootstrap.min.js.gz ${WEBFSDIR}/js/
gzip -c index.js > ${WEBFSDIR}/js/index.js.gz
gzip -c jquery.edittable.js > ${WEBFSDIR}/js/jquery.edittable.js.gz
gzip -c jquery.edittable.min.js > ${WEBFSDIR}/js/jquery.edittable.min.j.gz
cp jquery.min.js.gz ${WEBFSDIR}/js/
gzip -c keymap.js > ${WEBFSDIR}/js/keymap.js.gz
gzip -c mouse.js > ${WEBFSDIR}/js/mouse.js.gz
gzip -c ota.js > ${WEBFSDIR}/js/ota.js.gz
gzip -c wifimanager.js > ${WEBFSDIR}/js/wifimanager.js.gz
)
)
echo ${NEWFILEPACKVERSION} > ${PRJDIR}/filepack_version.txt
echo "Old Filepack Version:${OLDFILEPACKVERSION} -> New Version:${NEWFILEPACKVERSION}"
echo "Building FilePack v${NEWFILEPACKVERSION}..."
cd ${PRJDIR}
rm -f filepack*tar
mv filepack*gz archive/ 2>/dev/null
tar -cvf release/filepack_sharpkey_${NEWFILEPACKVERSION}.tar sharpkey_version.txt filepack_version.txt webfs/
gzip release/filepack_sharpkey_${NEWFILEPACKVERSION}.tar
else
echo "No FilePack change, current version:${OLDFILEPACKVERSION}"
fi

20
make_release.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/bin/bash
ROOTDIR=/dvlp/Projects
PRJDIR=${PWD}
if [[ "${PWD}" = "/srv${ROOTDIR}/SharpKey" ]]; then
ROOTDIR=/srv/dvlp/Projects
fi
if [[ "${PWD}" != "${ROOTDIR}/SharpKey" ]]; then
PRJDIR=$(dirname ${PWD})
fi
if [[ "${PRJDIR}" != "${ROOTDIR}/SharpKey" ]] && [[ "${PRJDIR}" != "/project" ]]; then
echo "Wrong run directory (${PRJDIR})! Should be <project>/SharpKey"
exit -1
fi
RELEASEDIR=${PRJDIR}/release
VERSION=$(perl -e "$(echo "print $(cat ${PRJDIR}/version.txt)-0.00")")
mkdir -p ${RELEASEDIR}
cp ${PRJDIR}/build/main.bin ${RELEASEDIR}/sharpkey_fw_v${VERSION}.bin
#cp ${PRJDIR}/filepack*gz ${RELEASEDIR}/

View File

@@ -131,8 +131,8 @@ CONFIG_PARTITION_TABLE_MD5=y
#
# SharpKey Configuration
#
CONFIG_SHARPKEY=y
# CONFIG_MZ25KEY_MZ2500 is not set
# CONFIG_SHARPKEY is not set
CONFIG_MZ25KEY_MZ2500=y
# CONFIG_MZ25KEY_MZ2800 is not set
CONFIG_DISABLE_FEATURE_SECURITY=y
# CONFIG_ENABLE_FEATURE_SECURITY is not set

1
sharpkey_version.txt Normal file
View File

@@ -0,0 +1 @@
1.05

16
update_version.sh Executable file
View File

@@ -0,0 +1,16 @@
#!/bin/bash
PRJDIR=$(dirname ${PWD})
OLDVERSION=$(cat ${PRJDIR}/version.txt)
NEWVERSION=$(perl -e "$(echo "print $(cat ${PRJDIR}/version.txt)+0.01")")
# NB: SharpKey project version is updated manually in the file ${PRJDIR}/sharpkey_version.txt
ISNEWER=$(find ${PRJDIR}/main -newer ${PRJDIR}/version.txt)
if [[ ${ISNEWER} != "" ]]; then
echo ${NEWVERSION} > ${PRJDIR}/version.txt
echo ""
echo "Version:${OLDVERSION} -> Next Version:${NEWVERSION}"
else
echo ""
echo "Build Version:${OLDVERSION}"
fi

View File

@@ -1 +1 @@
1.05
1.07

View File

@@ -1,8 +1,8 @@
/*
/*
Author: Start Bootstrap - http://startbootstrap.com
'SB Admin' HTML Template by Start Bootstrap
All Start Bootstrap themes are licensed under Apache 2.0.
All Start Bootstrap themes are licensed under Apache 2.0.
For more info and more free Bootstrap 3 HTML themes, visit http://startbootstrap.com!
*/
@@ -12,6 +12,23 @@ For more info and more free Bootstrap 3 HTML themes, visit http://startbootstrap
body {
margin-top: 50px;
background:
/* Subtle diagonal pinstripe pattern */
repeating-linear-gradient(
135deg,
transparent,
transparent 10px,
rgba(0,0,0,0.015) 10px,
rgba(0,0,0,0.015) 11px
),
/* Soft radial vignette - lighter centre, slightly darker edges */
radial-gradient(
ellipse at center,
#f5f6f8 0%,
#e8eaef 60%,
#dcdfe6 100%
);
background-attachment: fixed;
}
#wrapper {
@@ -23,6 +40,12 @@ body {
padding: 5px 15px;
}
/* Ensure all panel content can scroll horizontally on small screens */
.panel-body {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
/* Nav Messages */
.messages-dropdown .dropdown-menu .message-preview .avatar,
@@ -86,21 +109,40 @@ table.tablesorter thead tr th:hover {
/* Edit Below to Customize Widths > 768px */
@media (min-width:768px) {
/* Centered layout: sidebar(225) + content(1210) = 1435px total */
/* Navbar: dark background only within the centred area, page background outside */
.navbar-inverse {
background-color: transparent;
border: none;
}
.nav-centered {
max-width: 1435px;
margin: 0 auto;
position: relative;
background-color: #222222;
}
/* Wrappers */
#wrapper {
padding-left: 225px;
max-width: 1435px;
margin: 0 auto;
}
#page-wrapper {
padding: 15px 25px;
max-width: 1210px;
background: #fff;
min-height: calc(100vh - 50px);
}
/* Side Nav */
/* Side Nav - fixed but tracks the centred layout */
.side-nav {
margin-left: -225px;
left: 225px;
margin-left: 0;
left: max(0px, calc(50% - 717.5px));
width: 225px;
position: fixed;
top: 50px;

View File

@@ -262,5 +262,80 @@ input[type=radio] .radio-mouse,
input.radio .radio-mouse {
float: left;
clear: none;
margin: 2px 0 0 2px;
margin: 2px 0 0 2px;
}
/* Sidebar navigation styling */
.side-nav > li > a {
margin-left: 20px;
padding-left: 15px;
}
.side-nav .collapse {
padding-left: 0;
list-style: none;
}
.side-nav .collapse li > a {
margin-left: 40px;
font-size: 0.95em;
}
.side-nav .collapse .collapse li > a {
padding-left: 55px;
font-size: 0.92em;
}
.side-nav .collapse a > i {
margin-right: 8px;
width: 18px;
text-align: center;
}
.side-nav li > a.dropdown-toggle {
font-weight: 500;
}
.side-nav > li > a:hover,
.side-nav > li > a:focus,
.side-nav .collapse li > a:hover,
.side-nav .collapse li > a:focus {
background-color: rgba(255, 200, 50, 0.15) !important;
color: #ffd700 !important;
}
.side-nav li.active > a {
background-color: #262c32;
color: white !important;
}
.side-nav a.active {
background-color: #322f11;
color: white !important;
font-weight: bold;
}
.side-nav .dropdown-toggle {
padding-right: 25px;
position: relative;
}
.side-nav .dropdown-toggle .caret {
position: absolute;
right: 40px;
top: 50%;
margin-top: -2px;
float: none;
}
.side-nav a:focus:not(:focus-visible),
.side-nav .dropdown-toggle:focus:not(:focus-visible) {
outline: none !important;
box-shadow: none !important;
}
.side-nav a:focus-visible,
.side-nav .dropdown-toggle:focus-visible {
outline: 2px solid #1abc9c;
outline-offset: 1px;
}

168
webserver/hostconfig.html Normal file
View File

@@ -0,0 +1,168 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<title>Dashboard - SharpKey Admin</title>
<!-- Bootstrap core CSS -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- Add custom CSS here -->
<link href="css/sb-admin.css" rel="stylesheet">
<link href="css/sharpkey.css" rel="stylesheet">
<link rel="stylesheet" href="font-awesome/css/font-awesome.min.css">
</head>
<body>
<div id="wrapper">
<!-- Sidebar -->
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="nav-centered">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html">SharpKey Dashboard</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav side-nav">
<li><a href="index.html"><i class="fa fa-dashboard" style="color: yellow;"></i> Status</a></li>
<li>
<a href="#" data-toggle="collapse" data-target="#configmenu" class="dropdown-toggle" aria-expanded="true"><i class="fa fa-cogs" style="color: yellow;"></i> Config <b class="caret pull-right"></b></a>
<ul id="configmenu" class="collapse in">
<li id="keyMapAvailable"><a href="keymap.html"><i class="fa fa-keyboard-o" style="color: yellow;"></i> %SK_CURRENTIF%Keymap</a></li>
<li id="mouseCfgAvailable"><a href="mouse.html"><i class="fa fa-mouse-pointer" style="color: yellow;"></i> Mouse Config</a></li>
<li class="active"><a href="hostconfig.html"><i class="fa fa-plug" style="color: yellow;"></i> Host Config</a></li>
</ul>
</li>
<li>
<a href="#" data-toggle="collapse" data-target="#settings" class="dropdown-toggle" aria-expanded="false"><i class="fa fa-gear" style="color: yellow;"></i> Settings <b class="caret pull-right"></b></a>
<ul id="settings" class="collapse">
<li><a href="ota.html"><i class="fa fa-refresh" style="color: yellow;"></i> OTA Update</a></li>
<li><a href="wifimanager.html"><i class="fa fa-wifi" style="color: yellow;"></i> WiFi Manager</a></li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right navbar-user">
<li class="dropdown user-dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-rotate-left" style="color: yellow;"></i> Reset <b class="caret"></b></a>
<ul class="dropdown-menu">
<li style="margin-left: 1em;"><b>Reset Menu</b></li>
<li class="divider"></li>
<li><a href="reboot">Reboot</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div>
</nav>
<div id="page-wrapper">
<div class="row">
<div class="col-lg-12">
<h1>Host Configuration</h1>
<ol class="breadcrumb">
<li class="active"><i class="fa fa-plug"></i> Config &gt; Host Config</li>
</ol>
<div class="alert alert-success alert-dismissable justify">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
<p>Select the host machine the adapter is connected to. Choose <b>Auto</b> for automatic hardware detection at power-on, or select a specific host to override detection. A reboot is required after saving.</p>
</div>
</div>
</div><!-- /.row -->
<div class="row">
<div class="col-lg-12">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title"><i class="fa fa-plug"></i> Host Machine Selection</h3>
</div>
<div class="panel-body">
<form action="/data/hostconfig" method="POST" id="hostConfigForm">
<table class="table table-borderless table-sm">
<tbody>
<tr>
<td><label><input type="radio" name="hostmode" value="0" %SK_HOSTMODE_AUTO%> <b>Auto</b> <i>(detect from hardware)</i></label></td>
</tr>
<tr><td><hr class="hr_no_margin"></td></tr>
<tr>
<td><label><input type="radio" name="hostmode" value="2500" %SK_HOSTMODE_MZ2500%> Sharp MZ-2500</label></td>
</tr>
<tr>
<td><label><input type="radio" name="hostmode" value="2800" %SK_HOSTMODE_MZ2800%> Sharp MZ-2800</label></td>
</tr>
<tr>
<td><label><input type="radio" name="hostmode" value="1" %SK_HOSTMODE_X1%> Sharp X1 / X1 Turbo</label></td>
</tr>
<tr>
<td><label><input type="radio" name="hostmode" value="68000" %SK_HOSTMODE_X68000%> Sharp X68000</label></td>
</tr>
<tr>
<td><label><input type="radio" name="hostmode" value="5600" %SK_HOSTMODE_MZ5600%> Sharp MZ-5500 / MZ-5600 / MZ-6500</label></td>
</tr>
<tr>
<td><label><input type="radio" name="hostmode" value="9801" %SK_HOSTMODE_PC9801%> NEC PC-9801</label></td>
</tr>
<tr><td><hr class="hr_no_margin"></td></tr>
<tr>
<td><label><input type="radio" name="hostmode" value="2" %SK_HOSTMODE_MOUSE%> Mouse</label></td>
</tr>
</tbody>
</table>
<br>
<button type="submit" class="wm-button" id="hostConfigSave">Save</button>
<span id="hostConfigMsg" style="margin-left: 15px;">%SK_ERRMSG%</span>
</form>
</div>
</div>
</div>
</div><!-- /.row -->
</div><!-- /#page-wrapper -->
</div><!-- /#wrapper -->
<!-- JavaScript -->
<script src="js/jquery.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script>
// Store the name of the active and secondary interfaces.
const activeInterface = "%SK_CURRENTIF%";
const secondaryInterface = "%SK_SECONDIF%"
</script>
<script src="js/index.js"></script>
<script>
// Handle form submission via AJAX.
$(document).ready(function() {
$('#hostConfigForm').on('submit', function(e) {
e.preventDefault();
$.ajax({
url: '/data/hostconfig',
type: 'POST',
data: $(this).serialize(),
success: function(data) {
$('#hostConfigMsg').html(data);
},
error: function(xhr) {
$('#hostConfigMsg').html('<font color="red">Error saving configuration.</font>');
}
});
});
});
</script>
</body>
</html>

View File

@@ -23,6 +23,7 @@
<!-- Sidebar -->
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="nav-centered">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
@@ -31,30 +32,42 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html">SharpKey Interface</a>
<a class="navbar-brand" href="index.html">SharpKey Dashboard</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav side-nav">
<li class="active"><a href="index.html"><i class="fa fa-dashboard"></i> Status</a></li>
<li id="keyMapAvailable"><a href="keymap.html"><i class="fa fa-keyboard-o"></i> %SK_CURRENTIF%Keymap</a></li>
<li id="mouseCfgAvailable"><a href="mouse.html"><i class="fa fa-mouse-pointer"></i> Mouse Config</a></li>
<li><a href="ota.html"><i class="fa fa-file"></i> OTA Update</a></li>
<li><a href="wifimanager.html"><i class="fa fa-wifi"></i> WiFi Manager</a></li>
<li><a href="reboot"><i class="fa fa-power-off"></i> Reboot</a></li>
<li class="active"><a href="index.html"><i class="fa fa-dashboard" style="color: yellow;"></i> Status</a></li>
<li>
<a href="#" data-toggle="collapse" data-target="#configmenu" class="dropdown-toggle" aria-expanded="false"><i class="fa fa-cogs" style="color: yellow;"></i> Config <b class="caret pull-right"></b></a>
<ul id="configmenu" class="collapse">
<li id="keyMapAvailable"><a href="keymap.html"><i class="fa fa-keyboard-o" style="color: yellow;"></i> %SK_CURRENTIF%Keymap</a></li>
<li id="mouseCfgAvailable"><a href="mouse.html"><i class="fa fa-mouse-pointer" style="color: yellow;"></i> Mouse Config</a></li>
<li><a href="hostconfig.html"><i class="fa fa-plug" style="color: yellow;"></i> Host Config</a></li>
</ul>
</li>
<li>
<a href="#" data-toggle="collapse" data-target="#settings" class="dropdown-toggle" aria-expanded="false"><i class="fa fa-gear" style="color: yellow;"></i> Settings <b class="caret pull-right"></b></a>
<ul id="settings" class="collapse">
<li><a href="ota.html"><i class="fa fa-refresh" style="color: yellow;"></i> OTA Update</a></li>
<li><a href="wifimanager.html"><i class="fa fa-wifi" style="color: yellow;"></i> WiFi Manager</a></li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right navbar-user">
<li class="dropdown user-dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-gear"></i> Settings <b class="caret"></b></a>
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-rotate-left" style="color: yellow;"></i> Reset <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="#"><i class="fa fa-gear"></i> Settings</a></li>
<li style="margin-left: 1em;"><b>Reset Menu</b></li>
<li class="divider"></li>
<li><a href="reboot">Reboot</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div>
</nav>
<div id="page-wrapper">
@@ -85,15 +98,15 @@
<table class="table table-borderless table-sm">
<tbody>
<tr style="display: compact;">
<td>SSID:</td><td><span style="color: blue;">%SK_APSSID%</span></td>
<td>SSID:</td><td><span style="color: #5bf;">%SK_APSSID%</span></td>
</tr>
<tr style="display: compact;">
<td>Password:</td><td><span style="color: blue;">%SK_APPWD%</span></td>
<td>Password:</td><td><span style="color: #5bf;">%SK_APPWD%</span></td>
</tr>
<tr>
<td>IP (AP):</td><td><span style="color: blue;">%SK_CURRENTIP%</span></td>
<td>NETMASK:</td><td><span style="color: blue;">%SK_CURRENTNM%</span></td>
<td>GATEWAY:</td><td><span style="color: blue;">%SK_CURRENTGW%</span></td>
<td>IP (AP):</td><td><span style="color: #5bf;">%SK_CURRENTIP%</span></td>
<td>NETMASK:</td><td><span style="color: #5bf;">%SK_CURRENTNM%</span></td>
<td>GATEWAY:</td><td><span style="color: #5bf;">%SK_CURRENTGW%</span></td>
</tr>
</tbody>
</table>
@@ -102,20 +115,20 @@
<table class="table table-borderless table-sm">
<tbody>
<tr style="display: compact;">
<td>SSID:</td><td><span style="color: blue;">%SK_CLIENTSSID%</span></td>
<td>SSID:</td><td><span style="color: #5bf;">%SK_CLIENTSSID%</span></td>
</tr>
<tr style="display: compact;" id="wifiCfg1%SK_CLIENTDHCPON%">
<td>DHCP:</td><td><span style="color: blue;">Enabled</span></td>
<td>DHCP:</td><td><span style="color: #5bf;">Enabled</span></td>
</tr>
<tr id="wifiCfg3%SK_CLIENTDHCPON%">
<td>IP (assigned):</td><td><span style="color: blue;">%SK_CURRENTIP%</span></td>
<td>NETMASK:</td><td><span style="color: blue;">%SK_CURRENTNM%</span></td>
<td>GATEWAY:</td><td><span style="color: blue;">%SK_CURRENTGW%</span></td>
<td>IP (assigned):</td><td><span style="color: #5bf;">%SK_CURRENTIP%</span></td>
<td>NETMASK:</td><td><span style="color: #5bf;">%SK_CURRENTNM%</span></td>
<td>GATEWAY:</td><td><span style="color: #5bf;">%SK_CURRENTGW%</span></td>
</tr>
<tr id="wifiCfg3%SK_CLIENTDHCPOFF%">
<td>IP (fixed):</td><td><span style="color: blue;">%SK_CURRENTIP%</span></td>
<td>NETMASK:</td><td><span style="color: blue;">%SK_CURRENTNM%</span></td>
<td>GATEWAY:</td><td><span style="color: blue;">%SK_CURRENTGW%</span></td>
<td>IP (fixed):</td><td><span style="color: #5bf;">%SK_CURRENTIP%</span></td>
<td>NETMASK:</td><td><span style="color: #5bf;">%SK_CURRENTNM%</span></td>
<td>GATEWAY:</td><td><span style="color: #5bf;">%SK_CURRENTGW%</span></td>
</tr>
</tbody>
</table>

View File

@@ -1,18 +1,24 @@
function showIPConfig()
function showIPConfig()
{
var el;
if(document.getElementById("wifiCfg0checked"))
{
document.getElementById("wifiCfg0checked").style.display = 'compact';
document.getElementById("wifiCfg").style.display = 'none';
el = document.getElementById("wifiCfg");
if(el) el.style.display = 'none';
if(document.getElementById("wifiCfg3checked"))
{
document.getElementById("wifiCfg3checked").style.display = 'compact';
document.getElementById("wifiCfg3").style.display = 'none';
el = document.getElementById("wifiCfg3");
if(el) el.style.display = 'none';
} else
{
document.getElementById("wifiCfg3checked").style.display = 'none';
document.getElementById("wifiCfg3").style.display = 'compact';
el = document.getElementById("wifiCfg3checked");
if(el) el.style.display = 'none';
el = document.getElementById("wifiCfg3");
if(el) el.style.display = 'compact';
}
if(document.getElementById("wifiCfg1checked"))
@@ -20,12 +26,15 @@ function showIPConfig()
document.getElementById("wifiCfg1checked").style.display = 'compact';
} else
{
document.getElementById("wifiCfg1").style.display = 'none';
el = document.getElementById("wifiCfg1");
if(el) el.style.display = 'none';
}
} else
{
document.getElementById("wifiCfg0").style.display = 'none';
document.getElementById("wifiCfgchecked").style.display = 'compact';
el = document.getElementById("wifiCfg0");
if(el) el.style.display = 'none';
el = document.getElementById("wifiCfgchecked");
if(el) el.style.display = 'compact';
}
}
@@ -37,6 +46,7 @@ function enableIfConfig()
if(activeInterface === "KeyInterface ")
{
document.getElementById("keyMapAvailable").style.display = 'none';
document.getElementById("mouseCfgAvailable").style.display = 'none';
}
// Mouse interface active?
else if(activeInterface === "Mouse ")
@@ -61,6 +71,15 @@ function enableIfConfig()
// On document load, setup the items viewable on the page according to set values.
document.addEventListener("DOMContentLoaded", function setPageDefaults()
{
showIPConfig();
enableIfConfig();
showIPConfig();
});
// jQuery dropdown toggle handlers for sidebar submenu animations.
$(document).ready(function(){
$('.side-nav .dropdown-toggle').on('click', function(e) {
e.preventDefault();
var $target = $($(this).data('target'));
$target.collapse('toggle');
});
});

View File

@@ -25,6 +25,7 @@
<!-- Sidebar -->
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="nav-centered">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
@@ -33,30 +34,42 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html">SharpKey Interface</a>
<a class="navbar-brand" href="index.html">SharpKey Dashboard</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav side-nav">
<li><a href="index.html"><i class="fa fa-dashboard"></i> Status</a></li>
<li class="active" id="keyMapAvailable"><a href="keymap.html"><i class="fa fa-keyboard-o"></i> %SK_CURRENTIF%Keymap</a></li>
<li id="mouseCfgAvailable"><a href="mouse.html"><i class="fa fa-mouse-pointer"></i> Mouse Config</a></li>
<li><a href="ota.html"><i class="fa fa-file"></i> OTA Update</a></li>
<li><a href="wifimanager.html"><i class="fa fa-wifi"></i> WiFi Manager</a></li>
<li><a href="reboot"><i class="fa fa-power-off"></i> Reboot</a></li>
<li><a href="index.html"><i class="fa fa-dashboard" style="color: yellow;"></i> Status</a></li>
<li>
<a href="#" data-toggle="collapse" data-target="#configmenu" class="dropdown-toggle" aria-expanded="true"><i class="fa fa-cogs" style="color: yellow;"></i> Config <b class="caret pull-right"></b></a>
<ul id="configmenu" class="collapse in">
<li class="active" id="keyMapAvailable"><a href="keymap.html"><i class="fa fa-keyboard-o" style="color: yellow;"></i> %SK_CURRENTIF%Keymap</a></li>
<li id="mouseCfgAvailable"><a href="mouse.html"><i class="fa fa-mouse-pointer" style="color: yellow;"></i> Mouse Config</a></li>
<li><a href="hostconfig.html"><i class="fa fa-plug" style="color: yellow;"></i> Host Config</a></li>
</ul>
</li>
<li>
<a href="#" data-toggle="collapse" data-target="#settings" class="dropdown-toggle" aria-expanded="false"><i class="fa fa-gear" style="color: yellow;"></i> Settings <b class="caret pull-right"></b></a>
<ul id="settings" class="collapse">
<li><a href="ota.html"><i class="fa fa-refresh" style="color: yellow;"></i> OTA Update</a></li>
<li><a href="wifimanager.html"><i class="fa fa-wifi" style="color: yellow;"></i> WiFi Manager</a></li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right navbar-user">
<li class="dropdown user-dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-gear"></i> Settings <b class="caret"></b></a>
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-rotate-left" style="color: yellow;"></i> Reset <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="#"><i class="fa fa-gear"></i> Settings</a></li>
<li style="margin-left: 1em;"><b>Reset Menu</b></li>
<li class="divider"></li>
<li><a href="reboot">Reboot</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div>
</nav>
<div id="page-wrapper">

View File

@@ -25,6 +25,7 @@
<!-- Sidebar -->
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="nav-centered">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
@@ -33,30 +34,42 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html">SharpKey Interface</a>
<a class="navbar-brand" href="index.html">SharpKey Dashboard</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav side-nav">
<li><a href="index.html"><i class="fa fa-dashboard"></i> Status</a></li>
<li id="keyMapAvailable"><a href="keymap.html"><i class="fa fa-keyboard-o"></i> %SK_CURRENTIF%Keymap</a></li>
<li class="active" id="mouseCfgAvailable"><a href="mouse.html"><i class="fa fa-mouse-pointer"></i> Mouse Config</a></li>
<li><a href="ota.html"><i class="fa fa-file"></i> OTA Update</a></li>
<li><a href="wifimanager.html"><i class="fa fa-wifi"></i> WiFi Manager</a></li>
<li><a href="reboot"><i class="fa fa-power-off"></i> Reboot</a></li>
<li><a href="index.html"><i class="fa fa-dashboard" style="color: yellow;"></i> Status</a></li>
<li>
<a href="#" data-toggle="collapse" data-target="#configmenu" class="dropdown-toggle" aria-expanded="true"><i class="fa fa-cogs" style="color: yellow;"></i> Config <b class="caret pull-right"></b></a>
<ul id="configmenu" class="collapse in">
<li id="keyMapAvailable"><a href="keymap.html"><i class="fa fa-keyboard-o" style="color: yellow;"></i> %SK_CURRENTIF%Keymap</a></li>
<li class="active" id="mouseCfgAvailable"><a href="mouse.html"><i class="fa fa-mouse-pointer" style="color: yellow;"></i> Mouse Config</a></li>
<li><a href="hostconfig.html"><i class="fa fa-plug" style="color: yellow;"></i> Host Config</a></li>
</ul>
</li>
<li>
<a href="#" data-toggle="collapse" data-target="#settings" class="dropdown-toggle" aria-expanded="false"><i class="fa fa-gear" style="color: yellow;"></i> Settings <b class="caret pull-right"></b></a>
<ul id="settings" class="collapse">
<li><a href="ota.html"><i class="fa fa-refresh" style="color: yellow;"></i> OTA Update</a></li>
<li><a href="wifimanager.html"><i class="fa fa-wifi" style="color: yellow;"></i> WiFi Manager</a></li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right navbar-user">
<li class="dropdown user-dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-gear"></i> Settings <b class="caret"></b></a>
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-rotate-left" style="color: yellow;"></i> Reset <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="#"><i class="fa fa-gear"></i> Settings</a></li>
<li style="margin-left: 1em;"><b>Reset Menu</b></li>
<li class="divider"></li>
<li><a href="reboot">Reboot</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div>
</nav>
<div id="page-wrapper">

View File

@@ -23,6 +23,7 @@
<!-- Sidebar -->
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="nav-centered">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
@@ -31,30 +32,42 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html">SharpKey Interface</a>
<a class="navbar-brand" href="index.html">SharpKey Dashboard</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav side-nav">
<li><a href="index.html"><i class="fa fa-dashboard"></i> Status</a></li>
<li id="keyMapAvailable"><a href="keymap.html"><i class="fa fa-keyboard-o"></i> %SK_CURRENTIF%Keymap</a></li>
<li id="mouseCfgAvailable"><a href="mouse.html"><i class="fa fa-mouse-pointer"></i> Mouse Config</a></li>
<li class="active"><a href="ota.html"><i class="fa fa-table"></i> OTA Update</a></li>
<li><a href="wifimanager.html"><i class="fa fa-wifi"></i> WiFi Manager</a></li>
<li><a href="reboot"><i class="fa fa-power-off"></i> Reboot</a></li>
<li><a href="index.html"><i class="fa fa-dashboard" style="color: yellow;"></i> Status</a></li>
<li>
<a href="#" data-toggle="collapse" data-target="#configmenu" class="dropdown-toggle" aria-expanded="false"><i class="fa fa-cogs" style="color: yellow;"></i> Config <b class="caret pull-right"></b></a>
<ul id="configmenu" class="collapse">
<li id="keyMapAvailable"><a href="keymap.html"><i class="fa fa-keyboard-o" style="color: yellow;"></i> %SK_CURRENTIF%Keymap</a></li>
<li id="mouseCfgAvailable"><a href="mouse.html"><i class="fa fa-mouse-pointer" style="color: yellow;"></i> Mouse Config</a></li>
<li><a href="hostconfig.html"><i class="fa fa-plug" style="color: yellow;"></i> Host Config</a></li>
</ul>
</li>
<li>
<a href="#" data-toggle="collapse" data-target="#settings" class="dropdown-toggle" aria-expanded="true"><i class="fa fa-gear" style="color: yellow;"></i> Settings <b class="caret pull-right"></b></a>
<ul id="settings" class="collapse in">
<li class="active"><a href="ota.html"><i class="fa fa-refresh" style="color: yellow;"></i> OTA Update</a></li>
<li><a href="wifimanager.html"><i class="fa fa-wifi" style="color: yellow;"></i> WiFi Manager</a></li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right navbar-user">
<li class="dropdown user-dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-gear"></i> Settings <b class="caret"></b></a>
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-rotate-left" style="color: yellow;"></i> Reset <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="#"><i class="fa fa-gear"></i> Settings</a></li>
<li style="margin-left: 1em;"><b>Reset Menu</b></li>
<li class="divider"></li>
<li><a href="reboot">Reboot</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div>
</nav>
<div id="page-wrapper">

View File

@@ -1 +0,0 @@
1.02

View File

@@ -0,0 +1 @@
1.04

View File

@@ -24,6 +24,7 @@
<!-- Sidebar -->
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="nav-centered">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
@@ -32,30 +33,42 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html">SharpKey Interface</a>
<a class="navbar-brand" href="index.html">SharpKey Dashboard</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav side-nav">
<li><a href="index.html"><i class="fa fa-dashboard"></i> Status</a></li>
<li id="keyMapAvailable"><a href="keymap.html"><i class="fa fa-keyboard-o"></i> %SK_CURRENTIF%Keymap</a></li>
<li id="mouseCfgAvailable"><a href="mouse.html"><i class="fa fa-mouse-pointer"></i> Mouse Config</a></li>
<li><a href="ota.html"><i class="fa fa-table"></i> OTA Update</a></li>
<li class="active"><a href="wifimanager.html"><i class="fa fa-wifi"></i> WiFi Manager</a></li>
<li><a href="reboot"><i class="fa fa-power-off"></i> Reboot</a></li>
<li><a href="index.html"><i class="fa fa-dashboard" style="color: yellow;"></i> Status</a></li>
<li>
<a href="#" data-toggle="collapse" data-target="#configmenu" class="dropdown-toggle" aria-expanded="false"><i class="fa fa-cogs" style="color: yellow;"></i> Config <b class="caret pull-right"></b></a>
<ul id="configmenu" class="collapse">
<li id="keyMapAvailable"><a href="keymap.html"><i class="fa fa-keyboard-o" style="color: yellow;"></i> %SK_CURRENTIF%Keymap</a></li>
<li id="mouseCfgAvailable"><a href="mouse.html"><i class="fa fa-mouse-pointer" style="color: yellow;"></i> Mouse Config</a></li>
<li><a href="hostconfig.html"><i class="fa fa-plug" style="color: yellow;"></i> Host Config</a></li>
</ul>
</li>
<li>
<a href="#" data-toggle="collapse" data-target="#settings" class="dropdown-toggle" aria-expanded="true"><i class="fa fa-gear" style="color: yellow;"></i> Settings <b class="caret pull-right"></b></a>
<ul id="settings" class="collapse in">
<li><a href="ota.html"><i class="fa fa-refresh" style="color: yellow;"></i> OTA Update</a></li>
<li class="active"><a href="wifimanager.html"><i class="fa fa-wifi" style="color: yellow;"></i> WiFi Manager</a></li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right navbar-user">
<li class="dropdown user-dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-gear"></i> Settings <b class="caret"></b></a>
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-rotate-left" style="color: yellow;"></i> Reset <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="#"><i class="fa fa-gear"></i> Settings</a></li>
<li style="margin-left: 1em;"><b>Reset Menu</b></li>
<li class="divider"></li>
<li><a href="reboot">Reboot</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div>
</nav>
<div id="page-wrapper">