Worked on firmware

This commit is contained in:
Martin Bauer
2019-08-22 21:33:36 +02:00
parent 9889d41805
commit 6a724b284f
24 changed files with 422 additions and 209 deletions

17
lib/basic/Dtypes.h Normal file
View File

@@ -0,0 +1,17 @@
inline void _assert(const char* expression, const char* message, const char* file, int line)
{
Serial.print("Assert ");
Serial.print(file);
Serial.print(" : ");
Serial.print(line);
Serial.print(" '");
Serial.print(expression);
Serial.println("' failed.");
}
template< typename T>
inline String toString(const T & t) {
return String(t);
}
#define assert(EXPRESSION, MSG) ((EXPRESSION) ? (void)0 : _assert(#EXPRESSION, #MSG, __FILE__, __LINE__))

40
lib/basic/MockDtypes.h Normal file
View File

@@ -0,0 +1,40 @@
#include <string>
#include <cstdint>
#include <cstring>
#include <arpa/inet.h>
#include <algorithm>
#include <iostream>
#include <sstream>
typedef uint32_t uint_t;
typedef std::string String;
typedef uint8_t byte;
typedef const char * PGM_P;
using std::min;
using std::max;
inline uint32_t strlen_P(PGM_P str) {
return std::strlen(str);
}
inline const char * F( const char * in) { return in; }
inline void _assert(const char* expression, const char* message, const char* file, int line)
{
std::cerr << "Assert " << file << ":" << line << " '" << expression << "' failed." << std::endl;
std::cerr << message << std::endl;
abort();
}
template< typename T>
inline std::string toString(const T & t) {
std::stringstream stream;
stream << t;
return stream.str();
}
#define assert(EXPRESSION, MSG) ((EXPRESSION) ? (void)0 : _assert(#EXPRESSION, #MSG, __FILE__, __LINE__))

18
lib/basic/MockSerial.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
#include <iostream>
class SerialMock {
public:
template< typename T>
static inline void print(const T & str) {
std::cout << str;
}
template< typename T>
static inline void println(const T & str) {
std::cout << str << std::endl;
}
};
static SerialMock Serial;

32
lib/scale/MockScale.h Normal file
View File

@@ -0,0 +1,32 @@
#include <cstdint>
class MockScale
{
public:
MockScale( uint16_t valueMin=0, uint16_t valueMax=50)
: valueMin_(valueMin), valueMax_(valueMax), currentValue_(valueMin), direction(1)
{}
bool measure(uint16_t & measurementOut) {
currentValue_ += direction;
if ( currentValue_ >= valueMax_)
direction = -1;
else if ( currentValue_ <= valueMin_ )
direction = +1;
measurementOut = currentValue_;
return true;
}
void begin(uint32_t , uint32_t ) {
};
void tare(uint32_t ) {
}
private:
uint16_t valueMin_;
uint16_t valueMax_;
uint16_t currentValue_;
int direction;
};

30
lib/scale/Scale.h Normal file
View File

@@ -0,0 +1,30 @@
#include "HX711.h"
#include <cstdint>
template<int DIVIDER=128>
class Scale
{
public:
bool measure(uint16_t & measurementOut) {
if (hx711_.is_ready())
{
uint32_t value = hx711_.get_value(MEASUREMENT_AVG_COUNT);
measurementOut = (int16_t)(value / DIVIDER);
return true;
}
else
return false;
}
void begin(uint32_t pinDOUT, uint32_t pinSCK) {
hx711_.begin(pinDOUT, pinSCK);
};
void tare(uint32_t numMeasurementsToAverage=50) {
hx711_.tare(numMeasurementsToAverage);
}
private:
HX711 hx711_;
};

View File

