9 : timestampInterval_ms_(timestampInterval_ms),
11 launchTimestamp_ms_(0),
14 launchWriteAddress_(0),
15 postLaunchMode_(false),
17 isChipFullDueToPostLaunchProtection_(false) {
22 if (rebootedInPostLaunchMode_ || isChipFullDueToPostLaunchProtection_) {
28 if (timestamp - lastTimestamp_ms_ > timestampInterval_ms_) {
30 if (timestampResult != 0) {
31 return timestampResult;
36 if (addRecordToBuffer(&record) != 0) {
37 if (isChipFullDueToPostLaunchProtection_) {
43 lastDataPoint_ = dataPoint;
48 if (rebootedInPostLaunchMode_ || isChipFullDueToPostLaunchProtection_) {
53 if (addRecordToBuffer(&timeStampRecord) != 0) {
54 if (isChipFullDueToPostLaunchProtection_) {
60 lastTimestamp_ms_ = timestamp_ms;
64int DataSaverSPI::addDataToBuffer(
const uint8_t* data,
size_t length) {
67 if (flushBuffer() < 0) {
73 memcpy(buffer_ + bufferIndex_, data, length);
74 bufferIndex_ += length;
78uint32_t DataSaverSPI::normalizeDataAddress(uint32_t address)
const {
79 if (address >= flash_->size()) {
85bool DataSaverSPI::isProtectedLaunchSector(uint32_t sectorNumber)
const {
86 if (!postLaunchMode_) {
92DataSaverSPI::SectorEraseResult DataSaverSPI::eraseSectorForWriteAndLatchOnProtection(
93 uint32_t sectorNumber) {
94 if (isProtectedLaunchSector(sectorNumber)) {
95 isChipFullDueToPostLaunchProtection_ =
true;
96 return SectorEraseResult::kProtectedSectorLatched;
98 if (!flash_->eraseSector(sectorNumber)) {
99 return SectorEraseResult::kFlashEraseFailed;
101 preparedSectorNumber_ = sectorNumber;
102 return SectorEraseResult::kErased;
105bool DataSaverSPI::shouldStopForPostLaunchWindow() {
106 if (!postLaunchMode_) {
109 if (nextWriteAddress_ > launchWriteAddress_) {
115 isChipFullDueToPostLaunchProtection_ =
true;
120int DataSaverSPI::flushBuffer() {
121 if (bufferIndex_ == 0) {
130 if (shouldStopForPostLaunchWindow()) {
138 if (preparedSectorNumber_ != currentSectorNumber) {
139 SectorEraseResult
const eraseResult =
140 eraseSectorForWriteAndLatchOnProtection(currentSectorNumber);
141 if (eraseResult != SectorEraseResult::kErased) {
159 SectorEraseResult
const preEraseResult =
160 eraseSectorForWriteAndLatchOnProtection(nextSectorNumber);
161 if (preEraseResult == SectorEraseResult::kFlashEraseFailed) {
173 if (flash_ ==
nullptr) {
176 if (!flash_->begin()) {
181 if (postLaunchMode_) {
183 rebootedInPostLaunchMode_ =
true;
194 return this->postLaunchMode_;
203 postLaunchMode_ =
false;
209 std::array<uint8_t, SFLASH_PAGE_SIZE> buffer;
210 size_t recordSize =
sizeof(
Record_t);
216 if (!postLaunchMode_ && !ignoreEmptyPages) {
229 bool timedOut =
false;
230 bool stoppedFromEmptyPage =
false;
231 bool badRead =
false;
233 while (readAddress < flash_->size()) {
241 if (ignoreEmptyPages) {
245 stoppedFromEmptyPage =
true;
250 std::array<uint8_t, 3> startLine = {
'l',
's',
'h'};
251 serial.
write(startLine.data(), 3);
255 for (
size_t i = 0; i < numRecordsPerPage; i++) {
257 serial.
write(buffer.data() + i * recordSize, recordSize);
262 const uint32_t timeout =
static_cast<uint32_t
>(millis()) + 10000U;
263 while (serial.
read() !=
'n') {
264 if (
static_cast<uint32_t
>(millis()) > timeout) {
273 std::array<uint8_t, 3> doneLine = {
'E',
'O',
'F'};
274 serial.
write(doneLine.data(), doneLine.size());
281 if (stoppedFromEmptyPage){
287 if (readAddress >= flash_->size()){
296 lastDataPoint_ = {0, 0};
298 lastTimestamp_ms_ = 0;
299 postLaunchMode_ =
false;
300 launchWriteAddress_ = 0;
302 isChipFullDueToPostLaunchProtection_ =
false;
303 preparedSectorNumber_ = std::numeric_limits<uint32_t>::max();
313 launchWriteAddress_ = 0;
318 this->launchTimestamp_ms_ = launchTimestamp_ms;
321 if (postLaunchMode_) {
331 postLaunchMode_ =
true;
349 const size_t recordSize_bytes =
sizeof(uint32_t) +
sizeof(uint8_t) +
sizeof(
DataPoint);
350 uint32_t
const oneMinuteInMs = 60000;
351 uint32_t
const dataPointsPerMinute = oneMinuteInMs / timestampInterval_ms_;
352 uint64_t rollbackSize_bytes =
static_cast<uint64_t
>(dataPointsPerMinute) *
static_cast<uint64_t
>(recordSize_bytes);
356 const size_t flashSize_bytes = flash_->size();
357 const size_t maxUsable_bytes = (flashSize_bytes >
static_cast<size_t>(
kDataStartAddress))
360 const auto maxUsable =
static_cast<uint64_t
>(maxUsable_bytes);
361 if (rollbackSize_bytes > maxUsable) {
363 rollbackSize_bytes = maxUsable;
377 const auto sizeOfFlash =
static_cast<uint32_t
>(flashSize_bytes);
385 int64_t potentialAddr =
static_cast<int64_t
>(nextWriteAddress_)
386 +
static_cast<int64_t
>(sizeOfFlash)
387 -
static_cast<int64_t
>(rollbackSize_bytes);
390 potentialAddr = potentialAddr % sizeOfFlash;
394 potentialAddr += sizeOfFlash;
398 launchWriteAddress_ =
static_cast<uint32_t
>(potentialAddr);
400 std::array<uint8_t,
sizeof(launchWriteAddress_)> bytes;
401 std::memcpy(bytes.data(), &launchWriteAddress_,
sizeof(launchWriteAddress_));
406bool DataSaverSPI::writeToFlash(
const uint8_t* data,
size_t length) {
407 if (!flash_->
writeBuffer(nextWriteAddress_, data, length)) {
410 nextWriteAddress_ =
static_cast<uint32_t
>(nextWriteAddress_ +
static_cast<uint32_t
>(length));
414bool DataSaverSPI::readFromFlash(uint32_t& readAddress, uint8_t* buffer,
size_t length) {
415 if (!flash_->readBuffer(readAddress, buffer, length)) {
418 readAddress =
static_cast<uint32_t
>(readAddress +
static_cast<uint32_t
>(length));
#define SFLASH_SECTOR_SIZE
constexpr uint32_t kDataStartAddress
constexpr uint32_t kMetadataStartAddress
constexpr uint8_t kPostLaunchFlagTrue
constexpr uint32_t kPostLaunchFlagAddress
constexpr uint8_t kPostLaunchFlagFalse
constexpr uint32_t kLaunchStartAddressAddress
constexpr uint8_t kEmptyPageValue
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 kBufferSize_bytes
int saveDataPoint(const DataPoint &dataPoint, uint8_t name) override
Saves a DataPoint to SPI flash.
void clearInternalState()
Reset in-memory pointers without erasing flash contents.
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()
Erase 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)
Stream all recorded data to a serial connection.
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)