Imported RTC FIFO component + added Lua interface.

The rtcfifo module uses RTC memory to store sensor samples across deep-sleeps,
making it possible to batch up samples for less frequent uploads. This
component uses 9 RTC memory slots for control, and a variable number of
slots for sample storage (see rtcfifo.prepare() on how to control the
size/location of the latter).

When used together with the rtctime module, it also exposes the convenience
function rtcfifo.dsleep_until_sample() which can be used to easily take
readings on a regular basis without having to manually take into account
time spent awake to get an accurate sleep time.

The format used for storing samples is quite dense, and allows for 16 bits
of data in a fixed point format (per sample).
This commit is contained in:
Johny Mattsson 2015-06-26 16:36:39 +10:00
parent 2187424928
commit 67e72c45df
5 changed files with 694 additions and 1 deletions

485
app/include/rtc/rtcfifo.h Normal file
View File

@ -0,0 +1,485 @@
/*
* Copyright 2015 Dius Computing Pty Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
* - Neither the name of the copyright holders nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @author Bernd Meyer <bmeyer@dius.com.au>
*/
#ifndef _RTCFIFO_H_
#define _RTCFIFO_H_
#include "rtcaccess.h"
#include "rtctime.h"
// 1: measurement alignment, in microseconds
// 2: timestamp for next sample (seconds). For sensors which sense during the sleep phase. Set to
// 0 to indicate no sample waiting. Simply do not use for sensors which deliver values prior to
// deep sleep.
// 3: Number of samples to take before doing a "real" boot. Decremented as samples are obtained
// 4: Reload value for (10). Needs to be applied by the firmware in the real boot (rtc_restart_samples_to_take())
//
// 5: FIFO location. First FIFO address in bits 0:7, first non-FIFO address in bits 8:15.
// Number of tag spaces in bits 16:23
// 6: Number of samples in FIFO.
// 7: FIFO tail (where next sample will be written. Increments by 1 for each sample)
// 8: FIFO head (where next sample will be read. Increments by 1 for each sample)
// 9: FIFO head timestamp. Used and maintained when pulling things off the FIFO. This is the timestamp of the
// most recent sample pulled off; I.e. the head samples timestamp is this plus that sample's delta_t
// 10: FIFO tail timestamp. Used and maintained when adding things to the FIFO. This is the timestamp of the
// most recent sample to have been added. I.e. a new sample's delta-t is calculated relative to this
// (9/10) are meaningless when (3) is zero
//
#define RTC_FIFO_BASE 8
#define RTC_FIFO_MAGIC 0x44695553
// RTCFIFO storage
#define RTC_FIFO_MAGIC_POS (RTC_FIFO_BASE+0)
#define RTC_ALIGNMENT_POS (RTC_FIFO_BASE+1)
#define RTC_TIMESTAMP_POS (RTC_FIFO_BASE+2)
#define RTC_SAMPLESTOTAKE_POS (RTC_FIFO_BASE+3)
#define RTC_SAMPLESPERBOOT_POS (RTC_FIFO_BASE+4)
#define RTC_FIFOLOC_POS (RTC_FIFO_BASE+5)
#define RTC_FIFOCOUNT_POS (RTC_FIFO_BASE+6)
#define RTC_FIFOTAIL_POS (RTC_FIFO_BASE+7)
#define RTC_FIFOHEAD_POS (RTC_FIFO_BASE+8)
#define RTC_FIFOTAIL_T_POS (RTC_FIFO_BASE+9)
#define RTC_FIFOHEAD_T_POS (RTC_FIFO_BASE+10)
// 32-127: FIFO space. Consisting of a number of tag spaces (see 4), followed by data entries.
// Data entries consist of:
// Bits 28:31 -> tag index. 0-15
// Bits 25:27 -> decimals
// Bits 16:24 -> delta-t in seconds from previous entry
// Bits 0:15 -> sample value
#define RTC_DEFAULT_FIFO_START 32
#define RTC_DEFAULT_FIFO_END 128
#define RTC_DEFAULT_TAGCOUNT 5
#define RTC_DEFAULT_FIFO_LOC (RTC_DEFAULT_FIFO_START + (RTC_DEFAULT_FIFO_END<<8) + (RTC_DEFAULT_TAGCOUNT<<16))
typedef struct
{
uint32_t timestamp;
uint32_t value;
uint32_t decimals;
uint32_t tag;
} sample_t;
static inline void rtc_fifo_clear_content(void);
static inline uint32_t rtc_fifo_get_tail(void)
{
return rtc_mem_read(RTC_FIFOTAIL_POS);
}
static inline void rtc_fifo_put_tail(uint32_t val)
{
rtc_mem_write(RTC_FIFOTAIL_POS,val);
}
static inline uint32_t rtc_fifo_get_head(void)
{
return rtc_mem_read(RTC_FIFOHEAD_POS);
}
static inline void rtc_fifo_put_head(uint32_t val)
{
rtc_mem_write(RTC_FIFOHEAD_POS,val);
}
static inline uint32_t rtc_fifo_get_tail_t(void)
{
return rtc_mem_read(RTC_FIFOTAIL_T_POS);
}
static inline void rtc_fifo_put_tail_t(uint32_t val)
{
rtc_mem_write(RTC_FIFOTAIL_T_POS,val);
}
static inline uint32_t rtc_fifo_get_head_t(void)
{
return rtc_mem_read(RTC_FIFOHEAD_T_POS);
}
static inline void rtc_fifo_put_head_t(uint32_t val)
{
rtc_mem_write(RTC_FIFOHEAD_T_POS,val);
}
static inline uint32_t rtc_fifo_get_count(void)
{
return rtc_mem_read(RTC_FIFOCOUNT_POS);
}
static inline void rtc_fifo_put_count(uint32_t val)
{
rtc_mem_write(RTC_FIFOCOUNT_POS,val);
}
static inline uint32_t rtc_fifo_get_tagcount(void)
{
return (rtc_mem_read(RTC_FIFOLOC_POS)>>16)&0xff;
}
static inline uint32_t rtc_fifo_get_tagpos(void)
{
return (rtc_mem_read(RTC_FIFOLOC_POS)>>0)&0xff;
}
static inline uint32_t rtc_fifo_get_last(void)
{
return (rtc_mem_read(RTC_FIFOLOC_POS)>>8)&0xff;
}
static inline uint32_t rtc_fifo_get_first(void)
{
return rtc_fifo_get_tagpos()+rtc_fifo_get_tagcount();
}
static inline void rtc_fifo_put_loc(uint32_t first, uint32_t last, uint32_t tagcount)
{
rtc_mem_write(RTC_FIFOLOC_POS,first+(last<<8)+(tagcount<<16));
}
static inline uint32_t rtc_fifo_normalise_index(uint32_t index)
{
if (index>=rtc_fifo_get_last())
index=rtc_fifo_get_first();
return index;
}
static inline void rtc_fifo_increment_count(void)
{
rtc_fifo_put_count(rtc_fifo_get_count()+1);
}
static inline void rtc_fifo_decrement_count(void)
{
rtc_fifo_put_count(rtc_fifo_get_count()-1);
}
static inline uint32_t rtc_get_samples_to_take(void)
{
return rtc_mem_read(RTC_SAMPLESTOTAKE_POS);
}
static inline void rtc_put_samples_to_take(uint32_t val)
{
rtc_mem_write(RTC_SAMPLESTOTAKE_POS,val);
}
static inline void rtc_decrement_samples_to_take(void)
{
uint32_t stt=rtc_get_samples_to_take();
if (stt)
rtc_put_samples_to_take(stt-1);
}
static inline void rtc_restart_samples_to_take(void)
{
rtc_put_samples_to_take(rtc_mem_read(RTC_SAMPLESPERBOOT_POS));
}
static inline uint32_t rtc_fifo_get_value(uint32_t entry)
{
return entry&0xffff;
}
static inline uint32_t rtc_fifo_get_decimals(uint32_t entry)
{
return (entry>>25)&0x07;
}
static inline uint32_t rtc_fifo_get_deltat(uint32_t entry)
{
return (entry>>16)&0x1ff;
}
static inline uint32_t rtc_fifo_get_tagindex(uint32_t entry)
{
return (entry>>28)&0x0f;
}
static inline uint32_t rtc_fifo_get_tag_from_entry(uint32_t entry)
{
uint32_t index=rtc_fifo_get_tagindex(entry);
uint32_t tags_at=rtc_fifo_get_tagpos();
return rtc_mem_read(tags_at+index);
}
static inline void rtc_fifo_fill_sample(sample_t* dst, uint32_t entry, uint32_t timestamp)
{
dst->timestamp=timestamp;
dst->value=rtc_fifo_get_value(entry);
dst->decimals=rtc_fifo_get_decimals(entry);
dst->tag=rtc_fifo_get_tag_from_entry(entry);
}
// returns 1 if sample popped, 0 if not
static inline int8_t rtc_fifo_pop_sample(sample_t* dst)
{
uint32_t count=rtc_fifo_get_count();
if (count==0)
return 0;
uint32_t head=rtc_fifo_get_head();
uint32_t timestamp=rtc_fifo_get_head_t();
uint32_t entry=rtc_mem_read(head);
timestamp+=rtc_fifo_get_deltat(entry);
rtc_fifo_fill_sample(dst,entry,timestamp);
head=rtc_fifo_normalise_index(head+1);
rtc_fifo_put_head(head);
rtc_fifo_put_head_t(timestamp);
rtc_fifo_decrement_count();
return 1;
}
// returns 1 if sample is available, 0 if not
static inline int8_t rtc_fifo_peek_sample(sample_t* dst, uint32_t from_top)
{
if (rtc_fifo_get_count()<=from_top)
return 0;
uint32_t head=rtc_fifo_get_head();
uint32_t entry=rtc_mem_read(head);
uint32_t timestamp=rtc_fifo_get_head_t();
timestamp+=rtc_fifo_get_deltat(entry);
while (from_top--)
{
head=rtc_fifo_normalise_index(head+1);
entry=rtc_mem_read(head);
timestamp+=rtc_fifo_get_deltat(entry);
}
rtc_fifo_fill_sample(dst,entry,timestamp);
return 1;
}
static inline void rtc_fifo_drop_samples(uint32_t from_top)
{
uint32_t count=rtc_fifo_get_count();
if (count<=from_top)
from_top=count;
uint32_t head=rtc_fifo_get_head();
uint32_t head_t=rtc_fifo_get_head_t();
while (from_top--)
{
uint32_t entry=rtc_mem_read(head);
head_t+=rtc_fifo_get_deltat(entry);
head=rtc_fifo_normalise_index(head+1);
rtc_fifo_decrement_count();
}
rtc_fifo_put_head(head);
rtc_fifo_put_head_t(head_t);
}
static inline int rtc_fifo_find_tag_index(uint32_t tag)
{
uint32_t tags_at=rtc_fifo_get_tagpos();
uint32_t count=rtc_fifo_get_tagcount();
uint32_t i;
for (i=0;i<count;i++)
{
uint32_t stag=rtc_mem_read(tags_at+i);
if (stag==tag)
return i;
if (stag==0)
{
rtc_mem_write(tags_at+i,tag);
return i;
}
}
return -1;
}
static int32_t rtc_fifo_delta_t(uint32_t t, uint32_t ref_t)
{
uint32_t delta=t-ref_t;
if (delta>0x1ff)
return -1;
return delta;
}
static uint32_t rtc_fifo_construct_entry(uint32_t val, uint32_t tagindex, uint32_t decimals, uint32_t deltat)
{
return val+(deltat<<16)+(decimals<<25)+(tagindex<<28);
}
static inline void rtc_fifo_store_sample(const sample_t* s)
{
uint32_t head=rtc_fifo_get_head();
uint32_t tail=rtc_fifo_get_tail();
uint32_t count=rtc_fifo_get_count();
int32_t tagindex=rtc_fifo_find_tag_index(s->tag);
if (count==0)
{
rtc_fifo_put_head_t(s->timestamp);
rtc_fifo_put_tail_t(s->timestamp);
}
uint32_t tail_t=rtc_fifo_get_tail_t();
int32_t deltat=rtc_fifo_delta_t(s->timestamp,tail_t);
if (tagindex<0 || deltat<0)
{ // We got something that doesn't fit into the scheme. Might be a long delay, might
// be some sort of dynamic change. In order to go on, we need to start over....
// ets_printf("deltat is %d, tagindex is %d\n",deltat,tagindex);
rtc_fifo_clear_content();
rtc_fifo_put_head_t(s->timestamp);
rtc_fifo_put_tail_t(s->timestamp);
head=rtc_fifo_get_head();
tail=rtc_fifo_get_tail();
count=rtc_fifo_get_count();
tagindex=rtc_fifo_find_tag_index(s->tag); // This should work now
if (tagindex<0)
return; // Uh-oh! This should never happen
}
if (head==tail && count>0)
{ // Full! Need to remove a sample
sample_t dummy;
rtc_fifo_pop_sample(&dummy);
}
rtc_mem_write(tail++,rtc_fifo_construct_entry(s->value,tagindex,s->decimals,deltat));
rtc_fifo_put_tail(rtc_fifo_normalise_index(tail));
rtc_fifo_put_tail_t(s->timestamp);
rtc_fifo_increment_count();
}
static uint32_t rtc_fifo_make_tag(const uint8_t* s)
{
uint32_t tag=0;
int i;
for (i=0;i<4;i++)
{
if (!s[i])
break;
tag+=((uint32_t)(s[i]&0xff))<<(i*8);
}
return tag;
}
static void rtc_fifo_tag_to_string(uint32_t tag, uint8_t s[5])
{
int i;
s[4]=0;
for (i=0;i<4;i++)
s[i]=(tag>>(8*i))&0xff;
}
static inline uint32_t rtc_fifo_get_divisor(const sample_t* s)
{
uint8_t decimals=s->decimals;
uint32_t div=1;
while (decimals--)
div*=10;
return div;
}
static inline void rtc_fifo_clear_tags(void)
{
uint32_t tags_at=rtc_fifo_get_tagpos();
uint32_t count=rtc_fifo_get_tagcount();
while (count--)
rtc_mem_write(tags_at++,0);
}
static inline void rtc_fifo_clear_content(void)
{
uint32_t first=rtc_fifo_get_first();
rtc_fifo_put_tail(first);
rtc_fifo_put_head(first);
rtc_fifo_put_count(0);
rtc_fifo_put_tail_t(0);
rtc_fifo_put_head_t(0);
rtc_fifo_clear_tags();
}
static inline void rtc_fifo_init(uint32_t first, uint32_t last, uint32_t tagcount)
{
rtc_fifo_put_loc(first,last,tagcount);
rtc_fifo_clear_content();
}
static inline void rtc_fifo_init_default(uint32_t tagcount)
{
if (tagcount==0)
tagcount=RTC_DEFAULT_TAGCOUNT;
rtc_fifo_init(RTC_DEFAULT_FIFO_START,RTC_DEFAULT_FIFO_END,tagcount);
}
static inline uint8_t rtc_fifo_check_magic(void)
{
if (rtc_mem_read(RTC_FIFO_MAGIC_POS)==RTC_FIFO_MAGIC)
return 1;
return 0;
}
static inline void rtc_fifo_set_magic(void)
{
rtc_mem_write(RTC_FIFO_MAGIC_POS,RTC_FIFO_MAGIC);
}
static inline void rtc_fifo_unset_magic(void)
{
rtc_mem_write(RTC_FIFO_MAGIC_POS,0);
}
static inline void rtc_fifo_deep_sleep_until_sample(uint32_t min_sleep_us, uint32_t mhz)
{
uint32_t align=rtc_mem_read(RTC_ALIGNMENT_POS);
rtc_time_deep_sleep_until_aligned(align,min_sleep_us,mhz);
}
static inline void rtc_fifo_prepare(uint32_t samples_per_boot, uint32_t us_per_sample, uint32_t tagcount)
{
rtc_mem_write(RTC_SAMPLESPERBOOT_POS,samples_per_boot);
rtc_mem_write(RTC_ALIGNMENT_POS,us_per_sample);
rtc_put_samples_to_take(0);
rtc_fifo_init_default(tagcount);
rtc_fifo_set_magic();
}
#endif

