External I2C EEPROM cannot write

I’m currently working on a GPS speedometer for my car. I thought everything was working fine when I installed it in my car, but apparently that is not the case :confused:
I’m using an Arduino Uno, a 24LC04B I2C EEPROM, three DS18B20 temperature sensors, and a LCD.
For some reason I cannot figure out, my sketch won’t write to the EEPROM. I can read from it, but not write…

I’m trying to write in case a button is pressed or in case my speed is below 10. I think the solution in the two cases will be the same so I have focused on fixing the case with my speed (line 287-297). I made some massive delays (still in the code) to make sure the Arduino is not busy when I want it to write to EEPROM, I have tried freeing up some SRAM (still in code), and I have tried to read just before I write (no longer in the code), but without any luck :confused:

This is my complete sketch: (Too long to put in post. I will paste it as the first comment)

I made a small sketch only for writing to the EEPROM and it is wokring just fine. Here is the code for the small sketch:

#include <Wire.h> //I2C

#define ADDRESS 0x50 //Address of EEPROM

//EEPROM place
int EEPROMpos = 0;

//Saved in EEPROM
long int kmTank = 0;
long int kmTot = 0;
long int kmLife = 157392000;

void setup(){
  Wire.begin(); //Start I2C
  
  EEPROMpos = writeKm(kmLife, EEPROMpos);
  EEPROMpos = writeKm(kmTot, EEPROMpos);
  EEPROMpos = writeKm(kmTank, EEPROMpos);
      
  //Getting values from EEPROM
  kmLife = getKm(0);
  kmTot = getKm(lengthInt(kmLife) + 1);
  kmTank = getKm(lengthInt(kmLife) + lengthInt(kmTot) + 2);
}


void loop(){

}

void eepromWrite(byte wordAddress, byte data){
  Wire.beginTransmission(ADDRESS);
  Wire.write(wordAddress);
  Wire.write(data);
  Wire.endTransmission();
  
}

byte eepromRead(byte wordAddress){
  Wire.beginTransmission(ADDRESS);
  Wire.write(wordAddress);
  Wire.endTransmission();
  
  Wire.requestFrom(ADDRESS,1); //The address and number of bytes we want
  
  while(!Wire.available()){} //Waiting for request
  
  return Wire.read();
}

byte lengthInt(long int number){
  return (int)floor(log10(number)) + 1; //Get number of digits in number
  
}

int writeKm(long int km, int pos){
  int place = pos;
  long int kmVal = km;
  int lengthKm = lengthInt(kmVal);
  long int z = 10;
  String str = "10";
  
  //pow() does not work. Making str instead.
  for(int y = 1; y < lengthKm; y++){
    str += "0";
  }
  
  //Converting str to long int
  long int lengthKmPow = atol(str.c_str());
  
  eepromWrite(0 + place, lengthKm);
  delay(5);
  
  //separating numbers in km number
  for(int x = 1; x < lengthKm +1; x++){
    int m = kmVal / (lengthKmPow / z);
    kmVal = kmVal - m * (lengthKmPow / z);
    z = z*10;
    eepromWrite(x + place, m);
    delay(5);
  }
 
  //next empty position in EEPROM 
  place += lengthKm +1;
  
  return place;
  
}

long int getKm(int pos){
  
  long int km = 0;
  int length = 0;
  String str = "";
  
  length = eepromRead(pos);
  
  for(int x = pos + 1; x <= length + pos; x++){
    str += eepromRead(x);
  }
  
  km = atol(str.c_str());
  
  return km;
}

Can someone tell me what I’m doing wrong? :slight_smile:

Thank you in advance.

Christoffer

Sketch part 1:

#include <LiquidCrystal.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
#include <math.h>
#include <Wire.h> //I2C

#define ADDRESS 0x50 //Address of EEPROM
#define SERIAL_BUFFER_SIZE 32 //Serial buffer size default 128

//Setup GPS
//Set software serial pins (Tx 3, Rx 2)
SoftwareSerial mySerial(3, 2);

Adafruit_GPS GPS(&mySerial);

//Set to true to write GPS recived data in serial
#define GPSECHO false

// this keeps track of whether we're using the interrupt
// off by default!
boolean usingInterrupt = false;
void useInterrupt(boolean); // Func prototype keeps Arduino 0023 happy

//setup LCD
LiquidCrystal lcd(11,10,9,7,6,5);

//setup DS18B20
OneWire oneWire(12);
DallasTemperature sensors(&oneWire);

//vars for timer
unsigned long currentMillis = 0;
long previousMillis = 0;
long interval = 1000;

