FRAM memory size

Post your code and we will see

Thanks, I think that I have figured it out. I am experimenting with a network of devices sending data to one central device as a struct variable which saves the data to Fram.

The problem i was having was that i was updating the address location with each element of the struct variable. When another lot of data was recieved by esp now, it would interupt writing the data and the address location was out of alignment.

The next time I read the data, it came out with nonsense.

I fixed it by only updating the address location at the end of storing the full struct data rather than doing it sequentially.

It appears to be working fine now.

Good luck with your project

I was wrong I've not fixed it. I've included the code below with a lot of extraneous stuff commented out.

The basic idea is that it receives a struct variable from ESP NOW from 1 of 2 devices, each one sends data every 5 minutes. It then goes to dataToFram() to write the data to fram.

It then goes to readFram() and reads the data back and Serial.prints it as a list.

Everything appears to work fine. When I come back hours later, I find that the readFram() is printing lines for ever which are corrupted.

I assumed that an interrupt was interrupting the writing of data and messing up addrLoc (a counter of where the write address is up to), and so I made sure that the addrLoc wasn't updated until all information had been written.

I tried using noInterupts() but this just causes a crash.

If you can see any obvious problems I would be grateful for your advice.

#include <esp_now.h>
#include <WiFi.h>
#include <Wire.h>
//#include <LiquidCrystal_I2C.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "FRAM.h"

FRAM fram;

#define I2C_SDA 33
#define I2C_SCL 32
//LiquidCrystal_I2C lcd(0x27, 20, 4); 
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);


// Structure example to receive data
// Must match the sender structure
typedef struct struct_Data {
  char unit[8];
  char timeStamp[32];
  float humid;
  float temperature;
  float pressure;
  float battVolt;
} struct_Data;

// Create a struct_message called myData
struct_Data myData;

int counter=0;
int counter2;
float pressure_hold[16];
bool press=0;
const int inBytes = 56;
const unsigned long startAddr=1000;
unsigned long addrLoc=startAddr;
unsigned long readLoc;
const uint8_t IndexLoc = 100;
int Av=15;
bool output = 0;

///////////////////////////////////////////////////////////////////////////////////////////////
// callback function that will be executed when data is received
void OnDataRecv(const esp_now_recv_info_t * mac, const uint8_t *incomingData, int len) 
{
  memcpy(&myData, incomingData, sizeof(myData));
  
  if(sizeof(myData)==inBytes)
  {
    dataToFram();
    output = 1;
    //counter++;
    if(counter>=Av)
    {
      press=1;
      counter=0;
    }
    readFram();
    //Serial.print("counter = ");Serial.println(counter);
  }


}
 ////////////////////////////////////////////////////////////////////////////////////////////
void setup() {
  // Initialize Serial Monitor
  Serial.begin(115200);
  Wire.begin(I2C_SDA, I2C_SCL);
  fram.begin(0x50);
  //fram.begin(0x7C);
  /*if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }*/
  int resetFile = 0;
  bool reset=0;
  if (reset){
    fram.write32(IndexLoc, startAddr+resetFile*inBytes);///set initial index point
    readFram();
    while(1){}
  }
  addrLoc=fram.read32(IndexLoc);
  Serial.print("addrLoc =");
  Serial.println(addrLoc);

  Serial.print( "Number of records = ");
  int x=(addrLoc-startAddr)/inBytes;
  Serial.println( x);
  

  display.clearDisplay();

  WiFi.mode(WIFI_STA);

  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  

  esp_now_register_recv_cb(OnDataRecv);
  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.clearDisplay();
  display.display();
}
 
void loop() {
    
    if(output){
      
      
      pressure_hold[counter]=myData.pressure;
      
      counter++;
      memoryValidate();
      
      output=0;
    }
    /*allOutput();
    delay(5000);

    display.setTextSize(2); // Draw 2X-scale text    
    display.clearDisplay();
    display.setCursor(0, 0);
    display.println(F(" H "));
    display.setTextSize(4);
    display.println(myData.humid,1);
    display.setTextSize(2);
    display.print("% ");
    display.println(myData.unit);

    display.display();
    delay(5000);

    display.clearDisplay();
    display.setCursor(0, 0);
    display.println(F("  T"));
    display.setTextSize(4);
    display.println(myData.temperature,1);
    display.setTextSize(2);
    display.print("C ");
    display.println(myData.unit);

    display.display();
    delay(5000);

    display.clearDisplay();
    display.setCursor(0, 0);
    display.println("   V");
    display.setTextSize(4);
    display.println(myData.battVolt,2);
    display.setTextSize(2);
    display.println(myData.unit);

    display.display();
    delay(5000);*/

}
//////////////////////////////////////////////////////////////
void allOutput(){
    display.setTextSize(2); // Draw 2X-scale text
    display.setTextColor(SSD1306_WHITE);
    display.clearDisplay();
    display.setCursor(0, 0);
    display.print(F("H "));
    display.print(myData.humid,1);
    display.println("%");
    display.print(F("T "));
    display.print(myData.temperature,1);
    display.println(" C"); 
    display.print("P ");
    display.print(myData.pressure,0);

    

    if(!press){display.println();}

    else
    {
      
      float meanPressure = arrayAverage(pressure_hold, Av);

      //Serial.println(meanPressure);
      if(myData.pressure>meanPressure)
      {
        display.println("+");
      }
      else{
        display.println("+");
      }
    }
    
    display.print("V ");
    display.print(myData.battVolt,2);
    display.display();
}
///////////////////////////////////////////////////////////
float arrayAverage(float variable[], int count) 
{
  float num = 0;
  for (int i = 0; i <= count - 1; i++) 
  {
    num = num + variable[i];  // Calculate sum of all array values
  }
  float average = float(num / count);
  return average;
}

