How to implement ticker callback at 100 micro seconds for ESP8266? - esp8266

I have an ESP8266 NodeMCU 12E development board and I'm using the Arduino IDE. I'm trying to use a Ticker.h to sample an analog input consistently at a frequency of 10khz, which is one sample every 100us. I noticed that Ticker sampler; sampler.attach(0.0001,callbackfunc); didn't work because attach() won't take the value 0.0001.
So then I wrote the following code based on some guides that I saw:
#include <ESP8266WiFi.h>
#include <Ticker.h>
bool s = true;
void getSample()
{
s = !s;
}
Ticker tickerObject(getSample, 100, 0, MICROS_MICROS);
const char *ssid = "___"; // Change it
const char *pass = "___"; // Change it
void setup()
{
Serial.begin(115200);
Serial.println(0); //start
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, pass);
tickerObject.start();
}
void loop()
{
if(s == true)
{
Serial.println("True");
}
else
{
Serial.println("False");
}
}
However, this did not compile because tickerObject.start() method did not exist. So what I did next was:
Download the latest ticker package as a zip file
Unzip the package from point 1
Made a back up of C:\Users\john\Documents\ArduinoData\packages\esp8266\hardware\esp8266\2.5.0-beta2\libraries\Ticker
Replaced the folder mentioned in point 3 with the Ticker folder in point 2.
Restarted my Arduino IDE
Compiled and ran the code
Opened up the Serial Monitor
However, when I inspect the serial monitor, all it prints is "True". I was expecting the value s to toggle between true and false at a 10khz frequency.
What did I do wrong?

