timer, task loop management, logging, basic stateful hardware routines for fastCode on Arduino
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
SR_PID
SR_Radio
examples/SRResourceDemo
README.md
SRButton.h
SRCRC.h
SRHSVRGB.h
SRLED.h
SRLog.h
SRLoopTally.h
SRMessage.h
SRPID.h
SRPRNG.h
SRRadio.h
SRResources.h
SRSmooth.h
SRTimer.h
freeRAM.h
keywords.txt

README.md

####API descriptions for SR Resource functions:

#####SRTimer time-as-event action functions

#####SRLED communicating status through a blinking LED

#####SRMessage general-purpose messaging and commands including EEPROM configuration storage.

#####SRCRC 8-bit CRC (cyclical redundancy check) routines for communications

#####SRPRNG 32-bit Marsaglia XORshift pseudorandom number generator

#####SRloopTally monitor/measure loop() realitime performance

####simple timer event system based upon millis().

#####tom jennings

the timer system handles repetitive timed events. it uses millis() and a small data structure to maintain state. it creates self-rearming repeat timers with features described below.

the core function of the timer system is the boolean function timer(); it returns true when the scheduled time interval is reached or exceeded. the timer is self-resetting, eg. it auto-repeats.

	SRTimer T;
instantiate the timer object. 
	void begin (unsigned n);
allocate memory for n timers. must be called
before any timer is used.
	void setTimer (unsigned T, unsigned M);
	void setDeciTimer (unsigned T, unsigned M);
set timer T to fire M milliseconds (setTimer()) or
M deciseconds (setDeciTimer() from now, and every 
M milliseconds/deciseconds thereafter.
	boolean timer (unsigned T);
returns true if the set timer for timer T has been reached
or exceeded, and if so, re-set the timer to trigger again
in the previously milliseconds.
	void resetTimer (unsigned T);
	void trigTimer (unsigned T);
resetTimer causes the timer to immediately begin timing
from the current moment (maximum possible time); trigTimer()
causes the timer to fire immediately (shortest possible time),
eg. at the next invokation of timer ().
	unsigned long getTimer (unsigned T);
	unsigned long untilTimer (unsigned T);
getTimer() returns the interval previously set by 
setTimer()/setDeciTimer(), in milliseconds; untilTimer()
returns the time remaining until the timer fires, in
milliseconds.

####LED blinky functiony

this is a succinct system for visual system status using a single LED. there are two major modes of operation, blink and sequence.

as are most of the SR libraries, the heart of the LED system is a state machine that must be invoked within loop() continuously. the state machine has extremely low latency (tens of microseconds).

both "modes" below are actually interoperable; see the method descriptions for details.

BLINK

the blink functions cause the LED to turn on and blink in a proscribed manner; on time, off time, and number of on/off blink cycles specified.

SEQUENCE

the sequence functions create a visual signalling space, where a discernable number of sequential blinks is used to signal status or events. the idea is that an interval is defined, typically one or two seconds and is split into a number of "time slots".

typical usage is to want to indicate various operating states of some remote and/or embedded machine that has no explicit information display. with an interval of 2.5 seconds divided into 8 slots, "normal" is indicated by a blink sequence of 1; in other words, the LED blinks one time in 2.5 seconds, within 1/8th of the interval. different operating states idle, busy, ready, error, waiting, etc) can be indicated by 2, 3, 4, 5, etc sequential blinks. given 8 time slots, three blinks followed by a pause (that is the remaining 5 blink times) is easily seen.

(in detail, each time slot is split between LED on and off times, with OFF being 3/5ths of the time slot and ON the rest.)

   SRLED L;

instantiates the object, for blinking and/or sequencing.

   L.begin (99);	// LED on pin 99

both blink and sequence functions requires setup with begin() to initialize the digital pin and state machines.

additionally, if the sequence method is to be used then seqStart() must also be invoked:

   L.seqStart (unsigned interval, uint8_t slots);

this specifies the sequence interval length in milliseconds, and the number of blink time slots the interval is divided into. though it must be called at least once, it can be used at any time. reuse will stop all activity.

   L.LED ();

the LED() method runs all state machines necessary for LED blinking operations. it consumes a dozen microseconds only. must be called continuously, within loop(), and assumes all non-blocking code conforming to the implied Wiring model.

   L.blink (n);     // blink once, ON for N mS
   L.blink (n, m);  // blink ON N mS, M times

the blink() method triggers the future blinking of the LED as indicated: invoked with one argument n the LED blinks on for n milliseconds, one time. with two arguments n, m the LED blinks on n milliseconds, off for n milliseconds, and repeats m times.

if either is called within the time window in which a previous blink() or seq() is active, the previous activity is immediately cancelled and the new one begins.

  L.seq (n);

