initial commit

pull/1/head
Miroslav Pivovarsky 2024-04-18 21:55:30 +02:00
commit 0eefd45e11
76 changed files with 11110 additions and 0 deletions

View File

@ -0,0 +1,37 @@
/**
@file Camera_cfg.h
@brief Here is saved camera GPIO cfg for camera module OV2640
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#ifndef _CAMERA_CFG_H_
#define _CAMERA_CFG_H_
// OV2640 camera module pins (CAMERA_MODEL_AI_THINKER)
#define PWDN_GPIO_NUM 32 ///< Power down control pin
#define RESET_GPIO_NUM -1 ///< Reset control pin
#define XCLK_GPIO_NUM 0 ///< External clock pin
#define SIOD_GPIO_NUM 26 ///< SCCB: SI/O data pin
#define SIOC_GPIO_NUM 27 ///< SCCB: SI/O control pin
#define Y9_GPIO_NUM 35 ///< SCCB: Y9 pin
#define Y8_GPIO_NUM 34 ///< SCCB: Y8 pin
#define Y7_GPIO_NUM 39 ///< SCCB: Y7 pin
#define Y6_GPIO_NUM 36 ///< SCCB: Y6 pin
#define Y5_GPIO_NUM 21 ///< SCCB: Y5 pin
#define Y4_GPIO_NUM 19 ///< SCCB: Y4 pin
#define Y3_GPIO_NUM 18 ///< SCCB: Y3 pin
#define Y2_GPIO_NUM 5 ///< SCCB: Y2 pin
#define VSYNC_GPIO_NUM 25 ///< Vertical sync pin
#define HREF_GPIO_NUM 23 ///< Line sync pin
#define PCLK_GPIO_NUM 22 ///< Pixel clock pin
#define FLASH_GPIO_NUM 4 ///< Flash control pin
#endif
/* EOF */

View File

@ -0,0 +1,174 @@
/**
@file Certificate.cpp
@brief Here is saved certificate for communication with servers
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#include "Certificate.h"
/*
echo -n | openssl s_client -servername github.com -connect github.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > github_com.crt
echo -n | openssl s_client -servername objects.githubusercontent.com -connect objects.githubusercontent.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > objects_githubusercontent_com.crt
echo -n | openssl s_client -servername raw.githubusercontent.com -connect raw.githubusercontent.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > raw_githubusercontent_com.crt
echo -n | openssl s_client -servername api.github.com -connect api.github.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > api_github_com.crt
echo -n | openssl s_client -servername connect.prusa.com -connect connect.prusa.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > connect_prusa_com.crt
*/
/*
Certificates list:
- connect.prusa.com certificate
*/
const char* root_CAs =
"-----BEGIN CERTIFICATE-----\n"
"MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n"
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n"
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n"
"WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n"
"ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n"
"MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n"
"h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n"
"0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n"
"A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n"
"T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n"
"B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n"
"B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n"
"KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n"
"OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n"
"jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n"
"qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n"
"rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n"
"HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n"
"hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n"
"ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n"
"3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n"
"NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n"
"ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n"
"TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n"
"jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n"
"oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n"
"4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n"
"mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n"
"emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n"
"-----END CERTIFICATE-----";
/*
Certificates list:
- github.com certificate
- objects.githubusercontent.com certificate
- raw.githubusercontent.com certificate
- api.github.com certificate
*/
const char* root_CAs_ota =
" -----BEGIN CERTIFICATE-----\n"
"MIIEozCCBEmgAwIBAgIQTij3hrZsGjuULNLEDrdCpTAKBggqhkjOPQQDAjCBjzEL\n"
"MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE\n"
"BxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQDEy5T\n"
"ZWN0aWdvIEVDQyBEb21haW4gVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENBMB4X\n"
"DTI0MDMwNzAwMDAwMFoXDTI1MDMwNzIzNTk1OVowFTETMBEGA1UEAxMKZ2l0aHVi\n"
"LmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABARO/Ho9XdkY1qh9mAgjOUkW\n"
"mXTb05jgRulKciMVBuKB3ZHexvCdyoiCRHEMBfFXoZhWkQVMogNLo/lW215X3pGj\n"
"ggL+MIIC+jAfBgNVHSMEGDAWgBT2hQo7EYbhBH0Oqgss0u7MZHt7rjAdBgNVHQ4E\n"
"FgQUO2g/NDr1RzTK76ZOPZq9Xm56zJ8wDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB\n"
"/wQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMEkGA1UdIARCMEAw\n"
"NAYLKwYBBAGyMQECAgcwJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdvLmNv\n"
"bS9DUFMwCAYGZ4EMAQIBMIGEBggrBgEFBQcBAQR4MHYwTwYIKwYBBQUHMAKGQ2h0\n"
"dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGlnb0VDQ0RvbWFpblZhbGlkYXRpb25T\n"
"ZWN1cmVTZXJ2ZXJDQS5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3Rp\n"
"Z28uY29tMIIBgAYKKwYBBAHWeQIEAgSCAXAEggFsAWoAdwDPEVbu1S58r/OHW9lp\n"
"LpvpGnFnSrAX7KwB0lt3zsw7CAAAAY4WOvAZAAAEAwBIMEYCIQD7oNz/2oO8VGaW\n"
"WrqrsBQBzQH0hRhMLm11oeMpg1fNawIhAKWc0q7Z+mxDVYV/6ov7f/i0H/aAcHSC\n"
"Ii/QJcECraOpAHYAouMK5EXvva2bfjjtR2d3U9eCW4SU1yteGyzEuVCkR+cAAAGO\n"
"Fjrv+AAABAMARzBFAiEAyupEIVAMk0c8BVVpF0QbisfoEwy5xJQKQOe8EvMU4W8C\n"
"IGAIIuzjxBFlHpkqcsa7UZy24y/B6xZnktUw/Ne5q5hCAHcATnWjJ1yaEMM4W2zU\n"
"3z9S6x3w4I4bjWnAsfpksWKaOd8AAAGOFjrv9wAABAMASDBGAiEA+8OvQzpgRf31\n"
"uLBsCE8ktCUfvsiRT7zWSqeXliA09TUCIQDcB7Xn97aEDMBKXIbdm5KZ9GjvRyoF\n"
"9skD5/4GneoMWzAlBgNVHREEHjAcggpnaXRodWIuY29tgg53d3cuZ2l0aHViLmNv\n"
"bTAKBggqhkjOPQQDAgNIADBFAiEAru2McPr0eNwcWNuDEY0a/rGzXRfRrm+6XfZe\n"
"SzhYZewCIBq4TUEBCgapv7xvAtRKdVdi/b4m36Uyej1ggyJsiesA\n"
"-----END CERTIFICATE-----\n"
" -----BEGIN CERTIFICATE-----\n"
"MIIHOTCCBiGgAwIBAgIQBj1JF0BNOeUTyz/uzRsuGzANBgkqhkiG9w0BAQsFADBZ\n"
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMTMwMQYDVQQDEypE\n"
"aWdpQ2VydCBHbG9iYWwgRzIgVExTIFJTQSBTSEEyNTYgMjAyMCBDQTEwHhcNMjQw\n"
"MzE1MDAwMDAwWhcNMjUwMzE0MjM1OTU5WjBnMQswCQYDVQQGEwJVUzETMBEGA1UE\n"
"CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEVMBMGA1UEChMM\n"
"R2l0SHViLCBJbmMuMRQwEgYDVQQDDAsqLmdpdGh1Yi5pbzCCASIwDQYJKoZIhvcN\n"
"AQEBBQADggEPADCCAQoCggEBAK0rFKU6TEGvuLCY3ZOuXlG+3jerD6EP1gc1qe35\n"
"g68FqyGuVPOUddYNZiymjYMZxywoNp3qxlbFFBTf9etsayavT+uW+2UMjqCotAdK\n"
"KicBEspuExoACFuNgTi7sSUT7A55+k4/+5O+VtpaxQ5dmQk7HxcqvMYx5owBU+fB\n"
"wYDD+hXeg3YvxLZNeIlN8OlqWL8w9HbG+3ccegVEjOJQbkrcrW7IQMq2Uk92XjxI\n"
"PmMVIvaefqcC1poGYvS4VvEh3x64vJK1hEM4YLMKBaE/hqFtcMozi+H/8JqTCfzP\n"
"Qhnu21HIop9rSucxxnZbe9AeHz2LERpUTf3rjgOMg9PB1RUCAwEAAaOCA+0wggPp\n"
"MB8GA1UdIwQYMBaAFHSFgMBmx9833s+9KTeqAx2+7c0XMB0GA1UdDgQWBBTob1fr\n"
"hlGY65+lvlPa25SsKC777TB7BgNVHREEdDByggsqLmdpdGh1Yi5pb4IJZ2l0aHVi\n"
"LmlvghVnaXRodWJ1c2VyY29udGVudC5jb22CDnd3dy5naXRodWIuY29tggwqLmdp\n"
"dGh1Yi5jb22CFyouZ2l0aHVidXNlcmNvbnRlbnQuY29tggpnaXRodWIuY29tMD4G\n"
"A1UdIAQ3MDUwMwYGZ4EMAQICMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGln\n"
"aWNlcnQuY29tL0NQUzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUH\n"
"AwEGCCsGAQUFBwMCMIGfBgNVHR8EgZcwgZQwSKBGoESGQmh0dHA6Ly9jcmwzLmRp\n"
"Z2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbEcyVExTUlNBU0hBMjU2MjAyMENBMS0x\n"
"LmNybDBIoEagRIZCaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xv\n"
"YmFsRzJUTFNSU0FTSEEyNTYyMDIwQ0ExLTEuY3JsMIGHBggrBgEFBQcBAQR7MHkw\n"
"JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBRBggrBgEFBQcw\n"
"AoZFaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsRzJU\n"
"TFNSU0FTSEEyNTYyMDIwQ0ExLTEuY3J0MAwGA1UdEwEB/wQCMAAwggF/BgorBgEE\n"
"AdZ5AgQCBIIBbwSCAWsBaQB2AE51oydcmhDDOFts1N8/Uusd8OCOG41pwLH6ZLFi\n"
"mjnfAAABjkN89oAAAAQDAEcwRQIgU/M527Wcx0KQ3II7kCuG5WMuOHRSxKkf1xAj\n"
"JuSkyPACIQCVX0uurcIA2Ug7ipNN2S1ZygukWqJCh7hjIH0XsrXh8QB2AH1ZHhLh\n"
"eCp7HGFnfF79+NCHXBSgTpWeuQMv2Q6MLnm4AAABjkN89oEAAAQDAEcwRQIgCxpL\n"
"BDak+TWKarrCHlZn4DlqwEfAN3lvlgSo21HQuU8CIQDicrb72c0lA2suMWPWT92P\n"
"FLaRvFrFn9HVzI6Vh50YZgB3AObSMWNAd4zBEEEG13G5zsHSQPaWhIb7uocyHf0e\n"
"N45QAAABjkN89pQAAAQDAEgwRgIhAPJQX4QArFCjM0sKKzsWLmqmmU8lMhKEYR2T\n"
"ges1AQyQAiEA2Y3VhP5RG+dapcbwYgVbrTlgWzO7KE/lg1x11CVcz3QwDQYJKoZI\n"
"hvcNAQELBQADggEBAHKlvzObJBxxgyLaUNCEFf37mNFsUtXmaWvkmcfIt9V+TZ7Q\n"
"mtvjx5bsd5lqAflp/eqk4+JYpnYcKWrZfM/vMdxPQTeh/VQWewY/hYn6X/V1s2JI\n"
"MtjqEkW4aotVdWjHVvsx4rAjz5vtub/wVYgtrU8jusH3TVpT9/0AoFhKE5m2IS7M\n"
"Ig7wKR+DDxoNj4fFFluxteVNgbtwuJcb23NkBQqfHXCvQWqxXZZA4Nwl/WoGPoGG\n"
"dW5qVOc3BlhtITW53ASyhvKC7HArhj7LwQH8C/dRgn1agIHP9vVJ1NaZnPXhK98T\n"
"ohv++OO0E/F/bVGNWVnLBQ4v5PjQzRQUTGvM2mU=\n"
"-----END CERTIFICATE-----\n"
"-----BEGIN CERTIFICATE-----\n"
"MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh\n"
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH\n"
"MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT\n"
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n"
"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG\n"
"9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI\n"
"2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx\n"
"1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ\n"
"q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz\n"
"tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ\n"
"vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP\n"
"BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV\n"
"5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY\n"
"1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4\n"
"NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG\n"
"Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91\n"
"8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe\n"
"pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl\n"
"MrY=\n"
"-----END CERTIFICATE-----\n"
"-----BEGIN CERTIFICATE-----\n"
"MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL\n"
"MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl\n"
"eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT\n"
"JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx\n"
"MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT\n"
"Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg\n"
"VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm\n"
"aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo\n"
"I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng\n"
"o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G\n"
"A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD\n"
"VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB\n"
"zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW\n"
"RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=\n"
"-----END CERTIFICATE-----";
/* EOF */

View File

@ -0,0 +1,20 @@
/**
@file Certificate.h
@brief Here is saved certificate for communication with servers
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#ifndef _CERTIFICATE_H_
#define _CERTIFICATE_H_
extern const char* root_CAs; ///< root certificate for communication with prusa servers
extern const char* root_CAs_ota; ///< root certificate for communication with servers
#endif
/* EOF */

View File

@ -0,0 +1,146 @@
/*
This code is adapted for the ESP32-CAM board Ai Thinker version
It's neccesary install support for ESP32 board to the arduino IDE. In the board manager we need add next link
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
Then we can install "ESP32 by Espressif Systems" board in the board manager.
ESP32 lib version: 2.0.15 (ESP-IDF v4.4.7) by Espressif Systems
This project uses other libraries. It is necessary to install them in the arduino IDE.
- Library - License - Version - Link
- ESPAsyncWebSrv - LGPL 2.1 - 1.2.7 - https://github.com/dvarrel/ESPAsyncWebSrv
- AsyncTCP - LGPL 3.0 - 1.1.4 - https://github.com/dvarrel/ESPAsyncTCP
- ArduinoJson - MIT - 7.0.4 - https://github.com/bblanchon/ArduinoJson
- ArduinoUniqueID - MIT - 1.3.0 - https://github.com/ricaun/ArduinoUniqueID
- ESP32 - LGPL 2.1 - 2.0.15 - https://github.com/espressif/arduino-esp32
Board configuration in the arduino IDE 2.3.2
Tools -> Board -> ESP32 Arduino -> AI Thinker ESP32
Tools -> Flash frequency -> 80MHz
Tools -> Flash Mode -> DIO
Tools -> Partition scheme -> Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS)
When flashing the firmware to a new, empty ESP32-CAM device for the first time, it is necessary to use the 'Erase' function.
This can be found under 'Tools' -> 'Erase all Flash Before Sketch Upload' -> 'Enable'.
After the initial firmware upload to the MCU, it is possible to disable this option.
If you do not disable this option, your camera configuration will continue to be erased from the flash memory
after uploading new firmware from the Arduino IDE.
Here is partitions table
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x1E0000,
app1, app, ota_1, 0x1F0000,0x1E0000,
spiffs, data, spiffs, 0x3D0000,0x20000,
coredump, data, coredump,0x3F0000,0x10000,
Project: ESP32 PrusaConnect Camera
Developed for: Prusa Research, prusa3d.com
Author: Miroslav Pivovarsky
e-mail: miroslav.pivovarsky@gmail.com
*/
/* includes */
#include <WiFi.h>
#include "Arduino.h"
#include <esp_task_wdt.h>
#include <ESPmDNS.h>
#include <esp_wifi.h>
#include "esp32-hal-cpu.h"
#include "server.h"
#include "cfg.h"
#include "var.h"
#include "mcu_cfg.h"
#include "system.h"
#include "micro_sd.h"
#include "log.h"
#include "connect.h"
#include "wifi_mngt.h"
#include "stream.h"
#include "serial_cfg.h"
void setup() {
/* Serial port for debugging purposes */
Serial.begin(SERIAL_PORT_SPEED);
Serial.println("----------------------------------------------------------------");
Serial.println("Start MCU!");
Serial.println("Prusa ESP32-cam https://prusa3d.cz");
Serial.print("SW Version: ");
Serial.println(SW_VERSION);
Serial.print("Build: ");
Serial.println(SW_BUILD);
#if (CONSOLE_VERBOSE_DEBUG == true)
Serial.setDebugOutput(true);
#endif
/* Init EEPROM */
EEPROM.begin(EEPROM_SIZE);
/* init system led */
system_led.init();
/* init micro SD card and logs */
SystemLog.SetLogLevel((LogLevel_enum)EEPROM.read(EEPROM_ADDR_LOG_LEVEL));
SystemLog.Init();
/* init System lib */
System_Init();
/* read cfg from EEPROM */
SystemConfig.Init();
SystemConfig.CheckResetCfg();
System_LoadCfg();
Server_LoadCfg();
SystemCamera.LoadCameraCfgFromEeprom();
Connect.LoadCfgFromEeprom();
SystemWifiMngt.LoadCfgFromEeprom();
/* init WiFi mngt */
SystemWifiMngt.Init();
/* init camera interface */
SystemCamera.Init();
SystemCamera.CapturePhoto();
/* init WEB server */
Server_InitWebServer();
/* init class for communication with PrusaConnect */
Connect.Init();
/* init tasks */
SystemLog.AddEvent(LogLevel_Info, "Start tasks");
xTaskCreatePinnedToCore(System_TaskMain, "SystemNtpOtaUpdate", 8000, NULL, 1, &Task_SystemMain, 0); /*function, description, stack size, parameters, priority, task handle, core*/
xTaskCreatePinnedToCore(System_TaskCaptureAndSendPhoto, "CaptureAndSendPhoto", 10000, NULL, 2, &Task_CapturePhotoAndSend, 0); /*function, description, stack size, parameters, priority, task handle, core*/
xTaskCreatePinnedToCore(System_TaskWifiManagement, "WiFiManagement", 6000, NULL, 3, &Task_WiFiManagement, 0); /*function, description, stack size, parameters, priority, task handle, core*/
xTaskCreatePinnedToCore(System_TaskSdCardCheck, "CheckMicroSdCard", 5000, NULL, 4, &Task_SdCardCheck, 0); /*function, description, stack size, parameters, priority, task handle, core*/
xTaskCreatePinnedToCore(System_TaskSerialCfg, "CheckSerialConfiguration", 3000, NULL, 5, &Task_SerialCfg, 0); /*function, description, stack size, parameters, priority, task handle, core*/
xTaskCreatePinnedToCore(System_TaskStreamTelemetry, "PrintStreamTelemetry", 3000, 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", 3000, NULL, 8, &Task_WiFiWatchdog, 0); /*function, description, stack size, parameters, priority, task handle, core*/
/* init wdg */
SystemLog.AddEvent(LogLevel_Info, "Init WDG");
esp_task_wdt_init(WDG_TIMEOUT, true); /* enable panic so ESP32 restarts */
esp_task_wdt_add(NULL); /* add current thread to WDT watch */
esp_task_wdt_add(Task_CapturePhotoAndSend);
esp_task_wdt_add(Task_WiFiManagement);
esp_task_wdt_add(Task_SystemMain);
esp_task_wdt_add(Task_SdCardCheck);
esp_task_wdt_add(Task_SerialCfg);
esp_task_wdt_add(Task_StreamTelemetry);
esp_task_wdt_add(Task_SysLed);
esp_task_wdt_add(Task_WiFiWatchdog);
esp_task_wdt_reset(); /* reset wdg */
SystemLog.AddEvent(LogLevel_Info, "MCU configuration done");
}
void loop() {
/* reset wdg */
esp_task_wdt_reset();
}
/* EOF */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
/**
@file WebPage_Icons.h
@brief Here are all icons for web page
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#ifndef _WEB_PAGE_ICONS_H_
#define _WEB_PAGE_ICONS_H_
/* ------------------------------------------------------------------------------------------------------------ */
const char esp32_cam_logo_svg[] PROGMEM = R"rawliteral(
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="202" height="35" style="shape-rendering:geometricPrecision;text-rendering:geometricPrecision;image-rendering:optimizeQuality;fill-rule:evenodd;clip-rule:evenodd" viewBox="0 0 1250 216.67"><defs><style>.fil0{fill:#2b2a29}.fil1{fill:#fefefe;fill-rule:nonzero}</style></defs><g id="Layer_x0020_1"><path d="M0 63.3h502.81v153.37H0z" class="fil0"/><path d="M50.51 111.62h16.84c7.68 0 13.48 3.51 13.48 12.67 0 8.75-5.53 11.99-13.48 11.99H50.51v-24.66zm-25.05 76.12h25.05v-32.06h20.62c21.15 0 34.76-9.3 34.76-33.14 0-22.77-16.84-30.99-34.76-30.99H25.46v96.19zm113.1-76.12h19.94c7 0 12.66 3.24 12.66 10.38 0 7.41-4.04 11.31-11.58 11.31h-21.02v-21.69zm-25.06 76.12h25.06v-35.03h19c11.85 0 13.33 9.97 14.01 19.67.4 2.83.67 12 2.83 15.36h24.79c-3.37-3.77-3.64-17.51-4.05-21.69-.67-9.16-3.9-20.48-14.01-23.04v-.27c10.24-3.9 15.09-13.74 15.09-24.11 0-19.4-16.84-27.08-33.68-27.08H113.5v96.19zm176.15-96.19H264.6v57.93c0 12.4-4.45 18.73-16.85 18.73-12.39 0-16.84-6.33-16.84-18.73V91.55h-25.06V148c0 27.89 12.8 41.76 41.9 41.76 29.11 0 41.9-13.87 41.9-41.76V91.55zm6.94 63.86c0 24.65 19.81 34.35 41.77 34.35 21.15 0 41.9-7.81 41.9-32.19 0-17.38-14.01-23.85-27.89-27.76-14.01-3.91-27.89-5.12-27.89-12.66 0-6.34 6.74-8.22 12.13-8.22 7.54 0 15.89 2.96 15.49 11.59h25.06c0-21.56-19.54-30.99-38.53-30.99-18.06 0-39.21 8.22-39.21 29.23 0 17.79 14.55 23.85 28.16 27.76 13.88 3.9 27.62 5.39 27.62 14.01 0 7.14-7.82 9.83-15.36 9.83-10.78 0-17.65-3.63-18.19-14.95h-25.06zm81.31 32.33h25.73l6.06-17.24h33.55l5.93 17.24h26.14l-35.98-96.19h-25.46l-35.97 96.19zm48.5-69.52h.27l10.64 33.69h-21.82l10.91-33.69z" class="fil1"/><path d="M523.44 63.3h715.63v153.37H523.44z" class="fil0"/><path d="M643.75 125.9c-.67-23.17-23.04-36.37-43.92-36.37-29.24 0-47.96 22.23-47.96 50.12 0 27.88 18.72 50.11 47.96 50.11 24.39 0 42.44-14.41 43.92-39.34h-25.06c-.94 11.19-7.54 17.79-19.4 17.79-16.16 0-22.36-14.15-22.36-28.56 0-14.42 6.2-28.57 22.36-28.57 10.65 0 17.92 5.12 19.4 14.82h25.06zm30.52 13.75c0-14.42 6.19-28.57 22.36-28.57 16.17 0 22.37 14.15 22.37 28.57 0 14.41-6.2 28.56-22.37 28.56s-22.36-14.15-22.36-28.56zm-25.06 0c0 27.88 17.24 50.11 47.42 50.11 30.18 0 47.43-22.23 47.43-50.11 0-27.89-17.25-50.12-47.43-50.12s-47.42 22.23-47.42 50.12zm103.94 48.09h23.58v-59.95h.27l33.41 59.95h25.73V91.55h-23.58v59.28h-.27l-33.41-59.28h-25.73v96.19zm95.59 0h23.57v-59.95h.27L906 187.74h25.73V91.55h-23.58v59.28h-.27l-33.41-59.28h-25.73v96.19zm95.85 0h78.01v-21.55h-52.95v-18.33h47.16v-19.4h-47.16v-16.84h51.6V91.55h-76.66v96.19zm176.97-61.84c-.67-23.17-23.04-36.37-43.92-36.37-29.24 0-47.96 22.23-47.96 50.12 0 27.88 18.72 50.11 47.96 50.11 24.38 0 42.44-14.41 43.92-39.34h-25.06c-.94 11.19-7.54 17.79-19.4 17.79-16.17 0-22.36-14.15-22.36-28.56 0-14.42 6.19-28.57 22.36-28.57 10.64 0 17.92 5.12 19.4 14.82h25.06zm85.35-34.35h-82.45v21.55h28.69v74.64h25.06V113.1h28.7V91.55z" class="fil1"/><path d="M811.83 0H1250v79.86H811.83z" style="fill:#fa6e36"/><path d="M828.58 66.11h41.35V54.68h-28.06v-9.71h24.99V34.68h-24.99v-8.93h27.35V15.11h-40.64v51zm43.75-17.14c0 13.06 10.49 18.21 22.14 18.21 11.21 0 22.21-4.15 22.21-17.07 0-9.22-7.43-12.64-14.79-14.71-7.42-2.08-14.78-2.72-14.78-6.72 0-3.35 3.57-4.35 6.43-4.35 4 0 8.43 1.57 8.21 6.14h13.28c0-11.43-10.35-16.43-20.42-16.43-9.57 0-20.78 4.36-20.78 15.5 0 9.43 7.71 12.64 14.92 14.71 7.36 2.07 14.64 2.86 14.64 7.43 0 3.78-4.14 5.21-8.14 5.21-5.71 0-9.35-1.93-9.64-7.92h-13.28zm61.59-23.22h8.93c4.07 0 7.14 1.86 7.14 6.72 0 4.64-2.93 6.35-7.14 6.35h-8.93V25.75zm-13.28 40.36h13.28v-17h10.93c11.21 0 18.43-4.93 18.43-17.57 0-12.07-8.93-16.43-18.43-16.43h-24.21v51zm60.45-22c1.08-.07 2.15-.14 3.22-.14 4.86 0 8.85 1.28 8.85 6.78 0 4.29-3.92 6.14-8.07 6.14-6.07 0-9.07-2.93-8.92-8.85h-11.36c-.78 12.78 8.79 18.71 20.36 18.71 9.92 0 20.13-4.79 20.13-16 0-5.93-2.78-9.93-8.49-11.43v-.14c4.57-.78 6.92-5.36 6.92-9.71 0-10.14-9.92-13.79-18.56-13.79-10.57 0-18.79 5.79-19 16.93h11.35c-.07-4.57 2.79-7.07 7.72-7.07 4 0 7.14 1.78 7.14 5 0 4.28-4.43 5.21-8.07 5.21-1.07.07-2.14-.14-3.22-.07v8.43zm66.6 22V55.82h-23.64c3.29-3.14 5.14-4.43 9-6.71 7.29-4.57 14.14-8 14.14-17.64 0-10.64-8.78-15.79-18.64-15.79-12.35 0-20.35 8.36-19.57 20.72h11.36c0-5.43 1.85-10.07 8.28-10.07 3.93 0 6.43 2.28 6.43 6.21 0 3.35-2.71 5.78-6.5 8.28-3.93 2.5-8.71 5-13 8.72-4.92 4.28-7.71 10.21-7.78 16.57h39.92zm76.99-32.79c-.36-12.28-12.21-19.28-23.28-19.28-15.5 0-25.43 11.79-25.43 26.57 0 14.78 9.93 26.57 25.43 26.57 12.92 0 22.49-7.65 23.28-20.86h-13.28c-.5 5.93-4 9.43-10.29 9.43-8.57 0-11.85-7.5-11.85-15.14 0-7.64 3.28-15.14 11.85-15.14 5.64 0 9.5 2.71 10.29 7.85h13.28zm-.39 32.79h13.64l3.21-9.15h17.78l3.15 9.15h13.85l-19.07-51h-13.5l-19.06 51zM1150 29.25h.14l5.64 17.86h-11.57l5.79-17.86zm27.6 36.86h12.5V27.4h.14l10.71 38.71h10.14l10.72-38.71h.14v38.71h12.5v-51h-19.57l-8.79 34h-.14l-8.78-34h-19.57v51z" class="fil1"/></g></svg>
)rawliteral";
/* ------------------------------------------------------------------------------------------------------------ */
const char github_icon_svg[] PROGMEM = R"rawliteral(
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#797979" d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
)rawliteral";
/* ------------------------------------------------------------------------------------------------------------ */
const char light_icon_on_svg[] PROGMEM = R"rawliteral(
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#797979" d="M22 8.51v1.372h-2.538c.02-.223.038-.448.038-.681 0-.237-.017-.464-.035-.69h2.535zm-10.648-6.553v-1.957h1.371v1.964c-.242-.022-.484-.035-.726-.035-.215 0-.43.01-.645.028zm5.521 1.544l1.57-1.743 1.019.918-1.603 1.777c-.25-.297-.593-.672-.986-.952zm-10.738.952l-1.603-1.777 1.019-.918 1.57 1.743c-.392.28-.736.655-.986.952zm-1.597 5.429h-2.538v-1.372h2.535c-.018.226-.035.454-.035.691 0 .233.018.458.038.681zm9.462 9.118h-4c-.276 0-.5.224-.5.5s.224.5.5.5h4c.276 0 .5-.224.5-.5s-.224-.5-.5-.5zm0 2h-4c-.276 0-.5.224-.5.5s.224.5.5.5h4c.276 0 .5-.224.5-.5s-.224-.5-.5-.5zm.25 2h-4.5l1.188.782c.154.138.38.218.615.218h.895c.234 0 .461-.08.615-.218l1.187-.782zm3.75-13.799c0 3.569-3.214 5.983-3.214 8.799h-1.989c-.003-1.858.87-3.389 1.721-4.867.761-1.325 1.482-2.577 1.482-3.932 0-2.592-2.075-3.772-4.003-3.772-1.925 0-3.997 1.18-3.997 3.772 0 1.355.721 2.607 1.482 3.932.851 1.478 1.725 3.009 1.72 4.867h-1.988c0-2.816-3.214-5.23-3.214-8.799 0-3.723 2.998-5.772 5.997-5.772 3.001 0 6.003 2.051 6.003 5.772z"/></svg>
)rawliteral";
/* ------------------------------------------------------------------------------------------------------------ */
const char light_icon_off_svg[] PROGMEM = R"rawliteral(
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#797979" d="M10.741 17h-1.991c0-.17-.016-.338-.035-.506l1.703-1.548c.197.653.323 1.332.323 2.054zm-.04 6.659c.19.216.465.341.753.341h1.093c.288 0 .562-.125.752-.341l1.451-1.659h-5.5l1.451 1.659zm3.799-3.659h-5c-.276 0-.5.224-.5.5s.224.5.5.5h5c.276 0 .5-.224.5-.5s-.224-.5-.5-.5zm0-2h-5c-.276 0-.5.224-.5.5s.224.5.5.5h5c.276 0 .5-.224.5-.5s-.224-.5-.5-.5zm1.707-8.315c-1.104 2.28-2.948 4.483-2.949 7.315h1.992c0-3.169 3.479-5.906 3.726-9.832l-2.769 2.517zm6.793-8.201l-20.654 18.75-1.346-1.5 6.333-5.728c-1.062-1.873-2.333-3.843-2.333-6.272 0-4.343 3.498-6.734 6.996-6.734 2.408 0 4.798 1.146 6.064 3.267l3.598-3.267 1.342 1.484zm-14.147 10.142l7.676-6.969c-.833-1.742-2.682-2.657-4.533-2.657-2.483 0-4.996 1.626-4.996 4.734 0 1.713.907 3.246 1.853 4.892z"/></svg>
)rawliteral";
/* ------------------------------------------------------------------------------------------------------------ */
const char refresh_icon_svg[] PROGMEM = R"rawliteral(
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#797979" d="M23 12c0 1.042-.154 2.045-.425 3h-2.101c.335-.94.526-1.947.526-3 0-4.962-4.037-9-9-9-1.706 0-3.296.484-4.655 1.314l1.858 2.686h-6.994l2.152-7 1.849 2.673c1.684-1.049 3.659-1.673 5.79-1.673 6.074 0 11 4.925 11 11zm-6.354 7.692c-1.357.826-2.944 1.308-4.646 1.308-4.962 0-9-4.038-9-9 0-1.053.191-2.06.525-3h-2.1c-.271.955-.425 1.958-.425 3 0 6.075 4.925 11 11 11 2.127 0 4.099-.621 5.78-1.667l1.853 2.667 2.152-6.989h-6.994l1.855 2.681z"/></svg>
)rawliteral";
/* ------------------------------------------------------------------------------------------------------------ */
const char reboot_icon_svg[] PROGMEM = R"rawliteral(
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#797979" d="M10 18v-12h4v12h-4zm2-18c-.883 0-1.742.102-2.57.283l.349 1.974c.715-.163 1.456-.257 2.221-.257 5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10c0-1.914.551-3.697 1.489-5.217l2.173 2.173 1.353-7.014-7.015 1.35 2.037 2.038c-1.282 1.907-2.037 4.198-2.037 6.67 0 6.627 5.373 12 12 12s12-5.373 12-12-5.373-12-12-12z"/></svg>
)rawliteral";
/* ------------------------------------------------------------------------------------------------------------ */
const char wifi_icon_0_svg[] PROGMEM = R"rawliteral(
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="64" height="45" fill-rule="evenodd" clip-rule="evenodd" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="0 0 3.61 2.55"><path fill="#DADADA" d="M1.55 2.29c0.14,-0.14 0.37,-0.14 0.51,0l-0.26 0.26 -0.25 -0.26z"/><path fill="#DADADA" d="M1.03 1.78c0.43,-0.43 1.12,-0.43 1.55,0l-0.26 0.25c-0.29,-0.28 -0.75,-0.28 -1.03,0l-0.26 -0.25z"/><path fill="#DADADA" d="M0.52 1.26c0.71,-0.71 1.86,-0.71 2.57,0l-0.26 0.26c-0.57,-0.57 -1.49,-0.57 -2.06,0l-0.25 -0.26z"/><path fill="#DADADA" d="M0 0.75c1,-1 2.61,-1 3.61,0l-0.26 0.25c-0.86,-0.85 -2.24,-0.85 -3.09,0l-0.26 -0.25z"/></svg>
)rawliteral";
/* ------------------------------------------------------------------------------------------------------------ */
const char wifi_icon_1_svg[] PROGMEM = R"rawliteral(
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="64" height="45" fill-rule="evenodd" clip-rule="evenodd" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="0 0 3.61 2.55"><path fill="#EF5E63" d="M1.55 2.29c0.14,-0.14 0.37,-0.14 0.51,0l-0.26 0.26 -0.25 -0.26z"/><path fill="#DADADA" d="M1.03 1.78c0.43,-0.43 1.12,-0.43 1.55,0l-0.26 0.25c-0.29,-0.28 -0.75,-0.28 -1.03,0l-0.26 -0.25z"/><path fill="#DADADA" d="M0.52 1.26c0.71,-0.71 1.86,-0.71 2.57,0l-0.26 0.26c-0.57,-0.57 -1.49,-0.57 -2.06,0l-0.25 -0.26z"/><path fill="#DADADA" d="M0 0.75c1,-1 2.61,-1 3.61,0l-0.26 0.25c-0.86,-0.85 -2.24,-0.85 -3.09,0l-0.26 -0.25z"/></svg>
)rawliteral";
/* ------------------------------------------------------------------------------------------------------------ */
const char wifi_icon_2_svg[] PROGMEM = R"rawliteral(
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="64" height="45" fill-rule="evenodd" clip-rule="evenodd" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="0 0 3.61 2.55"><path fill="#F4E551" d="M1.55 2.29c0.14,-0.14 0.37,-0.14 0.51,0l-0.26 0.26 -0.25 -0.26z"/><path fill="#F4E551" d="M1.03 1.78c0.43,-0.43 1.12,-0.43 1.55,0l-0.26 0.25c-0.29,-0.28 -0.75,-0.28 -1.03,0l-0.26 -0.25z"/><path fill="#DADADA" d="M0.52 1.26c0.71,-0.71 1.86,-0.71 2.57,0l-0.26 0.26c-0.57,-0.57 -1.49,-0.57 -2.06,0l-0.25 -0.26z"/><path fill="#DADADA" d="M0 0.75c1,-1 2.61,-1 3.61,0l-0.26 0.25c-0.86,-0.85 -2.24,-0.85 -3.09,0l-0.26 -0.25z"/></svg>
)rawliteral";
/* ------------------------------------------------------------------------------------------------------------ */
const char wifi_icon_3_svg[] PROGMEM = R"rawliteral(
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="64" height="45" fill-rule="evenodd" clip-rule="evenodd" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="0 0 3.61 2.55"><path fill="#0D8CF7" d="M1.55 2.29c0.14,-0.14 0.37,-0.14 0.51,0l-0.26 0.26 -0.25 -0.26z"/><path fill="#0D8CF7" d="M1.03 1.78c0.43,-0.43 1.12,-0.43 1.55,0l-0.26 0.25c-0.29,-0.28 -0.75,-0.28 -1.03,0l-0.26 -0.25z"/><path fill="#0D8CF7" d="M0.52 1.26c0.71,-0.71 1.86,-0.71 2.57,0l-0.26 0.26c-0.57,-0.57 -1.49,-0.57 -2.06,0l-0.25 -0.26z"/><path fill="#DADADA" d="M0 0.75c1,-1 2.61,-1 3.61,0l-0.26 0.25c-0.86,-0.85 -2.24,-0.85 -3.09,0l-0.26 -0.25z"/></svg>
)rawliteral";
/* ------------------------------------------------------------------------------------------------------------ */
const char wifi_icon_4_svg[] PROGMEM = R"rawliteral(
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="64" height="45" fill-rule="evenodd" clip-rule="evenodd" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="0 0 3.61 2.55"><path fill="#40CD8A" d="M1.55 2.29c0.14,-0.14 0.37,-0.14 0.51,0l-0.26 0.26 -0.25 -0.26z"/><path fill="#40CD8A" d="M1.03 1.78c0.43,-0.43 1.12,-0.43 1.55,0l-0.26 0.25c-0.29,-0.28 -0.75,-0.28 -1.03,0l-0.26 -0.25z"/><path fill="#40CD8A" d="M0.52 1.26c0.71,-0.71 1.86,-0.71 2.57,0l-0.26 0.26c-0.57,-0.57 -1.49,-0.57 -2.06,0l-0.25 -0.26z"/><path fill="#40CD8A" d="M0 0.75c1,-1 2.61,-1 3.61,0l-0.26 0.25c-0.86,-0.85 -2.24,-0.85 -3.09,0l-0.26 -0.25z"/></svg>
)rawliteral";
/* ------------------------------------------------------------------------------------------------------------ */
const char eye_slash_svg[] PROGMEM = R"rawliteral(
<svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><g stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="m2 2 20 20"/><path d="m6.71277 6.7226c-3.04798 2.07267-4.71277 5.2774-4.71277 5.2774s3.63636 7 10 7c2.0503 0 3.8174-.7266 5.2711-1.7116m-6.2711-12.23018c.3254-.03809.6588-.05822 1-.05822 6.3636 0 10 7 10 7s-.6918 1.3317-2 2.8335"/><path d="m14 14.2362c-.5308.475-1.2316.7639-2 .7639-1.6569 0-3-1.3431-3-3 0-.8237.33193-1.5698.86932-2.11192"/></g></svg>
)rawliteral";
/* ------------------------------------------------------------------------------------------------------------ */
const char eye_svg[] PROGMEM = R"rawliteral(
<svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h24v24h-24z" fill="#fff" opacity="0"/><g fill="#231f20"><path d="m21.87 11.5c-.64-1.11-4.16-6.68-10.14-6.5-5.53.14-8.73 5-9.6 6.5a1 1 0 0 0 0 1c.63 1.09 4 6.5 9.89 6.5h.25c5.53-.14 8.74-5 9.6-6.5a1 1 0 0 0 0-1zm-9.65 5.5c-4.31.1-7.12-3.59-8-5 1-1.61 3.61-4.9 7.61-5 4.29-.11 7.11 3.59 8 5-1.03 1.61-3.61 4.9-7.61 5z"/><path d="m12 8.5a3.5 3.5 0 1 0 3.5 3.5 3.5 3.5 0 0 0 -3.5-3.5zm0 5a1.5 1.5 0 1 1 1.5-1.5 1.5 1.5 0 0 1 -1.5 1.5z"/></g></svg>
)rawliteral";
/* ------------------------------------------------------------------------------------------------------------ */
const char favicon_svg[] PROGMEM = R"rawliteral(
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="48" height="48">
<path d="M0 0 C15.84 0 31.68 0 48 0 C48 15.84 48 31.68 48 48 C32.16 48 16.32 48 0 48 C0 32.16 0 16.32 0 0 Z " fill="#F76833" transform="translate(0,0)"/>
<path d="M0 0 C3.67563264 1.74425476 5.45963496 2.73632744 7.7734375 6.20703125 C7.7734375 7.52703125 7.7734375 8.84703125 7.7734375 10.20703125 C6.4946875 10.26890625 5.2159375 10.33078125 3.8984375 10.39453125 C3.17914063 10.42933594 2.45984375 10.46414062 1.71875 10.5 C1.07679687 10.40332031 0.43484375 10.30664062 -0.2265625 10.20703125 C-0.8865625 9.21703125 -1.5465625 8.22703125 -2.2265625 7.20703125 C-5.68406199 6.54026247 -5.68406199 6.54026247 -9.2265625 7.20703125 C-11.4681567 9.07695708 -11.4681567 9.07695708 -12.2265625 12.20703125 C-12.25312485 15.28826398 -12.00651643 18.22209479 -11.2265625 21.20703125 C-9.34280575 23.5318691 -9.34280575 23.5318691 -6.2265625 23.39453125 C-3.30269647 23.40782734 -3.30269647 23.40782734 -1.2265625 22.20703125 C-0.09305716 20.19033298 -0.09305716 20.19033298 0.7734375 18.20703125 C3.4134375 18.20703125 6.0534375 18.20703125 8.7734375 18.20703125 C7.56839145 24.3527661 5.92972468 26.62919933 0.7734375 30.20703125 C-2.95708817 31.45053981 -6.35208293 31.72901996 -10.2265625 31.20703125 C-15.04078362 28.79992069 -17.98960736 26.11112522 -20.2265625 21.20703125 C-21.00954472 15.9691502 -21.52058755 10.69489877 -19.015625 5.87890625 C-13.9891341 -0.19370712 -7.70306169 -2.74337109 0 0 Z " fill="#FBF8F7" transform="translate(30.2265625,8.79296875)"/>
<path d="M0 0 C0.66 0 1.32 0 2 0 C1.90276381 4.08391981 1.00447569 6.50332575 -1.8125 9.375 C-6.64944192 13.26074413 -12.10863795 12.52238678 -18 12 C-18 11.67 -18 11.34 -18 11 C-16.89591797 10.85304688 -16.89591797 10.85304688 -15.76953125 10.703125 C-8.0584186 9.61422261 -8.0584186 9.61422261 -2 5 C-0.66701026 2.38181544 -0.66701026 2.38181544 0 0 Z " fill="#4A3C36" transform="translate(38,29)"/>
<path d="M0 0 C1.60875 0.185625 1.60875 0.185625 3.25 0.375 C3.58 1.035 3.91 1.695 4.25 2.375 C3.13625 2.478125 2.0225 2.58125 0.875 2.6875 C-0.32125 2.914375 -1.5175 3.14125 -2.75 3.375 C-4.68395001 6.27592502 -5.05131941 7.27174737 -5.375 10.5625 C-5.49875 11.820625 -5.6225 13.07875 -5.75 14.375 C-7.75 12.375 -7.75 12.375 -8.125 9.625 C-7.47406858 3.98359436 -5.89878557 0.58987856 0 0 Z " fill="#5B4A44" transform="translate(24.75,14.625)"/>
<path d="M0 0 C0.66 0 1.32 0 2 0 C1.38461538 5.53846154 1.38461538 5.53846154 -0.5625 7.875 C-1.2740625 8.431875 -1.2740625 8.431875 -2 9 C-2.99 8.67 -3.98 8.34 -5 8 C-4.360625 7.236875 -3.72125 6.47375 -3.0625 5.6875 C-0.85767219 3.11753287 -0.85767219 3.11753287 0 0 Z " fill="#4F4541" transform="translate(38,29)"/>
</svg>
)rawliteral";
#endif
/* EOF */

