diff --git a/WEB-SERVER.md b/WEB-SERVER.md index 6bd2a3c..d19ba7e 100644 --- a/WEB-SERVER.md +++ b/WEB-SERVER.md @@ -38,7 +38,7 @@ Installation steps: - 1: open EspLinkWebApp sample from Arduino - 2: upload the code onto an Arduino Nano/Uno - 3: jump to the Web Server page on esp-link UI - - 4: upload web-page.espfs.img ( arduino/libraries/EspLink/examples/EspLinkWebApp/web-page.espfs.img ) + - 4: upload the 3 HTML files in the samples ( multiple select from arduino/libraries/EspLink/examples/EspLinkWebApp/ ) - 5: jump to LED/User/Voltage pages - 6: try out different settings - + \ No newline at end of file diff --git a/createEspFs.pl b/createEspFs.pl deleted file mode 100755 index 1612098..0000000 --- a/createEspFs.pl +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/perl - -use strict; -use Data::Dumper; - -my $dir = shift @ARGV; -my $out = shift @ARGV; - -my $espfs = ''; - -my @structured = read_dir_structure($dir, ""); - -for my $file (@structured) -{ - my $flags = 0; - my $name = $file; - my $compression = 0; - - if( $name =~ /\.gz$/ ) - { - $flags |= 2; - $name =~ s/\.gz$//; - } - - my $head = 'esp-link
'; - - open IF, "<", "$dir/$file" or die "Can't read file: $!"; - my @fc = ; - close(IF); - my $cnt = join("", @fc); - - if( $name =~ /\.html$/ ) - { - if( ! ( $flags & 2 ) ) - { - $cnt = "$head$cnt"; - } - else - { - printf("TODO: prepend headers to GZipped HTML content!\n"); - } - } - - $name .= chr(0); - $name .= chr(0) while( (length($name) & 3) != 0 ); - - my $size = length($cnt); - - $espfs .= "ESfs"; - $espfs .= chr($flags); - $espfs .= chr($compression); - $espfs .= chr( length($name) & 255 ); - $espfs .= chr( length($name) / 256 ); - $espfs .= chr( $size & 255 ); - $espfs .= chr( ( $size / 0x100 ) & 255 ); - $espfs .= chr( ( $size / 0x10000 ) & 255 ); - $espfs .= chr( ( $size / 0x1000000 ) & 255 ); - $espfs .= chr( $size & 255 ); - $espfs .= chr( ( $size / 0x100 ) & 255 ); - $espfs .= chr( ( $size / 0x10000 ) & 255 ); - $espfs .= chr( ( $size / 0x1000000 ) & 255 ); - - $espfs .= $name; - - - - $cnt .= chr(0) while( (length($cnt) & 3) != 0 ); - $espfs .= $cnt; -} - -$espfs .= "ESfs"; -$espfs .= chr(1); -for(my $i=0; $i < 11; $i++) -{ - $espfs .= chr(0); -} - -open FH, ">", $out or die "Can't open file for write, $!"; -print FH $espfs; -close(FH); - - -exit(0); - -sub read_dir_structure -{ - my ($dir, $base) = @_; - - my @files; - - opendir my $dh, $dir or die "Could not open '$dir' for reading: $!\n"; - - while (my $file = readdir $dh) { - if ($file eq '.' or $file eq '..') { - next; - } - - my $path = "$dir/$file"; - if( -d "$path" ) - { - my @sd = read_dir_structure($path, "$base/$file"); - push @files, @sd ; - } - else - { - push @files, "$base/$file"; - } - } - - close( $dh ); - - $_ =~ s/^\/// for(@files); - return @files; -} diff --git a/esp-link/cgiwebserversetup.c b/esp-link/cgiwebserversetup.c index 2a25950..1d1d2e6 100644 --- a/esp-link/cgiwebserversetup.c +++ b/esp-link/cgiwebserversetup.c @@ -9,8 +9,9 @@ #include "config.h" #include "web-server.h" -int upload_offset = 0; // flash offset where to store page upload -int html_header_len = 0; // HTML header length (for uploading HTML files) +int header_position = 0; // flash offset of the file header +int upload_position = 0; // flash offset where to store page upload +int html_header_len = 0; // length of the HTML header added to the file // this is the header to add if user uploads HTML file const char * HTML_HEADER = "esp-link" @@ -18,127 +19,157 @@ const char * HTML_HEADER = "esp-link "
"; +// this method is for flash writing and erasing the page +// write is incremental, so whenever a page border is reached, the next page will be erased +int ICACHE_FLASH_ATTR webServerSetupWriteFlash( int addr, void * data, int length ) +{ + int end_addr = addr + length; + if( end_addr >= getUserPageSectionEnd() ) + { + os_printf("No more space in the flash!\n"); + return 1; + } + + void * free_ptr = 0; + if(( length & 3 ) != 0 ) // ESP8266 always writes 4 bytes, so the remaining ones should be oxFF-ed out + { + free_ptr = os_malloc(length + 4); + os_memset(free_ptr, 0xFF, length + 4); + os_memcpy(free_ptr, data, length); + data = free_ptr; + } + + int ptr = 0; + while( addr < end_addr ) + { + if (addr % SPI_FLASH_SEC_SIZE == 0){ + spi_flash_erase_sector(addr/SPI_FLASH_SEC_SIZE); + } + + int max = (addr | (SPI_FLASH_SEC_SIZE - 1)) + 1; + int len = end_addr - addr; + if( end_addr > max ) + len = max - addr; + + spi_flash_write( addr, (uint32_t *)((char *)data + ptr), len ); + ptr += len; + addr += len; + } + if( free_ptr != 0 ) + os_free(free_ptr); + return 0; +} + +// debug code +void ICACHE_FLASH_ATTR dumpFlash( int end ) +{ + int dump = getUserPageSectionStart(); + while( dump < end ) + { + char buffer[16]; + spi_flash_read(dump, (uint32_t *)buffer, sizeof(buffer)); + char dmpstr[sizeof(buffer)*3]; + os_sprintf(dmpstr, "%06X: ", dump); + for(int i=0; i < sizeof(buffer); i++ ) + os_sprintf(dmpstr + os_strlen(dmpstr), "%02X ", buffer[i]); + os_printf("%s\n", dmpstr); + dump += sizeof(buffer); + } +} + // multipart callback for uploading user defined pages int ICACHE_FLASH_ATTR webServerSetupMultipartCallback(MultipartCmd cmd, char *data, int dataLen, int position) { switch(cmd) { + case FILE_UPLOAD_START: + upload_position = getUserPageSectionStart(); + header_position = upload_position; + break; case FILE_START: - upload_offset = 0; - html_header_len = 0; - // simple HTML file - if( ( dataLen > 5 ) && ( os_strcmp(data + dataLen - 5, ".html") == 0 ) ) // if the file ends with .html, wrap into an espfs image { - // write the start block on esp-fs - int spi_flash_addr = getUserPageSectionStart(); - spi_flash_erase_sector(spi_flash_addr/SPI_FLASH_SEC_SIZE); + html_header_len = 0; + + // write the starting block on esp-fs EspFsHeader hdr; hdr.magic = 0xFFFFFFFF; // espfs magic is invalid during upload hdr.flags = 0; hdr.compression = 0; - + int len = dataLen + 1; while(( len & 3 ) != 0 ) len++; - + hdr.nameLen = len; hdr.fileLenComp = hdr.fileLenDecomp = 0xFFFFFFFF; - spi_flash_write( spi_flash_addr + upload_offset, (uint32_t *)(&hdr), sizeof(EspFsHeader) ); - upload_offset += sizeof(EspFsHeader); - + header_position = upload_position; + if( webServerSetupWriteFlash( upload_position, (uint32_t *)(&hdr), sizeof(EspFsHeader) ) ) + return 1; + upload_position += sizeof(EspFsHeader); + char nameBuf[len]; os_memset(nameBuf, 0, len); os_memcpy(nameBuf, data, dataLen); - spi_flash_write( spi_flash_addr + upload_offset, (uint32_t *)(nameBuf), len ); - upload_offset += len; - - html_header_len = os_strlen(HTML_HEADER) & ~3; // upload only 4 byte aligned part - char buf[html_header_len]; - os_memcpy(buf, HTML_HEADER, html_header_len); - spi_flash_write( spi_flash_addr + upload_offset, (uint32_t *)(buf), html_header_len ); - upload_offset += html_header_len; - } - break; - case FILE_DATA: - if(( position < 4 ) && (upload_offset == 0)) // for espfs images check the magic number - { - for(int p = position; p < 4; p++ ) + if( webServerSetupWriteFlash( upload_position, (uint32_t *)(nameBuf), len ) ) + return 1; + upload_position += len; + + // add header to HTML files + if( ( dataLen > 5 ) && ( os_strcmp(data + dataLen - 5, ".html") == 0 ) ) // if the file ends with .html, wrap into an espfs image { - if( data[p - position] != ((ESPFS_MAGIC >> (p * 8) ) & 255 ) ) - { - os_printf("Not an espfs image!\n"); + html_header_len = os_strlen(HTML_HEADER) & ~3; // upload only 4 byte aligned part + char buf[html_header_len]; + os_memcpy(buf, HTML_HEADER, html_header_len); + if( webServerSetupWriteFlash( upload_position, (uint32_t *)(buf), html_header_len ) ) return 1; - } - data[p - position] = 0xFF; // espfs magic is invalid during upload + upload_position += html_header_len; } } - - int spi_flash_addr = getUserPageSectionStart() + upload_offset + position; - int spi_flash_end_addr = spi_flash_addr + dataLen; - if( spi_flash_end_addr + dataLen >= getUserPageSectionEnd() ) - { - os_printf("No more space in the flash!\n"); + break; + case FILE_DATA: + if( webServerSetupWriteFlash( upload_position, data, dataLen ) ) return 1; - } - - int ptr = 0; - while( spi_flash_addr < spi_flash_end_addr ) + upload_position += dataLen; + break; + case FILE_DONE: { - if (spi_flash_addr % SPI_FLASH_SEC_SIZE == 0){ - spi_flash_erase_sector(spi_flash_addr/SPI_FLASH_SEC_SIZE); + // write padding after the file + uint8_t pad_cnt = (4 - position) & 3; + if( pad_cnt ) { + uint32_t pad = 0; + if( webServerSetupWriteFlash( upload_position, &pad, pad_cnt ) ) + return 1; + upload_position += pad_cnt; } - - int max = (spi_flash_addr | (SPI_FLASH_SEC_SIZE - 1)) + 1; - int len = spi_flash_end_addr - spi_flash_addr; - if( spi_flash_end_addr > max ) - len = max - spi_flash_addr; - - spi_flash_write( spi_flash_addr, (uint32_t *)(data + ptr), len ); - ptr += len; - spi_flash_addr += len; + + EspFsHeader hdr; + hdr.magic = ESPFS_MAGIC; + hdr.fileLenComp = hdr.fileLenDecomp = position + html_header_len; + + // restore ESPFS magic + spi_flash_write( header_position + ((char *)&hdr.magic - (char*)&hdr), (uint32_t *)&hdr.magic, sizeof(uint32_t) ); + // set file size + spi_flash_write( header_position + ((char *)&hdr.fileLenComp - (char*)&hdr), (uint32_t *)&hdr.fileLenComp, sizeof(uint32_t) ); + spi_flash_write( header_position + ((char *)&hdr.fileLenDecomp - (char*)&hdr), (uint32_t *)&hdr.fileLenDecomp, sizeof(uint32_t) ); } - break; - case FILE_DONE: + case FILE_UPLOAD_DONE: { - if( html_header_len != 0 ) - { - // write the terminating block on esp-fs - int spi_flash_addr = getUserPageSectionStart() + upload_offset + position; + // write the termination block + + EspFsHeader hdr; + hdr.magic = ESPFS_MAGIC; + hdr.flags = 1; + hdr.compression = 0; + hdr.nameLen = 0; + hdr.fileLenComp = hdr.fileLenDecomp = 0; + + if( webServerSetupWriteFlash( upload_position, (uint32_t *)(&hdr), sizeof(EspFsHeader) ) ) + return 1; + upload_position += sizeof(EspFsHeader); - uint32_t pad = 0; - uint8_t pad_cnt = (4 - position) & 3; - if( pad_cnt ) - spi_flash_write( spi_flash_addr, &pad, pad_cnt ); - - spi_flash_addr += pad_cnt; - - // create ESPFS image - EspFsHeader hdr; - hdr.magic = ESPFS_MAGIC; - hdr.flags = 1; - hdr.compression = 0; - hdr.nameLen = 0; - hdr.fileLenComp = hdr.fileLenDecomp = 0; - - spi_flash_write( spi_flash_addr, (uint32_t *)(&hdr), sizeof(EspFsHeader) ); - - uint32_t totallen = html_header_len + position; - - // restore ESPFS magic - spi_flash_write( (int)getUserPageSectionStart(), (uint32_t *)&hdr.magic, sizeof(uint32_t) ); - // set file size - spi_flash_write( (int)getUserPageSectionStart() + 8, &totallen, sizeof(uint32_t) ); - spi_flash_write( (int)getUserPageSectionStart() + 12, &totallen, sizeof(uint32_t) ); - } - else - { - // set espfs magic (set it valid) - uint32_t magic = ESPFS_MAGIC; - spi_flash_write( (int)getUserPageSectionStart(), (uint32_t *)&magic, sizeof(uint32_t) ); - } WEB_Init(); // reload the content } break; diff --git a/html/web-server.html b/html/web-server.html index a5a1d41..8d60ee9 100644 --- a/html/web-server.html +++ b/html/web-server.html @@ -8,7 +8,7 @@ the measurement data.

- The custom web page to upload: + The custom web page to upload:
diff --git a/httpd/multipart.c b/httpd/multipart.c index d709798..427bc70 100644 --- a/httpd/multipart.c +++ b/httpd/multipart.c @@ -4,7 +4,7 @@ #include "multipart.h" #include "cgi.h" -#define BOUNDARY_SIZE 100 +#define BOUNDARY_SIZE 128 typedef enum { STATE_SEARCH_BOUNDARY = 0, // state: searching multipart boundary @@ -227,6 +227,7 @@ int ICACHE_FLASH_ATTR multipartProcessData(MultipartCtx * context, char * bounda context->boundaryBufferPtr -= dataSize; os_memcpy(context->boundaryBuffer, context->boundaryBuffer + dataSize, context->boundaryBufferPtr); } + return 0; } @@ -253,6 +254,9 @@ int ICACHE_FLASH_ATTR multipartProcess(MultipartCtx * context, HttpdConnData * c context->state = STATE_SEARCH_BOUNDARY; multipartAllocBoundaryBuffer(context); + + if( context->callBack( FILE_UPLOAD_START, NULL, 0, context->position ) ) // start uploading files + context->state = STATE_ERROR; } if( context->state != STATE_ERROR ) @@ -281,6 +285,8 @@ int ICACHE_FLASH_ATTR multipartProcess(MultipartCtx * context, HttpdConnData * c // this is the last package, process the remaining data if( multipartProcessData(context, post->multipartBoundary, NULL, 0, 1) ) context->state = STATE_ERROR; + else if( context->callBack( FILE_UPLOAD_DONE, NULL, 0, context->position ) ) // done with files + context->state = STATE_ERROR; } multipartFreeBoundaryBuffer( context ); diff --git a/httpd/multipart.h b/httpd/multipart.h index 87a4bb2..a1205cd 100644 --- a/httpd/multipart.h +++ b/httpd/multipart.h @@ -4,9 +4,11 @@ #include typedef enum { - FILE_START, // multipart: the start of a new file - FILE_DATA, // multipart: file data - FILE_DONE, // multipart: file end + FILE_UPLOAD_START, // multipart: uploading files started + FILE_START, // multipart: the start of a new file (can be more) + FILE_DATA, // multipart: file data + FILE_DONE, // multipart: file end + FILE_UPLOAD_DONE, // multipart: finished for all files } MultipartCmd; // multipart callback