Added embedded filesystem. Whee!
This commit is contained in:
@@ -134,6 +134,15 @@ flash: firmware/0x00000.bin firmware/0x40000.bin
|
||||
sleep 3
|
||||
-$(ESPTOOL) --port $(ESPPORT) write_flash 0x40000 firmware/0x40000.bin
|
||||
|
||||
webpages.espfs: html/ mkespfsimage/mkespfsimage
|
||||
cd html; find | ../mkespfsimage/mkespfsimage > ../webpages.espfs; cd ..
|
||||
|
||||
mkespfsimage/mkespfsimage: mkespfsimage/
|
||||
make -C mkespfsimage
|
||||
|
||||
htmlflash: webpages.espfs
|
||||
-$(ESPTOOL) --port $(ESPPORT) write_flash 0x20000 webpages.espfs
|
||||
|
||||
clean:
|
||||
$(Q) rm -f $(APP_AR)
|
||||
$(Q) rm -f $(TARGET_OUT)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<head><title>Test</title></head>
|
||||
<body>
|
||||
<h1>Test!</h1>
|
||||
<p>Yay, the cpio filesystem thingy works. Click <a href="/test2.html">here</a> to see
|
||||
if the 2nd page works too.
|
||||
</p>
|
||||
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<head><title>Test</title></head>
|
||||
<body>
|
||||
<h1>Test2!</h1>
|
||||
<p>Here's an image of a cat (hopefully...)<br />
|
||||
<img src="cat.jpeg">
|
||||
</p>
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
|
||||
mkespfsimage: main.o
|
||||
$(CC) -o mkespfsimage $<
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef ESPROFSFORMAT_H
|
||||
#define ESPROFSFORMAT_H
|
||||
|
||||
/*
|
||||
Stupid cpio-like tool to make read-only 'filesystems' that live on the flash SPI chip of the module.
|
||||
Can (will) use lzf compression (when I come around to it) to make shit quicker. Aligns names, files,
|
||||
headers on 4-byte boundaries so the SPI abstraction hardware in the ESP8266 doesn't crap on itself
|
||||
when trying to do a <4byte or unaligned read.
|
||||
*/
|
||||
|
||||
/*
|
||||
The idea 'borrows' from cpio: it's basically a concatenation of {header, filename, file} data.
|
||||
Header, filename and file data is 32-bit aligned. The last file is indicated by data-less header
|
||||
with the FLAG_LASTFILE flag set.
|
||||
*/
|
||||
|
||||
|
||||
#define FLAG_LASTFILE (1<<0)
|
||||
#define COMPRESS_NONE 0
|
||||
#define COMPRESS_LZF 1
|
||||
|
||||
typedef struct {
|
||||
int32_t magic;
|
||||
int8_t flags;
|
||||
int8_t compression;
|
||||
int16_t nameLen;
|
||||
int32_t fileLenComp;
|
||||
int32_t fileLenDecomp;
|
||||
} EspFsHeader;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,109 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <string.h>
|
||||
#include "espfsformat.h"
|
||||
|
||||
//Routines to convert host format to the endianness used in the xtensa
|
||||
short htoxs(short in) {
|
||||
char r[2];
|
||||
r[0]=in;
|
||||
r[1]=in>>8;
|
||||
return *((short *)r);
|
||||
}
|
||||
|
||||
int htoxl(int in) {
|
||||
unsigned char r[4];
|
||||
r[0]=in;
|
||||
r[1]=in>>8;
|
||||
r[2]=in>>16;
|
||||
r[3]=in>>24;
|
||||
return *((int *)r);
|
||||
}
|
||||
|
||||
|
||||
void handleFile(int f, char *name) {
|
||||
char *fdat;
|
||||
off_t size;
|
||||
EspFsHeader h;
|
||||
int nameLen;
|
||||
size=lseek(f, 0, SEEK_END);
|
||||
fdat=mmap(NULL, size, PROT_READ, MAP_SHARED, f, 0);
|
||||
if (fdat==MAP_FAILED) {
|
||||
perror("mmap");
|
||||
return;
|
||||
}
|
||||
|
||||
//Fill header data
|
||||
h.magic=('E'<<0)+('S'<<8)+('f'<<16)+('s'<<24);
|
||||
h.flags=0;
|
||||
h.compression=COMPRESS_NONE;
|
||||
nameLen=strlen(name)+1;
|
||||
if (nameLen&3) nameLen+=4-(nameLen&3); //Round to next 32bit boundary
|
||||
h.nameLen=htoxs(nameLen);
|
||||
h.fileLenComp=htoxl(size);
|
||||
h.fileLenDecomp=htoxl(size);
|
||||
|
||||
write(1, &h, sizeof(EspFsHeader));
|
||||
write(1, name, nameLen); //ToDo: this can eat up a few bytes after the buffer.
|
||||
write(1, fdat, size);
|
||||
//Pad out to 32bit boundary
|
||||
while (size&3) {
|
||||
write(1, "\000", 1);
|
||||
size++;
|
||||
}
|
||||
munmap(fdat, size);
|
||||
}
|
||||
|
||||
//Write final dummy header with FLAG_LASTFILE set.
|
||||
void finishArchive() {
|
||||
EspFsHeader h;
|
||||
h.magic=('E'<<0)+('S'<<8)+('f'<<16)+('s'<<24);
|
||||
h.flags=FLAG_LASTFILE;
|
||||
h.compression=COMPRESS_NONE;
|
||||
h.nameLen=htoxs(0);
|
||||
h.fileLenComp=htoxl(0);
|
||||
h.fileLenDecomp=htoxl(0);
|
||||
write(1, &h, sizeof(EspFsHeader));
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int f;
|
||||
char fileName[1024];
|
||||
char *realName;
|
||||
struct stat statBuf;
|
||||
int serr;
|
||||
while(fgets(fileName, sizeof(fileName), stdin)) {
|
||||
//Kill off '\n' at the end
|
||||
fileName[strlen(fileName)-1]=0;
|
||||
//Only include files
|
||||
serr=stat(fileName, &statBuf);
|
||||
if ((serr==0) && S_ISREG(statBuf.st_mode)) {
|
||||
//Strip off './' or '/' madness.
|
||||
realName=fileName;
|
||||
if (fileName[0]=='.') realName++;
|
||||
if (realName[0]=='/') realName++;
|
||||
f=open(fileName, O_RDONLY);
|
||||
if (f>0) {
|
||||
handleFile(f, realName);
|
||||
fprintf(stderr, "%s\n", realName);
|
||||
close(f);
|
||||
} else {
|
||||
perror(fileName);
|
||||
}
|
||||
} else {
|
||||
if (serr!=0) {
|
||||
perror(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
finishArchive();
|
||||
}
|
||||
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
#include "driver/uart.h"
|
||||
#include "c_types.h"
|
||||
#include "user_interface.h"
|
||||
#include "espconn.h"
|
||||
#include "mem.h"
|
||||
#include "osapi.h"
|
||||
#include "../mkespfsimage/espfsformat.h"
|
||||
#include "espfs.h"
|
||||
|
||||
struct EspFsFile {
|
||||
EspFsHeader *header;
|
||||
char decompressor;
|
||||
int32_t posDecomp;
|
||||
char *posStart;
|
||||
char *posComp;
|
||||
void *decompData;
|
||||
};
|
||||
|
||||
/*
|
||||
Available locations, at least in my flash, with boundaries partially guessed:
|
||||
0x00000 (0x10000): Code/data (RAM data?)
|
||||
0x10000 (0x30000): Free (filled with zeroes) (parts used by ESPCloud and maybe SSL)
|
||||
0x40000 (0x20000): Code/data (ROM data?)
|
||||
0x60000 (0x1C000): Free
|
||||
0x7c000 (0x04000): Param store
|
||||
0x80000 - end of flash
|
||||
|
||||
Accessing the flash through the mem emulation at 0x40200000 is a bit hairy: All accesses
|
||||
*must* be aligned 32-bit accesses. Reading a short, byte or unaligned word will result in
|
||||
a memory exception, crashing the program.
|
||||
*/
|
||||
|
||||
|
||||
//Copies len bytes over from dst to src, but does it using *only*
|
||||
//aligned 32-bit reads.
|
||||
void memcpyAligned(char *dst, char *src, int len) {
|
||||
int x;
|
||||
int w, b;
|
||||
for (x=0; x<len; x++) {
|
||||
b=((int)src&3);
|
||||
w=*((int *)(src-b));
|
||||
if (b==0) *dst=(w>>0);
|
||||
if (b==1) *dst=(w>>8);
|
||||
if (b==2) *dst=(w>>16);
|
||||
if (b==3) *dst=(w>>24);
|
||||
dst++; src++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
EspFsFile *espFsOpen(char *fileName) {
|
||||
char *p=(char *)(ESPFS_POS+0x40200000);
|
||||
char *hpos;
|
||||
char namebuf[256];
|
||||
EspFsHeader h;
|
||||
EspFsFile *r;
|
||||
//Skip initial slashes
|
||||
while(fileName[0]=='/') fileName++;
|
||||
//Go find that file!
|
||||
while(1) {
|
||||
hpos=p;
|
||||
os_memcpy(&h, p, sizeof(EspFsHeader));
|
||||
//ToDo: check magic
|
||||
if (h.flags&FLAG_LASTFILE) {
|
||||
// os_printf("End of archive.\n");
|
||||
return NULL;
|
||||
}
|
||||
p+=sizeof(EspFsHeader);
|
||||
os_memcpy(namebuf, p, sizeof(namebuf));
|
||||
// os_printf("Found file %s . Namelen=%x fileLen=%x\n", namebuf, h.nameLen,h.fileLenComp);
|
||||
if (os_strcmp(namebuf, fileName)==0) {
|
||||
p+=h.nameLen;
|
||||
r=(EspFsFile *)os_malloc(sizeof(EspFsFile));
|
||||
if (r==NULL) return NULL;
|
||||
r->header=(EspFsHeader *)hpos;
|
||||
r->decompressor=h.compression;
|
||||
r->posComp=p;
|
||||
r->posStart=p;
|
||||
r->posDecomp=0;
|
||||
r->decompData=NULL;
|
||||
//ToDo: Init any decompressor
|
||||
return r;
|
||||
}
|
||||
//Skip name and file
|
||||
p+=h.nameLen+h.fileLenComp;
|
||||
if ((int)p&3) p+=4-((int)p&3); //align to next 32bit val
|
||||
// os_printf("Next addr = %x\n", (int)p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int espFsRead(EspFsFile *fh, char *buff, int len) {
|
||||
if (fh==NULL) return 0;
|
||||
if (fh->decompressor==COMPRESS_NONE) {
|
||||
int toRead;
|
||||
toRead=fh->header->fileLenComp-(fh->posComp-fh->posStart);
|
||||
if (len>toRead) len=toRead;
|
||||
// os_printf("Reading %d bytes from %x\n", len, fh->posComp);
|
||||
memcpyAligned(buff, fh->posComp, len);
|
||||
fh->posDecomp+=len;
|
||||
fh->posComp+=len;
|
||||
// os_printf("Done reading %d bytes, pos=%x\n", len, fh->posComp);
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
void espFsClose(EspFsFile *fh) {
|
||||
if (fh==NULL) return;
|
||||
os_free(fh);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef ESPFS_H
|
||||
#define ESPFS_H
|
||||
|
||||
//Pos of esp fs in flash
|
||||
#define ESPFS_POS 0x20000
|
||||
|
||||
typedef struct EspFsFile EspFsFile;
|
||||
|
||||
EspFsFile *espFsOpen(char *fileName);
|
||||
int espFsRead(EspFsFile *fh, char *buff, int len);
|
||||
void espFsClose(EspFsFile *fh);
|
||||
|
||||
|
||||
#endif
|
||||
+56
-14
@@ -8,18 +8,34 @@
|
||||
#include "espconn.h"
|
||||
#include "httpd.h"
|
||||
#include "io.h"
|
||||
#include "espfs.h"
|
||||
|
||||
#define MAX_HEAD_LEN 1024
|
||||
|
||||
int cgiSet(struct espconn *conn);
|
||||
|
||||
typedef int (* cgiSendCallback)(struct espconn *conn);
|
||||
//struct UrlData;
|
||||
typedef struct UrlData UrlData;
|
||||
|
||||
typedef struct {
|
||||
char *url;
|
||||
char *getArgs;
|
||||
const UrlData *effUrl;
|
||||
char *datPtr;
|
||||
int datLen;
|
||||
EspFsFile *file;
|
||||
} GetData;
|
||||
|
||||
typedef int (* cgiSendCallback)(struct espconn *conn, GetData *getData);
|
||||
|
||||
struct UrlData {
|
||||
const char *url;
|
||||
const char *fixedResp;
|
||||
cgiSendCallback cgiCb;
|
||||
} UrlData;
|
||||
};
|
||||
|
||||
int cgiSet(struct espconn *conn, GetData *getData);
|
||||
int cgiGetFlash(struct espconn *conn, GetData *getData);
|
||||
static int cgiSendFile(struct espconn *conn, GetData *getData);
|
||||
|
||||
|
||||
const char htmlIndex[]="<html><head><title>Hello World</title></head> \
|
||||
<body><h1>Hello, World!</h1></body> \
|
||||
@@ -29,21 +45,18 @@ const char htmlIndex[]="<html><head><title>Hello World</title></head> \
|
||||
static const UrlData urls[]={
|
||||
{"/", htmlIndex, NULL},
|
||||
{"/set", NULL, cgiSet},
|
||||
{"/flash.bin", NULL, cgiGetFlash},
|
||||
{NULL, NULL, NULL},
|
||||
};
|
||||
|
||||
static const UrlData cpioUrlData={"*", NULL, cgiSendFile};
|
||||
|
||||
static struct espconn conn;
|
||||
static esp_tcp tcp;
|
||||
|
||||
static int recLen;
|
||||
static char recBuff[MAX_HEAD_LEN];
|
||||
|
||||
typedef struct {
|
||||
char *url;
|
||||
char *getArgs;
|
||||
const UrlData *effUrl;
|
||||
} GetData;
|
||||
|
||||
static GetData getData;
|
||||
|
||||
//Find a specific arg in a string of get- or post-data.
|
||||
@@ -70,18 +83,35 @@ static char *ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg) {
|
||||
}
|
||||
|
||||
//ToDo: Move cgi functions to somewhere else
|
||||
int cgiSet(struct espconn *conn) {
|
||||
int ICACHE_FLASH_ATTR cgiSet(struct espconn *conn, GetData *getData) {
|
||||
char *on;
|
||||
static const char okStr[]="<html><head><title>OK</title></head><body><p>OK</p></body></html>";
|
||||
on=httpdFindArg(getData.getArgs, "led");
|
||||
on=httpdFindArg(getData->getArgs, "led");
|
||||
os_printf("cgiSet: on=%s\n", on?on:"not found");
|
||||
if (on!=NULL) ioLed(atoi(on));
|
||||
espconn_sent(conn, (uint8 *)okStr, os_strlen(okStr));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ICACHE_FLASH_ATTR cgiGetFlash(struct espconn *conn, GetData *getData) {
|
||||
static char *p=(char *)0x40200000;
|
||||
static int t=0;
|
||||
espconn_sent(conn, (uint8 *)p, 1024);
|
||||
p+=1024;
|
||||
t++;
|
||||
if (t<1024) return 0;
|
||||
t=0;
|
||||
p=(char*)0x40200000;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int ICACHE_FLASH_ATTR cgiSendFile(struct espconn *conn, GetData *getData) {
|
||||
int len;
|
||||
char buff[1024];
|
||||
len=espFsRead(getData->file, buff, 1024);
|
||||
espconn_sent(conn, (uint8 *)buff, len);
|
||||
return (len==0);
|
||||
}
|
||||
|
||||
static const char *httpOkHeader="HTTP/1.0 200 OK\r\nServer: esp8266-thingie/0.1\r\nContent-Type: text/html\r\n\r\n";
|
||||
static const char *httpNotFoundHeader="HTTP/1.0 404 Not Found\r\nServer: esp8266-thingie/0.1\r\n\r\nNot Found.\r\n";
|
||||
@@ -94,7 +124,7 @@ static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) {
|
||||
}
|
||||
|
||||
if (getData.effUrl->cgiCb!=NULL) {
|
||||
if (getData.effUrl->cgiCb(conn)) getData.effUrl=NULL;
|
||||
if (getData.effUrl->cgiCb(conn, &getData)) getData.effUrl=NULL;
|
||||
} else {
|
||||
espconn_sent(conn, (uint8 *)getData.effUrl->fixedResp, os_strlen(getData.effUrl->fixedResp));
|
||||
getData.effUrl=NULL;
|
||||
@@ -103,6 +133,8 @@ static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) {
|
||||
|
||||
static void ICACHE_FLASH_ATTR httpdSendResp(struct espconn *conn) {
|
||||
int i=0;
|
||||
EspFsFile *fdat;
|
||||
//See if the url is somewhere in our internal url table.
|
||||
while (urls[i].url!=NULL) {
|
||||
if (os_strcmp(urls[i].url, getData.url)==0) {
|
||||
getData.effUrl=&urls[i];
|
||||
@@ -112,6 +144,16 @@ static void ICACHE_FLASH_ATTR httpdSendResp(struct espconn *conn) {
|
||||
}
|
||||
i++;
|
||||
}
|
||||
//Nope. See if it's in the cpio archive
|
||||
fdat=espFsOpen(getData.url);
|
||||
if (fdat!=NULL) {
|
||||
//Found
|
||||
getData.file=fdat;
|
||||
getData.effUrl=&cpioUrlData;
|
||||
espconn_sent(conn, (uint8 *)httpOkHeader, os_strlen(httpOkHeader));
|
||||
return;
|
||||
}
|
||||
|
||||
//Can't find :/
|
||||
espconn_sent(conn, (uint8 *)httpNotFoundHeader, os_strlen(httpNotFoundHeader));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user