View File

@ -0,0 +1,988 @@
/**
@file camera.cpp
@brief Library for working with a camera
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#include "camera.h"
Camera SystemCamera(&SystemConfig, &SystemLog, FLASH_GPIO_NUM);
/**
@brief Init camera constructor
@param Configuration* - pointer to Configuration class
@param Logs* - pointer to Logs class
@param uint8_t - flash pin
@return none
*/
Camera::Camera(Configuration* i_conf, Logs* i_log, uint8_t i_FlashPin) {
config = i_conf;
log = i_log;
CameraFlashPin = i_FlashPin;
StreamOnOff = false;
frameBufferSemaphore = xSemaphoreCreateMutex();
}
/**
@brief Init the camera module and set the camera configuration
@param none
@return none
*/
void Camera::Init() {
log->AddEvent(LogLevel_Info, "Init camera lib");
log->AddEvent(LogLevel_Info, "Init GPIO");
ledcSetup(FLASH_PWM_CHANNEL, FLASH_PWM_FREQ, FLASH_PWM_RESOLUTION);
ledcAttachPin(FLASH_GPIO_NUM, FLASH_PWM_CHANNEL);
ledcWrite(FLASH_PWM_CHANNEL, FLASH_OFF_STATUS);
InitCameraModule();
ApplyCameraCfg();
}
/**
@brief Init the camera module
@param none
@return none
*/
void Camera::InitCameraModule() {
log->AddEvent(LogLevel_Info, "Init camera module");
/* Turn-off the 'brownout detector' */
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
esp_err_t err;
CameraConfig.ledc_channel = LEDC_CHANNEL_0;
CameraConfig.ledc_timer = LEDC_TIMER_0;
CameraConfig.pin_d0 = Y2_GPIO_NUM;
CameraConfig.pin_d1 = Y3_GPIO_NUM;
CameraConfig.pin_d2 = Y4_GPIO_NUM;
CameraConfig.pin_d3 = Y5_GPIO_NUM;
CameraConfig.pin_d4 = Y6_GPIO_NUM;
CameraConfig.pin_d5 = Y7_GPIO_NUM;
CameraConfig.pin_d6 = Y8_GPIO_NUM;
CameraConfig.pin_d7 = Y9_GPIO_NUM;
CameraConfig.pin_xclk = XCLK_GPIO_NUM;
CameraConfig.pin_pclk = PCLK_GPIO_NUM;
CameraConfig.pin_vsync = VSYNC_GPIO_NUM;
CameraConfig.pin_href = HREF_GPIO_NUM;
CameraConfig.pin_sccb_sda = SIOD_GPIO_NUM;
CameraConfig.pin_sccb_scl = SIOC_GPIO_NUM;
CameraConfig.pin_pwdn = PWDN_GPIO_NUM;
CameraConfig.pin_reset = RESET_GPIO_NUM;
CameraConfig.xclk_freq_hz = 16500000; // or 3000000; 16500000; 20000000
CameraConfig.pixel_format = PIXFORMAT_JPEG; /* YUV422,GRAYSCALE,RGB565,JPEG */
/* OV2640
FRAMESIZE_QVGA (320 x 240)
FRAMESIZE_CIF (352 x 288)
FRAMESIZE_VGA (640 x 480)
FRAMESIZE_SVGA (800 x 600)
FRAMESIZE_XGA (1024 x 768)
FRAMESIZE_SXGA (1280 x 1024)
FRAMESIZE_UXGA (1600 x 1200)
*/
CameraConfig.frame_size = TFrameSize; /* FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA */
CameraConfig.jpeg_quality = PhotoQuality; /* 10-63 lower number means higher quality */
CameraConfig.fb_count = 1; /* picture frame buffer alocation */
CameraConfig.grab_mode = CAMERA_GRAB_LATEST; /* CAMERA_GRAB_WHEN_EMPTY or CAMERA_GRAB_LATEST */
if (CameraConfig.fb_location == CAMERA_FB_IN_DRAM) {
log->AddEvent(LogLevel_Verbose, "Camera frame buffer location: DRAM");
} else if (CameraConfig.fb_location == CAMERA_FB_IN_PSRAM) {
log->AddEvent(LogLevel_Verbose, "Camera frame buffer location: PSRAM");
} else {
log->AddEvent(LogLevel_Verbose, "Camera frame buffer location: Unknown");
}
/* Camera init */
err = esp_camera_init(&CameraConfig);
if (err != ESP_OK) {
log->AddEvent(LogLevel_Warning, "Camera init failed. Error: " + String(err, HEX));
log->AddEvent(LogLevel_Warning, "Reset ESP32-cam!");
ESP.restart();
}
}
/**
@brief Load camera CFG from EEPROM
@param none
@return none
*/
void Camera::LoadCameraCfgFromEeprom() {
log->AddEvent(LogLevel_Info, "Load camera CFG from EEPROM");
PhotoQuality = config->LoadPhotoQuality();
FrameSize = config->LoadFrameSize();
TFrameSize = TransformFrameSizeDataType(config->LoadFrameSize());
brightness = config->LoadBrightness();
contrast = config->LoadContrast();
saturation = config->LoadSaturation();
awb = config->LoadAwb();
awb_gain = config->LoadAwbGain();
wb_mode = config->LoadAwbMode();
aec2 = config->LoadAec2();
ae_level = config->LoadAeLevel();
aec_value = config->LoadAecValue();
gain_ctrl = config->LoadGainCtrl();
agc_gain = config->LoadAgcGain();
bpc = config->LoadBpc();
wpc = config->LoadWpc();
raw_gama = config->LoadRawGama();
hmirror = config->LoadHmirror();
vflip = config->LoadVflip();
lensc = config->LoadLensCorrect();
exposure_ctrl = config->LoadExposureCtrl();
CameraFlashEnable = config->LoadCameraFlashEnable();
CameraFlashTime = config->LoadCameraFlashTime();
}
/**
@brief transform uint8_t from web interface to framesize_t
@param uint8_t - int value from web
@return framesize_t
*/
framesize_t Camera::TransformFrameSizeDataType(uint8_t i_data) {
framesize_t ret = FRAMESIZE_QVGA;
switch (i_data) {
case 0:
ret = FRAMESIZE_QVGA;
break;
case 1:
ret = FRAMESIZE_CIF;
break;
case 2:
ret = FRAMESIZE_VGA;
break;
case 3:
ret = FRAMESIZE_SVGA;
break;
case 4:
ret = FRAMESIZE_XGA;
break;
case 5:
ret = FRAMESIZE_SXGA;
break;
case 6:
ret = FRAMESIZE_UXGA;
break;
default:
ret = FRAMESIZE_QVGA;
log->AddEvent(LogLevel_Warning, "Bad frame size. Set default value. " + String(i_data));
break;
}
return ret;
}
/**
@brief Function set flash status
@param bool i_data - true = on, false = off
@return none
*/
void Camera::SetFlashStatus(bool i_data) {
if (true == i_data) {
ledcWrite(FLASH_PWM_CHANNEL, FLASH_ON_STATUS);
} else if (false == i_data) {
ledcWrite(FLASH_PWM_CHANNEL, FLASH_OFF_STATUS);
}
}
/**
@brief Function get flash status
@param none
@return bool - true = on, false = off
*/
bool Camera::GetFlashStatus() {
if (ledcRead(FLASH_PWM_CHANNEL) == FLASH_OFF_STATUS) {
return false;
} else if (ledcRead(FLASH_PWM_CHANNEL) == FLASH_ON_STATUS) {
return true;
}
return false;
}
/**
@brief Function set camer acfg
@param none
@return none
*/
void Camera::ApplyCameraCfg() {
log->AddEvent(LogLevel_Info, "Set camera CFG");
/* sensor configuration */
sensor_t* sensor = esp_camera_sensor_get();
sensor->set_brightness(sensor, brightness); // -2 to 2
sensor->set_contrast(sensor, contrast); // -2 to 2
sensor->set_saturation(sensor, saturation); // -2 to 2
sensor->set_special_effect(sensor, 0); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
sensor->set_whitebal(sensor, awb); // automatic white balancing 0 = disable , 1 = enable
sensor->set_awb_gain(sensor, awb_gain); // automatic white balancing gain 0 = disable , 1 = enable
sensor->set_wb_mode(sensor, wb_mode); // white balancing mode 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
sensor->set_exposure_ctrl(sensor, exposure_ctrl); // exposition controll 0 = disable , 1 = enable
sensor->set_aec2(sensor, aec2); // enable exposition controll 0 = disable , 1 = enable
sensor->set_ae_level(sensor, ae_level); // automatic exposition level -2 to 2
sensor->set_aec_value(sensor, aec_value); // expozition time - 0 to 1200
sensor->set_gain_ctrl(sensor, gain_ctrl); // automatic gain controll 0 = disable , 1 = enable
sensor->set_agc_gain(sensor, agc_gain); // automatic gain controll level 0 to 30
sensor->set_gainceiling(sensor, (gainceiling_t)0); // maximum gain 0 to 6
sensor->set_bpc(sensor, bpc); // bad pixel correction 0 = disable , 1 = enable
sensor->set_wpc(sensor, wpc); // white pixel correction 0 = disable , 1 = enable
sensor->set_raw_gma(sensor, raw_gama); // raw gama correction 0 = disable , 1 = enable
sensor->set_lenc(sensor, lensc); // lens correction 0 = disable , 1 = enable
sensor->set_hmirror(sensor, hmirror); // horizontal mirror 0 = disable , 1 = enable
sensor->set_vflip(sensor, vflip); // vertical flip 0 = disable , 1 = enable
sensor->set_dcw(sensor, 1); // 0 = disable , 1 = enable
sensor->set_colorbar(sensor, 0); // external collor lines, 0 = disable , 1 = enable
}
/**
@brief Function for reinit camera module
@param none
@return none
*/
void Camera::ReinitCameraModule() {
esp_err_t err = esp_camera_deinit();
if (err != ESP_OK) {
log->AddEvent(LogLevel_Warning, "Camera error deinit camera module. Error: " + String(err, HEX));
}
InitCameraModule();
ApplyCameraCfg();
}
/**
@brief Capture Photo and Save it to string array
@param none
@return none
*/
void Camera::CapturePhoto() {
if (false == StreamOnOff) {
if (xSemaphoreTake(frameBufferSemaphore, portMAX_DELAY)) {
/* check flash, and enable FLASH LED */
if (true == CameraFlashEnable) {
ledcWrite(FLASH_PWM_CHANNEL, FLASH_ON_STATUS);
delay(CameraFlashTime);
}
/* get train photo */
FrameBuffer = esp_camera_fb_get();
esp_camera_fb_return(FrameBuffer);
do {
log->AddEvent(LogLevel_Info, "Taking photo...");
/* capture final photo */
FrameBuffer = esp_camera_fb_get();
if (!FrameBuffer) {
log->AddEvent(LogLevel_Error, "Camera capture failed! photo");
return;
} else {
/* copy photo from buffer to string array */
char buf[150] = { '\0' };
uint8_t ControlFlag = (uint8_t)FrameBuffer->buf[15];
sprintf(buf, "The picture has been saved. Size: %d bytes, Photo resolution: %zu x %zu", FrameBuffer->len, FrameBuffer->width, FrameBuffer->height);
log->AddEvent(LogLevel_Info, buf);
/* check corrupted photo */
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));
}
esp_camera_fb_return(FrameBuffer);
}
/* check if photo is correctly saved */
} while (!(FrameBuffer->len > 100));
/* Disable flash */
if (true == CameraFlashEnable) {
delay(CameraFlashTime);
ledcWrite(FLASH_PWM_CHANNEL, FLASH_OFF_STATUS);
}
xSemaphoreGive(frameBufferSemaphore);
}
}
}
/**
@brief Capture Stream
@param camera_fb_t * - pointer to camera_fb_t
@return none
*/
void Camera::CaptureStream(camera_fb_t* i_buf) {
if (xSemaphoreTake(frameBufferSemaphore, portMAX_DELAY)) {
do {
/* capture final photo */
FrameBuffer = esp_camera_fb_get();
if (!FrameBuffer) {
log->AddEvent(LogLevel_Error, "Camera capture failed! stream");
i_buf = NULL;
return;
}
/* check if photo is correctly saved */
} while (!(FrameBuffer->len > 100));
*i_buf = *FrameBuffer;
xSemaphoreGive(frameBufferSemaphore);
}
}
/**
@brief Capture Return Frame Buffer
@param none
@return none
*/
void Camera::CaptureReturnFrameBuffer() {
esp_camera_fb_return(FrameBuffer);
}
/**
@brief Set Stream Status
@param bool - true = on, false = off
@return none
*/
void Camera::SetStreamStatus(bool i_status) {
StreamOnOff = i_status;
log->AddEvent(LogLevel_Info, "Camera video stream: " + String(StreamOnOff));
}
/**
@brief Get Stream Status
@param none
@return bool - true = on, false = off
*/
bool Camera::GetStreamStatus() {
return StreamOnOff;
}
/**
@brief Set Frame Size
@param uint16_t - frame size
@return none
*/
void Camera::StreamSetFrameSize(uint16_t i_data) {
StreamAverageSize = (StreamAverageSize + i_data) / 2;
}
/**
@brief Set average Frame Fps
@param float - frame fps
@return none
*/
void Camera::StreamSetFrameFps(float i_data) {
StreamAverageFps = (StreamAverageFps + i_data) / 2.0;
}
/**
@brief Get average stream Frame Size
@param none
@return uint16_t - frame size
*/
uint16_t Camera::StreamGetFrameAverageSize() {
return StreamAverageSize;
}
/**
@brief Get average stream Frame Fps
@param none
@return float - frame fps
*/
float Camera::StreamGetFrameAverageFps() {
return StreamAverageFps;
}
/**
@brief Clear Frame Data
@param none
@return none
*/
void Camera::StreamClearFrameData() {
StreamAverageFps = 0.0;
StreamAverageSize = 0;
}
/**
@brief Get Photo
@param none
@return String - photo
*/
String Camera::GetPhoto() {
Photo = "";
for (size_t i = 0; i < FrameBuffer->len; i++) {
Photo += (char)FrameBuffer->buf[i];
}
return Photo;
}
/**
@brief Get Photo Frame Buffer
@param none
@return camera_fb_t* - photo frame buffer
*/
camera_fb_t* Camera::GetPhotoFb() {
return FrameBuffer;
}
/**
@brief Copy Photo
@param camera_fb_t* - pointer to camera_fb_t
@return none
*/
void Camera::CopyPhoto(camera_fb_t* i_data) {
*i_data = *FrameBuffer;
}
/**
@brief Copy Photo
@param String* - pointer to string
@return none
*/
void Camera::CopyPhoto(String* i_data) {
Photo = "";
for (size_t i = 0; i < FrameBuffer->len; i++) {
Photo += (char)FrameBuffer->buf[i];
}
*i_data = Photo;
}
/**
@brief Set Photo Quality
@param uint8_t - photo quality
@return none
*/
void Camera::SetPhotoQuality(uint8_t i_data) {
config->SavePhotoQuality(i_data);
PhotoQuality = i_data;
ReinitCameraModule();
}
/**
@brief Set Frame Size
@param uint8_t - frame size
@return none
*/
void Camera::SetFrameSize(uint8_t i_data) {
config->SaveFrameSize(i_data);
FrameSize = i_data;
TFrameSize = TransformFrameSizeDataType(i_data);
ReinitCameraModule();
}
/**
@brief Set Brightness
@param int8_t - brightness
@return none
*/
void Camera::SetBrightness(int8_t i_data) {
config->SaveBrightness(i_data);
brightness = i_data;
ApplyCameraCfg();
}
/**
@brief Set Contrast
@param int8_t - contrast
@return none
*/
void Camera::SetContrast(int8_t i_data) {
config->SaveContrast(i_data);
contrast = i_data;
ApplyCameraCfg();
}
/**
@brief Set Saturation
@param int8_t - saturation
@return none
*/
void Camera::SetSaturation(int8_t i_data) {
config->SaveSaturation(i_data);
saturation = i_data;
ApplyCameraCfg();
}
/**
@brief Set automatic white balance
@param bool - automatic white balance
@return none
*/
void Camera::SetAwb(bool i_data) {
config->SaveAwb(i_data);
awb = i_data;
ApplyCameraCfg();
}
/**
@brief Set automatic white balance gain
@param bool - automatic white balance gain
@return none
*/
void Camera::SetAwbGain(bool i_data) {
config->SaveAwbGain(i_data);
awb_gain = i_data;
ApplyCameraCfg();
}
/**
@brief Set automatic white balance mode
@param uint8_t - automatic white balance mode
@return none
*/
void Camera::SetAwbMode(uint8_t i_data) {
config->SaveAwbMode(i_data);
wb_mode = i_data;
ApplyCameraCfg();
}
/**
@brief Set automatic exposure control 2
@param bool - automatic exposure control 2
@return none
*/
void Camera::SetAec2(bool i_data) {
config->SaveAec2(i_data);
aec2 = i_data;
ApplyCameraCfg();
}
/**
@brief Set automatic exposure level
@param int8_t - automatic exposure level
@return none
*/
void Camera::SetAeLevel(int8_t i_data) {
config->SaveAeLevel(i_data);
ae_level = i_data;
ApplyCameraCfg();
}
/**
@brief Set automatic exposition control value
@param uint16_t - automatic exposure control value
@return none
*/
void Camera::SetAecValue(uint16_t i_data) {
config->SaveAecValue(i_data);
aec_value = i_data;
ApplyCameraCfg();
}
/**
@brief Set Gain Ctrl
@param bool - gain ctrl
@return none
*/
void Camera::SetGainCtrl(bool i_data) {
config->SaveGainCtrl(i_data);
gain_ctrl = i_data;
ApplyCameraCfg();
}
/**
@brief Set automatic gain control gain
@param uint8_t - automatic gain control gain
@return none
*/
void Camera::SetAgcGain(uint8_t i_data) {
config->SaveAgcGain(i_data);
agc_gain = i_data;
ApplyCameraCfg();
}
/**
@brief Set bad pixel correction
@param bool - bad pixel correction
@return none
*/
void Camera::SetBpc(bool i_data) {
config->SaveBpc(i_data);
bpc = i_data;
ApplyCameraCfg();
}
/**
@brief Set white pixel correction
@param bool - white pixel correction
@return none
*/
void Camera::SetWpc(bool i_data) {
config->SaveWpc(i_data);
wpc = i_data;
ApplyCameraCfg();
}
/**
@brief Set Raw Gama
@param bool - raw gama
@return none
*/
void Camera::SetRawGama(bool i_data) {
config->SaveRawGama(i_data);
raw_gama = i_data;
ApplyCameraCfg();
}
/**
@brief Set horizontal Mirror
@param bool - horizontal mirror
@return none
*/
void Camera::SetHMirror(bool i_data) {
config->SaveHmirror(i_data);
hmirror = i_data;
ApplyCameraCfg();
}
/**
@brief Set V Flip
@param bool - vflip
@return none
*/
void Camera::SetVFlip(bool i_data) {
config->SaveVflip(i_data);
vflip = i_data;
ApplyCameraCfg();
}
/**
@brief Set Lens correction
@param bool - lens correction
@return none
*/
void Camera::SetLensC(bool i_data) {
config->SaveLensCorrect(i_data);
lensc = i_data;
ApplyCameraCfg();
}
/**
@brief Set Exposure Ctrl
@param bool - exposure ctrl
@return none
*/
void Camera::SetExposureCtrl(bool i_data) {
config->SaveExposureCtrl(i_data);
exposure_ctrl = i_data;
ApplyCameraCfg();
}
/**
@brief Set Camera Flash Enable
@param bool - flash enable/disable
@return none
*/
void Camera::SetCameraFlashEnable(bool i_data) {
config->SaveCameraFlashEnable(i_data);
CameraFlashEnable = i_data;
}
/**
@brief Set Camera Flash Time
@param uint16_t - flash time
@return none
*/
void Camera::SetCameraFlashTime(uint16_t i_data) {
config->SaveCameraFlashTime(i_data);
CameraFlashTime = i_data;
}
/**
@brief Get Photo Quality
@param none
@return uint8_t - photo quality
*/
uint8_t Camera::GetPhotoQuality() {
return PhotoQuality;
}
/**
@brief Get Frame Size
@param none
@return uint8_t - frame size
*/
uint8_t Camera::GetFrameSize() {
return FrameSize;
}
/**
* @brief transform framesize_t to uint16_t width
*
FRAMESIZE_QVGA (320 x 240)
FRAMESIZE_CIF (352 x 288)
FRAMESIZE_VGA (640 x 480)
FRAMESIZE_SVGA (800 x 600)
FRAMESIZE_XGA (1024 x 768)
FRAMESIZE_SXGA (1280 x 1024)
FRAMESIZE_UXGA (1600 x 1200)
*
* @return uint16_t
*/
uint16_t Camera::GetFrameSizeWidth() {
uint16_t ret = 0;
switch (FrameSize) {
case 0:
ret = 320;
break;
case 1:
ret = 352;
break;
case 2:
ret = 640;
break;
case 3:
ret = 800;
break;
case 4:
ret = 1024;
break;
case 5:
ret = 1280;
break;
case 6:
ret = 1600;
break;
default:
ret = 320;
break;
}
return ret;
}
/**
* @brief transform framesize_t to uint16_t height
*
FRAMESIZE_QVGA (320 x 240)
FRAMESIZE_CIF (352 x 288)
FRAMESIZE_VGA (640 x 480)
FRAMESIZE_SVGA (800 x 600)
FRAMESIZE_XGA (1024 x 768)
FRAMESIZE_SXGA (1280 x 1024)
FRAMESIZE_UXGA (1600 x 1200)
*
* @return uint16_t
*/
uint16_t Camera::GetFrameSizeHeight() {
uint16_t ret = 0;
switch (FrameSize) {
case 0:
ret = 240;
break;
case 1:
ret = 288;
break;
case 2:
ret = 480;
break;
case 3:
ret = 600;
break;
case 4:
ret = 768;
break;
case 5:
ret = 1024;
break;
case 6:
ret = 1200;
break;
default:
ret = 240;
break;
}
return ret;
}
/**
@brief Get Brightness
@param none
@return int8_t - brightness
*/
int8_t Camera::GetBrightness() {
return brightness;
}
/**
@brief Get Contrast
@param none
@return int8_t - contrast
*/
int8_t Camera::GetContrast() {
return contrast;
}
/**
@brief Get Saturation
@param none
@return int8_t - saturation
*/
int8_t Camera::GetSaturation() {
return saturation;
}
/**
@brief Get Auto white balance status
@param none
@return bool - Auto white balance status
*/
bool Camera::GetAwb() {
return awb;
}
/**
@brief Get Auto white balance gain status
@param none
@return bool - Auto white balance gain status
*/
bool Camera::GetAwbGain() {
return awb_gain;
}
/**
@brief Get Auto white balance mode
@param none
@return uint8_t - Auto white balance mode
*/
uint8_t Camera::GetAwbMode() {
return wb_mode;
}
/**
@brief Get automatic exposure control 2 status
@param none
@return bool - Automatic exposure control 2 status
*/
bool Camera::GetAec2() {
return aec2;
}
/**
@brief Get automatic exposure level
@param none
@return int8_t - Automatic exposure level
*/
int8_t Camera::GetAeLevel() {
return ae_level;
}
/**
@brief Get automatic exposure control value
@param none
@return uint16_t - Automatic exposure control value
*/
uint16_t Camera::GetAecValue() {
return aec_value;
}
/**
@brief Get Gain Ctrl status
@param none
@return bool - Gain control status
*/
bool Camera::GetGainCtrl() {
return gain_ctrl;
}
/**
@brief Get Agc Gaint
@param none
@return uint8_t - agc gain
*/
uint8_t Camera::GetAgcGaint() {
return agc_gain;
}
/**
@brief Get Bpc
@param none
@return bool - bpc status
*/
bool Camera::GetBpc() {
return bpc;
}
/**
@brief Get Wpc
@param none
@return bool - wpc status
*/
bool Camera::GetWpc() {
return wpc;
}
/**
@brief Get Raw Gama value
@param none
@return bool - raw gamma value
*/
bool Camera::GetRawGama() {
return raw_gama;
}
/**
@brief Get horizontal Mirror status
@param none
@return bool - horizontal mirror status
*/
bool Camera::GetHMirror() {
return hmirror;
}
/**
@brief Get vertical Flip status
@param none
@return bool - vertical flip status
*/
bool Camera::GetVFlip() {
return vflip;
}
/**
@brief Get Lens correction status
@param none
@return bool - lens correction status
*/
bool Camera::GetLensC() {
return lensc;
}
/**
@brief Get exposure control status
@param none
@return bool - exposure control status
*/
bool Camera::GetExposureCtrl() {
return exposure_ctrl;
}
/**
@brief Get Camera Flash Enable status
@param none
@return bool - camera flash enable status
*/
bool Camera::GetCameraFlashEnable() {
return CameraFlashEnable;
}
/**
* @brief Get camera flash time
* @param none
* @return uint16_t - camera flash time
*/
uint16_t Camera::GetCameraFlashTime() {
return CameraFlashTime;
}
/* EOF */

View File

@ -0,0 +1,150 @@
/**
@file camera.h
@brief Library for working with a camera
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#ifndef _CAMERA_H_
#define _CAMERA_H_
#include "esp_camera.h"
#include "esp_timer.h"
#include "img_converters.h"
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "cfg.h"
#include "Camera_cfg.h"
#include "Arduino.h"
#include "mcu_cfg.h"
#include "var.h"
#include "log.h"
class Camera {
private:
uint8_t PhotoQuality; ///< photo quality
uint8_t FrameSize; ///< framesize
int8_t brightness; ///< brightness
int8_t contrast; ///< contrast
int8_t saturation; ///< saturation
bool awb; ///< automatic white balancing
bool awb_gain; ///< automatic white balancing gain
uint8_t wb_mode; ///< white balancing mode
bool aec2; ///< automatic exposition controll
int8_t ae_level; ///< automatic exposition level
uint16_t aec_value; ///< automatic exposition time
bool gain_ctrl; ///< automatic gain
uint8_t agc_gain; ///< automatic gain level
bool bpc; ///< bad pixel correction
bool wpc; ///< white pixel correction
bool raw_gama; ///< raw gama correction
bool hmirror; ///< horizontal mirror
bool vflip; ///< vertical flip
bool lensc; ///< lens corection
bool exposure_ctrl; ///< exposure control
bool CameraFlashEnable; ///< enable/disable camera flash function
uint16_t CameraFlashTime; ///< camera fash duration time
uint8_t CameraFlashPin; ///< GPIO pin for LED
framesize_t TFrameSize; ///< framesize_t type for camera module
/* OV2640 camera module pinout and cfg*/
camera_config_t CameraConfig; ///< camera configuration
camera_fb_t *FrameBuffer; ///< frame buffer
String Photo; ///< photo in string format
bool StreamOnOff; ///< stream on/off
SemaphoreHandle_t frameBufferSemaphore; ///< semaphore for frame buffer
float StreamAverageFps; ///< stream average fps
uint16_t StreamAverageSize; ///< stream average size
Configuration *config; ///< pointer to Configuration object
Logs *log; ///< pointer to Logs object
void InitCameraModule();
public:
Camera(Configuration*, Logs*, uint8_t);
~Camera(){};
void Init();
void ApplyCameraCfg();
void LoadCameraCfgFromEeprom();
void ReinitCameraModule();
void CapturePhoto();
void CaptureStream(camera_fb_t *);
void CaptureReturnFrameBuffer();
void SetStreamStatus(bool);
bool GetStreamStatus();
void StreamSetFrameSize(uint16_t);
void StreamSetFrameFps(float);
uint16_t StreamGetFrameAverageSize();
float StreamGetFrameAverageFps();
void StreamClearFrameData();
void CopyPhoto(camera_fb_t *);
void CopyPhoto(String*);
String GetPhoto();
camera_fb_t *GetPhotoFb();
framesize_t TransformFrameSizeDataType(uint8_t);
void SetFlashStatus(bool);
bool GetFlashStatus();
void SetPhotoQuality(uint8_t);
void SetFrameSize(uint8_t);
void SetBrightness(int8_t);
void SetContrast(int8_t);
void SetSaturation(int8_t);
void SetAwb(bool);
void SetAwbGain(bool);
void SetAwbMode(uint8_t);
void SetAec2(bool);
void SetAeLevel(int8_t);
void SetAecValue(uint16_t);
void SetGainCtrl(bool);
void SetAgcGain(uint8_t);
void SetBpc(bool);
void SetWpc(bool);
void SetRawGama(bool);
void SetHMirror(bool);
void SetVFlip(bool);
void SetLensC(bool);
void SetExposureCtrl(bool);
void SetCameraFlashEnable(bool);
void SetCameraFlashTime(uint16_t);
uint8_t GetPhotoQuality();
uint8_t GetFrameSize();
uint16_t GetFrameSizeWidth();
uint16_t GetFrameSizeHeight();
int8_t GetBrightness();
int8_t GetContrast();
int8_t GetSaturation();
bool GetAwb();
bool GetAwbGain();
uint8_t GetAwbMode();
bool GetAec2();
int8_t GetAeLevel();
uint16_t GetAecValue();
bool GetGainCtrl();
uint8_t GetAgcGaint();
bool GetBpc();
bool GetWpc();
bool GetRawGama();
bool GetHMirror();
bool GetVFlip();
bool GetLensC();
bool GetExposureCtrl();
bool GetCameraFlashEnable();
uint16_t GetCameraFlashTime();
};
extern Camera SystemCamera; ///< Camera object
#endif
/* EOF */

