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;
137 postLaunchMode =
false;
143 std::array<uint8_t, SFLASH_PAGE_SIZE> buffer;
144 size_t recordSize =
sizeof(
Record_t);
150 if (!postLaunchMode && !ignoreEmptyPages) {
163 bool timedOut =
false;
164 bool stoppedFromEmptyPage =
false;
165 bool badRead =
false;
167 while (readAddress < flash->size()) {
175 if (ignoreEmptyPages) {
179 stoppedFromEmptyPage =
true;
184 std::array<uint8_t, 3> startLine = {
'l',
's',
'h'};
185 serial.
write(startLine.data(), 3);
189 for (
size_t i = 0; i < numRecordsPerPage; i++) {
191 serial.
write(buffer.data() + i * recordSize, recordSize);
196 uint32_t
const timeout = millis() + 10000;
197 while (serial.
read() !=
'n') {
198 if (millis() > timeout) {
208 std::array<uint8_t, 3> doneLine = {
'E',
'O',
'F'};
209 serial.
write(doneLine.data(), doneLine.size());
216 if (stoppedFromEmptyPage){
222 if (readAddress >= flash->size()){
231 lastDataPoint = {0, 0};
233 lastTimestamp_ms = 0;
234 postLaunchMode =
false;
235 launchWriteAddress = 0;
237 isChipFullDueToPostLaunchProtection =
false;
247 launchWriteAddress = 0;
252 this->launchTimestamp_ms = launchTimestamp_ms;
255 if (postLaunchMode) {
265 postLaunchMode =
true;
283 size_t recordSize =
sizeof(uint32_t) +
sizeof(uint8_t) +
sizeof(
DataPoint);
284 uint32_t
const oneMinuteInMs = 60000;
285 uint32_t
const dataPointsPerMinute = oneMinuteInMs / timestampInterval_ms;
286 uint32_t rollbackBytes = dataPointsPerMinute * recordSize;
291 if (rollbackBytes > maxUsable) {
293 rollbackBytes = maxUsable;
307 uint32_t
const sizeOfFlash = flash->size();
315 int64_t potentialAddr =
static_cast<int64_t
>(nextWriteAddress)
316 +
static_cast<int64_t
>(sizeOfFlash)
317 -
static_cast<int64_t
>(rollbackBytes);
320 potentialAddr = potentialAddr % sizeOfFlash;
324 potentialAddr += sizeOfFlash;
328 launchWriteAddress =
static_cast<uint32_t
>(potentialAddr);
330 std::array<uint8_t,
sizeof(launchWriteAddress)> bytes;
331 std::memcpy(bytes.data(), &launchWriteAddress,
sizeof(launchWriteAddress));
336bool DataSaverSPI::writeToFlash(
const uint8_t* data,
size_t length) {
337 if (!flash->
writeBuffer(nextWriteAddress, data, length)) {
340 nextWriteAddress += length;
344bool DataSaverSPI::readFromFlash(uint32_t& readAddress, uint8_t* buffer,
size_t length) {
345 if (!flash->readBuffer(readAddress, buffer, length)) {
348 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)