diff --git a/html_build.sh b/.web-build_do.sh similarity index 71% rename from html_build.sh rename to .web-build_do.sh index fe92b25..f43daa7 100755 --- a/html_build.sh +++ b/.web-build_do.sh @@ -21,5 +21,5 @@ find "$BLDDIR" -name "*.map" -delete mkdir -p "$BLDDIR/pages" -php "$SRCDIR/home.php" > "$BLDDIR/pages/home.tpl.html" -php "$SRCDIR/wifi.php" > "$BLDDIR/pages/wifi.tpl.html" +php "$SRCDIR/home.php" > "$BLDDIR/pages/home.tpl" +php "$SRCDIR/wifi.php" > "$BLDDIR/pages/wifi.tpl" diff --git a/esp_meas.pro b/esp_meas.pro index f425de1..609a182 100644 --- a/esp_meas.pro +++ b/esp_meas.pro @@ -135,7 +135,6 @@ HEADERS += \ sbmp/library/payload_parser.h \ user/sampling.h \ user/page_home.h \ - user/timeout.h \ user/sbmp_config.h \ sbmp/library/sbmp_config.example.h \ user/ftoa.h \ diff --git a/esp_meas.pro.user b/esp_meas.pro.user index dec474b..d12ab14 100644 --- a/esp_meas.pro.user +++ b/esp_meas.pro.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/flash.sh b/flash.sh new file mode 100755 index 0000000..d465ea2 --- /dev/null +++ b/flash.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +xterm -title "ESP html build" -e "source $HOME/.bashrc && make flash -B -j4" diff --git a/html/json/samples.tpl.json b/html/json/samples.tpl similarity index 100% rename from html/json/samples.tpl.json rename to html/json/samples.tpl diff --git a/html/pages/home.tpl.html b/html/pages/home.tpl similarity index 100% rename from html/pages/home.tpl.html rename to html/pages/home.tpl diff --git a/html/pages/wifi.tpl.html b/html/pages/wifi.tpl similarity index 100% rename from html/pages/wifi.tpl.html rename to html/pages/wifi.tpl diff --git a/libesphttpd/Makefile b/libesphttpd/Makefile index cc2bc3b..1c77b19 100644 --- a/libesphttpd/Makefile +++ b/libesphttpd/Makefile @@ -169,7 +169,7 @@ ifeq ("$(COMPRESS_W_YUI)","yes") $(Q) for file in `find html_compressed -type f -name "*.js"`; do $(YUI-COMPRESSOR) --type js $$file -o $$file; done $(Q) for file in `find html_compressed -type f -name "*.css"`; do $(YUI-COMPRESSOR) --type css $$file -o $$file; done ifeq ("$(COMPRESS_W_HTMLMINIFIER)","yes") - $(Q) for file in `find html_compressed -type f -name "*.html" -o -name "*.htm" -o -name "*.tpl"`; do $(HTML-MINIFIER) $$file -o $$file; done + $(Q) for file in `find html_compressed/pages -type f -name "*.html" -o -name "*.htm" -o -name "*.tpl"`; do $(HTML-MINIFIER) $$file -o $$file; done endif $(Q) awk "BEGIN {printf \"YUI compression ratio was: %.2f%%\\n\", (`du -b -s html_compressed/ | sed 's/\([0-9]*\).*/\1/'`/`du -b -s ../html/ | sed 's/\([0-9]*\).*/\1/'`)*100}" # mkespfsimage will compress html, css, svg and js files with gzip by default if enabled diff --git a/user/cgi.c b/user/cgi.c index c7e92bd..791b433 100644 --- a/user/cgi.c +++ b/user/cgi.c @@ -17,6 +17,8 @@ flash as a binary. Also handles the hit counter on the main page. #include "cgi.h" #include "uptime.h" #include "datalink.h" +#include "sampling.h" +#include "serial.h" // ------------------------------------------------------------------------------- @@ -24,8 +26,8 @@ flash as a binary. Also handles the hit counter on the main page. // Read multiple samples from the ADC as JSON typedef struct { - uint32_t total_count; - uint32_t done_count; + uint16_t total_count; + uint16_t done_count; bool success; } tplReadSamplesJSON_state; @@ -57,9 +59,10 @@ int FLASH_FN tplReadSamplesJSON(HttpdConnData *connData, char *token, void **arg } st->total_count = count; st->done_count = 0; - st->success = true; + st->success = true; // success true by default - // TODO Start the capture + // REQUEST THE DATA + meas_request_data(st->total_count); } // the "success" field is after the data, @@ -67,34 +70,52 @@ int FLASH_FN tplReadSamplesJSON(HttpdConnData *connData, char *token, void **arg if (strcmp(token, "values") == 0) { - if (st->done_count == 0) { - dbg("Delay to simulate readout..."); - // 1000 ms delay - for(int i=0;i<1000;i++) { - os_delay_us(1000); - } + // Wait for a chunk + + uint8_t *chunk = NULL; + uint16_t chunk_len = 0; + + // 5 secs or 500 ms + for (int i = 0; i < (st->done_count == 0 ? 5000*100: 500*100); i++) { + uart_poll(); + if (meas_chunk_ready()) break; + os_delay_us(10); // 1 ms + system_soft_wdt_feed(); } + chunk = meas_get_chunk(&chunk_len); - // TODO wait for data to be received - // on error, terminate and proceed to the "success" field. + if (!chunk) { + // abort, proceed to the next field. + meas_close(); + st->success = false; + return HTTPD_CGI_DONE; + } - u32 chunk = MIN(10, st->total_count - st->done_count); // chunks of 10 + PayloadParser pp = pp_start(chunk, chunk_len); // chunk of data... - for (u32 i = 0; i < chunk; i++, st->done_count++) { + for (; pp.ptr < pp.len; st->done_count++) { // preceding comma if not the first number if (st->done_count > 0) { httpdSend(connData, ", ", 2); } - // one number - os_sprintf(buff20, "%lu", os_random()); + uint32_t samp = pp_u32(&pp); + + // print the number + os_sprintf(buff20, "%d", samp); httpdSend(connData, buff20, -1); } // wait for more in this substitution - if (st->done_count < st->total_count) + if (st->done_count < st->total_count) { + meas_request_next_chunk(); return HTTPD_CGI_MORE; // more numbers to come + } else { + // we're done + meas_close(); + return HTTPD_CGI_DONE; + } } else if (strcmp(token, "success") == 0) { // success status diff --git a/user/routes.c b/user/routes.c index e6a94f0..840865e 100644 --- a/user/routes.c +++ b/user/routes.c @@ -35,13 +35,10 @@ static int FLASH_FN myPassFn(HttpdConnData *connData, int no, char *user, int us HttpdBuiltInUrl builtInUrls[] = { ROUTE_CGI_ARG("*", cgiRedirectApClientToHostname, "esp8266.nonet"), // redirect func for the captive portal - ROUTE_TPL_FILE("/", tplHome, "/pages/home.tpl.html"), - - ROUTE_CGI("/acquire.cgi", cgiReadSamples), - + ROUTE_TPL_FILE("/", tplHome, "/pages/home.tpl"), // API for measurements - ROUTE_TPL_FILE("/meas/samples.json", tplReadSamplesJSON, "/json/samples.tpl.json"), + ROUTE_TPL_FILE("/api/raw.json", tplReadSamplesJSON, "/json/samples.tpl"), #if WIFI_PROTECT @@ -49,7 +46,7 @@ HttpdBuiltInUrl builtInUrls[] = { #endif ROUTE_REDIRECT("/wifi/", "/wifi"), - ROUTE_TPL_FILE("/wifi", tplWlan, "/pages/wifi.tpl.html"), + ROUTE_TPL_FILE("/wifi", tplWlan, "/pages/wifi.tpl"), ROUTE_CGI("/wifi/scan.cgi", cgiWiFiScan), ROUTE_CGI("/wifi/connect.cgi", cgiWiFiConnect), diff --git a/user/sampling.c b/user/sampling.c index 16384ea..541e68f 100644 --- a/user/sampling.c +++ b/user/sampling.c @@ -3,116 +3,167 @@ #include "datalink.h" #include "sampling.h" -#include "timeout.h" // The buffer is big enough for 256 data bytes - 4*64 // chunk size for bulk transfer (must be multiple of 4 - length of uint32 / float) -// NOTE: If too large, strange errors occur (problem with the UART FIFO at high speed) +// NOTE: If too large, strange errors can occur (problem with the underlying UART FIFO at high speed) +// the FIFO has 128 bytes, and should accomodate ideally the whole frame. #define CHUNK_LEN 100 -static volatile bool acquire_pending = false; -static volatile uint16_t acquire_session; +// Only one readout can happen at a time. +static struct { + bool pending; /*!< Flag that data is currently being read */ + uint16_t sesn; /*!< SBMP session of the readout sequence */ + bool chunk_ready; /*!< Chunk was received and is ready for reading */ + uint8_t received_chunk[CHUNK_LEN]; /*!< Copy of the latest received chunk of data */ + uint16_t received_chunk_size; /*!< Size of the chunk in latest_chunk_copy */ + + // the readout state + uint32_t pos; + uint32_t total; + ETSTimer abortTimer; +} rd; -static ETSTimer prSampleAbortTimer; -static void FLASH_FN prSampleAbortTimerCb(void *arg) +// --- timeout --- + +static void FLASH_FN abortTimerCb(void *arg) { + (void)arg; warn("Sampling aborted due to timeout."); // try to abort the readout - sbmp_bulk_abort(dlnk_ep, acquire_session); + sbmp_bulk_abort(dlnk_ep, rd.sesn); // free the data obj if not NULL - sbmp_ep_free_listener_obj(dlnk_ep, acquire_session); + sbmp_ep_free_listener_obj(dlnk_ep, rd.sesn); // release the slot - sbmp_ep_remove_listener(dlnk_ep, acquire_session); - acquire_pending = false; -} + sbmp_ep_remove_listener(dlnk_ep, rd.sesn); + // invalidate the chunk buffer and indicate that a new readout can start + rd.pending = false; +} -static void setReadoutTmeoTimer(int ms) +static void FLASH_FN setReadoutTmeoTimer(int ms) { - dbg("Set read timeout %d", ms); - os_timer_disarm(&prSampleAbortTimer); - os_timer_setfn(&prSampleAbortTimer, prSampleAbortTimerCb, NULL); - os_timer_arm(&prSampleAbortTimer, ms, 0); +// dbg("Set read timeout %d", ms); + os_timer_disarm(&rd.abortTimer); + os_timer_setfn(&rd.abortTimer, abortTimerCb, NULL); + os_timer_arm(&rd.abortTimer, ms, 0); } -static void stopReadoutTmeoTimer(void) +static void FLASH_FN stopReadoutTmeoTimer(void) { - dbg("Stop read timeout"); - os_timer_disarm(&prSampleAbortTimer); +// dbg("Stop read timeout"); + os_timer_disarm(&rd.abortTimer); } +// ------------- -typedef struct { - uint32_t pos; - uint32_t total; -} DataReadState; -static void FLASH_FN request_data_sesn_listener(SBMP_Endpoint *ep, SBMP_Datagram *dg, void **obj) + +/** request next chunk */ +void FLASH_FN meas_request_next_chunk(void) { - dbg("Received msg in session %d, dg type %d", dg->session, dg->type); + if (!rd.pending) return; + rd.chunk_ready = false; // invalidate the current chunk, so waiting for chunk is possible. + sbmp_bulk_request(dlnk_ep, rd.pos, CHUNK_LEN, rd.sesn); +} - DataReadState *readState = *obj; +/** Check if chunk ready to be read */ +bool FLASH_FN meas_chunk_ready(void) +{ + return rd.pending && rd.chunk_ready; +} - // allocate the state struct - if (readState == NULL) { - readState = malloc(sizeof(DataReadState)); - *obj = readState; +/** + * @brief Get received chunk. NULL if none. + * + * The array is valid until next chunk is requested. + * Chunk length in bytes is stored in the argument. + */ +uint8_t FLASH_FN *meas_get_chunk(uint16_t *chunk_len) +{ + if (!rd.pending) { + warn("Meas not pending, cant get chunk"); + return NULL; + } + if (!rd.chunk_ready) { + warn("Rx chunk not ready"); + return NULL; } + *chunk_len = rd.received_chunk_size; + return rd.received_chunk; +} + +/** Check if this was the last chunk */ +bool FLASH_FN meas_is_last_chunk(void) +{ + return rd.pos >= rd.total; +} + +/** Terminate the readout. */ +void FLASH_FN meas_close(void) +{ + sbmp_ep_remove_listener(dlnk_ep, rd.sesn); + stopReadoutTmeoTimer(); + rd.pending = false; + + info("Transfer closed."); +} + + +static void FLASH_FN request_data_sesn_listener(SBMP_Endpoint *ep, SBMP_Datagram *dg, void **obj) +{ + (void)obj; + +// dbg("Received msg in session %d, dg type %d", dg->session, dg->type); + PayloadParser pp; switch (dg->type) { case DG_BULK_OFFER:// Data ready notification stopReadoutTmeoTimer(); - info("--- Data offered for bulk transfer ---"); +// info("--- Peer offers data for bulk transfer ---"); // data is ready to be read pp = pp_start(dg->payload, dg->length); - readState->pos = 0; - readState->total = pp_u32(&pp); + rd.pos = 0; + rd.total = pp_u32(&pp); - dbg("Total bytes: %d", readState->total); - - // we choose to request the data immediately +// dbg("Total bytes avail: %d", rd.total); + // renew the timeout setReadoutTmeoTimer(1000); - sbmp_bulk_request(ep, readState->pos, CHUNK_LEN, dg->session); + + // request first chunk + sbmp_bulk_request(ep, rd.pos, CHUNK_LEN, dg->session); break; case DG_BULK_DATA: // data received stopReadoutTmeoTimer(); - info("--- Received a chunk, length %d ---", dg->length); +// info("--- Received a chunk, length %d ---", dg->length); // Process the received data - pp = pp_start(dg->payload, dg->length); - while(pp.ptr < pp.len) { - uint32_t x = pp_u32(&pp); - printf("%d,", x); - } - printf("\n"); + memcpy(rd.received_chunk, dg->payload, dg->length); + rd.chunk_ready = true; + rd.received_chunk_size = dg->length; - // and ask for more - readState->pos += dg->length; + // move the pointer for next request + rd.pos += dg->length; - if (readState->pos >= readState->total) { - dbg("Transfer is complete."); - // transfer complete + setReadoutTmeoTimer(1000); // timeout to retrieve the data & ask for more - // make sure the peer has freed the buffer - // (may be waiting for us if we wanted to re-read something) + if (rd.pos >= rd.total) { + info("Transfer is complete."); + // transfer complete + // ask peer to release the buffer & go idle sbmp_bulk_abort(ep, dg->session); - goto cleanup; - } else { - // read next part - setReadoutTmeoTimer(1000); - sbmp_bulk_request(ep, readState->pos, CHUNK_LEN, dg->session); } break; @@ -125,24 +176,18 @@ static void FLASH_FN request_data_sesn_listener(SBMP_Endpoint *ep, SBMP_Datagram return; cleanup: - // free the obj - free(readState); // remove the listener - sbmp_ep_remove_listener(ep, dg->session); - stopReadoutTmeoTimer(); - acquire_pending = false; - - // In case the error was in SBMP (bad state) + meas_close(); } -static bool FLASH_FN meas_request_data(uint16_t count) +bool FLASH_FN meas_request_data(uint16_t count) { bool suc = false; info("Requesting data capture - %d samples.", count); - if (acquire_pending) { + if (rd.pending) { error("Acquire request already in progress."); return false; } @@ -152,7 +197,10 @@ static bool FLASH_FN meas_request_data(uint16_t count) return false; } - acquire_pending = true; + rd.chunk_ready = false; + rd.pos = 0; + rd.total = 0; + rd.pending = true; // start the abort timer - timeout setReadoutTmeoTimer(6000); @@ -170,7 +218,7 @@ static bool FLASH_FN meas_request_data(uint16_t count) goto fail; } - acquire_session = sesn; + rd.sesn = sesn; // request N values sbmp_ep_send_u16(dlnk_ep, count); @@ -181,7 +229,7 @@ static bool FLASH_FN meas_request_data(uint16_t count) fail: stopReadoutTmeoTimer(); - acquire_pending = false; + rd.pending = false; return false; } @@ -189,7 +237,7 @@ fail: // ------ C G I --------- - +/* int FLASH_FN cgiReadSamples(HttpdConnData *connData) { char buff[128]; @@ -218,3 +266,4 @@ int FLASH_FN cgiReadSamples(HttpdConnData *connData) return HTTPD_CGI_DONE; } +*/ diff --git a/user/sampling.h b/user/sampling.h index 47c58ba..9407c58 100644 --- a/user/sampling.h +++ b/user/sampling.h @@ -4,7 +4,41 @@ #include #include -// temporary func to nread samples -int cgiReadSamples(HttpdConnData *connData); +/** + * Reading procedure + * ----------------- + * + * 1. meas_request_data(count) + * 2. wait for meas_chunk_ready() == true + * 3. meas_get_chunk() to read the chunk last received + * 4. if meas_is_last_chunk(), call meas_close() and DONE. + * 5. meas_request_next_chunk() to get more data, goto 2 + * + * Waiting should have a timeout, if data not received, return error status to client. + */ + +/** Request data from the sampling module. Count - number of samples. */ +bool meas_request_data(uint16_t count); // TODO specify what kind of data - currently direct samples. + +/** request next chunk */ +void meas_request_next_chunk(void); + +/** Check if chunk ready to be read */ +bool meas_chunk_ready(void); + +/** + * @brief Get received chunk. NULL if none. + * + * The array is valid until next chunk is requested. + * Chunk length in bytes is stored in the argument. + */ +uint8_t *meas_get_chunk(uint16_t *chunk_len); + +/** Check if this was the last chunk */ +bool meas_is_last_chunk(void); + +/** Terminate the readout. */ +void meas_close(void); + #endif // SAMPLING_H diff --git a/user/serial.c b/user/serial.c index ea660e8..9f2f17e 100644 --- a/user/serial.c +++ b/user/serial.c @@ -17,7 +17,7 @@ static void uart0_rx_intr_handler(void *para); static void uart_recvTask(os_event_t *events); -#define uart_recvTaskPrio 0 +#define uart_recvTaskPrio 1 #define uart_recvTaskQueueLen 10 static os_event_t uart_recvTaskQueue[uart_recvTaskQueueLen]; @@ -123,18 +123,21 @@ void uart_rx_intr_enable(uint8 uart_no) #define UART_GetRxFifoCount(uart_no) ((READ_PERI_REG(UART_STATUS((uart_no))) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT) -static void FLASH_FN uart_recvTask(os_event_t *events) +void FLASH_FN uart_poll(void) { - if (events->sig == 0) { - uint8 fifo_len = UART_GetRxFifoCount(UART0); + uint8 fifo_len = UART_GetRxFifoCount(UART0); + + for (uint8 idx = 0; idx < fifo_len; idx++) { + uint8 d_tmp = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF; + datalink_receive(d_tmp); + } +} - // read from the FIFO & print back - for (uint8 idx = 0; idx < fifo_len; idx++) { - uint8 d_tmp = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF; - //UART_WriteChar(UART0, d_tmp, 0); - datalink_receive(d_tmp); - } +static void FLASH_FN uart_recvTask(os_event_t *events) +{ + if (events->sig == 0) { + uart_poll(); // clear irq flags WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR | UART_RXFIFO_TOUT_INT_CLR); diff --git a/user/serial.h b/user/serial.h index 90b9419..b3e4dc5 100644 --- a/user/serial.h +++ b/user/serial.h @@ -3,6 +3,10 @@ #include +/** Init the uarts */ void serialInit(); +/** poll uart while waiting for something */ +void uart_poll(void); + #endif diff --git a/user/timeout.h b/user/timeout.h deleted file mode 100644 index ecaebb0..0000000 --- a/user/timeout.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef TIMEOUT_H -#define TIMEOUT_H - -#include - -#define until_timeout(to_ms) for(uint32_t _utmeo = system_get_time(); system_get_time() - _utmeo < ((to_ms)*1000);) - -/** Retry a call until a timeout. Variable 'suc' is set to the return value. Must be defined. */ -#define retry_TO(to_ms, call) \ - until_timeout(to_ms) { \ - suc = call; \ - if (suc) break; \ - } - -#endif // TIMEOUT_H diff --git a/web-build.sh b/web-build.sh new file mode 100755 index 0000000..8917b48 --- /dev/null +++ b/web-build.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +xterm -title "ESP html build" -e "source $HOME/.bashrc && ./.web-build_do.sh" diff --git a/html_serve.sh b/web-serve.sh similarity index 100% rename from html_serve.sh rename to web-serve.sh