added saving image snapshot to micro SD card

pull/32/head
Miroslav Pivovarsky 2024-05-27 17:42:44 +02:00
parent b32f0e7a86
commit 55bdcecf84
27 changed files with 1272 additions and 837 deletions

View File

@ -123,6 +123,7 @@ void setup() {
xTaskCreatePinnedToCore(System_TaskStreamTelemetry, "PrintStreamTelemetry", 3300, NULL, 6, &Task_StreamTelemetry, 0); /*function, description, stack size, parameters, priority, task handle, core*/
xTaskCreatePinnedToCore(System_TaskSysLed, "SystemLed", 3000, NULL, 7, &Task_SysLed, 0); /*function, description, stack size, parameters, priority, task handle, core*/
xTaskCreatePinnedToCore(System_TaskWiFiWatchdog, "WiFiWatchdog", 3500, NULL, 8, &Task_WiFiWatchdog, 0); /*function, description, stack size, parameters, priority, task handle, core*/
//xTaskCreatePinnedToCore(System_TaskSdCardRemove, "SdCardRemove", 3000, NULL, 9, &Task_SdCardFileRemove, 0); /*function, description, stack size, parameters, priority, task handle, core*/
/* init wdg */
SystemLog.AddEvent(LogLevel_Info, F("Init WDG"));
@ -136,6 +137,7 @@ void setup() {
esp_task_wdt_add(Task_StreamTelemetry);
esp_task_wdt_add(Task_SysLed);
esp_task_wdt_add(Task_WiFiWatchdog);
//esp_task_wdt_add(Task_SdCardFileRemove);
esp_task_wdt_reset(); /* reset wdg */
SystemLog.AddEvent(LogLevel_Info, F("MCU configuration done"));

View File

@ -14,13 +14,13 @@
#ifndef _WEB_PAGE_H_
#define _WEB_PAGE_H_
#define MSG_REBOOT_MCU "Reboot process started, wait several seconds for mcu to boot up. You can close this window now."
#define MSG_REBOOT_MCU "Reboot process started, wait several seconds for mcu to boot up. You can close this window now"
#define MSG_SAVE_OK_REBOOT "Save OK. Please reboot MCU" ///< WEB app msg save OK
#define MSG_SAVE_OK_WIFI "Save OK. Connecting to Wi-Fi. Please wait several second."
#define MSG_SAVE_OK_WIFI "Save OK. Connecting to Wi-Fi. Please wait several second"
#define MSG_SAVE_OK "Save cfg OK" ///< WEB app msg save OK
#define MSG_SAVE_NOTOK "Save cfg NOT OK!" ///< WEB app msg save NOT OK
#define MSG_SCANNING "Scanning Wi-Fi networks. Wait 8s..." ///< WEB app msg Scanning wifi
#define MSG_UPDATE_START "Start updating."
#define MSG_UPDATE_START "Start updating"
/* ------------------------------------------------------------------------------------------------------------ */
const char index_html[] PROGMEM = R"rawliteral(
@ -40,7 +40,6 @@ const char index_html[] PROGMEM = R"rawliteral(
$("#content").load(page);
setActive(this);
});
$("#content").load("page_config.html", function() {
var defaultLink = document.querySelector('a[href="page_config.html"]');
setActive(defaultLink);
@ -48,7 +47,6 @@ const char index_html[] PROGMEM = R"rawliteral(
});
</script>
</head>
<body>
<nav>
<img src="esp32_cam.svg" id=logo alt="Logo image" style="margin-left: 10px;" />
@ -62,7 +60,6 @@ const char index_html[] PROGMEM = R"rawliteral(
</ul>
</nav>
<hr>
<section class="container">
<div class="container_left-half">
<article>
@ -78,23 +75,20 @@ const char index_html[] PROGMEM = R"rawliteral(
</article>
</div>
</section>
<br>
<cfg>
<cfg_bar>
<div id="cfg">
<div id="cfg_bar">
<div id="links">
<li><a href="page_config.html">Camera configuration</a></li>
<li><a href="page_wifi.html">Wi-Fi configuration</a></li>
<li><a href="page_auth.html">Authentication</a></li>
<li><a href="page_system.html">System</a></li>
</div>
</cfg_bar>
</cfg>
</div>
</div>
<hr>
<div id="content">
</div>
<br><br><br><br>
<table id=botton><tr>
<td><p class=p2>Prusa Connect ESP32 cam</p> </td>
@ -103,7 +97,6 @@ const char index_html[] PROGMEM = R"rawliteral(
<td><a href="https://github.com/Prusa-Development/Prusa-Firmware-ESP32-Cam/" id="github-link"><svg height="25"><image href="github-icon.svg"></svg></a></td>
</tr></table>
</body>
<script src="scripts.js"></script>
<script>
sliderCheck();
@ -118,7 +111,6 @@ const char index_html[] PROGMEM = R"rawliteral(
const char page_auth_html[] PROGMEM = R"rawliteral(
<style>@import url("styles.css");</style>
<script src="jquery-3.7.0.min.js"></script>
<body>
<center>
<table>
@ -131,7 +123,6 @@ const char page_auth_html[] PROGMEM = R"rawliteral(
</table>
</center>
</body>
<script src="scripts.js"></script>
<script>
get_data("auth");
@ -145,7 +136,6 @@ const char page_auth_html[] PROGMEM = R"rawliteral(
const char page_wifi_html[] PROGMEM = R"rawliteral(
<style>@import url("styles.css");</style>
<script src="jquery-3.7.0.min.js"></script>
<body>
<table id="center_tb">
<tr><td></td><td style="width:40%">
@ -156,12 +146,10 @@ const char page_wifi_html[] PROGMEM = R"rawliteral(
<p class="w2">IP Address: <span id="ip"></span></p>
<p class="w2">mDNS: http://<span id="mdns"></span>.local</p>
</td><td></td></tr>
<tr><td></td><td style="width:40%">
<br>
<p class="w1">Available networks</p>
</td><td></td></tr>
<tr><td></td><td style="width:40%">
<table id="wifi_ntw">
<colgroup>
@ -178,12 +166,10 @@ const char page_wifi_html[] PROGMEM = R"rawliteral(
</tr>
</table>
</td><td></td></tr>
<tr><td></td><td style="width:40%" align="right">
<button class="btn" onclick="scanWifi()">Scan Wi-Fi networks</button>
<br>
</td><td></td></tr>
<tr><td></td><td style="width:40%">
<br>
<table>
@ -193,9 +179,7 @@ const char page_wifi_html[] PROGMEM = R"rawliteral(
<tr><td></td><td align="center"><button class="btn_save_w" onclick="setWifi(document.getElementById('wifi_ssid_id').value, document.getElementById('wifi_pass_id').value)">Save & Connect</button></td></tr>
</table>
</td><td></td></tr>
</table>
<br>
<center>
<button class="btn_collapsible_wifi">Advanced Wi-Fi settings</button>
@ -219,9 +203,7 @@ const char page_wifi_html[] PROGMEM = R"rawliteral(
<tr><td></td><td align="center"><button class="btn_save_w" onclick="setWifiNet(document.getElementById('net_ip_id').value, document.getElementById('net_mask_id').value, document.getElementById('net_gw_id').value, document.getElementById('net_dns_id').value)">Save</button></td></tr>
</table>
</div>
</body>
<script src="scripts.js"></script>
<script>
setTimeout(function(){GetDataAndPrintTableWiFi();}, 500);
@ -236,7 +218,7 @@ const char page_config_html[] PROGMEM = R"rawliteral(
<script src="jquery-3.7.0.min.js"></script>
<body>
<center><table id="data">
<tr><td class="ps3">Basic image settings</td><td></td></tr>
<tr><td class="ps3">Basic settings</td><td></td></tr>
<tr><td class="pc1">Connect Token</td><td ><input type="text" name="token" id=tokenid >&nbsp;<button class="btn_save" onclick="changeValue(encodeURIComponent(document.getElementById('tokenid').value), 'set_token?token=', 'config')">Save</button></td></tr>
<tr><td class="pc1">Fingerprint</td><td class=pc2 id="fingerprint"></td></tr>
<tr><td class="pc1">Trigger Interval [s]</td><td ><input type="text" name="refresh" id=refreshid >&nbsp;<button class="btn_save" onclick="changeValue(document.getElementById('refreshid').value, 'set_int?refresh=', 'config')">Save</button></td></tr>
@ -261,7 +243,7 @@ const char page_config_html[] PROGMEM = R"rawliteral(
<tr><td class="pc1">Saturation</td><td class="pc2">Low <input type="range" class="slider" name="saturation" id=saturationid min="-2" max="2" step="1" onchange="changeValue(this.value, 'set_int?saturation=', 'config')"> High</td></tr>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr>
<td class="pc1">Image rotation</td><td><label for="image_rotation"></label>
<td class="pc1">Image Rotation</td><td><label for="image_rotation"></label>
<select class="select" id="image_rotationid" name="image_rotation" onchange="changeValue(this.value, 'set_int?image_rotation=', 'config')">
<option value="1">0°</option>
<option value="6">90°</option>
@ -278,13 +260,14 @@ const char page_config_html[] PROGMEM = R"rawliteral(
<tr><td class="pc1">Flash duration</td><td class="pc2">Low <input type="range" class="slider" name="flash_time" id=flash_timeid min="50" max="1500" step="50" onchange="changeValue(this.value, 'set_int?flash_time=', 'config')">High</td></tr>
<tr><td class="pc1">Flash duration</td><td class="pc2"><span id="flash_time_value"></span> ms</td></tr>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr><td class="pc1">Save images to micro SD</td><td class="pc2"><label class="switch"><input type="checkbox" name="timelaps" id=timelapsid onchange="changeValue(this.checked, 'set_bool?timelaps_enable=', 'config')"><span class="checkbox_slider round"></span></label> <span id="status_timelaps"></span></td></tr>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
</table></center>
<center><button class="btn_collapsible">Advanced image settings</button></center>
<center><button class="btn_collapsible">Advanced settings</button></center>
<div class="content">
<center><table id="data">
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr><td class="ps3">Advanced image settings</td><td></td></tr>
<tr><td class="ps3">Advanced settings</td><td></td></tr>
<tr><td class="pc1">Automatic white balancing</td><td class="pc2"><label class="switch"><input type="checkbox" name="awb" id=awbid onchange="changeValue(this.checked, 'set_bool?awb=', 'config')"><span class="checkbox_slider round"></span></label> <span id="status_awb"></span></td></tr>
<tr><td class="pc1">Automatic white balancing gain</td><td class="pc2"><label class="switch"><input type="checkbox" name="awb_gain" id=awb_gainid onchange="changeValue(this.checked, 'set_bool?awb_gain=', 'config')"><span class="checkbox_slider round"></span></label> <span id="status_awb_gain"></span></td></tr>
<tr>
@ -315,7 +298,6 @@ const char page_config_html[] PROGMEM = R"rawliteral(
</table></center>
</div>
</body>
<script src="scripts.js"></script>
<script>
get_data("config");
@ -335,12 +317,14 @@ const char page_system_html[] PROGMEM = R"rawliteral(
<tr><td class="ps1">Wi-Fi mode</td><td class="ps2" id="wifi_mode"></td></tr>
<tr><td class="ps1">Wi-Fi service AP SSID</td><td class="ps2" id="service_ap_ssid"></td></tr>
<tr><td class="ps1">Uptime</td><td class="ps2" id="uptime"></td></tr>
<tr><td class="ps1">Software version</td><td class="ps2" id="sw_ver"></td></tr>
<tr><td class="ps1">Software build</td><td class="ps2" id="sw_build"></td></tr>
<tr><td class="ps1">Available software update</td><td class="ps2"><span id="sw_new_ver"></span> <span class="underlined-text" onclick="checkUpdate()">Check update from cloud</span></td></tr>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr><td class="ps3">Firmware</td><td></td></tr>
<tr><td class="ps1">Version</td><td class="ps2" id="sw_ver"></td></tr>
<tr><td class="ps1">Build</td><td class="ps2" id="sw_build"></td></tr>
<tr><td class="ps1">Available update</td><td class="ps2"><span id="sw_new_ver"></span> <span class="underlined-text" onclick="checkUpdate()">Check update from cloud</span></td></tr>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr><td class="ps3">System configuration</td><td></td></tr>
<tr><td class="pc1">Cam name & mDNS record</td><td ><input type="text" name="mdns" id=mdnsid ><span class=pc1>.local</span>&nbsp;<button class="btn_save" onclick="changeValue(document.getElementById('mdnsid').value, 'set_mdns?mdns=', 'system')">Save</button></td></tr>
<tr><td class="pc1">Camera name & mDNS record</td><td ><input type="text" name="mdns" id=mdnsid ><span class=pc1>.local</span>&nbsp;<button class="btn_save" onclick="changeValue(document.getElementById('mdnsid').value, 'set_mdns?mdns=', 'system')">Save</button></td></tr>
<tr>
<td class="pc1">Log level</td><td><label for="loglevel"></label>
<select class="select" id="loglevelid" name="loglevel" onchange="changeValue(this.value, 'set_int?log_level=', 'system')">
@ -352,21 +336,26 @@ const char page_system_html[] PROGMEM = R"rawliteral(
</td>
</tr>
<tr><td class="pc1">Get logs</td><td ><button class="btn_update" onclick="window.open('get_logs')">Get logs</button></td></tr>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr><td class="ps3">Micro SD card</td><td></td></tr>
<tr><td class="ps1">Card status</td><td class="ps2" id="sd_status"></td></tr>
<tr><td class="ps1">Capacity</td><td class="ps2"><span id="sd_total"></span> MB</td></tr>
<tr><td class="ps1">Available</td><td class="ps2"><div class="progress-container"><div class="progress-bar" id="progress_bar_sd_free">0%</div></div></td></tr>
<tr><td class="ps1">Used</td><td class="ps2"><div class="progress-container"><div class="progress-bar" id="progress_bar_sd_used">0%</div></div></td></tr>
</table>
</center>
<br>
<center>
<table id="update">
<tr><td class="ps3">Firmware update</td><td></td></tr>
<tr><td class="ps1">Status:</td><td><span class="ps2" id="status">Ready</span></td></tr>
<tr><td class="ps1">Processing:</td><td class="ps2"><div class="progress-container"><div class="progress-bar" id="myProgressBar">0%</div></div><!--<span class="ps2" id="progressValue">0</span>%--></td></tr>
<tr><td></td><td style="width:50%"><input type="file" id="firmwareInput" accept=".bin"></td></tr>
<tr><td class="ps1">Status</td><td><span class="ps2" id="status">Ready</span></td></tr>
<tr><td class="ps1">Progress</td><td class="ps2"><div class="progress-container"><div class="progress-bar" id="myProgressBar">0%</div></div></td></tr>
<tr><td></td><td><input type="file" id="firmwareInput" accept=".bin"></td></tr>
<tr><td></td><td><input type="submit" class="btn_update" value="Update from file" onclick="uploadFile()"></td></tr>
<tr><td></td><td><input type="submit" class="btn_update" value="Update from cloud" onclick="updateWeb()"></td></tr>
</table>
</center>
</body>
<script src="scripts.js"></script>
<script>
var updateCompleted = false;
@ -380,7 +369,6 @@ const char styles_css[] PROGMEM = R"rawliteral(
body {
font-family: sans-serif;
}
/* index styles */
.p1 {
color: #797979;
@ -449,7 +437,7 @@ body {
text-decoration-thickness: 2px;
}
/* CFG BAR */
cfg {
#cfg {
display: flex;
flex-direction: column;
text-align: center;
@ -458,18 +446,18 @@ cfg {
color: #2A2A2A;
opacity: 1;
}
cfg_bar li {
#cfg_bar li {
display: inline-block;
padding: 14px;
font-size: 16px;
left: 50%;
}
cfg_bar li a {
#cfg_bar li a {
text-decoration: none;
cursor: pointer;
color: #212529;
}
cfg_bar li a:hover {
#cfg_bar li a:hover {
color: #fa6831;
}
/* CONTAINER */
@ -739,7 +727,6 @@ cfg_bar li a:hover {
color: #2A2A2A;
opacity: 1;
}
/* BUTTON */
.btn_save_a {
width: 178px;
@ -797,23 +784,8 @@ cfg_bar li a:hover {
color: #2A2A2A;
opacity: 1;
}
/* data table */
#data {
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 100%;
table-layout: fixed;
}
#data td, #data th {
padding: 8px;
}
#data th {
padding-top: 12px;
padding-bottom: 12px;
text-align: left;
}
/* update table */
update {
#update {
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 100%;
@ -849,7 +821,7 @@ cfg_bar li a:hover {
}
/* progress bar*/
.progress-container {
width: 100%;
width: 30%;
background-color: #ccc;
}
.progress-bar {
@ -859,13 +831,12 @@ cfg_bar li a:hover {
text-align: center;
line-height: 15px;
color: white;
padding-left: 3px;
}
/* advanced cam cfg */
.content {
display: none;
}
.btn_collapsible {
width: 300px;
height: 24px;
@ -880,12 +851,10 @@ cfg_bar li a:hover {
background-color: #FA6831;
color: white;
}
/* advanced wifi cfg */
.content_wifi {
display: none;
}
.btn_collapsible_wifi {
width: 300px;
height: 24px;
@ -952,6 +921,7 @@ function get_data(val) {
document.getElementById('agc_gainid').value = obj.agc_gain;
document.getElementById("flash_time_value").innerText = obj.flash_time;
document.getElementById("aec_value_value").innerText = obj.aec_value;
document.getElementById('timelapsid').checked = obj.timelaps;
$("#status_hmirror").text((obj.hmirror == "true") ? "On" : "Off");
$("#status_vflip").text((obj.vflip == "true") ? "On" : "Off");
$("#status_lensc").text((obj.lensc == "true") ? "On" : "Off");
@ -965,6 +935,7 @@ function get_data(val) {
$("#status_raw_gama").text((obj.raw_gama == "true") ? "On" : "Off");
$("#status_aec2").text((obj.aec2 == "true") ? "On" : "Off");
$("#status_gain_ctrl").text((obj.gain_ctrl == "true") ? "On" : "Off");
$("#status_timelaps").text((obj.timelaps == "true") ? "On" : "Off");
sliderCheck();
}
@ -1006,6 +977,19 @@ function get_data(val) {
$("#wifi_mode").text(obj.wifi_mode);
$("#sw_new_ver").text(obj.sw_new_ver);
$("#service_ap_ssid").text(obj.service_ap_ssid);
$("#sd_status").text(obj.sd_status);
$("#sd_total").text(obj.sd_total);
$("#sd_free_p").text(obj.sd_free_p);
$("#sd_used_p").text(obj.sd_used_p);
var sd_free_prog = document.getElementById("progress_bar_sd_free");
sd_free_prog.style.width = obj.sd_free_p + "%";
sd_free_prog.innerHTML = obj.sd_free_p + "%";
var sd_free_prog = document.getElementById("progress_bar_sd_used");
sd_free_prog.style.width = obj.sd_used_p + "%";
sd_free_prog.innerHTML = obj.sd_used_p + "%";
document.getElementById('mdnsid').value = obj.mdns;
document.getElementById('loglevelid').value = obj.log_level;
}

View File

@ -30,6 +30,7 @@ Camera::Camera(Configuration* i_conf, Logs* i_log, uint8_t i_FlashPin) {
PhotoExifData.header = NULL;
PhotoExifData.len = 0;
PhotoExifData.offset = 0;
PhotoSending = false;
}
/**
@ -185,6 +186,10 @@ framesize_t Camera::TransformFrameSizeDataType(uint8_t i_data) {
return ret;
}
void Camera::SetPhotoSending(bool i_data) {
PhotoSending = i_data;
}
/**
@brief Function set flash status
@param bool i_data - true = on, false = off
@ -268,6 +273,11 @@ void Camera::ReinitCameraModule() {
@return none
*/
void Camera::CapturePhoto() {
if (true == PhotoSending) {
return;
}
if (false == StreamOnOff) {
if (!xSemaphoreTake(frameBufferSemaphore, portMAX_DELAY)) {
log->AddEvent(LogLevel_Error, F("Failed to take frame buffer semaphore"));
@ -313,13 +323,21 @@ void Camera::CapturePhoto() {
if (ControlFlag != 0x00) {
log->AddEvent(LogLevel_Error, "Camera capture failed! photo " + String(ControlFlag, HEX));
FrameBuffer->len = 0;
} else {
log->AddEvent(LogLevel_Info, "Photo OK! " + String(ControlFlag, HEX));
/* generate exif header */
update_exif_from_cfg(imageExifRotation);
get_exif_header(FrameBuffer, &PhotoExifData.header, &PhotoExifData.len);
PhotoExifData.offset = get_jpeg_data_offset(FrameBuffer);
CameraCaptureSuccess = true;
if (PhotoExifData.header != NULL) {
log->AddEvent(LogLevel_Info, "Exif header OK! Len: " + String(PhotoExifData.len));
} else {
log->AddEvent(LogLevel_Error, "Exif header failed! " + String(PhotoExifData.len));
}
}
attempts++;
@ -335,33 +353,6 @@ void Camera::CapturePhoto() {
ledcWrite(FLASH_PWM_CHANNEL, FLASH_OFF_STATUS);
}
xSemaphoreGive(frameBufferSemaphore);
/*
// Save picture
File file = SD_MMC.open("/photo.jpg", FILE_WRITE);
if (file) {
size_t ret = 0;
if (PhotoExifData.header != NULL) {
ret = file.write(PhotoExifData.header, PhotoExifData.len);
if (ret != PhotoExifData.len) {
Serial.println("Failed\nError while writing header to file");
PhotoExifData.offset = 0;
}
} else {
PhotoExifData.offset = 0;
}
ret = file.write(&FrameBuffer->buf[PhotoExifData.offset], FrameBuffer->len - PhotoExifData.offset);
if (ret != FrameBuffer->len - PhotoExifData.offset) {
Serial.println("Failed\nError while writing to file");
} else {
Serial.printf("Saved as %s\n", "photo.jpg");
}
file.close();
} else {
Serial.printf("Failed\nCould not open file: %s\n", "photo.jpg");
}
*/
}
}

View File

@ -64,6 +64,7 @@ private:
uint8_t imageExifRotation; ///< image rotation. 0 degree: value 1, 90 degree: value 6, 180 degree: value 3, 270 degree: value 8
bool CameraCaptureSuccess; ///< camera capture success
bool PhotoSending; ///< photo sending
/* OV2640 camera module pinout and cfg*/
camera_config_t CameraConfig; ///< camera configuration
@ -109,6 +110,7 @@ public:
camera_fb_t *GetPhotoFb();
PhotoExifData_t * GetPhotoExifData();
framesize_t TransformFrameSizeDataType(uint8_t);
void SetPhotoSending(bool);
void SetFlashStatus(bool);
bool GetFlashStatus();

View File

@ -83,6 +83,13 @@ void Configuration::ReadCfg() {
LoadGainCtrl();
LoadAgcGain();
LoadPrusaConnectHostname();
LoadNetworkIpMethod();
LoadNetworkIp();
LoadNetworkMask();
LoadNetworkGateway();
LoadNetworkDns();
LoadCameraImageExifRotation();
LoadTimeLapseFunctionStatus();
Log->AddEvent(LogLevel_Info, "Active WiFi client cfg: " + String(CheckActifeWifiCfgFlag() ? "true" : "false"));
Log->AddEvent(LogLevel_Info, F("Load CFG from EEPROM done"));
}
@ -167,6 +174,7 @@ void Configuration::DefaultCfg() {
SaveNetworkGateway(FACTORY_CFG_NETWORK_STATIC_GATEWAY);
SaveNetworkDns(FACTORY_CFG_NETWORK_STATIC_DNS);
SaveCameraImageExifRotation(FACTORY_CFG_IMAGE_EXIF_ROTATION);
SaveTimeLapseFunctionStatus(FACTORY_CFG_TIMELAPS_ENABLE);
Log->AddEvent(LogLevel_Warning, F("+++++++++++++++++++++++++++"));
}
@ -651,7 +659,6 @@ void Configuration::SaveBasicAuthUsername(String i_data) {
SaveString(EEPROM_ADDR_BASIC_AUTH_USERNAME_START, EEPROM_ADDR_BASIC_AUTH_USERNAME_LENGTH, i_data);
}
/**
@info save password fof BasicAuth to EEPROM
@param string - password
@ -834,6 +841,16 @@ void Configuration::SaveCameraImageExifRotation(uint8_t i_data) {
SaveUint8(EEPROM_ADDR_IMAGE_ROTATION_START, i_data);
}
/**
@info Save time lapse function status
@param bool - value
@return none
*/
void Configuration::SaveTimeLapseFunctionStatus(bool i_data) {
Log->AddEvent(LogLevel_Verbose, "Save time lapse function status: " + String(i_data));
SaveBool(EEPROM_ADDR_TIMELAPS_ENABLE_START, i_data);
}
/**
@info load refresh interval from eeprom
@param none
@ -1311,6 +1328,11 @@ String Configuration::LoadNetworkDns() {
return ret;
}
/**
* @brief Load camera image rotation from EEPROM
*
* @return uint8_t - rotation
*/
uint8_t Configuration::LoadCameraImageExifRotation() {
uint8_t ret = EEPROM.read(EEPROM_ADDR_IMAGE_ROTATION_START);
@ -1324,4 +1346,20 @@ uint8_t Configuration::LoadCameraImageExifRotation() {
return ret;
}
/**
* @brief Load time lapse function status
*
* @return bool - status
*/
bool Configuration::LoadTimeLapseFunctionStatus() {
uint8_t ret = EEPROM.read(EEPROM_ADDR_TIMELAPS_ENABLE_START);
Log->AddEvent(LogLevel_Info, F("Time lapse function status: "), String(ret));
if (ret == 255) {
ret = 0;
}
return (bool) ret;
}
/* EOF */

View File

@ -72,6 +72,7 @@ public:
void SaveNetworkGateway(String);
void SaveNetworkDns(String);
void SaveCameraImageExifRotation(uint8_t);
void SaveTimeLapseFunctionStatus(bool);
uint8_t LoadRefreshInterval();
String LoadToken();
@ -113,6 +114,7 @@ public:
String LoadNetworkGateway();
String LoadNetworkDns();
uint8_t LoadCameraImageExifRotation();
bool LoadTimeLapseFunctionStatus();
private:
Logs *Log; ///< Pointer to Logs object

View File

@ -37,6 +37,7 @@ PrusaConnect::PrusaConnect(Configuration *i_conf, Logs *i_log, Camera *i_camera,
*/
void PrusaConnect::Init() {
log->AddEvent(LogLevel_Info, F("Init PrusaConnect lib"));
BackendReceivedStatus = F("Wait for first connection");
}
/**
@ -51,6 +52,7 @@ void PrusaConnect::LoadCfgFromEeprom() {
Fingerprint = config->LoadFingerprint();
RefreshInterval = config->LoadRefreshInterval();
PrusaConnectHostname = config->LoadPrusaConnectHostname();
EnableTimelapsPhotoSave = config->LoadTimeLapseFunctionStatus();
}
/**
@ -171,7 +173,7 @@ bool PrusaConnect::SendDataToBackend(String *i_data, int i_data_length, String i
/* read response from server */
String response = "";
String fullResponse = "";
log->AddEvent(LogLevel_Verbose, "Response:");
log->AddEvent(LogLevel_Verbose, F("Response:"));
while (client.connected()) {
if (client.available()) {
response = client.readStringUntil('\n');
@ -219,6 +221,7 @@ bool PrusaConnect::SendDataToBackend(String *i_data, int i_data_length, String i
*/
void PrusaConnect::SendPhotoToBackend() {
log->AddEvent(LogLevel_Info, F("Start sending photo to prusaconnect"));
camera->SetPhotoSending(true);
String Photo = "";
size_t total_len = 0;
@ -227,7 +230,8 @@ void PrusaConnect::SendPhotoToBackend() {
} else {
total_len = camera->GetPhotoFb()->len;
}
SendDataToBackend(&Photo, total_len, "image/jpg", "Photo", HOST_URL_CAM_PATH, SendPhoto);
SendDataToBackend(&Photo, total_len, F("image/jpg"), F("Photo"), HOST_URL_CAM_PATH, SendPhoto);
camera->SetPhotoSending(false);
}
/**
@ -258,7 +262,7 @@ void PrusaConnect::SendInfoToBackend() {
serializeJson(json_data, json_string);
log->AddEvent(LogLevel_Info, "Data: " + json_string);
bool response = SendDataToBackend(&json_string, json_string.length(), "application/json", "Info", HOST_URL_INFO_PATH, SendInfo);
bool response = SendDataToBackend(&json_string, json_string.length(), F("application/json"), F("Info"), HOST_URL_INFO_PATH, SendInfo);
if (true == response) {
SendDeviceInformationToBackend = false;
@ -274,11 +278,22 @@ void PrusaConnect::SendInfoToBackend() {
*/
void PrusaConnect::TakePictureAndSendToBackend() {
camera->CapturePhoto();
/* check if photo was captured */
if (camera->GetCameraCaptureSuccess() == true) {
/* send photo to backend */
SendPhotoToBackend();
/* save photo to SD card */
if (false == camera->GetStreamStatus()) {
SavePhotoToSdCard();
}
} else {
log->AddEvent(LogLevel_Error, F("Error capturing photo. Stop sending to backend!"));
}
camera->CaptureReturnFrameBuffer();
}
@ -428,6 +443,51 @@ void PrusaConnect::SetPrusaConnectHostname(String i_data) {
config->SavePrusaConnectHostname(PrusaConnectHostname);
}
/**
* @brief Set time laps photo save status
*
* @param bool - status
*/
void PrusaConnect::SetTimeLapsPhotoSaveStatus(bool i_data) {
EnableTimelapsPhotoSave = i_data;
config->SaveTimeLapseFunctionStatus(EnableTimelapsPhotoSave);
}
/**
@brief Function for saving photo to SD card
@param none
@return none
*/
void PrusaConnect::SavePhotoToSdCard() {
if (EnableTimelapsPhotoSave == true) {
log->AddEvent(LogLevel_Info, F("Save TimeLaps photo to SD card"));
if (false == log->CheckDir(SD_MMC, TIMELAPS_PHOTO_FOLDER)) {
log->AddEvent(LogLevel_Info, F("Create folder for TimeLaps photos"));
log->CreateDir(SD_MMC, TIMELAPS_PHOTO_FOLDER);
}
String FileName = String(TIMELAPS_PHOTO_FOLDER) + "/" + String(TIMELAPS_PHOTO_PREFIX) + "_";
FileName += log->GetSystemTime();
FileName += TIMELAPS_PHOTO_SUFFIX;
log->AddEvent(LogLevel_Verbose, "Saving file: " + FileName);
if (camera->GetPhotoExifData()->header != NULL) {
if (log->WritePicture(FileName, camera->GetPhotoFb()->buf + camera->GetPhotoExifData()->offset, camera->GetPhotoFb()->len - camera->GetPhotoExifData()->offset, camera->GetPhotoExifData()->header, camera->GetPhotoExifData()->len) == true) {
log->AddEvent(LogLevel_Info, F("Photo saved to SD card. EXIF"));
} else {
log->AddEvent(LogLevel_Error, F("Error saving photo to SD card. EXIF"));
}
} else {
if (log->WritePicture(FileName, camera->GetPhotoFb()->buf, camera->GetPhotoFb()->len) == true) {
log->AddEvent(LogLevel_Info, F("Photo saved to SD card"));
} else {
log->AddEvent(LogLevel_Error, F("Error saving photo to SD card"));
}
}
}
}
/**
* @brief Get refresh interval
*
@ -512,6 +572,10 @@ String PrusaConnect::CovertBackendAvailabilitStatusToString(BackendAvailabilitSt
return ret;
}
bool PrusaConnect::GetTimeLapsPhotoSaveStatus() {
return EnableTimelapsPhotoSave;
}
/**
* @brief Increase sending interval counter
*

View File

@ -55,6 +55,7 @@ private:
BackendAvailabilitStatus BackendAvailability; ///< status of backend availability
bool SendDeviceInformationToBackend; ///< flag for sending device information to backend
uint8_t SendingIntervalCounter; ///< counter for sending interval, represents seconds
bool EnableTimelapsPhotoSave; ///< flag for saving photo to SD card
String Token; ///< token for backend communication
String Fingerprint; ///< fingerprint for backend communication
@ -86,6 +87,9 @@ public:
void SetToken(String);
void SetBackendAvailabilitStatus(BackendAvailabilitStatus);
void SetPrusaConnectHostname(String);
void SetTimeLapsPhotoSaveStatus(bool);
void SavePhotoToSdCard();
uint8_t GetRefreshInterval();
String GetBackendReceivedStatus();
@ -94,6 +98,7 @@ public:
String GetPrusaConnectHostname();
BackendAvailabilitStatus GetBackendAvailabilitStatus();
String CovertBackendAvailabilitStatusToString(BackendAvailabilitStatus);
bool GetTimeLapsPhotoSaveStatus();
void IncreaseSendingIntervalCounter();
void SetSendingIntervalCounter(uint8_t);

View File

@ -101,13 +101,7 @@ void Logs::Init() {
if (true == GetCardDetectedStatus()) {
/* check maximum log file size */
uint32_t FileSize = GetFileSize(SD_MMC, FilePath + FileName);
Serial.printf("Log file size: %d\n", FileSize);
if (FileSize >= LOGS_FILE_MAX_SIZE) {
uint16_t file_count = FileCount(SD_MMC, FilePath, FileName);
Serial.printf("Maximum log file size.\nFile count: %d\n", file_count);
RenameFile(SD_MMC, FilePath + FileName, FilePath + FileName + String(file_count));
}
CheckMaxLogFileSize();
/* added first message to log file after start MCU */
String msg = F("----------------------------------------------------------------\n");
@ -169,6 +163,30 @@ void Logs::AddEvent(LogLevel_enum level, String msg, bool newLine, bool date) {
#endif
}
void Logs::AddEvent(LogLevel_enum level, const __FlashStringHelper *msg, String parameters, bool newLine, bool date) {
if (LogLevel >= level) {
String LogMsg = "";
if (true == date) {
LogMsg += GetSystemTime();
LogMsg += " - ";
}
LogMsg += msg;
LogMsg += parameters;
if (true == newLine) {
LogMsg += "\n";
}
AppendFile(SD_MMC, FilePath + FileName, LogMsg);
Serial.print(LogMsg);
}
#if (true == CONSOLE_VERBOSE_DEBUG)
else {
Serial.println(msg);
}
#endif
}
/**
@info Set file name
@param String - file name
@ -242,6 +260,21 @@ bool Logs::GetNtpTimeSynced() {
return NtpTimeSynced;
}
/**
@info Check maximum log file size
@param none
@return none
*/
void Logs::CheckMaxLogFileSize() {
uint32_t FileSize = GetFileSize(SD_MMC, FilePath + FileName);
Serial.printf("Log file size: %d bytes\n", FileSize);
if (FileSize >= LOGS_FILE_MAX_SIZE) {
uint16_t file_count = FileCount(SD_MMC, FilePath, FileName);
Serial.printf("Maximum log file size.\nFile count: %d\n", file_count);
RenameFile(SD_MMC, FilePath + FileName, FilePath + FileName + String(file_count));
}
}
/**
@info Get system time
@param none

View File

@ -43,6 +43,7 @@ public:
void Init();
void AddEvent(LogLevel_enum, String, bool = true, bool = true);
void AddEvent(LogLevel_enum, const __FlashStringHelper*, String, bool = true, bool = true);
void SetLogLevel(LogLevel_enum);
void SetFileName(String);
void SetFilePath(String);
@ -53,8 +54,8 @@ public:
String GetFilePath();
LogLevel_enum GetLogLevel();
bool GetNtpTimeSynced();
void CheckMaxLogFileSize();
protected:
String GetSystemTime();
};

View File

@ -14,7 +14,7 @@
#define _MCU_CFG_H_
/* ---------------- BASIC MCU CFG --------------*/
#define SW_VERSION "1.0.2-rc3" ///< SW version
#define SW_VERSION "1.0.3-rc1" ///< SW version
#define SW_BUILD __DATE__ " " __TIME__ ///< build number
#define CONSOLE_VERBOSE_DEBUG false ///< enable/disable verbose debug log level for console
#define DEVICE_HOSTNAME "Prusa-ESP32cam" ///< device hostname
@ -50,12 +50,13 @@
#define TASK_STREAM_TELEMETRY 30000 ///< stream telemetry task interval [ms]
#define TASK_WIFI_WATCHDOG 20000 ///< wifi watchdog task interval [ms]
#define TASK_PHOTO_SEND 1000 ///< photo send task interval [ms]
#define TASK_SDCARD_FILE_REMOVE 30000 ///< sd card file remove task interval [ms]
/* --------------- WEB SERVER CFG --------------*/
#define WEB_SERVER_PORT 80 ///< WEB server port
#define SERIAL_PORT_SPEED 115200 ///< baud rate
#define WDG_TIMEOUT 40 ///< wdg timeout [second]
#define PHOTO_FRAGMENT_SIZE 1024 ///< photo fragmentation size [bytes]
#define PHOTO_FRAGMENT_SIZE 2048 ///< photo fragmentation size [bytes]
#define LOOP_DELAY 100 ///< loop delay [ms]
#define WIFI_CLIENT_WAIT_CON false ///< wait for connecting to WiFi network
#define DYNMIC_JSON_SIZE 1024 ///< maximum size for dynamic json [bytes]
@ -63,8 +64,8 @@
/* --------------- OTA UPDATE CFG --------------*/
#define OTA_UPDATE_API_SERVER "api.github.com" ///< OTA update server URL
#define OTA_UPDATE_API_URL "/repos/prusa3d/Prusa-Firmware-ESP32-Cam/releases/latest" ///< path to file with OTA update
#define OTA_UPDATE_FW_FILE "ESP32_PrusaConnectCam.ino.bin" ///< OTA update firmware file name
#define OTA_UPDATE_API_URL F("/repos/prusa3d/Prusa-Firmware-ESP32-Cam/releases/latest") ///< path to file with OTA update
#define OTA_UPDATE_FW_FILE PSTR("ESP32_PrusaConnectCam.ino.bin") ///< OTA update firmware file name
/* ---------- RESET CFG CONFIGURATION ----------*/
#define CFG_RESET_PIN 12 ///< GPIO 16 is for reset CFG to default
@ -74,18 +75,19 @@
/* ---------------- MicroSD Logs ----------------*/
#define LOGS_FILE_NAME "SysLog.log" ///< syslog file name
#define LOGS_FILE_PATH "/" ///< directory for log files
#define LOGS_FILE_MAX_SIZE 1024 ///< maximum file size in the [kb]
#define LOGS_FILE_MAX_SIZE 512 ///< maximum file size in the [kb]
#define FILE_REMOVE_MAX_COUNT 5 ///< maximum count for remove files from sd card
/* ---------------- AP MODE CFG ----------------*/
#define STA_AP_MODE_TIMEOUT 300000 ///< how long is AP enable after start, when is module in the STA mode [ms]
#define SERVICE_WIFI_SSID_UID true ///< enable/disable added UID to service SSID name
#define SERVICE_WIFI_SSID "ESP32_camera" ///< service WI-FI SSID name. Maximum length SERVICE_WIFI_SSID + UID = 32
#define SERVICE_WIFI_PASS "12345678" ///< service WI-FI password
#define SERVICE_WIFI_SSID F("ESP32_camera") ///< service WI-FI SSID name. Maximum length SERVICE_WIFI_SSID + UID = 32
#define SERVICE_WIFI_PASS F("12345678") ///< service WI-FI password
#define SERVICE_WIFI_CHANNEL 10 ///< service WI-FI channel
#define SERVICE_LOCAL_IP "192.168.0.1" ///< service WI-FI module IP address
#define SERVICE_LOCAL_GATEWAY "192.168.0.1" ///< service WI-FI module gateway
#define SERVICE_LOCAL_MASK "255.255.255.0" ///< service WI-FI module mask
#define SERVICE_LOCAL_DNS "192.168.0.1" ///< service WI-FI module DNS
#define SERVICE_LOCAL_IP F("192.168.0.1") ///< service WI-FI module IP address
#define SERVICE_LOCAL_GATEWAY F("192.168.0.1") ///< service WI-FI module gateway
#define SERVICE_LOCAL_MASK F("255.255.255.0") ///< service WI-FI module mask
#define SERVICE_LOCAL_DNS F("192.168.0.1") ///< service WI-FI module DNS
/* ----------------- IPv4 CFG -------------------*/
#define IPV4_ADDR_MAX_LENGTH 15 ///< maximum length for IPv4 address
@ -104,6 +106,11 @@
#define CAMERA_MODEL "OV2640" ///< Camera model string
#define CAMERA_SOFTWARE "Prusa ESP32-cam" ///< Camera software string
/* ---------------- TIMELAPS CFG ----------------*/
#define TIMELAPS_PHOTO_FOLDER "/timelaps" ///< folder for timelaps photos
#define TIMELAPS_PHOTO_PREFIX "photo" ///< photo name for timelaps
#define TIMELAPS_PHOTO_SUFFIX ".jpg" ///< photo file type for timelaps
/* ---------------- FACTORY CFG ----------------*/
#define FACTORY_CFG_PHOTO_REFRESH_INTERVAL 30 ///< in the second
#define FACTORY_CFG_PHOTO_QUALITY 10 ///< 10-63, lower is better
@ -121,25 +128,26 @@
#define FACTORY_CFG_BPC 1 ///< bad pixel detection
#define FACTORY_CFG_WPC 1 ///< white pixel correction
#define FACTORY_CFG_RAW_GAMA 1 ///< raw gama
#define FACTORY_CFG_WEB_AUTH_USERNAME "admin" ///< user name for login to WEB interface. definition WEB_ENABLE_BASIC_AUTH must be true
#define FACTORY_CFG_WEB_AUTH_PASSWORD "admin" ///< password for login to WEB interface. definition WEB_ENABLE_BASIC_AUTH must be true
#define FACTORY_CFG_WEB_AUTH_USERNAME F("admin") ///< user name for login to WEB interface. definition WEB_ENABLE_BASIC_AUTH must be true
#define FACTORY_CFG_WEB_AUTH_PASSWORD F("admin") ///< password for login to WEB interface. definition WEB_ENABLE_BASIC_AUTH must be true
#define FACTORY_CFG_WEB_AUTH_ENABLE false ///< enable web auth for login to WEB interface. definition WEB_ENABLE_BASIC_AUTH must be
#define FACTORY_CFG_CAMERA_FLASH_ENABLE false ///< enable camera flash functionality
#define FACTORY_CFG_CAMERA_FLASH_TIME 200 ///< time for camera flash duration time [ms]
#define FACTORY_CFG_MDNS_RECORD_HOST "prusa-esp32cam" ///< mdns record http://MDNS_RECORD_HOST.local
#define FACTORY_CFG_MDNS_RECORD_HOST F("prusa-esp32cam") ///< mdns record http://MDNS_RECORD_HOST.local
#define FACTORY_CFG_AEC2 0 ///< enable automatic exposition
#define FACTORY_CFG_AE_LEVEL 0 ///< automatic exposition level
#define FACTORY_CFG_AEC_VALUE 300 ///< automatic exposition time
#define FACTORY_CFG_GAIN_CTRL 1 ///< enable automatic gain
#define FACTORY_CFG_AGC_GAIN 0 ///< automatic gain controll gain
#define FACTORY_CFG_HOSTNAME "connect.prusa3d.com" ///< hostname for Prusa Connect
#define FACTORY_CFG_HOSTNAME F("connect.prusa3d.com") ///< hostname for Prusa Connect
#define FACTORY_CFG_ENABLE_SERVICE_AP 1 ///< enable service AP mode
#define FACTORY_CFG_NETWORK_IP_METHOD 0 ///< 0 - DHCP, 1 - Static IP
#define FACTORY_CFG_NETWORK_STATIC_IP "255.255.255.255" ///< Static IP address
#define FACTORY_CFG_NETWORK_STATIC_MASK "255.255.255.255" ///< Static Mask
#define FACTORY_CFG_NETWORK_STATIC_GATEWAY "255.255.255.255" ///< Static Gateway
#define FACTORY_CFG_NETWORK_STATIC_DNS "255.255.255.255" ///< Static DNS
#define FACTORY_CFG_NETWORK_STATIC_IP F("255.255.255.255") ///< Static IP address
#define FACTORY_CFG_NETWORK_STATIC_MASK F("255.255.255.255") ///< Static Mask
#define FACTORY_CFG_NETWORK_STATIC_GATEWAY F("255.255.255.255") ///< Static Gateway
#define FACTORY_CFG_NETWORK_STATIC_DNS F("255.255.255.255") ///< Static DNS
#define FACTORY_CFG_IMAGE_EXIF_ROTATION 1 ///< Image rotation 1 - 0°, 6 - 90°, 3 - 180°, 8 - 270°
#define FACTORY_CFG_TIMELAPS_ENABLE 0 ///< enable timelaps functionality
/* ---------------- CFG FLAGS ------------------*/
#define CFG_WIFI_SETTINGS_SAVED 0x0A ///< flag saved config
@ -275,6 +283,9 @@
#define EEPROM_ADDR_IMAGE_ROTATION_START (EEPROM_ADDR_NETWORK_STATIC_DNS_START + EEPROM_ADDR_NETWORK_STATIC_DNS_LENGTH)
#define EEPROM_ADDR_IMAGE_ROTATION_LENGTH 1
#define EEPROM_ADDR_TIMELAPS_ENABLE_START (EEPROM_ADDR_IMAGE_ROTATION_START + EEPROM_ADDR_IMAGE_ROTATION_LENGTH)
#define EEPROM_ADDR_TIMELAPS_ENABLE_LENGTH 1
#define EEPROM_SIZE (EEPROM_ADDR_REFRESH_INTERVAL_LENGTH + EEPROM_ADDR_FINGERPRINT_LENGTH + EEPROM_ADDR_TOKEN_LENGTH + \
EEPROM_ADDR_FRAMESIZE_LENGTH + EEPROM_ADDR_BRIGHTNESS_LENGTH + EEPROM_ADDR_CONTRAST_LENGTH + \
EEPROM_ADDR_SATURATION_LENGTH + EEPROM_ADDR_HMIRROR_LENGTH + EEPROM_ADDR_VFLIP_LENGTH + \
@ -289,7 +300,7 @@
EEPROM_ADDR_AEC_VALUE_LENGTH + EEPROM_ADDR_GAIN_CTRL_LENGTH + EEPROM_ADDR_AGC_GAIN_LENGTH + EEPROM_ADDR_LOG_LEVEL_LENGTH + \
EEPROM_ADDR_HOSTNAME_LENGTH + EEPROM_ADDR_SERVICE_AP_ENABLE_LENGTH + EEPROM_ADDR_NETWORK_IP_METHOD_LENGTH +\
EEPROM_ADDR_NETWORK_STATIC_IP_LENGTH + EEPROM_ADDR_NETWORK_STATIC_MASK_LENGTH + EEPROM_ADDR_NETWORK_STATIC_GATEWAY_LENGTH + \
EEPROM_ADDR_NETWORK_STATIC_DNS_LENGTH + EEPROM_ADDR_IMAGE_ROTATION_LENGTH) ///< how many bits do we need for eeprom memory
EEPROM_ADDR_NETWORK_STATIC_DNS_LENGTH + EEPROM_ADDR_IMAGE_ROTATION_LENGTH + EEPROM_ADDR_TIMELAPS_ENABLE_LENGTH) ///< how many bits do we need for eeprom memory
#endif

View File

@ -18,7 +18,6 @@
*/
MicroSd::MicroSd() {
CardDetected = false;
CardSize = 0;
DetectAfterBoot = false;
}
@ -50,7 +49,7 @@ void MicroSd::InitSdCard() {
if (!SD_MMC.begin("/sdcard", true)) {
Serial.println(F("SD Card Mount Failed"));
CardDetected = false;
CardSize = 0;
CardSizeMB = 0;
//DetectAfterBoot = false;
return;
}
@ -60,7 +59,7 @@ void MicroSd::InitSdCard() {
if (cardType == CARD_NONE) {
Serial.println(F("No SD_MMC card attached"));
CardDetected = false;
CardSize = 0;
CardSizeMB = 0;
//DetectAfterBoot = false;
return;
}
@ -77,11 +76,11 @@ void MicroSd::InitSdCard() {
Serial.print(F("UNKNOWN"));
}
/* calculation card size */
CardSize = SD_MMC.cardSize() / (1024 * 1024);
Serial.printf(", Card Size: %d MB\n", CardSize);
CardDetected = true;
DetectAfterBoot = true;
/* calculation card size */
CheckCardUsedStatus();
}
/**
@ -124,6 +123,27 @@ void MicroSd::ListDir(fs::FS &fs, String DirName, uint8_t levels) {
}
}
/**
@brief Check directory on the micro SD card
@param fs::FS - card
@param String - dir name
@return bool - status
*/
bool MicroSd::CheckDir(fs::FS &fs, String path) {
bool status = false;
if (true == CardDetected) {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.printf("Checking Dir: %s... ", path.c_str());
#endif
if (fs.exists(path.c_str())) {
status = true;
}
}
return status;
}
/**
@brief List directory on the micro SD card
@param fs::FS - card
@ -389,6 +409,140 @@ uint16_t MicroSd::FileCount(fs::FS &fs, String DirName, String FileName) {
return FileCount;
}
/**
@brief Remove files in directory
@param fs::FS - card
@param String - dir name
@param int - max files
@return bool - status
*/
bool MicroSd::RemoveFilesInDir(fs::FS &fs, String path, int maxFiles) {
bool ret = false;
File dir = fs.open(path.c_str());
if (!dir) {
return ret;
}
int fileCount = 0;
File file = dir.openNextFile();
while (file) {
ret = true;
String fileName = path + "/" + file.name();
fs.remove(fileName.c_str());
Serial.printf("Removing file: %s\n", fileName.c_str());
fileCount++;
if (fileCount >= maxFiles) {
break;
}
file = dir.openNextFile();
}
return ret;
}
int MicroSd::CountFilesInDir(fs::FS &fs, String path) {
uint16_t file_count = FileCount(fs, path, "");
return file_count;
}
/**
@brief Check card used status
@param none
@return bool - status
*/
bool MicroSd::CheckCardUsedStatus() {
CardSizeMB = SD_MMC.cardSize() / (1024 * 1024);
CardTotalMB = SD_MMC.totalBytes() / (1024 * 1024);
CardUsedMB = SD_MMC.usedBytes() / (1024 * 1024);
CardFreeMB = CardSizeMB - CardUsedMB;
FreeSpacePercent = (CardFreeMB * 100) / CardSizeMB;
UsedSpacePercent = 100 - FreeSpacePercent;
Serial.printf("Card size: %d MB, Total: %d MB, Used: %d MB, Free: %d GB, Free: %d %% \n", CardSizeMB, CardTotalMB, CardUsedMB, CardFreeMB, FreeSpacePercent);
return true;
}
/**
@brief Write picture to the SD card
@param fs::FS - card
@param String - file name
@return String - data
*/
bool MicroSd::WritePicture(String i_PhotoName, uint8_t *i_PhotoData, size_t i_PhotoLen) {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.println(f("WritePicture"));
#endif
bool ret_stat = false;
File file = SD_MMC.open(i_PhotoName, FILE_WRITE);
if (file) {
size_t ret = 0;
ret = file.write(i_PhotoData, i_PhotoLen);
if (ret != i_PhotoLen) {
Serial.println(F("Failed. Error while writing to file"));
} else {
Serial.printf("Saved as %s\n", i_PhotoName.c_str());
ret_stat = true;
}
file.close();
} else {
Serial.printf("Failed. Could not open file: %s\n", i_PhotoName.c_str());
}
return ret_stat;
}
/**
@brief Write picture to the SD card with EXIF data
@param fs::FS - card
@param String - file name
@param uint8_t - data
@param size_t - data length
@param const uint8_t - EXIF data
@param size_t - EXIF data length
@return bool - status
*/
bool MicroSd::WritePicture(String i_PhotoName, uint8_t *i_PhotoData, size_t i_PhotoLen, const uint8_t *i_PtohoExif, size_t i_PhotoExifLen) {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.println(F("WritePicture EXIF"));
#endif
bool ret_stat = false;
File file = SD_MMC.open(i_PhotoName, FILE_WRITE);
if (file) {
size_t ret = 0;
ret = file.write(i_PtohoExif, i_PhotoExifLen);
ret += file.write(i_PhotoData, i_PhotoLen);
if (ret != (i_PhotoLen + i_PhotoExifLen)) {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.println(F("Failed. Error while writing to file"));
#endif
ret_stat = false;
} else {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.printf("Saved as %s\n", i_PhotoName.c_str());
#endif
ret_stat = true;
}
file.close();
} else {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.printf("Failed. Could not open file: %s\n", i_PhotoName.c_str());
#endif
ret_stat = false;
}
return ret_stat;
}
/**
@brief Get card detected status
@param none
@ -403,8 +557,8 @@ bool MicroSd::GetCardDetectedStatus() {
@param none
@return uint16_t - size
*/
uint16_t MicroSd::GetCardSize() {
return CardSize;
uint16_t MicroSd::GetCardSizeMB() {
return CardSizeMB;
}
/**
@ -416,4 +570,49 @@ bool MicroSd::GetCardDetectAfterBoot() {
return DetectAfterBoot;
}
/**
@brief Get card total MB
@param none
@return uint16_t - size
*/
uint16_t MicroSd::GetCardTotalMB() {
return CardTotalMB;
}
/**
@brief Get card used MB
@param none
@return uint16_t - size
*/
uint16_t MicroSd::GetCardUsedMB() {
return CardUsedMB;
}
/**
@brief Get card free MB
@param none
@return uint16_t - size
*/
uint16_t MicroSd::GetCardFreeMB() {
return CardFreeMB;
}
/**
@brief Get free space percent
@param none
@return uint8_t - percent
*/
uint8_t MicroSd::GetFreeSpacePercent() {
return FreeSpacePercent;
}
/**
@brief Get used space percent
@param none
@return uint8_t - percent
*/
uint8_t MicroSd::GetUsedSpacePercent() {
return UsedSpacePercent;
}
/* EOF */

View File

@ -34,8 +34,13 @@
class MicroSd {
private:
bool CardDetected; ///< Card detected status
uint16_t CardSize; ///< Card size
bool DetectAfterBoot; ///< Card detect after boot
uint32_t CardSizeMB; ///< Card size
uint32_t CardTotalMB;
uint32_t CardUsedMB;
uint32_t CardFreeMB;
uint8_t FreeSpacePercent;
uint8_t UsedSpacePercent;
public:
MicroSd();
@ -45,6 +50,7 @@ public:
void ReinitCard();
void ListDir(fs::FS &, String, uint8_t);
bool CheckDir(fs::FS &, String);
bool CreateDir(fs::FS &, String);
bool RemoveDir(fs::FS &, String);
void ReadFileConsole(fs::FS &, String);
@ -54,10 +60,23 @@ public:
bool DeleteFile(fs::FS &, String);
uint32_t GetFileSize(fs::FS &, String);
uint16_t FileCount(fs::FS &, String, String);
bool RemoveFilesInDir(fs::FS &, String, int );
int CountFilesInDir(fs::FS &, String );
bool WritePicture(String, uint8_t *, size_t);
bool WritePicture(String, uint8_t *, size_t, const uint8_t *, size_t);
bool CheckCardUsedStatus();
bool GetCardDetectedStatus();
uint16_t GetCardSize();
bool GetCardDetectAfterBoot();
uint16_t GetCardSizeMB();
uint16_t GetCardTotalMB();
uint16_t GetCardUsedMB();
uint16_t GetCardFreeMB();
uint32_t GetFreeSpaceMB();
uint8_t GetFreeSpacePercent();
uint8_t GetUsedSpacePercent();
};
#endif

View File

@ -44,6 +44,7 @@ void Server_InitWebServer() {
request->send_P(404, "text/plain", "Photo not found!");
return;
}
SystemCamera.SetPhotoSending(true);
SystemLog.AddEvent(LogLevel_Verbose, "Photo size: " + String(SystemCamera.GetPhotoFb()->len) + " bytes");
@ -62,6 +63,8 @@ void Server_InitWebServer() {
SystemLog.AddEvent(LogLevel_Verbose, F("Send photo without EXIF data"));
request->send_P(200, "image/jpg", SystemCamera.GetPhotoFb()->buf, SystemCamera.GetPhotoFb()->len);
}
SystemCamera.SetPhotoSending(false);
});
/* route to jquery */
@ -434,6 +437,17 @@ void Server_InitWebServer_Actions() {
delay(100); /* wait for sending data */
ESP.restart();
});
/* route for change LED status */
server.on("/action_sderase", HTTP_GET, [](AsyncWebServerRequest* request) {
SystemLog.AddEvent(LogLevel_Verbose, F("WEB server: /action_sderase remove files from SD card"));
if (Server_CheckBasicAuth(request) == false)
return;
StartRemoveSdCard = 1;
request->send_P(200, F("text/plain"), "Starting remove files from SD card");
});
}
/**
@ -680,6 +694,12 @@ void Server_InitWebServer_Sets() {
response = true;
}
if (request->hasParam("timelaps_enable")) {
SystemLog.AddEvent(LogLevel_Verbose, F("Set timelaps enable"));
Connect.SetTimeLapsPhotoSaveStatus(Server_TransfeStringToBool(request->getParam("timelaps_enable")->value()));
response = true;
}
if (true == response) {
request->send_P(200, F("text/html"), MSG_SAVE_OK);
}
@ -1019,14 +1039,14 @@ void Server_handleNotFound(AsyncWebServerRequest* request) {
String message = "URL not Found\n\n";
message += "URI: " + request->url() + "\nMethod: ";
message += (request->method() == HTTP_GET) ? "GET" : "POST";
message += (request->method() == HTTP_GET) ? F("GET") : F("POST");
message += "\nArguments: " + String(request->args()) + "\n";
for (uint8_t i = 0; i < request->args(); i++) {
message += " " + request->argName(i) + ": " + request->arg(i) + "\n";
}
request->send(404, "text/plain", message);
request->send(404, F("text/plain"), message);
}
/**
@ -1048,23 +1068,23 @@ String Server_GetJsonData() {
doc_json["brightness"] = String(SystemCamera.GetBrightness());
doc_json["contrast"] = String(SystemCamera.GetContrast());
doc_json["saturation"] = String(SystemCamera.GetSaturation());
doc_json["hmirror"] = (SystemCamera.GetHMirror() == true) ? "true" : "";
doc_json["vflip"] = (SystemCamera.GetVFlip() == true) ? "true" : "";
doc_json["lensc"] = (SystemCamera.GetLensC() == true) ? "true" : "";
doc_json["exposure_ctrl"] = (SystemCamera.GetExposureCtrl() == true) ? "true" : "";
doc_json["awb"] = (SystemCamera.GetAwb() == true) ? "true" : "";
doc_json["awb_gain"] = (SystemCamera.GetAwbGain() == true) ? "true" : "";
doc_json["hmirror"] = Server_TranslateBoolToString(SystemCamera.GetHMirror());
doc_json["vflip"] = Server_TranslateBoolToString(SystemCamera.GetVFlip());
doc_json["lensc"] = Server_TranslateBoolToString(SystemCamera.GetLensC());
doc_json["exposure_ctrl"] = Server_TranslateBoolToString(SystemCamera.GetExposureCtrl());
doc_json["awb"] = Server_TranslateBoolToString(SystemCamera.GetAwb());
doc_json["awb_gain"] = Server_TranslateBoolToString(SystemCamera.GetAwbGain());
doc_json["wb_mode"] = String(SystemCamera.GetAwbMode());
doc_json["bpc"] = (SystemCamera.GetBpc() == true) ? "true" : "";
doc_json["wpc"] = (SystemCamera.GetWpc() == true) ? "true" : "";
doc_json["raw_gama"] = (SystemCamera.GetRawGama() == true) ? "true" : "";
doc_json["aec2"] = (SystemCamera.GetAec2() == true) ? "true" : "";
doc_json["bpc"] = Server_TranslateBoolToString(SystemCamera.GetBpc());
doc_json["wpc"] = Server_TranslateBoolToString(SystemCamera.GetWpc());
doc_json["raw_gama"] = Server_TranslateBoolToString(SystemCamera.GetRawGama());
doc_json["aec2"] = Server_TranslateBoolToString(SystemCamera.GetAec2());
doc_json["ae_level"] = SystemCamera.GetAeLevel();
doc_json["aec_value"] = SystemCamera.GetAecValue();
doc_json["gain_ctrl"] = (SystemCamera.GetGainCtrl() == true) ? "true" : "";
doc_json["gain_ctrl"] = Server_TranslateBoolToString(SystemCamera.GetGainCtrl());
doc_json["agc_gain"] = SystemCamera.GetAgcGaint();
doc_json["led"] = (SystemCamera.GetFlashStatus() == true) ? "true" : "";
doc_json["flash"] = (SystemCamera.GetCameraFlashEnable() == true) ? "true" : "";
doc_json["led"] = Server_TranslateBoolToString(SystemCamera.GetFlashStatus());
doc_json["flash"] = Server_TranslateBoolToString(SystemCamera.GetCameraFlashEnable());
doc_json["flash_time"] = SystemCamera.GetCameraFlashTime();
doc_json["ssid"] = SystemWifiMngt.GetStaSsid();
doc_json["bssid"] = SystemWifiMngt.GetStaBssid();
@ -1075,8 +1095,8 @@ String Server_GetJsonData() {
doc_json["wifi_mode"] = SystemWifiMngt.GetWiFiMode();
doc_json["mdns"] = SystemWifiMngt.GetMdns();
doc_json["service_ap_ssid"] = SystemWifiMngt.GetServiceApSsid();
doc_json["serviceap"] = (SystemWifiMngt.GetEnableServiceAp() == true) ? "true" : "";
doc_json["auth"] = (WebBasicAuth.EnableAuth == true) ? "true" : "";
doc_json["serviceap"] = Server_TranslateBoolToString(SystemWifiMngt.GetEnableServiceAp());
doc_json["auth"] = Server_TranslateBoolToString(WebBasicAuth.EnableAuth);
doc_json["auth_username"] = WebBasicAuth.UserName;
doc_json["last_upload_status"] = Connect.GetBackendReceivedStatus();
doc_json["wifi_network_status"] = SystemWifiMngt.GetStaStatus();
@ -1090,6 +1110,11 @@ String Server_GetJsonData() {
doc_json["net_gw"] = SystemWifiMngt.GetNetStaticGateway();
doc_json["net_dns"] = SystemWifiMngt.GetNetStaticDns();
doc_json["image_rotation"] = SystemCamera.GetCameraImageRotation();
doc_json["timelaps"] = Server_TranslateBoolToString(Connect.GetTimeLapsPhotoSaveStatus());
doc_json["sd_status"] = (SystemLog.GetCardDetectedStatus() == true) ? F("Card detected") : F("No card detected");
doc_json["sd_total"] = SystemLog.GetCardSizeMB();
doc_json["sd_free_p"] = SystemLog.GetFreeSpacePercent();
doc_json["sd_used_p"] = SystemLog.GetUsedSpacePercent();
doc_json["sw_build"] = SW_BUILD;
doc_json["sw_ver"] = SW_VERSION;
doc_json["sw_new_ver"] = FirmwareUpdate.NewVersionFw;
@ -1185,4 +1210,15 @@ bool Server_TransfeStringToBool(String data) {
return ret;
}
String Server_TranslateBoolToString(bool i_data) {
String ret = "";
if (true == i_data) {
ret = "true";
} else {
ret = "";
}
return ret;
}
/* EOF */

View File

@ -65,6 +65,8 @@ void Server_streamJpg(AsyncWebServerRequest *);
void Server_GetModuleUptime(String &);
bool Server_TransfeStringToBool(String);
String Server_TranslateBoolToString(bool);
#endif
/* EOF */

View File

@ -117,14 +117,14 @@ void System_CheckNewVersion() {
WiFiClientSecure client;
client.setCACert(root_CAs_ota);
//client.setInsecure();
FirmwareUpdate.CheckNewVersionFwStatus = "N/A";
FirmwareUpdate.NewVersionFw = "Unknown";
FirmwareUpdate.CheckNewVersionFwStatus = F("N/A");
FirmwareUpdate.NewVersionFw = F("Unknown");
FirmwareUpdate.OtaUpdateFwUrl = "";
FirmwareUpdate.OtaUpdateFwAvailable = false;
/* connect to server and get json */
if (!client.connect("api.github.com", 443)) {
FirmwareUpdate.CheckNewVersionFwStatus = "Failed connect to OTA server!";
if (!client.connect(OTA_UPDATE_API_SERVER, 443)) {
FirmwareUpdate.CheckNewVersionFwStatus = F("Failed connect to OTA server!");
SystemLog.AddEvent(LogLevel_Info, FirmwareUpdate.CheckNewVersionFwStatus);
} else {
@ -181,7 +181,7 @@ void System_CheckNewVersion() {
SystemLog.AddEvent(LogLevel_Info, "Assets[" + String(i) + "]: " + String(name));
/* get FW file and URL */
if (strcmp(name, OTA_UPDATE_FW_FILE) == 0) {
if (strcmp_P(name, OTA_UPDATE_FW_FILE) == 0) {
/* get download URL */
const char* download_url = asset["browser_download_url"];
FirmwareUpdate.OtaUpdateFwUrl = download_url;
@ -544,6 +544,19 @@ void System_TaskSdCardCheck(void *pvParameters) {
SystemLog.ReinitCard();
SystemLog.AddEvent(LogLevel_Warning, F("Reinit micro SD card done!"));
}
/* check card free space */
if (true == SystemLog.GetCardDetectedStatus()) {
SystemLog.AddEvent(LogLevel_Verbose, "Check card free space");
SystemLog.CheckCardUsedStatus();
}
/* check maximum log file size */
if (true == SystemLog.GetCardDetectedStatus()) {
SystemLog.AddEvent(LogLevel_Verbose, "Check maximum log file size");
SystemLog.CheckMaxLogFileSize();
}
SystemLog.AddEvent(LogLevel_Verbose, "MicroSdCard task. Stack free size: " + String(uxTaskGetStackHighWaterMark(NULL)) + " bytes");
/* reset wdg */
@ -649,4 +662,44 @@ void System_TaskWiFiWatchdog(void *pvParameters) {
}
}
/**
* @brief Function for micro SD card remove files task
*
* @param void *pvParameters
* @return none
*/
void System_TaskSdCardRemove(void *pvParameters) {
SystemLog.AddEvent(LogLevel_Info, "TaskSdCardRemove. core: " + String(xPortGetCoreID()));
TickType_t xLastWakeTime = xTaskGetTickCount();
SdCardRemoveTime = TASK_SDCARD_FILE_REMOVE;
while (1) {
esp_task_wdt_reset();
if (0 != StartRemoveSdCard) {
if (1 == StartRemoveSdCard) {
SdCardRemoveTime = 5000;
SystemLog.AddEvent(LogLevel_Info, "Start remove timelaps photo");
uint16_t file_count = SystemLog.CountFilesInDir(SD_MMC, TIMELAPS_PHOTO_FOLDER);
SystemLog.AddEvent(LogLevel_Info, "Files in dir: " + String(file_count));
esp_task_wdt_reset();
StartRemoveSdCard = 2;
}
if ( false == SystemLog.RemoveFilesInDir(SD_MMC, TIMELAPS_PHOTO_FOLDER, FILE_REMOVE_MAX_COUNT)) {
SystemLog.AddEvent(LogLevel_Info, "Remove files in dir done");
StartRemoveSdCard = 0;
SdCardRemoveTime = TASK_SDCARD_FILE_REMOVE;
}
}
SystemLog.AddEvent(LogLevel_Verbose, "MicroSdCard task. Stack free size: " + String(uxTaskGetStackHighWaterMark(NULL)) + " bytes");
/* reset wdg */
esp_task_wdt_reset();
/* next start task */
vTaskDelayUntil(&xLastWakeTime, SdCardRemoveTime / portTICK_PERIOD_MS);
}
}
/* EOF */

View File

@ -33,10 +33,10 @@
#include "serial_cfg.h"
#include "sys_led.h"
#define SYSTEM_MSG_UPDATE_DONE "FW update successfully done! Please reboot the MCU."
#define SYSTEM_MSG_UPDATE_FAIL "FW update failed! Please reboot MCU, and try again."
#define SYSTEM_MSG_UPDATE_PROCESS "FW update in progress"
#define SYSTEM_MSG_UPDATE_NO_FW "No new FW version available!"
#define SYSTEM_MSG_UPDATE_DONE F("FW update successfully done! Please reboot the MCU.")
#define SYSTEM_MSG_UPDATE_FAIL F("FW update failed! Please reboot MCU, and try again.")
#define SYSTEM_MSG_UPDATE_PROCESS F("FW update in progress")
#define SYSTEM_MSG_UPDATE_NO_FW F("No new FW version available!")
void System_Init();
void System_LoadCfg();
@ -63,6 +63,7 @@ void System_TaskSerialCfg(void *);
void System_TaskStreamTelemetry(void *);
void System_TaskSysLed(void *);
void System_TaskWiFiWatchdog(void *);
void System_TaskSdCardRemove(void *);
#endif
/* EOF */

View File

@ -22,5 +22,9 @@ TaskHandle_t Task_SerialCfg;
TaskHandle_t Task_StreamTelemetry;
TaskHandle_t Task_SysLed;
TaskHandle_t Task_WiFiWatchdog;
//TaskHandle_t Task_SdCardFileRemove;
uint8_t StartRemoveSdCard = 0;
uint32_t SdCardRemoveTime = 0;
/* EOF */

View File

@ -48,6 +48,10 @@ extern TaskHandle_t Task_SerialCfg; ///< task handle for serial
extern TaskHandle_t Task_StreamTelemetry; ///< task handle for stream telemetry
extern TaskHandle_t Task_SysLed; ///< task handle for system led
extern TaskHandle_t Task_WiFiWatchdog; ///< task handle for wifi watchdog
//extern TaskHandle_t Task_SdCardFileRemove; ///< task handle for remove file from sd card
extern uint8_t StartRemoveSdCard;
extern uint32_t SdCardRemoveTime;
#endif

View File

@ -74,14 +74,14 @@ void WiFiMngt::Init() {
ServiceMode = true;
WiFi.softAPConfig(Service_LocalIp, Service_Gateway, Service_Subnet);
WiFi.softAP(SericeApSsid.c_str(), SERVICE_WIFI_PASS, SERVICE_WIFI_CHANNEL);
WiFiMode = "AP + Client";
WiFiMode = F("AP + Client");
log->AddEvent(LogLevel_Info, "Service IP Address: http://" + WiFi.softAPIP().toString());
} else {
log->AddEvent(LogLevel_Warning, F("Service AP mode disabled!"));
WiFi.mode(WIFI_STA);
ServiceMode = false;
WiFiMode = "Client";
WiFiMode = F("Client");
}
/* Set STA IP method. Static or DHCP */
@ -150,7 +150,7 @@ void WiFiMngt::WifiManagement() {
esp_wifi_set_ps(WIFI_PS_NONE);
WiFiStaConnect();
//WiFi.begin(WifiSsid, WifiPassword);
WiFiMode = "Client";
WiFiMode = F("Client");
#if (WIFI_CLIENT_WAIT_CON == true)
while (WiFi.status() != WL_CONNECTED) {

View File

@ -14,7 +14,6 @@
$("#content").load(page);
setActive(this);
});
$("#content").load("page_config.html", function() {
var defaultLink = document.querySelector('a[href="page_config.html"]');
setActive(defaultLink);
@ -22,7 +21,6 @@
});
</script>
</head>
<body>
<nav>
<img src="esp32_cam.svg" id=logo alt="Logo image" style="margin-left: 10px;" />
@ -36,7 +34,6 @@
</ul>
</nav>
<hr>
<section class="container">
<div class="container_left-half">
<article>
@ -52,23 +49,20 @@
</article>
</div>
</section>
<br>
<cfg>
<cfg_bar>
<div id="cfg">
<div id="cfg_bar">
<div id="links">
<li><a href="page_config.html">Camera configuration</a></li>
<li><a href="page_wifi.html">Wi-Fi configuration</a></li>
<li><a href="page_auth.html">Authentication</a></li>
<li><a href="page_system.html">System</a></li>
</div>
</cfg_bar>
</cfg>
</div>
</div>
<hr>
<div id="content">
</div>
<br><br><br><br>
<table id=botton><tr>
<td><p class=p2>Prusa Connect ESP32 cam</p> </td>
@ -77,7 +71,6 @@
<td><a href="https://github.com/Prusa-Development/Prusa-Firmware-ESP32-Cam/" id="github-link"><svg height="25"><image href="github-icon.svg"></svg></a></td>
</tr></table>
</body>
<script src="scripts.js"></script>
<script>
sliderCheck();

View File

@ -1,6 +1,5 @@
<style>@import url("styles.css");</style>
<script src="jquery-3.7.0.min.js"></script>
<body>
<center>
<table>
@ -13,7 +12,6 @@
</table>
</center>
</body>
<script src="scripts.js"></script>
<script>
get_data("auth");

View File

@ -2,7 +2,7 @@
<script src="jquery-3.7.0.min.js"></script>
<body>
<center><table id="data">
<tr><td class="ps3">Basic image settings</td><td></td></tr>
<tr><td class="ps3">Basic settings</td><td></td></tr>
<tr><td class="pc1">Connect Token</td><td ><input type="text" name="token" id=tokenid >&nbsp;<button class="btn_save" onclick="changeValue(encodeURIComponent(document.getElementById('tokenid').value), 'set_token?token=', 'config')">Save</button></td></tr>
<tr><td class="pc1">Fingerprint</td><td class=pc2 id="fingerprint"></td></tr>
<tr><td class="pc1">Trigger Interval [s]</td><td ><input type="text" name="refresh" id=refreshid >&nbsp;<button class="btn_save" onclick="changeValue(document.getElementById('refreshid').value, 'set_int?refresh=', 'config')">Save</button></td></tr>
@ -27,7 +27,7 @@
<tr><td class="pc1">Saturation</td><td class="pc2">Low <input type="range" class="slider" name="saturation" id=saturationid min="-2" max="2" step="1" onchange="changeValue(this.value, 'set_int?saturation=', 'config')"> High</td></tr>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr>
<td class="pc1">Image rotation</td><td><label for="image_rotation"></label>
<td class="pc1">Image Rotation</td><td><label for="image_rotation"></label>
<select class="select" id="image_rotationid" name="image_rotation" onchange="changeValue(this.value, 'set_int?image_rotation=', 'config')">
<option value="1"></option>
<option value="6">90°</option>
@ -44,13 +44,14 @@
<tr><td class="pc1">Flash duration</td><td class="pc2">Low <input type="range" class="slider" name="flash_time" id=flash_timeid min="50" max="1500" step="50" onchange="changeValue(this.value, 'set_int?flash_time=', 'config')">High</td></tr>
<tr><td class="pc1">Flash duration</td><td class="pc2"><span id="flash_time_value"></span> ms</td></tr>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr><td class="pc1">Save images to micro SD</td><td class="pc2"><label class="switch"><input type="checkbox" name="timelaps" id=timelapsid onchange="changeValue(this.checked, 'set_bool?timelaps_enable=', 'config')"><span class="checkbox_slider round"></span></label> <span id="status_timelaps"></span></td></tr>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
</table></center>
<center><button class="btn_collapsible">Advanced image settings</button></center>
<center><button class="btn_collapsible">Advanced settings</button></center>
<div class="content">
<center><table id="data">
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr><td class="ps3">Advanced image settings</td><td></td></tr>
<tr><td class="ps3">Advanced settings</td><td></td></tr>
<tr><td class="pc1">Automatic white balancing</td><td class="pc2"><label class="switch"><input type="checkbox" name="awb" id=awbid onchange="changeValue(this.checked, 'set_bool?awb=', 'config')"><span class="checkbox_slider round"></span></label> <span id="status_awb"></span></td></tr>
<tr><td class="pc1">Automatic white balancing gain</td><td class="pc2"><label class="switch"><input type="checkbox" name="awb_gain" id=awb_gainid onchange="changeValue(this.checked, 'set_bool?awb_gain=', 'config')"><span class="checkbox_slider round"></span></label> <span id="status_awb_gain"></span></td></tr>
<tr>
@ -81,7 +82,6 @@
</table></center>
</div>
</body>
<script src="scripts.js"></script>
<script>
get_data("config");

View File

@ -8,12 +8,14 @@
<tr><td class="ps1">Wi-Fi mode</td><td class="ps2" id="wifi_mode"></td></tr>
<tr><td class="ps1">Wi-Fi service AP SSID</td><td class="ps2" id="service_ap_ssid"></td></tr>
<tr><td class="ps1">Uptime</td><td class="ps2" id="uptime"></td></tr>
<tr><td class="ps1">Software version</td><td class="ps2" id="sw_ver"></td></tr>
<tr><td class="ps1">Software build</td><td class="ps2" id="sw_build"></td></tr>
<tr><td class="ps1">Available software update</td><td class="ps2"><span id="sw_new_ver"></span> <span class="underlined-text" onclick="checkUpdate()">Check update from cloud</span></td></tr>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr><td class="ps3">Firmware</td><td></td></tr>
<tr><td class="ps1">Version</td><td class="ps2" id="sw_ver"></td></tr>
<tr><td class="ps1">Build</td><td class="ps2" id="sw_build"></td></tr>
<tr><td class="ps1">Available update</td><td class="ps2"><span id="sw_new_ver"></span> <span class="underlined-text" onclick="checkUpdate()">Check update from cloud</span></td></tr>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr><td class="ps3">System configuration</td><td></td></tr>
<tr><td class="pc1">Cam name & mDNS record</td><td ><input type="text" name="mdns" id=mdnsid ><span class=pc1>.local</span>&nbsp;<button class="btn_save" onclick="changeValue(document.getElementById('mdnsid').value, 'set_mdns?mdns=', 'system')">Save</button></td></tr>
<tr><td class="pc1">Camera name & mDNS record</td><td ><input type="text" name="mdns" id=mdnsid ><span class=pc1>.local</span>&nbsp;<button class="btn_save" onclick="changeValue(document.getElementById('mdnsid').value, 'set_mdns?mdns=', 'system')">Save</button></td></tr>
<tr>
<td class="pc1">Log level</td><td><label for="loglevel"></label>
<select class="select" id="loglevelid" name="loglevel" onchange="changeValue(this.value, 'set_int?log_level=', 'system')">
@ -25,21 +27,26 @@
</td>
</tr>
<tr><td class="pc1">Get logs</td><td ><button class="btn_update" onclick="window.open('get_logs')">Get logs</button></td></tr>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr><td class="ps3">Micro SD card</td><td></td></tr>
<tr><td class="ps1">Card status</td><td class="ps2" id="sd_status"></td></tr>
<tr><td class="ps1">Capacity</td><td class="ps2"><span id="sd_total"></span> MB</td></tr>
<tr><td class="ps1">Available</td><td class="ps2"><div class="progress-container"><div class="progress-bar" id="progress_bar_sd_free">0%</div></div></td></tr>
<tr><td class="ps1">Used</td><td class="ps2"><div class="progress-container"><div class="progress-bar" id="progress_bar_sd_used">0%</div></div></td></tr>
</table>
</center>
<br>
<center>
<table id="update">
<tr><td class="ps3">Firmware update</td><td></td></tr>
<tr><td class="ps1">Status:</td><td><span class="ps2" id="status">Ready</span></td></tr>
<tr><td class="ps1">Processing:</td><td class="ps2"><div class="progress-container"><div class="progress-bar" id="myProgressBar">0%</div></div><!--<span class="ps2" id="progressValue">0</span>%--></td></tr>
<tr><td></td><td style="width:50%"><input type="file" id="firmwareInput" accept=".bin"></td></tr>
<tr><td class="ps1">Status</td><td><span class="ps2" id="status">Ready</span></td></tr>
<tr><td class="ps1">Progress</td><td class="ps2"><div class="progress-container"><div class="progress-bar" id="myProgressBar">0%</div></div></td></tr>
<tr><td></td><td><input type="file" id="firmwareInput" accept=".bin"></td></tr>
<tr><td></td><td><input type="submit" class="btn_update" value="Update from file" onclick="uploadFile()"></td></tr>
<tr><td></td><td><input type="submit" class="btn_update" value="Update from cloud" onclick="updateWeb()"></td></tr>
</table>
</center>
</body>
<script src="scripts.js"></script>
<script>
var updateCompleted = false;

View File

@ -1,6 +1,5 @@
<style>@import url("styles.css");</style>
<script src="jquery-3.7.0.min.js"></script>
<body>
<table id="center_tb">
<tr><td></td><td style="width:40%">
@ -11,12 +10,10 @@
<p class="w2">IP Address: <span id="ip"></span></p>
<p class="w2">mDNS: http://<span id="mdns"></span>.local</p>
</td><td></td></tr>
<tr><td></td><td style="width:40%">
<br>
<p class="w1">Available networks</p>
</td><td></td></tr>
<tr><td></td><td style="width:40%">
<table id="wifi_ntw">
<colgroup>
@ -33,12 +30,10 @@
</tr>
</table>
</td><td></td></tr>
<tr><td></td><td style="width:40%" align="right">
<button class="btn" onclick="scanWifi()">Scan Wi-Fi networks</button>
<br>
</td><td></td></tr>
<tr><td></td><td style="width:40%">
<br>
<table>
@ -48,9 +43,7 @@
<tr><td></td><td align="center"><button class="btn_save_w" onclick="setWifi(document.getElementById('wifi_ssid_id').value, document.getElementById('wifi_pass_id').value)">Save & Connect</button></td></tr>
</table>
</td><td></td></tr>
</table>
<br>
<center>
<button class="btn_collapsible_wifi">Advanced Wi-Fi settings</button>
@ -74,9 +67,7 @@
<tr><td></td><td align="center"><button class="btn_save_w" onclick="setWifiNet(document.getElementById('net_ip_id').value, document.getElementById('net_mask_id').value, document.getElementById('net_gw_id').value, document.getElementById('net_dns_id').value)">Save</button></td></tr>
</table>
</div>
</body>
<script src="scripts.js"></script>
<script>
setTimeout(function(){GetDataAndPrintTableWiFi();}, 500);

View File

@ -46,6 +46,7 @@ function get_data(val) {
document.getElementById('agc_gainid').value = obj.agc_gain;
document.getElementById("flash_time_value").innerText = obj.flash_time;
document.getElementById("aec_value_value").innerText = obj.aec_value;
document.getElementById('timelapsid').checked = obj.timelaps;
$("#status_hmirror").text((obj.hmirror == "true") ? "On" : "Off");
$("#status_vflip").text((obj.vflip == "true") ? "On" : "Off");
$("#status_lensc").text((obj.lensc == "true") ? "On" : "Off");
@ -59,6 +60,7 @@ function get_data(val) {
$("#status_raw_gama").text((obj.raw_gama == "true") ? "On" : "Off");
$("#status_aec2").text((obj.aec2 == "true") ? "On" : "Off");
$("#status_gain_ctrl").text((obj.gain_ctrl == "true") ? "On" : "Off");
$("#status_timelaps").text((obj.timelaps == "true") ? "On" : "Off");
sliderCheck();
}
@ -100,6 +102,19 @@ function get_data(val) {
$("#wifi_mode").text(obj.wifi_mode);
$("#sw_new_ver").text(obj.sw_new_ver);
$("#service_ap_ssid").text(obj.service_ap_ssid);
$("#sd_status").text(obj.sd_status);
$("#sd_total").text(obj.sd_total);
$("#sd_free_p").text(obj.sd_free_p);
$("#sd_used_p").text(obj.sd_used_p);
var sd_free_prog = document.getElementById("progress_bar_sd_free");
sd_free_prog.style.width = obj.sd_free_p + "%";
sd_free_prog.innerHTML = obj.sd_free_p + "%";
var sd_free_prog = document.getElementById("progress_bar_sd_used");
sd_free_prog.style.width = obj.sd_used_p + "%";
sd_free_prog.innerHTML = obj.sd_used_p + "%";
document.getElementById('mdnsid').value = obj.mdns;
document.getElementById('loglevelid').value = obj.log_level;
}

View File

@ -1,7 +1,6 @@
body {
font-family: sans-serif;
}
/* index styles */
.p1 {
color: #797979;
@ -70,7 +69,7 @@ body {
text-decoration-thickness: 2px;
}
/* CFG BAR */
cfg {
#cfg {
display: flex;
flex-direction: column;
text-align: center;
@ -79,18 +78,18 @@ cfg {
color: #2A2A2A;
opacity: 1;
}
cfg_bar li {
#cfg_bar li {
display: inline-block;
padding: 14px;
font-size: 16px;
left: 50%;
}
cfg_bar li a {
#cfg_bar li a {
text-decoration: none;
cursor: pointer;
color: #212529;
}
cfg_bar li a:hover {
#cfg_bar li a:hover {
color: #fa6831;
}
/* CONTAINER */
@ -360,7 +359,6 @@ cfg_bar li a:hover {
color: #2A2A2A;
opacity: 1;
}
/* BUTTON */
.btn_save_a {
width: 178px;
@ -418,23 +416,8 @@ cfg_bar li a:hover {
color: #2A2A2A;
opacity: 1;
}
/* data table */
#data {
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 100%;
table-layout: fixed;
}
#data td, #data th {
padding: 8px;
}
#data th {
padding-top: 12px;
padding-bottom: 12px;
text-align: left;
}
/* update table */
update {
#update {
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 100%;
@ -470,7 +453,7 @@ cfg_bar li a:hover {
}
/* progress bar*/
.progress-container {
width: 100%;
width: 30%;
background-color: #ccc;
}
.progress-bar {
@ -480,13 +463,12 @@ cfg_bar li a:hover {
text-align: center;
line-height: 15px;
color: white;
padding-left: 3px;
}
/* advanced cam cfg */
.content {
display: none;
}
.btn_collapsible {
width: 300px;
height: 24px;
@ -501,12 +483,10 @@ cfg_bar li a:hover {
background-color: #FA6831;
color: white;
}
/* advanced wifi cfg */
.content_wifi {
display: none;
}
.btn_collapsible_wifi {
width: 300px;
height: 24px;