mirror of https://github.com/lumapu/ahoy.git
lumapu
2 years ago
committed by
GitHub
34 changed files with 2593 additions and 1773 deletions
@ -0,0 +1,3 @@ |
|||
#include "dbg.h" |
|||
|
|||
DBG_CB mCb = NULL; |
@ -1,194 +0,0 @@ |
|||
//-----------------------------------------------------------------------------
|
|||
// 2022 Ahoy, https://www.mikrocontroller.net/topic/525778
|
|||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
|||
//-----------------------------------------------------------------------------
|
|||
|
|||
|
|||
// a) https://www.favicon-generator.org/
|
|||
// b) exiftool -all:all= -r
|
|||
// c) hexlify.py:
|
|||
// import sys
|
|||
// f = open (sys.argv[1], 'rb').read()
|
|||
// for n, c in enumerate(f):
|
|||
// if n % 16 == 0: print (' "', end = '')
|
|||
// print (f"\\x{c:02x}", end = '')
|
|||
// if n % 16 == 15: print ('" \\')
|
|||
// if n % 16 != 15: print ('"')
|
|||
|
|||
#define FAVICON_PANEL_16 \ |
|||
"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" \ |
|||
"\x00\x00\x00\x10\x00\x00\x00\x10\x08\x03\x00\x00\x00\x28\x2d\x0f" \ |
|||
"\x53\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xb1\x8f\x0b\xfc\x61" \ |
|||
"\x05\x00\x00\x00\x20\x63\x48\x52\x4d\x00\x00\x7a\x26\x00\x00\x80" \ |
|||
"\x84\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00\x75\x30\x00\x00\xea" \ |
|||
"\x60\x00\x00\x3a\x98\x00\x00\x17\x70\x9c\xba\x51\x3c\x00\x00\x01" \ |
|||
"\x9b\x50\x4c\x54\x45\xfc\xfe\xff\xff\xff\xff\xcb\xcd\xcf\x22\x25" \ |
|||
"\x30\x12\x16\x21\x11\x15\x21\x11\x15\x23\x12\x16\x25\x10\x13\x1f" \ |
|||
"\x8f\x91\x93\x9b\x9d\xa1\x14\x17\x22\x14\x18\x25\x14\x17\x23\x13" \ |
|||
"\x17\x22\x14\x17\x24\x17\x19\x27\x22\x24\x2e\xc1\xc2\xc3\xf8\xfa" \ |
|||
"\xfb\x61\x63\x6b\x11\x13\x1f\x15\x19\x24\x17\x19\x24\x18\x1a\x24" \ |
|||
"\x18\x1b\x26\x16\x19\x26\x42\x44\x4b\xe7\xe8\xe9\xfd\xff\xff\xfe" \ |
|||
"\xff\xff\xe0\xe2\xe4\x33\x36\x3f\x16\x19\x24\x16\x17\x22\x16\x18" \ |
|||
"\x22\x17\x18\x23\x11\x12\x1e\x71\x73\x76\xfb\xfc\xfd\xb5\xb7\xba" \ |
|||
"\x1a\x1c\x27\x15\x17\x22\x15\x17\x21\x14\x15\x20\x14\x16\x23\x17" \ |
|||
"\x1a\x26\x1b\x1c\x25\xaa\xac\xad\x7c\x7f\x85\x11\x13\x1d\x16\x18" \ |
|||
"\x24\x17\x19\x25\x18\x1a\x25\x30\x32\x38\xd7\xd9\xd9\xef\xf1\xf2" \ |
|||
"\x47\x49\x51\x12\x13\x1e\x17\x19\x22\x16\x17\x23\x10\x11\x1c\x59" \ |
|||
"\x5a\x5f\xf4\xf6\xf7\xcc\xce\xd0\x24\x25\x2e\x16\x18\x23\x19\x1a" \ |
|||
"\x24\x16\x17\x21\x15\x16\x21\x17\x17\x21\x15\x17\x20\x92\x93\x95" \ |
|||
"\x99\x9a\x9e\x16\x16\x20\x17\x19\x23\x1a\x1b\x25\x23\x25\x2c\xc4" \ |
|||
"\xc6\xc7\xf8\xfa\xfa\x61\x62\x68\x15\x16\x20\x11\x12\x1d\x44\x45" \ |
|||
"\x49\xea\xec\xec\xdf\xe1\xe2\x33\x35\x3b\x15\x16\x1f\x13\x14\x1d" \ |
|||
"\x78\x7a\x7c\xfc\xfd\xfe\xb2\xb4\xb7\x1a\x1c\x26\x18\x1b\x25\x1a" \ |
|||
"\x1b\x22\x19\x1b\x22\xae\xaf\xb0\x86\x88\x8b\x14\x16\x1e\x19\x1b" \ |
|||
"\x25\x17\x19\x21\x17\x19\x20\x12\x13\x1a\x2c\x2d\x32\xcc\xce\xcf" \ |
|||
"\xfb\xfe\xff\xd8\xdb\xdb\x72\x74\x76\x25\x26\x2c\x19\x1c\x24\x13" \ |
|||
"\x16\x1d\x11\x13\x18\x4c\x4d\x4f\xc1\xc3\xc3\xe2\xe4\xe6\xf6\xf8" \ |
|||
"\xf8\xb6\xb9\xb9\x4f\x51\x55\x1b\x1c\x23\x19\x1b\x23\x15\x17\x1f" \ |
|||
"\x70\x71\x72\xbb\xbb\xbc\xc3\xc4\xc5\xde\xe0\xe1\xf6\xf8\xf9\xfd" \ |
|||
"\xfe\xff\xe3\xe5\xe6\x7e\x81\x82\x21\x22\x27\x1d\x1f\x26\x94\x95" \ |
|||
"\x95\xbd\xbd\xbe\xbd\xbe\xbe\xc8\xca\xcb\xe5\xe7\xe9\xfa\xfc\xfd" \ |
|||
"\x52\x5f\xd3\xea\x00\x00\x00\x01\x62\x4b\x47\x44\x01\xff\x02\x2d" \ |
|||
"\xde\x00\x00\x00\xd2\x49\x44\x41\x54\x18\xd3\x63\x60\x00\x02\x46" \ |
|||
"\x26\x66\x16\x16\x56\x36\x76\x0e\x4e\x46\x06\x30\x60\xe4\xe2\xe6" \ |
|||
"\xe1\xe5\xe3\x17\x10\x14\x82\x0a\x08\x8b\x88\x8a\x89\x4b\x48\x4a" \ |
|||
"\x49\xcb\xc8\x82\xf9\x72\xf2\x0a\xdc\x8a\x4a\xca\x2a\xaa\x6a\xea" \ |
|||
"\x50\x1d\x1a\x9a\x5a\xda\x3a\xba\x7a\xfa\x06\x50\x1d\xb2\x86\x46" \ |
|||
"\x5a\xbc\xc6\x26\xa6\x66\xe6\x72\x10\xbe\x85\xa5\x95\xb5\x89\xa9" \ |
|||
"\x8d\xad\x9d\x3d\xc4\x08\x46\x07\x47\x27\x67\x17\x57\x37\x77\x0f" \ |
|||
"\x39\xa8\x11\x9e\x4a\xa6\x36\x5e\xde\x3e\xbe\x7e\x50\x23\xfc\x03" \ |
|||
"\x02\x55\x8c\x9d\x55\x82\x82\x43\xa0\x96\x86\x86\x85\x4b\x48\xb8" \ |
|||
"\x84\x47\x44\x46\x41\x75\x44\xc7\x78\xc7\xba\x87\xc7\xc5\x27\x40" \ |
|||
"\x75\xc8\x25\x26\x25\x7b\xa7\xa4\xa6\xa5\x67\x64\x42\x5d\x91\x95" \ |
|||
"\x9d\x93\x92\x9b\x97\x5f\x50\x58\x24\x0c\x55\x52\x5c\x52\x5a\x56" \ |
|||
"\x5e\x51\x59\x55\x5d\x53\x0b\x11\xa9\x93\xab\x6f\x68\x6c\x6a\x6e" \ |
|||
"\x69\x6d\x6b\xef\x60\x00\x00\x01\x53\x2a\x2a\x63\x34\xcd\xf7\x00" \ |
|||
"\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82" |
|||
|
|||
#define FAVICON_PANEL_32 \ |
|||
"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" \ |
|||
"\x00\x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a" \ |
|||
"\xf4\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xb1\x8f\x0b\xfc\x61" \ |
|||
"\x05\x00\x00\x00\x20\x63\x48\x52\x4d\x00\x00\x7a\x26\x00\x00\x80" \ |
|||
"\x84\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00\x75\x30\x00\x00\xea" \ |
|||
"\x60\x00\x00\x3a\x98\x00\x00\x17\x70\x9c\xba\x51\x3c\x00\x00\x00" \ |
|||
"\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00" \ |
|||
"\x00\x07\x4a\x49\x44\x41\x54\x58\xc3\x9d\x97\xd9\x72\x5d\xc5\x15" \ |
|||
"\x86\xbf\xee\xde\xd3\xd9\x67\xd0\x99\x74\x34\x3b\x48\x32\x48\xe0" \ |
|||
"\x21\x60\xe1\x32\xd8\x15\x5e\x80\xdc\xf2\x00\xe4\x41\x12\x9e\x21" \ |
|||
"\x37\xc9\x43\xa4\x20\x95\xe2\x9a\x82\x0c\x10\x70\x62\x06\x0f\xc2" \ |
|||
"\x24\x15\xe2\x59\x96\xce\xbc\xe7\xb1\x73\x21\x93\x32\x20\x4b\xa7" \ |
|||
"\xe8\xaa\x7d\xb3\x2f\x7a\x7d\xf5\xff\xfd\xaf\x5e\x2d\xf2\x52\x6b" \ |
|||
"\x66\x58\x4a\xc0\x67\xff\xf8\x27\x6f\xbd\xf5\x36\xa3\xf1\x04\xad" \ |
|||
"\x0b\x84\x94\x68\xad\xc9\xf3\x1c\xc7\x76\x08\xa3\x04\xdb\xb6\xc8" \ |
|||
"\xf3\x12\x0d\xb8\x15\x9b\x28\x8c\x90\x52\xb0\xfe\xdc\x02\xbf\xff" \ |
|||
"\xdd\x6f\xb9\x72\xf9\x32\xc5\x53\x15\xe5\x2c\xc5\xbf\x5b\xdf\x7e" \ |
|||
"\x7b\x87\x34\x4b\x68\x36\xeb\x38\x8e\x4d\xb3\x59\xc7\xb6\x0d\x3a" \ |
|||
"\x9d\x16\xb6\x63\x52\x71\x6c\xea\xb5\x2a\x52\x0a\x6a\xd5\x0a\x96" \ |
|||
"\x65\x02\x1a\xc7\x36\xd9\xde\xda\x64\x6b\x6b\x9b\xf2\x07\x7b\x1a" \ |
|||
"\xb3\x16\xcf\x8a\x82\x4f\x3f\xbd\x4a\x18\x86\x34\x1a\x73\x68\x24" \ |
|||
"\x52\x1a\x08\xa1\xa8\x54\x1c\xa2\x30\xa4\xd9\xac\xd3\x9d\xef\xa0" \ |
|||
"\xb5\x66\x65\x65\x91\x30\x8c\x50\x52\xd3\x6a\xd5\xd9\xda\x7a\x81" \ |
|||
"\x56\xbb\xcd\x0f\xf5\x9e\x09\x40\x0a\x78\xb4\xf7\x98\x8f\x3f\xfe" \ |
|||
"\x8c\xa2\x28\x99\x4c\xa6\x94\x25\xe4\x59\x49\x14\x47\xc4\x51\x4c" \ |
|||
"\x92\x24\x54\x9c\x0a\xfd\xfe\x88\x3c\xcb\x89\xc2\x88\x2c\xcb\xd1" \ |
|||
"\x5a\x23\x74\xce\xf6\xf6\x36\x4a\xf0\x3d\xf9\x67\x06\x10\xc0\xd7" \ |
|||
"\xb7\xbf\xa1\x3f\x18\xb1\xb0\x30\x8f\xef\xfb\x98\xa6\x49\x96\x6b" \ |
|||
"\xda\x9d\x39\x6c\x4b\x31\x1c\x8e\xa9\xd5\xaa\x8c\xc7\x1e\xae\xeb" \ |
|||
"\x50\x71\xab\x0c\xfa\x63\x4c\x43\xb2\x79\xfa\x39\x2e\xbd\xf6\x1a" \ |
|||
"\x47\x1d\xb6\x99\x2d\xb8\x7a\xf5\x73\xee\xde\x79\x80\x6d\x9b\x64" \ |
|||
"\x79\x86\x6d\xdb\x64\x59\x86\x28\x73\x22\xa1\xa1\x2c\xb1\x2d\x03" \ |
|||
"\xd3\x54\x2c\x2e\xf6\x70\x6c\x87\xb2\x28\xa8\xd5\x1c\xce\x9f\x3b" \ |
|||
"\xc3\xda\xda\x29\x4a\xfd\x13\x00\x84\x00\xcf\x0f\xb8\x7e\xfd\x3a" \ |
|||
"\xbd\x85\x0e\x8e\x6d\x31\x99\x7a\x58\x96\xc5\x70\x38\x22\x8a\x63" \ |
|||
"\xd2\x24\xc5\xb2\x4c\x1e\xde\x7f\x44\x9c\xa4\xa4\x71\x42\x91\xe7" \ |
|||
"\x14\x65\x49\x96\xda\xac\xad\xad\xe2\x58\xe6\x8f\xe4\x9f\x09\x40" \ |
|||
"\x02\xb7\xbf\xf9\x86\x4f\x3e\xb9\x4a\x92\xc4\x68\x5d\xd2\x68\xd4" \ |
|||
"\x71\x9c\x0a\x4a\x49\x1c\xdb\xe2\xe0\x60\xc0\xe2\x52\x0f\x7f\xea" \ |
|||
"\xe3\xe4\x19\xae\x5b\xe5\xe0\x60\x88\x10\x8a\x4e\xbb\xc5\xeb\x97" \ |
|||
"\x2f\x3f\x73\xff\x99\x2c\xd8\xdd\xbd\xcd\x70\x34\x46\x6b\xcd\x74" \ |
|||
"\xea\x63\x99\x16\x79\x9e\x63\x5b\x06\x9e\x80\x2c\xcb\x99\x4e\x7d" \ |
|||
"\x92\xa4\xa0\xd9\x6a\xb1\xd0\xeb\x90\x65\x39\x96\x65\xb0\xb3\xf3" \ |
|||
"\x73\xce\xbc\x74\xe6\x47\xf1\x9b\x19\x20\x2b\x0a\xbe\xf8\xe2\x3a" \ |
|||
"\x95\x4a\x85\xb9\x46\x95\xfe\x60\x48\xb7\xdb\x65\x32\x1a\xa1\x94" \ |
|||
"\x62\x3a\x0d\x90\x52\x12\x85\x11\x7e\x90\x50\x14\x05\xfd\xbd\x3d" \ |
|||
"\xb2\x3c\xa3\xde\x70\x59\x5b\x5b\x65\xae\x39\xc7\xb3\xda\xdd\xb1" \ |
|||
"\x00\x52\xc0\x83\x47\x7b\x7c\xf8\xd1\xdf\x18\x8f\x27\x44\xbe\x07" \ |
|||
"\x42\xe1\xfb\x11\x85\x96\x74\xda\x6d\xb4\x50\xb8\xae\x4d\x51\x94" \ |
|||
"\x98\x56\x82\xeb\x3a\x0c\xfb\x03\x84\x90\x28\x25\x39\x7f\xfe\x3c" \ |
|||
"\x12\x28\x7e\x8a\x02\x02\xd8\xdd\xfd\x9a\xbd\xbd\x7d\x96\x16\x7b" \ |
|||
"\x84\xbe\x87\x46\x3e\xe9\x03\x9a\xfb\xf7\xf6\x88\x93\x84\x38\x76" \ |
|||
"\x28\x72\xa8\x56\x5d\xe6\xe7\x3b\x14\x79\x86\xd6\x70\xee\xdc\xf3" \ |
|||
"\x5c\xba\x74\x89\xe3\x7a\xfd\x89\x16\x5c\xbb\xf6\x25\xfd\x7e\x9f" \ |
|||
"\xb9\x46\x0d\x29\x25\xed\x76\x0b\x06\x23\x9a\xed\x16\x9e\x17\x81" \ |
|||
"\x00\x43\x29\x02\xcf\x47\x08\xf8\xd7\xed\x7f\x13\xc7\x09\x8d\xb9" \ |
|||
"\x3a\xa7\x37\x37\x58\x5e\x59\x39\x32\x7e\x27\x02\x08\x01\x5e\x10" \ |
|||
"\x70\xfd\xc6\x4d\x2a\x8e\x4d\x96\x66\x24\x49\x42\x9e\x17\x44\x51" \ |
|||
"\x8c\x32\x14\x68\x58\x5d\x5d\x24\x4f\x13\xa4\x94\xd4\xea\xd5\x43" \ |
|||
"\xf9\xa5\xa4\x2c\x33\x36\xd6\x9f\xc3\x32\x8c\x23\xe3\x77\x22\x80" \ |
|||
"\x7c\x22\xff\xd5\xcf\xae\xd2\xeb\x75\x48\xd3\x0c\xcb\xb2\x48\x93" \ |
|||
"\x84\x2c\xcb\x49\xa2\x98\x30\x88\x88\xa3\x94\xb2\x48\x31\x0c\x03" \ |
|||
"\x77\xbe\x45\xde\xa8\xe3\xe4\x05\xa7\xd6\x7a\x5c\xbe\x72\xe5\x24" \ |
|||
"\x81\x8f\xb7\xe0\xe6\xcd\x5b\x3c\x7c\xf0\x08\x29\x0c\x0a\xad\xe9" \ |
|||
"\x74\x5a\x20\x24\xcb\x2b\x8b\xa4\x69\x8a\x65\xd9\x28\x43\x31\x1a" \ |
|||
"\x44\x80\xe4\xe1\x83\x3d\x82\x20\xa2\x5a\xaf\xb2\xbd\xf5\x02\xdb" \ |
|||
"\x2f\xbe\xf8\xcc\xf8\x9d\x08\x90\xe5\x05\x37\x6f\xdc\xa2\xd1\x68" \ |
|||
"\x50\x6f\xcc\xb1\xbf\xdf\x27\x08\x62\xa2\x30\xc4\x77\x2c\xb4\x2e" \ |
|||
"\x69\xb7\x9b\x74\xe6\xbb\xa4\x69\x4a\xaf\xd7\x65\xd0\x1f\x20\xa4" \ |
|||
"\x40\x88\x92\x53\xa7\x56\xa9\xd7\x6a\xc7\xfa\xff\x4c\x00\x29\xe0" \ |
|||
"\xde\xc3\x87\x7c\xf0\xc1\x5f\x08\xc2\x14\x43\x05\x74\x3b\x73\x18" \ |
|||
"\x96\xc3\x74\x3c\x01\x34\xa3\xe1\x98\xd1\x70\x42\x7f\x30\xa1\x2c" \ |
|||
"\x72\xb2\x34\xc3\xa9\xb8\x28\x43\xd1\xee\x34\xb8\x70\xe1\x02\xe2" \ |
|||
"\x44\x03\x9e\x01\x70\x18\xbf\x5d\x06\xc3\x21\x8e\x63\x33\x19\x4f" \ |
|||
"\xb0\x2c\x93\x42\x2b\x1a\xcd\x26\xad\xa6\x4b\x51\xe4\xd4\x1b\x75" \ |
|||
"\x1e\xef\x1d\x00\x30\x1a\x4d\xf0\xbd\x90\x8a\x6b\xb1\xbe\x73\x96" \ |
|||
"\x57\x2f\x5e\x64\x96\x51\xeb\x48\x00\x0d\x7c\x7e\xed\x4b\x26\x93" \ |
|||
"\x29\xbd\x5e\x97\x40\x81\x53\xa9\xf0\xf8\xf1\x90\xf1\x68\xcc\xb0" \ |
|||
"\xbf\x8f\x69\x2a\x5c\xd7\xa5\x5a\xab\xd2\xed\xb6\xf1\xfd\x10\xdf" \ |
|||
"\x0f\x50\x0a\xd6\x7f\x76\x8a\x85\x85\xc5\x13\xe5\x87\x23\x46\xb2" \ |
|||
"\xc3\xdb\xcf\xe7\xda\xb5\x6b\x64\x69\x42\x7f\xbf\x4f\x92\xa4\xb8" \ |
|||
"\xd5\x0a\xcd\x66\x8d\x85\xc5\x0e\x08\x41\xa9\x05\x77\xff\x7b\x9f" \ |
|||
"\xc9\xd8\xc3\x0f\x42\x84\x28\x69\xb6\x1b\x74\xba\x2d\x5e\xd8\xda" \ |
|||
"\xc2\x54\xb3\x4d\x7b\xf2\xa8\x1f\xbb\xbb\xbb\xdc\xb8\x79\x8b\xc5" \ |
|||
"\xa5\x1e\xae\xeb\xa0\x81\x7b\x77\x1f\xe2\xf9\x01\xe8\x82\x7a\xdd" \ |
|||
"\xa5\x3b\xdf\xc2\xb4\x14\x52\x68\xa6\xe3\x31\x7b\x8f\x1e\x13\xf8" \ |
|||
"\x01\xed\x76\x8b\x2b\x33\xc4\xef\x58\x0b\xbe\xfa\xea\x16\x77\xee" \ |
|||
"\x3c\xc2\x75\x1d\x2c\xd3\xa0\xd9\x6e\xe2\xfb\x11\x59\x96\xb1\xbf" \ |
|||
"\xb7\x4f\x9e\x17\x28\x29\x31\x2d\x83\xd5\xce\x12\x42\x1c\xaa\xa1" \ |
|||
"\x94\xe2\xf4\xe6\x3a\xa7\x9f\x7f\xfe\xc4\xf8\x3d\x13\x20\xcd\x0b" \ |
|||
"\xae\xdf\xb8\x49\xad\xd1\xc0\x50\x8a\xe9\x64\x42\x56\x68\xa4\x54" \ |
|||
"\x74\x3b\x4d\x3c\xcf\x27\x89\x13\xc2\x20\x24\x0c\x02\x04\x1a\xcb" \ |
|||
"\xb6\x69\x34\x1b\x54\xaa\x15\x4e\x6f\x6e\x50\x75\xdd\x99\xfc\xff" \ |
|||
"\x11\x80\x14\x70\xe7\xfe\x3d\x3e\xfa\xf0\xcf\x58\xa6\xa2\x56\xab" \ |
|||
"\x61\x59\x26\x69\x9a\x32\x9d\x78\xa0\x0b\x40\xb3\xb4\xbc\x80\xe7" \ |
|||
"\x79\x24\x69\x4c\xe0\x07\x8c\xfa\x63\x84\x61\xd0\x68\xb8\x9c\x39" \ |
|||
"\x7b\x96\xa2\xd4\x08\x31\x4b\x08\x7f\x00\x20\x80\x7b\xf7\xef\x93" \ |
|||
"\xc4\x01\xa1\x37\xc1\x9f\x4e\x59\x5a\x5e\xc2\xb2\x4c\x2c\xcb\x24" \ |
|||
"\x8e\x22\xbc\xa9\x47\x59\x14\x08\x43\x31\xbf\xd0\xc5\x75\x1d\xee" \ |
|||
"\xfc\xe7\x2e\x52\xc1\xea\xca\x12\x4b\xcb\x2b\x0c\x87\x23\xea\xf5" \ |
|||
"\xfa\x93\x77\xc1\xf1\x4b\xfd\xfa\x37\xef\xbc\xf3\x34\x41\xbd\x56" \ |
|||
"\x67\x71\xb9\xc7\x78\x32\xe2\xe0\xf1\x3e\x81\x17\xe0\x79\x3e\xdd" \ |
|||
"\x6e\x07\xc3\x30\x10\x42\x20\xa4\xc4\x9b\x7a\x68\x01\xba\x28\x91" \ |
|||
"\x4a\xd2\x6c\xd6\x79\xe9\xa5\x2d\x76\x76\x76\xc8\x8b\x02\x21\x04" \ |
|||
"\xa6\x69\xa0\x94\x3a\x56\x8d\xef\x03\x00\xae\x5b\xe1\xdc\xd9\xb3" \ |
|||
"\xbc\xfc\xca\xcb\x38\xae\xcd\x64\x32\x66\xd0\x1f\x10\xf8\x3e\x41" \ |
|||
"\x10\xd2\x5b\xe8\x61\x3b\x0e\x49\x92\x52\x16\x05\xde\xd4\x23\x4d" \ |
|||
"\x12\x2c\x5b\xf1\xfa\x6b\x97\xd8\xd8\xd8\x20\x4b\x53\xf2\x3c\x07" \ |
|||
"\x0e\x21\xbe\x03\x9f\x09\xe0\x50\x09\xc1\x42\xaf\xc7\xc5\x8b\x17" \ |
|||
"\x59\xdf\x5c\x47\x8b\x92\xc9\x74\xc2\x68\x30\x22\x89\x53\xa2\x30" \ |
|||
"\xa6\x56\xab\xd2\x9d\xef\x12\xf8\x11\x5a\xc3\xf2\x52\x8f\x5f\xbe" \ |
|||
"\xf9\x26\x8d\x46\x9d\xa2\x28\xc8\xb2\x9c\x2c\xcf\x0f\x7d\x36\x0c" \ |
|||
"\x0c\xc3\x3c\x12\xe2\x68\x00\x0e\xbb\xa1\x65\x59\x9c\xde\xdc\xe4" \ |
|||
"\xc2\x2b\xaf\xd0\x6c\xcd\x31\x1a\x8f\x89\xe3\x98\xe9\x68\x8c\x2e" \ |
|||
"\x0b\xb4\x06\xa9\x14\xcd\x56\x93\xed\xad\x4d\xde\x78\xe3\x17\x48" \ |
|||
"\xa9\x00\x8d\x2e\x4b\x8a\x22\x27\xcf\x0f\x5f\x47\x87\x10\x06\x52" \ |
|||
"\x7e\x1f\xe2\xd8\xeb\xf8\xbb\x41\x72\x75\x75\x95\x5f\xbd\xfd\x36" \ |
|||
"\xaf\xee\xec\xf0\xa7\xf7\xdf\xe7\xdd\x3f\xfc\x91\x83\xfd\x7d\xa6" \ |
|||
"\xe3\x11\x85\x96\xd8\x8e\xc9\xd2\xe2\x02\x8e\xe3\xa0\xff\x3f\x7d" \ |
|||
"\x6a\x8a\x3c\x27\x0c\x43\x84\x10\x68\x0d\x5a\x6b\xaa\xd5\x2a\xea" \ |
|||
"\xa9\x2e\x39\xd3\x58\xae\x35\x98\x86\xc9\xab\x3b\x3b\xac\x9d\x3a" \ |
|||
"\xc5\xc6\xfa\x3a\xef\xbe\xf7\x1e\x7f\xff\xeb\xa7\xe4\x71\x46\x99" \ |
|||
"\xe7\xac\xaf\xaf\xa3\x94\xa2\x2c\xf5\x93\x2b\x59\x22\xe5\xe1\xa7" \ |
|||
"\x35\x94\x65\x49\x59\x16\x68\x5d\xf2\x74\x03\xfe\x1f\xc2\x60\x72" \ |
|||
"\xe2\x6a\x9b\x4e\x8f\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60" \ |
|||
"\x82" |
@ -0,0 +1,79 @@ |
|||
function toggle(id, hide) { |
|||
var elm = document.getElementById(id); |
|||
if(hide) { |
|||
if(!elm.classList.contains("hide")) |
|||
elm.classList.add("hide"); |
|||
} |
|||
else |
|||
elm.classList.remove('hide'); |
|||
} |
|||
|
|||
function getAjax(url, ptr, method="GET", json=null) { |
|||
var xhr = new XMLHttpRequest(); |
|||
if(xhr != null) { |
|||
xhr.open(method, url, true); |
|||
xhr.onreadystatechange = p; |
|||
if("POST" == method) |
|||
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); |
|||
xhr.send(json); |
|||
} |
|||
function p() { |
|||
if(xhr.readyState == 4) { |
|||
if(null != xhr.responseText) |
|||
ptr(JSON.parse(xhr.responseText)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function des(val) { |
|||
e = document.createElement('p'); |
|||
e.classList.add("subdes"); |
|||
e.innerHTML = val; |
|||
return e; |
|||
} |
|||
|
|||
function lbl(htmlfor, val, cl=null, id=null) { |
|||
e = document.createElement('label'); |
|||
e.htmlFor = htmlfor; |
|||
e.innerHTML = val; |
|||
if(null != cl) e.classList.add(...cl); |
|||
if(null != id) e.id = id; |
|||
return e; |
|||
} |
|||
|
|||
function inp(name, val, max=32, cl=["text"], id=null) { |
|||
e = document.createElement('input'); |
|||
e.classList.add(...cl); |
|||
e.name = name; |
|||
e.value = val; |
|||
e.maxLength = max; |
|||
if(null != id) e.id = id; |
|||
return e; |
|||
} |
|||
|
|||
function sel(name, opt, selId) { |
|||
e = document.createElement('select'); |
|||
e.name = name; |
|||
for(it of opt) { |
|||
o = document.createElement('option'); |
|||
o.value = it[0]; |
|||
o.innerHTML = it[1]; |
|||
if(it[0] == selId) |
|||
o.selected = true; |
|||
e.appendChild(o); |
|||
} |
|||
return e; |
|||
} |
|||
|
|||
function div(cl) { |
|||
e = document.createElement('div'); |
|||
e.classList.add(...cl); |
|||
return e; |
|||
} |
|||
|
|||
function span(val, cl) { |
|||
e = document.createElement('span'); |
|||
e.innerHTML = val; |
|||
e.classList.add(...cl); |
|||
return e; |
|||
} |
@ -0,0 +1,100 @@ |
|||
#ifndef __FAVICON_ICO_GZ_H__ |
|||
#define __FAVICON_ICO_GZ_H__ |
|||
#define favicon_ico_gz_len 1533 |
|||
const uint8_t favicon_ico_gz[] PROGMEM = {0x1f, 0x8b, 0x08, 0x08, 0xf2, 0xc5, 0xd5, 0x62, 0x04, 0x00, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f, |
|||
0x6e, 0x2e, 0x69, 0x63, 0x6f, 0x00, 0xed, 0x5c, 0x49, 0x68, 0x13, 0x51, 0x18, 0xfe, 0x62, 0xa3, |
|||
0x51, 0x28, 0xd6, 0x83, 0x82, 0xa0, 0x98, 0xb8, 0x1c, 0xbc, 0x59, 0x11, 0x5c, 0x50, 0xac, 0x88, |
|||
0x8a, 0xb8, 0xdd, 0x3c, 0x89, 0xd0, 0x93, 0x7a, 0x53, 0x51, 0x9b, 0x80, 0x4b, 0x46, 0xad, 0xfb, |
|||
0xd2, 0xb4, 0x2e, 0xb8, 0xa3, 0xc6, 0xba, 0xe1, 0x02, 0xae, 0xad, 0x0a, 0x26, 0x3d, 0xe8, 0xc5, |
|||
0x83, 0x57, 0x31, 0x2d, 0xc1, 0x8b, 0xb7, 0x92, 0x63, 0x0e, 0xa1, 0xcf, 0xff, 0xcf, 0xbc, 0xc9, |
|||
0x32, 0xa4, 0x66, 0xcf, 0x4b, 0xf3, 0xfa, 0xc3, 0xc7, 0x97, 0xcc, 0xcc, 0xcb, 0xf7, 0xbe, 0x6f, |
|||
0x26, 0x6f, 0x26, 0xf3, 0x92, 0x00, 0x0e, 0x34, 0x61, 0xda, 0x34, 0x66, 0x0f, 0xf6, 0x38, 0x81, |
|||
0xa5, 0x00, 0x3c, 0x1e, 0xf3, 0xf9, 0x53, 0x5a, 0x7e, 0x8f, 0x96, 0xad, 0x59, 0x63, 0x3e, 0x5f, |
|||
0xb8, 0x16, 0xd8, 0x30, 0x03, 0x58, 0x48, 0xdb, 0xd0, 0x2a, 0x5a, 0x62, 0x2e, 0x1f, 0xad, 0x06, |
|||
0xc3, 0x5d, 0xc6, 0x60, 0x38, 0x20, 0x08, 0x5e, 0xf1, 0x08, 0x77, 0x09, 0x42, 0xf4, 0xe2, 0xd7, |
|||
0x67, 0x2f, 0x56, 0xf5, 0x7b, 0x21, 0x18, 0xfc, 0xb8, 0x63, 0x0b, 0x7e, 0x11, 0x84, 0x77, 0x0b, |
|||
0xee, 0x2e, 0x9a, 0x03, 0x6f, 0xab, 0x1b, 0x82, 0x31, 0x38, 0x10, 0x38, 0xce, 0xaf, 0x51, 0x72, |
|||
0x7b, 0xd2, 0xe6, 0xf6, 0xbf, 0xbf, 0xf6, 0xcc, 0x2e, 0xbd, 0x7d, 0xf2, 0x35, 0x76, 0x88, 0x20, |
|||
0xf6, 0x96, 0xde, 0x3e, 0xe0, 0xa5, 0x76, 0xad, 0xd4, 0xfe, 0x64, 0xc9, 0xed, 0xa9, 0xff, 0xd4, |
|||
0xd6, 0x4f, 0x68, 0x2f, 0xb9, 0x3d, 0xf5, 0x9f, 0x70, 0xb8, 0xaf, 0x03, 0x4b, 0x4b, 0x6e, 0x4f, |
|||
0xfd, 0xe7, 0x7d, 0x4b, 0x6d, 0x4f, 0x96, 0xd3, 0xde, 0x7a, 0x8d, 0x72, 0xda, 0x73, 0x7d, 0xdb, |
|||
0x8f, 0x29, 0x45, 0xb5, 0x7f, 0x84, 0x4e, 0xfb, 0x71, 0xda, 0xb1, 0x19, 0x9d, 0x05, 0xb5, 0xef, |
|||
0xc5, 0xa5, 0xd1, 0x8e, 0x75, 0xef, 0x56, 0x9c, 0xfa, 0x6f, 0xfb, 0x87, 0x58, 0x86, 0x3c, 0x75, |
|||
0x70, 0x13, 0x96, 0xe5, 0x6a, 0x1f, 0x19, 0xe8, 0x6a, 0x47, 0x81, 0xd5, 0xea, 0x41, 0x7b, 0x56, |
|||
0xfb, 0x50, 0xe0, 0x72, 0xae, 0xed, 0xbe, 0x1a, 0x98, 0xcc, 0xc8, 0xf9, 0x1a, 0x73, 0x70, 0x9a, |
|||
0xda, 0x8f, 0xda, 0xf6, 0xb9, 0x81, 0x49, 0xfd, 0x3e, 0xbc, 0xa5, 0xfd, 0xd7, 0xcf, 0xf9, 0x8f, |
|||
0xf6, 0x1a, 0xff, 0xeb, 0xe7, 0x04, 0x42, 0x13, 0xc3, 0x20, 0x84, 0x00, 0x57, 0x14, 0x68, 0x89, |
|||
0x01, 0xee, 0x38, 0xb0, 0xfa, 0x08, 0x70, 0x64, 0xb5, 0x39, 0xce, 0x78, 0x08, 0x6b, 0x0a, 0x1f, |
|||
0x67, 0x2c, 0x0c, 0xf3, 0x72, 0xf1, 0x18, 0xab, 0xe4, 0x3e, 0x4f, 0x23, 0x88, 0x37, 0xbc, 0xae, |
|||
0xcf, 0x07, 0xaf, 0xf9, 0x1e, 0x48, 0x83, 0x97, 0xf1, 0xba, 0x43, 0x5b, 0xf0, 0x86, 0x8f, 0x87, |
|||
0x4c, 0xd0, 0xfe, 0x5d, 0xc5, 0xeb, 0x28, 0x97, 0x61, 0x33, 0xdf, 0x34, 0x78, 0x39, 0x8f, 0x55, |
|||
0xaa, 0xf4, 0x23, 0x03, 0x81, 0x53, 0x56, 0x1f, 0x54, 0xe8, 0xb3, 0xe6, 0x50, 0x28, 0x70, 0xde, |
|||
0xea, 0x83, 0x0a, 0xfd, 0x64, 0x1f, 0xc2, 0x81, 0x93, 0xbc, 0x0d, 0xf1, 0x01, 0x15, 0xfa, 0x49, |
|||
0x0c, 0x74, 0x9d, 0xe3, 0xed, 0x94, 0xe9, 0x13, 0x78, 0x1f, 0x24, 0xf5, 0x7b, 0xb1, 0x54, 0x89, |
|||
0x3e, 0xef, 0x7f, 0x59, 0x7c, 0xce, 0x53, 0xa5, 0x2f, 0x7a, 0xe0, 0x92, 0x7d, 0x38, 0xad, 0x44, |
|||
0x9f, 0xf6, 0x3f, 0x9f, 0xb3, 0x65, 0x1f, 0x3a, 0x95, 0xe8, 0x9b, 0xbe, 0x8f, 0x59, 0x7d, 0x50, |
|||
0xa4, 0x9f, 0xea, 0x03, 0xe9, 0xaf, 0x57, 0xa4, 0x9f, 0xea, 0x83, 0x42, 0x7d, 0xbe, 0x7e, 0xd9, |
|||
0xcf, 0xeb, 0x3e, 0xf9, 0xd0, 0xae, 0x44, 0x9f, 0xf7, 0xbf, 0x2c, 0x3a, 0x87, 0x9f, 0x56, 0xa9, |
|||
0x6f, 0xf5, 0x41, 0xa5, 0xbe, 0xbc, 0x0e, 0x3d, 0xa3, 0x52, 0xdf, 0xea, 0x83, 0x4a, 0x7d, 0xae, |
|||
0x0f, 0x87, 0x30, 0x5f, 0xa5, 0x3e, 0x57, 0x0d, 0xf4, 0x8d, 0x7c, 0xfa, 0xa4, 0x67, 0x54, 0x49, |
|||
0xff, 0x86, 0x10, 0x74, 0x29, 0x9b, 0xbf, 0x1c, 0xa4, 0xd9, 0x5d, 0x51, 0xfd, 0x20, 0x6e, 0x16, |
|||
0xa8, 0x9d, 0xd5, 0x87, 0x8a, 0xe8, 0x17, 0xaf, 0x9d, 0xea, 0x03, 0xe9, 0xf6, 0x94, 0xa9, 0x7f, |
|||
0xab, 0x44, 0xed, 0xac, 0x3e, 0x94, 0xa4, 0xff, 0x1c, 0xcd, 0x65, 0x6a, 0xa7, 0xfa, 0x60, 0x6c, |
|||
0x47, 0x73, 0xb1, 0xfa, 0xd5, 0xa8, 0x42, 0xf4, 0x23, 0xe1, 0xc0, 0x15, 0x54, 0xa9, 0x16, 0xbb, |
|||
0x71, 0xe5, 0x7f, 0xfa, 0x91, 0x50, 0xf7, 0x6d, 0x21, 0x8c, 0x09, 0xa8, 0x5e, 0x39, 0x48, 0xf3, |
|||
0x6a, 0x86, 0x7e, 0x59, 0xda, 0xfd, 0x07, 0x31, 0x97, 0x81, 0xe2, 0x2a, 0xdd, 0x87, 0xb4, 0xfe, |
|||
0x9d, 0x62, 0xb5, 0xe9, 0xde, 0xcb, 0x1c, 0x3a, 0xef, 0x0c, 0xf5, 0x77, 0xe0, 0xcf, 0x17, 0x1f, |
|||
0xe6, 0xa1, 0xb8, 0xe2, 0x3e, 0x5c, 0x23, 0x94, 0xa4, 0xfd, 0xd1, 0x07, 0x0f, 0x5d, 0x03, 0x45, |
|||
0xad, 0x73, 0x3f, 0x3f, 0xe6, 0x65, 0x28, 0xae, 0x92, 0x7d, 0x70, 0x4e, 0x9e, 0xe6, 0xe9, 0x24, |
|||
0xac, 0x23, 0x2c, 0x20, 0x4c, 0x27, 0x34, 0x13, 0x78, 0xf9, 0x24, 0xc2, 0x44, 0x7e, 0x7c, 0x96, |
|||
0xb0, 0x91, 0xb0, 0x90, 0x30, 0xd3, 0x5c, 0xe7, 0x6c, 0x26, 0x4c, 0x25, 0xb4, 0x30, 0x02, 0x84, |
|||
0x9f, 0x84, 0x58, 0x1a, 0xee, 0xb8, 0xc7, 0xd9, 0x96, 0xf0, 0x38, 0xfd, 0x23, 0x1e, 0xa7, 0x10, |
|||
0x1e, 0x97, 0x10, 0x51, 0x0b, 0x2d, 0x23, 0xfe, 0x98, 0x3b, 0xd1, 0x16, 0x6f, 0x8b, 0xbb, 0x13, |
|||
0x0b, 0xe5, 0x3d, 0x0a, 0xa3, 0xf8, 0xfb, 0x14, 0xb9, 0x3e, 0xbb, 0x45, 0xac, 0xed, 0xe5, 0x67, |
|||
0x38, 0x2f, 0x8f, 0xa1, 0x79, 0xd1, 0x8b, 0x5d, 0xb6, 0xeb, 0x89, 0x61, 0xce, 0x35, 0x0f, 0x86, |
|||
0x6d, 0xf7, 0xc1, 0x76, 0xf1, 0x58, 0x9b, 0x17, 0x9b, 0xe1, 0xb5, 0x8d, 0x09, 0x91, 0xf4, 0xfb, |
|||
0x21, 0x8d, 0x02, 0xee, 0x93, 0x68, 0xe7, 0x3f, 0x6f, 0x06, 0x1a, 0xf8, 0xe7, 0x1a, 0x0a, 0x75, |
|||
0x9d, 0xd0, 0xcd, 0xff, 0xe0, 0x40, 0xf7, 0xa5, 0xbc, 0x19, 0x34, 0xb2, 0x7f, 0xf3, 0xda, 0xe0, |
|||
0xa2, 0x3d, 0x03, 0x9d, 0xfc, 0xe7, 0xca, 0x80, 0xef, 0x5b, 0xea, 0xe4, 0xdf, 0xcc, 0xa0, 0xeb, |
|||
0x82, 0x3d, 0x03, 0x9d, 0xfc, 0xe7, 0xca, 0x20, 0x12, 0xee, 0xee, 0xd4, 0xc9, 0x3f, 0x21, 0x35, |
|||
0x77, 0x91, 0xce, 0x20, 0xb0, 0x5b, 0x27, 0xff, 0xe9, 0xb9, 0x93, 0x74, 0xe9, 0xe6, 0xdf, 0xca, |
|||
0x40, 0x67, 0xff, 0xd6, 0xdc, 0x91, 0xd6, 0xfe, 0x93, 0xe8, 0xf6, 0xdb, 0xfc, 0xb7, 0xe7, 0xf5, |
|||
0xde, 0x48, 0xfe, 0x6d, 0xe3, 0xbf, 0x35, 0x7f, 0xa5, 0xb3, 0xff, 0x82, 0x32, 0x68, 0x70, 0xff, |
|||
0x32, 0x83, 0x33, 0xba, 0xf9, 0x17, 0x0f, 0x30, 0x2b, 0xeb, 0x79, 0x10, 0x97, 0xb5, 0xf2, 0x4f, |
|||
0xe3, 0x3f, 0xc1, 0x6f, 0xcb, 0xe0, 0xac, 0x56, 0xfe, 0xe5, 0xdc, 0xad, 0x3d, 0x03, 0xcd, 0xfc, |
|||
0x67, 0x65, 0xc0, 0xf3, 0x0a, 0xf4, 0xbc, 0x5b, 0x33, 0xff, 0x3c, 0x97, 0x74, 0xd4, 0x9e, 0x81, |
|||
0x56, 0xfe, 0x73, 0x67, 0xd0, 0xa3, 0x95, 0x7f, 0x13, 0x87, 0x6d, 0x19, 0xcc, 0xd6, 0xcc, 0x3f, |
|||
0x1f, 0xf3, 0x47, 0x32, 0xb7, 0xd5, 0xce, 0xbf, 0x3c, 0x0e, 0x34, 0xf7, 0x9f, 0xcc, 0x40, 0x73, |
|||
0xff, 0x3c, 0x26, 0x6e, 0xb3, 0xda, 0xc8, 0xef, 0x57, 0x5d, 0xd1, 0xca, 0xbf, 0x6d, 0xfc, 0x17, |
|||
0x34, 0x15, 0xd9, 0xe7, 0xc5, 0x55, 0x5d, 0xfd, 0x17, 0x98, 0x41, 0x43, 0xfb, 0xb7, 0x32, 0xa0, |
|||
0xef, 0xd8, 0x5d, 0xd3, 0xd5, 0x7f, 0x66, 0x06, 0xba, 0xfa, 0xcf, 0xcc, 0x40, 0x57, 0xff, 0x56, |
|||
0x06, 0xe4, 0xf9, 0xba, 0xae, 0xfe, 0x33, 0x33, 0xd0, 0xd5, 0x7f, 0x66, 0x06, 0xba, 0xfa, 0xcf, |
|||
0x18, 0x0f, 0x4e, 0xeb, 0xea, 0xdf, 0xaa, 0x71, 0xff, 0xe3, 0xfe, 0x1b, 0xc4, 0xff, 0x4e, 0x94, |
|||
0x59, 0x1d, 0x5b, 0xb1, 0x33, 0xaf, 0xf7, 0x7a, 0xf4, 0x1f, 0xc4, 0x13, 0xf1, 0x9c, 0x7e, 0x4e, |
|||
0x5a, 0x66, 0x19, 0x06, 0x26, 0x50, 0x06, 0xf7, 0xc7, 0x94, 0xff, 0x0a, 0x79, 0x2f, 0x2a, 0x83, |
|||
0xfa, 0xf1, 0xff, 0x54, 0x7c, 0x85, 0x13, 0x15, 0x2e, 0xce, 0x80, 0x7e, 0x83, 0xf2, 0xa0, 0xce, |
|||
0xfd, 0x57, 0xc5, 0xbb, 0x55, 0xdb, 0xb7, 0xa3, 0x89, 0x33, 0xa8, 0x53, 0xff, 0xcf, 0xaa, 0xe9, |
|||
0x3d, 0x33, 0x03, 0xf2, 0xfb, 0xb0, 0xce, 0xfc, 0xd7, 0xc4, 0xbb, 0x3d, 0x83, 0x3a, 0xf1, 0x5f, |
|||
0x53, 0xef, 0x59, 0xef, 0x85, 0xad, 0x08, 0x2a, 0xf6, 0xff, 0x5c, 0x85, 0x77, 0x7b, 0x06, 0x8a, |
|||
0xfc, 0x2b, 0xf5, 0x6e, 0xcf, 0xa0, 0xa6, 0xfe, 0x83, 0x58, 0x54, 0xc9, 0xf3, 0x7b, 0xb9, 0xc5, |
|||
0x19, 0xd0, 0xff, 0x9c, 0x2c, 0xaa, 0x95, 0xff, 0xb1, 0x50, 0xe3, 0xfe, 0xc7, 0xfd, 0xb7, 0x56, |
|||
0xc8, 0xff, 0x50, 0xb8, 0xeb, 0x3e, 0xc6, 0x58, 0xd1, 0x7f, 0xfd, 0xdc, 0x6f, 0xad, 0x8c, 0xff, |
|||
0x17, 0x3f, 0x7e, 0xdc, 0x9c, 0x88, 0x31, 0x56, 0xdb, 0xe9, 0x2f, 0x8c, 0xc8, 0x5f, 0x6f, 0x99, |
|||
0xfe, 0xc7, 0xa4, 0xf7, 0x62, 0x32, 0x68, 0x54, 0xef, 0xb6, 0x0c, 0x1e, 0x17, 0xe9, 0xff, 0x65, |
|||
0x23, 0x78, 0x2f, 0x24, 0x03, 0x95, 0xde, 0x69, 0x1e, 0xf8, 0x14, 0x03, 0x35, 0x28, 0xce, 0x60, |
|||
0x91, 0x1b, 0x4f, 0x6c, 0xfe, 0xd5, 0x79, 0xf7, 0xe1, 0x44, 0x6a, 0xce, 0xab, 0x03, 0xe7, 0x51, |
|||
0x83, 0xca, 0x91, 0x41, 0xe6, 0x77, 0xfc, 0xdf, 0xfd, 0xfe, 0xd0, 0xe3, 0x42, 0x0d, 0xea, 0x93, |
|||
0x17, 0xc7, 0xed, 0xf3, 0xbe, 0x74, 0x1c, 0x9c, 0x43, 0x0d, 0x6a, 0xc9, 0x12, 0x4c, 0x24, 0xdf, |
|||
0xaf, 0x6c, 0xfe, 0xdf, 0xab, 0xf4, 0xae, 0x28, 0x83, 0xd7, 0x84, 0xba, 0xf1, 0x6e, 0x81, 0xb7, |
|||
0x41, 0x0d, 0xca, 0xca, 0x00, 0xcb, 0x11, 0x75, 0xd1, 0xd7, 0x14, 0x0d, 0xc9, 0x21, 0xc9, 0x51, |
|||
0xc9, 0xfb, 0x24, 0x2f, 0x97, 0x3c, 0x5b, 0xf2, 0x54, 0xc9, 0x2e, 0xc9, 0x4d, 0x7d, 0x26, 0x3b, |
|||
0xe2, 0x26, 0xc3, 0x62, 0x9f, 0xe4, 0xe5, 0x92, 0x57, 0x49, 0x5e, 0x2d, 0xb9, 0x4d, 0xf2, 0x4a, |
|||
0xc3, 0xe4, 0x15, 0x21, 0xb9, 0x7d, 0x54, 0x72, 0xbb, 0xe4, 0x56, 0xc9, 0x33, 0x25, 0x37, 0x4b, |
|||
0x76, 0x49, 0x6e, 0x92, 0xec, 0xb0, 0xf4, 0xec, 0x1c, 0x93, 0x1c, 0x97, 0x9c, 0x90, 0x3c, 0x22, |
|||
0x59, 0x58, 0x7c, 0x46, 0xf2, 0x77, 0xc9, 0x7f, 0x25, 0x8b, 0x82, 0xd8, 0x41, 0x7f, 0x3b, 0x91, |
|||
0xec, 0x8f, 0x10, 0x21, 0x66, 0xfe, 0x67, 0x06, 0xe6, 0x16, 0x21, 0x62, 0xcc, 0x6e, 0x21, 0xe2, |
|||
0xcc, 0x6d, 0x42, 0x24, 0x98, 0xfd, 0x42, 0x8c, 0x30, 0x0b, 0x2a, 0xf6, 0xcf, 0x7c, 0x87, 0x72, |
|||
0x61, 0x4e, 0xe4, 0x67, 0x3f, 0xf3, 0x08, 0x7d, 0x28, 0x4f, 0x16, 0x1c, 0x26, 0x1b, 0x4d, 0xb4, |
|||
0x90, 0x56, 0x85, 0x5c, 0xb4, 0x11, 0x6d, 0x1a, 0x6d, 0x21, 0x55, 0x52, 0x8e, 0x31, 0xf8, 0x31, |
|||
0x2f, 0xe3, 0x75, 0xff, 0x00, 0xd3, 0x39, 0x74, 0x2c, 0x6e, 0x57, 0x00, 0x00}; |
|||
#endif /*__FAVICON_ICO_GZ_H__*/ |
@ -1,72 +1,127 @@ |
|||
<!doctype html> |
|||
<html> |
|||
<head> |
|||
<title>Index - {DEVICE}</title> |
|||
<title>Index</title> |
|||
<link rel="stylesheet" type="text/css" href="style.css"/> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
|||
<script type="text/javascript"> |
|||
getAjax('/uptime', 'uptime'); |
|||
getAjax('/cmdstat', 'cmds'); |
|||
window.setInterval("getAjax('/uptime', 'uptime')", {JS_TS}); |
|||
window.setInterval("getAjax('/cmdstat', 'cmds')", {JS_TS}); |
|||
|
|||
function getAjax(url, resid) { |
|||
var http = null; |
|||
http = new XMLHttpRequest(); |
|||
if(http != null) { |
|||
http.open("GET", url, true); |
|||
http.onreadystatechange = print; |
|||
http.send(null); |
|||
} |
|||
|
|||
function print() { |
|||
if(http.readyState == 4) { |
|||
document.getElementById(resid).innerHTML = http.responseText; |
|||
} |
|||
} |
|||
} |
|||
function getInverterInfo(data){ |
|||
var http = null; |
|||
http = new XMLHttpRequest(); |
|||
if(http != null) { |
|||
http.open("POST", "/api"); |
|||
http.setRequestHeader("Accept", "application/json"); |
|||
http.setRequestHeader("Content-Type", "application/json"); |
|||
http.send(data); |
|||
} |
|||
} |
|||
</script> |
|||
<script type="text/javascript" src="api.js"></script> |
|||
</head> |
|||
<body> |
|||
<h1>AHOY - {DEVICE}</h1> |
|||
<h1>AHOY</h1> |
|||
<div id="content" class="content"> |
|||
<p> |
|||
<a href="/visualization">Visualization</a><br/> |
|||
<a href="/live">Visualization</a><br/> |
|||
<br/> |
|||
<a href="/setup">Setup</a><br/> |
|||
<a href="/serial">Webserial & Commands</a><br/> |
|||
</p> |
|||
<p><span class="des">Uptime: </span><span id="uptime"></span></p> |
|||
<p><span class="des">Statistics: </span><pre id="cmds"></pre></p> |
|||
<p>Every {TS}seconds the values are updated</p> |
|||
<p><span class="des">ESP-Time: </span><span id="date"></span></p> |
|||
<p><span class="des">RSSI: </span><span id="wifi_rssi"></span>dBm</p> |
|||
<p> |
|||
<span class="des">Statistics: </span> |
|||
<pre id="stat"></pre> |
|||
<pre id="iv"></pre> |
|||
<pre id="warn_info"></pre> |
|||
</p> |
|||
<p>Every <span id="refresh"></span> seconds the values are updated</p> |
|||
|
|||
<div id="note"> |
|||
This project was started from <a href="https://www.mikrocontroller.net/topic/525778" target="_blank">this discussion. (Mikrocontroller.net)</a><br/> |
|||
New updates can be found on Github: <a href="https://github.com/grindylow/ahoy" target="_blank">https://github.com/grindylow/ahoy</a><br/> |
|||
New updates can be found on Github: <a href="https://github.com/lumapu/ahoy" target="_blank">https://github.com/lumapu/ahoy</a><br/> |
|||
<br/> |
|||
Please report issues using the feature provided by <a href="https://github.com/grindylow/ahoy/issues">Github</a><br/> |
|||
Please report issues using the feature provided by <a href="https://github.com/lumapu/ahoy/issues">Github</a><br/> |
|||
<br/> |
|||
Discuss with us on <a href="https://discord.gg/WzhxEY62mB">Discord</a> |
|||
<br/> |
|||
<p class="lic"><a href="https://creativecommons.org/licenses/by-nc-sa/3.0/de">Creative Commons - https://creativecommons.org/licenses/by-nc-sa/3.0/de/</a><br/> |
|||
Check the licenses which are published on <a href="https://github.com/grindylow/ahoy">https://github.com/grindylow/ahoy</a> as well</p> |
|||
Check the licenses which are published on <a href="https://github.com/lumapu/ahoy">https://github.com/lumapu/ahoy</a> as well</p> |
|||
</div> |
|||
</div> |
|||
<div id="footer"> |
|||
<p class="left">© 2022</p> |
|||
<p class="left"><a href="/update">Update Firmware</a></p> |
|||
<p class="right">AHOY :: {VERSION}</p> |
|||
<p class="right" id="version"></p> |
|||
<p class="right"><a href="/reboot">Reboot</a></p> |
|||
<p class="right">Git SHA: {BUILD}</p> |
|||
<p class="right"><a href="/api">REST API</a></p> |
|||
</div> |
|||
<script type="text/javascript"> |
|||
var mIntervalSet = false; |
|||
function parseSys(obj) { |
|||
document.getElementById("version").innerHTML = "Git SHA: " + obj["build"] + " :: " + obj["version"]; |
|||
document.getElementById("wifi_rssi").innerHTML = obj["wifi_rssi"];; |
|||
|
|||
var date = new Date(obj["ts_now"] * 1000); |
|||
var up = obj["ts_uptime"]; |
|||
var days = parseInt(up / 86400) % 365; |
|||
var hrs = parseInt(up / 3600) % 24; |
|||
var min = parseInt(up / 60) % 60; |
|||
var sec = up % 60; |
|||
document.getElementById("uptime").innerHTML = days + " Days, " |
|||
+ ("0"+hrs).substr(-2) + ":" |
|||
+ ("0"+min).substr(-2) + ":" |
|||
+ ("0"+sec).substr(-2); |
|||
document.getElementById("date").innerHTML = date.toLocaleString('de-DE', {timeZone: 'UTC'}); |
|||
} |
|||
|
|||
function parseStat(obj) { |
|||
document.getElementById("stat").innerHTML = "RX success: " + obj["rx_success"] |
|||
+ "\nRX fail: " + obj["rx_fail"] |
|||
+ "\nRX no anwser: " + obj["rx_fail_answer"] |
|||
+ "\nFrames received: " + obj["frame_cnt"] |
|||
+ "\nTX Cnt: " + obj["tx_cnt"]; |
|||
} |
|||
|
|||
function parseIv(obj) { |
|||
var html = ""; |
|||
for(var i of obj) { |
|||
html += "Inverter #" + i["id"] + ": " + i["name"] + " (v" + i["version"] + ") is "; |
|||
if(false == i["is_avail"]) |
|||
html += "not "; |
|||
html += "available and is "; |
|||
if(false == i["is_producing"]) |
|||
html += "not "; |
|||
html += "producing\n"; |
|||
|
|||
if(false == i["is_avail"]) { |
|||
if(i["ts_last_success"] > 0) { |
|||
var date = new Date(i["ts_last_success"] * 1000); |
|||
html += "-> last successful transmission: " + date.toLocaleString('de-DE', {timeZone: 'UTC'}); |
|||
} |
|||
} |
|||
|
|||
} |
|||
document.getElementById("iv").innerHTML = html; |
|||
} |
|||
|
|||
function parseWarnInfo(warn, info) { |
|||
var html = ""; |
|||
for(var w of warn) { |
|||
html += "WARN: " + w + "\n"; |
|||
} |
|||
for(var i of info) { |
|||
html += "INFO: " + i + "\n"; |
|||
} |
|||
document.getElementById("warn_info").innerHTML = html; |
|||
} |
|||
|
|||
function parse(obj) { |
|||
if(null != obj) { |
|||
parseSys(obj["system"]); |
|||
parseStat(obj["statistics"]); |
|||
parseIv(obj["inverter"]); |
|||
parseWarnInfo(obj["warnings"], obj["infos"]); |
|||
document.getElementById("refresh").innerHTML = obj["refresh_interval"]; |
|||
if(false == mIntervalSet) { |
|||
window.setInterval("getAjax('/api/index', parse)", obj["refresh_interval"] * 1000); |
|||
mIntervalSet = true; |
|||
} |
|||
} |
|||
else |
|||
document.getElementById("refresh").innerHTML = "n/a"; |
|||
} |
|||
|
|||
getAjax("/api/index", parse); |
|||
</script> |
|||
</body> |
|||
</html> |
|||
|
@ -0,0 +1,188 @@ |
|||
<!doctype html> |
|||
<html> |
|||
<head> |
|||
<title>Serial Console</title> |
|||
<link rel="stylesheet" type="text/css" href="style.css"/> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
|||
<script type="text/javascript" src="api.js"></script> |
|||
</head> |
|||
<body> |
|||
<h1>Serial Console</h1> |
|||
<div id="content" class="content"> |
|||
<div class="serial"> |
|||
<textarea id="serial" cols="80" rows="20" readonly></textarea><br/> |
|||
connected: <span class="dot" id="connected"></span> |
|||
Uptime: <span id="uptime"></span> |
|||
<input type="button" value="clear" class="btn" id="clear"/> |
|||
<input type="button" value="autoscroll" class="btn" id="scroll"/> |
|||
<br/> |
|||
<br/> |
|||
<br/> |
|||
<br/> |
|||
<hr> |
|||
<h3>Commands</h3> |
|||
<br/> |
|||
<label for="iv">Select Inverter:</label> |
|||
<select name="iv" id="InvID"> |
|||
</select> |
|||
<br/> |
|||
<div id="power"> |
|||
<input type="button" value="Restart" class="btn" id="restart"/> |
|||
<input type="button" value="Turn Off" class="btn" id="power_off"/> |
|||
<input type="button" value="Turn On" class="btn" id="power_on"/> |
|||
</div> |
|||
<br/> |
|||
<br/> |
|||
<br/> |
|||
<br/> |
|||
<br/> |
|||
<label>Send Power Limit: </label> |
|||
<input type="number" class="text" name="pwrlimval" maxlength="4"/> |
|||
<label> </label> |
|||
<select name="pwrlimcntrl" id="pwrlimcntrl"> |
|||
<option value="" selected disabled hidden>select the unit and persistence</option> |
|||
<option value="0">absolute in Watt non persistent</option> |
|||
<option value="1">relative in percent non persistent</option> |
|||
<option value="256">absolute in Watt persistent</option> |
|||
<option value="257">relative in percent persistent</option> |
|||
</select> |
|||
<br/> |
|||
<input type="button" value="Send Power Limit" class="btn" id="sendpwrlim"/> |
|||
<br/> |
|||
<p>Ctrl result: <span id="result">n/a</span></p> |
|||
</div> |
|||
</div> |
|||
<div id="footer"> |
|||
<p class="left">© 2022</p> |
|||
<p class="left"><a href="/">Home</a></p> |
|||
<p class="right" id="version"></p> |
|||
</div> |
|||
<script type="text/javascript"> |
|||
var mAutoScroll = true; |
|||
var con = document.getElementById("serial"); |
|||
var mIntervalSet = false; |
|||
|
|||
function parseSys(obj) { |
|||
var up = obj["ts_uptime"]; |
|||
var days = parseInt(up / 86400) % 365; |
|||
var hrs = parseInt(up / 3600) % 24; |
|||
var min = parseInt(up / 60) % 60; |
|||
var sec = up % 60; |
|||
document.getElementById("uptime").innerHTML = days + " Days, " |
|||
+ ("0"+hrs).substr(-2) + ":" |
|||
+ ("0"+min).substr(-2) + ":" |
|||
+ ("0"+sec).substr(-2); |
|||
|
|||
if(false == mIntervalSet) { |
|||
document.getElementById("version").innerHTML = "Git SHA: " + obj["build"] + " :: " + obj["version"]; |
|||
window.setInterval("getAjax('/api/system', parseSys)", 10000); |
|||
mIntervalSet = true; |
|||
} |
|||
} |
|||
|
|||
function parse(root) { |
|||
select = document.getElementById('InvID'); |
|||
|
|||
if(null == root) return; |
|||
root = root.inverter; |
|||
for(var i = 0; i < root.inverter.length; i++) |
|||
{ |
|||
inv = root.inverter[i]; |
|||
var opt = document.createElement('option'); |
|||
opt.value = inv.id; |
|||
opt.innerHTML = inv.name; |
|||
select.appendChild(opt); |
|||
} |
|||
} |
|||
|
|||
document.getElementById("clear").addEventListener("click", function() { |
|||
con.value = ""; |
|||
}); |
|||
document.getElementById("scroll").addEventListener("click", function() { |
|||
mAutoScroll = !mAutoScroll; |
|||
this.value = (mAutoScroll) ? "autoscroll" : "manual scoll"; |
|||
}); |
|||
|
|||
if (!!window.EventSource) { |
|||
var source = new EventSource('/events'); |
|||
source.addEventListener('open', function(e) { |
|||
document.getElementById("connected").style.backgroundColor = "#0c0"; |
|||
}, false); |
|||
|
|||
source.addEventListener('error', function(e) { |
|||
if (e.target.readyState != EventSource.OPEN) { |
|||
document.getElementById("connected").style.backgroundColor = "#f00"; |
|||
} |
|||
}, false); |
|||
|
|||
source.addEventListener('serial', function(e) { |
|||
con.value += e.data.replace(/\<rn\>/g, '\r\n'); |
|||
if(mAutoScroll) |
|||
con.scrollTop = con.scrollHeight; |
|||
}, false); |
|||
} |
|||
|
|||
getAjax("/api/system", parseSys); |
|||
|
|||
// only for test |
|||
function ctrlCb(obj) { |
|||
var e = document.getElementById("result"); |
|||
if(obj["success"]) |
|||
e.innerHTML = "ok"; |
|||
else |
|||
e.innerHTML = "Error: " + obj["error"]; |
|||
} |
|||
|
|||
function get_selected_iv() |
|||
{ |
|||
var e = document.getElementById("InvID"); |
|||
return parseInt(e.value); |
|||
} |
|||
|
|||
const wrapper = document.getElementById('power'); |
|||
|
|||
wrapper.addEventListener('click', (event) => { |
|||
var power = event.target.value; |
|||
var obj = new Object(); |
|||
|
|||
switch (power) |
|||
{ |
|||
case "Turn On": |
|||
obj.cmd = 0; |
|||
break; |
|||
case "Turn Off": |
|||
obj.cmd = 1; |
|||
break; |
|||
default: |
|||
obj.cmd = 2; |
|||
} |
|||
|
|||
obj.inverter = get_selected_iv(); |
|||
obj.tx_request = 81; |
|||
getAjax("/api/ctrl", ctrlCb, "POST", JSON.stringify(obj)); |
|||
}); |
|||
|
|||
document.getElementById("sendpwrlim").addEventListener("click", function() { |
|||
var val = parseInt(document.getElementsByName('pwrlimval')[0].value); |
|||
var ctrl = parseInt(document.getElementsByName('pwrlimcntrl')[0].value); |
|||
|
|||
if((ctrl == 1 || ctrl == 257) && unit < 2) unit = 2; |
|||
if(isNaN(val) || isNaN(ctrl)) |
|||
{ |
|||
var tmp = (isNaN(val)) ? "Value" : "Unit"; |
|||
document.getElementById("result").textContent = tmp + " is missing"; |
|||
return; |
|||
} |
|||
|
|||
var obj = new Object(); |
|||
obj.inverter = get_selected_iv(); |
|||
obj.cmd = 11; |
|||
obj.tx_request = 81; |
|||
obj.payload = [val, ctrl]; |
|||
getAjax("/api/ctrl", ctrlCb, "POST", JSON.stringify(obj)); |
|||
}); |
|||
|
|||
getAjax("/api/setup", parse); |
|||
</script> |
|||
</body> |
|||
</html> |
@ -0,0 +1,33 @@ |
|||
<!doctype html> |
|||
<html> |
|||
<head> |
|||
<title>Update</title> |
|||
<link rel="stylesheet" type="text/css" href="style.css"/> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
|||
<script type="text/javascript" src="api.js"></script> |
|||
</head> |
|||
<body> |
|||
<h1>Update</h1> |
|||
<div id="content" class="content"> |
|||
<div> |
|||
Make sure that you have noted all your settings before starting an update. New versions may have changed their memory layout which can break your existing settings. |
|||
</div> |
|||
<br/><br/> |
|||
<form method="POST" action="/update" enctype="multipart/form-data" accept-charset="utf-8"> |
|||
<input type="file" name="update"><input type="submit" value="Update"> |
|||
</form> |
|||
</div> |
|||
<div id="footer"> |
|||
<p class="left">© 2022</p> |
|||
<p class="left"><a href="/">Home</a></p> |
|||
<p class="right" id="version"></p> |
|||
</div> |
|||
<script type="text/javascript"> |
|||
function parseSys(obj) { |
|||
document.getElementById("version").innerHTML = "Git SHA: " + obj["build"] + " :: " + obj["version"]; |
|||
} |
|||
|
|||
getAjax("/api/system", parseSys); |
|||
</script> |
|||
</body> |
|||
</html> |
@ -1,43 +1,118 @@ |
|||
<!doctype html> |
|||
<html> |
|||
<head> |
|||
<title>Index - {DEVICE}</title> |
|||
<title>Live</title> |
|||
<link rel="stylesheet" type="text/css" href="style.css"/> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
|||
<meta name="apple-mobile-web-app-capable" content="yes"> |
|||
<script type="text/javascript"> |
|||
getAjax('/livedata', 'livedata'); |
|||
window.setInterval("getAjax('/livedata', 'livedata')", {JS_TS}); |
|||
|
|||
function getAjax(url, resid) { |
|||
var http = null; |
|||
http = new XMLHttpRequest(); |
|||
if(http != null) { |
|||
http.open("GET", url, true); |
|||
http.onreadystatechange = print; |
|||
http.send(null); |
|||
} |
|||
|
|||
function print() { |
|||
if(http.readyState == 4) { |
|||
document.getElementById(resid).innerHTML = http.responseText; |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
<style type="text/css"> |
|||
</style> |
|||
<script type="text/javascript" src="api.js"></script> |
|||
</head> |
|||
<body> |
|||
<h1>AHOY - {DEVICE}</h1> |
|||
<h1>AHOY</h1> |
|||
<div id="content" class="content"> |
|||
<div id="livedata"></div> |
|||
<p>Every {TS}seconds the values are updated</p> |
|||
<div id="live"></div> |
|||
<p>Every <span id="refresh"></span> seconds the values are updated</p> |
|||
</div> |
|||
<div id="footer"> |
|||
<p class="left">© 2022</p> |
|||
<p class="left"><a href="/">Home</a></p> |
|||
<p class="right">AHOY :: {VERSION}</p> |
|||
<p class="right" id="version"></p> |
|||
</div> |
|||
<script type="text/javascript"> |
|||
var intervalSet = false; |
|||
function parseSys(obj) { |
|||
document.getElementById("version").innerHTML = "Git SHA: " + obj["build"] + " :: " + obj["version"]; |
|||
} |
|||
|
|||
function parseIv(obj, root) { |
|||
var ivHtml = []; |
|||
|
|||
var tDiv = div(["ch-all", "iv"]); |
|||
tDiv.appendChild(span("Total", ["head"])); |
|||
var total = new Array(root.ch0_fld_names.length).fill(0); |
|||
if(obj.length > 1) |
|||
ivHtml.push(tDiv); |
|||
|
|||
for(var iv of obj) { |
|||
main = div(["iv"]); |
|||
var ch0 = div(["ch-iv"]); |
|||
var limit = iv["power_limit_read"] + "%"; |
|||
if(limit == "65535%") |
|||
limit = "n/a"; |
|||
ch0.appendChild(span(iv["name"] + " Limit " + limit + " | last Alarm: " + iv["last_alarm"], ["head"])); |
|||
|
|||
for(var j = 0; j < root.ch0_fld_names.length; j++) { |
|||
var val = Math.round(iv["ch"][0][j] * 100) / 100; |
|||
if(val > 0) { |
|||
var sub = div(["subgrp"]); |
|||
sub.appendChild(span(val + " " + span(root["ch0_fld_units"][j], ["unit"]).innerHTML, ["value"])); |
|||
sub.appendChild(span(root["ch0_fld_names"][j], ["info"])); |
|||
ch0.appendChild(sub); |
|||
|
|||
switch(j) { |
|||
case 2: total[j] += val; break; // P_AC |
|||
case 6: total[j] += val; break; // YieldTotal |
|||
case 7: total[j] += val; break; // YieldDay |
|||
case 8: total[j] += val; break; // P_DC |
|||
case 10: total[j] += val; break; // Q_AC |
|||
} |
|||
} |
|||
} |
|||
main.appendChild(ch0); |
|||
|
|||
|
|||
for(var i = 1; i < (iv["channels"] + 1); i++) { |
|||
var ch = div(["ch"]); |
|||
ch.appendChild(span(("" == iv["ch_names"][i]) ? ("CHANNEL " + i) : iv["ch_names"][i], ["head"])); |
|||
|
|||
for(var j = 0; j < root.fld_names.length; j++) { |
|||
var val = Math.round(iv["ch"][i][j] * 100) / 100; |
|||
if(val > 0) { |
|||
ch.appendChild(span(val + " " + span(root["fld_units"][j], ["unit"]).innerHTML, ["value"])); |
|||
ch.appendChild(span(root["fld_names"][j], ["info"])); |
|||
} |
|||
} |
|||
main.appendChild(ch); |
|||
} |
|||
|
|||
var ts = div(["ts"]); |
|||
var date = new Date(iv["ts_last_success"] * 1000); |
|||
ts.innerHTML = "Last received data requested at: " + date.toLocaleString('de-DE', {timeZone: 'UTC'}); |
|||
main.appendChild(ts); |
|||
ivHtml.push(main); |
|||
} |
|||
|
|||
// total |
|||
if(obj.length > 1) { |
|||
for(var j = 0; j < root.ch0_fld_names.length; j++) { |
|||
var val = total[j]; |
|||
if(val > 0) { |
|||
var sub = div(["subgrp"]); |
|||
sub.appendChild(span(val + " " + span(root["ch0_fld_units"][j], ["unit"]).innerHTML, ["value"])); |
|||
sub.appendChild(span(root["ch0_fld_names"][j], ["info"])); |
|||
tDiv.appendChild(sub); |
|||
} |
|||
} |
|||
} |
|||
|
|||
document.getElementById("live").replaceChildren(...ivHtml); |
|||
} |
|||
|
|||
function parse(obj) { |
|||
if(null != obj) { |
|||
parseSys(obj["system"]); |
|||
parseIv(obj["inverter"], obj); |
|||
document.getElementById("refresh").innerHTML = obj["refresh_interval"]; |
|||
if(false == intervalSet) { |
|||
window.setInterval("getAjax('/api/live', parse)", obj["refresh_interval"] * 1000); |
|||
intervalSet = true; |
|||
} |
|||
} |
|||
else |
|||
document.getElementById("refresh").innerHTML = "n/a"; |
|||
} |
|||
|
|||
getAjax("/api/live", parse); |
|||
</script> |
|||
</body> |
|||
</html> |
|||
|
@ -0,0 +1,415 @@ |
|||
//-----------------------------------------------------------------------------
|
|||
// 2022 Ahoy, https://www.mikrocontroller.net/topic/525778
|
|||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
|||
//-----------------------------------------------------------------------------
|
|||
|
|||
#if defined(ESP32) && defined(F) |
|||
#undef F |
|||
#define F(sl) (sl) |
|||
#endif |
|||
|
|||
#include "webApi.h" |
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
webApi::webApi(AsyncWebServer *srv, app *app, sysConfig_t *sysCfg, config_t *config, statistics_t *stat, char version[]) { |
|||
mSrv = srv; |
|||
mApp = app; |
|||
mSysCfg = sysCfg; |
|||
mConfig = config; |
|||
mStat = stat; |
|||
mVersion = version; |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void webApi::setup(void) { |
|||
mSrv->on("/api", HTTP_GET, std::bind(&webApi::onApi, this, std::placeholders::_1)); |
|||
mSrv->on("/api", HTTP_POST, std::bind(&webApi::onApiPost, this, std::placeholders::_1)).onBody( |
|||
std::bind(&webApi::onApiPostBody, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void webApi::loop(void) { |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void webApi::onApi(AsyncWebServerRequest *request) { |
|||
AsyncJsonResponse* response = new AsyncJsonResponse(false, 8192); |
|||
JsonObject root = response->getRoot(); |
|||
|
|||
Inverter<> *iv = mApp->mSys->getInverterByPos(0, false); |
|||
String path = request->url().substring(5); |
|||
if(path == "system") getSystem(root); |
|||
else if(path == "statistics") getStatistics(root); |
|||
else if(path == "inverter/list") getInverterList(root); |
|||
else if(path == "index") getIndex(root); |
|||
else if(path == "setup") getSetup(root); |
|||
else if(path == "live") getLive(root); |
|||
else if(path == "record/info") getRecord(root, iv->getRecordStruct(InverterDevInform_All)); |
|||
else if(path == "record/alarm") getRecord(root, iv->getRecordStruct(AlarmData)); |
|||
else if(path == "record/config") getRecord(root, iv->getRecordStruct(SystemConfigPara)); |
|||
else if(path == "record/live") getRecord(root, iv->getRecordStruct(RealTimeRunData_Debug)); |
|||
else |
|||
getNotFound(root, F("http://") + request->host() + F("/api/")); |
|||
|
|||
response->addHeader("Access-Control-Allow-Origin", "*"); |
|||
response->addHeader("Access-Control-Allow-Headers", "content-type"); |
|||
response->setLength(); |
|||
request->send(response); |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void webApi::onApiPost(AsyncWebServerRequest *request) { |
|||
DPRINTLN(DBG_VERBOSE, "onApiPost"); |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void webApi::onApiPostBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) { |
|||
DPRINTLN(DBG_VERBOSE, "onApiPostBody"); |
|||
DynamicJsonDocument json(200); |
|||
AsyncJsonResponse* response = new AsyncJsonResponse(false, 200); |
|||
JsonObject root = response->getRoot(); |
|||
|
|||
DeserializationError err = deserializeJson(json, (const char *)data); |
|||
root[F("success")] = (err) ? false : true; |
|||
if(!err) { |
|||
String path = request->url().substring(5); |
|||
if(path == "ctrl") |
|||
root[F("success")] = setCtrl(json, root); |
|||
else if(path == "setup") |
|||
root[F("success")] = setSetup(json, root); |
|||
else { |
|||
root[F("success")] = false; |
|||
root[F("error")] = "Path not found: " + path; |
|||
} |
|||
} |
|||
else { |
|||
switch (err.code()) { |
|||
case DeserializationError::Ok: break; |
|||
case DeserializationError::InvalidInput: root[F("error")] = F("Invalid input"); break; |
|||
case DeserializationError::NoMemory: root[F("error")] = F("Not enough memory"); break; |
|||
default: root[F("error")] = F("Deserialization failed"); break; |
|||
} |
|||
} |
|||
|
|||
response->setLength(); |
|||
request->send(response); |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void webApi::getNotFound(JsonObject obj, String url) { |
|||
JsonObject ep = obj.createNestedObject("avail_endpoints"); |
|||
ep[F("system")] = url + F("system"); |
|||
ep[F("statistics")] = url + F("statistics"); |
|||
ep[F("inverter/list")] = url + F("inverter/list"); |
|||
ep[F("index")] = url + F("index"); |
|||
ep[F("setup")] = url + F("setup"); |
|||
ep[F("live")] = url + F("live"); |
|||
ep[F("record/info")] = url + F("record/info"); |
|||
ep[F("record/alarm")] = url + F("record/alarm"); |
|||
ep[F("record/config")] = url + F("record/config"); |
|||
ep[F("record/live")] = url + F("record/live"); |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void webApi::getSystem(JsonObject obj) { |
|||
obj[F("ssid")] = mSysCfg->stationSsid; |
|||
obj[F("device_name")] = mSysCfg->deviceName; |
|||
obj[F("version")] = String(mVersion); |
|||
obj[F("build")] = String(AUTO_GIT_HASH); |
|||
obj[F("ts_uptime")] = mApp->getUptime(); |
|||
obj[F("ts_now")] = mApp->getTimestamp(); |
|||
obj[F("wifi_rssi")] = WiFi.RSSI(); |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void webApi::getStatistics(JsonObject obj) { |
|||
obj[F("rx_success")] = mStat->rxSuccess; |
|||
obj[F("rx_fail")] = mStat->rxFail; |
|||
obj[F("rx_fail_answer")] = mStat->rxFailNoAnser; |
|||
obj[F("frame_cnt")] = mStat->frmCnt; |
|||
obj[F("tx_cnt")] = mApp->mSys->Radio.mSendCnt; |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void webApi::getInverterList(JsonObject obj) { |
|||
JsonArray invArr = obj.createNestedArray(F("inverter")); |
|||
|
|||
Inverter<> *iv; |
|||
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) { |
|||
iv = mApp->mSys->getInverterByPos(i); |
|||
if(NULL != iv) { |
|||
JsonObject obj2 = invArr.createNestedObject(); |
|||
obj2[F("id")] = i; |
|||
obj2[F("name")] = String(iv->name); |
|||
obj2[F("serial")] = String(iv->serial.u64, HEX); |
|||
obj2[F("channels")] = iv->channels; |
|||
obj2[F("version")] = String(iv->fwVersion); |
|||
|
|||
for(uint8_t j = 0; j < iv->channels; j ++) { |
|||
obj2[F("ch_max_power")][j] = iv->chMaxPwr[j]; |
|||
obj2[F("ch_name")][j] = iv->chName[j]; |
|||
} |
|||
} |
|||
} |
|||
obj[F("interval")] = String(mConfig->sendInterval); |
|||
obj[F("retries")] = String(mConfig->maxRetransPerPyld); |
|||
obj[F("max_num_inverters")] = MAX_NUM_INVERTERS; |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void webApi::getMqtt(JsonObject obj) { |
|||
obj[F("broker")] = String(mConfig->mqtt.broker); |
|||
obj[F("port")] = String(mConfig->mqtt.port); |
|||
obj[F("user")] = String(mConfig->mqtt.user); |
|||
obj[F("pwd")] = (strlen(mConfig->mqtt.pwd) > 0) ? F("{PWD}") : String(""); |
|||
obj[F("topic")] = String(mConfig->mqtt.topic); |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void webApi::getNtp(JsonObject obj) { |
|||
obj[F("addr")] = String(mConfig->ntpAddr); |
|||
obj[F("port")] = String(mConfig->ntpPort); |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void webApi::getPinout(JsonObject obj) { |
|||
obj[F("cs")] = mConfig->pinCs; |
|||
obj[F("ce")] = mConfig->pinCe; |
|||
obj[F("irq")] = mConfig->pinIrq; |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void webApi::getRadio(JsonObject obj) { |
|||
obj[F("power_level")] = mConfig->amplifierPower; |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void webApi::getSerial(JsonObject obj) { |
|||
obj[F("interval")] = (uint16_t)mConfig->serialInterval; |
|||
obj[F("show_live_data")] = mConfig->serialShowIv; |
|||
obj[F("debug")] = mConfig->serialDebug; |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void webApi::getIndex(JsonObject obj) { |
|||
getSystem(obj.createNestedObject(F("system"))); |
|||
getStatistics(obj.createNestedObject(F("statistics"))); |
|||
obj["refresh_interval"] = SEND_INTERVAL; |
|||
|
|||
JsonArray inv = obj.createNestedArray(F("inverter")); |
|||
Inverter<> *iv; |
|||
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) { |
|||
iv = mApp->mSys->getInverterByPos(i); |
|||
if(NULL != iv) { |
|||
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); |
|||
JsonObject invObj = inv.createNestedObject(); |
|||
invObj[F("id")] = i; |
|||
invObj[F("name")] = String(iv->name); |
|||
invObj[F("version")] = String(iv->fwVersion); |
|||
invObj[F("is_avail")] = iv->isAvailable(mApp->getTimestamp(), rec); |
|||
invObj[F("is_producing")] = iv->isProducing(mApp->getTimestamp(), rec); |
|||
invObj[F("ts_last_success")] = iv->getLastTs(rec); |
|||
} |
|||
} |
|||
|
|||
JsonArray warn = obj.createNestedArray(F("warnings")); |
|||
if(!mApp->mSys->Radio.isChipConnected()) |
|||
warn.add(F("your NRF24 module can't be reached, check the wiring and pinout")); |
|||
if(!mApp->mqttIsConnected()) |
|||
warn.add(F("MQTT is not connected")); |
|||
|
|||
JsonArray info = obj.createNestedArray(F("infos")); |
|||
if(mApp->getRebootRequestState()) |
|||
info.add(F("reboot your ESP to apply all your configuration changes!")); |
|||
if(!mApp->getSettingsValid()) |
|||
info.add(F("your settings are invalid")); |
|||
if(mApp->mqttIsConnected()) |
|||
info.add(F("MQTT is connected")); |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void webApi::getSetup(JsonObject obj) { |
|||
getSystem(obj.createNestedObject(F("system"))); |
|||
getInverterList(obj.createNestedObject(F("inverter"))); |
|||
getMqtt(obj.createNestedObject(F("mqtt"))); |
|||
getNtp(obj.createNestedObject(F("ntp"))); |
|||
getPinout(obj.createNestedObject(F("pinout"))); |
|||
getRadio(obj.createNestedObject(F("radio"))); |
|||
getSerial(obj.createNestedObject(F("serial"))); |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void webApi::getLive(JsonObject obj) { |
|||
getSystem(obj.createNestedObject(F("system"))); |
|||
JsonArray invArr = obj.createNestedArray(F("inverter")); |
|||
obj["refresh_interval"] = SEND_INTERVAL; |
|||
|
|||
uint8_t list[] = {FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_PF, FLD_T, FLD_YT, FLD_YD, FLD_PDC, FLD_EFF, FLD_Q}; |
|||
|
|||
Inverter<> *iv; |
|||
uint8_t pos; |
|||
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) { |
|||
iv = mApp->mSys->getInverterByPos(i); |
|||
if(NULL != iv) { |
|||
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); |
|||
JsonObject obj2 = invArr.createNestedObject(); |
|||
obj2[F("name")] = String(iv->name); |
|||
obj2[F("channels")] = iv->channels; |
|||
obj2[F("power_limit_read")] = round3(iv->actPowerLimit); |
|||
obj2[F("last_alarm")] = String(iv->lastAlarmMsg); |
|||
obj2[F("ts_last_success")] = rec->ts; |
|||
|
|||
JsonArray ch = obj2.createNestedArray("ch"); |
|||
JsonArray ch0 = ch.createNestedArray(); |
|||
obj2[F("ch_names")][0] = "AC"; |
|||
for (uint8_t fld = 0; fld < sizeof(list); fld++) { |
|||
pos = (iv->getPosByChFld(CH0, list[fld], rec)); |
|||
ch0[fld] = (0xff != pos) ? round3(iv->getValue(pos, rec)) : 0.0; |
|||
obj[F("ch0_fld_units")][fld] = (0xff != pos) ? String(iv->getUnit(pos, rec)) : notAvail; |
|||
obj[F("ch0_fld_names")][fld] = (0xff != pos) ? String(iv->getFieldName(pos, rec)) : notAvail; |
|||
} |
|||
|
|||
for(uint8_t j = 1; j <= iv->channels; j ++) { |
|||
obj2[F("ch_names")][j] = String(iv->chName[j-1]); |
|||
JsonArray cur = ch.createNestedArray(); |
|||
for (uint8_t k = 0; k < 6; k++) { |
|||
switch(k) { |
|||
default: pos = (iv->getPosByChFld(j, FLD_UDC, rec)); break; |
|||
case 1: pos = (iv->getPosByChFld(j, FLD_IDC, rec)); break; |
|||
case 2: pos = (iv->getPosByChFld(j, FLD_PDC, rec)); break; |
|||
case 3: pos = (iv->getPosByChFld(j, FLD_YD, rec)); break; |
|||
case 4: pos = (iv->getPosByChFld(j, FLD_YT, rec)); break; |
|||
case 5: pos = (iv->getPosByChFld(j, FLD_IRR, rec)); break; |
|||
} |
|||
cur[k] = (0xff != pos) ? round3(iv->getValue(pos, rec)) : 0.0; |
|||
if(1 == j) { |
|||
obj[F("fld_units")][k] = (0xff != pos) ? String(iv->getUnit(pos, rec)) : notAvail; |
|||
obj[F("fld_names")][k] = (0xff != pos) ? String(iv->getFieldName(pos, rec)) : notAvail; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void webApi::getRecord(JsonObject obj, record_t<> *rec) { |
|||
JsonArray invArr = obj.createNestedArray(F("inverter")); |
|||
|
|||
Inverter<> *iv; |
|||
uint8_t pos; |
|||
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) { |
|||
iv = mApp->mSys->getInverterByPos(i); |
|||
if(NULL != iv) { |
|||
JsonArray obj2 = invArr.createNestedArray(); |
|||
for(uint8_t j = 0; j < rec->length; j++) { |
|||
byteAssign_t *assign = iv->getByteAssign(j, rec); |
|||
pos = (iv->getPosByChFld(assign->ch, assign->fieldId, rec)); |
|||
obj2[j]["fld"] = (0xff != pos) ? String(iv->getFieldName(pos, rec)) : notAvail; |
|||
obj2[j]["unit"] = (0xff != pos) ? String(iv->getUnit(pos, rec)) : notAvail; |
|||
obj2[j]["val"] = (0xff != pos) ? String(iv->getValue(pos, rec)) : notAvail; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
bool webApi::setCtrl(DynamicJsonDocument jsonIn, JsonObject jsonOut) { |
|||
uint8_t cmd = jsonIn[F("cmd")]; |
|||
|
|||
// Todo: num is the inverter number 0-3. For better display in DPRINTLN
|
|||
uint8_t num = jsonIn[F("inverter")]; |
|||
uint8_t tx_request = jsonIn[F("tx_request")]; |
|||
|
|||
if(TX_REQ_DEVCONTROL == tx_request) |
|||
{ |
|||
DPRINTLN(DBG_INFO, F("devcontrol [") + String(num) + F("], cmd: 0x") + String(cmd, HEX)); |
|||
|
|||
Inverter<> *iv = getInverter(jsonIn, jsonOut); |
|||
JsonArray payload = jsonIn[F("payload")].as<JsonArray>(); |
|||
|
|||
if(NULL != iv) |
|||
{ |
|||
switch (cmd) |
|||
{ |
|||
case TurnOn: |
|||
iv->devControlCmd = TurnOn; |
|||
iv->devControlRequest = true; |
|||
break; |
|||
case TurnOff: |
|||
iv->devControlCmd = TurnOff; |
|||
iv->devControlRequest = true; |
|||
break; |
|||
case CleanState_LockAndAlarm: |
|||
iv->devControlCmd = CleanState_LockAndAlarm; |
|||
iv->devControlRequest = true; |
|||
break; |
|||
case Restart: |
|||
iv->devControlCmd = Restart; |
|||
iv->devControlRequest = true; |
|||
break; |
|||
case ActivePowerContr: |
|||
iv->devControlCmd = ActivePowerContr; |
|||
iv->devControlRequest = true; |
|||
iv->powerLimit[0] = payload[0]; |
|||
iv->powerLimit[1] = payload[1]; |
|||
break; |
|||
default: |
|||
jsonOut["error"] = "unknown 'cmd' = " + String(cmd); |
|||
return false; |
|||
} |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
else { |
|||
jsonOut[F("error")] = F("unknown 'tx_request'"); |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
bool webApi::setSetup(DynamicJsonDocument jsonIn, JsonObject jsonOut) { |
|||
if(F("set_time") == jsonIn[F("cmd")]) |
|||
mApp->setTimestamp(jsonIn[F("ts")]); |
|||
else if(F("sync_ntp") == jsonIn[F("cmd")]) |
|||
mApp->setTimestamp(0); // 0: update ntp flag
|
|||
else { |
|||
jsonOut[F("error")] = F("unknown cmd"); |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
Inverter<> *webApi::getInverter(DynamicJsonDocument jsonIn, JsonObject jsonOut) { |
|||
uint8_t id = jsonIn[F("inverter")]; |
|||
Inverter<> *iv = mApp->mSys->getInverterByPos(id); |
|||
if(NULL == iv) |
|||
jsonOut[F("error")] = F("inverter index to high: ") + String(id); |
|||
return iv; |
|||
} |
@ -0,0 +1,62 @@ |
|||
#ifndef __WEB_API_H__ |
|||
#define __WEB_API_H__ |
|||
|
|||
#include "dbg.h" |
|||
#ifdef ESP32 |
|||
#include "AsyncTCP.h" |
|||
#else |
|||
#include "ESPAsyncTCP.h" |
|||
#endif |
|||
#include "ESPAsyncWebServer.h" |
|||
#include "AsyncJson.h" |
|||
#include "app.h" |
|||
|
|||
|
|||
class app; |
|||
|
|||
class webApi { |
|||
public: |
|||
webApi(AsyncWebServer *srv, app *app, sysConfig_t *sysCfg, config_t *config, statistics_t *stat, char version[]); |
|||
|
|||
void setup(void); |
|||
void loop(void); |
|||
|
|||
private: |
|||
void onApi(AsyncWebServerRequest *request); |
|||
void onApiPost(AsyncWebServerRequest *request); |
|||
void onApiPostBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total); |
|||
void getNotFound(JsonObject obj, String url); |
|||
|
|||
void getSystem(JsonObject obj); |
|||
void getStatistics(JsonObject obj); |
|||
void getInverterList(JsonObject obj); |
|||
void getMqtt(JsonObject obj); |
|||
void getNtp(JsonObject obj); |
|||
void getPinout(JsonObject obj); |
|||
void getRadio(JsonObject obj); |
|||
void getSerial(JsonObject obj); |
|||
|
|||
void getIndex(JsonObject obj); |
|||
void getSetup(JsonObject obj); |
|||
void getLive(JsonObject obj); |
|||
void getRecord(JsonObject obj, record_t<> *rec); |
|||
|
|||
bool setCtrl(DynamicJsonDocument jsonIn, JsonObject jsonOut); |
|||
bool setSetup(DynamicJsonDocument jsonIn, JsonObject jsonOut); |
|||
|
|||
Inverter<> *getInverter(DynamicJsonDocument jsonIn, JsonObject jsonOut); |
|||
|
|||
double round3(double value) { |
|||
return (int)(value * 1000 + 0.5) / 1000.0; |
|||
} |
|||
|
|||
AsyncWebServer *mSrv; |
|||
app *mApp; |
|||
|
|||
config_t *mConfig; |
|||
sysConfig_t *mSysCfg; |
|||
statistics_t *mStat; |
|||
char *mVersion; |
|||
}; |
|||
|
|||
#endif /*__WEB_API_H__*/ |
Loading…
Reference in new issue