working wifi readout

master
Ondřej Hruška 9 years ago
parent d57d8dd692
commit 94cec1e47c
  1. 4
      .web-build_do.sh
  2. 1
      esp_meas.pro
  3. 2
      esp_meas.pro.user
  4. 3
      flash.sh
  5. 0
      html/json/samples.tpl
  6. 0
      html/pages/home.tpl
  7. 0
      html/pages/wifi.tpl
  8. 2
      libesphttpd/Makefile
  9. 55
      user/cgi.c
  10. 9
      user/routes.c
  11. 187
      user/sampling.c
  12. 38
      user/sampling.h
  13. 23
      user/serial.c
  14. 4
      user/serial.h
  15. 15
      user/timeout.h
  16. 3
      web-build.sh
  17. 0
      web-serve.sh

@ -21,5 +21,5 @@ find "$BLDDIR" -name "*.map" -delete
mkdir -p "$BLDDIR/pages" mkdir -p "$BLDDIR/pages"
php "$SRCDIR/home.php" > "$BLDDIR/pages/home.tpl.html" php "$SRCDIR/home.php" > "$BLDDIR/pages/home.tpl"
php "$SRCDIR/wifi.php" > "$BLDDIR/pages/wifi.tpl.html" php "$SRCDIR/wifi.php" > "$BLDDIR/pages/wifi.tpl"

@ -135,7 +135,6 @@ HEADERS += \
sbmp/library/payload_parser.h \ sbmp/library/payload_parser.h \
user/sampling.h \ user/sampling.h \
user/page_home.h \ user/page_home.h \
user/timeout.h \
user/sbmp_config.h \ user/sbmp_config.h \
sbmp/library/sbmp_config.example.h \ sbmp/library/sbmp_config.example.h \
user/ftoa.h \ user/ftoa.h \

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject> <!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 3.6.0, 2016-03-26T17:22:49. --> <!-- Written by QtCreator 3.6.0, 2016-03-27T01:16:02. -->
<qtcreator> <qtcreator>
<data> <data>
<variable>EnvironmentId</variable> <variable>EnvironmentId</variable>

@ -0,0 +1,3 @@
#!/bin/bash
xterm -title "ESP html build" -e "source $HOME/.bashrc && make flash -B -j4"

@ -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 "*.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 $(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") 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 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}" $(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 # mkespfsimage will compress html, css, svg and js files with gzip by default if enabled