File diff suppressed because it is too large Load Diff

125
ESP32_PrusaConnectCam/cfg.h Normal file
View File

@ -0,0 +1,125 @@
/**
@file cfg.h
@brief Library for save and load MCU configuration
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#ifndef _CFG_H_
#define _CFG_H_
#include <WiFi.h>
#include <EEPROM.h>
#include "Arduino.h"
#include <ArduinoUniqueID.h>
#include <base64.h>
#include "mcu_cfg.h"
#include "Camera_cfg.h"
#include "var.h"
#include "log.h"
class Configuration {
public:
Configuration(Logs*);
~Configuration(){};
void Init();
bool CheckActifeWifiCfgFlag();
void CheckResetCfg();
void SaveRefreshInterval(uint8_t);
void SaveToken(String);
void SaveFingerprint(String);
void SavePhotoQuality(uint8_t);
void SaveFrameSize(uint8_t);
void SaveBrightness(int8_t);
void SaveContrast(int8_t);
void SaveSaturation(int8_t);
void SaveHmirror(bool);
void SaveVflip(bool);
void SaveLensCorrect(bool);
void SaveExposureCtrl(bool);
void SaveAwb(bool);
void SaveAwbGain(bool);
void SaveAwbMode(uint8_t);
void SaveBpc(bool);
void SaveWpc(bool);
void SaveRawGama(bool);
void SaveWifiSsid(String);
void SaveWifiPassword(String);
void SaveWifiCfgFlag(uint8_t);
void SaveBasicAuthUsername(String);
void SaveBasicAuthPassword(String);
void SaveBasicAuthFlag(bool);
void SaveCameraFlashEnable(uint8_t);
void SaveCameraFlashTime(uint16_t);
void SaveMdnsRecord(String);
void SaveAec2(bool);
void SaveAeLevel(int8_t);
void SaveAecValue(uint16_t);
void SaveGainCtrl(bool);
void SaveAgcGain(uint8_t);
void SaveLogLevel(LogLevel_enum);
void SavePrusaConnectHostname(String);
uint8_t LoadRefreshInterval();
String LoadToken();
String LoadFingerprint();
uint8_t LoadPhotoQuality();
uint8_t LoadFrameSize();
int8_t LoadBrightness();
int8_t LoadContrast();
int8_t LoadSaturation();
bool LoadHmirror();
bool LoadVflip();
bool LoadLensCorrect();
bool LoadExposureCtrl();
bool LoadAwb();
bool LoadAwbGain();
uint8_t LoadAwbMode();
bool LoadBpc();
bool LoadWpc();
bool LoadRawGama();
String LoadWifiSsid();
String LoadWifiPassowrd();
String LoadBasicAuthUsername();
String LoadBasicAuthPassword();
bool LoadBasicAuthFlag();
bool LoadCameraFlashEnable();
uint16_t LoadCameraFlashTime();
String LoadMdnsRecord();
bool LoadAec2();
int8_t LoadAeLevel();
uint16_t LoadAecValue();
bool LoadGainCtrl();
uint8_t LoadAgcGain();
String LoadPrusaConnectHostname();
private:
Logs *Log; ///< Pointer to Logs object
String WiFiMacAddress; ///< WiFi MAC address
void ReadCfg();
void DefaultCfg();
bool CheckFirstMcuStart();
void SaveFirstMcuStartFlag(uint8_t);
void GetFingerprint();
void SaveUint8(uint16_t, uint8_t);
void SaveInt8(uint16_t, int8_t);
void SaveBool(uint16_t, bool);
void SaveUint16(uint16_t, uint16_t);
void SaveString(uint16_t, uint16_t, String);
uint16_t LoadUint16(uint16_t);
String LoadString(uint16_t, uint16_t, bool);
};
extern Configuration SystemConfig; ///< Configuration object
#endif
/* EOF */

View File

@ -0,0 +1,462 @@
/**
@file connnect.cpp
@brief library for communication with prusa connect backend
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#include "connect.h"
PrusaConnect Connect(&SystemConfig, &SystemLog, &SystemCamera);
/**
* @brief Constructor for PrusaConnect class
*
* @param Configuration* - pointer to Configuration class
* @param Logs* - pointer to Logs class
* @param Camera* - pointer to Camera class
*/
PrusaConnect::PrusaConnect(Configuration *i_conf, Logs *i_log, Camera *i_camera) {
config = i_conf;
log = i_log;
camera = i_camera;
BackendAvailability = WaitForFirstConnection;
SendDeviceInformationToBackend = true;
}
/**
* @brief init library PrusaConnect
*
* @param none
* @return none
*/
void PrusaConnect::Init() {
log->AddEvent(LogLevel_Info, "Init PrusaConnect lib");
TakePicture();
}
/**
* @brief Load configuration from EEPROM
*
* @param none
* @return none
*/
void PrusaConnect::LoadCfgFromEeprom() {
log->AddEvent(LogLevel_Info, "Load PrusaConnect CFG from EEPROM");
Token = config->LoadToken();
Fingerprint = config->LoadFingerprint();
RefreshInterval = config->LoadRefreshInterval();
PrusaConnectHostname = config->LoadPrusaConnectHostname();
}
/**
* @brief take picture
*
* @param none
* @return none
*/
void PrusaConnect::TakePicture() {
camera->CapturePhoto();
}
/**
* @brief Sending data to prusa connect backend
*
* @param i_data - data to send
* @param i_content_type - data content type
* @param i_type - type of data for log message
* @param i_url_path - url path for backend
* @param i_fragmentation - flag for enable/disable data fragmentation
* @return true - if data was sent successfully
* @return false - if data was not sent successfully
*/
bool PrusaConnect::SendDataToBackend(String *i_data, String i_content_type, String i_type, String i_url_path, bool i_fragmentation) {
WiFiClientSecure client;
BackendReceivedStatus = "";
bool ret = false;
log->AddEvent(LogLevel_Info, "Sending " + i_type + " to PrusaConnect");
/* check fingerprint and token length */
if ((Fingerprint.length() > 0) && (Token.length() > 0)) {
client.setCACert(root_CAs);
log->AddEvent(LogLevel_Verbose, "Connecting to server...");
/* connecting to server */
if (!client.connect(PrusaConnectHostname.c_str(), 443)) {
char err_buf[200];
int last_error = client.lastError(err_buf, sizeof(err_buf));
int error = client.getWriteError();
if (BackendAvailability != WaitForFirstConnection) {
BackendAvailability = BackendUnavailable;
}
BackendReceivedStatus = "Connetion failed to domain! Error: " + String(last_error) + " - " + String(err_buf) + " : " + String(error);
log->AddEvent(LogLevel_Info, BackendReceivedStatus + " ,BA:" + CovertBackendAvailabilitStatusToString(BackendAvailability));
return false;
} else {
/* send data to server */
log->AddEvent(LogLevel_Verbose, "Connected to server!");
client.println("PUT https://" + PrusaConnectHostname + i_url_path + " HTTP/1.0");
client.println("Host: " + PrusaConnectHostname);
client.println("User-Agent: ESP32-CAM");
client.println("Connection: close");
client.println("Content-Type: " + i_content_type);
client.println("fingerprint: " + Fingerprint);
client.println("token: " + Token);
client.print("Content-Length: ");
client.println(i_data->length());
client.println();
esp_task_wdt_reset();
if (true == i_fragmentation) {
log->AddEvent(LogLevel_Verbose, "Send data fragmented");
for (int index = 0; index < i_data->length(); index = index + PHOTO_FRAGMENT_SIZE) {
client.print(i_data->substring(index, index + PHOTO_FRAGMENT_SIZE));
log->AddEvent(LogLevel_Verbose, String(index));
}
} else {
log->AddEvent(LogLevel_Verbose, "Send data");
client.print(*i_data);
}
log->AddEvent(LogLevel_Info, "Send done");
esp_task_wdt_reset();
String response = "";
String fullResponse = "";
log->AddEvent(LogLevel_Verbose, "Response:");
while (client.connected()) {
if (client.available()) {
response = client.readStringUntil('\n');
fullResponse += response;
log->AddEvent(LogLevel_Verbose, response.c_str());
if (response.startsWith("HTTP/1.1")) {
int httpCode = response.substring(9, 12).toInt();
BackendReceivedStatus = i_type;
BackendReceivedStatus += ": ";
BackendReceivedStatus += ProcessHttpResponseCode(httpCode);
if (true == ProcessHttpResponseCodeBool(httpCode)) {
ret = true;
}
}
}
}
log->AddEvent(LogLevel_Verbose, "Full response: " + fullResponse);
BackendAvailability = BackendAvailable;
client.stop();
}
} else {
/* err message */
log->AddEvent(LogLevel_Verbose, "ERROR SEND DATA TO SERVER! INVALID DATA!");
log->AddEvent(LogLevel_Verbose, "Fingerprint: " + Fingerprint);
log->AddEvent(LogLevel_Verbose, "Token: " + Token);
if (Fingerprint.length() == 0) {
BackendReceivedStatus = "Missing fingerprint";
} else if (Token.length() == 0) {
BackendReceivedStatus = "Missing token";
}
}
log->AddEvent(LogLevel_Info, "Upload done. Response code: " + BackendReceivedStatus + " ,BA:" + CovertBackendAvailabilitStatusToString(BackendAvailability));
return ret;
}
/**
* @brief Send photo to prusa connect backend
*
* @param none
* @return none
*/
void PrusaConnect::SendPhotoToBackend() {
log->AddEvent(LogLevel_Info, "Start sending photo to prusaconnect");
camera->CopyPhoto(&Photo);
SendDataToBackend(&Photo, "image/jpg", "Photo", HOST_URL_CAM_PATH, true);
}
/**
* @brief seding device info to prusaconnect backend
*
*/
void PrusaConnect::SendInfoToBackend() {
if (false == SendDeviceInformationToBackend) {
return;
} else {
log->AddEvent(LogLevel_Info, "Start sending device information to prusaconnect");
JsonDocument json_data;
String json_string = "";
JsonObject config = json_data["config"].to<JsonObject>();
config["name"] = "ESP32-CAM";
JsonObject resolution = config["resolution"].to<JsonObject>();
resolution["width"] = SystemCamera.GetFrameSizeWidth();
resolution["height"] = SystemCamera.GetFrameSizeHeight();
JsonObject network_info = config["network_info"].to<JsonObject>();
network_info["wifi_mac"] = SystemWifiMngt.GetWifiMac();
network_info["wifi_ipv4"] = SystemWifiMngt.GetStaIp();
network_info["wifi_ssid"] = SystemWifiMngt.GetStaSsid();
serializeJson(json_data, json_string);
log->AddEvent(LogLevel_Info, "Data: " + json_string);
bool response = SendDataToBackend(&json_string, "application/json", "Info", HOST_URL_INFO_PATH, false);
if (true == response) {
SendDeviceInformationToBackend = false;
}
}
}
/**
* @brief Take picture and send to backend
*
* @param none
* @return none
*/
void PrusaConnect::TakePictureAndSendToBackend() {
TakePicture();
SendPhotoToBackend();
}
/**
@brief Function for processing http response code from prusa backend
@param int - http response code
@return none
*/
String PrusaConnect::ProcessHttpResponseCode(int code) {
String ret = "";
switch (code) {
case 200:
ret = "200 - OK";
break;
case 201:
ret = "201 - OK entry created";
break;
case 204:
ret = "204 - Upload OK";
break;
case 304:
ret = "304 - Response has not been modified";
break;
case 400:
ret = "400 - Some data received is not valid";
break;
case 401:
ret = "401 - Missing security toker or it is not valid";
break;
case 403:
ret = "403 - Security toke is not valid or is outdated";
break;
case 404:
ret = "404 - Entity not found or invalid auth token";
break;
case 409:
ret = "409 - Conflict with the state of target resource (user error)";
break;
case 503:
ret += "503 - Service is unavailable at this moment. Try again later";
break;
default:
ret = String(code);
ret += " - unknown error code";
break;
}
return ret;
}
/**
* @brief Translate http response code to boolean
*
* @param code - http response code
* @return true - if response code is OK
* @return false - if response code is not OK
*/
bool PrusaConnect::ProcessHttpResponseCodeBool(int code) {
bool ret = false;
switch (code) {
case 200:
ret = true;
break;
case 201:
ret = true;
break;
case 204:
ret = true;
break;
case 304:
ret = false;
break;
case 400:
ret = false;
break;
case 401:
ret = false;
break;
case 403:
ret = false;
break;
case 404:
ret = false;
break;
case 409:
ret = false;
break;
case 503:
ret = false;
break;
default:
ret = false;
break;
}
return ret;
}
/**
* @brief Update device information
*
* @param none
* @return none
*/
void PrusaConnect::UpdateDeviceInformation() {
SendDeviceInformationToBackend = true;
}
/**
* @brief Set refresh interval
*
* @param uint8_t i_data - refresh interval
* @return none
*/
void PrusaConnect::SetRefreshInterval(uint8_t i_data) {
RefreshInterval = i_data;
config->SaveRefreshInterval(RefreshInterval);
}
/**
* @brief Set token
*
* @param String i_data - token
* @return none
*/
void PrusaConnect::SetToken(String i_data) {
Token = i_data;
config->SaveToken(Token);
}
/**
* @brief Set backend availability status
*
* @param BackendAvailabilitStatus - backend status
* @return none
*/
void PrusaConnect::SetBackendAvailabilitStatus(BackendAvailabilitStatus i_data) {
BackendAvailability = i_data;
}
/**
* @brief set prusa connect hostname
*
* @param String i_data - hostname
*/
void PrusaConnect::SetPrusaConnectHostname(String i_data) {
PrusaConnectHostname = i_data;
config->SavePrusaConnectHostname(PrusaConnectHostname);
}
/**
* @brief Get refresh interval
*
* @param none
* @return uint8_t - refresh interval
*/
uint8_t PrusaConnect::GetRefreshInterval() {
return RefreshInterval;
}
/**
* @brief get backend received status
*
* @param none
* @return String - backend received status
*/
String PrusaConnect::GetBackendReceivedStatus() {
return BackendReceivedStatus;
}
/**
* @brief get token
*
* @param none
* @return String - token
*/
String PrusaConnect::GetToken() {
return Token;
}
/**
* @brief get fingerprint
*
* @param none
* @return String - fingerprint
*/
String PrusaConnect::GetFingerprint() {
return Fingerprint;
}
/**
* @brief get prusa connect hostname
*
* @return String - hostanme
*/
String PrusaConnect::GetPrusaConnectHostname() {
return PrusaConnectHostname;
}
/**
* @brief Get backend availability status
*
* @param none
* @return BackendAvailabilitStatus - backend status
*/
BackendAvailabilitStatus PrusaConnect::GetBackendAvailabilitStatus() {
return BackendAvailability;
}
/**
* @brief Convert backend availability status to string
* @param BackendAvailabilitStatus - backend status
* @return String - backend status as string
*/
String PrusaConnect::CovertBackendAvailabilitStatusToString(BackendAvailabilitStatus i_data) {
String ret = "";
switch (i_data) {
case WaitForFirstConnection:
ret = "Wait for first connection";
break;
case BackendAvailable:
ret = "Backend available";
break;
case BackendUnavailable:
ret = "Backend unavailable";
break;
default:
ret = "Unknown";
break;
}
return ret;
}
/* EOF */

View File

@ -0,0 +1,88 @@
/**
@file connnect.h
@brief library for communication with prusa connect backend
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#ifndef _PRUSA_CONNECT_H_
#define _PRUSA_CONNECT_H_
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <esp_task_wdt.h>
#include "Arduino.h"
#include <ArduinoJson.h>
#include "wifi_mngt.h"
#include "mcu_cfg.h"
#include "var.h"
#include "log.h"
#include "camera.h"
#include "cfg.h"
#include "Certificate.h"
/**
* @brief BackendAvailabilitStatus enum
* status of backend availability
*/
enum BackendAvailabilitStatus {
WaitForFirstConnection = 0, ///< waiting for first connection to backend
BackendAvailable = 1, ///< backend is available
BackendUnavailable = 2, ///< backend is unavailable
};
class PrusaConnect {
private:
uint8_t RefreshInterval; ///< interval for sending photo to backend
String BackendReceivedStatus; ///< status of backend response
BackendAvailabilitStatus BackendAvailability; ///< status of backend availability
bool SendDeviceInformationToBackend; ///< flag for sending device information to backend
String Token; ///< token for backend communication
String Fingerprint; ///< fingerprint for backend communication
String Photo; ///< photo for sending to backend
String PrusaConnectHostname; ///< hostname of prusa connect backend
Configuration *config; ///< pointer to configuration object
Logs *log; ///< pointer to logs object
Camera *camera; ///< pointer to camera object
bool SendDataToBackend(String *, String, String, String, bool);
public:
PrusaConnect(Configuration*, Logs*, Camera*);
~PrusaConnect(){};
void Init();
void LoadCfgFromEeprom();
void TakePicture();
void SendPhotoToBackend();
void SendInfoToBackend();
void TakePictureAndSendToBackend();
String ProcessHttpResponseCode(int);
bool ProcessHttpResponseCodeBool(int);
void UpdateDeviceInformation();
void SetRefreshInterval(uint8_t);
void SetToken(String);
void SetBackendAvailabilitStatus(BackendAvailabilitStatus);
void SetPrusaConnectHostname(String);
uint8_t GetRefreshInterval();
String GetBackendReceivedStatus();
String GetToken();
String GetFingerprint();
String GetPrusaConnectHostname();
BackendAvailabilitStatus GetBackendAvailabilitStatus();
String CovertBackendAvailabilitStatusToString(BackendAvailabilitStatus);
};
extern PrusaConnect Connect; ///< PrusaConnect object
#endif

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,271 @@
/**
@file log.cpp
@brief log library
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#include "log.h"
Logs SystemLog(LOGS_FILE_PATH, LOGS_FILE_NAME, LOGS_FILE_MAX_SIZE);
/**
@info Constructor
@param none
@return none
*/
Logs::Logs() {
FileName = "log.txt";
FilePath = "/";
LogLevel = LogLevel_Verbose;
FileMaxSize = 1024;
NtpTimeSynced = false;
}
/**
* @brief Construct a new Logs:: Logs object with file path and file name
*
* @param String - file path
* @param String - file name
*/
Logs::Logs(String i_FilePath, String i_FileName) {
FileName = i_FileName;
FilePath = i_FilePath;
LogLevel = LogLevel_Verbose;
FileMaxSize = 1024;
NtpTimeSynced = false;
}
/**
* @brief Construct a new Logs:: Logs object with log level, file path and file name
*
* @param LogLevel_enum - log level
* @param String - file path
* @param String - file name
*/
Logs::Logs(LogLevel_enum i_LogLevel, String i_FilePath, String i_FileName) {
FileName = i_FileName;
FilePath = i_FilePath;
LogLevel = i_LogLevel;
FileMaxSize = 1024;
NtpTimeSynced = false;
}
/**
* @brief Construct a new Logs:: Logs object with file path, file name and file size
*
* @param String - file path
* @param String - file name
* @param uint16_t - file size
*/
Logs::Logs(String i_FilePath, String i_FileName, uint16_t i_FileSize) {
FileName = i_FileName;
FilePath = i_FilePath;
LogLevel = LogLevel_Verbose;
FileMaxSize = i_FileSize;
NtpTimeSynced = false;
}
/**
* @brief Construct a new Logs:: Logs object with log level, file path, file name and file size
*
* @param LogLevel_enum - log level
* @param String - file path
* @param String - file name
* @param uint16_t - file size
*/
Logs::Logs(LogLevel_enum i_LogLevel, String i_FilePath, String i_FileName, uint16_t i_FileSize) {
FileName = i_FileName;
FilePath = i_FilePath;
LogLevel = i_LogLevel;
FileMaxSize = i_FileSize;
NtpTimeSynced = false;
}
/**
@info Init library for logs
@param LogLevel_enum - log level
@param String - file path
@param String - file name
@return none
*/
void Logs::Init() {
Serial.println("----------------------------------------------------------------");
Serial.println("Init Logs library");
/* init micro SD card */
InitSdCard();
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));
}
/* added first message to log file after start MCU */
String msg = "----------------------------------------------------------------\n";
msg += "Start MCU!\nSW Version: ";
msg += String(SW_VERSION);
msg += " ,Build: ";
msg += String(SW_BUILD);
msg += "\n";
msg += "Verbose mode: ";
msg += (true == CONSOLE_VERBOSE_DEBUG) ? "true" : "false";
msg += "\n";
msg += "Log level: ";
msg += String(LogLevel);
msg += "\n";
AppendFile(SD_MMC, FilePath + FileName, msg);
} else {
Serial.println("Micro-SD card not found! Disable logs");
}
}
/**
@info set log level
@param LogLevel_enum - log level
@return none
*/
void Logs::SetLogLevel(LogLevel_enum level) {
LogLevel = level;
}
/**
@info Add new log event
@param LogLevel_enum - log level
@param String - log message
@param bool - new line
@param bool - date
@return none
*/
void Logs::AddEvent(LogLevel_enum level, String msg, bool newLine, bool date) {
if (LogLevel >= level) {
String LogMsg = "";
if (true == date) {
LogMsg += GetSystemTime();
LogMsg += " - ";
}
LogMsg += msg;
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
@return none
*/
void Logs::SetFileName(String i_data) {
FileName = i_data;
}
/**
@info Set file path
@param String - file path
@return none
*/
void Logs::SetFilePath(String i_data) {
FilePath = i_data;
}
/**
@info Set file max size
@param uint16_t - file max size
@return none
*/
void Logs::SetFileMaxSize(uint16_t i_data) {
FileMaxSize = i_data;
}
/**
@info Set NTP time synced
@param bool - NTP time synced
@return none
*/
void Logs::SetNtpTimeSynced(bool i_data) {
NtpTimeSynced = i_data;
AddEvent(LogLevel_Info, "System time: " + GetSystemTime());
}
/**
@info Get file name
@param none
@return String - file name
*/
String Logs::GetFileName() {
return FileName;
}
/**
@info Get file path
@param none
@return String - file path
*/
String Logs::GetFilePath() {
return FilePath;
}
/**
@info Get log level
@param none
@return LogLevel_enum - log level
*/
LogLevel_enum Logs::GetLogLevel() {
return LogLevel;
}
/**
@info Get NTP time synced
@param none
@return bool - NTP time synced
*/
bool Logs::GetNtpTimeSynced() {
return NtpTimeSynced;
}
/**
@info Get system time
@param none
@return String - time
*/
String Logs::GetSystemTime() {
String ret = "0000-00-00_00-00-00";
if (true == NtpTimeSynced) {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.println("Failed to obtain time");
#endif
return ret;
}
char timeString[20];
strftime(timeString, sizeof(timeString), "%Y-%m-%d_%H-%M-%S", &timeinfo);
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.println(timeString);
#endif
ret = String(timeString);
}
return ret;
}
/* EOF */

View File

@ -0,0 +1,65 @@
/**
@file log.h
@brief log library
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#ifndef _LOG_H_
#define _LOG_H_
#include "Arduino.h"
#include "mcu_cfg.h"
#include "var.h"
#include "micro_sd.h"
enum LogLevel_enum {
LogLevel_Error = 0, ///< Error
LogLevel_Warning = 1, ///< Warning
LogLevel_Info = 2, ///< Info
LogLevel_Verbose = 3 ///< Verbose
};
class Logs : public MicroSd {
private:
LogLevel_enum LogLevel; ///< LogLevel
String FileName; ///< log File name
String FilePath; ///< log file patch
uint16_t FileMaxSize; ///< log file max size
bool NtpTimeSynced; ///< status NTP time sync
public:
Logs();
Logs(String, String);
Logs(LogLevel_enum, String, String);
Logs(String, String, uint16_t);
Logs(LogLevel_enum, String, String, uint16_t);
~Logs(){};
void Init();
void AddEvent(LogLevel_enum, String, bool = true, bool = true);
void SetLogLevel(LogLevel_enum);
void SetFileName(String);
void SetFilePath(String);
void SetFileMaxSize(uint16_t);
void SetNtpTimeSynced(bool);
String GetFileName();
String GetFilePath();
LogLevel_enum GetLogLevel();
bool GetNtpTimeSynced();
protected:
String GetSystemTime();
};
extern Logs SystemLog; ///< log object
#endif
/* EOF */

View File

@ -0,0 +1,252 @@
/**
@file mcu_cfg.h
@brief Library configuration MCU
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#ifndef _MCU_CFG_H_
#define _MCU_CFG_H_
/* ---------------- BASIC MCU CFG --------------*/
#define SW_VERSION "1.0.0" ///< 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
/* ------------ PRUSA BACKEND CFG --------------*/
#define HOST_URL_CAM_PATH "/c/snapshot" ///< path for sending photo to prusa connect
#define HOST_URL_INFO_PATH "/c/info" ///< path for sending info to prusa connect
#define REFRESH_INTERVAL_MIN 5 ///< minimum refresh interval for sending photo to prusa connect [s]
#define REFRESH_INTERVAL_MAX 240 ///< maximum refresh interval for sending photo to prusa connect [s]
/* --------------- FLASH LED CFG ---------------*/
#define FLASH_GPIO_NUM 4 ///< GPIO pin for light
#define FLASH_OFF_STATUS 0 ///< PWM intensity LED for OFF. 0-2^FLASH_PWM_RESOLUTION = 0-255
#define FLASH_ON_STATUS 205 ///< PWM intensity LED for ON. limitation to 80%. 2^FLASH_PWM_RESOLUTION * 0.8% = 204
#define FLASH_PWM_FREQ 2000 ///< frequency of pwm [240MHz / (100 prescale * pwm cycles)] = frequency
#define FLASH_PWM_CHANNEL 0 ///< channel 0
#define FLASH_PWM_RESOLUTION 8 ///< range 1-20bit. 8bit = 0-255 range
/* -------------- STATUS LED CFG ----------------*/
#define STATUS_LED_GPIO_NUM 33 ///< GPIO pin for status LED
#define STATUS_LED_ENABLE true ///< enable/disable status LED
#define STATUS_LED_ON_DURATION 100 ///< time for blink status LED when is module in the ON state [ms]
#define STATUS_LED_WIFI_AP 400 ///< time for blink status LED when is module in the AP mode [ms]
#define STATUS_LED_STA_CONNECTING 800 ///< time for blink status LED when is module connecting to the WiFi network [ms]
#define STATUS_LED_STA_CONNECTED 4000 ///< time for blink status LED when is module connected to the WiFi network [ms]
#define STATUS_LED_ERROR 100 ///< time for blink status LED when is module in the error state [ms]
/* ------------------- TASKS --------------------*/
#define TASK_SYSTEM 1000 ///< system task interval [ms]
#define TASK_SDCARD 30000 ///< sd card task interval [ms]
#define TASK_WIFI 30000 ///< wifi reconnect interval. Checking when is signal lost [ms]
#define TASK_SERIAL_CFG 1000 ///< serial cfg task interval [ms]
#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]
/* --------------- 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 5000 ///< 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]
#define WEB_CACHE_INTERVAL 86400 ///< cache interval for browser [s] 86400s = 24h
/* --------------- 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
/* ---------- RESET CFG CONFIGURATION ----------*/
#define CFG_RESET_PIN 12 ///< GPIO 16 is for reset CFG to default
#define CFG_RESET_TIME_WAIT 10000 ///< wait to 10 000 ms = 10s for reset cfg during grounded CFG_RESET_PIN
#define CFG_RESET_LOOP_DELAY 100 ///< delay in the loop for reset cfg
/* ---------------- 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]
/* ---------------- 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_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
/* ----------------- WiFi CFG -------------------*/
#define WIFI_STA_WDG_TIMEOUT 60000 ///< STA watchdog timeout [ms]
/* ---------------- FACTORY CFG ----------------*/
#define FACTORY_CFG_PHOTO_REFRESH_INTERVAL 30 ///< in the second
#define FACTORY_CFG_PHOTO_QUALITY 10 ///< 10-63, lower is better
#define FACTORY_CFG_FRAME_SIZE 0 ///< 0 - FRAMESIZE_QVGA, ..., 6 - FRAMESIZE_UXGA. Look function Cfg_TransformFrameSizeDataType
#define FACTORY_CFG_BRIGHTNESS 0 ///< from -2 to 2
#define FACTORY_CFG_CONTRAST 0 ///< from -2 to 2
#define FACTORY_CFG_SATURATION 0 ///< from -2 to 2
#define FACTORY_CFG_H_MIRROR 0 ///< Horizontal mirror. 0 - false, 1 - true
#define FACTORY_CFG_V_FLIP 0 ///< Vertical flip. 0 - false, 1 - true
#define FACTORY_CFG_LENS_CORRECT 1 ///< 0 - false, 1 - true
#define FACTORY_CFG_EXPOSURE_CTRL 1 ///< 0 - false, 1 - true
#define FACTORY_CFG_AWB 1 ///< automatic white balancing 0 - false, 1 - true
#define FACTORY_CFG_AWB_GAIN 1 ///< automatic white balancing gain 0 - false, 1 - true
#define FACTORY_CFG_AWB_MODE 0 ///< automatic white balancing mode (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
#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_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_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
/* ---------------- CFG FLAGS ------------------*/
#define CFG_WIFI_SETTINGS_SAVED 0x0A ///< flag saved config
#define CFG_WIFI_SETTINGS_NOT_SAVED 0xFF ///< flag notsaved config
#define CFG_FIRST_MCU_START_ACK 0xFF ///< flag first MCU start ACK -> yes, it's first mcu start
#define CFG_FIRST_MCU_START_NAK 0x0F ///< flag first MCU start NAK -> no, it's not first MCU start
#define SECOND_TO_MILISECOND 1000 ///< constant for convert ms to second
/* ---------------- EEPROM CFG ------------------*/
#define EEPROM_ADDR_REFRESH_INTERVAL_START 0 ///< whre is stored first byte from refresh data
#define EEPROM_ADDR_REFRESH_INTERVAL_LENGTH 1 ///< how long is the refresh data variable stored in the eeprom [bytes]
#define EEPROM_ADDR_FINGERPRINT_START (EEPROM_ADDR_REFRESH_INTERVAL_START + EEPROM_ADDR_REFRESH_INTERVAL_LENGTH) ///< where is stored first byte from refresh interval
#define EEPROM_ADDR_FINGERPRINT_LENGTH 80 ///< how long is refresh interval [bytes]
#define EEPROM_ADDR_TOKEN_START (EEPROM_ADDR_FINGERPRINT_START + EEPROM_ADDR_FINGERPRINT_LENGTH) ///< where is stored first byte from fingerprint
#define EEPROM_ADDR_TOKEN_LENGTH 40 ///< how long is fingerprint [bytes]
#define EEPROM_ADDR_FRAMESIZE_START (EEPROM_ADDR_TOKEN_START + EEPROM_ADDR_TOKEN_LENGTH) ///< where is stored token
#define EEPROM_ADDR_FRAMESIZE_LENGTH 1 ///< how long is token
#define EEPROM_ADDR_BRIGHTNESS_START (EEPROM_ADDR_FRAMESIZE_START + EEPROM_ADDR_FRAMESIZE_LENGTH) ///< where is stored framesize
#define EEPROM_ADDR_BRIGHTNESS_LENGTH 1 ///< how long is framesize
#define EEPROM_ADDR_CONTRAST_START (EEPROM_ADDR_BRIGHTNESS_START + EEPROM_ADDR_BRIGHTNESS_LENGTH) ///< where is stored brightness
#define EEPROM_ADDR_CONTRAST_LENGTH 1 ///< how long is brightness
#define EEPROM_ADDR_SATURATION_START (EEPROM_ADDR_CONTRAST_START + EEPROM_ADDR_CONTRAST_LENGTH) ///< where is stored contrast
#define EEPROM_ADDR_SATURATION_LENGTH 1 ///< how long is contrast
#define EEPROM_ADDR_HMIRROR_START (EEPROM_ADDR_SATURATION_START + EEPROM_ADDR_SATURATION_LENGTH) ///< where is stored saturation
#define EEPROM_ADDR_HMIRROR_LENGTH 1 ///< how long is saturation
#define EEPROM_ADDR_VFLIP_START (EEPROM_ADDR_HMIRROR_START + EEPROM_ADDR_HMIRROR_LENGTH) ///< where is stored hmirror
#define EEPROM_ADDR_VFLIP_LENGTH 1 ///< how long is hmirror
#define EEPROM_ADDR_LENSC_START (EEPROM_ADDR_VFLIP_START + EEPROM_ADDR_VFLIP_LENGTH) ///< where is stored vflip
#define EEPROM_ADDR_LENSC_LENGTH 1 ///< how long is vflip
#define EEPROM_ADDR_EXPOSURE_CTRL_START (EEPROM_ADDR_LENSC_START + EEPROM_ADDR_LENSC_LENGTH) ///< where is stored lens correction
#define EEPROM_ADDR_EXPOSURE_CTRL_LENGTH 1 ///< how long is lens correction
#define EEPROM_ADDR_PHOTO_QUALITY_START (EEPROM_ADDR_EXPOSURE_CTRL_START + EEPROM_ADDR_EXPOSURE_CTRL_LENGTH) ///< where is stored exposure ctrl
#define EEPROM_ADDR_PHOTO_QUALITY_LENGTH 1 ///< how long is exposure ctrl
#define EEPROM_ADDR_WIFI_SSID_START (EEPROM_ADDR_PHOTO_QUALITY_START + EEPROM_ADDR_PHOTO_QUALITY_LENGTH) ///< where is stored wi-fi ssid
#define EEPROM_ADDR_WIFI_SSID_LENGTH 33 ///< maximum length for IEEE 802.11 is 32 + 1 for save ssid length
#define EEPROM_ADDR_WIFI_PASSWORD_START (EEPROM_ADDR_WIFI_SSID_START + EEPROM_ADDR_WIFI_SSID_LENGTH) ///< where is stored wifi password
#define EEPROM_ADDR_WIFI_PASSWORD_LENGTH 64 ///< maximum length for IEEE 802.11 is 63 + 1 for save password length
#define EEPROM_ADDR_WIFI_ACTIVE_FLAG_START (EEPROM_ADDR_WIFI_PASSWORD_START + EEPROM_ADDR_WIFI_PASSWORD_LENGTH) ///< where is stored information about stored cfg
#define EEPROM_ADDR_WIFI_ACTIVE_FLAG_LENGTH 1 ///< maximum lenght for cfg flag
#define EEPROM_ADDR_BASIC_AUTH_USERNAME_START (EEPROM_ADDR_WIFI_ACTIVE_FLAG_START + EEPROM_ADDR_WIFI_ACTIVE_FLAG_LENGTH) ///< where is stored username for login with basic auth.
#define EEPROM_ADDR_BASIC_AUTH_USERNAME_LENGTH 11 ///< maximum length for username is 10 byte + 1 byte for save length
#define EEPROM_ADDR_BASIC_AUTH_PASSWORD_START (EEPROM_ADDR_BASIC_AUTH_USERNAME_START + EEPROM_ADDR_BASIC_AUTH_USERNAME_LENGTH) ///< where is stored password for login with basic auth
#define EEPROM_ADDR_BASIC_AUTH_PASSWORD_LENGTH 21 ///< maximum length for password is 20 byte + 1 byte for save length
#define EEPROM_ADDR_BASIC_AUTH_ENABLE_FLAG_START (EEPROM_ADDR_BASIC_AUTH_PASSWORD_START + EEPROM_ADDR_BASIC_AUTH_PASSWORD_LENGTH) ///< where is stored flag for enable/disable basic auth from user
#define EEPROM_ADDR_BASIC_AUTH_ENABLE_FLAG_LENGTH 1 ///< how long is flag
#define EEPROM_ADDR_FIRST_MCU_START_FLAG_START (EEPROM_ADDR_BASIC_AUTH_ENABLE_FLAG_START + EEPROM_ADDR_BASIC_AUTH_ENABLE_FLAG_LENGTH) ///< where is stored flag for first MCU start check
#define EEPROM_ADDR_FIRST_MCU_START_FLAG_LENGTH 1 ///< how long is flag
#define EEPROM_ADDR_CAMERA_FLASH_ENABLE_START (EEPROM_ADDR_FIRST_MCU_START_FLAG_START + EEPROM_ADDR_FIRST_MCU_START_FLAG_LENGTH) ///< where is stored flag for enable/disable camera flash
#define EEPROM_ADDR_CAMERA_FLASH_ENABLE_LENGTH 1 ///< how long is flag
#define EEPROM_ADDR_CAMERA_FLASH_TIME_START (EEPROM_ADDR_CAMERA_FLASH_ENABLE_START + EEPROM_ADDR_CAMERA_FLASH_ENABLE_LENGTH) ///< where is stored value camera flash during time
#define EEPROM_ADDR_CAMERA_FLASH_TIME_LENGTH 2 ///< how long is the value
#define EEPROM_ADDR_MDNS_RECORD_START (EEPROM_ADDR_CAMERA_FLASH_TIME_START + EEPROM_ADDR_CAMERA_FLASH_TIME_LENGTH)
#define EEPROM_ADDR_MDNS_RECORD_LENGTH 41
#define EEPROM_ADDR_AWB_ENABLE_START (EEPROM_ADDR_MDNS_RECORD_START + EEPROM_ADDR_MDNS_RECORD_LENGTH)
#define EEPROM_ADDR_AWB_ENABLE_LENGTH 1
#define EEPROM_ADDR_AWB_GAIN_ENABLE_START (EEPROM_ADDR_AWB_ENABLE_START + EEPROM_ADDR_AWB_ENABLE_LENGTH)
#define EEPROM_ADDR_AWB_GAIN_ENABLE_LENGTH 1
#define EEPROM_ADDR_AWB_MODE_ENABLE_START (EEPROM_ADDR_AWB_GAIN_ENABLE_START + EEPROM_ADDR_AWB_GAIN_ENABLE_LENGTH)
#define EEPROM_ADDR_AWB_MODE_ENABLE_LENGTH 1
#define EEPROM_ADDR_BPC_ENABLE_START (EEPROM_ADDR_AWB_MODE_ENABLE_START + EEPROM_ADDR_AWB_MODE_ENABLE_LENGTH)
#define EEPROM_ADDR_BPC_ENABLE_LENGTH 1
#define EEPROM_ADDR_WPC_ENABLE_START (EEPROM_ADDR_BPC_ENABLE_START + EEPROM_ADDR_BPC_ENABLE_LENGTH)
#define EEPROM_ADDR_WPC_ENABLE_LENGTH 1
#define EEPROM_ADDR_RAW_GAMA_ENABLE_START (EEPROM_ADDR_WPC_ENABLE_START + EEPROM_ADDR_WPC_ENABLE_LENGTH)
#define EEPROM_ADDR_RAW_GAMA_ENABLE_LENGTH 1
#define EEPROM_ADDR_AEC2_START (EEPROM_ADDR_RAW_GAMA_ENABLE_START + EEPROM_ADDR_RAW_GAMA_ENABLE_LENGTH)
#define EEPROM_ADDR_AEC2_LENGTH 1
#define EEPROM_ADDR_AE_LEVEL_START (EEPROM_ADDR_AEC2_START + EEPROM_ADDR_AEC2_LENGTH)
#define EEPROM_ADDR_AE_LEVEL_LENGTH 1
#define EEPROM_ADDR_AEC_VALUE_START (EEPROM_ADDR_AE_LEVEL_START + EEPROM_ADDR_AE_LEVEL_LENGTH)
#define EEPROM_ADDR_AEC_VALUE_LENGTH 2
#define EEPROM_ADDR_GAIN_CTRL_START (EEPROM_ADDR_AEC_VALUE_START + EEPROM_ADDR_AEC_VALUE_LENGTH)
#define EEPROM_ADDR_GAIN_CTRL_LENGTH 1
#define EEPROM_ADDR_AGC_GAIN_START (EEPROM_ADDR_GAIN_CTRL_START + EEPROM_ADDR_GAIN_CTRL_LENGTH)
#define EEPROM_ADDR_AGC_GAIN_LENGTH 1
#define EEPROM_ADDR_LOG_LEVEL (EEPROM_ADDR_AGC_GAIN_START + EEPROM_ADDR_AGC_GAIN_LENGTH)
#define EEPROM_ADDR_LOG_LEVEL_LENGTH 1
#define EEPROM_ADDR_HOSTNAME_START (EEPROM_ADDR_LOG_LEVEL + EEPROM_ADDR_LOG_LEVEL_LENGTH)
#define EEPROM_ADDR_HOSTNAME_LENGTH 51
#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 + \
EEPROM_ADDR_LENSC_LENGTH + EEPROM_ADDR_EXPOSURE_CTRL_LENGTH + EEPROM_ADDR_PHOTO_QUALITY_LENGTH + \
EEPROM_ADDR_PHOTO_QUALITY_LENGTH + EEPROM_ADDR_WIFI_SSID_LENGTH + EEPROM_ADDR_WIFI_PASSWORD_LENGTH + \
EEPROM_ADDR_WIFI_ACTIVE_FLAG_LENGTH + EEPROM_ADDR_BASIC_AUTH_USERNAME_LENGTH + EEPROM_ADDR_BASIC_AUTH_PASSWORD_LENGTH + \
EEPROM_ADDR_BASIC_AUTH_ENABLE_FLAG_LENGTH + EEPROM_ADDR_FIRST_MCU_START_FLAG_LENGTH + \
EEPROM_ADDR_CAMERA_FLASH_ENABLE_LENGTH + EEPROM_ADDR_CAMERA_FLASH_TIME_LENGTH + \
EEPROM_ADDR_MDNS_RECORD_LENGTH + EEPROM_ADDR_AWB_ENABLE_LENGTH + EEPROM_ADDR_AWB_GAIN_ENABLE_LENGTH + \
EEPROM_ADDR_AWB_MODE_ENABLE_LENGTH + EEPROM_ADDR_BPC_ENABLE_LENGTH + EEPROM_ADDR_WPC_ENABLE_LENGTH + \
EEPROM_ADDR_RAW_GAMA_ENABLE_LENGTH + EEPROM_ADDR_AEC2_LENGTH + EEPROM_ADDR_AE_LEVEL_LENGTH + \
EEPROM_ADDR_AEC_VALUE_LENGTH + EEPROM_ADDR_GAIN_CTRL_LENGTH + EEPROM_ADDR_AGC_GAIN_LENGTH + EEPROM_ADDR_LOG_LEVEL_LENGTH + \
EEPROM_ADDR_HOSTNAME_LENGTH ) ///< how many bits do we need for eeprom memory
#endif
/* EOF */