@@ -0,0 +1,124 @@
#include "SessionChunk.h"
template<typename Measurement_T, typename Reader, typename Writer, uint32_t CHUNK_SIZE>
class MeasurementSession {
public:
typedef SessionChunk<Measurement_T, CHUNK_SIZE> Chunk_T;
MeasurementSession()
: currentChunk(&chunks[0]),
otherChunk(&chunks[1]) {}
void init(uint32_t epochStartTime) {
currentChunk = &chunks[0];
currentChunk->init(epochStartTime, 0);
}
bool addPoint(Measurement_T measurement) {
const bool successful = currentChunk->addPoint(measurement);
if (!successful) {
rotate();
const bool secondInsertSuccess = currentChunk->addPoint(measurement);
assert(secondInsertSuccess, "Session: insertion after rotation failed");
//TODO check that there is place for file - remove old files
}
return true;
}
void finalize() {
if( otherChunkFilled() )
saveChunkToFile(otherChunk);
if( currentChunk->numMeasurements() > 0) {
saveChunkToFile(currentChunk);
}
currentChunk->init(0, 0);
otherChunk->init(0, 0);
}
template<typename T>
void serialize(StreamingMsgPackEncoder<T> & encoder, uint32_t startIdx) const
{
const uint32_t lastIdx = currentChunk->getStartIndex() + currentChunk->numMeasurements();
if( lastIdx <= startIdx) {
encoder.template sendArray<Measurement_T>(nullptr, 0);
return;
}
Chunk_T::sendHeader(encoder, currentChunk->getStartTime(), startIdx);
encoder.template sendArrayHeader<Measurement_T>(lastIdx - startIdx);
while(startIdx < lastIdx)
startIdx = serializeChunk(encoder, startIdx);
assert(startIdx == lastIdx, "Not all data was sent");
}
private:
void rotate() {
if( otherChunkFilled() )
saveChunkToFile(otherChunk);
swapChunks();
currentChunk->init(otherChunk->getStartTime(), otherChunk->getStartIndex() + CHUNK_SIZE);
}
bool otherChunkFilled() const {
return otherChunk->numMeasurements() > 0;
}
void swapChunks() {
Chunk_T *tmp = currentChunk;
currentChunk = otherChunk;
otherChunk = tmp;
}
void saveChunkToFile(Chunk_T *chunk) const {
const uint32_t chunkNr = chunk->getStartIndex() / CHUNK_SIZE;
const auto fileName = chunkFileName(chunkNr, chunk->getStartTime());
Writer writer( fileName );
chunk->serialize(writer.encoder());
};
template< typename T>
uint32_t serializeChunk(StreamingMsgPackEncoder<T> & encoder, uint32_t startIdx) const {
assert( startIdx < currentChunk->getStartIndex() + currentChunk->numMeasurements(),
"serializeChunk: invalid startIdx" );
if( startIdx >= currentChunk->getStartIndex() ) {
encoder.sendArrayPartialContents( currentChunk->getDataPointer(), currentChunk->numMeasurements() );
return currentChunk->getStartIndex() + currentChunk->numMeasurements();
} else if( startIdx >= otherChunk->getStartIndex() && otherChunkFilled() ) {
encoder.sendArrayPartialContents( otherChunk->getDataPointer(), otherChunk->numMeasurements() );
assert( otherChunk->numMeasurements(), CHUNK_SIZE );
return otherChunk->getStartIndex() + otherChunk->numMeasurements();
} else {
if( encoder.getSizeCountMode() ) {
encoder.template sendArrayPartialContents<Measurement_T>(nullptr, CHUNK_SIZE);
} else {
const uint32_t chunkNr = startIdx / CHUNK_SIZE;
const auto chunkFileNameStr = chunkFileName(chunkNr, currentChunk->getStartTime());
Reader reader(chunkFileNameStr);
reader.seek(Chunk_T::template valueOffset<T>());
const uint32_t PART_SIZE = 32;
#ifndef ARDUINO
static_assert((PART_SIZE < CHUNK_SIZE) && (CHUNK_SIZE % PART_SIZE == 0));
#endif
Measurement_T buffer[PART_SIZE];
for(uint32_t i = 0; i < CHUNK_SIZE; i += PART_SIZE)
{
reader.readBytes((char*) buffer, sizeof(Measurement_T) * PART_SIZE);
encoder.template sendArrayPartialContents<Measurement_T>(buffer, PART_SIZE);
}
}
return startIdx + CHUNK_SIZE;
}
}
static String chunkFileName(uint32_t chunkNr, uint32_t startTime) {
return("/dat/" + toString(startTime) + "_" + toString(chunkNr));
}
Chunk_T chunks[2];
Chunk_T *currentChunk;
Chunk_T *otherChunk;
};

84
lib/session/MockStorage.h Normal file
View File