View File

@ -37,6 +37,7 @@
#define LUA_USE_MODULES_DHT
#define LUA_USE_MODULES_RTCMEM
#define LUA_USE_MODULES_RTCTIME
#define LUA_USE_MODULES_RTCFIFO
#endif /* LUA_USE_MODULES */

View File

@ -197,6 +197,14 @@
#define ROM_MODULES_RTCTIME
#endif
#if defined(LUA_USE_MODULES_RTCFIFO)
#define MODULES_RTCFIFO "rtcfifo"
#define ROM_MODULES_RTCFIFO \
_ROM(MODULES_RTCFIFO, luaopen_rtcfifo, rtcfifo_map)
#else
#define ROM_MODULES_RTCFIFO
#endif
#define LUA_MODULES_ROM \
ROM_MODULES_GPIO \
ROM_MODULES_PWM \
@ -222,5 +230,6 @@
ROM_MODULES_DHT \
ROM_MODULES_RTCMEM \
ROM_MODULES_RTCTIME \
ROM_MODULES_RTCFIFO \
#endif

198
app/modules/rtcfifo.c Normal file
View File

@ -0,0 +1,198 @@
// Module for RTC sample FIFO storage
#include "lauxlib.h"
#include "user_modules.h"
#include "rtc/rtcfifo.h"
// rtcfifo.prepare ([{sensor_count=n, interval_us=m, storage_begin=x, storage_end=y}])
static int rtcfifo_prepare (lua_State *L)
{
uint32_t sensor_count = RTC_DEFAULT_TAGCOUNT;
uint32_t interval_us = 0;
int first = -1, last = -1;
if (lua_istable (L, 1))
{
#ifdef LUA_USE_MODULES_RTCTIME
lua_getfield (L, 1, "interval_us");
if (lua_isnumber (L, -1))
interval_us = lua_tonumber (L, -1);
lua_pop (L, 1);
#endif
lua_getfield (L, 1, "sensor_count");
if (lua_isnumber (L, -1))
sensor_count = lua_tonumber (L, -1);
lua_pop (L, 1);
lua_getfield (L, 1, "storage_begin");
if (lua_isnumber (L, -1))
first = lua_tonumber (L, -1);
lua_pop (L, 1);
lua_getfield (L, 1, "storage_end");
if (lua_isnumber (L, -1))
last = lua_tonumber (L, -1);
lua_pop (L, 1);
}
else if (!lua_isnone (L, 1))
return luaL_error (L, "expected table as arg #1");
rtc_fifo_prepare (0, interval_us, sensor_count);
if (first != -1 && last != -1)
rtc_fifo_put_loc (first, last, sensor_count);
return 0;
}
// ready = rtcfifo.ready ()
static int rtcfifo_ready (lua_State *L)
{
lua_pushnumber (L, rtc_fifo_check_magic ());
return 1;
}
static void check_fifo_magic (lua_State *L)
{
if (!rtc_fifo_check_magic ())
luaL_error (L, "rtcfifo not prepared!");
}
// rtcfifo.put (timestamp, value, decimals, sensor_name)
static int rtcfifo_put (lua_State *L)
{
check_fifo_magic (L);
sample_t s;
s.timestamp = luaL_checknumber (L, 1);
double val = luaL_checknumber (L, 2);
s.decimals = luaL_checknumber (L, 3);
uint32_t i = s.decimals;
while (i--)
val *= 10;
s.value = val;
size_t len;
const char *str = luaL_checklstring (L, 4, &len);
union {
uint32_t u;
char s[4];
} conv = { 0 };
strncpy (conv.s, str, len > 4 ? 4 : len);
s.tag = conv.u;
rtc_fifo_store_sample (&s);
return 0;
}
static int extract_sample (lua_State *L, const sample_t *s)
{
lua_pushnumber (L, s->timestamp);
double val = s->value;
int i;
for (i = 0; i < s->decimals; ++i)
val /= 10;
lua_pushnumber (L, val);
lua_pushnumber (L, s->decimals);
union {
uint32_t u;
char s[4];
} conv = { s->tag };
if (conv.s[3] == 0)
lua_pushstring (L, conv.s);
else
lua_pushlstring (L, conv.s, 4);
return 4;
}
// timestamp, value, decimals, sensor_name = rtcfifo.pop ()
static int rtcfifo_pop (lua_State *L)
{
check_fifo_magic (L);
sample_t s;
if (!rtc_fifo_pop_sample (&s))
return 0;
else
return extract_sample (L, &s);
}
// timestamp, value, decimals, sensor_name = rtcfifo.peek ([offset])
static int rtcfifo_peek (lua_State *L)
{
check_fifo_magic (L);
sample_t s;
uint32_t offs = 0;
if (lua_isnumber (L, 1))
offs = lua_tonumber (L, 1);
if (!rtc_fifo_peek_sample (&s, offs))
return 0;
else
return extract_sample (L, &s);
}
// rtcfifo.drop (num)
static int rtcfifo_drop (lua_State *L)
{
check_fifo_magic (L);
rtc_fifo_drop_samples (luaL_checknumber (L, 1));
return 0;
}
// num = rtcfifo.count ()
static int rtcfifo_count (lua_State *L)
{
check_fifo_magic (L);
lua_pushnumber (L, rtc_fifo_get_count ());
return 1;
}
#ifdef LUA_USE_MODULES_RTCTIME
// rtcfifo.dsleep_until_sample (min_sleep_us)
static int rtcfifo_dsleep_until_sample (lua_State *L)
{
check_fifo_magic (L);
uint32_t min_us = luaL_checknumber (L, 1);
rtc_fifo_deep_sleep_until_sample (min_us, CPU_DEFAULT_MHZ); // no return
return 0;
}
#endif
// Module function map
#define MIN_OPT_LEVEL 2
#include "lrodefs.h"
const LUA_REG_TYPE rtcfifo_map[] =
{
{ LSTRKEY("prepare"), LFUNCVAL(rtcfifo_prepare) },
{ LSTRKEY("ready"), LFUNCVAL(rtcfifo_ready) },
{ LSTRKEY("put"), LFUNCVAL(rtcfifo_put) },
{ LSTRKEY("pop"), LFUNCVAL(rtcfifo_pop) },
{ LSTRKEY("peek"), LFUNCVAL(rtcfifo_peek) },
{ LSTRKEY("drop"), LFUNCVAL(rtcfifo_drop) },
{ LSTRKEY("count"), LFUNCVAL(rtcfifo_count) },
#ifdef LUA_USE_MODULES_RTCTIME
{ LSTRKEY("dsleep_until_sample"), LFUNCVAL(rtcfifo_dsleep_until_sample) },
#endif
{ LNILKEY, LNILVAL }
};
LUALIB_API int luaopen_rtcfifo (lua_State *L)
{
#if LUA_OPTIMIZE_MEMORY > 0
return 0;
#else
luaL_register (L, AUXLIB_RTCFIFO, rtcfifo_map);
return 1;
#endif
}

View File

@ -1,4 +1,4 @@
// Module for RTC user memory access
// Module for RTC time keeping
#include "lauxlib.h"
#include "rtc/rtctime.h"