seq() sets the number of ON blinks of the LED within the set interval. 0 stops all blinking, and n .GE. slots is continuous blinking.

the blink sequence will begin with the new value n given starting at the beginning of the next interval; the current or previous value will remain in force until then. if no sequence was running, or blink() was in use, then the sequence begins immediately.

#####SRMessage

the SR Message system is a very generalized system for send, receiving, parsing text-based messages. messages consist of a single command letter (A..Za..z) and a decimal numeric argument. messages fit through all channels (Arduino IDE Serial Monitor, USB, radio, EEPROM), are readable and typeable by humans. it is extremely compact.

the Message system accommodates up to four levels of priority, to limit what a particular channel can do; eg. highest priority reserved for EEPROM (to store/fetch configuration), or lowest priority for commands received via radio.

all message handling is done internally to the object, with the actual dispatching of (command-letter, numeric-argument) handled by user code of near trivial simplicity and compactness.

the overall programming model is straightforward. you the programmer need to code the command dispatcher (example below). after instantiation and initialization, the Message object is passed the address of your command dispatcher. thence, within loop(), block/string (radio, EEPROM) or character data (USB, etc) is passed to the object as it arrives. no further action is needed.

whether characters arrive all at once (block/string) or one at a time, the Message state machine decomposes characters into numeric value and command, and when complete, calls the user-code dispatcher to execute it.

since messages are simple digits:letter text strings, they can be generated with anything that can assemble character strings. however the provided message-building methods make this easy, and support Flock and Peep radio addressing as well.

when your program uses text-based commands for both communication (to a desktop Processing or other application, or to another controller via serial or SoftwareSerial, or via radio), the advantages of a uniform interface for all channels becomes more obvious. for example, program configuration storage in EEPROM is simply another destination/source for sending/receiving messages, eg. configuration commands. and it is then when priority levels become useful; you can easily give USB (Arduino IDE Serial Monitor) and EEPROM highest-priority to set global congiguration data (ID strings, whatever) that would be a bad idea to honor when arriving via radio.

instantiate the object with

SRMessage M;

instantiation does not allocate RAM nor resources.

initialize the object (allocates RAM and resources) with:

void begin();

and if Message will be used to compose messages, then it needs to be told what buffer to use and how large it is. if the FLock radio system is used, it's transmit buffer is most useful.

void setMessageBuffer (char * p, unsigned n);

Message command dispatcher(s) must be added before Message can be used. while these could be added/changed at any time, generally they're set once in setup(). there can be up to four dispatchers.

two dispatchers are "normal" priority and two are high priority. unless specified priority is normal. the data channels typically use normal priority, but options exist to specify otherwise (eg. for EEPROM to use a high-priority dispatcher).

you the programmer must write the command dispatcher, but follows is a perfectly usable example. the idea is that the hard part, parsing state machine, error handling, etc, is done by the Message object, and the easiest part is done in your code -- storing or using a numeric argument from a command.

here is a typical command dispatcher. the address of this is passed to the Message object via addRxParser(), below. the function must accept two arguments: a character and an unsigned integer. it must return a value that indicates whether the given command is "yours", eg. you ate it, you processed it. this is of critical importance only if you have more than one command dispatcher.

boolean radioMessages (char c, unsigned nnn) {

boolean ok= true;					// let's be optimistic...
switch (c) {
	case 'A': SOMEVARIABLE= nnn; break;		// store a value
	case 'B': someFunction (nnn); break;		// pass a value to a function
	case 'Z': digitalWrite (13, nnn); break;	// manipulate a digital pin

	default: ok= false; break;			// wasn't a command, after all
}
return ok;

}

with the above function somewhere in your source code, you attach it to the Message system with one of these methods:

void addRxParser (bool (* func) (char, unsigned)); void addRxParser (bool (* func) (char, unsigned), unsigned index);

if there is only one data source (for example, USB) and one command dispatcher, then that dispatcher is given all commands to execute or ignore.

the flag returned by your command dispatcher code is how Message supports multiple command dispatchers. there are four dispatcher slots/priorities, 0..3, with 0 being the highest priority and 3 the lowest. each channel has an associated "starting level". when commands arrive on a channel, Message passes (command, value) to the starting level command dispatcher. if that dispatcher does not "eat" the command, then the command is passed to the next, frmo highest to lowest priority. a useful example might be when an Arduino is powered on and runs setup(), configuration data stored as text messages in EEPROM are passed through the highest-priority command dispatcher to set global one-time settings. also for example, command messages received via USB (from a host computer) would use the rxUSB() channel, which has lower priority, and cannot access commands that EEPROM can. this scheme can be extended to four levels of priority.

data sources, channels, are either serial characters from (at the moment, only) USB serial, or a block or string of text (stored in EEPROM, arrived via radio, a string constant or array, etc).

