Storing sensor data to Winbond chip

I am using a teensy 4.0 and a Winbond W25Q64FV chip and i'm logging to store sensor data values to the chip, I've tried using SerialFlash and SPIFlash and SPIMemory libraries but none seem to work/show me how to directly store data straight to the chip simply, as simple as the SD library.
Also, id then like to transfer this data to an SD card, the SD card with be subject to vibrations as to why i'm saving the data to a chip first.
Any help or guidance would be appreciated

Thank you :slight_smile:

A quick look on github at the SPIMemory library and it seems pretty straightforward. Did you look at the readWriteString example?

Perhaps the readByteArray() and writeByteArray() functions will be what you are looking for?

Start at address 0 and increase the address by the size of the byte array each time.

Ive tried all of the examples for the latest version, even tried downloading older versions of the library and I have used this library before and its worked for this chip (In terms of reading it I actually never attempted to write sensor data to it) and it keeps either failing, showing no outputs to the serial monitor, getting stuck on initialising or saying there's an error with my chip, which I know is not right as the library has previous been able to communicate with my chip.

Sorry for the long reply, this issue is just very frustrating

Thank you for your helpful comments :slight_smile:

As a bit of a sanity check, are you able to read any form of manufacturers ID from the chip? That should at least confirm that basic low level comms are working.

No, not using the SPIMemory or older Flash library, but I can with the Paul Stoffregen SerialFlash library, so the chip does work, it an read it, however that library isn't exactly straight forward.

@markd833
So I've been trying to get the chip working, again no progress with the SPI Memory library, I've managed to copy files form an SD card to the chip through the Paul Stoffregen library, but the SPI Memory library can't even read the chip at all, which it previously has been able to

I had a quick look at the library Paul Stoffregen provides. It seems pretty easy to use. What aspect are you having trouble with?

I just need to simply store and log sensor data, none of the examples contain the code required to (At least in a straight forward way) and the GitHub page shows the commands but once put together it just doesn't seem to work.

Im not sure how to create a file, name it what I want, and add data to that file on the chip

Sorry, this issues just been annoying me for so long

again thank you for your help :slight_smile:

Do you need to have a file system on it if its not going to be read by anything else other than the teensy? My point is, you can write raw data into it. All you need to do is keep track of where (flash memory location) you are writing the data

Yes, as I need to transfer this data to an SD card, i'm building a model rocket flight computer so I need to use the chip as I sort of black box, and then transfer the data to an SD for ease once landed, which will be done in the software. :slight_smile:

Have to agree with @hzrnbgy here. If you packaged up your sensor data values in a struct along with an incrementing record counter, then you can just write the struct to the chip using low level flash routines, starting at address 0 and incrementing the address as you go.

That will avoid any overhead of a filesystem and creating files etc.

You would then know the format of the data on the flash chip and you can then read it back into the same struct using low level flash routines. You can then post process the data on the fly whilst writing it to an SD card. Maybe you can simply generate a CSV file on the SD card with the previously recorded parameters in it.

I did a bit of digging around for you and came up with these 3 hits:

This one is a data logger that logs wind speed to a flash chip.

And this one is a data logger for a 3-axis accelerometer.

Finally, this one is his instructions on how to retrieve the logged data.

Oh, and a slight caveat - i've not tried any of this ....

@markd833 Thank you for the links, they were really good, however I think a lot of the source code went straight over my head. I'm still relatively new to Arduino and a lot of the programming to do with flash chips is a bit too complicated.
Ive tried to make a sample code to go off for my other projects but it keeps having issues and doesn't compile, i'm just a bit stuck as I don't have the knowledge to work this out myself and its just frustrating that nothing seems to work for me, the only thing is the SerialFlash library and I can't even seem to even get the right.
Thank you again for helping and i'm sorry nothing has worked but i'm not giving up just yet.

:slight_smile:

Sometimes figuring out somebody else's completed project can be overwhelming when getting started. I would suggest you break down your project into simple chunks so you learn about each part before combining them into the complete solution.