From the documentation of this library:
The library use no interupts of the hardware timers and works with the micros() / millis() function.
This library implements timers in software by polling the micros() and millis() functions. It requires the update() method to be called in loop().
So the start of loop() should be:
void loop()
{
tickerObject.update();
if(s == true)
I'm trying to use a Ticker.h to sample an analog input consistently at a frequency of 10khz
It is worth a go but this is a software based solution that is prone to jitter depending on how often the event loop can be called.

Related

IOT - Error : LEDC is not initialized Nodemcu ESP32 in wokwi

i am using wokwi for ESP32 simulation and i get error LEDC is not initialized, i am confused which part of my code there is error like that. I attached my code:
#define Balarm 13
#define PIRsensor 2
#define LedPin 14
#define pitch 262
void setup() {
Serial.begin(9600);
pinMode(PIRsensor, INPUT); // PIR sensor as input
pinMode(Balarm, OUTPUT); // Buzzer alaram as output
pinMode(LedPin, OUTPUT);
digitalWrite (Balarm, LOW);// Initially buzzer off
}
void loop(){
int state = digitalRead(PIRsensor);
delay(500);
if(state == HIGH){
tone(Balarm, pitch);
digitalWrite (LedPin , HIGH);
delay(1000);
}
else {
noTone(Balarm); //No intrusion Buzzer off
digitalWrite (LedPin , LOW);
}
}
I also want to ask why the buzzer sound can't be heard, how to make it sound, shouldn't it just use toner(). I also added an image, and I also want to ask if the flow of my image is suitable for making anti-theft alarms,if the sensor is on then the buzzer and led are also lit.I ask for his help to correct the location of the error I made:
https://ibb.co/6PC18Qz
Sorry that you haven't received a response.
You are using the Arduino tone library. When using the ESP32, you should refer to the LEDC API.
Further, a good guide for using this API can be found here.

ESP8266 Soft WDT Error

How can I avoid the Soft WDT reset Error in this loop.
The Error consistently occurs when reaching the number 3190.
unsigned long TimeFrame = 10000;
void setup() {
Serial.begin(9600);
}
void loop() {
unsigned long StartTime = millis();
while (millis() - StartTime <= TimeFrame){
Serial.println(millis() - StartTime);
}
}
I could count 4 times to 2500 but would this be the correct approach to this error?
Thanks for the explanation. I added a delay(10) to the code and it works.
void setup() {
Serial.begin(9600);
}
void loop() {
unsigned long StartTime = millis();
while (millis() - StartTime <= TimeFrame){
Serial.println(millis() - StartTime);
delay(10);
}
}
WDT is the "watchdog timer". Watchdog timers are used to get control back when something goes wrong in a system - say, an infinite loop or some other unexpected condition. When the underlying system gets control back it resets these timers so that they start counting up from zero again. When they hit their maximum value they trigger a hardware reset on the chip.
Your code measures the duration of the ESP8266 software watchdog timer - in this case 3.19 seconds.
The ESP8266 has both hardware and software watchdog timers. loop() isn't intended to run indefinitely - it's intended to do a small amount of work and then return. When it returns, the ESP8266 SDK gets to reset the watchdog timers.
Both the delay() and yield() functions give the SDK a chance to say "things are okay" and reset the timers. If you need to have long running code in loop() you should call one of those occasionally to give the rest of the system a chance to run.
Keeping loop() brief isn't just about the watchdog timer, either. It also gives the network stack a chance to run and do processing that it needs to do.
You should always design your program so that loop() does a small batch of repetitive processing and then returns. It should never contain an infinite loop or long loop of code.
For instance, suppose you need to do something every 20 seconds. This is the wrong way to do it:
void loop() {
unsigned long start = millis();
while(millis() - start < 20*1000) ;
do_something();
}
That breaks the way the software is designed to work - with loop() executing briefly. It doesn't allow any other software to run while it's waiting. The watchdog timer will fire and reset your CPU.
This is better:
void loop() {
delay(20*1000);
do_something();
}
because delay() lets the underlying system get control back, reset the watchdog timer and also do network-related processing.
In my opinion, this is best:
static unsigned long start_time;
void setup() {
start_time = millis();
}
void loop() {
if(millis() - start_time > 20*1000) {
do_something();
start_time = millis();
}
}
because it does the least possible work inside loop(), only when it's time to do the work.

Can't send data from my custom IOS App using BLE Shield 2.1 to my Arduino Uno

I am currently working on designing a snake-like robot that will be controlled via a custom Iphone iOS app.
I am using the following technologies:
- Arduino-Uno
- Adafruit Motor Shield
- Nema 17 Bipolar Stepper Motor
- BLE Shield 2.1 from RedBearLab
My iOS app includes a slider, such that when the slider moves, a stepper motors speed will be controlled. I am using a Write function in xcode to send the speed data to my arduino, but when I try to print the read results in my Serial Monitor, the data is just 0.
func writeSpeed(_ speed: UInt8)
{
// See if characteristic has been discovered before writing to it
if let positionCharacteristic = self.positionCharacteristic
{
let data = Data(bytes: [speed])
self.peripheral?.writeValue(data, for: positionCharacteristic, type: CBCharacteristicWriteType.withResponse) //with originally
print(data)
print(speed)
}
}
When I print the data and speed it's results look as follows...
1 bytes
3
1 bytes
4
1 bytes
3
1 bytes
2
end sliding
My Arduino code is as follows...
// Add in necessary libraries
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include <SoftwareSerial.h>
#include <SPI.h>
#include <boards.h>
#include <RBL_nRF8001.h>
#include <RBL_services.h>
SoftwareSerial BLE_Shield(9,8); // Configure the Serial port to be on pins D8 and D9. This
// will match the jumpers on the BLE Shield (RX -> D8 & TX /> D9)
// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
// Define the stepper motor attached to the motor shield.
// i.e. 200 steps per revolution (1.8 degree) in motor port #2 (M3 and M4)
const int stepsPerRevolution = 200;
const int motorPort = 2;
Adafruit_StepperMotor *myMotor = AFMS.getStepper(stepsPerRevolution,
motorPort);
void setup()
{
// put your setup code here, to run once:
BLE_Shield.begin(9600); // Setup the serial port at 9600 bps. This is the BLE Shield default baud rate.
ble_begin();
// Initialize serial port at 9600 bps
Serial.begin(9600);
// Print a ready statement to serial monitor
Serial.println("Stepper Motor is Ready!");
// Stepper motor
AFMS.begin(1600); // create with the default frequency 1.6KHz
}
void loop()
{
// put your main code here, to run repeatedly:
ble_do_events();
if(BLE_Shield.available())
{
Serial.println("Available");
// Read the value of the slider and store it in the variable data
int data = BLE_Shield.read(); //Read the incoming data & store into data
Serial.println("The data from iphone is...");
Serial.print(data);
// Eventually sliderData will be a variable, but for now, a constant
const int sliderData = 30;
// Specify the speed of my stepper motor
myMotor->setSpeed(sliderData); // 30 rpm
Serial.println("Speed is 30 rpm");
}
else
{
Serial.println("Stopped working");
}
//ble_do_events();
delay(500);
}
I really don't know why this isn't working.

Windows 10 IoT GPIO interrupt frequency

I have a raspberry pi 3 with Windows 10 IoT. I would like to get the data from a sensor that sends pulses. Namely the Swiss Flow SF800 link. This sensor will send out an amount of pulses equal to the amount of flow through the sensor. The datasheet says that I will send up to 2kHz.
My question is will the GPIO on the raspberry pi handle an interrupt frequency this high? I have looked into the lightning provider https://developer.microsoft.com/en-us/windows/iot/docs/lightningproviders which is supposed to be a huge performance gain but cannot find any documentation about what kind of performance I should expect.
There is no official bench marks of GPIO interrupt for now.
Here is Windows IoT Lightning Performance Testing. It tested GPIO performance by toggling GPIO 5 between 0 and 1 at the fastest possible speed. It seems at least 17.4 kHz can be achieved.
And GPIO interrupt event should be pushed into the queue and will not be lost.
So, based on above information, for 2kHz, app will be able to handle such speed interrupt event in time and without missing.
Feel free to use it and if there is any concern please let me know.
Initially I suspected that I would need to use the lightning driver in order to achieve the interrupt frequency that I needed. It turns out that the standard Inbox Driver is adequate for what I need.
Here are the steps to reproduce my situation:
I created a simple Arduino sketch that would send out pulses at the rate of 10,000 Hz.
int dataPin = 12;
void setup() {
pinMode(dataPin, OUTPUT);
}
void loop() {
int count = 0;
while (count < 400)
{
//pulse
digitalWrite(dataPin, HIGH);
digitalWrite(dataPin, LOW);
//This delay presumably makes the pulse be 10000 Hz
delayMicroseconds(100);
count++;
}
delay(5000);
}
Created a UWP app with a simple UI that had a TextBlock in the center of the page.
public sealed partial class MainPage : Page
{
private GpioController gpio;
private const int inputPinNumber = 17;
private GpioPin inputPin;
private int count;
private I2cController i2cController;
private SpiController spiController;
public MainPage()
{
this.InitializeComponent();
this.Setup();
}
private void Setup()
{
if (LightningProvider.IsLightningEnabled)
{
LowLevelDevicesController.DefaultProvider = LightningProvider.GetAggregateProvider();
}
this.gpio = GpioController.GetDefault();
this.inputPin = this.gpio.OpenPin(inputPinNumber);
if (this.inputPin.IsDriveModeSupported(GpioPinDriveMode.InputPullUp))
{
this.inputPin.SetDriveMode(GpioPinDriveMode.InputPullUp);
}
else
{
this.inputPin.SetDriveMode(GpioPinDriveMode.Input);
}
this.inputPin.ValueChanged += InputPinOnValueChanged;
}
private void InputPinOnValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
{
var task = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
if (args.Edge == GpioPinEdge.FallingEdge)
{
this.count++;
this.CountBlock.Text = this.count.ToString();
}
else
{
}
});
}
}
}
Set Windows IoT to use the Direct Memory Mapped Driver.
The next step was to connect the pin on the Arduino with the pin on the Pi through a transistor. I did this so that I could take advantage of the built in Pull-Up resistor on the GPIO pins on the Pi.
When both applications were run at the same time I was only collecting about 30 pulses per cycle.
Went back into the Windows IoT setup and reset the driver back to the Inbox Driver and reran both applications. This time I did not miss a pulse.
In conclusion the Inbox Driver should be sufficient to give me up to 10khz without any issue.