//var for changing "page"
int pageChange = 2;

//EEPROM place
int EEPROMpos = 0;

//vars for temp, speed and distance
float inTemp;
float outTemp;
float enTemp;
float hast = 0;
float prevHast = 0;

//Saved in EEPROM
/*long int kmTank = 0;
long int kmTot = 0;
long int kmLife = 156244000;*/

long int kmTank;
long int kmTot;
long int kmLife;

//Vars for position
float latA = 0;
float latB = 0;
float lonA = 0;
float lonB = 0;

//Calc of distance
const float two = 2.0;
const float R = 6372795.477598;
int distMeter = 0;
int distTemp = 0;

//time var
int hour;

void setup(){
  //input for page change
  pinMode(A2,INPUT);
  pinMode(A3,INPUT);
  
  Wire.begin(); //Start I2C
      
  //setup for DS18B20
  sensors.begin();
  
  //Setup LCD and write start msg
  lcd.begin(16,2);
  lcd.setCursor(0,0);
  lcd.print(F("--Saxo 1.4i 8v--"));
  lcd.setCursor(0,1);
  lcd.print(F("---Telemetri----"));
  
  Serial.begin(115200); //To read faster
  Serial.println(F("BEGIN!"));
  
  GPS.begin(9600); //Default baud
  
  //Amount of data parsed (recommended minimum)
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
  
  //Update rate 1Hz (1Hz is Arduino max)
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
  
  // Request updates on antenna status, comment out to keep quiet
  GPS.sendCommand(PGCMD_ANTENNA);
  
  //Timer every 1 millisecond (reads data)
  useInterrupt(true);

  delay(1000);
  // Ask for firmware version
  mySerial.println(PMTK_Q_RELEASE);
    
  //Getting values from EEPROM
  kmLife = getKm(0);
  kmTot = getKm(lengthInt(kmLife) + 1);
  kmTank = getKm(lengthInt(kmLife) + lengthInt(kmTot) + 2);
}


// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
SIGNAL(TIMER0_COMPA_vect) {
  char c = GPS.read();

#ifdef UDR0
  if (GPSECHO)
    if (c) UDR0 = c;  
    // writing direct to UDR0 is much much faster than Serial.print 
    // but only one character can be written at a time. 
#endif
}

void useInterrupt(boolean v) {
  if (v) {
    // Timer0 is already used for millis() - we'll just interrupt somewhere
    // in the middle and call the "Compare A" function above
    OCR0A = 0xAF;
    TIMSK0 |= _BV(OCIE0A);
    usingInterrupt = true;
  } else {
    // do not call the interrupt function COMPA anymore
    TIMSK0 &= ~_BV(OCIE0A);
    usingInterrupt = false;
  }
}

uint32_t timer = millis();