View File

@ -0,0 +1,419 @@
/**
@file micro_sd.cpp
@brief library for communication with micro-SD card
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#include "micro_sd.h"
/**
@brief Constructor
@param none
@return none
*/
MicroSd::MicroSd() {
CardDetected = false;
CardSize = 0;
DetectAfterBoot = false;
}
/**
@brief Reinit micro SD card
@param none
@return none
*/
void MicroSd::ReinitCard() {
Serial.println("Reinit micro SD card!");
Serial.println("Deinit micro SD card");
SD_MMC.end();
delay(50);
Serial.println("Init micro SD card");
InitSdCard();
}
/**
@brief Init SD card. And check, if is SD card inserted
@param none
@return none
*/
void MicroSd::InitSdCard() {
/* Start INIT Micro SD card */
Serial.println("Start init micro-SD Card");
/* set SD card to 1-line/1-bit mode. GPIO 4 is used for LED and for microSD card. But communication is slower. */
/* https://github.com/espressif/arduino-esp32/blob/master/libraries/SD_MMC/src/SD_MMC.h */
if (!SD_MMC.begin("/sdcard", true)) {
Serial.println("SD Card Mount Failed");
CardDetected = false;
CardSize = 0;
//DetectAfterBoot = false;
return;
}
/* check microSD card and card type */
uint8_t cardType = SD_MMC.cardType();
if (cardType == CARD_NONE) {
Serial.println("No SD_MMC card attached");
CardDetected = false;
CardSize = 0;
//DetectAfterBoot = false;
return;
}
/* print card type */
Serial.print("Found card. Card Type: ");
if (cardType == CARD_MMC) {
Serial.print("MMC");
} else if (cardType == CARD_SD) {
Serial.print("SDSC");
} else if (cardType == CARD_SDHC) {
Serial.print("SDHC");
} else {
Serial.print("UNKNOWN");
}
/* calculation card size */
CardSize = SD_MMC.cardSize() / (1024 * 1024);
Serial.printf(", Card Size: %d MB\n", CardSize);
CardDetected = true;
DetectAfterBoot = true;
}
/**
@brief List directory on the micro SD card
@param fs::FS - card
@param String - Directory name
@param uint8_t - levels
@return none
*/
void MicroSd::ListDir(fs::FS &fs, String DirName, uint8_t levels) {
if (true == CardDetected) {
Serial.printf("Listing directory: %s\n", DirName.c_str());
File root = fs.open(DirName.c_str());
if (!root) {
Serial.println("Failed to open directory");
return;
}
if (!root.isDirectory()) {
Serial.println("Not a directory");
return;
}
File file = root.openNextFile();
while (file) {
if (file.isDirectory()) {
Serial.print(" DIR : ");
Serial.println(file.name());
if (levels) {
ListDir(fs, file.path(), levels - 1);
}
} else {
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print(" SIZE: ");
Serial.println(file.size());
}
file = root.openNextFile();
}
}
}
/**
@brief List directory on the micro SD card
@param fs::FS - card
@param String - dir name
@return bool - status
*/
bool MicroSd::CreateDir(fs::FS &fs, String path) {
bool status = false;
if (true == CardDetected) {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.printf("Creating Dir: %s... ", path.c_str());
#endif
if (fs.mkdir(path.c_str())) {
status = true;
}
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.println((status == true) ? "Created" : "Failed");
#endif
}
return status;
}
/**
@brief remove directory on the micro SD card
@param fs::FS - card
@param String - dir name
@return bool - status
*/
bool MicroSd::RemoveDir(fs::FS &fs, String path) {
bool status = false;
if (true == CardDetected) {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.printf("Removing Dir: %s... ", path.c_str());
#endif
if (fs.rmdir(path.c_str())) {
status = true;
}
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.println((status == true) ? "Removed" : "Failed");
#endif
}
return status;
}
/**
@brief Read file and print data to console
@param fs::FS - card
@param String - file name
@return none
*/
void MicroSd::ReadFileConsole(fs::FS &fs, String path) {
if (true == CardDetected) {
Serial.printf("Reading file: %s\n", path.c_str());
File file = fs.open(path.c_str());
if (!file) {
Serial.println("Failed to open file for reading");
return;
}
Serial.print("Read from file: ");
while (file.available()) {
Serial.write(file.read());
}
}
}
/**
@brief Write message to file
@param fs::FS - card
@param String - file name
@param String - message
@return bool - status
*/
bool MicroSd::WriteFile(fs::FS &fs, String path, String message) {
bool status = false;
if (true == CardDetected) {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.printf("Writing file: %s... ", path.c_str());
#endif
File file = fs.open(path.c_str(), FILE_WRITE);
if (!file) {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.printf("Failed to open file for writing");
#endif
} else {
if (file.print(message.c_str())) {
status = true;
}
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.println((status == true) ? "File written" : "Write Failed");
#endif
}
}
return status;
}
/**
@brief Added text to end of file
@param fs::FS - card
@param String - file name
@param String - message
@return bool - status
*/
bool MicroSd::AppendFile(fs::FS &fs, String path, String message) {
bool status = false;
if (true == CardDetected) {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.printf("Appending to file: %s... ", path.c_str());
#endif
File file = fs.open(path.c_str(), FILE_APPEND);
if (!file) {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.println("Failed to open file for appending");
#endif
CardDetected = false;
} else {
if (file.print(message.c_str())) {
status = true;
}
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.println((status == true) ? "Message appended" : "Append Failed");
#endif
}
}
return status;
}
/**
@brief Rename file on the SD card
@param fs::FS - card
@param String - origin file name
@param String - new file name
@return bool - status
*/
bool MicroSd::RenameFile(fs::FS &fs, String path1, String path2) {
bool status = false;
if (true == CardDetected) {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.printf("Renaming file %s to %s... ", path1.c_str(), path2.c_str());
#endif
if (fs.rename(path1.c_str(), path2.c_str())) {
status = true;
}
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.println((status == true) ? "File renamed" : "Rename Failed");
#endif
}
return status;
}
/**
@brief Delete file on the SD card
@param fs::FS - card
@param String - file name
@return bool - status
*/
bool MicroSd::DeleteFile(fs::FS &fs, String path) {
bool status = false;
if (true == CardDetected) {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.printf("Deleting file: %s... ", path.c_str());
#endif
if (fs.remove(path.c_str())) {
status = true;
}
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.println((status == true) ? "File deleted" : "Delete Failed");
#endif
}
return status;
}
/**
@brief Get file size in the kb
@param fs::FS - card
@param String - file name
@return uint32_t - size
*/
uint32_t MicroSd::GetFileSize(fs::FS &fs, String path) {
uint32_t ret = 0;
if (true == CardDetected) {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.printf("Getting file size: %s... ", path.c_str());
#endif
File file = fs.open(path.c_str(), FILE_APPEND);
if (!file) {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.println("Failed to open file for appending");
#endif
return 0;
}
ret = file.size() / 1024; /* convert from bytes to kb */
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.printf(" File size: %d\n ", ret);
#endif
}
return ret; /* kb*/
}
/**
@brief Check file count with partial match
@param fs::FS - card
@param String - dir name
@param String - file name
@return int16_t - count
*/
uint16_t MicroSd::FileCount(fs::FS &fs, String DirName, String FileName) {
uint16_t FileCount = 0;
if (true == CardDetected) {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.printf("File name count: %s\n", DirName.c_str());
#endif
File root = fs.open(DirName.c_str());
if (!root) {
Serial.println("Failed to open directory");
return 0;
}
if (!root.isDirectory()) {
Serial.println("Not a directory");
return 0;
}
File file = root.openNextFile();
while (file) {
if (!file.isDirectory()) {
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print(" SIZE: ");
Serial.print(file.size());
#endif
if (String(file.name()).indexOf(FileName) != -1) {
FileCount++;
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.print(" - MATCH");
#endif
}
#if (true == CONSOLE_VERBOSE_DEBUG)
Serial.println("");
#endif
}
file = root.openNextFile();
}
}
return FileCount;
}
/**
@brief Get card detected status
@param none
@return bool - status
*/
bool MicroSd::GetCardDetectedStatus() {
return CardDetected;
}
/**
@brief Get card size
@param none
@return uint16_t - size
*/
uint16_t MicroSd::GetCardSize() {
return CardSize;
}
/**
@brief Get card detect after boot
@param none
@return bool - status
*/
bool MicroSd::GetCardDetectAfterBoot() {
return DetectAfterBoot;
}
/* EOF */

View File

@ -0,0 +1,65 @@
/**
@file micro_sd.h
@brief library for communication with micro-SD card
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
WARNING: ALL data pins must be pulled up to 3.3V with an external 10k Ohm resistor! Note to ESP32 pin 2 (D0): Add a 1K Ohm pull-up resistor to 3.3V after flashing
SD Card | ESP32
D2 12
D3 13
CMD 15
VSS GND
VDD 3.3V
CLK 14
VSS GND
D0 2 (add 1K pull up after flashing)
D1 4
*/
#ifndef _MICRO_SD_H_
#define _MICRO_SD_H_
#include "Arduino.h"
#include "FS.h"
#include "SD_MMC.h"
#include "mcu_cfg.h"
#include "var.h"
class MicroSd {
private:
bool CardDetected; ///< Card detected status
uint16_t CardSize; ///< Card size
bool DetectAfterBoot; ///< Card detect after boot
public:
MicroSd();
~MicroSd(){};
void InitSdCard();
void ReinitCard();
void ListDir(fs::FS &, String, uint8_t);
bool CreateDir(fs::FS &, String);
bool RemoveDir(fs::FS &, String);
void ReadFileConsole(fs::FS &, String);
bool WriteFile(fs::FS &, String, String);
bool AppendFile(fs::FS &, String, String);
bool RenameFile(fs::FS &, String, String);
bool DeleteFile(fs::FS &, String);
uint32_t GetFileSize(fs::FS &, String);
uint16_t FileCount(fs::FS &, String, String);
bool GetCardDetectedStatus();
uint16_t GetCardSize();
bool GetCardDetectAfterBoot();
};
#endif
/* EOF */

View File

@ -0,0 +1,134 @@
/**
@file serial_cfg.cpp
@brief
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#include "serial_cfg.h"
SerialCfg SystemSerialCfg(&SystemConfig, &SystemLog, &SystemWifiMngt, &Connect);
/**
@brief Constructor
@param Configuration * - pointer to Configuration object
@param Logs * - pointer to Logs object
@param WiFiMngt * - pointer to WiFiMngt object
@param PrusaConnect * - pointer to PrusaConnect object
@return none
*/
SerialCfg::SerialCfg(Configuration *i_conf, Logs *i_log, WiFiMngt *i_wifi, PrusaConnect *i_connect) {
config = i_conf;
log = i_log;
wifim = i_wifi;
connect = i_connect;
}
/**
@brief Initialize serial port
@param none
@return none
*/
void SerialCfg::ProcessIncommingData() {
static String inputBuffer = "";
while (Serial.available() > 0) {
char incomingChar = Serial.read();
if (incomingChar == '\n') {
// Ak nájdeme nový riadok, spracujeme prikaz
ParseIncommingData(inputBuffer);
inputBuffer = ""; // Vynulujeme buffer pre ďalší príkaz
} else {
// Inak pridáme znak do bufferu
inputBuffer += incomingChar;
}
}
}
/**
@brief Parse incomming data from serial port
@param String command - incomming command
@return none
*/
void SerialCfg::ParseIncommingData(String command) {
/* check command */
if (command.startsWith("setwifissid:") && command.endsWith(";")) {
/* remove prefix "setwifissid:" and end of command symbol ";" */
wifi_ssid = command.substring(12, command.length() - 1);
log->AddEvent(LogLevel_Info, "--> Console set WiFi SSID: " + wifi_ssid);
wifim->SetStaSsid(wifi_ssid);
} else if (command.startsWith("setwifipass:") && command.endsWith(";")) {
/* remove prefix "setwifipass:" and end of command symbol ";" */
wifi_pass = command.substring(12, command.length() - 1);
log->AddEvent(LogLevel_Info, "--> Console set WiFi password: " + wifi_pass);
wifim->SetStaPassword(wifi_pass);
} else if (command.startsWith("setauthtoken:") && command.endsWith(";")) {
/* remove prefix "setauthtoken:" and end of command symbol ";" */
auth_token = command.substring(13, command.length() - 1);
log->AddEvent(LogLevel_Info, "--> Console set auth TOKEN for backend: " + auth_token);
connect->SetToken(auth_token);
} else if (command.startsWith("wificonnect") && command.endsWith(";")) {
log->AddEvent(LogLevel_Info, "--> Console connecting to wifi...");
wifim->ConnectToSta();
} else if (command.startsWith("getwifimode") && command.endsWith(";")) {
log->AddEvent(LogLevel_Info, "--> Console print WiFi mode...");
Serial.print("wifimode:" + wifim->GetWiFiMode() + ";");
} else if (command.startsWith("getwifistastatus") && command.endsWith(";")) {
log->AddEvent(LogLevel_Info, "--> Console print STA status...");
Serial.print("wifistastatus:" + wifim->GetStaStatus() + ";");
} else if (command.startsWith("getwifistaip") && command.endsWith(";")) {
log->AddEvent(LogLevel_Info, "--> Console print STA IP...");
Serial.print("wifistaip:" + wifim->GetStaIp() + ";");
} else if (command.startsWith("getserviceapssid") && command.endsWith(";")) {
log->AddEvent(LogLevel_Info, "--> Console print service WiFi AP SSID...");
Serial.print("getserviceapssid:" + wifim->GetServiceApSsid() + ";");
} else if (command.startsWith("mcureboot") && command.endsWith(";")) {
log->AddEvent(LogLevel_Warning, "--> Reboot MCU!");
ESP.restart();
} else if (command.startsWith("commandslist") && command.endsWith(";")) {
log->AddEvent(LogLevel_Warning, "--> Available commands");
PrintAvailableCommands();
} else {
log->AddEvent(LogLevel_Warning, "--> Unknown command: " + command + "!");
PrintAvailableCommands();
return;
}
}
/**
@brief Print available commands
@param none
@return none
*/
void SerialCfg::PrintAvailableCommands() {
Serial.println("Available commands: ");
Serial.println("setwifissid:SSID; - set WiFi SSID");
Serial.println("setwifipass:PASS; - set WiFi password");
Serial.println("setauthtoken:TOKEN; - set auth TOKEN for backend");
Serial.println("wificonnect; - connect to WiFi network");
Serial.println("getwifimode; - get WiFi mode (AP/STA)");
Serial.println("getwifistastatus; - get STA status (connected/disconnected)");
Serial.println("getwifistaip; - get STA IP address");
Serial.println("getserviceapssid;- get service WiFi AP SSID");
Serial.println("mcureboot; - reboot MCU");
Serial.println("commandslist; - print available commands");
}
/* EOF */

View File

@ -0,0 +1,51 @@
/**
@file wifi_mngt.h
@brief Library for configuration module via serial port
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#ifndef _SERIAL_CFG_H
#define _SERIAL_CFG_H
#include "Arduino.h"
#include "mcu_cfg.h"
#include "var.h"
#include "log.h"
#include "wifi_mngt.h"
#include "cfg.h"
#include "connect.h"
class WiFiMngt;
class PrusaConnect;
class SerialCfg {
private:
Configuration *config; ///< pointer to Configuration object
Logs *log; ///< pointer to Logs object
WiFiMngt *wifim; ///< pointer to WiFiMngt object
PrusaConnect *connect; ///< pointer to PrusaConnect object
String wifi_ssid; ///< wifi ssid
String wifi_pass; ///< wifi password
String auth_token; ///< auth token
public:
SerialCfg(Configuration*, Logs*, WiFiMngt*, PrusaConnect*);
~SerialCfg(){};
void ProcessIncommingData();
void ParseIncommingData(String);
void PrintAvailableCommands();
};
extern SerialCfg SystemSerialCfg; ///< SerialCfg object
#endif
/* EOF */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,65 @@
/**
@file server.h
@brief Library for WEB server
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#ifndef _SERVER_H_
#define _SERVER_H_
#include <AsyncEventSource.h>
#include <AsyncWebSocket.h>
#include <AsyncWebSynchronization.h>
#include <ESPAsyncWebSrv.h>
#include <esp_task_wdt.h>
#include <StringArray.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <EEPROM.h>
#include <Update.h>
#include <ArduinoJson.h>
#include "WebPage.h"
#include "WebPage_Icons.h"
#include "mcu_cfg.h"
#include "var.h"
#include "camera.h"
#include "cfg.h"
#include "jquery.h"
#include "system.h"
#include "log.h"
#include "connect.h"
#include "wifi_mngt.h"
#include "stream.h"
extern AsyncWebServer server; ///< global variable for web server
void Server_LoadCfg();
void Server_InitWebServer();
void Server_InitWebServer_JsonData();
void Server_InitWebServer_WebPages();
void Server_InitWebServer_Icons();
void Server_InitWebServer_Actions();
void Server_InitWebServer_Sets();
void Server_InitWebServer_Update();
void Server_InitWebServer_Stream();
void Server_handleCacheRequest(AsyncWebServerRequest*, const char*, const char*);
void Server_handleNotFound(AsyncWebServerRequest *);
String Server_GetJsonData();
bool Server_CheckBasicAuth(AsyncWebServerRequest *);
void Server_streamJpg(AsyncWebServerRequest *);
void Server_GetModuleUptime(String &);
bool Server_TransfeStringToBool(String);
#endif
/* EOF */

View File

@ -0,0 +1,290 @@
/**
@file stream.cpp
@brief stream library.
Inspiration is from git repository https://gist.github.com/me-no-dev/d34fba51a8f059ac559bf62002e61aa3
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#include "stream.h"
#define PART_BOUNDARY "123456789000000000000987654321" ///< Must be unique for each stream
static const char *STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY; ///< content type for stream
static const char *STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n"; ///< boundary for stream
static const char *STREAM_PART = "Content-Type: %s\r\nContent-Length: %u\r\n\r\n"; ///< part for stream
static const char *JPG_CONTENT_TYPE = "image/jpeg"; ///< content type for jpg
/**
* @brief Construct a new Async Buffer Response:: Async Buffer Response object
*
* @param buf
* @param len
* @param contentType
*/
AsyncBufferResponse::AsyncBufferResponse(uint8_t *buf, size_t len, const char *contentType) {
_buf = buf;
_len = len;
_callback = nullptr;
_code = 200;
_contentLength = _len;
_contentType = contentType;
_index = 0;
}
/**
* @brief Destroy the Async Buffer Response:: Async Buffer Response object
*
*/
AsyncBufferResponse::~AsyncBufferResponse() {
if (_buf != nullptr) {
free(_buf);
}
}
/**
* @brief Check if source is valid
*
* @return bool
*/
bool AsyncBufferResponse::_sourceValid() const {
return _buf != nullptr;
}
/**
* @brief Fill buffer
*
* @param buf
* @param maxLen
* @return size_t
*/
size_t AsyncBufferResponse::_fillBuffer(uint8_t *buf, size_t maxLen) {
size_t ret = _content(buf, maxLen, _index);
if (ret != RESPONSE_TRY_AGAIN) {
_index += ret;
}
return ret;
}
/**
* @brief Content
*
* @param buffer
* @param maxLen
* @param index
* @return size_t
*/
size_t AsyncBufferResponse::_content(uint8_t *buffer, size_t maxLen, size_t index) {
memcpy(buffer, _buf + index, maxLen);
if ((index + maxLen) == _len) {
free(_buf);
_buf = nullptr;
}
return maxLen;
}
/**
* @brief Construct a new Async Frame Response:: Async Frame Response object
*
* @param frame
* @param contentType
*/
AsyncFrameResponse::AsyncFrameResponse(camera_fb_t *frame, const char *contentType) {
_callback = nullptr;
_code = 200;
_contentLength = frame->len;
_contentType = contentType;
_index = 0;
fb = frame;
}
/**
* @brief Destroy the Async Frame Response:: Async Frame Response object
*
*/
AsyncFrameResponse::~AsyncFrameResponse() {
if (fb != nullptr) {
esp_camera_fb_return(fb);
}
}
/**
* @brief Check if source is valid
*
* @return bool
*/
bool AsyncFrameResponse::_sourceValid() const {
return fb != nullptr;
}
/**
* @brief Fill buffer
*
* @param buf
* @param maxLen
* @return size_t
*/
size_t AsyncFrameResponse::_fillBuffer(uint8_t *buf, size_t maxLen) {
size_t ret = _content(buf, maxLen, _index);
if (ret != RESPONSE_TRY_AGAIN) {
_index += ret;
}
return ret;
}
/**
* @brief Content
*
* @param buffer
* @param maxLen
* @param index
* @return size_t
*/
size_t AsyncFrameResponse::_content(uint8_t *buffer, size_t maxLen, size_t index) {
memcpy(buffer, fb->buf + index, maxLen);
if ((index + maxLen) == fb->len) {
esp_camera_fb_return(fb);
fb = nullptr;
}
return maxLen;
}
/**
* @brief Construct a new Async Jpeg Stream Response:: Async Jpeg Stream Response object
*
* @param i_cam
* @param i_log
*/
AsyncJpegStreamResponse::AsyncJpegStreamResponse(Camera *i_cam, Logs *i_log) {
_callback = nullptr;
_code = 200;
_contentLength = 0;
_contentType = STREAM_CONTENT_TYPE;
_sendContentLength = false;
_chunked = true;
_index = 0;
lastAsyncRequest = 0;
memset(&_frame, 0, sizeof(camera_frame_t));
camera = i_cam;
log = i_log;
camera->SetStreamStatus(true);
}
/**
* @brief Destroy the Async Jpeg Stream Response:: Async Jpeg Stream Response object
*
*/
AsyncJpegStreamResponse::~AsyncJpegStreamResponse() {
camera->SetStreamStatus(false);
camera->CaptureReturnFrameBuffer();
}
/**
* @brief Check if source is valid
*
* @return bool
*/
bool AsyncJpegStreamResponse::_sourceValid() const {
return true;
}
/**
* @brief Fill buffer
*
* @param buf
* @param maxLen
* @return size_t
*/
size_t AsyncJpegStreamResponse::_fillBuffer(uint8_t *buf, size_t maxLen) {
size_t ret = _content(buf, maxLen, _index);
if (ret != RESPONSE_TRY_AGAIN) {
_index += ret;
}
return ret;
}
/**
* @brief Content - get picture from camera and send it to client
*
* @param buffer
* @param maxLen
* @param index
* @return size_t
*/
size_t AsyncJpegStreamResponse::_content(uint8_t *buffer, size_t maxLen, size_t index) {
if (!_frame.fb || _frame.index == _frame.fb->len) {
delay(1);
if (index && _frame.fb) {
uint64_t end = (uint64_t)micros();
int fp = (end - lastAsyncRequest) / 1000;
float fps = 1000.0 / fp;
char buf[50] = { '\0' };
camera->StreamSetFrameSize(_frame.fb->len / 1024);
camera->StreamSetFrameFps(fps);
//sprintf(buf, "Size: %uKB, Time: %ums (%.1f fps)", _frame.fb->len / 1024, fp, fps);
sprintf(buf, "Size: %uKB, FPS: %.1f", _frame.fb->len / 1024, fps);
Serial.println(buf);
lastAsyncRequest = end;
camera->CaptureReturnFrameBuffer();
_frame.fb = NULL;
}
/* check space for headers */
if (maxLen < (strlen(STREAM_BOUNDARY) + strlen(STREAM_PART) + strlen(JPG_CONTENT_TYPE) + 8)) {
log->AddEvent(LogLevel_Error, "Stream Not space for headers");
return RESPONSE_TRY_AGAIN;
}
/* get frame */
_frame.index = 0;
camera->CaptureStream(&_dframe);
_frame.fb = &_dframe;
if (_frame.fb == NULL) {
log->AddEvent(LogLevel_Error, "Stream capture frame failed");
return 0;
}
/* send boundary */
size_t blen = 0;
if (index) {
blen = strlen(STREAM_BOUNDARY);
memcpy(buffer, STREAM_BOUNDARY, blen);
buffer += blen;
}
/* send header */
size_t hlen = sprintf((char *)buffer, STREAM_PART, JPG_CONTENT_TYPE, _frame.fb->len);
buffer += hlen;
/* send frame */
hlen = maxLen - hlen - blen;
if (hlen > _frame.fb->len) {
maxLen -= hlen - _frame.fb->len;
hlen = _frame.fb->len;
}
memcpy(buffer, _frame.fb->buf, hlen);
_frame.index += hlen;
delay(1);
return maxLen;
}
/* check next send data */
size_t available = _frame.fb->len - _frame.index;
if (maxLen > available) {
maxLen = available;
}
delay(1);
memcpy(buffer, _frame.fb->buf + _frame.index, maxLen);
_frame.index += maxLen;
return maxLen;
}
/* EOF */

View File

@ -0,0 +1,75 @@
/**
@file stream.h
@brief stream library
Inspiration is from git repository https://gist.github.com/me-no-dev/d34fba51a8f059ac559bf62002e61aa3
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#ifndef _STREAM_H_
#define _STREAM_H_
#include "ESPAsyncWebSrv.h"
#include "Arduino.h"
#include "mcu_cfg.h"
#include "var.h"
#include "log.h"
#include "camera.h"
typedef struct {
camera_fb_t *fb; ///< pointer to frame buffer
size_t index; ///< index of frame
} camera_frame_t; ///< camera frame structure
class AsyncBufferResponse : public AsyncAbstractResponse {
private:
uint8_t *_buf; ///< pointer to frame buffer
size_t _len; ///< length of frame buffer
size_t _index; ///< index of frame
public:
AsyncBufferResponse(uint8_t *, size_t, const char *);
~AsyncBufferResponse();
bool _sourceValid() const;
virtual size_t _fillBuffer(uint8_t *, size_t) override;
size_t _content(uint8_t *, size_t, size_t);
};
class AsyncFrameResponse : public AsyncAbstractResponse {
private:
camera_fb_t *fb; ///< pointer to frame buffer
size_t _index; ///< index of frame
public:
AsyncFrameResponse(camera_fb_t *, const char *);
~AsyncFrameResponse();
bool _sourceValid() const;
virtual size_t _fillBuffer(uint8_t *, size_t) override;
size_t _content(uint8_t *, size_t, size_t);
};
class AsyncJpegStreamResponse : public AsyncAbstractResponse {
private:
camera_frame_t _frame; ///< camera frame
size_t _index; ///< index of frame
uint64_t lastAsyncRequest; ///< last async request
Camera *camera; ///< pointer to camera
Logs *log; ///< pointer to logs
camera_fb_t _dframe; ///< camera frame
public:
AsyncJpegStreamResponse(Camera *, Logs *);
~AsyncJpegStreamResponse();
bool _sourceValid() const;
virtual size_t _fillBuffer(uint8_t *, size_t ) override;
size_t _content(uint8_t *, size_t , size_t );
};
#endif
/* EOF */

View File

@ -0,0 +1,103 @@
/**
@file sys_led.cpp
@brief Library for system LED control
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#include "sys_led.h"
sys_led system_led(STATUS_LED_GPIO_NUM, STATUS_LED_ON_DURATION);
/**
* @brief Construct a new sys led::sys led object
*
* @param uint8_t - pin number for system LED
* @param uint32_t - duration of LED on
*/
sys_led::sys_led(uint8_t i_pin, uint32_t i_on_duration) {
pin = i_pin;
time = 100;
ledOnDuration = i_on_duration;
}
/**
* @brief Construct a new sys led::sys led object
*
* @param uint8_t - pin number for system LED
* @param uint32_t - duration of LED on
* @param Logs * - pointer to log class
*/
sys_led::sys_led(uint8_t i_pin, uint32_t i_on_duration, Logs *i_log) {
pin = i_pin;
time = 100;
log = i_log;
ledOnDuration = i_on_duration;
}
/**
* @brief Init system LED
*
*/
void sys_led::init() {
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
}
/**
* @brief Toggle system LED
*
*/
void sys_led::toggle() {
digitalWrite(pin, !digitalRead(pin));
}
/**
* @brief Set system LED
*
* @param bool - state of LED
*/
void sys_led::set(bool state) {
digitalWrite(pin, state);
}
/**
* @brief Get system LED
*
* @return bool - state of LED
*/
bool sys_led::get() {
return digitalRead(pin);
}
/**
* @brief Set timer for system LED
*
* @param uint32_t - time in ms
*/
void sys_led::setTimer(uint32_t i_time) {
time = i_time;
}
/**
* @brief Get timer for next start task for system LED
*
* @return uint32_t - time in ms
*/
uint32_t sys_led::getTimer() {
uint32_t tmp = 0;
if (digitalRead(pin) == LOW) {
tmp = ledOnDuration;
} else {
tmp = time;
}
return tmp;
}
/* EOF */

View File