Start by working out how to read each of your sensors and print their values out on the serial port.

For the flash storage, maybe this may help you out. It's a very low level flash read/write library.

@markd833 wow thank you that library is so simple and useful, I've managed to break down the example code and am starting to create my own

I am able to use the sensors and everything, I've logged them all to an SD card and I understand them, they're a lot simpler, however it was more of the low-level constructs that are a bit daunting.

This is a part of the code which writes to the chip
<
Serial.println("Writing test HEX values to first 1024 bytes");
uint8_t myVal[4] = {0xDE, 0xAD, 0xBE, 0xEF};
for (int x = 0 ; x < 0x0400 ; x += 4)
{
myFlash.writeBlock(x, myVal, 4); //Address, pointer, size
}

I can see that it writes values from 1 to 1024 in hex to the chip, but does that mean that the values I need to write to the chip have to be in hex and how to save decimal point numbers such as 22.546?

I'm not too sure about the myVal part and how I would have to change that to suit me.

Also, I need to store about 3 different values on the chip, do I go about that storing them in three different blocks, but also I need to work out the size of each block as i'd need to store a lot of data values.

Thank you again you've been really helpful and I can't thank you enough :slight_smile:

myFlash.writeBlock(x, myVal, 4);

More specifically which part of this write writes the value stored, x, myVal ? and so where would I write the señor value, stored as variable "temp" or is the function not writeBlock?

Not quite ...

Looking at that bit of code:

uint8_t myVal[4] = {0xDE, 0xAD, 0xBE, 0xEF};

This defines an array of 4 bytes (uint8_t => unsigned 8 bit integer type) and puts the value 0xDE into myVal[0], 0xAD into myVal[1] etc.

for (int x = 0 ; x < 0x0400 ; x += 4)
{
myFlash.writeBlock(x, myVal, 4); //Address, pointer, size
}

This loop causes the integer x to increment from 0 in steps of 4 (x +=4 is the same as x=x+4) for as long as x is less than 0x400 (i.e. 1024). So it's actually going to go round the loop 256 times. Each time round the loop, it calls the myFlash.writeBlock() function to write 4 bytes from the array myVal to the flash chip at address x. So the address x increments 0,4,8,12,16,20 etc.

What you could do to avoid exposing yourself to a lot of the inner workings of how numbers are stored in memory, is to define a structure to hold 1 set of your values.

Now I've written this off the top of my head so hopefully it will work and I've not mucked it up and wasted you time - but if I have mucked it up, then maybe someone else will jump in and correct me!

#include <SparkFun_SPI_SerialFlash.h>

SFE_SPI_FLASH myFlash;

// edit this structure to hold one set of parameters you want to log
struct oneRecordType {
  int recordNumber;
  float temperature;
  float pressure;
} oneRecord;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);

  oneRecord.temperature = 17.0;   // 17 degC or 17 degF - you decide
  oneRecord.pressure = 1012.25;   // millibars - or in Hg if you prefer
  oneRecord.recordNumber = 0;
  
  Serial.println("Writing test values to first 1024 bytes");
  for (int x = 0 ; x < 0x0400 ; x += sizeof(oneRecord) )
  {
    myFlash.writeBlock(x, (uint8_t *)&oneRecord, sizeof(oneRecord) ); //Address, pointer, size
    oneRecord.recordNumber++;
  }
}

void loop() {
  // put your main code here, to run repeatedly:
}

@markd833 I managed to upload your code, I did just have to add myflash.begin(CS) (incase anyone plans on using the code) and it has uploaded values to the chip :slight_smile:
only issue is i'm not sure how to then convert those values back to decimal, and it seems to loop and repeat " 00 00 88 41 00 10 7D 44 52 00 00 " I'm not too sure if that's supposed to happen or not, so the code has worked now just need to decode it and also add continuously changing values of temperature