void loop(){
  
  GPSdata();
    
  if(GPS.fix){   
    //Change "page"
    currentMillis = millis();
    if(currentMillis - previousMillis > interval && analogRead(A2) >= 1000){
      previousMillis = currentMillis;
      
      if(pageChange == 2){
        pageChange = -1;
      }
      
      pageChange++;
    }
    
    //Get data from DS18B20
    sensors.requestTemperatures();
    inTemp = sensors.getTempCByIndex(0);
    outTemp = sensors.getTempCByIndex(1);
    enTemp = sensors.getTempCByIndex(2);
    
    //Get speed 
    hast = GPS.speed*1.852;
    
    //calculate distance
    if(hast > 2 && millis() - timer > 2000){
      
      timer = millis();
            
      //Get position
      latA = GPS.latitudeDegrees, 6;
      lonA = GPS.longitudeDegrees, 6;
          
      //Get rad
      latA = (latA*PI)/180;
      lonA = (lonA*PI)/180;   
      
      if(latB != 0 && lonB != 0){
        
        //distTemp = R*acos(sin(latA)*sin(latB)+cos(latA)*cos(latB)*cos(lonA-lonB)); acos is inaccurate
        
        distTemp = R*two*asin(sqrt(square(sin((latA-latB)/two)) + cos(latA)*cos(latB)*square(sin((lonB-lonA)/two))));
                                
        if(distTemp <= 200 && distTemp > 0){
          distMeter = distMeter + distTemp;
        }
        
      }
      
      latB = latA;
      lonB = lonA;
    }
    
    //Save to screen vars
    if(distMeter != 0.0){
      
      kmTank = kmTank + distMeter;
      kmTot = kmTot + distMeter;
      kmLife = kmLife + distMeter;
    
      distMeter = 0.0;
      
    }

I will post part 2 in next comment…

Sketch part 2:

    //Draw "page"
    if(pageChange == 0){
      lcd.clear();
      lcd.print(F("V"));
      lcd.print(hast);
      lcd.setCursor(9,0);
      lcd.print(F("I"));
      lcd.print(inTemp);
      lcd.setCursor(0,1);
      lcd.print((float)kmTank/1000);
      lcd.setCursor(9,1);
      lcd.print(F("U"));
      lcd.print(outTemp);
      
    }else if(pageChange == 1){
      lcd.clear();
      lcd.print((float)kmTot/1000);
      lcd.setCursor(9,0);
      lcd.print(F("M"));
      lcd.print(enTemp);
      lcd.setCursor(0,1);
      lcd.print(kmLife/1000);
      lcd.print(F("km"));
      //GET TIME AND SHOW READ PDF PAGE 13!
      //ONE MORE BUTTON FOR RESET
      
    }else if(pageChange == 2){
      lcd.clear();
      //Get time
      
      if(GPS.hour, DEC == 23){
        hour = 0;
      }else{
        hour = GPS.hour + 1;
      }
      lcd.print(hour); lcd.print(':');
      lcd.print(GPS.minute); lcd.print(':');
      lcd.print(GPS.seconds);
      lcd.setCursor(9,0);
      lcd.print(F("I"));
      lcd.print(inTemp);
      lcd.setCursor(0,1);
      lcd.print(F("V"));
      lcd.print(hast);
      lcd.setCursor(9,1);
      lcd.print(F("U"));
      lcd.print(outTemp);   
    }
  
  }
  
  //Reset km per tank
    if(currentMillis - previousMillis > interval && analogRead(A3) >= 1000){
      previousMillis = currentMillis;
      
      delay(5);
      EEPROMpos = writeKm(kmLife, EEPROMpos);
      delay(50);
      EEPROMpos = writeKm(kmTot, EEPROMpos);
      delay(50);
      kmTank = 0;
      delay(5);
      EEPROMpos = writeKm(kmTank, EEPROMpos);
      delay(50);
      
      kmLife = getKm(0);
      kmTot = getKm(lengthInt(kmLife) + 1);
      kmTank = getKm(lengthInt(kmLife) + lengthInt(kmTot) + 2);
      
    }
  
  delay(50);
  
  if(prevHast >= 10 && hast < 10){
            
      delay(2000);
      
      EEPROMpos = writeKm(kmLife, EEPROMpos);
      delay(500);
      EEPROMpos = writeKm(kmTot, EEPROMpos);
      delay(500);
      EEPROMpos = writeKm(kmTank, EEPROMpos);
      delay(500);
    }
	
    prevHast = hast;
  
}

void GPSdata(){
  //Get data from GPS
  if (! usingInterrupt) {
    // read data from the GPS in the 'main loop'
    char c = GPS.read();
    // if you want to debug, this is a good time to do it!
    if (GPSECHO)
      if (c) Serial.print(c);
  }
  
  // if a sentence is received, we can check the checksum, parse it...
  if (GPS.newNMEAreceived()) {
    
    if (!GPS.parse(GPS.lastNMEA()))   // this sets the newNMEAreceived() flag to false
      return;  // we can fail to parse a sentence in which case we should just wait for another
  }

  // if millis() or timer wraps around, we'll just reset it
  if (timer > millis())  timer = millis();
  
}

void eepromWrite(byte wordAddress, byte data){
  Wire.beginTransmission(ADDRESS);
  Wire.write(wordAddress);
  Wire.write(data);
  Wire.endTransmission();
  
}

byte eepromRead(byte wordAddress){
  Wire.beginTransmission(ADDRESS);
  Wire.write(wordAddress);
  Wire.endTransmission();
  
  Wire.requestFrom(ADDRESS,1); //The address and number of bytes we want
  
  while(!Wire.available()){} //Waiting for request
  
  return Wire.read();
}

byte lengthInt(long int number){
  return (int)floor(log10(number)) + 1; //Get number of digits in number
  
}

int writeKm(long int km, int pos){
  int place = pos;
  long int kmVal = km;
  int lengthKm = lengthInt(kmVal);
  long int z = 10;
  String str = "10";
  
  //pow() does not work. Making str instead.
  for(int y = 1; y < lengthKm; y++){
    str += "0";
  }
  
  //Converting str to long int
  long int lengthKmPow = atol(str.c_str());
  
  eepromWrite(0 + place, lengthKm);
  delay(5);
  
  //separating numbers in km number
  for(int x = 1; x < lengthKm +1; x++){
    int m = kmVal / (lengthKmPow / z);
    kmVal = kmVal - m * (lengthKmPow / z);
    z = z*10;
    eepromWrite(x + place, m);
    delay(5);
  }
 
  //next empty position in EEPROM 
  place += lengthKm +1;
  
  return place;
  
}