@ -17,6 +17,8 @@ flash as a binary. Also handles the hit counter on the main page.
#include "cgi.h" #include "cgi.h"
#include "uptime.h" #include "uptime.h"
#include "datalink.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 // Read multiple samples from the ADC as JSON
typedef struct { typedef struct {
uint32_t total_count; uint16_t total_count;
uint32_t done_count; uint16_t done_count;
bool success; bool success;
} tplReadSamplesJSON_state; } tplReadSamplesJSON_state;
@ -57,9 +59,10 @@ int FLASH_FN tplReadSamplesJSON(HttpdConnData *connData, char *token, void **arg
} }
st->total_count = count; st->total_count = count;
st->done_count = 0; 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, // 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 (strcmp(token, "values") == 0) {
if (st->done_count == 0) { // Wait for a chunk
dbg("Delay to simulate readout...");
// 1000 ms delay uint8_t *chunk = NULL;
for(int i=0;i<1000;i++) { uint16_t chunk_len = 0;
os_delay_us(1000);
} // 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 if (!chunk) {
// on error, terminate and proceed to the "success" field. // 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... // 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 // preceding comma if not the first number
if (st->done_count > 0) { if (st->done_count > 0) {
httpdSend(connData, ", ", 2); httpdSend(connData, ", ", 2);
} }
// one number uint32_t samp = pp_u32(&pp);
os_sprintf(buff20, "%lu", os_random());
// print the number
os_sprintf(buff20, "%d", samp);
httpdSend(connData, buff20, -1); httpdSend(connData, buff20, -1);
} }
// wait for more in this substitution // 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 return HTTPD_CGI_MORE; // more numbers to come
} else {
// we're done
meas_close();
return HTTPD_CGI_DONE;
}
} else if (strcmp(token, "success") == 0) { } else if (strcmp(token, "success") == 0) {
// success status // success status

@ -35,13 +35,10 @@ static int FLASH_FN myPassFn(HttpdConnData *connData, int no, char *user, int us
HttpdBuiltInUrl builtInUrls[] = { HttpdBuiltInUrl builtInUrls[] = {
ROUTE_CGI_ARG("*", cgiRedirectApClientToHostname, "esp8266.nonet"), // redirect func for the captive portal ROUTE_CGI_ARG("*", cgiRedirectApClientToHostname, "esp8266.nonet"), // redirect func for the captive portal
ROUTE_TPL_FILE("/", tplHome, "/pages/home.tpl.html"), ROUTE_TPL_FILE("/", tplHome, "/pages/home.tpl"),
ROUTE_CGI("/acquire.cgi", cgiReadSamples),
// API for measurements // 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 #if WIFI_PROTECT
@ -49,7 +46,7 @@ HttpdBuiltInUrl builtInUrls[] = {
#endif #endif
ROUTE_REDIRECT("/wifi/", "/wifi"), 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/scan.cgi", cgiWiFiScan),
ROUTE_CGI("/wifi/connect.cgi", cgiWiFiConnect), ROUTE_CGI("/wifi/connect.cgi", cgiWiFiConnect),

@ -3,116 +3,167 @@
#include "datalink.h" #include "datalink.h"
#include "sampling.h" #include "sampling.h"
#include "timeout.h"
// The buffer is big enough for 256 data bytes - 4*64 // 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) // 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 #define CHUNK_LEN 100
static volatile bool acquire_pending = false; // Only one readout can happen at a time.
static volatile uint16_t acquire_session;
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."); warn("Sampling aborted due to timeout.");
// try to abort the readout // 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 // 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 // release the slot
sbmp_ep_remove_listener(dlnk_ep, acquire_session); sbmp_ep_remove_listener(dlnk_ep, rd.sesn);
acquire_pending = false;
}
// 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); // dbg("Set read timeout %d", ms);
os_timer_disarm(&prSampleAbortTimer); os_timer_disarm(&rd.abortTimer);
os_timer_setfn(&prSampleAbortTimer, prSampleAbortTimerCb, NULL); os_timer_setfn(&rd.abortTimer, abortTimerCb, NULL);
os_timer_arm(&prSampleAbortTimer, ms, 0); os_timer_arm(&rd.abortTimer, ms, 0);
} }
static void stopReadoutTmeoTimer(void) static void FLASH_FN stopReadoutTmeoTimer(void)
{ {
dbg("Stop read timeout"); // dbg("Stop read timeout");
os_timer_disarm(&prSampleAbortTimer); 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) { * @brief Get received chunk. NULL if none.
readState = malloc(sizeof(DataReadState)); *
*obj = readState; * 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; PayloadParser pp;
switch (dg->type) { switch (dg->type) {
case DG_BULK_OFFER:// Data ready notification case DG_BULK_OFFER:// Data ready notification
stopReadoutTmeoTimer(); stopReadoutTmeoTimer();
info("--- Data offered for bulk transfer ---"); // info("--- Peer offers data for bulk transfer ---");
// data is ready to be read // data is ready to be read
pp = pp_start(dg->payload, dg->length); pp = pp_start(dg->payload, dg->length);
readState->pos = 0; rd.pos = 0;
readState->total = pp_u32(&pp); rd.total = pp_u32(&pp);
dbg("Total bytes: %d", readState->total); // dbg("Total bytes avail: %d", rd.total);
// we choose to request the data immediately
// renew the timeout
setReadoutTmeoTimer(1000); 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; break;
case DG_BULK_DATA: // data received case DG_BULK_DATA: // data received
stopReadoutTmeoTimer(); stopReadoutTmeoTimer();
info("--- Received a chunk, length %d ---", dg->length); // info("--- Received a chunk, length %d ---", dg->length);
// Process the received data // Process the received data
pp = pp_start(dg->payload, dg->length); memcpy(rd.received_chunk, dg->payload, dg->length);
while(pp.ptr < pp.len) { rd.chunk_ready = true;
uint32_t x = pp_u32(&pp); rd.received_chunk_size = dg->length;
printf("%d,", x);
}
printf("\n");
// and ask for more // move the pointer for next request
readState->pos += dg->length; rd.pos += dg->length;
if (readState->pos >= readState->total) { setReadoutTmeoTimer(1000); // timeout to retrieve the data & ask for more
dbg("Transfer is complete.");
// transfer complete
// make sure the peer has freed the buffer if (rd.pos >= rd.total) {
// (may be waiting for us if we wanted to re-read something) info("Transfer is complete.");
// transfer complete
// ask peer to release the buffer & go idle
sbmp_bulk_abort(ep, dg->session); 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; break;
@ -125,24 +176,18 @@ static void FLASH_FN request_data_sesn_listener(SBMP_Endpoint *ep, SBMP_Datagram
return; return;
cleanup: cleanup:
// free the obj
free(readState);
// remove the listener // remove the listener
sbmp_ep_remove_listener(ep, dg->session); meas_close();
stopReadoutTmeoTimer();
acquire_pending = false;
// In case the error was in SBMP (bad state)
} }
static bool FLASH_FN meas_request_data(uint16_t count) bool FLASH_FN meas_request_data(uint16_t count)
{ {
bool suc = false; bool suc = false;
info("Requesting data capture - %d samples.", count); info("Requesting data capture - %d samples.", count);
if (acquire_pending) { if (rd.pending) {
error("Acquire request already in progress."); error("Acquire request already in progress.");
return false; return false;
} }
@ -152,7 +197,10 @@ static bool FLASH_FN meas_request_data(uint16_t count)
return false; return false;
} }
acquire_pending = true; rd.chunk_ready = false;
rd.pos = 0;
rd.total = 0;
rd.pending = true;
// start the abort timer - timeout // start the abort timer - timeout
setReadoutTmeoTimer(6000); setReadoutTmeoTimer(6000);
@ -170,7 +218,7 @@ static bool FLASH_FN meas_request_data(uint16_t count)
goto fail; goto fail;
} }
acquire_session = sesn; rd.sesn = sesn;
// request N values // request N values
sbmp_ep_send_u16(dlnk_ep, count); sbmp_ep_send_u16(dlnk_ep, count);
@ -181,7 +229,7 @@ static bool FLASH_FN meas_request_data(uint16_t count)
fail: fail:
stopReadoutTmeoTimer(); stopReadoutTmeoTimer();
acquire_pending = false; rd.pending = false;
return false; return false;
} }
@ -189,7 +237,7 @@ fail:
// ------ C G I --------- // ------ C G I ---------
/*
int FLASH_FN cgiReadSamples(HttpdConnData *connData) int FLASH_FN cgiReadSamples(HttpdConnData *connData)
{ {
char buff[128]; char buff[128];
@ -218,3 +266,4 @@ int FLASH_FN cgiReadSamples(HttpdConnData *connData)
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
*/

@ -4,7 +4,41 @@
#include <esp8266.h> #include <esp8266.h>
#include <httpd.h> #include <httpd.h>
// 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 #endif // SAMPLING_H

@ -17,7 +17,7 @@
static void uart0_rx_intr_handler(void *para); static void uart0_rx_intr_handler(void *para);
static void uart_recvTask(os_event_t *events); static void uart_recvTask(os_event_t *events);
#define uart_recvTaskPrio 0 #define uart_recvTaskPrio 1
#define uart_recvTaskQueueLen 10 #define uart_recvTaskQueueLen 10
static os_event_t uart_recvTaskQueue[uart_recvTaskQueueLen]; 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) #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 // clear irq flags
WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR | UART_RXFIFO_TOUT_INT_CLR); WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR | UART_RXFIFO_TOUT_INT_CLR);

@ -3,6 +3,10 @@
#include <esp8266.h> #include <esp8266.h>
/** Init the uarts */
void serialInit(); void serialInit();
/** poll uart while waiting for something */
void uart_poll(void);
#endif #endif

@ -1,15 +0,0 @@
#ifndef TIMEOUT_H
#define TIMEOUT_H
#include <esp8266.h>
#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

@ -0,0 +1,3 @@
#!/bin/bash
xterm -title "ESP html build" -e "source $HOME/.bashrc && ./.web-build_do.sh"
Loading…
Cancel
Save