serial characters received via USB are probably the most common. however they arrive one at a time, either at-once or separated in time by milliseconds or seconds.

void rxUSB(); void rxUSB (char c);

rxUSB() polls for available characters and handles them as/when/if they arrive. this code does not block. rxUSB (char c) is the same but processes the given character instead of checking via Serial.available(). rxUSB() uses privilege level 1, second highest.

void rxString (char *p, unsigned len); void rxString (char *p, unsigned len, unsigned level);

rxString() processes all of the commands (assuming there are any) contained in the given null-terminated string.

if the Message object's message-building functions are to be used, setMessageBuffer() must be called first (see note above near begin()).

there are three functions involved: messageBegin() prepares the message buffer for a new message. messageAddr() inserts a Flock type radio address and optional acknowledgement requst to the top of the message. if used it must be called before any messageAdd().

messageAdd() appends a Message formatted command to the message buffer, assuming there is room in the buffer.

all of these functions leave a valid formatted message in the buffer; it is if nothing else a standard C null-terminated string.

void messageBegin (); void messageAddr (char dest, char id, bool ack); void messageAdd (char cmd, unsigned nnn);

#####SRCRC8

this is an implementation of the Dallas/Maxim iButton 8-bit CRC algorithm, more than adequate for most modest communication needs.

from http://nongnu.org/avr-libc/user-manual/group__util__crc.html

Optimized Dallas (now Maxim) iButton 8-bit CRC calculation.

Polynomial: x^8 + x^5 + x^4 + 1 (0x8C)

Initial value: 0x0

See http://www.maxim-ic.com/appnotes.cfm/appnote_number/27

instantiate with

SRCRC8 CRC;

initialize with

CRC.begin();

uint8_t crc8 (uint8_t crc, uint8_t data); uint8_t crc8buff (uint8_t * p, unsigned count);

the first form calculates the CRC described on the given data byte. for most purposes crc passed to it is 0, but this form allows accumulating a CRC calculation over many bytes as they arrive from some unbuffered source, in which case the caller is maintaining the intermediate CRC value, initially 0. the second form calculates the polynomial over the specified block of data instead of just one byte at a time.

#####SRPRNG

SRPRNG is a 32-bit Marsaglia XORshift pseudo-random number generator. these are well-known, high quality pseudorandom sequences. all PRNGs return the same sequence given the same starting seed, which is required for certain applications such as radio frequency-hop sequences.

this code is courtesy WebDrake https://github.com/WebDrake/xorshift/blob/master/xorshift.c

see also this dicussion http://www.javamex.com/tutorials/random_numbers/xorshift.shtml#.VNmzLy5gHLU

i have added a seed function that multplies the original, example seed with a passed parameter. this allows both ends to begin a predictable sequence.

Marsaglia PRNG never returns 0, a serious problem if the return value is traversing a table; but used modulo (N < 32) problem solved.

instantiate with

SRPRNG RNG;

this instantiates the object but doesn't allocate resources for it. do that with

RNG.begin();

begin() also seeds the sequence with the 0'th permutation of the seed.

RNG.xor32seed (unsigned long s);

begin a sequence anew after seeding it with s.

RNG.xor32();

with each invokation of xor32(), the next value in the sequence is returned.

####arduino loop() performance tally

SRLoop measures basic loop() statistics: loop() iterations per second, average loop() execution time, longest loop() execution time.

the strategic goal is to help write code that does not block, that runs through loop() as fast as possible, in order to code to a state-machine, non-blocking, cooperative-tasking model so strongly implied (and desireable) in the Wiring setup()/loop() model.

this is a bit of a crock, intentionally; it's meant to be a lightweight test/performance hack. it makes some simplistic assumptions: the cpu can't execute more than (unsigned) iterations per specified report time. that loop() execution is more or less 1..65535 per second, all of the above entirely reasonable for ATMEGA328 and it's ilk.

the first report is often nonsense, as it initializes after the first report.

instantiate with

   SRloopTally SRL;

initialize with

   SRL.begin (10);		// 10-second report interval

the background state machine is run by calling

   unsigned x= SRL.tally();

continuously in loop(). the return value is 0 for most invokations, but when a report is made it returns the longest iteration, in milliseconds. somewhat useful for exceptionalizing crazy values ("if x > 100 SOMETHING BLOCKED")

at/after the specified report interval, tally() prints out one compact line:

#loop: 6102/s, 164uS avg, (2mS)

which indicates 6102 iterations/second, 164 microsecond average loop() execution time, the longest single loop() iteration of 2mS. 'longest' is of low resolution because the goal is to identify grossly anomalous loops (eg. encountering some function or code that blocks for an extended period.)