8 : timestampInterval_ms(timestampInterval_ms),
13 launchTimestamp_ms(0),
14 postLaunchMode(false),
15 launchWriteAddress(0),
16 isChipFullDueToPostLaunchProtection(false) {
21 if (rebootedInPostLaunchMode || isChipFullDueToPostLaunchProtection) {
27 if (timestamp - lastTimestamp_ms > timestampInterval_ms) {
34 if (addRecordToBuffer(&record) < 0) {
38 lastDataPoint = dataPoint;
43 if (rebootedInPostLaunchMode || isChipFullDueToPostLaunchProtection) {
48 if (addRecordToBuffer(&timeStampRecord) != 0) {
52 lastTimestamp_ms = timestamp_ms;
56int DataSaverSPI::addDataToBuffer(
const uint8_t* data,
size_t length) {
59 if (flushBuffer() < 0) {
65 memcpy(buffer + bufferIndex, data, length);
66 bufferIndex += length;
71int DataSaverSPI::flushBuffer() {
72 if (bufferIndex == 0) {
77 if (nextWriteAddress + bufferIndex > flash->size()) {
83 if (postLaunchMode && nextWriteAddress <= launchWriteAddress && nextWriteAddress + BUFFER_SIZE * 2 > launchWriteAddress) {
84 isChipFullDueToPostLaunchProtection =
true;
95 if (!flash->writeBuffer(nextWriteAddress, buffer,
BUFFER_SIZE)) {
107 if (flash ==
nullptr) {
110 if (!flash->begin()) {
115 if (postLaunchMode) {
117 rebootedInPostLaunchMode =
true;
128 return this->postLaunchMode;
142 std::array<uint8_t, SFLASH_PAGE_SIZE> buffer;
143 size_t recordSize =
sizeof(
Record_t);
149 if (!postLaunchMode && !ignoreEmptyPages) {
162 bool timedOut =
false;
163 bool stoppedFromEmptyPage =
false;
164 bool badRead =
false;
166 while (readAddress < flash->size()) {
174 if (ignoreEmptyPages) {
178 stoppedFromEmptyPage =
true;
183 std::array<uint8_t, 3> startLine = {
'l',
's',
'h'};
184 serial.
write(startLine.data(), 3);
188 for (
size_t i = 0; i < numRecordsPerPage; i++) {
190 serial.
write(buffer.data() + i * recordSize, recordSize);
195 uint32_t
const timeout = millis() + 10000;
196 while (serial.
read() !=
'n') {
197 if (millis() > timeout) {
207 std::array<uint8_t, 3> doneLine = {
'E',
'O',
'F'};
208 serial.
write(doneLine.data(), doneLine.size());
215 if (stoppedFromEmptyPage){
221 if (readAddress >= flash->size()){
230 lastDataPoint = {0, 0};
232 lastTimestamp_ms = 0;
233 postLaunchMode =
false;
234 launchWriteAddress = 0;
236 isChipFullDueToPostLaunchProtection =
false;
246 launchWriteAddress = 0;
251 this->launchTimestamp_ms = launchTimestamp_ms;
254 if (postLaunchMode) {
264 postLaunchMode =
true;
282 size_t recordSize =
sizeof(uint32_t) +
sizeof(uint8_t) +
sizeof(
DataPoint);
283 uint32_t
const oneMinuteInMs = 60000;
284 uint32_t
const dataPointsPerMinute = oneMinuteInMs / timestampInterval_ms;
285 uint32_t rollbackBytes = dataPointsPerMinute * recordSize;
290 if (rollbackBytes > maxUsable) {
292 rollbackBytes = maxUsable;
306 uint32_t
const sizeOfFlash = flash->size();
314 int64_t potentialAddr =
static_cast<int64_t
>(nextWriteAddress)
315 +
static_cast<int64_t
>(sizeOfFlash)
316 -
static_cast<int64_t
>(rollbackBytes);
319 potentialAddr = potentialAddr % sizeOfFlash;
323 potentialAddr += sizeOfFlash;
327 launchWriteAddress =
static_cast<uint32_t
>(potentialAddr);
329 std::array<uint8_t,
sizeof(launchWriteAddress)> bytes;
330 std::memcpy(bytes.data(), &launchWriteAddress,
sizeof(launchWriteAddress));
335bool DataSaverSPI::writeToFlash(
const uint8_t* data,
size_t length) {
336 if (!flash->
writeBuffer(nextWriteAddress, data, length)) {
339 nextWriteAddress += length;
343bool DataSaverSPI::readFromFlash(uint32_t& readAddress, uint8_t* buffer,
size_t length) {
344 if (!flash->readBuffer(readAddress, buffer, length)) {
347 readAddress += length;
#define SFLASH_SECTOR_SIZE
#define POST_LAUNCH_FLAG_TRUE
#define DATA_START_ADDRESS
constexpr uint8_t EMPTY_PAGE
#define LAUNCH_START_ADDRESS_ADDRESS
#define POST_LAUNCH_FLAG_FALSE
#define METADATA_START_ADDRESS
#define POST_LAUNCH_FLAG_ADDRESS
bool writeBuffer(uint32_t address, const uint8_t *buffer, size_t length)
Timestamped float measurement container.
DataSaverSPI(uint16_t timestampInterval_ms, Adafruit_SPIFlash *flash)
Construct a new DataSaverSPI object.
static constexpr size_t BUFFER_SIZE
int saveDataPoint(const DataPoint &dataPoint, uint8_t name) override
Saves a DataPoint to SPI flash.
void clearInternalState()
Resets all internal state values (buffer, lastDataPoint, nextWriteAddress, lastTimestamp_ms) Does not...
bool isPostLaunchMode()
Returns whether the metadata from the flash chip indicates that it contains post-launch data that has...
void clearPostLaunchMode()
Clears the post-launch mode flag on the flash chip **WARNING: This will allow the data to be overwrit...
void eraseAllData()
Clears/erases the entire flash chip to start fresh.
int saveTimestamp(uint32_t timestamp_ms)
Persist a bare timestamp entry to flash.
void dumpData(Stream &serial, bool ignoreEmptyPages)
Dumps all data from flash to Serial.
virtual bool begin() override
Initialize the flash chip and metadata.
void launchDetected(uint32_t launchTimestamp_ms)
Call this when launch is detected to set the post-launch flag and prevent any data from being overwri...
virtual size_t write(uint8_t byte)