@ -0,0 +1,43 @@
/**
@file sys_led.h
@brief Library for system LED control
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#ifndef _SYS_LED_H_
#define _SYS_LED_H_
#include "log.h"
#include "mcu_cfg.h"
#include "arduino.h"
class sys_led {
private:
uint8_t pin; ///< pin number for system LED
uint32_t time; ///< speed blinking time system LED
uint32_t ledOnDuration; ///< duration of LED on
Logs *log; ///< pointer to log class
public:
sys_led(uint8_t, uint32_t);
sys_led(uint8_t, uint32_t, Logs *);
~sys_led(){};
void init();
void toggle();
void set(bool);
bool get();
void setTimer(uint32_t);
uint32_t getTimer();
};
extern sys_led system_led;
#endif
/* EOF */

View File

@ -0,0 +1,644 @@
/**
@file system.cpp
@brief system library
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#include "system.h"
/**
@brief Function for init system library
@param none
@return none
*/
void System_Init() {
SystemLog.AddEvent(LogLevel_Info, "Init system lib");
/* show last reset status */
String reason_simple = System_printMcuResetReasonSimple();
SystemLog.AddEvent(LogLevel_Warning, "CPU reset reason: " + reason_simple);
String reason_core0 = System_PrintMcuResetReason(rtc_get_reset_reason(0));
String reason_core1 = System_PrintMcuResetReason(rtc_get_reset_reason(1));
SystemLog.AddEvent(LogLevel_Warning, "CPU0 reset reason: " + reason_core0);
SystemLog.AddEvent(LogLevel_Warning, "CPU1 reset reason: " + reason_core1);
SystemLog.AddEvent(LogLevel_Info, "MCU Temperature: " + String(temperatureRead()) + " *C");
SystemLog.AddEvent(LogLevel_Info, "Internal Total heap: " + String(ESP.getHeapSize()) + " ,internal Free Heap: " + String(ESP.getFreeHeap()));
SystemLog.AddEvent(LogLevel_Info, "SPIRam Total heap: " + String(ESP.getPsramSize()) + " ,SPIRam Free Heap: " + String(ESP.getFreePsram()));
SystemLog.AddEvent(LogLevel_Info, "ChipRevision: " + String(ESP.getChipRevision()) + " ,Cpu Freq: " + String(ESP.getCpuFreqMHz()) + " ,SDK Version: " + String(ESP.getSdkVersion()));
SystemLog.AddEvent(LogLevel_Info, "Flash Size: " + String(ESP.getFlashChipSize()) + " ,Flash Speed " + String(ESP.getFlashChipSpeed()));
System_CheckIfPsramIsUsed();
}
/**
@brief Function for load configuration from EEPROM
@param none
@return none
*/
void System_LoadCfg() {
}
/**
@brief Function for check if PSRAM is used
@param none
@return none
*/
void System_CheckIfPsramIsUsed() {
if (psramFound()) {
SystemLog.AddEvent(LogLevel_Info, "PSRAM is used.");
void *ptr = malloc(100);
if (ptr != NULL) {
if (esp_ptr_external_ram(ptr)) {
SystemLog.AddEvent(LogLevel_Info, "malloc/new is using SPIRAM");
} else {
SystemLog.AddEvent(LogLevel_Info, "malloc/new is not using SPIRAM");
}
free(ptr);
} else {
SystemLog.AddEvent(LogLevel_Info, "Failed to allocate memory");
}
} else {
SystemLog.AddEvent(LogLevel_Info, "PSRAM is not used.");
}
}
/**
@brief Function for init update functions from file
@param none
@return none
*/
void System_UpdateInit() {
Update.onProgress([](int progress, size_t total) {
/* update from file */
SystemCamera.SetFlashStatus(true);
uint8_t updateProgress = (progress * 100) / FirmwareUpdate.FirmwareSize;
SystemLog.AddEvent(LogLevel_Info, "Updating: " + String(FirmwareUpdate.FirmwareSize) + "/" + String(progress) + " -> " + String(updateProgress) + "%");
FirmwareUpdate.PercentProcess = updateProgress;
FirmwareUpdate.TransferedBytes = progress;
delay(10);
SystemCamera.SetFlashStatus(false);
});
}
/**
@brief Main function for system lib
@param none
@return none
*/
void System_Main() {
/* check new FW version */
if (false == FirmwareUpdate.CheckNewVersionAfterBoot) {
System_CheckNewVersion();
}
/* task for download and flash FW from server */
System_OtaCloudUpdate();
}
/**
@brief Function for check FW version on the WEB server
https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28
@param none
@return none
*/
void System_CheckNewVersion() {
if (WL_CONNECTED == WiFi.status()) {
SystemLog.AddEvent(LogLevel_Info, "Check new FW version from OTA");
FirmwareUpdate.CheckNewVersionAfterBoot = true;
WiFiClientSecure client;
client.setCACert(root_CAs_ota);
FirmwareUpdate.CheckNewVersionFwStatus = "N/A";
FirmwareUpdate.NewVersionFw = "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!";
SystemLog.AddEvent(LogLevel_Info, FirmwareUpdate.CheckNewVersionFwStatus);
} else {
SystemLog.AddEvent(LogLevel_Verbose, "Connected to server!");
client.println("GET https://" + String(OTA_UPDATE_API_SERVER) + String(OTA_UPDATE_API_URL) + " HTTP/1.0");
client.println("Host: " + String(OTA_UPDATE_API_SERVER));
client.println("User-Agent: " + String(DEVICE_HOSTNAME));
client.println("Connection: close");
client.println();
while (client.connected()) {
String line = client.readStringUntil('\n');
if (line == "\r") {
SystemLog.AddEvent(LogLevel_Verbose, "headers received: " + line);
break;
}
}
/* wait for data, and read it */
delay(350);
String Data = "";
while (client.connected()) {
while (client.available()) {
Data += (char)client.read();
}
}
SystemLog.AddEvent(LogLevel_Verbose, Data);
client.stop();
/* json analyzed */
JsonDocument jsonDoc;
DeserializationError error = deserializeJson(jsonDoc, Data);
if (error) {
FirmwareUpdate.CheckNewVersionFwStatus = "Failed to parse JSON from OTA server!";
SystemLog.AddEvent(LogLevel_Warning, FirmwareUpdate.CheckNewVersionFwStatus);
SystemLog.AddEvent(LogLevel_Warning, Data);
} else {
const char *firmwareVersion = jsonDoc["tag_name"];
if (firmwareVersion) {
FirmwareUpdate.CheckNewVersionFwStatus = "Download successful";
FirmwareUpdate.NewVersionFw = firmwareVersion;
SystemLog.AddEvent(LogLevel_Info, "Available OTA firmware: " + FirmwareUpdate.NewVersionFw);
/* get assets */
JsonArray assets = jsonDoc["assets"];
int assetsCount = assets.size();
SystemLog.AddEvent(LogLevel_Info, "Assets count: " + String(assetsCount));
for(int i = 0; i < assetsCount; i++) {
JsonObject asset = assets[i];
const char* name = asset["name"];
SystemLog.AddEvent(LogLevel_Info, "Assets[" + String(i) + "]: " + String(name));
/* get FW file and URL */
if (strcmp(name, OTA_UPDATE_FW_FILE) == 0) {
/* get download URL */
const char* download_url = asset["browser_download_url"];
FirmwareUpdate.OtaUpdateFwUrl = download_url;
SystemLog.AddEvent(LogLevel_Info, "Found FW file: " + String(name) + " URL: " + FirmwareUpdate.OtaUpdateFwUrl);
FirmwareUpdate.OtaUpdateFwAvailable = true;
}
}
} else {
FirmwareUpdate.CheckNewVersionFwStatus = "JSON key 'tag_name' from OTA server not found!";
SystemLog.AddEvent(LogLevel_Warning, FirmwareUpdate.CheckNewVersionFwStatus);
}
}
}
}
}
/**
@brief Function for check start OTA update from WEB server
@param none
@return none
*/
void System_OtaCloudUpdate() {
if (true == FirmwareUpdate.StartOtaUpdate) {
FirmwareUpdate.Processing = true;
FirmwareUpdate.StartOtaUpdate = false;
FirmwareUpdate.UpdatingStatus = "Sync NTP time...";
SystemWifiMngt.SyncNtpTime();
FirmwareUpdate.UpdatingStatus = "Start updating";
System_OtaUpdateStart();
}
}
/**
@brief Function for downloading FW and flash new FW to the memmory
https://github.com/espressif/arduino-esp32/blob/master/libraries/HTTPUpdate/src/HTTPUpdate.h
@param none
@return bool - true if update is done
*/
bool System_OtaUpdateStart() {
bool b_ret = false;
WiFiClientSecure client;
/* check if new FW version is available */
if (FirmwareUpdate.OtaUpdateFwAvailable == false) {
FirmwareUpdate.UpdatingStatus = SYSTEM_MSG_UPDATE_NO_FW;
SystemLog.AddEvent(LogLevel_Info, FirmwareUpdate.UpdatingStatus);
FirmwareUpdate.Processing = false;
return b_ret;
}
/* reset wdg */
esp_task_wdt_reset();
/* set certificat for secure connection, set timeout and follow redirect */
client.setCACert(root_CAs_ota);
// client.setInsecure();
client.setTimeout(12000 / SECOND_TO_MILISECOND); /* timeout argument is defined in seconds for setTimeout */
httpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); /* HTTPC_FORCE_FOLLOW_REDIRECTS */
/* set callback function */
httpUpdate.onStart(System_OtaUpdateStartCB);
httpUpdate.onEnd(System_OtaUpdateEndCB);
httpUpdate.onError(System_OtaUpdateErrorCB);
httpUpdate.onProgress(System_OtaUpdateProgressCB);
/* mcu configuration */
httpUpdate.rebootOnUpdate(false);
FirmwareUpdate.UpdatingStatus = SYSTEM_MSG_UPDATE_PROCESS;
httpUpdate.setLedPin(4, HIGH);
SystemLog.AddEvent(LogLevel_Info, "Start OTA update URL: " + FirmwareUpdate.OtaUpdateFwUrl + ";");
/* start update */
t_httpUpdate_return ret = httpUpdate.update(client, FirmwareUpdate.OtaUpdateFwUrl.c_str());
/* check result */
switch (ret) {
case HTTP_UPDATE_FAILED:
b_ret = true;
FirmwareUpdate.UpdatingStatus = String(SYSTEM_MSG_UPDATE_FAIL) + "[" + String(httpUpdate.getLastError()) + "]: " + httpUpdate.getLastErrorString();
break;
case HTTP_UPDATE_NO_UPDATES:
FirmwareUpdate.UpdatingStatus = "No updates";
break;
case HTTP_UPDATE_OK:
FirmwareUpdate.UpdatingStatus = SYSTEM_MSG_UPDATE_DONE;
break;
}
FirmwareUpdate.Processing = false;
SystemLog.AddEvent(LogLevel_Info, "OTA update DONE. " + FirmwareUpdate.UpdatingStatus);
return b_ret;
}
/**
@brief CB function OTA update process
@param int - current bytes
@param int - total bytes
@return none
*/
void System_OtaUpdateProgressCB(int cur, int total) {
/* OTA update from server */
FirmwareUpdate.FirmwareSize = total;
uint8_t updateProgress = (cur * 100) / FirmwareUpdate.FirmwareSize;
FirmwareUpdate.PercentProcess = updateProgress;
FirmwareUpdate.TransferedBytes = cur;
SystemLog.AddEvent(LogLevel_Info, "Downloaded: " + String(cur) + "/" + String(FirmwareUpdate.FirmwareSize) + " -> " + String(FirmwareUpdate.PercentProcess) + "%");
}
/**
@brief CB function start OTA update
@param none
@return none
*/
void System_OtaUpdateStartCB() {
SystemLog.AddEvent(LogLevel_Info, "Start OTA update");
}
/**
@brief CB function start OTA update
@param none
@return none
*/
void System_OtaUpdateEndCB() {
SystemLog.AddEvent(LogLevel_Info, "OTA update done");
}
/**
@brief CB function error OTA update
@param int - error code
@return none
*/
void System_OtaUpdateErrorCB(int error) {
SystemLog.AddEvent(LogLevel_Info, "OTA update error: " + String(error));
}
/**
@brief Function for print reset reason
@param int - reset reason
@return String - reset reason
*/
String System_PrintMcuResetReason(int reason) {
String ret = "";
switch (reason) {
case 1: /**<1, Vbat power on reset*/
ret = "POWERON_RESET";
break;
case 3: /**<3, Software reset digital core*/
ret = "SW_RESET";
break;
case 4: /**<4, Legacy watch dog reset digital core*/
ret = "OWDT_RESET";
break;
case 5: /**<5, Deep Sleep reset digital core*/
ret = "DEEPSLEEP_RESET";
break;
case 6: /**<6, Reset by SLC module, reset digital core*/
ret = "SDIO_RESET";
break;
case 7: /**<7, Timer Group0 Watch dog reset digital core*/
ret = "TG0WDT_SYS_RESET";
break;
case 8: /**<8, Timer Group1 Watch dog reset digital core*/
ret = "TG1WDT_SYS_RESET";
break;
case 9: /**<9, RTC Watch dog Reset digital core*/
ret = "RTCWDT_SYS_RESET";
break;
case 10: /**<10, Instrusion tested to reset CPU*/
ret = "INTRUSION_RESET";
break;
case 11: /**<11, Time Group reset CPU*/
ret = "TGWDT_CPU_RESET";
break;
case 12: /**<12, Software reset CPU*/
ret = "SW_CPU_RESET";
break;
case 13: /**<13, RTC Watch dog Reset CPU*/
ret = "RTCWDT_CPU_RESET";
break;
case 14: /**<14, for APP CPU, reseted by PRO CPU*/
ret = "EXT_CPU_RESET";
break;
case 15: /**<15, Reset when the vdd voltage is not stable*/
ret = "RTCWDT_BROWN_OUT_RESET";
break;
case 16: /**<16, RTC Watch dog reset digital core and rtc module*/
ret = "RTCWDT_RTC_RESET";
break;
default:
ret = "NO_MEAN";
}
return ret;
}
/**
@brief Function for print reset reason
@param none
@return String - reset reason
*/
String System_printMcuResetReasonSimple() {
String ret = "";
esp_reset_reason_t reason = esp_reset_reason();
switch (reason) {
case ESP_RST_UNKNOWN:
ret = "Reset reason can not be determined";
break;
case ESP_RST_POWERON:
ret = "Reset due to power-on event";
break;
case ESP_RST_EXT:
ret = "Reset by external pin (not applicable for ESP32)";
break;
case ESP_RST_SW:
ret = "Software reset via esp_restart";
break;
case ESP_RST_PANIC:
ret = "Software reset due to exception/panic";
break;
case ESP_RST_INT_WDT:
ret = "Reset (software or hardware) due to interrupt watchdog";
break;
case ESP_RST_TASK_WDT:
ret = "Reset due to task watchdog";
break;
case ESP_RST_WDT:
ret = "Reset due to other watchdogs";
break;
case ESP_RST_DEEPSLEEP:
ret = "Reset after exiting deep sleep mode";
break;
case ESP_RST_BROWNOUT:
ret = "Brownout reset (software or hardware)";
break;
case ESP_RST_SDIO:
ret = "Reset over SDIO";
break;
default:
ret = "N/A";
break;
}
return ret;
}
/**
@brief Function for WiFi management system task
@param void *pvParameters
@return none
*/
void System_TaskWifiManagement(void *pvParameters) {
SystemLog.AddEvent(LogLevel_Info, "Task Wifi Management. core: " + String(xPortGetCoreID()));
TickType_t xLastWakeTime = xTaskGetTickCount();
while (1) {
/* wifi management. Enable/disable AP_STA mode and STA mode*/
SystemWifiMngt.WifiManagement();
/* wifi reconnect after signal lost */
SystemWifiMngt.WiFiReconnect();
SystemLog.AddEvent(LogLevel_Info, "Free RAM: " + String(ESP.getFreeHeap()) + " bytes");
SystemLog.AddEvent(LogLevel_Info, "Free SPIRAM: " + String(ESP.getFreePsram()) + " bytes");
SystemLog.AddEvent(LogLevel_Info, "Temperature: " + String(temperatureRead()) + " *C");
SystemLog.AddEvent(LogLevel_Verbose, "WiFiManagement task. Stack free size: " + String(uxTaskGetStackHighWaterMark(NULL)) + " bytes");
/* reset wdg */
esp_task_wdt_reset();
/* next start task */
vTaskDelayUntil(&xLastWakeTime, TASK_WIFI / portTICK_PERIOD_MS);
}
}
/**
* @brief Function for main system task
*
* @param void *pvParameters
* @return none
*/
void System_TaskMain(void *pvParameters) {
SystemLog.AddEvent(LogLevel_Info, "System task. core: " + String(xPortGetCoreID()));
TickType_t xLastWakeTime = xTaskGetTickCount();
while (1) {
/* for ota update */
esp_task_wdt_reset();
System_Main();
SystemLog.AddEvent(LogLevel_Verbose, "System task. Stack free size: " + String(uxTaskGetStackHighWaterMark(NULL)) + " bytes");
/* reset wdg */
esp_task_wdt_reset();
/* next start task */
vTaskDelayUntil(&xLastWakeTime, TASK_SYSTEM / portTICK_PERIOD_MS);
}
}
/**
* @brief Function for capture and send photo task
*
* @param void *pvParameters
* @return none
*/
void System_TaskCaptureAndSendPhoto(void *pvParameters) {
SystemLog.AddEvent(LogLevel_Info, "Task photo processing. core: " + String(xPortGetCoreID()));
TickType_t xLastWakeTime = xTaskGetTickCount();
uint16_t SendingIntervalCounter = 0; ///< counter for sending interval
while (1) {
if (Connect.GetRefreshInterval() <= SendingIntervalCounter) {
SendingIntervalCounter = 0;
/* send network information to backend */
if ((WL_CONNECTED == WiFi.status()) && (false == FirmwareUpdate.Processing)) {
esp_task_wdt_reset();
Connect.SendInfoToBackend();
}
/* send photo to backend*/
if ((WL_CONNECTED == WiFi.status()) && (false == FirmwareUpdate.Processing)) {
esp_task_wdt_reset();
Connect.TakePictureAndSendToBackend();
}
} else {
/* update counter */
SendingIntervalCounter++;
}
SystemLog.AddEvent(LogLevel_Verbose, "Photo processing task. Stack free size: " + String(uxTaskGetStackHighWaterMark(NULL)) + " bytes");
/* reset wdg */
esp_task_wdt_reset();
/* next start task */
vTaskDelayUntil(&xLastWakeTime, TASK_PHOTO_SEND / portTICK_PERIOD_MS);
}
}
/**
* @brief Function for micro SD card check task
*
* @param void *pvParameters
* @return none
*/
void System_TaskSdCardCheck(void *pvParameters) {
SystemLog.AddEvent(LogLevel_Info, "MicroSdCard check task. core: " + String(xPortGetCoreID()));
TickType_t xLastWakeTime = xTaskGetTickCount();
while (1) {
esp_task_wdt_reset();
/* check micro SD card */
if ((true == SystemLog.GetCardDetectAfterBoot()) && (false == SystemLog.GetCardDetectedStatus())) {
SystemLog.ReinitCard();
SystemLog.AddEvent(LogLevel_Warning, "Reinit micro SD card done!");
}
SystemLog.AddEvent(LogLevel_Verbose, "MicroSdCard task. Stack free size: " + String(uxTaskGetStackHighWaterMark(NULL)) + " bytes");
/* reset wdg */
esp_task_wdt_reset();
/* next start task */
vTaskDelayUntil(&xLastWakeTime, TASK_SDCARD / portTICK_PERIOD_MS);
}
}
/**
* @brief Function for serial configuration task
*
* @param void *pvParameters
* @return none
*/
void System_TaskSerialCfg(void *pvParameters) {
SystemLog.AddEvent(LogLevel_Info, "SerialCg task. core: " + String(xPortGetCoreID()));
TickType_t xLastWakeTime = xTaskGetTickCount();
while (1) {
esp_task_wdt_reset();
SystemSerialCfg.ProcessIncommingData();
SystemLog.AddEvent(LogLevel_Verbose, "SerialCfg task. Stack free size: " + String(uxTaskGetStackHighWaterMark(NULL)) + " bytes");
/* reset wdg */
esp_task_wdt_reset();
/* next start task */
vTaskDelayUntil(&xLastWakeTime, TASK_SERIAL_CFG / portTICK_PERIOD_MS);
}
}
/**
* @brief Function for stream telemetry task
*
* @param void *pvParameters
* @return none
*/
void System_TaskStreamTelemetry(void *pvParameters) {
SystemLog.AddEvent(LogLevel_Info, "StreamTelemetry task. core: " + String(xPortGetCoreID()));
TickType_t xLastWakeTime = xTaskGetTickCount();
while (1) {
esp_task_wdt_reset();
SystemLog.AddEvent(LogLevel_Verbose, "StreamTelemetry task. Stack free size: " + String(uxTaskGetStackHighWaterMark(NULL)) + " bytes");
if (SystemCamera.GetStreamStatus()) {
char buf[80] = { '\0' };
sprintf(buf, "Stream, average data in %dsec. FPS: %.1f, Size: %uKB", (TASK_STREAM_TELEMETRY / SECOND_TO_MILISECOND), SystemCamera.StreamGetFrameAverageFps(), SystemCamera.StreamGetFrameAverageSize());
SystemLog.AddEvent(LogLevel_Info, buf);
SystemCamera.StreamClearFrameData();
}
/* reset wdg */
esp_task_wdt_reset();
/* next start task */
vTaskDelayUntil(&xLastWakeTime, TASK_STREAM_TELEMETRY / portTICK_PERIOD_MS);
}
}
/**
* @brief Function for system led task
*
* @param void *pvParameters
* @return none
*/
void System_TaskSysLed(void *pvParameters) {
SystemLog.AddEvent(LogLevel_Info, "SystemLed task. core: " + String(xPortGetCoreID()));
TickType_t xLastWakeTime = xTaskGetTickCount();
while (1) {
system_led.toggle();
/* reset wdg */
esp_task_wdt_reset();
/* next start task */
vTaskDelayUntil(&xLastWakeTime, system_led.getTimer() / portTICK_PERIOD_MS);
}
}
void System_TaskWiFiWatchdog(void *pvParameters) {
SystemLog.AddEvent(LogLevel_Info, "WiFiWatchdog task. core: " + String(xPortGetCoreID()));
TickType_t xLastWakeTime = xTaskGetTickCount();
while (1) {
esp_task_wdt_reset();
SystemWifiMngt.WiFiWatchdog();
SystemLog.AddEvent(LogLevel_Verbose, "WiFiWatchdog task. Stack free size: " + String(uxTaskGetStackHighWaterMark(NULL)) + " bytes");
/* reset wdg */
esp_task_wdt_reset();
/* next start task */
vTaskDelayUntil(&xLastWakeTime, TASK_WIFI_WATCHDOG / portTICK_PERIOD_MS);
}
}
/* EOF */

View File

@ -0,0 +1,68 @@
/**
@file system.h
@brief system library
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#ifndef _SYSTEM_H_
#define _SYSTEM_H_
#include <WiFi.h>
#include <Update.h>
#include "Arduino.h"
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <HTTPUpdate.h>
#include <ArduinoJson.h>
#include <esp_wifi.h>
#include "esp32/rom/rtc.h"
#include <esp_task_wdt.h>
#include "mcu_cfg.h"
#include "var.h"
#include "cfg.h"
#include "log.h"
#include "Certificate.h"
#include "wifi_mngt.h"
#include "connect.h"
#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!"
void System_Init();
void System_LoadCfg();
void System_CheckIfPsramIsUsed();
void System_Main();
void System_UpdateInit();
void System_CheckNewVersion();
void System_OtaCloudUpdate();
bool System_OtaUpdateStart();
void System_OtaUpdateProgressCB(int, int);
void System_OtaUpdateErrorCB(int);
void System_OtaUpdateEndCB();
void System_OtaUpdateStartCB();
String System_PrintMcuResetReason(int);
String System_printMcuResetReasonSimple();
void System_TaskWifiManagement(void *);
void System_TaskMain(void *);
void System_TaskCaptureAndSendPhoto(void *);
void System_TaskSdCardCheck(void *);
void System_TaskSerialCfg(void *);
void System_TaskStreamTelemetry(void *);
void System_TaskSysLed(void *);
void System_TaskWiFiWatchdog(void *);
#endif
/* EOF */

View File

@ -0,0 +1,26 @@
/**
@file variable.cpp
@brief Library with global variables
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#include "var.h"
WebBasicAuth_struct WebBasicAuth = { false, "", "" };
struct FirmwareUpdate_struct FirmwareUpdate = { "Ready", false, 0, 0, 0, false, false, "", "", "", false };
TaskHandle_t Task_CapturePhotoAndSend;
TaskHandle_t Task_WiFiManagement;
TaskHandle_t Task_SystemMain;
TaskHandle_t Task_SdCardCheck;
TaskHandle_t Task_SerialCfg;
TaskHandle_t Task_StreamTelemetry;
TaskHandle_t Task_SysLed;
TaskHandle_t Task_WiFiWatchdog;
/* EOF */

View File

@ -0,0 +1,54 @@
/**
@file variable.h
@brief Library with global variables
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#ifndef _VARIABLE_H_
#define _VARIABLE_H_
#include "Arduino.h"
#include "mcu_cfg.h"
struct WebBasicAuth_struct {
bool EnableAuth; ///< user definition for enable/disable basic auth
String UserName; ///< login name for basic auth
String Password; ///< password for basic auth
};
struct FirmwareUpdate_struct {
String UpdatingStatus; ///< Updateing status
bool Processing; ///< status abour processing firmware update
uint8_t PercentProcess; ///< processed firmware update
int TransferedBytes; ///< transfered bytes
int FirmwareSize; ///< uploaded firmware size
bool StartOtaUpdate; ///< Start OTA update process
bool CheckNewVersionAfterBoot; ///< Check new version OTA update after MCU boot
String NewVersionFw; ///< New FW version
String CheckNewVersionFwStatus; ///< connection status from checking new OTA update version
String OtaUpdateFwUrl; ///< URL for OTA update
bool OtaUpdateFwAvailable; ///< flag for available new FW version
};
extern struct WebBasicAuth_struct WebBasicAuth; ///< structure with configuration for basic auth
extern struct FirmwareUpdate_struct FirmwareUpdate; ///< firmware update status and process
extern TaskHandle_t Task_CapturePhotoAndSend; ///< task handle for capture photo and send
extern TaskHandle_t Task_WiFiManagement; ///< task handle for wifi management
extern TaskHandle_t Task_SystemMain; ///< task handle for system main
extern TaskHandle_t Task_SdCardCheck; ///< task handle for sd card check
extern TaskHandle_t Task_SerialCfg; ///< task handle for serial configuration
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
#endif
/* EOF */

View File

