PairHist C Reference

This maintains a circular buffer named pairhistory. It contains all pairs that are in the NDEF message circular buffer stored in the NFC-readable EEPROM. A crucial difference is that pairhistory is stored in RAM, so its contents can be accessed quickly.

This allows for a hash to be taken of the unencoded circular buffer pairs, each time this list changes. The decoder uses this to verify that it has decoded the circular buffer faithfully: It must output the same list of pairs, as fed to the encoder with multiple calls to enc_pushsample().

After decoding the circular buffer, the hash is calculated. The decoder checks this equals the encoder hash, which is extracted from endstop_t::hashnb64 in the NDEF message. If it does not, an error is raised and no data are returned.

The hash function can either be MD5 or HMAC-MD5 The former is a simple checksum for debugging the codec. It should not be used in production, because it is no good as a hash function and collisions can be found easily. The HMAC-MD5 should be used instead. A detailed discussion can be found in the CODEC_FEAT_24.

When a pair is pushed to or overwritten in the NDEF message, pairhistory must be updated with pairhist_push() and pairhist_ovr() respectively. This ensures that the output of pairhist_hash() will be accurate.

struct pair_t
#include <pairhist.h>

Structure to hold one sample consisting of two 12-bit readings.

Public Members

unsigned char rd0Msb

Reading0 Most significant byte.

unsigned char rd1Msb

Reading1 Most significant byte.

unsigned char Lsb

Least significant 4-bit nibbles of reading0 and reading1.

struct hashn_t
#include <pairhist.h>

Structure to hold hash and npairs as per CODEC_SPEC_14.

Public Members

unsigned char hash[7]

Last 7 bytes of the MD5 or HMAC-MD5 hash.

unsigned char npairs[2]

Number of valid pairs in the circular buffer.



Length of the MD5 digest (output) in bytes.


Length of the MD5 input message block in bytes.


void fram_write_enable(void)

Enable writes to FRAM. Should be defined in the processor-specific cuplTag project.

void fram_write_disable(void)

Disable writes to FRAM. Should be defined in the processor-specific cuplTag project.

void pairhist_ovr(pair_t pair)

Overwrites the most recent pair in the history buffer. This is used when the format stipulates one reading per sample (rather than one pair per sample). For the first sample, a full pair is written with The second reading is set to an invalid value. On the next sample, the second reading in the pair is overwritten, so the history buffer must be overwritten with pairhist_ovr.


pair – New value of the most recent pair.

void pairhist_init()

Initialise the cursorindex The circular buffer itself (pairhistory) does not need to be initialised.

void pairhist_push(pair_t pair)

Pushes a new pair onto the history buffer. This operation overwrites an old pair if the circular buffer is full.


pair – Value of the new pair.

pair_t pairhist_read(unsigned int offset, int *error)

Reads one pair at an offset from the end of pairhistory. This function makes it possible to read pairhistory as if it was a linear buffer.

  • offset – When 0, the most recent pair is returned. When 1, the 2nd most recent pair is returned. When BUFLEN_PAIRS-1, the oldest pair is returned. Any larger offset is invalid.

  • error – Pointer to an error variable. This is set to 1 when offset exceeds the length of the circular buffer (BUFLEN_PAIRS-1). It is 0 otherwise.


A pair read from pairhistory or a struct containing 3 zeroes if an error has occurred.

hashn_t pairhist_hash(int npairs, int usehmac, unsigned int loopcount, unsigned int resetsalltime, unsigned int batv_resetcause, int endstopindex)

Calculates a hash from pairhistory and other state variables.

The hash is calculated according to the table in CODEC_FEAT_24. If HMAC is enabled then the output is the last seven bytes of the HMAC-MD5 digest. If it is not then the hash is an MD5 checksum only. Note that the latter is intended for debug purposes only.

The MD5 is calculated iteratively 64 bytes at a time with multiple calls to MD5_Update().

  • npairs – The number of pairs from pairhistory to include in the hash.

  • usehmac – When 0 the hash is MD5 only. Otherwise it is HMAC-MD5.

  • loopcount – Number of times the circular buffer cursor has looped (or wrapped) from the end to the beginning.

  • resetsalltime – Number of times the host firmware has logged a Power-on-Reset.

  • batv_resetcause – 8-bit battery voltage concatenated with the 8-bit resetcause variable.

  • endstopindex – Offset of the ENDSTOP_BYTE relative to the start of the NDEF message circular buffer.


A value of type hashn_t. This contains the last 7 hash bytes together with npairs.


nv_t nv
pair_t pairhistory[BUFLEN_PAIRS] = {0}

Array of unencoded pairs. This mirrors the circular buffer of base64 encoded pairs stored in EEPROM.

int cursorindex = -1

Index marking the end of the circular buffer. The most recent sample is stored here. The next index contains the oldest sample.

unsigned char msgblock[MD5BLKLEN_BYTES]

Block to hold message data as an input to the MD5 algorithm.

const int buflenpairs = BUFLEN_PAIRS

Length of the circular buffer in pairs.

static const char ipadchar = 0x36

Inner padding byte for HMAC as defined in RFC 2104.

static const char opadchar = 0x5C

Outer padding byte for HMAC as defined in RFC 2104.

static MD5_CTX ctx

MD5 context.