long int getKm(int pos){
  
  long int km = 0;
  int length = 0;
  String str = "";
  
  length = eepromRead(pos);
  
  for(int x = pos + 1; x <= length + pos; x++){
    str += eepromRead(x);
  }
  
  km = atol(str.c_str());
  
  return km;
}

You do have the WP pin of the EEPROM held high right?

Can you reproduce the problem with a much smaller sketch? I would think that a trivial sketch, something like:

byte x=eepromRead(0);
eepromWrite(0,x-1);
delay(15); //write delay 10ms + margin
byte y=eepromRead(0);
if (x==y){serial.println("write did not work!");

should be able to demonstrate writing.

Thank you for your reply! :slight_smile:
Writing does not seem to be the problem as long as it is a small and simple sketch.
I have not tried you small sketch, but the sketch I made (the small sketch in the original post) is working just fine. So WP should be correct. I was thinking it was because of lack of SRAM, because of some interference with the pins, or because the Arduino is trying to do more than it can handle?

I could confirm that your simplified sketch was indeed writing and reading to the eeprom, but I do not know why the longer sketch does not work. I would take a different approach and not count digits, break and store numbers as single digits, or use Strings. The String objects certainly won’t help any memory issues you are having.

I would just use a cast and memcpy to pack the long kmTank/kmTot/kmLife values into a byte array, and write that into the eeprom. Rather than track length and control storage for long and short numbers, I would just use 4 bytes for each long, and manage the individual address locations if needed. I’d read the bytes back out and recast them into longs.

I’ve demonstrated that approach in this sketch, but I have a larger eeprom, and I used the two byte memory address form in my Wire.read and Wire.write. I think that in your sketch you are only able to write to the lower 256 address locations of the 4k eeprom and there is control bit for the upper locations which needs to be in the device address byte.

#include <Wire.h>
#define ADDRESS 0x50 //Address of EEPROM

void setup() {
  Serial.begin(9600);
  Wire.begin();

  long int km[3];
  km[0] = 123456789;
  km[1] = 234567891;
  km[2] = 345678912;

  Serial.println("Long DataIn");
  for (byte i = 0; i < 3; i++) 
  {
    Serial.println(km[i]);
  }

  byte eepromStoreBytes[sizeof(km)];

  memcpy(&eepromStoreBytes, (uint8_t*)km, sizeof(km));

  Serial.println("Writing bytes to eeprom");
  for (int i = 0; i < sizeof(km); i++) 
  {
    Serial.println(eepromStoreBytes[i]);
    writeByte(ADDRESS, i, eepromStoreBytes[i]);
  }

  long int dataBack[3];
  byte eepromReadBytes[sizeof(km)];

  Serial.println("Reading bytes from eeprom");
  for (int i = 0; i < sizeof(km); i++) 
  {
    eepromReadBytes[i] = readByte(ADDRESS, i);
    Serial.println(eepromReadBytes[i]);
  }

  memcpy (&dataBack, eepromReadBytes, sizeof(eepromReadBytes));

  Serial.println("Long DataBack");
  for (byte i = 0; i < 3; i++) 
  {
    Serial.println(dataBack[i]);
  }

}

void loop() {}

void writeByte(int device, unsigned int add, byte data)
{
  Wire.beginTransmission(device);
  Wire.write((int)(add >> 8)); // left-part of pointer address
  Wire.write((int)(add & 0xFF)); // and the right
  Wire.write(data);
  Wire.endTransmission();
  delay(5);//small delay for eeprom to save data
}


// reads a byte of data to the I2C address 'device', in memory location 'add'
byte readByte(int device, unsigned int add)
{
  Wire.beginTransmission(device); // I2C address
  Wire.write((int)(add >> 8)); // bit shift for high byte of pointer address
  Wire.write((int)(add & 0xFF)); // mask for the low byte
  Wire.endTransmission();
  Wire.requestFrom(device, 1); 
  return Wire.read();
}

Chrstoffer:
Sketch part 1:

I will post part 2 in next comment...

You can attach files to your posts.

I will give your sketch a try on my way home, cattledog :slight_smile:
Yes, I know I can attach files Nick. I thought it would be easier for people willing to help me if I just pasted it in a post.

Thank you, thank you, thank you!! With a tiny bit of tweaking, I got your sketch to work with my EEPROM and it is working beautifully!