@@ -0,0 +1,84 @@
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <vector>
class VectorAdaptor {
public:
VectorAdaptor(std::vector<uint8_t> * v) : v_(v) {}
void write(const char *data, uint32_t size) {
v_->insert(v_->end(), data, data + size );
}
private:
std::vector<uint8_t> * v_;
};
class FilePtrAdaptor {
public:
FilePtrAdaptor(const String &fileName) {
static const String baseDirectory(".");
auto fullFileName = baseDirectory + fileName;
fptr = fopen(fullFileName.c_str(), "wb");
}
~FilePtrAdaptor() {
fclose(fptr);
}
FilePtrAdaptor(const FilePtrAdaptor &) = delete;
void operator=(const FilePtrAdaptor &) = delete;
void write(const char *data, uint32_t size) {
fwrite(data, size, 1, fptr);
}
private:
FILE * fptr;
};
class MockStorageWriter {
public:
MockStorageWriter(const String &fileName) {
adaptor_ = new FilePtrAdaptor(fileName);
encoder_ = new StreamingMsgPackEncoder <FilePtrAdaptor>(adaptor_);
}
~MockStorageWriter(){
delete adaptor_;
delete encoder_;
}
MockStorageWriter(const MockStorageWriter &) = delete;
StreamingMsgPackEncoder<FilePtrAdaptor> &encoder() { return *encoder_; }
private:
FilePtrAdaptor * adaptor_;
StreamingMsgPackEncoder <FilePtrAdaptor> * encoder_;
};
class MockStorageReader {
public:
MockStorageReader(const String &fileName)
{
static const String baseDirectory(".");
auto fullFileName = baseDirectory + fileName;
fptr = fopen(fullFileName.c_str(), "rb");
}
uint32_t readBytes(char *buffer, size_t length) {
return fread(buffer, length, 1, fptr);
}
bool seek(uint32_t pos) {
auto ret = fseek(fptr, pos, SEEK_SET);
return ret == 0;
}
private:
FILE * fptr;
};

111
lib/session/SessionChunk.h Normal file
View File

@@ -0,0 +1,111 @@
#include "StreamingMsgPackEncoder.h"
#include <cstdint>
template<typename Measurement_T, uint32_t SIZE>
class SessionChunk
{
public:
SessionChunk()
: nextFree(0), sessionStartTime(0), startIndex(0)
{}
void init(uint32_t epochStartTime, uint32_t startIdx)
{
nextFree = 0;
sessionStartTime = epochStartTime;
startIndex = startIdx;
}
uint32_t getStartTime() const {
return sessionStartTime;
}
uint32_t getStartIndex() const {
return startIndex;
}
uint32_t numMeasurements() const {
return nextFree;
}
bool addPoint(Measurement_T measurement)
{
if( nextFree >= SIZE)
return false;
values[nextFree] = measurement;
nextFree++;
return true;
}
template<typename T>
void serialize(StreamingMsgPackEncoder<T> & encoder) const
{
sendHeader(encoder, sessionStartTime, startIndex);
encoder.sendArray(values, nextFree);
}
/*
template<typename T>
void serialize(StreamingMsgPackEncoder<T> & encoder, uint32_t start, uint32_t end) const
{
if( start < this->startIndex )
start = this->startIndex;
if( end == 0 ) {
end = start + this->nextFree;
}
sendHeader(encoder, sessionStartTime, start);
bool sendEmpty =
(start >= end) ||
(end <= this->startIndex) ||
(start >= (this->startIndex + this->nextFree));
if( sendEmpty ) {
encoder.sendArray(nullptr, 0);
} else {
const uint32_t idxStart = (start - this->startIndex);
const uint32_t length = min(nextFree, end - start);
encoder.sendArray(values + idxStart, length);
}
}*/
template<typename T>
static uint32_t valueOffset()
{
StreamingMsgPackEncoder<T> encoder(nullptr);
encoder.setSizeCountMode(true);
sendHeader(encoder, 0, 0);
encoder.template sendArrayHeader<Measurement_T>(0);
return encoder.getContentLength();
}
Measurement_T * getDataPointer() {
return values;
}
const Measurement_T * getDataPointer() const {
return values;
}
template<typename T>
static void sendHeader(StreamingMsgPackEncoder<T> & encoder, uint32_t sessionStartTime, uint32_t startIndex)
{
encoder.sendMap16(3);
encoder.sendString255("sessionStartTime");
encoder.sendInt(sessionStartTime);
encoder.sendString255("startIndex");
encoder.sendInt(startIndex);
encoder.sendString255("values");
}
private:
uint32_t nextFree = 0;
uint32_t sessionStartTime;
uint32_t startIndex;
Measurement_T values[SIZE];
};