///////////////////////////////////////////////////////
void readFram()
{
  //no//interrupts();
  readLoc=startAddr;
  char str[8];
  char str2[32];
  float humid;
  float temperature;
  float pressure;
  float battVolt;

  int fileCount=0;
  
  Serial.println(" ");
  Serial.print(" ");
  int x =(addrLoc-inBytes);
  while(readLoc<=x){
  fileCount++;
  Serial.print(fileCount);Serial.print("\t");
    
    fram.read(readLoc, (uint8_t *)str, 8);
    readLoc+=8;
    
    fram.read(readLoc, (uint8_t *)str2, 32);
    readLoc+=32;

    humid=fram.readFloat(readLoc);
    readLoc+=4;

    temperature=fram.readFloat(readLoc);
    readLoc+=4;

    pressure=fram.readFloat(readLoc);
    readLoc+=4;

    battVolt=fram.readFloat(readLoc);
    readLoc+=4;

    Serial.print(str);Serial.print("\t");
    Serial.print(str2);Serial.print(" ");
    Serial.print(humid);Serial.print(" ");
    Serial.print(temperature);Serial.print(" ");
    Serial.print(pressure);Serial.print(" ");
    Serial.println(battVolt);Serial.print(" ");
  }

  Serial.print( "Number of records = ");
  x=(readLoc-startAddr)/inBytes;
  Serial.println( x);
  //interrupts();  
}

void memoryValidate(){
    addrLoc=fram.read32(IndexLoc);
    float memoryCheck=((addrLoc-startAddr)/inBytes);
     Serial.print("memory check (should be whole number) =");
    Serial.println(memoryCheck,8);
    Serial.print(" addrLoc =");
    Serial.println(addrLoc);
    Serial.print("memoryCheck * inBytes = ");
    long x=memoryCheck * inBytes+startAddr;

    addrLoc=(int (memoryCheck)*inBytes+startAddr);//get rid of partial entries due to interupt
    fram.write32(IndexLoc, addrLoc);
}
//////////////////////////////////////////////////////////////
void dataToFram(){
  //noInterrupts();
  unsigned long addrTemp=fram.read32(IndexLoc);
  char str[8];
  strcpy(str, myData.unit);
  fram.write(addrTemp, (uint8_t *)str, 8);
  addrTemp+=8;
  char str2[32];
  strcpy(str2, myData.timeStamp);
  fram.write(addrTemp, (uint8_t *)str2, 32);
  addrTemp+=32;
  fram.writeFloat(addrTemp, myData.humid);
  addrTemp+=4;
  fram.writeFloat(addrTemp, myData.temperature);
  addrTemp+=4;
  Serial.println(myData.pressure);
  fram.writeFloat(addrTemp, myData.pressure);
  addrTemp+=4;
  fram.writeFloat(addrTemp, myData.battVolt);
  addrTemp+=4;
  addrLoc=(fram.read32(IndexLoc)+inBytes);
  fram.write32(IndexLoc, addrLoc);
  for(long i=addrLoc;i<= addrLoc+inBytes;i++){fram.write8(i, 0);}/// clear following block

  //interrupts();

  Serial.println();
  Serial.println(myData.unit);
  Serial.println(myData.timeStamp);
  Serial.println(myData.humid);
  Serial.println(myData.temperature);
  Serial.println(myData.battVolt);
  Serial.print("addrLoc = ");
  Serial.println(addrLoc);
  Serial.print( "Number of records = ");
  int x=(addrLoc-startAddr)/inBytes;
  Serial.println( x);
}

I've just realised that because I have comented out the display.begin, display. commands cause it to crash. All the display. commands also need commenting out.

I think that I may have figured out my mistake. I basically filled the memory but the loop keeps trying to write more data.

It was my confusion about memory size. 32kB really isn't a lot for data storage.

At 56 bytes from 2 devices every 5 minutes, only gives about 12 hours recording.

In comparison, one of the two devices has been recording to SPIFFS for 24 days and only used 30% of SPIFFS memory.

So why not an SD card?

I was trying this because of earlier problems that I had with sd cards. I have since found that when it was soldered, rather than solderless, the sd card works a treat.

What is Fram generally used for? For non volatile memery, during sleep mode for example, I've been using RTC_DATA_ATTR. For longer term memory, there is spiffs and sd. What niche does Fram fill?

I don't know if it ever did fill anything.
It has the writing speed of SRAM but is nonvolatile like flash.

You can connect 8 of these to the I2C bus using address 0x50. That are what the A0-A3 pins on the FRAM memory board are.

You can also use different chips such as the MB85RC1Mt: https://www.fujitsu.com/jp/group/fsl/en/imagesgig5/MB85RC1MT-DS501-00027-4v0-E.pdf I believe the pinout is the same but you will have to go to 32 bit addressing.

So is it then treated as one block of memory?

What do you mean by "block"

Such elementary calculations are the very first thing to do when deciding on data storage methods and hardware.

1 Like

That depends on the library you use. A disk is broken into blocks. The driver I use I specify the address and the data to be written to that location. The library has code allowing up to maybe 4K in one write operation. You can write your data every 56 bytes and use all of the memory.

I'm just experimenting at the moment and seeing how things work. I only started with Arduinos at the new year and I am going through a process of finding out what I can do with them.

I have to admit, rather embarrassingly, i misread, or missunderstood the capacity of the fram. Got my MB and kB mixed up.

That's fine. There is a lot to learn in this very technical hobby.

From what Jim has said earlier, it seams that the main advantage of fram is access speed. I think that I may stick to SDs for now unless i find a specific need for the fast access.

As I tell my kids "I'm a geek and proud of it"

FRAM has better write endurance than most non-volatile memories.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.