@ -0,0 +1,919 @@
/**
@file wifi_mngt.cpp
@brief
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#include "wifi_mngt.h"
WiFiMngt SystemWifiMngt(&SystemConfig, &SystemLog, &SystemCamera);
/**
@brief Constructor for WiFiMngt class
@param Configuration* - pointer to Configuration class
@param Logs* - pointer to Logs class
@param Camera* - pointer to Camera class
@return none
*/
WiFiMngt::WiFiMngt(Configuration *i_conf, Logs *i_log, Camera *i_cam) {
config = i_conf;
log = i_log;
cam = i_cam;
Service_LocalIp.fromString(String(SERVICE_LOCAL_IP));
Service_Gateway.fromString(String(SERVICE_LOCAL_GATEWAY));
Service_Subnet.fromString(String(SERVICE_LOCAL_MASK));
Service_Dns.fromString(String(SERVICE_LOCAL_DNS));
NtpFirstSync = false;
StartStaWdg = false;
}
/**
@brief Loaf cfg from EEPROM
@param none
@return none
*/
void WiFiMngt::LoadCfgFromEeprom() {
WifiSsid = config->LoadWifiSsid();
WifiPassword = config->LoadWifiPassowrd();
mDNS_record = config->LoadMdnsRecord();
}
/**
@brief function for init wifi module
@param none
@return none
*/
void WiFiMngt::Init() {
/* check WI-FI mode */
system_led.setTimer(STATUS_LED_WIFI_AP);
ServiceMode = true;
log->AddEvent(LogLevel_Info, "WiFi MAC: " + WiFi.macAddress());
/* Set Wi-Fi networks */
SetWifiEvents();
CreateApSsid();
log->AddEvent(LogLevel_Warning, "Set WiFi AP mode");
WiFi.mode(WIFI_AP_STA);
esp_wifi_set_ps(WIFI_PS_NONE);
WiFi.softAPConfig(Service_LocalIp, Service_Gateway, Service_Subnet);
WiFi.softAP(SericeApSsid.c_str(), SERVICE_WIFI_PASS, SERVICE_WIFI_CHANNEL);
WiFi.setHostname(DEVICE_HOSTNAME);
WiFiMode = "AP + Client";
log->AddEvent(LogLevel_Info, "Service IP Address: http://" + WiFi.softAPIP().toString());
FirstConnected = false;
//WiFi.setTxPower(WIFI_POWER_18_5dBm);
if (config->CheckActifeWifiCfgFlag() == true) {
if (true == CheckAvailableWifiNetwork(WifiSsid)) {
WiFiStaConnect();
log->AddEvent(LogLevel_Warning, "Connecting to WiFi: " + WifiSsid);
#if (WIFI_CLIENT_WAIT_CON == true)
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
log->AddEvent(LogLevel_Verbose, ".");
}
WifiCfg.FirstConnected = true;
/* Print ESP32 Local IP Address */
log->AddEvent(LogLevel_Info, "WiFi network IP Address: http://" + WiFi.localIP().toString());
#endif
} else {
log->AddEvent(LogLevel_Warning, "Wifi unavailable. Skip connecting to WiFi: " + WifiSsid);
}
}
TaskAp_previousMillis = millis();
/* Init MDNS record */
log->AddEvent(LogLevel_Info, "Starting mDNS record: http://" + mDNS_record + ".local");
if (!MDNS.begin(mDNS_record)) {
log->AddEvent(LogLevel_Error, "Error starting mDNS");
} else {
log->AddEvent(LogLevel_Info, "Starting mDNS OK");
}
MDNS.addService("http", "tcp", 80);
}
/**
@brief function for management wifi network
@param none
@return none
*/
void WiFiMngt::WifiManagement() {
/* check disable service AP */
unsigned long currentMillis = millis();
if (currentMillis - TaskAp_previousMillis >= STA_AP_MODE_TIMEOUT) {
TaskAp_previousMillis = currentMillis;
if ((true == config->CheckActifeWifiCfgFlag()) && (true == ServiceMode) && (WL_CONNECTED == WiFi.status())
&& (false == FirmwareUpdate.Processing) && (false == cam->GetStreamStatus())) {
if (WiFi.softAPgetStationNum() == 0) {
log->AddEvent(LogLevel_Info, "Disable service AP mode");
WiFi.mode(WIFI_STA);
esp_wifi_set_ps(WIFI_PS_NONE);
WiFiStaConnect();
//WiFi.begin(WifiSsid, WifiPassword);
WiFiMode = "Client";
#if (WIFI_CLIENT_WAIT_CON == true)
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
log->AddEvent(LogLevel_Verbose, ".");
}
log->AddEvent(LogLevel_Verbose, "Connected to WiFi");
#endif
ServiceMode = false;
WiFi.softAPdisconnect(true);
} else {
log->AddEvent(LogLevel_Info, "Client [" + String(WiFi.softAPgetStationNum()) + "] still is connected to Wi-Fi AP!");
}
}
}
/* check first NTP time sync after boot */
if ((true == config->CheckActifeWifiCfgFlag()) && (false == NtpFirstSync) && (WL_CONNECTED == WiFi.status())) {
SyncNtpTime();
}
}
/**
@brief CB function for reconnect to wifi network
@param none
@return none
*/
void WiFiMngt::WiFiReconnect() {
if ((WiFi.status() != WL_CONNECTED) && (FirstConnected == true)) {
log->AddEvent(LogLevel_Warning, "Reconnecting to WiFi. STA");
WiFi.disconnect();
log->AddEvent(LogLevel_Warning, "Disconnect from WiFi");
WiFi.reconnect();
log->AddEvent(LogLevel_Warning, "Reconnecting to WiFi. STA");
} else if (WiFi.status() == WL_CONNECTED) {
char cstr[150];
sprintf(cstr, "Wifi connected. SSID: %s, BSSID: %s, RSSI: %d dBm, IP: %s, TX power: %s", WiFi.SSID().c_str(), WiFi.BSSIDstr().c_str(), WiFi.RSSI(), WiFi.localIP().toString().c_str(), TranslateTxPower(WiFi.getTxPower()).c_str()); //print 3 digits
log->AddEvent(LogLevel_Info, "WiFi status: " + String(cstr));
}
if (Connect.GetBackendAvailabilitStatus() == BackendUnavailable) {
log->AddEvent(LogLevel_Warning, "Reconnecting to WiFi. STA. Problem with connecting to backend!");
WiFi.disconnect();
WiFi.reconnect();
Connect.SetBackendAvailabilitStatus(WaitForFirstConnection);
}
}
/**
@brief Function for sets WiFi CB calls
@param none
@return none
*/
void WiFiMngt::SetWifiEvents() {
WiFi.onEvent(WiFiMngt_WiFiEventScanDone, WiFiEvent_t::ARDUINO_EVENT_WIFI_SCAN_DONE);
WiFi.onEvent(WiFiMngt_WiFiEventStationStart, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_START);
WiFi.onEvent(WiFiMngt_WiFiEventStationStop, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_STOP);
WiFi.onEvent(WiFiMngt_WiFiEventStationConnected, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_CONNECTED);
WiFi.onEvent(WiFiMngt_WiFiEventStationDisconnected, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED);
WiFi.onEvent(WiFiMngt_WiFiEventGotIP, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP);
WiFi.onEvent(WiFiMngt_WiFiEventLostIP, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_LOST_IP);
WiFi.onEvent(WiFiMngt_WiFiEventApStart, WiFiEvent_t::ARDUINO_EVENT_WIFI_AP_START);
WiFi.onEvent(WiFiMngt_WiFiEventApStop, WiFiEvent_t::ARDUINO_EVENT_WIFI_AP_STOP);
WiFi.onEvent(WiFiMngt_WiFiEventApStaConnected, WiFiEvent_t::ARDUINO_EVENT_WIFI_AP_STACONNECTED);
WiFi.onEvent(WiFiMngt_WiFiEventApStaDisconnected, WiFiEvent_t::ARDUINO_EVENT_WIFI_AP_STADISCONNECTED);
WiFi.onEvent(WiFiMngt_WiFiEventApStaIpAssigned, WiFiEvent_t::ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED);
WiFi.onEvent(WiFiMngt_WiFiEventApStaProbeReqRecved, WiFiEvent_t::ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED);
}
/**
@brief Function for connect to STA
@param none
@return none
@note https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/api/wifi.html#sta-connection
*/
void WiFiMngt::WiFiStaConnect() {
if (config->CheckActifeWifiCfgFlag() == true) {
system_led.setTimer(STATUS_LED_STA_CONNECTING);
if (false == WiFiStaMultipleNetwork) {
WiFi.begin(WifiSsid, WifiPassword);
log->AddEvent(LogLevel_Info, "Connecting to STA SSID");
} else if (true == WiFiStaMultipleNetwork) {
WiFi.begin(WifiSsid, WifiPassword, 0, WiFiStaNetworkBssid);
log->AddEvent(LogLevel_Info, "Connecting to STA BSSID");
}
WiFi.setAutoReconnect(true);
}
}
/**
@brief Function for set time from NTP server
@param none
@return none
*/
void WiFiMngt::SyncNtpTime() {
if (WL_CONNECTED == WiFi.status()) {
/* configure NTP server and timezone to UTC */
configTime(0, 0, "pool.ntp.org", "time.nist.gov"); // UTC
log->AddEvent(LogLevel_Info, "Waiting for NTP time sync: ");
log->SetNtpTimeSynced(false);
/* wait maximum 10s for time sync */
unsigned long startMillis = millis();
struct tm timeinfo;
while ((millis() - startMillis) < 10000) {
if (getLocalTime(&timeinfo)) {
log->SetNtpTimeSynced(true);
break;
}
yield();
delay(500);
}
/* report sync status */
if (true == log->GetNtpTimeSynced()) {
log->AddEvent(LogLevel_Info, "Sync NTP time done. Set UTC timezone");
NtpFirstSync = true;
} else {
log->AddEvent(LogLevel_Info, "Sync NTP time fail");
}
}
}
/**
@brief Function for scan wifi network
@param none
@return none
*/
void WiFiMngt::ScanWiFiNetwork() {
ScanWifiNetwork("");
}
/**
@brief Function for scan wifi network and check available wifi network
@param String - searched wifi network
@return uint8_t - count of found wifi networks with same SSID
*/
uint8_t WiFiMngt::ScanWifiNetwork(String ssid) {
log->AddEvent(LogLevel_Info, "Scan WI-FI networks");
log->AddEvent(LogLevel_Info, "Check available WI-FI network: " + ssid);
uint8_t ret = 0; ///< total wifi network count
int bestSignal = -100; ///< wifi network with best signal (when is available multiple networks with same SSID)
uint8_t bssid[6] = { 0 };
/* scan WI-FI networks */
int n = WiFi.scanNetworks();
log->AddEvent(LogLevel_Verbose, "Scan done");
JsonDocument doc_json;
JsonArray wifiArray = doc_json.to<JsonArray>();
WifiScanJson = "";
/* make json with each found WI-FI networks */
if (n == 0) {
log->AddEvent(LogLevel_Info, "No networks found!");
} else {
log->AddEvent(LogLevel_Info, String(n) + " networks found");
log->AddEvent(LogLevel_Info, "Nr | SSID | RSSI | CH | BSSID | Encryption");
for (int i = 0; i < n; ++i) {
/* check available wifi network */
if (WiFi.SSID(i) == ssid) {
ret++;
if (WiFi.RSSI(i) > bestSignal) {
bestSignal = WiFi.RSSI(i);
uint8_t *tmp = WiFi.BSSID(i);
memcpy(bssid, tmp, 6);
}
}
/* make json with available wifi networks */
JsonObject wifi_arr = wifiArray.add<JsonObject>();
wifi_arr["ssid"] = WiFi.SSID(i);
wifi_arr["bssid"] = WiFi.BSSIDstr(i);
wifi_arr["rssi"] = String(WiFi.RSSI(i));
wifi_arr["rssi_percentage"] = String(Rssi2Percent(WiFi.RSSI(i)));
wifi_arr["channel"] = String(WiFi.channel(i));
wifi_arr["encryption"] = TranslateWiFiEncrypion(WiFi.encryptionType(i));
// Print SSID and RSSI for each network found
char formattedString[100] = { '\0' };
sprintf(formattedString, "%2d | %-32.32s | %4d | %2d | %-17s | %s", i + 1,
WiFi.SSID(i).c_str(), WiFi.RSSI(i), WiFi.channel(i), WiFi.BSSIDstr(i).c_str(), TranslateWiFiEncrypion(WiFi.encryptionType(i)).c_str());
log->AddEvent(LogLevel_Info, formattedString);
}
}
serializeJson(doc_json, WifiScanJson);
// Delete the scan result to free memory for code below.
WiFi.scanDelete();
log->AddEvent(LogLevel_Verbose, WifiScanJson);
/* print status */
if (1 <= ret) {
log->AddEvent(LogLevel_Info, "SSID: " + ssid + " found, " + String(ret) + "x");
if (1 < ret) {
memcpy(WiFiStaNetworkBssid, bssid, 6);
WiFiStaMultipleNetwork = true;
char mac[18] = { 0 };
sprintf(mac, "%02X:%02X:%02X:%02X:%02X:%02X", WiFiStaNetworkBssid[0], WiFiStaNetworkBssid[1], WiFiStaNetworkBssid[2], WiFiStaNetworkBssid[3], WiFiStaNetworkBssid[4], WiFiStaNetworkBssid[5]);
log->AddEvent(LogLevel_Info, "WiFi roaming found! Connecting to " + String(mac) + " -> " + String(bestSignal) + "dBm, " + String(WiFiStaMultipleNetwork));
}
} else {
log->AddEvent(LogLevel_Info, "SSID: " + ssid + " not found");
}
return ret;
}
/**
@brief Function for check available wifi network
@param String - ssid
@return bool - true if wifi network is available
*/
bool WiFiMngt::CheckAvailableWifiNetwork(String i_data) {
uint8_t tmp = ScanWifiNetwork(i_data);
bool ret = false;
if (tmp >= 1) {
ret = true;
}
return ret;
}
/**
@brief Function for create AP SSID name
@param none
@return none
*/
void WiFiMngt::CreateApSsid() {
String name = SERVICE_WIFI_SSID;
#if (true == SERVICE_WIFI_SSID_UID)
name += "_";
/* copy just last 3 numbers from ID */
for (size_t i = UniqueIDsize - 3; i < UniqueIDsize; i++) {
name += String(UniqueID[i]);
if (i < (UniqueIDsize - 1))
name += ".";
}
#endif
SericeApSsid = name;
log->AddEvent(LogLevel_Info, "Service AP SSID: " + SericeApSsid);
}
/**
* @brief WiFi watchdog for check connection to STA
*
*/
void WiFiMngt::WiFiWatchdog() {
/* when is enabled wifi configuration, and is not connected to wifi network, and is available at least one wifi network */
if ((true == config->CheckActifeWifiCfgFlag()) && (WL_CONNECTED != WiFi.status()) && (true == GetFirstConnection())) {
log->AddEvent(LogLevel_Warning, "WiFi WDG. STA connection lost.");
unsigned long currentMillis = millis();
if (false == StartStaWdg) {
if (ScanWifiNetwork(WifiSsid) >= 1) {
StartStaWdg = true;
TaskWdg_previousMillis = currentMillis;
log->AddEvent(LogLevel_Warning, "WiFi STA connection lost. Start watchdog timer!");
}
}
if ((true == StartStaWdg) && (currentMillis - TaskWdg_previousMillis >= WIFI_STA_WDG_TIMEOUT)) {
log->AddEvent(LogLevel_Warning, "WiFi STA connection lost. WDG timer expired. Restart MCU!");
/* restart MCU, or disconnect and connect to WiFi again ? From my point of view, and testing, restart MCU is better */
ESP.restart();
}
} else if (true == StartStaWdg) {
log->AddEvent(LogLevel_Info, "WiFi STA connection OK. Stop watchdog timer!");
StartStaWdg = false;
TaskWdg_previousMillis = millis();
}
}
/**
@brief Convert WiFi signal RSSI to percent
@param int - rssi
@return int - percent
*/
int WiFiMngt::Rssi2Percent(int rssi) {
int signalStrength = 0;
if (0 == rssi) {
signalStrength = 0;
} else {
if (rssi <= -100)
signalStrength = 0;
else if (rssi >= -50)
signalStrength = 100;
else
signalStrength = 2 * (rssi + 100);
}
return signalStrength;
}
/**
@brief function for translate from wifi_power_t to string TX power
@param wifi_power_t - value
@return String - value
*/
String WiFiMngt::TranslateTxPower(wifi_power_t data) {
String ret = "";
switch (data) {
case WIFI_POWER_MINUS_1dBm:
ret = "-1dBm";
break;
case WIFI_POWER_2dBm:
ret = "2dBm";
break;
case WIFI_POWER_5dBm:
ret = "5dBm";
break;
case WIFI_POWER_7dBm:
ret = "7dBm";
break;
case WIFI_POWER_8_5dBm:
ret = "8.5dBm";
break;
case WIFI_POWER_11dBm:
ret = "11dBm";
break;
case WIFI_POWER_13dBm:
ret = "13dBm";
break;
case WIFI_POWER_15dBm:
ret = "15dBm";
break;
case WIFI_POWER_17dBm:
ret = "17dBm";
break;
case WIFI_POWER_18_5dBm:
ret = "18.5dBm";
break;
case WIFI_POWER_19dBm:
ret = "19dBm";
break;
case WIFI_POWER_19_5dBm:
ret = "19.5dBm";
break;
}
return ret;
}
/**
@brief function for translate from wl_status_t to string
@param wl_status_t - value
@return String - value
@note https://github.com/esp8266/Arduino/blob/0c897c37a6eab3eab34147219617945a32a9b155/libraries/ESP8266WiFi/src/include/wl_definitions.h#L50
*/
String WiFiMngt::TranslateWiFiStatus(wl_status_t i_wifi_status) {
String ret = "";
switch (i_wifi_status) {
case WL_IDLE_STATUS:
ret = "Idle";
break;
case WL_NO_SSID_AVAIL:
ret = "No SSID available";
break;
case WL_SCAN_COMPLETED:
ret = "Scan completed";
break;
case WL_CONNECTED:
ret = "Connected";
break;
case WL_CONNECT_FAILED:
ret = "Connect failed";
break;
case WL_CONNECTION_LOST:
ret = "Connection lost";
break;
case WL_DISCONNECTED:
ret = "Disconnected";
break;
case WL_NO_SHIELD:
ret = "No WiFi shield";
break;
default:
ret = "Unkcnown status";
ret += String(i_wifi_status);
}
return ret;
}
/**
@brief function for translate from wifi_auth_mode_t to string
@param wifi_auth_mode_t - value
@return String - value
*/
String WiFiMngt::TranslateWiFiEncrypion(wifi_auth_mode_t i_data) {
String ret = "";
switch (i_data) {
case WIFI_AUTH_OPEN:
ret = "open";
break;
case WIFI_AUTH_WEP:
ret = "WEP";
break;
case WIFI_AUTH_WPA_PSK:
ret = "WPA";
break;
case WIFI_AUTH_WPA2_PSK:
ret = "WPA2";
break;
case WIFI_AUTH_WPA_WPA2_PSK:
ret = "WPA+WPA2";
break;
case WIFI_AUTH_WPA2_ENTERPRISE:
ret = "WPA2-EAP";
break;
case WIFI_AUTH_WPA3_PSK:
ret = "WPA3";
break;
case WIFI_AUTH_WPA2_WPA3_PSK:
ret = "WPA2+WPA3";
break;
case WIFI_AUTH_WAPI_PSK:
ret = "WAPI";
break;
default:
ret = "unknown";
}
return ret;
}
/**
@brief function for get service AP SSID name
@param none
@return String - value
*/
String WiFiMngt::GetServiceApSsid() {
return SericeApSsid;
}
/**
@brief function for get STA SSID name
@param none
@return String - value
*/
String WiFiMngt::GetStaSsid() {
return WifiSsid;
}
/**
@brief function for get STA BSSID
@param none
@return String - value
*/
String WiFiMngt::GetStaBssid() {
return WiFi.BSSIDstr();
}
/**
@brief function for get STA password
@param none
@return String - value
*/
String WiFiMngt::GetStaPassword() {
return WifiPassword;
}
/**
@brief function for get STA status
@param none
@return String - value
*/
String WiFiMngt::GetStaStatus() {
return TranslateWiFiStatus(WiFi.status());
}
/**
@brief function for get STA IP
@param none
@return String - value
*/
String WiFiMngt::GetStaIp() {
return WiFi.localIP().toString();
}
/**
@brief function for get available wifi networks
@param none
@return String - value
*/
String WiFiMngt::GetAvailableWifiNetworks() {
return WifiScanJson;
}
/**
@brief function for get Wi-Fi mode
@param none
@return String - value
*/
String WiFiMngt::GetWiFiMode() {
return WiFiMode;
}
/**
* @brief function for get Wi-Fi MAC address
*
* @return String - value
*/
String WiFiMngt::GetWifiMac() {
return String(WiFi.macAddress());
}
/**
@brief function for get mDNS record
@param none
@return String - value
*/
String WiFiMngt::GetMdns() {
return mDNS_record;
}
/**
@brief function for get first time NTP sync status
@param none
@return bool - value
*/
bool WiFiMngt::GetNtpFirstTimeSync() {
return NtpFirstSync;
}
/**
* @brief get first connection status
*
* @return bool - status
*/
bool WiFiMngt::GetFirstConnection() {
return FirstConnected;
}
/**
@brief function for set STA credentials
@param String - ssid
@param String - password
@return none
*/
void WiFiMngt::SetStaCredentials(String i_ssid, String i_pass) {
WifiSsid = i_ssid;
config->SaveWifiSsid(WifiSsid);
WifiPassword = i_pass;
config->SaveWifiPassword(WifiPassword);
config->SaveWifiCfgFlag(CFG_WIFI_SETTINGS_SAVED);
}
/**
@brief function for set STA SSID
@param String - STA SSID name
@return none
*/
void WiFiMngt::SetStaSsid(String i_ssid) {
WifiSsid = i_ssid;
config->SaveWifiSsid(WifiSsid);
}
/**
@brief function for set STA password
@param String - STA password
@return none
*/
void WiFiMngt::SetStaPassword(String i_pass) {
WifiPassword = i_pass;
config->SaveWifiPassword(WifiPassword);
}
/**
@brief function for connect to STA
@param none
@return none
*/
void WiFiMngt::ConnectToSta() {
config->SaveWifiCfgFlag(CFG_WIFI_SETTINGS_SAVED);
}
/**
@brief function for get active wifi configuration flag
@param none
@return bool - value
*/
bool WiFiMngt::GetkActifeWifiCfgFlag() {
return config->CheckActifeWifiCfgFlag();
}
/**
@brief function for set mDNS record
@param String - mDNS record
@return none
*/
void WiFiMngt::SetMdns(String i_data) {
mDNS_record = i_data;
config->SaveMdnsRecord(mDNS_record);
}
/**
@brief function for set first connection flag
@param bool - data
@return none
*/
void WiFiMngt::SetFirstConnection(bool i_data) {
FirstConnected = i_data;
}
/* ----------------------- Static function ----------------------- */
/**
@brief CB function event about scan wifi networks done
@param WiFiEvent_t
@param WiFiEventInfo_t
@return none
*/
void WiFiMngt_WiFiEventScanDone(WiFiEvent_t event, WiFiEventInfo_t info) {
SystemLog.AddEvent(LogLevel_Info, "WiFi networks scan done");
}
/**
@brief CB function for event start station
@param WiFiEvent_t
@param WiFiEventInfo_t
@return none
*/
void WiFiMngt_WiFiEventStationStart(WiFiEvent_t event, WiFiEventInfo_t info) {
SystemLog.AddEvent(LogLevel_Info, "WiFi STA start");
}
/**
@brief CB function for event stop station
@param WiFiEvent_t
@param WiFiEventInfo_t
@return none
*/
void WiFiMngt_WiFiEventStationStop(WiFiEvent_t event, WiFiEventInfo_t info) {
SystemLog.AddEvent(LogLevel_Info, "WiFi STA stop");
}
/**
@brief CB function for event got information about check station connected
@param WiFiEvent_t
@param WiFiEventInfo_t
@return none
*/
void WiFiMngt_WiFiEventStationConnected(WiFiEvent_t event, WiFiEventInfo_t info) {
SystemLog.AddEvent(LogLevel_Info, "WiFi connected to STA");
}
/**
@brief CB function for got IP after connecting to wifi network
@param WiFiEvent_t
@param WiFiEventInfo_t
@return none
*/
void WiFiMngt_WiFiEventGotIP(WiFiEvent_t event, WiFiEventInfo_t info) {
system_led.setTimer(STATUS_LED_STA_CONNECTED);
SystemLog.AddEvent(LogLevel_Info, "WiFi Got IP address: " + WiFi.localIP().toString());
SystemLog.AddEvent(LogLevel_Info, "WiFi Got mask: " + WiFi.subnetMask().toString());
SystemLog.AddEvent(LogLevel_Info, "WiFi Got gateway: " + WiFi.gatewayIP().toString());
SystemLog.AddEvent(LogLevel_Info, "WiFi Got DNS 1: " + WiFi.dnsIP(0).toString());
SystemLog.AddEvent(LogLevel_Info, "WiFi Got DNS 2: " + WiFi.dnsIP(1).toString());
SystemWifiMngt.SetFirstConnection(true);
/* update device information */
Connect.UpdateDeviceInformation();
/* check first NTP time sync after boot */
if (false == SystemWifiMngt.GetNtpFirstTimeSync()) {
SystemWifiMngt.SyncNtpTime();
}
}
/**
@brief CB function for lost IP after connecting to wifi network
@param WiFiEvent_t
@param WiFiEventInfo_t
@return none
*/
void WiFiMngt_WiFiEventLostIP(WiFiEvent_t event, WiFiEventInfo_t info) {
SystemLog.AddEvent(LogLevel_Info, "WiFi lost IP address");
}
/**
@brief CB function for got information about disconnecting from wifi network
REASON_UNSPECIFIED = 1,
REASON_AUTH_EXPIRE = 2,
REASON_AUTH_LEAVE = 3,
REASON_ASSOC_EXPIRE = 4,
REASON_ASSOC_TOOMANY = 5,
REASON_NOT_AUTHED = 6,
REASON_NOT_ASSOCED = 7,
REASON_ASSOC_LEAVE = 8,
REASON_ASSOC_NOT_AUTHED = 9,
REASON_DISASSOC_PWRCAP_BAD = 10,
REASON_DISASSOC_SUPCHAN_BAD = 11,
REASON_IE_INVALID = 13,
REASON_MIC_FAILURE = 14,
REASON_4WAY_HANDSHAKE_TIMEOUT = 15,
REASON_GROUP_KEY_UPDATE_TIMEOUT = 16,
REASON_IE_IN_4WAY_DIFFERS = 17,
REASON_GROUP_CIPHER_INVALID = 18,
REASON_PAIRWISE_CIPHER_INVALID = 19,
REASON_AKMP_INVALID = 20,
REASON_UNSUPP_RSN_IE_VERSION = 21,
REASON_INVALID_RSN_IE_CAP = 22,
REASON_802_1X_AUTH_FAILED = 23,
REASON_CIPHER_SUITE_REJECTED = 24,
REASON_BEACON_TIMEOUT = 200,
REASON_NO_AP_FOUND = 201,
REASON_AUTH_FAIL = 202,
REASON_ASSOC_FAIL = 203,
REASON_HANDSHAKE_TIMEOUT = 204,
@param WiFiEvent_t
@param WiFiEventInfo_t
@return none
@note https://github.com/espressif/arduino-esp32/blob/04963009eedfbc1e0ea2e1378ae69e7cebda6fd6/tools/sdk/include/esp32/esp_event_legacy.h
*/
void WiFiMngt_WiFiEventStationDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) {
SystemLog.AddEvent(LogLevel_Warning, String("WiFi disconnected from access point. Reason: ") + String(info.wifi_sta_disconnected.reason));
system_led.setTimer(STATUS_LED_ERROR);
}
/**
@brief CB function for start AP
@param WiFiEvent_t
@param WiFiEventInfo_t
@return none
*/
void WiFiMngt_WiFiEventApStart(WiFiEvent_t event, WiFiEventInfo_t info) {
SystemLog.AddEvent(LogLevel_Info, "WiFi AP start");
}
/**
@brief CB function for stop AP
@param WiFiEvent_t
@param WiFiEventInfo_t
@return none
*/
void WiFiMngt_WiFiEventApStop(WiFiEvent_t event, WiFiEventInfo_t info) {
SystemLog.AddEvent(LogLevel_Info, "WiFi AP stop");
}
/**
@brief CB function for AP STA connected
@param WiFiEvent_t
@param WiFiEventInfo_t
@return none
*/
void WiFiMngt_WiFiEventApStaConnected(WiFiEvent_t event, WiFiEventInfo_t info) {
uint8_t mac[6] = { 0 };
char msg[100] = { '\0' };
memcpy(mac, info.wifi_ap_staconnected.mac, 6);
sprintf(msg, "WiFi AP STA. station connected to AP, MAC: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
SystemLog.AddEvent(LogLevel_Info, msg);
}
/**
@brief CB function for AP STA disconnected
@param WiFiEvent_t
@param WiFiEventInfo_t
@return none
*/
void WiFiMngt_WiFiEventApStaDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) {
uint8_t mac[6] = { 0 };
char msg[100] = { '\0' };
memcpy(mac, info.wifi_ap_stadisconnected.mac, 6);
// String(info.wifi_ap_stadisconnected.reason)
sprintf(msg, "WiFi AP STA. station dicconnected from AP, MAC: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
SystemLog.AddEvent(LogLevel_Info, msg);
}
/**
@brief CB function for AP STA IP assigned
@param WiFiEvent_t
@param WiFiEventInfo_t
@return none
*/
void WiFiMngt_WiFiEventApStaIpAssigned(WiFiEvent_t event, WiFiEventInfo_t info) {
IPAddress stationIP = info.wifi_ap_staipassigned.ip.addr;
SystemLog.AddEvent(LogLevel_Info, "WiFi AP STA. IP assigned to connected station. IP: " + stationIP.toString());
}
/**
@brief CB function for AP STA probe request received
@param WiFiEvent_t
@param WiFiEventInfo_t
@return none
*/
void WiFiMngt_WiFiEventApStaProbeReqRecved(WiFiEvent_t event, WiFiEventInfo_t info) {
SystemLog.AddEvent(LogLevel_Info, "WiFi AP STA receive probe request packet in soft-AP interface");
}
/* EOF */

View File

@ -0,0 +1,123 @@
/**
@file wifi_mngt.h
@brief
@author Miroslav Pivovarsky
Contact: miroslav.pivovarsky@gmail.com
@bug: no know bug
*/
#ifndef _WIFI_MNGT_H_
#define _WIFI_MNGT_H_
#include <WiFi.h>
#include "Arduino.h"
#include <esp_task_wdt.h>
#include <ESPmDNS.h>
#include <esp_wifi.h>
#include "esp32-hal-cpu.h"
#include "mcu_cfg.h"
#include "var.h"
#include "log.h"
#include "system.h"
#include "camera.h"
#include "sys_led.h"
/* https://github.com/espressif/arduino-esp32/blob/51cb927712e512664a0a0f7b1219fdc18e11b857/libraries/WiFi/src/WiFiGeneric.h#L52 */
/* https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/network/esp_wifi.html#_CPPv429wifi_event_sta_disconnected_t */
void WiFiMngt_WiFiEventScanDone(WiFiEvent_t, WiFiEventInfo_t);
void WiFiMngt_WiFiEventStationStart(WiFiEvent_t, WiFiEventInfo_t);
void WiFiMngt_WiFiEventStationStop(WiFiEvent_t, WiFiEventInfo_t);
void WiFiMngt_WiFiEventStationConnected(WiFiEvent_t, WiFiEventInfo_t);
void WiFiMngt_WiFiEventStationDisconnected(WiFiEvent_t, WiFiEventInfo_t);
void WiFiMngt_WiFiEventGotIP(WiFiEvent_t, WiFiEventInfo_t);
void WiFiMngt_WiFiEventLostIP(WiFiEvent_t, WiFiEventInfo_t);
void WiFiMngt_WiFiEventApStart(WiFiEvent_t , WiFiEventInfo_t);
void WiFiMngt_WiFiEventApStop(WiFiEvent_t , WiFiEventInfo_t);
void WiFiMngt_WiFiEventApStaConnected(WiFiEvent_t , WiFiEventInfo_t);
void WiFiMngt_WiFiEventApStaDisconnected(WiFiEvent_t , WiFiEventInfo_t);
void WiFiMngt_WiFiEventApStaIpAssigned(WiFiEvent_t , WiFiEventInfo_t);
void WiFiMngt_WiFiEventApStaProbeReqRecved(WiFiEvent_t , WiFiEventInfo_t);
class WiFiMngt {
private:
String WifiSsid; ///< WI-FI SSID
String WifiPassword; ///< WI-FI password
bool ServiceMode; ///< flag for enable service AP mode after MCU start during STA_AP_MODE_TIMEOUT ms
String WiFiMode; ///< Wi-Fi mode (AP, STA, STA_AP)
String SericeApSsid; ///< Service AP SSID
bool FirstConnected; ///< flag about first connecting to WiFi network status
bool NtpFirstSync; ///< flag about first NTP sync status
uint8_t WiFiStaNetworkBssid[6]; ///< BSSID of the network
bool WiFiStaMultipleNetwork; ///< flag about multiple STA networks
bool StartStaWdg; ///< flag about start STA watchdog
IPAddress Service_LocalIp; ///< Service IP when si module in the AP mode
IPAddress Service_Gateway; ///< Service gateway when si module in the AP mode
IPAddress Service_Subnet; ///< Service mask when si module in the AP mode
IPAddress Service_Dns; ///< Service DNS when is module in the AP mode
unsigned long TaskAp_previousMillis; ///< previous time for task AP
unsigned long TaskWdg_previousMillis; ///< previous time for task STA watchdog
String mDNS_record; ///< mDNS record
String WifiScanJson; ///< global variable with wifi networks
Configuration *config; ///< pointer to configuration class
Logs *log; ///< pointer to log class
Camera *cam; ///< pointer to camera class
public:
WiFiMngt(Configuration*, Logs*, Camera*);
~WiFiMngt() {};
void LoadCfgFromEeprom();
void Init();
void WifiManagement();
void WiFiReconnect();
void SetWifiEvents();
void WiFiStaConnect();
void SyncNtpTime();
void ScanWiFiNetwork();
uint8_t ScanWifiNetwork(String);
bool CheckAvailableWifiNetwork(String);
int Rssi2Percent(int);
String TranslateTxPower(wifi_power_t);
String TranslateWiFiStatus(wl_status_t);
String TranslateWiFiEncrypion(wifi_auth_mode_t );
void CreateApSsid();
void WiFiWatchdog();
String GetServiceApSsid();
String GetStaSsid();
String GetStaBssid();
String GetStaPassword();
String GetStaStatus();
String GetStaIp();
String GetAvailableWifiNetworks();
String GetWiFiMode();
String GetWifiMac();
String GetMdns();
bool GetkActifeWifiCfgFlag();
bool GetNtpFirstTimeSync();
bool GetFirstConnection();
void SetStaCredentials(String, String);
void SetStaSsid(String);
void SetStaPassword(String);
void ConnectToSta();
void SetMdns(String);
void SetFirstConnection(bool);
};
extern WiFiMngt SystemWifiMngt; ///< global variable for wifi management
#endif
/* EOF */

255
README.md Normal file
View File

@ -0,0 +1,255 @@
# PrusaConnect ESP32-CAM
This repository includes source code and firmware releases for the ESP32-cam module programmed in the Arduino IDE
This project uses other libraries. It is necessary to install them in the arduino IDE.
- App [Arduino IDE 2.3.2](https://www.arduino.cc/en/software)
- MCU support [ESP32 2.0.15](https://github.com/espressif/arduino-esp32)
- Library [ESPAsyncWebSrv 1.2.7](https://github.com/dvarrel/ESPAsyncWebSrv)
- Library [AsyncTCP 1.1.4](https://github.com/dvarrel/AsyncTCP)
- Library [ArduinoJson 7.0.4](https://github.com/bblanchon/ArduinoJson)
- Library [UniqueID 1.3.0](https://github.com/ricaun/ArduinoUniqueID)
What we need for functionality
- ESP32-CAM AI-thinker board with OV2640 camera module [ here ](#esp32)
- Module board version [here](#different_mcu)
- Install the necessary libraries in the Arduino IDE [ here ](#arduino_lib)
- Arduino IDE configuration [ here ](#arduino_cfg)
- How to flash firmware to ESP32-cam and connect to PrusaConnect [ here ](https://help.prusa3d.com/preview/guide/esp32-cam-for-prusa-connect_673528)
- How to flash binnary files to ESP32-cam board from Linux/MAC/Windows [ here ](#flash_fw)
- How to reset configuration to factory settings [here](#factory_cfg)
- Status LED [ here ](#status_led)
- Schematic main board is [here](#schematic)
- Issue with FLASH LED on the main board [here](#led_issue)
- External WiFi antena [here](#ext_wifi)
- Power supply [here](#power_supply)
- Debug logs [here](#logs)
- Serial console configuration [here](#serial_cfg)
<a name="esp32"></a>
## ESP32-CAM AI-thinker board
It's a few dolars board with **ESP32** MCU and Camera. It's neccesary to buy a board with **camera module OV2640**. The board is sold without a programmer by default. It is possible to program it using the FTDI USB to UART converter, or purchase an official programmer for the board. We recommend purchasing a official programmer. It can save a lot of trouble with connecting and programming the board. There are currently [2 different board version](#different_mcu) but only one is compatible with the official programmer.
<img src="doc/esp32-cam.jpg" width=30% height=30%>
In the next picture we can see **ESP32-CAM** board and programator for board.
<img src="doc/esp32_and_prog.jpg" width=30% height=30%>
It's neccesary use a camera version **OV2640**. If using a different camera, then it may be necessary to modify the camera's pinout, or some camera settings may not work correctly. We recommend using a camera module with a viewing angle of 120° or 160°.
These are currently known or tested camera modules:
| Camera chip | FOV | Resolution | Tested | Works | Description |
|-------------|------|------------|--------|-------|------------------------------------------|
| OV2640 | 30° | 2MP | No | N/A | |
| OV2640 | 44° | 2MP | No | N/A | |
| OV2640 | 66° | 2MP | Yes | Yes | Recomended. Standard camera module |
| OV2640 | 120° | 2MP | Yes | Yes | Recomended |
| OV2640 | 160° | 2MP | Yes | Yes | Recomended |
| OV2640 | 200° | 2MP | No | N/A | |
| OV2640 | 222° | 2MP | No | N/A | |
| OV2640IR | 160° | 2MP | Yes | Yes | |
| OV8225N | 66° | 2MP | Yes | Yes | |
| OV3360 | 66° | 3MP | Yes | Yes | |
| OV5640-AF | 72° | 5MP | Yes | Yes | Overheating, slow photo loading |
<a name="different_mcu"></a>
## Different MCU version
There are currently 2 versions of the board, but only one version is possible programming via CH340. The blue rectangle shows the differences between the HW versions.
<img src="doc/cam_versions.jpg" width=50% height=50%>
The red arrow points to a pin that differs between these boards. In version 1, this pin is used for MCU RESET (GND/R). In version 2, this pin serves as ground (GND). Version 1 can be programmed via CH340, whereas version 2 cannot be programmed via CH340. For version 2, we tested programming via FT232RL or CP2102, and the programming process worked successfully.
<a name="arduino_lib"></a>
## Necessary libraries in the Arduino IDE
Software compilation was done in Arduino IDE 2.3.2. To ensure proper functionality, it is necessary to install support for ESP32 boards into Arduino IDE, as well as several other libraries
At the first step we need to install support for **ESP32 board**.
**File** -> **Preferences** -> **Additional boards managers URLs**
```
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
```
then go to **Tools** -> **Board** -> **Boards Manager...** and install module **ESP32** by **Espressif Systems**, version **2.0.15**
Next step is to install the necessary libraries. Go to **Sketch** -> **Include Library** -> **Manage Libraries...** or you can use **Ctrl+Shift+I**. Then you can search for the necessary libraries and install them.
- Library [ESPAsyncWebSrv by dvarrel 1.2.7](https://github.com/dvarrel/ESPAsyncWebSrv)
- Library [AsyncTCP by dvarrel 1.1.4](https://github.com/dvarrel/AsyncTCP)
- Library [ArduinoJson by bblanchon 7.0.4](https://github.com/bblanchon/ArduinoJson)
- Library [UniqueID by Luiz Henrique Cassettari1.3.0](https://github.com/ricaun/ArduinoUniqueID)
<a name="arduino_cfg"></a>
## Arduino IDE configuration
Board configuration in the arduino IDE 2.3.2
- Tools -> Board -> ESP32 Arduino -> AI Thinker ESP32
- Tools -> Flash frequency -> 80MHz
- Tools -> Core Debug Level -> None
- Tools -> Erase all Flash Before Sketch Upload -> Disable **(first flash, new board = enable. otherwise = disable)**
- Tools -> Flash Mode -> DIO
- Tools -> Partition scheme -> Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS)
When flashing the firmware to a new, empty ESP32-CAM device for the first time, it is necessary to use the 'Erase' function.
This can be found under **Tools** -> **Erase all Flash Before Sketch Upload** -> **Enable**.
After the initial firmware upload to the MCU, it's necessary to disable this option. If you do not disable this option, your camera configuration will continue to be erased from the flash memory after uploading new firmware from the Arduino IDE.
<a name="flash_fw"></a>
## How to flash binnary files to ESP32-cam board from Linux/MAC/Windows
To upload the firmware on the MAC or Linux platform, you must use the console. First, ensure you have installed esptool for Python. You can find it on the manufacturer's website, ESPRESSIF, [here](https://docs.espressif.com/projects/esp-at/en/latest/esp32/Get_Started/Downloading_guide.html).
And command for FLASH FW is here, where **/dev/ttya0** is your serial interface for communication with the ESP32-cam board.
```
python3 -m esptool -p /dev/ttya0 -b 460800 --before default_reset --after hard_reset --chip
esp32 write_flash --erase-all --flash_mode dio --flash_size 4MB --flash_freq 80m 0x1000
ESP32_PrusaConnectCam_web.ino.bootloader.bin 0x8000
ESP32_PrusaConnectCam_web.ino.partitions.bin 0x10000 ESP32_PrusaConnectCam_web.ino.bin
```
Here is the partitions table:
| Name | Type | SubType | Offset | Size | Flags |
|---------|---------|---------|----------|----------|-------|
| nvs | data | nvs | 0x9000 | 0x5000 | |
| otadata | data | ota | 0xe000 | 0x2000 | |
| app0 | app | ota_0 | 0x10000 | 0x1E0000 | |
| app1 | app | ota_1 | 0x1F0000 | 0x1E0000 | |
| spiffs | data | spiffs | 0x3D0000 | 0x20000 | |
| coredump| data | coredump| 0x3F0000 | 0x10000 | |
However, for uploading the firmware, it's important to use this configuration of addresses and files:
- address **0x1000** - **ESP32_PrusaConnectCam.ino.bootloader.bin**
- address **0x8000** - **ESP32_PrusaConnectCam.ino.partitions.bin**
- address **0x10000** - **ESP32_PrusaConnectCam.ino.bin**
Here is tool and configuration for [windows platform](https://www.espressif.com/en/support/download/other-tools)
<img src="doc/how to flash chip select.jpg" width=15% height=15%>
It's necessary to erase the FLASH using the **ERASE** button before the first firmware flash.
<img src="doc/how to flash.jpg" width=25% height=25%>
<a name="factory_cfg"></a>
## How to reset configuration to factory settings
To reset the settings to factory defaults, follow these instructions:
<img src="doc/factory_cfg.jpg" width=30% height=30%>
- Connect PIN **IO12** to **ground**.
- **Plug in** the power supply.
- Wait for **10 seconds**.
- After 10 seconds, the **FLASH LED will start flashing**.
- **Disconnect** PIN **IO12** from **ground** (but do't disconnect the power supply).
- After disconnecting **IO12** from **ground**, the **FLASH LED** will **stop flashing**, and the MCU will **automatically rebooted**.
- Now the MCU is in the factory settings.
<a name="status_led"></a>
## Status LED
On the board, there is a status LED that provides a visual indicator of the module's current status
through blinking at defined intervals.
<img src="doc/status_led.jpg" width=25% height=25%>
Upon module activation, the LED illuminates. After processor initialization, the LED exhibits different blinking intervals based on the current mode of the module
- **Just service AP Mode:** The LED blinks every **400 ms**, indicating the module's availability in service AP mode.
- **Connecting to WiFi AP:** While connecting to a WiFi Access Point, the LED blinks at intervals of **800 ms**.
- **Connected to WiFi Network:** Upon successful connection to a WiFi network, the LED blinks at intervals of **4000 ms**, signaling a stable connection.
- **Problematic State:** If an issue or error occurs, the LED accelerates its blinking to every **100 ms**.
<a name="schematic"></a>
## Schematic for ESP32-cam board
<img src="doc/ESP32-CAM-AI-Thinker-schematic-diagram.png" width=70% height=70%>
Parts description
<img src="doc/esp32-cam_parts.jpg" width=50% height=50%>
Pinout
<img src="doc/pinout.png" width=50% height=50%>
<a name="led_issue"></a>
## FLASH LED issue
The board has a problem with the FLASH LED, as it lacks any current limitation for the LED. Consequently, frequent use of the FLASH LED can lead to corruption, due to excessive current flow.
One simple solution is to connect an external LED via a relay, transistor, or MOSFET to the board, as shown in the next picture. Using a relay is not ideal, but it provides a simple solution.
<img src="doc/relay_flash_bb.png" width=40% height=40%>
Another solution is to use a LED COB or a USB LED lamp. I utilized a board from a simple USB LED lamp. The transistor has a current limitation of 500mA, and my USB lamp has a current consumption of 180mA. The original LED has a current consumption of 60-80mA, and the USB lamp has a current consumption of approximately 180mA. After calculation, the total current consumption is approximately 260mA, which falls within the current limitation of the transistor. Therefore, it is possible to solder the negative wire from the COB LED or the USB LED lamp to the transistor. The positive wire needs to be soldered to +5V.
This is my USB LED lamp
<img src="doc/usb_lamp.jpg" width=40% height=40%>
The next step is to solder the negative wire from the LED lamp to the transistor collector, and the positive wire from the LED lamp to the +5V on the board.
<img src="doc/esp32-cam_with_led_lamp.jpg" width=40% height=40%>
The third option is to solder a resistor between the collector of the transistor and the PCB. I used a 10-ohm resistor in a 0603 package. This option is more complicated for users with limited soldering experience.
<img src="doc/esp32-cam_flash_led_resistor.jpg" width=40% height=40%>
<a name="ext_wifi"></a>
## External/internal WiFi antenna
The standard ESP32-CAM board utilizes an internal antenna on the PCB. However, this antenna can sometimes cause issues with the quality of the WiFi signal, leading to slow photo uploads to PrusaConnect or connectivity problems. Fortunately, there is an option to connect an external antenna. This requires changing the resistor position, as shown in the picture below. Then, you can use a 2.4GHz Wi-Fi cable with a U.FL to RP-SMA connector and a standard 2.4GHz WiFi antenna
<img src="doc/esp32-cam_ext_ant.png" width=40% height=40%>
<a name="power_supply"></a>
## Power Supply
The device requires a 5V power supply, with a maximum current consumption of 2A. When using the original programmer, power is supplied via a micro USB connector.
<a name="logs"></a>
## Debug logs
It is possible to save debug logs to a microSD card, but the card must be formatted to FAT32. Currently, the maximum tested capacity for a microSD card is 16GB. If a microSD card is inserted into the camera, it is necessary to reboot the camera. When a microSD card is inserted into the camera before boot, logging to the microSD card is automatically enabled. If no microSD card is inserted, the saving of debug logs to the microSD card is automatically disabled. Enabling the saving of debug logs to a microSD card is only possible during camera boot, so it is necessary to restart the camera after inserting the microSD card. Debug logs are saved as plain text in the file Syslog.log
<a name="serial_cfg"></a>
## Serial console configuration
Currently is possible set the basicaly camera configuration during serial console. Baud speed for communication with MCU is **115200 8N1**
Commands for configuration have simple syntax
| command | separator | variable | termination | line terminator |
|--------------|-----------|-----------|-------------|-----------------|
| setwifissid | : | SSID | ; | \n |
Currently available commands are listed in the table below:
| Command | Description |
|-------------------|---------------------------------------------------------------------|
| setwifissid | Setting WiFi SSID, where variable SSID is network name |
| setwifipass | Setting WiFi password, where variable PASSWORD is WiFi password |
| wificonnect | Connecting to WiFi network |
| mcureboot | Rebooting the MCU |
| commandslist | Listing currently supported commands via serial console |
| getwifimode | Print currently WiFi mode. STA/AP/AP+STA |
| getwifistastatus | Print WiFi STA status. Connected/Disconnected/Connecting.... |
| getwifistaip | Print IP address for WiFi STA |
| getserviceapssid | Print service AP SSID name |
| setauthtoken: | Set authentication token for Prusa Connect |
Standard commands sequence for camera basic settings is
- setwifissid:SSID;
- setwifipass:PASSWORD;
- wificonnect;
- setauthtoken:TOKEN;
- mcureboot;

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

BIN
doc/cam_versions.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 KiB

BIN
doc/esp32-cam.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

BIN
doc/esp32-cam_ext_ant.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
doc/esp32-cam_parts.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 KiB

BIN
doc/esp32_and_prog.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

BIN
doc/esp32cam flash.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 KiB

BIN
doc/factory_cfg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 507 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
doc/how to flash.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
doc/pinout.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 KiB

BIN
doc/relay_flash_bb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

BIN
doc/status_led.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 KiB

BIN
doc/usb_lamp.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@ -0,0 +1 @@
<svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><g stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="m2 2 20 20"/><path d="m6.71277 6.7226c-3.04798 2.07267-4.71277 5.2774-4.71277 5.2774s3.63636 7 10 7c2.0503 0 3.8174-.7266 5.2711-1.7116m-6.2711-12.23018c.3254-.03809.6588-.05822 1-.05822 6.3636 0 10 7 10 7s-.6918 1.3317-2 2.8335"/><path d="m14 14.2362c-.5308.475-1.2316.7639-2 .7639-1.6569 0-3-1.3431-3-3 0-.8237.33193-1.5698.86932-2.11192"/></g></svg>

After

Width:  |  Height:  |  Size: 537 B

View File

@ -0,0 +1 @@
<svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h24v24h-24z" fill="#fff" opacity="0"/><g fill="#231f20"><path d="m21.87 11.5c-.64-1.11-4.16-6.68-10.14-6.5-5.53.14-8.73 5-9.6 6.5a1 1 0 0 0 0 1c.63 1.09 4 6.5 9.89 6.5h.25c5.53-.14 8.74-5 9.6-6.5a1 1 0 0 0 0-1zm-9.65 5.5c-4.31.1-7.12-3.59-8-5 1-1.61 3.61-4.9 7.61-5 4.29-.11 7.11 3.59 8 5-1.03 1.61-3.61 4.9-7.61 5z"/><path d="m12 8.5a3.5 3.5 0 1 0 3.5 3.5 3.5 3.5 0 0 0 -3.5-3.5zm0 5a1.5 1.5 0 1 1 1.5-1.5 1.5 1.5 0 0 1 -1.5 1.5z"/></g></svg>

After

Width:  |  Height:  |  Size: 551 B

View File

@ -0,0 +1,7 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="48" height="48">
<path d="M0 0 C15.84 0 31.68 0 48 0 C48 15.84 48 31.68 48 48 C32.16 48 16.32 48 0 48 C0 32.16 0 16.32 0 0 Z " fill="#F76833" transform="translate(0,0)"/>
<path d="M0 0 C3.67563264 1.74425476 5.45963496 2.73632744 7.7734375 6.20703125 C7.7734375 7.52703125 7.7734375 8.84703125 7.7734375 10.20703125 C6.4946875 10.26890625 5.2159375 10.33078125 3.8984375 10.39453125 C3.17914063 10.42933594 2.45984375 10.46414062 1.71875 10.5 C1.07679687 10.40332031 0.43484375 10.30664062 -0.2265625 10.20703125 C-0.8865625 9.21703125 -1.5465625 8.22703125 -2.2265625 7.20703125 C-5.68406199 6.54026247 -5.68406199 6.54026247 -9.2265625 7.20703125 C-11.4681567 9.07695708 -11.4681567 9.07695708 -12.2265625 12.20703125 C-12.25312485 15.28826398 -12.00651643 18.22209479 -11.2265625 21.20703125 C-9.34280575 23.5318691 -9.34280575 23.5318691 -6.2265625 23.39453125 C-3.30269647 23.40782734 -3.30269647 23.40782734 -1.2265625 22.20703125 C-0.09305716 20.19033298 -0.09305716 20.19033298 0.7734375 18.20703125 C3.4134375 18.20703125 6.0534375 18.20703125 8.7734375 18.20703125 C7.56839145 24.3527661 5.92972468 26.62919933 0.7734375 30.20703125 C-2.95708817 31.45053981 -6.35208293 31.72901996 -10.2265625 31.20703125 C-15.04078362 28.79992069 -17.98960736 26.11112522 -20.2265625 21.20703125 C-21.00954472 15.9691502 -21.52058755 10.69489877 -19.015625 5.87890625 C-13.9891341 -0.19370712 -7.70306169 -2.74337109 0 0 Z " fill="#FBF8F7" transform="translate(30.2265625,8.79296875)"/>
<path d="M0 0 C0.66 0 1.32 0 2 0 C1.90276381 4.08391981 1.00447569 6.50332575 -1.8125 9.375 C-6.64944192 13.26074413 -12.10863795 12.52238678 -18 12 C-18 11.67 -18 11.34 -18 11 C-16.89591797 10.85304688 -16.89591797 10.85304688 -15.76953125 10.703125 C-8.0584186 9.61422261 -8.0584186 9.61422261 -2 5 C-0.66701026 2.38181544 -0.66701026 2.38181544 0 0 Z " fill="#4A3C36" transform="translate(38,29)"/>
<path d="M0 0 C1.60875 0.185625 1.60875 0.185625 3.25 0.375 C3.58 1.035 3.91 1.695 4.25 2.375 C3.13625 2.478125 2.0225 2.58125 0.875 2.6875 C-0.32125 2.914375 -1.5175 3.14125 -2.75 3.375 C-4.68395001 6.27592502 -5.05131941 7.27174737 -5.375 10.5625 C-5.49875 11.820625 -5.6225 13.07875 -5.75 14.375 C-7.75 12.375 -7.75 12.375 -8.125 9.625 C-7.47406858 3.98359436 -5.89878557 0.58987856 0 0 Z " fill="#5B4A44" transform="translate(24.75,14.625)"/>
<path d="M0 0 C0.66 0 1.32 0 2 0 C1.38461538 5.53846154 1.38461538 5.53846154 -0.5625 7.875 C-1.2740625 8.431875 -1.2740625 8.431875 -2 9 C-2.99 8.67 -3.98 8.34 -5 8 C-4.360625 7.236875 -3.72125 6.47375 -3.0625 5.6875 C-0.85767219 3.11753287 -0.85767219 3.11753287 0 0 Z " fill="#4F4541" transform="translate(38,29)"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#797979" d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>

After

Width:  |  Height:  |  Size: 829 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#797979" d="M10.741 17h-1.991c0-.17-.016-.338-.035-.506l1.703-1.548c.197.653.323 1.332.323 2.054zm-.04 6.659c.19.216.465.341.753.341h1.093c.288 0 .562-.125.752-.341l1.451-1.659h-5.5l1.451 1.659zm3.799-3.659h-5c-.276 0-.5.224-.5.5s.224.5.5.5h5c.276 0 .5-.224.5-.5s-.224-.5-.5-.5zm0-2h-5c-.276 0-.5.224-.5.5s.224.5.5.5h5c.276 0 .5-.224.5-.5s-.224-.5-.5-.5zm1.707-8.315c-1.104 2.28-2.948 4.483-2.949 7.315h1.992c0-3.169 3.479-5.906 3.726-9.832l-2.769 2.517zm6.793-8.201l-20.654 18.75-1.346-1.5 6.333-5.728c-1.062-1.873-2.333-3.843-2.333-6.272 0-4.343 3.498-6.734 6.996-6.734 2.408 0 4.798 1.146 6.064 3.267l3.598-3.267 1.342 1.484zm-14.147 10.142l7.676-6.969c-.833-1.742-2.682-2.657-4.533-2.657-2.483 0-4.996 1.626-4.996 4.734 0 1.713.907 3.246 1.853 4.892z"/></svg>

After

Width:  |  Height:  |  Size: 858 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#797979" d="M22 8.51v1.372h-2.538c.02-.223.038-.448.038-.681 0-.237-.017-.464-.035-.69h2.535zm-10.648-6.553v-1.957h1.371v1.964c-.242-.022-.484-.035-.726-.035-.215 0-.43.01-.645.028zm5.521 1.544l1.57-1.743 1.019.918-1.603 1.777c-.25-.297-.593-.672-.986-.952zm-10.738.952l-1.603-1.777 1.019-.918 1.57 1.743c-.392.28-.736.655-.986.952zm-1.597 5.429h-2.538v-1.372h2.535c-.018.226-.035.454-.035.691 0 .233.018.458.038.681zm9.462 9.118h-4c-.276 0-.5.224-.5.5s.224.5.5.5h4c.276 0 .5-.224.5-.5s-.224-.5-.5-.5zm0 2h-4c-.276 0-.5.224-.5.5s.224.5.5.5h4c.276 0 .5-.224.5-.5s-.224-.5-.5-.5zm.25 2h-4.5l1.188.782c.154.138.38.218.615.218h.895c.234 0 .461-.08.615-.218l1.187-.782zm3.75-13.799c0 3.569-3.214 5.983-3.214 8.799h-1.989c-.003-1.858.87-3.389 1.721-4.867.761-1.325 1.482-2.577 1.482-3.932 0-2.592-2.075-3.772-4.003-3.772-1.925 0-3.997 1.18-3.997 3.772 0 1.355.721 2.607 1.482 3.932.851 1.478 1.725 3.009 1.72 4.867h-1.988c0-2.816-3.214-5.23-3.214-8.799 0-3.723 2.998-5.772 5.997-5.772 3.001 0 6.003 2.051 6.003 5.772z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#797979" d="M10 18v-12h4v12h-4zm2-18c-.883 0-1.742.102-2.57.283l.349 1.974c.715-.163 1.456-.257 2.221-.257 5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10c0-1.914.551-3.697 1.489-5.217l2.173 2.173 1.353-7.014-7.015 1.35 2.037 2.038c-1.282 1.907-2.037 4.198-2.037 6.67 0 6.627 5.373 12 12 12s12-5.373 12-12-5.373-12-12-12z"/></svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#797979" d="M23 12c0 1.042-.154 2.045-.425 3h-2.101c.335-.94.526-1.947.526-3 0-4.962-4.037-9-9-9-1.706 0-3.296.484-4.655 1.314l1.858 2.686h-6.994l2.152-7 1.849 2.673c1.684-1.049 3.659-1.673 5.79-1.673 6.074 0 11 4.925 11 11zm-6.354 7.692c-1.357.826-2.944 1.308-4.646 1.308-4.962 0-9-4.038-9-9 0-1.053.191-2.06.525-3h-2.1c-.271.955-.425 1.958-.425 3 0 6.075 4.925 11 11 11 2.127 0 4.099-.621 5.78-1.667l1.853 2.667 2.152-6.989h-6.994l1.855 2.681z"/></svg>

After

Width:  |  Height:  |  Size: 549 B

View File

@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="64" height="45" fill-rule="evenodd" clip-rule="evenodd" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="0 0 3.61 2.55">
<path fill="#DADADA" d="M1.55 2.29c0.14,-0.14 0.37,-0.14 0.51,0l-0.26 0.26 -0.25 -0.26z"/>
<path fill="#DADADA" d="M1.03 1.78c0.43,-0.43 1.12,-0.43 1.55,0l-0.26 0.25c-0.29,-0.28 -0.75,-0.28 -1.03,0l-0.26 -0.25z"/>
<path fill="#DADADA" d="M0.52 1.26c0.71,-0.71 1.86,-0.71 2.57,0l-0.26 0.26c-0.57,-0.57 -1.49,-0.57 -2.06,0l-0.25 -0.26z"/>
<path fill="#DADADA" d="M0 0.75c1,-1 2.61,-1 3.61,0l-0.26 0.25c-0.86,-0.85 -2.24,-0.85 -3.09,0l-0.26 -0.25z"/>
</svg>

After

Width:  |  Height:  |  Size: 817 B

View File

@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="64" height="45" fill-rule="evenodd" clip-rule="evenodd" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="0 0 3.61 2.55">
<path fill="#EF5E63" d="M1.55 2.29c0.14,-0.14 0.37,-0.14 0.51,0l-0.26 0.26 -0.25 -0.26z"/>
<path fill="#DADADA" d="M1.03 1.78c0.43,-0.43 1.12,-0.43 1.55,0l-0.26 0.25c-0.29,-0.28 -0.75,-0.28 -1.03,0l-0.26 -0.25z"/>
<path fill="#DADADA" d="M0.52 1.26c0.71,-0.71 1.86,-0.71 2.57,0l-0.26 0.26c-0.57,-0.57 -1.49,-0.57 -2.06,0l-0.25 -0.26z"/>
<path fill="#DADADA" d="M0 0.75c1,-1 2.61,-1 3.61,0l-0.26 0.25c-0.86,-0.85 -2.24,-0.85 -3.09,0l-0.26 -0.25z"/>
</svg>

After

Width:  |  Height:  |  Size: 817 B

View File

@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="64" height="45" fill-rule="evenodd" clip-rule="evenodd" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="0 0 3.61 2.55">
<path fill="#F4E551" d="M1.55 2.29c0.14,-0.14 0.37,-0.14 0.51,0l-0.26 0.26 -0.25 -0.26z"/>
<path fill="#F4E551" d="M1.03 1.78c0.43,-0.43 1.12,-0.43 1.55,0l-0.26 0.25c-0.29,-0.28 -0.75,-0.28 -1.03,0l-0.26 -0.25z"/>
<path fill="#DADADA" d="M0.52 1.26c0.71,-0.71 1.86,-0.71 2.57,0l-0.26 0.26c-0.57,-0.57 -1.49,-0.57 -2.06,0l-0.25 -0.26z"/>
<path fill="#DADADA" d="M0 0.75c1,-1 2.61,-1 3.61,0l-0.26 0.25c-0.86,-0.85 -2.24,-0.85 -3.09,0l-0.26 -0.25z"/>
</svg>

After

Width:  |  Height:  |  Size: 817 B

View File

@ -0,0 +1,2 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="64" height="45" fill-rule="evenodd" clip-rule="evenodd" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="0 0 3.61 2.55"><path fill="#0D8CF7" d="M1.55 2.29c0.14,-0.14 0.37,-0.14 0.51,0l-0.26 0.26 -0.25 -0.26z"/><path fill="#0D8CF7" d="M1.03 1.78c0.43,-0.43 1.12,-0.43 1.55,0l-0.26 0.25c-0.29,-0.28 -0.75,-0.28 -1.03,0l-0.26 -0.25z"/><path fill="#0D8CF7" d="M0.52 1.26c0.71,-0.71 1.86,-0.71 2.57,0l-0.26 0.26c-0.57,-0.57 -1.49,-0.57 -2.06,0l-0.25 -0.26z"/><path fill="#DADADA" d="M0 0.75c1,-1 2.61,-1 3.61,0l-0.26 0.25c-0.86,-0.85 -2.24,-0.85 -3.09,0l-0.26 -0.25z"/></svg>

After

Width:  |  Height:  |  Size: 804 B

View File

@ -0,0 +1,2 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="64" height="45" fill-rule="evenodd" clip-rule="evenodd" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="0 0 3.61 2.55"><path fill="#40CD8A" d="M1.55 2.29c0.14,-0.14 0.37,-0.14 0.51,0l-0.26 0.26 -0.25 -0.26z"/><path fill="#40CD8A" d="M1.03 1.78c0.43,-0.43 1.12,-0.43 1.55,0l-0.26 0.25c-0.29,-0.28 -0.75,-0.28 -1.03,0l-0.26 -0.25z"/><path fill="#40CD8A" d="M0.52 1.26c0.71,-0.71 1.86,-0.71 2.57,0l-0.26 0.26c-0.57,-0.57 -1.49,-0.57 -2.06,0l-0.25 -0.26z"/><path fill="#40CD8A" d="M0 0.75c1,-1 2.61,-1 3.61,0l-0.26 0.25c-0.86,-0.85 -2.24,-0.85 -3.09,0l-0.26 -0.25z"/></svg>

After

Width:  |  Height:  |  Size: 804 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill-rule="evenodd" clip-rule="evenodd" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="0 0 235.29 235.29"><path fill="#797979" d="M23.4 105.15c24.06,-25.64 57.46,-41.42 94.27,-41.42 36.81,0 70.08,15.78 94.27,41.42l23.4 -24.85c-30.11,-31.95 -71.66,-51.67 -117.67,-51.67 -46.02,0 -87.56,19.72 -117.67,51.67l23.4 24.85z"></path><path fill="#797979" d="M63.37 147.62c13.93,-14.73 33.13,-23.8 54.3,-23.8 21.17,0 40.36,9.07 54.3,23.8l22.88 -24.19c-19.72,-20.91 -46.94,-33.92 -77.04,-33.92 -30.11,0 -57.33,12.89 -77.04,33.92l22.88 24.19 0 0 -0.26 0z"></path><path fill="#797979" d="M80.46 165.76c9.47,-10.12 22.62,-16.3 37.08,-16.3 14.46,0 27.61,6.18 37.07,16.3l-37.07 39.44 -37.08 -39.44z"></path></svg>

After

Width:  |  Height:  |  Size: 829 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="202" height="35" style="shape-rendering:geometricPrecision;text-rendering:geometricPrecision;image-rendering:optimizeQuality;fill-rule:evenodd;clip-rule:evenodd" viewBox="0 0 1250 216.67"><defs><style>.fil0{fill:#2b2a29}.fil1{fill:#fefefe;fill-rule:nonzero}</style></defs><g id="Layer_x0020_1"><path d="M0 63.3h502.81v153.37H0z" class="fil0"/><path d="M50.51 111.62h16.84c7.68 0 13.48 3.51 13.48 12.67 0 8.75-5.53 11.99-13.48 11.99H50.51v-24.66zm-25.05 76.12h25.05v-32.06h20.62c21.15 0 34.76-9.3 34.76-33.14 0-22.77-16.84-30.99-34.76-30.99H25.46v96.19zm113.1-76.12h19.94c7 0 12.66 3.24 12.66 10.38 0 7.41-4.04 11.31-11.58 11.31h-21.02v-21.69zm-25.06 76.12h25.06v-35.03h19c11.85 0 13.33 9.97 14.01 19.67.4 2.83.67 12 2.83 15.36h24.79c-3.37-3.77-3.64-17.51-4.05-21.69-.67-9.16-3.9-20.48-14.01-23.04v-.27c10.24-3.9 15.09-13.74 15.09-24.11 0-19.4-16.84-27.08-33.68-27.08H113.5v96.19zm176.15-96.19H264.6v57.93c0 12.4-4.45 18.73-16.85 18.73-12.39 0-16.84-6.33-16.84-18.73V91.55h-25.06V148c0 27.89 12.8 41.76 41.9 41.76 29.11 0 41.9-13.87 41.9-41.76V91.55zm6.94 63.86c0 24.65 19.81 34.35 41.77 34.35 21.15 0 41.9-7.81 41.9-32.19 0-17.38-14.01-23.85-27.89-27.76-14.01-3.91-27.89-5.12-27.89-12.66 0-6.34 6.74-8.22 12.13-8.22 7.54 0 15.89 2.96 15.49 11.59h25.06c0-21.56-19.54-30.99-38.53-30.99-18.06 0-39.21 8.22-39.21 29.23 0 17.79 14.55 23.85 28.16 27.76 13.88 3.9 27.62 5.39 27.62 14.01 0 7.14-7.82 9.83-15.36 9.83-10.78 0-17.65-3.63-18.19-14.95h-25.06zm81.31 32.33h25.73l6.06-17.24h33.55l5.93 17.24h26.14l-35.98-96.19h-25.46l-35.97 96.19zm48.5-69.52h.27l10.64 33.69h-21.82l10.91-33.69z" class="fil1"/><path d="M523.44 63.3h715.63v153.37H523.44z" class="fil0"/><path d="M643.75 125.9c-.67-23.17-23.04-36.37-43.92-36.37-29.24 0-47.96 22.23-47.96 50.12 0 27.88 18.72 50.11 47.96 50.11 24.39 0 42.44-14.41 43.92-39.34h-25.06c-.94 11.19-7.54 17.79-19.4 17.79-16.16 0-22.36-14.15-22.36-28.56 0-14.42 6.2-28.57 22.36-28.57 10.65 0 17.92 5.12 19.4 14.82h25.06zm30.52 13.75c0-14.42 6.19-28.57 22.36-28.57 16.17 0 22.37 14.15 22.37 28.57 0 14.41-6.2 28.56-22.37 28.56s-22.36-14.15-22.36-28.56zm-25.06 0c0 27.88 17.24 50.11 47.42 50.11 30.18 0 47.43-22.23 47.43-50.11 0-27.89-17.25-50.12-47.43-50.12s-47.42 22.23-47.42 50.12zm103.94 48.09h23.58v-59.95h.27l33.41 59.95h25.73V91.55h-23.58v59.28h-.27l-33.41-59.28h-25.73v96.19zm95.59 0h23.57v-59.95h.27L906 187.74h25.73V91.55h-23.58v59.28h-.27l-33.41-59.28h-25.73v96.19zm95.85 0h78.01v-21.55h-52.95v-18.33h47.16v-19.4h-47.16v-16.84h51.6V91.55h-76.66v96.19zm176.97-61.84c-.67-23.17-23.04-36.37-43.92-36.37-29.24 0-47.96 22.23-47.96 50.12 0 27.88 18.72 50.11 47.96 50.11 24.38 0 42.44-14.41 43.92-39.34h-25.06c-.94 11.19-7.54 17.79-19.4 17.79-16.17 0-22.36-14.15-22.36-28.56 0-14.42 6.19-28.57 22.36-28.57 10.64 0 17.92 5.12 19.4 14.82h25.06zm85.35-34.35h-82.45v21.55h28.69v74.64h25.06V113.1h28.7V91.55z" class="fil1"/><path d="M811.83 0H1250v79.86H811.83z" style="fill:#fa6e36"/><path d="M828.58 66.11h41.35V54.68h-28.06v-9.71h24.99V34.68h-24.99v-8.93h27.35V15.11h-40.64v51zm43.75-17.14c0 13.06 10.49 18.21 22.14 18.21 11.21 0 22.21-4.15 22.21-17.07 0-9.22-7.43-12.64-14.79-14.71-7.42-2.08-14.78-2.72-14.78-6.72 0-3.35 3.57-4.35 6.43-4.35 4 0 8.43 1.57 8.21 6.14h13.28c0-11.43-10.35-16.43-20.42-16.43-9.57 0-20.78 4.36-20.78 15.5 0 9.43 7.71 12.64 14.92 14.71 7.36 2.07 14.64 2.86 14.64 7.43 0 3.78-4.14 5.21-8.14 5.21-5.71 0-9.35-1.93-9.64-7.92h-13.28zm61.59-23.22h8.93c4.07 0 7.14 1.86 7.14 6.72 0 4.64-2.93 6.35-7.14 6.35h-8.93V25.75zm-13.28 40.36h13.28v-17h10.93c11.21 0 18.43-4.93 18.43-17.57 0-12.07-8.93-16.43-18.43-16.43h-24.21v51zm60.45-22c1.08-.07 2.15-.14 3.22-.14 4.86 0 8.85 1.28 8.85 6.78 0 4.29-3.92 6.14-8.07 6.14-6.07 0-9.07-2.93-8.92-8.85h-11.36c-.78 12.78 8.79 18.71 20.36 18.71 9.92 0 20.13-4.79 20.13-16 0-5.93-2.78-9.93-8.49-11.43v-.14c4.57-.78 6.92-5.36 6.92-9.71 0-10.14-9.92-13.79-18.56-13.79-10.57 0-18.79 5.79-19 16.93h11.35c-.07-4.57 2.79-7.07 7.72-7.07 4 0 7.14 1.78 7.14 5 0 4.28-4.43 5.21-8.07 5.21-1.07.07-2.14-.14-3.22-.07v8.43zm66.6 22V55.82h-23.64c3.29-3.14 5.14-4.43 9-6.71 7.29-4.57 14.14-8 14.14-17.64 0-10.64-8.78-15.79-18.64-15.79-12.35 0-20.35 8.36-19.57 20.72h11.36c0-5.43 1.85-10.07 8.28-10.07 3.93 0 6.43 2.28 6.43 6.21 0 3.35-2.71 5.78-6.5 8.28-3.93 2.5-8.71 5-13 8.72-4.92 4.28-7.71 10.21-7.78 16.57h39.92zm76.99-32.79c-.36-12.28-12.21-19.28-23.28-19.28-15.5 0-25.43 11.79-25.43 26.57 0 14.78 9.93 26.57 25.43 26.57 12.92 0 22.49-7.65 23.28-20.86h-13.28c-.5 5.93-4 9.43-10.29 9.43-8.57 0-11.85-7.5-11.85-15.14 0-7.64 3.28-15.14 11.85-15.14 5.64 0 9.5 2.71 10.29 7.85h13.28zm-.39 32.79h13.64l3.21-9.15h17.78l3.15 9.15h13.85l-19.07-51h-13.5l-19.06 51zM1150 29.25h.14l5.64 17.86h-11.57l5.79-17.86zm27.6 36.86h12.5V27.4h.14l10.71 38.71h10.14l10.72-38.71h.14v38.71h12.5v-51h-19.57l-8.79 34h-.14l-8.78-34h-19.57v51z" class="fil1"/></g></svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

7
webpage/cookies.html Normal file
View File

@ -0,0 +1,7 @@
<style>@import url("styles.css");</style>
<script src="jquery-3.7.0.min.js"></script>
<center><p>To read the Cookie policy, please visit <a href="https://www.prusa3d.com/en/page/privacy-policy_231258/" id="cookies-link">this page</a>.</p></center>
<script src="scripts.js"></script>
<script>
addClickListener('cookies-link');
</script>

7
webpage/gtac.html Normal file
View File

@ -0,0 +1,7 @@
<style>@import url("styles.css");</style>
<script src="jquery-3.7.0.min.js"></script>
<center><p>To read the General Terms and Conditions, please visit <a href="https://www.prusa3d.com/page/general-terms-and-conditions_231236/" id="gtac-link">this page</a>.</p></center>
<script src="scripts.js"></script>
<script>
addClickListener('gtac-link');
</script>

90
webpage/index.html Normal file
View File

@ -0,0 +1,90 @@
<!DOCTYPE HTML><html lang="en">
<head>
<meta name="Prusa ESP32-cam" content="width=device-width, initial-scale=1">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<title>Prusa ESP32-cam</title>
<link rel="stylesheet" type="text/css" href="styles.css">
<script src="jquery-3.7.0.min.js"></script>
<script>
$(document).ready(function () {
$("a").click(function (e) {
e.preventDefault();
var page = $(this).attr("href");
$("#content").load(page);
setActive(this);
});
$("#content").load("page_config.html", function() {
var defaultLink = document.querySelector('a[href="page_config.html"]');
setActive(defaultLink);
});
});
</script>
</head>
<body>
<nav>
<img src="esp32_cam.svg" id=logo alt="Logo image" style="margin-left: 10px;" />
<ul class="top_bar" style="justify-content:flex-start;">
<li><a class=p5 href="https://forum.prusa3d.com/forum/prusa-connect-prusalink" id="forum-link">Forum</a></li>
<li><a class=p5 href="https://help.prusa3d.com" id="help-link">Help</a></li>
</ul>
<ul class="top_bar" style="justify-content:flex-end;">
<li><a class=p4 href="#" onclick="javascript:actionButton('/action_led',true,'')"><div id="light-icon"></div> Light </a></li>
<li><a class=p4 href="#" onclick="location.reload();"><svg height="25" width="25"><image href="refresh-icon.svg"></svg> Refresh </a></li>
<li><a class=p4 href="#" onclick="javascript:actionButton('/action_reboot',false,'Reboot process started, wait several seconds for mcu to boot up. You can close this window now.')"><svg height="25" width="25"><image href="reboot-icon.svg"></svg> Reboot </a></li>
</ul>
</nav>
<hr>
<section class="container">
<div class="container_left-half">
<article>
<img src="saved-photo.jpg" id="photo" width="60%" onclick="openImage()"/>
</article>
</div>
<div class="container_right-half">
<article>
<p class="p1">Trigger interval: <b><span id="refreshInterval"></span><b>s</p><br><br>
<button class="btn" onclick="actionButton('/action_capture',true,'')">Refresh Snapshot</button> <br><br>
<button class="btn" onclick="actionButton('/action_send',false,'')">Send snapshot</button><br><br><br>
<button class="btn" onclick="window.open('saved-photo.jpg')">Download snapshot</button><br><br>
</article>
</div>
</section>
<br>
<cfg>
<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">Autentification</a></li>
<li ><a href="page_system.html">System</a></li>
</div>
</cfg_bar>
</cfg>
<hr>
<div id="content">
</div>
<br><br><br><br>
<table id=botton><tr>
<td><p class=p2>Prusa Connect ESP32 cam</p> </td>
<td><p class=p3>Author</p> <p class=p2>Miroslav Pivovarsky</p> </td>
<td><a href="license.html" class=p3>Licence</a> | <a href="gtac.html" class=p3>General Terms and Conditions</a> | <a href="privacypolicy.html" class=p3>Privacy Policy</a> | <a href="cookie.html" class=p3>Cookie Preferences</a> </td>
<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();
get_data();
addClickListener('github-link');
addClickListener('help-link');
addClickListener('forum-link');
</script>
</html>

8
webpage/license.html Normal file
View File

@ -0,0 +1,8 @@
<style>@import url("styles.css");</style>
<script src="jquery-3.7.0.min.js"></script>
<center><p>The software for device falls under the <a href="https://www.gnu.org/licenses/gpl-3.0.html" id="license-link">GPL-3.0 license terms</a>. To read the license terms please visit <a href="https://www.gnu.org/licenses/gpl-3.0.html" id="license-link-sec">this page</a>.</p></center>
<script src="scripts.js"></script>
<script>
addClickListener('license-link');
addClickListener('license-link-sec');
</script>

23
webpage/page_auth.html Normal file
View File

@ -0,0 +1,23 @@
<style>@import url("styles.css");</style>
<script src="jquery-3.7.0.min.js"></script>
<body>
<center>
<table>
<tr><td class=pa3>Set web authentication</td><td></td></tr>
<tr><td class=pa1 align="right">WEB authentication </td><td><label class="switch"><input type="checkbox" name="basicauth_enable" id="authid" onchange="changeValue(this.checked, 'basicauth_cfg?basicauth_enable=', 'auth')"><span class="checkbox_slider round"></span></label></label> <span class=pa1 id="status_auth"></span></td></tr>
<tr><td class=pa1 align="right">Username </td><td><input type="text" name="auth_username" id="auth_username"></td></tr>
<tr><td class=pa1 align="right">Password </td><td><input type="password" name="auth_password" id="auth_password"><span class="toggle-password" onclick="togglePasswordVisibility()"><img id="eye-icon" src="eye.svg" alt="Show Password"></span></td></tr>
<tr><td class=pa1 align="right">Confirm Password </td><td><input type="password" name="auth_password_confirm" id="auth_password_confirm"><span class="toggle-password" onclick="togglePasswordVisibility()"><img id="eye-icon-confirm" src="eye.svg" alt="Show Password"></span><span id="pass_match"></span></td></tr>
<tr><td></td><td><button class="btn_save_a" onclick="setAuth( document.getElementById('auth_username').value, document.getElementById('auth_password').value)">Save</button></td></tr>
</table>
</center>
</body>
<script src="scripts.js"></script>
<script>
get_data("auth");
document.getElementById("auth_password").addEventListener("input", validatePasswords);
document.getElementById("auth_password_confirm").addEventListener("input", validatePasswords);
validatePasswords();
</script>

79
webpage/page_config.html Normal file
View File

@ -0,0 +1,79 @@
<style>@import url("styles.css");</style>
<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="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>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr><td class="pc1">Image quality</td><td class="pc2">Low <input type="range" class="slider" name="photo_quality" id=photo_qualityid min="10" max="63" step="1" onchange="changeValue(this.value, 'set_int?photo_quality=', 'config')"> High</td></tr>
<tr>
<td class="pc1">Resolution</td><td><label for="framesize"></label>
<select class="select" id="framesizeid" name="framesize" onchange="changeValue(this.value, 'set_int?framesize=', 'config')">
<option value="0">320x240</option>
<option value="1">352x288</option>
<option value="2">640x480</option>
<option value="3">800x600</option>
<option value="4">1024x768</option>
<option value="5">1280x1024</option>
<option value="6">1600x1200</option>
</select>
</td>
</tr>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr><td class="pc1">Brightness</td><td class="pc2">Low <input type="range" class="slider" name="brightness" id=brightnessid min="-2" max="2" step="1" onchange="changeValue(this.value, 'set_int?brightness=', 'config')">High</td></tr>
<tr><td class="pc1">Contrast</td><td class="pc2">Low <input type="range" class="slider" name="contrast" id=contrastid min="-2" max="2" step="1" onchange="changeValue(this.value, 'set_int?contrast=', 'config')"> High</td></tr>
<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">Horizontal mirror</td><td class="pc2"><label class="switch"><input type="checkbox" name="hmirror" id=hmirrorid onchange="changeValue(this.checked, 'set_bool?hmirror=', 'config')"><span class="checkbox_slider round"></span></label> <span id="status_hmirror"></span></td></tr>
<tr><td class="pc1">Vertical flip</td><td class="pc2"><label class="switch"><input type="checkbox" name="vflip" id=vflipid onchange="changeValue(this.checked, 'set_bool?vflip=', 'config')"><span class="checkbox_slider round"></span></label> <span id="status_vflip"></span></td></tr>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr><td class="pc1">LED light</td><td class="pc2"><label class="switch"><input type="checkbox" name="led" id=ledid onchange="changeValue(this.checked, 'action_led?/led=', 'config')"><span class="checkbox_slider round"></span></label> <span id="status_led"></span></td></tr>
<tr><td class="pc1">LED flash</td><td class="pc2"><label class="switch"><input type="checkbox" name="flash" id=flashid onchange="changeValue(this.checked, 'set_bool?flash=', 'config')"><span class="checkbox_slider round"></span></label> <span id="status_flash"></span></td></tr>
<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>
</table></center>
<center><button class="btn_collapsible">Advanced image 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="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>
<td class="pc1">Automatic white balancing mode</td><td><label for="wb_mode"></label>
<select class="select" id="wb_modeid" name="wb_mode" onchange="changeValue(this.value, 'set_int?wb_mode=', 'config')">
<option value="0">Auto</option>
<option value="1">Sunny</option>
<option value="2">Office</option>
<option value="3">Home</option>
</select>
</td>
</tr>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr><td class="pc1">Automatic Exposure Control</td><td class="pc2"><label class="switch"><input type="checkbox" name="exposure_ctrl" id=exposure_ctrlid onchange="changeValue(this.checked, 'set_bool?exposure_ctrl=', 'config')"><span class="checkbox_slider round"></span></label> <span id="status_exposure_ctrl"></span></td></tr>
<tr><td class="pc1">Second Level Automatic Exposure Control</td><td class="pc2"><label class="switch"><input type="checkbox" name="aec2" id=aec2id onchange="changeValue(this.checked, 'set_bool?aec2=', 'config')"><span class="checkbox_slider round"></span></label> <span id="status_aec2"></span></td></tr>
<tr><td class="pc1">Automatic exposure level</td><td class="pc2">Low <input type="range" class="slider" name="ae_level" id=ae_levelid min="-2" max="2" step="1" onchange="changeValue(this.value, 'set_int?ae_level=', 'config')"> High</td></tr>
<tr><td class="pc1">Automatic exposure time</td><td class="pc2">Low <input type="range" class="slider" name="aec_value" id=aec_valueid min="0" max="1200" step="10" onchange="changeValue(this.value, 'set_int?aec_value=', 'config')">High</td></tr>
<tr><td class="pc1">Automatic exposure time</td><td class="pc2"><span id="aec_value_value"></span> ms</td></tr>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr><td class="pc1">Automatic gain control</td><td class="pc2"><label class="switch"><input type="checkbox" name="gain_ctrl" id=gain_ctrlid onchange="changeValue(this.checked, 'set_bool?gain_ctrl=', 'config')"><span class="checkbox_slider round"></span></label> <span id="status_gain_ctrl"></span></td></tr>
<tr><td class="pc1">Automatic gain control level</td><td class="pc2">Low <input type="range" class="slider" name="agc_gain" id=agc_gainid min="0" max="30" step="1" onchange="changeValue(this.value, 'set_int?agc_gain=', 'config')">High</td></tr>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr><td class="pc1">Bad pixel correction</td><td class="pc2"><label class="switch"><input type="checkbox" name="bpc" id=bpcid onchange="changeValue(this.checked, 'set_bool?bpc=', 'config')"><span class="checkbox_slider round"></span></label> <span id="status_bpc"></span></td></tr>
<tr><td class="pc1">White pixel correction</td><td class="pc2"><label class="switch"><input type="checkbox" name="wpc" id=wpcid onchange="changeValue(this.checked, 'set_bool?wpc=', 'config')"><span class="checkbox_slider round"></span></label> <span id="status_wpc"></span></td></tr>
<tr><td class="pc1">Raw gamma correction</td><td class="pc2"><label class="switch"><input type="checkbox" name="raw_gama" id=raw_gamaid onchange="changeValue(this.checked, 'set_bool?raw_gama=', 'config')"><span class="checkbox_slider round"></span></label> <span id="status_raw_gama"></span></td></tr>
<tr><td style="height: 1px;"></td><td style="height: 1px;"></td></tr>
<tr><td class="pc1">Lens correction</td><td class="pc2"><label class="switch"><input type="checkbox" name="lenc" id=lencid onchange="changeValue(this.checked, 'set_bool?lenc=', 'config')"><span class="checkbox_slider round"></span></label> <span id="status_lensc"></span></td></tr>
</table></center>
</div>
</body>
<script src="scripts.js"></script>
<script>
get_data("config");
setupCollapsibleButtons();
</script>

48
webpage/page_system.html Normal file
View File

@ -0,0 +1,48 @@
<style>@import url("styles.css");</style>
<script src="jquery-3.7.0.min.js"></script>
<body>
<center>
<table id="data">
<tr><td class="ps3">System status</td><td></td></tr>
<tr><td class="ps1">PrusaConnect Status</td><td class="ps2" id="last_upload_status"></td></tr>
<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">System configuration</td><td></td></tr>
<tr><td class="pc1">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')">
<option value="0">Error</option>
<option value="1">Warning</option>
<option value="2">Info</option>
<option value="3">Verbose</option>
</select>
</td>
</tr>
<tr><td class="pc1">Get logs</td><td ><button class="btn_update" onclick="window.open('get_logs')">Get logs</button></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></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;
var updateInterval = setInterval(updateProgress, 800);
get_data("system");
</script>

58
webpage/page_wifi.html Normal file
View File

@ -0,0 +1,58 @@
<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%">
<p class="w1">Connection status</p>
<p class="w2">Status: <span id="wifi_network_status"></span></p>
<p class="w2">SSID: <span id="ssid"></span></p>
<p class="w2">Signal: <span id="main-wifi-signal" ></span><span id="rssi_percentage"></span>% / <span id="rssi"></span>dBm</p>
<p class="w2">IP Address: <span id="ip"></span></p>
<p class="w2">mDNS: https://<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>
<col style="width: 30%;">
<col style="width: 35%;">
<col style="width: 15%;">
<col style="width: 20%;">
</colgroup>
<tr>
<th>Network name (SSID)</th>
<th>Signal strength (RSSI)</th>
<th>Channel</th>
<th>Encryption</th>
</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>
<tr><td class="w1">Connect to Wi-Fi network</td></tr>
<tr><td class="w2" align="right">Wi-Fi network name (SSID) </td><td><input type="text" name="wifi_ssid" id=wifi_ssid_id ></td></tr>
<tr><td class="w2" align="right">Password </td><td><input type="text" name="wifi_pass" id=wifi_pass_id></td></tr>
<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>
</body>
<script src="scripts.js"></script>
<script>
setTimeout(function(){GetDataAndPrintTableWiFi();}, 500);
get_data("wifi");
</script>

View File

@ -0,0 +1,7 @@
<style>@import url("styles.css");</style>
<script src="jquery-3.7.0.min.js"></script>
<center><p>To read the Privacy Policy, please visit <a href="https://www.prusa3d.com/en/page/privacy-policy_231258/" id="privacy-link">this page</a>.</p></center>
<script src="scripts.js"></script>
<script>
addClickListener('privacy-link');
</script>

448
webpage/scripts.js Normal file
View File

@ -0,0 +1,448 @@
function get_data(val) {
jQuery.ajax({
url: 'json_input',
type: 'GET',
timeout: 5000,
success: function(data) {
console.log("Incommming data: ");
console.log(data);
var obj = JSON.parse(data);
console.log(obj);
if (!document.querySelector('#light-icon img')) {
var img = document.createElement('img');
img.src = (obj.led == "true") ? 'light-on-icon.svg' : 'light-off-icon.svg';
document.getElementById('light-icon').appendChild(img);
}
if (val == "config") {
$("#fingerprint").text(obj.fingerprint);
$("#refreshInterval").text(obj.refreshInterval);
document.getElementById('tokenid').value = obj.token;
document.getElementById('refreshid').value = obj.refreshInterval;
document.getElementById('photo_qualityid').value = obj.photoquality;
document.getElementById('framesizeid').value = obj.framesize;
document.getElementById('brightnessid').value = obj.brightness;
document.getElementById('contrastid').value = obj.contrast;
document.getElementById('saturationid').value = obj.saturation;
document.getElementById('hmirrorid').checked = obj.hmirror;
document.getElementById('vflipid').checked = obj.vflip;
document.getElementById('lencid').checked = obj.lensc;
document.getElementById('exposure_ctrlid').checked = obj.exposure_ctrl;
document.getElementById('awbid').checked = obj.awb;
document.getElementById('awb_gainid').checked = obj.awb_gain;
document.getElementById('wb_modeid').value = obj.wb_mode;
document.getElementById('ledid').checked = obj.led;
document.getElementById('flashid').checked = obj.flash;
document.getElementById('flash_timeid').value = obj.flash_time;
document.getElementById('bpcid').checked = obj.bpc;
document.getElementById('wpcid').checked = obj.wpc;
document.getElementById('raw_gamaid').checked = obj.raw_gama;
document.getElementById('aec2id').checked = obj.aec2;
document.getElementById('ae_levelid').value = obj.ae_level;
document.getElementById('aec_valueid').value = obj.aec_value;
document.getElementById('gain_ctrlid').checked = obj.gain_ctrl;
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;
$("#status_hmirror").text((obj.hmirror == "true") ? "On" : "Off");
$("#status_vflip").text((obj.vflip == "true") ? "On" : "Off");
$("#status_lensc").text((obj.lensc == "true") ? "On" : "Off");
$("#status_exposure_ctrl").text((obj.exposure_ctrl == "true") ? "On" : "Off");
$("#status_awb").text((obj.awb == "true") ? "On" : "Off");
$("#status_awb_gain").text((obj.awb_gain == "true") ? "On" : "Off");
$("#status_led").text((obj.led == "true") ? "On" : "Off");
$("#status_flash").text((obj.flash == "true") ? "On" : "Off");
$("#status_bpc").text((obj.bpc == "true") ? "On" : "Off");
$("#status_wpc").text((obj.wpc == "true") ? "On" : "Off");
$("#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");
sliderCheck();
}
if (val == "auth") {
document.getElementById('authid').checked = obj.auth;
$("#status_auth").text((obj.auth == "true") ? "On" : "Off");
document.getElementById('auth_username').value = obj.auth_username;
}
if (val == "wifi") {
$("#ssid").text(obj.ssid);
$("#rssi").text(obj.rssi);
$("#rssi_percentage").text(obj.rssi_percentage);
$("#ip").text(obj.ip);
$("#mdns").text(obj.mdns);
$("#wifi_network_status").text(obj.wifi_network_status);
if (!document.querySelector('#main-wifi-signal wifi_img')) {
var wifi_img = document.createElement('wifi_img');
wifi_img.width = 19;
wifi_img.height = 12;
wifi_img.src = getIconPath(obj.rssi);
document.getElementById('main-wifi-signal').appendChild(wifi_img);
}
}
if (val == "system") {
$("#uptime").text(obj.uptime);
$("#sw_ver").text(obj.sw_ver);
$("#sw_build").text(obj.sw_build);
$("#last_upload_status").text(obj.last_upload_status);
$("#wifi_mode").text(obj.wifi_mode);
$("#sw_new_ver").text(obj.sw_new_ver);
$("#service_ap_ssid").text(obj.service_ap_ssid);
document.getElementById('mdnsid').value = obj.mdns;
document.getElementById('loglevelid').value = obj.log_level;
}
},
error: function(html) {
console.log("json Timeout or error");
//alert("jquery timeout or comunication error");
}
});
}
function sliderCheck() {
var ranges = document.querySelectorAll(".slider");
ranges.forEach(function(range) {
var percent = (range.value - range.min) / (range.max - range.min) * 100;
var gradient = "linear-gradient(to right, #FA6831 " + percent + "%, #d3d3d3 " + percent + "%)";
range.style.background = gradient;
range.oninput = function() {
var percent = (this.value - this.min) / (this.max - this.min) * 100;
var gradient = "linear-gradient(to right, #FA6831 " + percent + "%, #d3d3d3 " + percent + "%)";
this.style.background = gradient;
}
});
}
function getIconPath(rssi) {
let path;
if (rssi == 0) {
path = 'wifi-icon-0.svg';
} else if (rssi <= -70) {
path = 'wifi-icon-1.svg';
} else if (rssi > -70 && rssi <= -60) {
path = 'wifi-icon-2.svg';
} else if (rssi > -60 && rssi <= -50) {
path = 'wifi-icon-3.svg';
} else {
path = 'wifi-icon-4.svg';
}
return path;
}
var OpenImageclickCount = 0;
function openImage() {
var img = document.getElementById("photo");
if (OpenImageclickCount % 2 == 0) {
img.style.position = "fixed";
img.style.top = "5%";
img.style.left = "5%";
img.style.width = "auto";
img.style.height = "auto";
img.style.maxWidth = "100%";
img.style.maxHeight = "90%";
img.style.zIndex = "9999";
} else {
img.style.position = "";
img.style.top = "";
img.style.left = "";
img.style.width = "";
img.style.height = "";
img.style.zIndex = "";
}
OpenImageclickCount++;
}
function actionButton(url, reload, msg) {
var xhr = new XMLHttpRequest();
if (msg != '') {
alert(msg);
}
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
if (reload == true) {
setTimeout(function() {
location.reload();
}, 200);
}
}
};
xhr.open('GET', url, true);
xhr.send();
}
function setActive(link) {
var links = document.querySelectorAll('#links li a');
links.forEach(function(item) {
item.classList.remove('active');
});
link.classList.add('active');
}
var links = document.querySelectorAll('#links li a');
links.forEach(function(link) {
link.addEventListener('click', function() {
setActive(link);
});
});
function addClickListener(id) {
var link = document.getElementById(id);
if (!link.hasOwnProperty('clickListener')) {
link.addEventListener('click', function(event) {
event.preventDefault();
window.open(link.href, '_blank');
});
link.clickListener = true;
}
}
/* wifi page */
function setWifi(val_ssid, val_pass) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", "wifi_cfg?wifi_ssid=" + encodeURIComponent(val_ssid) + "&wifi_pass=" + encodeURIComponent(val_pass), false);
xmlHttp.send(null);
alert(xmlHttp.responseText);
get_data("wifi");
}
function scanWifi() {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", "wifi_scan?", false);
xmlHttp.send(null);
alert(xmlHttp.responseText);
get_data("wifi");
setTimeout(function() {
GetDataAndPrintTableWiFi();
}, 8000);
}
function GetDataAndPrintTableWiFi() {
$("#wifi_ntw").find("tr:gt(0)").remove();
$.ajax({
url: 'json_wifi',
type: 'GET',
timeout: 15000,
dataType: 'json',
data: {},
success: function(data) {
for (var i = 0; i < data.length; i++) {
const IconName = "wifi-icon-" + i;
var row = $('<tr><td>' + data[i].ssid + '</td><td><div id="' + IconName + '" style="display: flex; align-items: center;"></div></td><td>' + data[i].channel + '</td><td>' + data[i].encryption + '</td></tr>');
$('#wifi_ntw').append(row);
if (!document.querySelector('#' + IconName + ' img')) {
var img = document.createElement('img');
img.src = getIconPath(data[i].rssi);
document.getElementById(IconName).prepend(img);
document.getElementById(IconName).append(data[i].rssi_percentage);
document.getElementById(IconName).append("% / ");
document.getElementById(IconName).append(data[i].rssi);
document.getElementById(IconName).append("dBm");
}
}
},
error: function(jqXHR, textStatus, errorThrown) {
console.log('Error:' + textStatus + '-' + errorThrown);
}
});
}
/* auth page */
function setAuth(val_name, val_pass) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", "basicauth_cfg?" + "auth_username=" + encodeURIComponent(val_name) + "&auth_password=" + encodeURIComponent(val_pass), false);
xmlHttp.send(null);
alert(xmlHttp.responseText);
get_data("auth");
}
function changeValue(val, url, reload) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", url + val, false);
xmlHttp.send(null);
if ((url == "set_int?refresh=") || (url == "set_token?token=") || (url == "set_mdns?mdns=")) {
alert(xmlHttp.responseText);
}
if (url == "set_flash_time?flash_time=") {
document.getElementById("flash_time_value").innerText = val;
}
get_data(reload);
}
function togglePasswordVisibility() {
const passwordInput = document.getElementById("auth_password");
const eyeIcon = document.getElementById("eye-icon");
if (passwordInput.getAttribute("type") === "password") {
passwordInput.setAttribute("type", "text");
passwordInput.classList.add("reveal");
eyeIcon.src = "eye-slash.svg";
eyeIcon.alt = "Hide Password";
} else {
passwordInput.setAttribute("type", "password");
passwordInput.classList.remove("reveal");
eyeIcon.src = "eye.svg";
eyeIcon.alt = "Show Password";
}
}
/* system page */
if (typeof uploadingFirmware === 'undefined') {
var uploadingFirmware = false;
}
if (typeof FileSize === 'undefined') {
var FileSize = 0;
}
function uploadFile() {
alert("Started updating...");
const firmwareInput = document.getElementById('firmwareInput');
const statusDiv = document.getElementById('status');
const file = firmwareInput.files[0];
FileSize = file.size;
SetFirmwareSize(file.size);
if (file) {
statusDiv.innerText = 'Updating...';
uploadingFirmware = true;
const formData = new FormData();
formData.append('firmware', file);
fetch('/upload', {
method: 'POST',
body: formData,
})
.then((response) => {
if (response.ok) {
response.text().then((data) => {
const jsonData = JSON.parse(data);
updateProgress();
uploadingFirmware = false;
if (jsonData.errorMessage) {
alert(`Error message: ${jsonData.errorMessage}`);
}
});
} else {
uploadingFirmware = false;
response.text().then((errorMessage) => {
alert(`Error message: ${errorMessage}`);
});
}
})
.catch((error) => {
console.error('Error:', error);
uploadingFirmware = false;
});
} else {
statusDiv.innerText = 'No file selected';
}
}
function SetFirmwareSize(val) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", "set_firmware_size?size=" + val, false);
xmlHttp.send(null);
}
function updateProgress() {
if (!uploadingFirmware) {
return;
}
fetch('/UpdateProcessing', {
method: 'GET',
})
.then((response) => {
if (response.ok) {
return response.json();
} else {
throw new Error('Failed to fetch progress');
}
})
.then((data) => {
const statusDiv = document.getElementById('status');
var progressBar = document.getElementById("myProgressBar");
progressBar.style.width = data.processed_percent + "%";
progressBar.innerHTML = data.processed_percent + "%";
statusDiv.innerText = data.message;
uploadingFirmware = data.updating;
if (data.updating == false && !updateCompleted) {
alert('Operation done. Please reboot MCU.');
uploadingFirmware = false;
updateCompleted = true;
clearInterval(updateInterval);
}
})
.catch((error) => {
console.error('Error:', error);
var progressBar = document.getElementById("myProgressBar");
progressBar.innerHTML = "Error";
clearInterval(updateInterval);
});
}
function checkUpdate() {
var xmlHttp = new XMLHttpRequest();
alert("Connecting to server... Please wait several second");
xmlHttp.open("GET", "/check_web_ota_update", false);
xmlHttp.send(null);
alert(xmlHttp.responseText);
get_data("system");
}
function updateWeb() {
alert("Started updating from cloud.");
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", "web_ota_update?update=true", false);
xmlHttp.send(null);
uploadingFirmware = true;
}
function validatePasswords() {
var password = document.getElementById("auth_password").value;
var confirmPassword = document.getElementById("auth_password_confirm").value;
var saveButton = document.querySelector(".btn_save_a");
var passwordStatus = document.getElementById("pass_match");
if (password === confirmPassword) {
passwordStatus.innerHTML = "✔️";
saveButton.disabled = false;
} else {
passwordStatus.innerHTML = "❌";
saveButton.disabled = true;
}
}
if (document.getElementById("auth_password")) {
document.getElementById("auth_password").addEventListener("input", validatePasswords);
}
if (document.getElementById("auth_password_confirm")) {
document.getElementById("auth_password_confirm").addEventListener("input", validatePasswords);
}
if ((document.getElementById("auth_password")) && (document.getElementById("auth_password_confirm"))) {
validatePasswords();
}
function setupCollapsibleButtons() {
$(".btn_collapsible").click(function(){
$(this).toggleClass("active");
var content = $(this).parent().next();
if (content.css("display") === "block") {
content.css("display", "none");
} else {
content.css("display", "block");
}
});
}

500
webpage/styles.css Normal file
View File

@ -0,0 +1,500 @@
body {
font-family: sans-serif;
}
/* index styles */
.p1 {
color: #797979;
font: normal normal normal 18px/5px sans-serif;
letter-spacing: 0px;
}
.p2 {
text-align: left;
font: normal normal bold 14px/20px sans-serif;
letter-spacing: 0px;
color: #808080;
opacity: 1;
display: inline-block;
}
.p3 {
text-align: left;
font: normal normal normal 14px/20px sans-serif;
letter-spacing: 0px;
color: #808080;
opacity: 1;
display: inline-block;
}
.p4 {
text-align: left;
font: normal normal normal 14px/20px sans-serif;
letter-spacing: 0px;
color: #808080;
opacity: 1;
}
.p5 {
text-align: center;
font: normal normal bold 14px/20px sans-serif;
letter-spacing: 0px;
color: #000000;
opacity: 1;
}
/* NAVIGATION BAR */
nav {
display: flex;
background-color: transparent;
}
.top_bar {
display: flex;
width: 100%;
}
.top_bar li {
display: inline-block;
padding: 5px;
}
.top_bar li a {
text-decoration: none;
cursor: pointer;
display: flex;
align-items: center;
}
.top_bar li a:hover {
text-decoration: underline #fa6831;
text-underline-position: under;
text-underline-offset: 8px;
text-decoration-thickness: 2px;
}
#links li a.active {
text-decoration: underline #fa6831;
text-underline-position: under;
text-underline-offset: 8px;
text-decoration-thickness: 2px;
}
/* CFG BAR */
cfg {
display: flex;
flex-direction: column;
text-align: center;
font: normal normal bold 14px/20px sans-serif;
letter-spacing: 0px;
color: #2A2A2A;
opacity: 1;
}
cfg_bar li {
display: inline-block;
padding: 14px;
font-size: 16px;
left: 50%;
}
cfg_bar li a {
text-decoration: none;
cursor: pointer;
color: #212529;
}
cfg_bar li a:hover {
color: #fa6831;
}
/* CONTAINER */
.container {
display: table;
height: 100%;
width: 100%;
}
.container_left-half {
grid-column: 1;
display: table-cell;
vertical-align: middle;
width: 50%;
text-align: center;
}
.container_right-half {
grid-column: 2;
display: table-cell;
vertical-align: middle;
width: 50%;
}
/* CHECKBOX SLIDER */
.switch {
position: relative;
display: inline-block;
width: 30px;
height: 17px;
vertical-align: middle;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.checkbox_slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.checkbox_slider:before {
position: absolute;
content: "";
height: 13px;
width: 13px;
left: 2px;
bottom: 2px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked+.checkbox_slider {
background-color: #797979;
}
input:focus+.checkbox_slider {
box-shadow: 0 0 1px #797979;
}
input:checked+.checkbox_slider:before {
-webkit-transform: translateX(13px);
-ms-transform: translateX(13px);
transform: translateX(13px);
}
.checkbox_slider.round {
border-radius: 13px;
}
.checkbox_slider.round:before {
border-radius: 50%;
}
/* BUTTON */
.btn {
width: 306px;
height: 30px;
text-align: center;
font: normal normal bold 14px/5px sans-serif;
color: #000000;
background-color: white;
border-radius: 5px;
border: 1px solid #343a40;
}
.btn:hover {
background-color: #FA6831;
color: white;
}
/* BOTTON table */
#botton {
width: 100%;
text-align: center;
background: #F5F5F5 0% 0% no-repeat padding-box;
opacity: 1;
bottom: 0;
}
/* ----- styles config ----- */
.pc1 {
text-align: right;
font: normal normal normal 11px/5px sans-serif;
letter-spacing: 0px;
color: #797979;
opacity: 1;
}
.pc2 {
text-align: left;
font: normal normal normal 12px/5px sans-serif;
letter-spacing: 0px;
color: #000000;
opacity: 1;
}
.pc3 {
text-align: right;
font: normal normal normal bold 12px/17px sans-serif;
letter-spacing: 0px;
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;
}
/* BUTTON */
.btn_save {
width: 69px;
height: 24px;
text-align: center;
font: normal normal bold 14px/5px sans-serif;
color: #000000;
background-color: white;
border-radius: 5px;
border: 1px solid #343a40;
}
.btn_save:hover {
background-color: #FA6831;
color: white;
}
/* RANGE */
.slider {
-webkit-appearance: none;
width: 133px;
height: 10px;
border-radius: 5px;
background: linear-gradient(to right, #d3d3d3 50%, #FA6831 50%);
outline: none;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
background: #FA6831;
cursor: pointer;
}
.slider::-moz-range-thumb {
width: 16px;
height: 16px;
border-radius: 50%;
background: #FA6831;
cursor: pointer;
}
/* ----- styles wifi ----- */
.w1 {
text-align: left;
font: normal normal bold 12px/17px sans-serif;
letter-spacing: 0px;
color: #2A2A2A;
opacity: 1;
}
.w2 {
font: normal normal normal 11px/5px sans-serif;
letter-spacing: 0px;
color: #797979;
opacity: 1;
}
.w2 span {
vertical-align: middle;
}
#center_tb {
border-collapse: collapse;
width: 100%;
table-layout: fixed;
text-align: left;
}
/* wifi_ntw table */
#wifi_ntw {
font: normal normal normal 12px/5px sans-serif;
border-collapse: collapse;
width: 100%;
table-layout: fixed;
text-align: left;
}
#wifi_ntw td {
border-bottom: 1px solid #ddd;
padding: 8px;
}
#wifi_ntw tr:nth-child(even) {
background: #F8F8F8 0% 0% no-repeat padding-box;
}
#wifi_ntw tr:hover {
background-color: #ddd;
}
#wifi_ntw th {
background-color: transparent;
text-align: left;
font: normal normal bold 13px/11px sans-serif;
letter-spacing: 0px;
color: #2A2A2A;
opacity: 1;
}
#wifi_ntw tr {
border-bottom: 1px solid #ccc;
}
#wifi_ntw img {
width: 19px;
height: 12px;
}
/* BUTTON */
.btn_save_w {
width: 178px;
height: 24px;
text-align: center;
font: normal normal bold 14px/5px sans-serif;
color: #000000;
background-color: white;
border-radius: 5px;
border: 1px solid #343a40;
}
.btn_save_w:hover {
background-color: #FA6831;
color: white;
}
/* ----- styles auth ----- */
.pa1 {
text-align: right;
font: normal normal normal 11px/5px sans-serif;
letter-spacing: 0px;
color: #797979;
opacity: 1;
}
.pa2 {
text-align: left;
font: normal normal bold 12px/17px sans-serif;
letter-spacing: 0px;
color: #2A2A2A;
opacity: 1;
}
.pa3 {
text-align: right;
font: normal normal normal bold 12px/17px sans-serif;
letter-spacing: 0px;
color: #2A2A2A;
opacity: 1;
}
/* BUTTON */
.btn_save_a {
width: 178px;
height: 24px;
text-align: center;
font: normal normal bold 14px/5px sans-serif;
color: #000000;
background-color: white;
border-radius: 5px;
border: 1px solid #343a40;
}
.btn_save_a:hover {
background-color: #FA6831;
color: white;
}
.toggle-password {
position: relative;
cursor: pointer;
}
.toggle-password img {
position: absolute;
top: 50%;
right: 0;
transform: translateY(-50%);
}
.password-container {
display: flex;
align-items: center;
}
#auth_password.reveal {
-webkit-text-security: none;
}
#auth_password {
-webkit-text-security: disc;
}
/* ----- styles system ----- */
.ps1 {
text-align: right;
font: normal normal normal 11px/5px sans-serif;
letter-spacing: 0px;
color: #797979;
opacity: 1;
}
.ps2 {
text-align: left;
font: normal normal normal 12px/5px sans-serif;
letter-spacing: 0px;
color: #000000;
opacity: 1;
}
.ps3 {
text-align: right;
font: normal normal normal bold 12px/17px sans-serif;
letter-spacing: 0px;
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 {
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 100%;
table-layout: fixed;
}
#update td, #update th {
padding: 8px;
}
#update th {
padding-top: 12px;
padding-bottom: 12px;
text-align: left;
}
/* BUTTON */
.btn_update {
width: 178px;
height: 24px;
text-align: center;
font: normal normal bold 14px/5px sans-serif;
color: #000000;
background-color: white;
border-radius: 5px;
border: 1px solid #343a40;
}
.btn_update:hover {
background-color: #FA6831;
color: white;
}
.underlined-text {
text-decoration: underline;
color: blue;
cursor: pointer;
}
/* progress bar*/
.progress-container {
width: 100%;
background-color: #ccc;
}
.progress-bar {
width: 0px;
height: 15px;
background-color: #FA6831;
text-align: center;
line-height: 15px;
color: white;
}
/* advanced cam cfg */
.content {
display: none;
}
.btn_collapsible {
width: 300px;
height: 24px;
text-align: center;
font: normal normal bold 14px/5px sans-serif;
color: #000000;
background-color: white;
border-radius: 5px;
border: 1px solid #343a40;
}
.btn_collapsible:hover {
background-color: #FA6831;
color: white;
}

View File

@ -0,0 +1,72 @@
#!/bin/bash
original_webpage_path=../ESP32_PrusaConnectCam/WebPage.h
new_webpage_path=./WebPage.h
echo "Starting the WebPage.h generator"
echo "Copying the original WebPage.h file to the new location"
cp ${original_webpage_path} ${new_webpage_path}
# Read the index.html file and generate the WebPage.h file
echo "Generating index.html"
html_content=$(cat index.html | awk '{printf "%s\\n", $0}')
awk -v var="$html_content" '/^const char index_html\[\] PROGMEM = R"rawliteral\(/,/rawliteral";/ { if (/^const char index_html\[\] PROGMEM = R"rawliteral\(/) { print "const char index_html[] PROGMEM = R\"rawliteral(\n" var ")rawliteral\";"; next } { next } } 1' WebPage.h > temp && mv temp WebPage.h
# Read the page_auth.html file and generate the WebPage.h file
echo "Generating page_auth.html"
html_content=$(cat page_auth.html | awk '{printf "%s\\n", $0}')
awk -v var="$html_content" '/^const char page_auth_html\[\] PROGMEM = R"rawliteral\(/,/rawliteral";/ { if (/^const char page_auth_html\[\] PROGMEM = R"rawliteral\(/) { print "const char page_auth_html[] PROGMEM = R\"rawliteral(\n" var ")rawliteral\";"; next } { next } } 1' WebPage.h > temp && mv temp WebPage.h
# Read the page_wifi.html file and generate the WebPage.h file
echo "Generating page_wifi.html"
html_content=$(cat page_wifi.html | awk '{printf "%s\\n", $0}')
awk -v var="$html_content" '/^const char page_wifi_html\[\] PROGMEM = R"rawliteral\(/,/rawliteral";/ { if (/^const char page_wifi_html\[\] PROGMEM = R"rawliteral\(/) { print "const char page_wifi_html[] PROGMEM = R\"rawliteral(\n" var ")rawliteral\";"; next } { next } } 1' WebPage.h > temp && mv temp WebPage.h
# Read the page_config.html file and generate the WebPage.h file
echo "Generating page_config.html"
html_content=$(cat page_config.html | awk '{printf "%s\\n", $0}')
awk -v var="$html_content" '/^const char page_config_html\[\] PROGMEM = R"rawliteral\(/,/rawliteral";/ { if (/^const char page_config_html\[\] PROGMEM = R"rawliteral\(/) { print "const char page_config_html[] PROGMEM = R\"rawliteral(\n" var ")rawliteral\";"; next } { next } } 1' WebPage.h > temp && mv temp WebPage.h
# Read the page_system.html file and generate the WebPage.h file
echo "Generating page_system.html"
html_content=$(cat page_system.html | awk '{printf "%s\\n", $0}')
awk -v var="$html_content" '/^const char page_system_html\[\] PROGMEM = R"rawliteral\(/,/rawliteral";/ { if (/^const char page_system_html\[\] PROGMEM = R"rawliteral\(/) { print "const char page_system_html[] PROGMEM = R\"rawliteral(\n" var ")rawliteral\";"; next } { next } } 1' WebPage.h > temp && mv temp WebPage.h
# Read the syles.css file and generate the WebPage.h file
echo "Generating styles.css"
html_content=$(cat styles.css | awk '{printf "%s\\n", $0}')
awk -v var="$html_content" '/^const char styles_css\[\] PROGMEM = R"rawliteral\(/,/rawliteral";/ { if (/^const char styles_css\[\] PROGMEM = R"rawliteral\(/) { print "const char styles_css[] PROGMEM = R\"rawliteral(\n" var ")rawliteral\";"; next } { next } } 1' WebPage.h > temp && mv temp WebPage.h
# Read the scripts.js file and generate the WebPage.h file
echo "Generating scripts.js"
html_content=$(cat scripts.js | awk '{printf "%s\\n", $0}')
awk -v var="$html_content" '/^const char scripts_js\[\] PROGMEM = R"rawliteral\(/,/rawliteral";/ { if (/^const char scripts_js\[\] PROGMEM = R"rawliteral\(/) { print "const char scripts_js[] PROGMEM = R\"rawliteral(\n" var ")rawliteral\";"; next } { next } } 1' WebPage.h > temp && mv temp WebPage.h
# Read the license.html file and generate the WebPage.h file
echo "Generating license.html"
html_content=$(cat license.html | awk '{printf "%s\\n", $0}')
awk -v var="$html_content" '/^const char license_html\[\] PROGMEM = R"rawliteral\(/,/rawliteral";/ { if (/^const char license_html\[\] PROGMEM = R"rawliteral\(/) { print "const char license_html[] PROGMEM = R\"rawliteral(\n" var ")rawliteral\";"; next } { next } } 1' WebPage.h > temp && mv temp WebPage.h
# Read the gtac.html file and generate the WebPage.h file
echo "Generating gtac.html"
html_content=$(cat gtac.html | awk '{printf "%s\\n", $0}')
awk -v var="$html_content" '/^const char gtac_html\[\] PROGMEM = R"rawliteral\(/,/rawliteral";/ { if (/^const char gtac_html\[\] PROGMEM = R"rawliteral\(/) { print "const char gtac_html[] PROGMEM = R\"rawliteral(\n" var ")rawliteral\";"; next } { next } } 1' WebPage.h > temp && mv temp WebPage.h
# Read the privacypolicy.html file and generate the WebPage.h file
echo "Generating privacypolicy.html"
html_content=$(cat privacypolicy.html | awk '{printf "%s\\n", $0}')
awk -v var="$html_content" '/^const char privacypolicy_html\[\] PROGMEM = R"rawliteral\(/,/rawliteral";/ { if (/^const char privacypolicy_html\[\] PROGMEM = R"rawliteral\(/) { print "const char privacypolicy_html[] PROGMEM = R\"rawliteral(\n" var ")rawliteral\";"; next } { next } } 1' WebPage.h > temp && mv temp WebPage.h
# Read the cookies.html file and generate the WebPage.h file
echo "Generating cookies.html"
html_content=$(cat cookies.html | awk '{printf "%s\\n", $0}')
awk -v var="$html_content" '/^const char cookies_html\[\] PROGMEM = R"rawliteral\(/,/rawliteral";/ { if (/^const char cookies_html\[\] PROGMEM = R"rawliteral\(/) { print "const char cookies_html[] PROGMEM = R\"rawliteral(\n" var ")rawliteral\";"; next } { next } } 1' WebPage.h > temp && mv temp WebPage.h
# Copy the generated WebPage.h file to the ESP32 project
echo "WebPage.h generated successfully"
echo "Copy the new WebPage.h file to the ESP32 project"
cp ${new_webpage_path} ${original_webpage_path}
rm -f ${new_webpage_path}
exit 0