But thank you for taking time to write the code, you've been really helpful thank you again :slight_smile: - Harvey

The values written should be fixed for 2 parameters. The first parameter should be incrementing by 1 each time.

Regarding the decoding, you don't have to worry about it. Once you've logged your flight data to the flash chip, your code that transfers the data to the SD card should do a readBlock back out of the flash chip into the same struct. You can then simply use something like the sprintf() function to generate a human readable text string that you can write to your SD card. If you print each row if recorded parameters separated by commas - i.e. a CSV file, then you should be able to read that file straight into Excel or OpenOffice calc.

@markd833
can I save the data as the hex values for the ascii vales of the numerical values, so 17.0 would be 31 37 2e 30, and knowing that the sensor values will be 5 digits, (3d.p for Temp) I can manually create lines of the data values, but i'm not too sure, the values i've gotten from the test code does that repeat the values 17.0 and 1012.25 continuously, or is there a way to save 17.0 once and 1012.25 once and the rest of the block be empty, as id like to have (In pseudo code)

Temp = Temp reading (E.g 17.0)
write to flash chip
new reading
write to flash chip
and continue until my program is finished recording the temperature, so id have a long line of just the temperature readings like 17.017.017.0 and on the same chip (different block) have a repeat of the pressure values also.

so would I have in the loop function temp = bmp.temperature
unit8_t WriteTemp == Temp
myFlash.writeBlock(x, temp, sizeof(oneRecord) );
and this should write, 4 bits as 1st temp reading, 4 bits 2nd..... being positions 0x000 and then 0x004 and 0x008 etc.... ?

thank you again, sorry that I have so many questions :slight_smile:

You can save the data in whichever format you like. Saving the raw values usually takes up less space and therefore less time to write to the flash chip.

A lot will depend on how fast you want to log the data. Purely from watching youtube videos, most model rocket "flights" seem to happen very quickly so I would assume that you would want to log data at a fairly quick rate - maybe 10 or 20 times per second or maybe even faster....

Here's a bit of code that may help you out. You will need to insert the code that reads from each of your sensors and set the correct pin for your flash CS signal.

#include <SparkFun_SPI_SerialFlash.h>

const byte PIN_FLASH_CS = 8; // Change this to match the Chip Select pin on your board

SFE_SPI_FLASH myFlash;

// edit this structure to hold one set of parameters you want to log
struct oneRecordType {
  int recordNumber;
  uint32_t timeStamp;
  float temperature;
  float pressure;
  float acceleration;
} oneRecord;

// this variable holds the address to write to in the flash chip
uint32_t flashWriteAddress = 0;

void setup() {
  Serial.begin(115200);
  Serial.println(F("Rocket SPI Flash Data Logger."));
  
  myFlash.begin( PIN_FLASH_CS );
  oneRecord.recordNumber = 0;
}

void loop() {
  // read the temperature from whatever device you are using
  oneRecord.temperature = <temperature sensor reading>;

  // read the pressure from whatever device you are using
  oneRecord.pressure = <pressure sensor reading>;

  // read the acceleration from whatever device you are using
  oneRecord.acceleration = <acceleration sensor reading>;

  // add in the time stamp so you know roughly when the reading happened
  oneRecord.timeStamp = millis();
  
  // write the readings to the flash chip
  myFlash.writeBlock(flashWriteAddress, (uint8_t *)&oneRecord, sizeof(oneRecord) );

  // increment the record number - you don't really need this but it will help
  // detecting the end of the data when retrieving the data from the flash chip 
  oneRecord.recordNumber++;

  // update the flash write address
  flashWriteAddress = flashWriteAddress + sizeof(oneRecord);

  // delay for a specific period of time - say 100ms
  delay( 100 );
}

As you know, you need to erase the flash chip before each run.

I also added in a timestamp feature that also saves the value of the internal millis() time. This is the time in milliseconds since the processor was reset. It's reasonably accurate - more so given the short time period over which you would be logging data.