View File

@@ -0,0 +1,36 @@
#pragma once
#include "StreamingMsgPackEncoder.h"
#include <FS.h>
class SpiffsStorageWriter {
public:
SpiffsStorageWriter(const String &fileName) :
f_(SPIFFS.open(fileName, "w")),
encoder_(&f_) {}
StreamingMsgPackEncoder<File> &encoder() { return encoder_; }
private:
File f_;
StreamingMsgPackEncoder<File> encoder_;
};
class SpiffsStorageReader
{
public:
SpiffsStorageReader(const String &fileName) :
f_(SPIFFS.open(fileName, "w"))
{}
uint32_t readBytes(char *buffer, size_t length) {
return f_.readBytes(buffer, length);
}
bool seek(uint32_t pos) {
return f_.seek(pos);
}
private:
File f_;
};

View File

@@ -0,0 +1,127 @@
#pragma once
template<typename T> struct TypeToMsgPackCode{};
template<> struct TypeToMsgPackCode<uint8_t> { static const char CODE; };
template<> struct TypeToMsgPackCode<uint16_t>{ static const char CODE; };
template<> struct TypeToMsgPackCode<uint32_t>{ static const char CODE; };
template<> struct TypeToMsgPackCode<int8_t> { static const char CODE; };
template<> struct TypeToMsgPackCode<int16_t> { static const char CODE; };
template<> struct TypeToMsgPackCode<int32_t> { static const char CODE; };
const char TypeToMsgPackCode<uint8_t>::CODE = '\xcc';
const char TypeToMsgPackCode<uint16_t>::CODE = '\xcd';
const char TypeToMsgPackCode<uint32_t>::CODE = '\xce';
const char TypeToMsgPackCode<int8_t>::CODE = '\xd0';
const char TypeToMsgPackCode<int16_t>::CODE = '\xd1';
const char TypeToMsgPackCode<int32_t>::CODE = '\xd2';
template<typename Writer>
class StreamingMsgPackEncoder
{
public:
StreamingMsgPackEncoder(Writer * writer_)
: writer(writer_), contentLength(0), sizeCountMode(false)
{}
void sendMap16(byte size)
{
if( sizeCountMode )
{
contentLength += 1;
}
else
{
size |= 0b10000000;
writer->write((const char*)(&size), 1);
}
}
void sendString255(PGM_P s)
{
auto len = strlen_P(s);
if( len >= 255 ) {
Serial.println(F("ERROR: StreamingMsgPackEncoder::string255 - string too long"));
return;
}
byte castedLen = (byte)(len);
if( sizeCountMode )
{
contentLength += 2 + castedLen;
}
else
{
writer->write("\xd9", 1);
writer->write((const char*)&castedLen, 1);
writer->write(s, len);
}
}
template<typename T>
void sendInt(T value)
{
if( sizeCountMode )
{
contentLength += 1 + sizeof(T);
}
else
{
if( sizeof(T) == 4 )
value = htonl(value);
else if( sizeof(T) == 2)
value = htons(value);
writer->write(&TypeToMsgPackCode<T>::CODE, 1);
writer->write((const char*)&value, sizeof(T));
}
}
template<typename T>
void sendArray(const T * data, uint32_t length)
{
sendArrayHeader<T>(length);
sendArrayPartialContents(data, length);
}
template<typename T>
void sendArrayHeader(uint32_t length)
{
if( sizeCountMode )
{
contentLength += 1 + sizeof(uint32_t) + 1 + length * sizeof(T);
}
else
{
uint32_t nlength = htonl(length * sizeof(T));
writer->write("\xc9", 1); // ext dtype since typed arrays are not supported by msgpack
writer->write((char*)(&nlength), sizeof(uint32_t) );
writer->write(&TypeToMsgPackCode<T>::CODE, 1); // put code for type here, this is not part of msgpack but custom
}
}
template<typename T>
void sendArrayPartialContents(T * data, uint32_t length)
{
if( !sizeCountMode ) {
writer->write((char*)(data), length * sizeof(T));
}
}
void setSizeCountMode(bool sizeCountMode_=true)
{
sizeCountMode = sizeCountMode_;
}
bool getSizeCountMode() const {
return sizeCountMode;
}
uint32_t getContentLength() const {
return contentLength;
}
private:
Writer * writer;
uint32_t contentLength;
bool sizeCountMode;
};