mirror of https://github.com/lumapu/ahoy.git
				
				
			
							committed by
							
								 GitHub
								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> | <!doctype html> | ||||
| <html> | <html> | ||||
|     <head> |     <head> | ||||
|         <title>Index - {DEVICE}</title> |         <title>Index</title> | ||||
|         <link rel="stylesheet" type="text/css" href="style.css"/> |         <link rel="stylesheet" type="text/css" href="style.css"/> | ||||
|         <meta name="viewport" content="width=device-width, initial-scale=1"> |         <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|         <script type="text/javascript"> |         <script type="text/javascript" src="api.js"></script> | ||||
|             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> |  | ||||
|     </head> |     </head> | ||||
|     <body> |     <body> | ||||
|         <h1>AHOY - {DEVICE}</h1> |         <h1>AHOY</h1> | ||||
|         <div id="content" class="content"> |         <div id="content" class="content"> | ||||
|             <p> |             <p> | ||||
|                 <a href="/visualization">Visualization</a><br/> |                 <a href="/live">Visualization</a><br/> | ||||
|                 <br/> |                 <br/> | ||||
|                 <a href="/setup">Setup</a><br/> |                 <a href="/setup">Setup</a><br/> | ||||
|  |                 <a href="/serial">Webserial & Commands</a><br/> | ||||
|             </p> |             </p> | ||||
|             <p><span class="des">Uptime: </span><span id="uptime"></span></p> |             <p><span class="des">Uptime: </span><span id="uptime"></span></p> | ||||
|             <p><span class="des">Statistics: </span><pre id="cmds"></pre></p> |             <p><span class="des">ESP-Time: </span><span id="date"></span></p> | ||||
|             <p>Every {TS}seconds the values are updated</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"> |             <div id="note"> | ||||
|                 This project was started from <a href="https://www.mikrocontroller.net/topic/525778" target="_blank">this discussion. (Mikrocontroller.net)</a><br/> |                 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/> |                 <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/> |                 <br/> | ||||
|                 Discuss with us on <a href="https://discord.gg/WzhxEY62mB">Discord</a> |                 Discuss with us on <a href="https://discord.gg/WzhxEY62mB">Discord</a> | ||||
|                 <br/> |                 <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/> |                 <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> |         </div> | ||||
|         <div id="footer"> |         <div id="footer"> | ||||
|             <p class="left">© 2022</p> |             <p class="left">© 2022</p> | ||||
|             <p class="left"><a href="/update">Update Firmware</a></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"><a href="/reboot">Reboot</a></p> | ||||
|             <p class="right">Git SHA: {BUILD}</p> |             <p class="right"><a href="/api">REST API</a></p> | ||||
|         </div> |         </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> |     </body> | ||||
| </html> | </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> | <!doctype html> | ||||
| <html> | <html> | ||||
|     <head> |     <head> | ||||
|         <title>Index - {DEVICE}</title> |         <title>Live</title> | ||||
|         <link rel="stylesheet" type="text/css" href="style.css"/> |         <link rel="stylesheet" type="text/css" href="style.css"/> | ||||
|         <meta name="viewport" content="width=device-width, initial-scale=1"> |         <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|         <meta name="apple-mobile-web-app-capable" content="yes"> |         <meta name="apple-mobile-web-app-capable" content="yes"> | ||||
|         <script type="text/javascript"> |         <script type="text/javascript" src="api.js"></script> | ||||
|             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> |  | ||||
|     </head> |     </head> | ||||
|     <body> |     <body> | ||||
|         <h1>AHOY - {DEVICE}</h1> |         <h1>AHOY</h1> | ||||
|         <div id="content" class="content"> |         <div id="content" class="content"> | ||||
|             <div id="livedata"></div> |             <div id="live"></div> | ||||
|             <p>Every {TS}seconds the values are updated</p> |             <p>Every <span id="refresh"></span> seconds the values are updated</p> | ||||
|         </div> |         </div> | ||||
|         <div id="footer"> |         <div id="footer"> | ||||
|             <p class="left">© 2022</p> |             <p class="left">© 2022</p> | ||||
|             <p class="left"><a href="/">Home</a></p> |             <p class="left"><a href="/">Home</a></p> | ||||
|             <p class="right">AHOY :: {VERSION}</p> |             <p class="right" id="version"></p> | ||||
|         </div> |         </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> |     </body> | ||||
| </html> | </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