How to decrease memory usage on Arduino Uno

I am using a clone of Arduino UNO, Dccduino and I have problem with the memory.Sketch uses 25,114 bytes (77%) of program storage space. Maximum is 32,256 bytes. Global variables use 1,968 bytes (96%) of dynamic memory, leaving 80 bytes for local variables. Maximum is 2,048 bytes. Low memory available, stability problems may occur.
Is there any way to reduce the memory about 20% if not I think I have to buy Arduino Mega
Here is the code:
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include "RTClib.h"
#include <GPRS_Shield_Arduino.h>
#include <SoftwareSerial.h>
// Data wire is plugged into port 3 and 2 on the Arduino
#define ONE_WIRE_BUS_1 3 // Many sensors on pin 3
#define ONE_WIRE_BUS_2 2 // Many sensors on pin 2
#define TEMPERATURE_PRECISION 9 // Lower resolution
#define PIN_TX 7
#define PIN_RX 8
#define BAUDRATE 9600
#define PHONE_NUMBER "xxxxxxxxxxxxx"
GPRS gprsTest(PIN_TX, PIN_RX, BAUDRATE); //RX,TX,PWR,BaudRate
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire1(ONE_WIRE_BUS_1);
OneWire oneWire2(ONE_WIRE_BUS_2);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors1(&oneWire1);
DallasTemperature sensors2(&oneWire2);
int numberOfDevices1; // Number of temperature devices found on pin 3
int numberOfDevices2; // Number of temperature devices found on pin 2
DeviceAddress tempDeviceAddress1; // We'll use this variable to store a found device address for bus 3
DeviceAddress tempDeviceAddress2; // We'll use this variable to store a found device address for bus 2
File myFile;
RTC_DS3231 rtc; // Create a RealTimeClock object
void setup(void)
{
// start serial port
#ifndef ESP8266
while (!Serial); // for Leonardo/Micro/Zero
#endif
Serial.begin(9600);
delay(3000);
Serial.println(F("Dallas Temperature IC Control Library Demo"));
Serial.print( F("Initializing SD card..."));
if (!SD.begin(4)) {
Serial.println(F("\ninitialization failed!"));
return;
}
Serial.println(F("initialization done."));
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
if (rtc.lostPower()) {
Serial.println("RTC lost power, lets set the time!");
// following line sets the RTC to the date & time this sketch was compiled
//rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}
/* while(!gprsTest.init()) { //gprs init
delay(1000);
Serial.print(F("init error\r\n"));
Serial.println(F("gprs init success"));*/ It takes 20% of dynamic memory so i cant use it
}
Serial.println(F("start to call ..."));// Call when device will start
gprsTest.callUp(PHONE_NUMBER);
Serial.println("start to send message ...");
gprsTest.sendSMS(PHONE_NUMBER, "Hi device is ON"); //define phone number and text
// Start up the library
sensors1.begin();
sensors2.begin();
// Grab a count of devices on the wire
numberOfDevices1 = sensors1.getDeviceCount();
numberOfDevices2 = sensors2.getDeviceCount();
// locate devices on the bus
Serial.print(F("Locating devices..."));
Serial.print(F("Found "));
Serial.print(numberOfDevices1, DEC );
Serial.print(F("+"));
Serial.print(numberOfDevices2, DEC );
Serial.println(F(" devices."));
// report parasite power requirements
Serial.print("Parasite power is: ");
if (sensors1.isParasitePowerMode()) Serial.println(F("Sensors 1 ON"));
else Serial.println(F("\nSensors 1 OFF"));
if (sensors2.isParasitePowerMode()) Serial.println(F("Sensors 2 ON"));
else Serial.println(F("Sensors 2 OFF"));
// Loop through each device, print out address for pin 3
for (int i = 0; i < numberOfDevices1; i++)
{
// Search the wire for address
if (sensors1.getAddress(tempDeviceAddress1, i))
{
Serial.print(F("Found device "));
Serial.print(i, DEC);
Serial.print(F(" with address: "));
printAddress(tempDeviceAddress1);
Serial.println();
Serial.println(F("\n"));
// set the resolution to TEMPERATURE_PRECISION bit (Each Dallas/Maxim device is capable of several different resolutions)
sensors1.setResolution(tempDeviceAddress1, TEMPERATURE_PRECISION);
} else {
Serial.print(F("Found ghost device for pin 3 at "));
Serial.print(i, DEC);
Serial.print(F(" but could not detect address. Check power and cabling"));
}
}
// Loop through each device, print out address for pin 2
for (int i = 0; i < numberOfDevices2; i++)
{
// Search the wire for address
if (sensors2.getAddress(tempDeviceAddress2, i))
{
Serial.print(F("Found device "));
Serial.print(i + numberOfDevices1, DEC);
Serial.print(F(" with address: "));
printAddress(tempDeviceAddress2);
Serial.println();
Serial.println(F("\n"));
// set the resolution to TEMPERATURE_PRECISION bit (Each Dallas/Maxim device is capable of several different resolutions)
sensors2.setResolution(tempDeviceAddress2, TEMPERATURE_PRECISION);
} else {
Serial.print(F("Found ghost device for pin 2 at "));
Serial.print(i, DEC);
Serial.print(F(" but could not detect address. Check power and cabling"));
}
}
}
void loop(void)
{
// call sensors1.requestTemperatures() to issue a global temperature
// request to all devices on the bus
Serial.print(F("Requesting temperatures to pin 3..."));
sensors1.requestTemperatures(); // Send the command to get temperatures for pin 3
Serial.println(F("DONE"));
myFile = SD.open("test1.txt", FILE_WRITE); //open file
// Loop through each device , print out temperature data for pin 3
for (int i = 0; i < numberOfDevices1; i++)
{
// Search the wire for address
if (sensors1.getAddress(tempDeviceAddress1, i))
{
// Output the device ID
Serial.print(F("Temperature for device: "));
Serial.println(i, DEC);
// It responds almost immediately. Let's print out the data
printTemperature1(tempDeviceAddress1);// Use a simple function to print out the data
Serial.print(F("\n"));
}
delay(4000);
//else ghost device! Check your power requirements and cabling
}// End forloop for pin 3
if (numberOfDevices2 != 0) {
Serial.print(F("Requesting temperatures to pin 2..."));
sensors2.requestTemperatures(); // Send the command to get temperatures for pin 2
Serial.println(F("DONE"));
}
// Loop through each device for pin 2, print out temperature data
for (int i = 0; i < numberOfDevices2; i++)
{
// Search the wire for address
if (sensors2.getAddress(tempDeviceAddress2, i))
{
// Output the device ID
Serial.print(F("Temperature for device: "));
Serial.println(i + numberOfDevices1, DEC);
// It responds almost immediately. Let's print out the data
printTemperature2(tempDeviceAddress2);// Use a simple function to print out the data
Serial.print(F("\n"));
}
else Serial.print(F("ghost device! Check your power requirements and cabling"));
delay(4000);
} //End forloop for pin 3
myFile.close(); // Should I close it?
}// End loop()
void printAddress(DeviceAddress deviceAddress) // function to print a device address
{
for (uint8_t i = 0; i < 8; i++)
{
if (deviceAddress[i] < 16) Serial.print(F("0"));
Serial.print(deviceAddress[i], HEX);
}
}
void printTemperature1(DeviceAddress deviceAddress1) // function to print the temperature for a device (pin 3)
{
float tempC = sensors1.getTempC(deviceAddress1);
Serial.print("Temp C: ");
Serial.print(tempC);
if (myFile)
{
Serial.println(F("\nWriting to test.txt..."));
myFile.print(F("C: "));
myFile.print(tempC);
print_time(); // Call print_time() function to print time on file
myFile.print(F("\n"));
Serial.print(F("Done!"));
}
else Serial.print(F("Error opening file 1"));
Serial.println("\n");
}
void printTemperature2(DeviceAddress deviceAddress2) // function to print the temperature for a device (pin 2)
{
float tempC = sensors2.getTempC(deviceAddress2);
Serial.print(F("Temp C: "));
Serial.print(tempC);
if (myFile)
{
Serial.print(F("\nWriting to test.txt..."));
myFile.print(F("C: "));
myFile.print(tempC);
print_time(); // Call print_time() function to print time on file
myFile.print(F("\n"));
Serial.print(F("Done!"));
} else Serial.print(F("Error opening file 2"));
Serial.println("\n");
}
void print_time() { // print time function
DateTime now = rtc.now();
Serial.print(now.year(), DEC);
Serial.print('/');
Serial.print(now.month(), DEC);
Serial.print('/');
Serial.print(now.day(), DEC);
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println();
myFile.print(now.year(), DEC);
myFile.print('/');
myFile.print(now.month(), DEC);
myFile.print('/');
myFile.print(now.day(), DEC);
myFile.print(now.hour(), DEC);
myFile.print(':');
myFile.print(now.minute(), DEC);
myFile.print(':');
myFile.print(now.second(), DEC);
myFile.println();
}
I'm doing more than that for a system in my barn - SD card, RTC, LCD display, GPRS modem, radio communication with other devices, controlling pumps based on programmed timing by season, rain sensors, float sensors, temp sensors, voltage sensors, etc. Here are some things I've found:
All string literals should be replaced by F() macro calls and or native string functions working on flash. So, strcpy_P(string1, PSTR(string2)), strcat_P(string1, PSTR(string2)) kind of calls.
Turn lots of accesses to devices into function calls that contain any data structures needed for those calls. Your arduino will work harder adding and removing stack and stack frames, but the data structures will be removed from the stack when the function is done, and machine cycles are much cheaper on Arduino than RAM. So, separate your temperature reading code in a separate function from your file-writing. Return the float, then send that float to your SD-writing function.
Hide all of your Serial code in debug_print code instead. So, you'll use debug_print calls when debugging, and they all disappear completely from your production code.
Make sure you're only calling the SD code in subroutines, and instantiating the actual FAT code in those subroutines as needed, not as a big global.
There are a number of different SD libraries, some are cheaper (memory-wise) than others. Shop around.
Use one of the freeMemory variants to decide if you have the memory available before calling one of those RAM-hungry SD calls. If you don't have enough RAM at the time, you may want to implement some kind of circular buffer in EEPROM to store messages to be written to SD when you do have the RAM available.
Use booleans and bytes instead of ints when possible, and consider using bit fields for flags to save even more RAM. Are you really going to have up to 32,000 devices on your temperature buses? You can get 255 in a byte.
I think I've seen this code before on another site.
From your code I assume you're making a temperature logger and you want to log the data on the SD card.
Most of your code is redundant if you use the DS1307RTC.h library and the Time.h library. DS1307RTC is a generic RTC library. With it you don't need OneWire, Wire or SPI and Wire. Software Serial also is unnecessary.
However, I suggest you check out my Arduino DataLogger Library on Github: https://github.com/FreelanceJavaDev/DataLogger
I've pretty much maxed out the memory I saved on my Uno down to 22,680 bytes (70%) of program storage space and 1,237 bytes (60%) of SRAM (Dynamic memory). It automatically configures the RTC and SD card. It makes a CSV file for export to excel for each month it's running organized by date.
I've built several Arduino data loggers in my time, and all of the ones using SD cards have failed because of lack of memory. The SD on its own uses half of the memory available on an ATMega 328. Add to that a couple of libraries for other hardware and you have no memory left at all for your sketch.
I have gone over to 24LC512s. One is normally enough, but you can use up to 4 with different addresses if you want. This is a relatively small amount of memory, but I have found it is always enough. It is too easy to generate megabytes of data which is too large to be analysed. One 24LC512 holds more than enough data for one spreadsheet. The only down side is that you have to use an Arduino to read back the data through the USB.
I use the first two bytes to store the number of records, and the next byte to store the number of bytes per record. (Although in retrospect the latter is not really necessary.) You might think that the first two bytes would “wear out” because they are re-written every time a new entry is made, but this has not happened to me yet. I have had the same Arduino continuously running for 7 years now (uninterrupted apart from a few updates to the sketch), and generating over 1000 records per month, so the first two bytes must have been updated that number of times. I have never had a problem with the first two bytes, and even if I did, it would be cheap enough to replace the 24LC512.
You can even get away with “hot plugging”: I leave the logger running and change over the memory chip for a fresh one so that I can read the data without interrupting the logger. (Read the number of records, then increment it and write the new number and the data.)

Resources