In my previous article I have explained how to divide the memory into equal parts, so called blocks. I have concluded that to utilize the entire memory, I could divide it into 256 blocks of 16 bytes or maybe even 128 blocks for 32 bytes. Having full 32 bytes seem like the overkill and would probably lead to wasting precious space.
Also, many I2C (Wire) libraries have a fixed limit of 30 bytes per write. I Am currently using and makuna Rtc library and it states this in the source comment. This means, that it is not possible to write full 32 bytes in one go. It is however possible to split it to two writes without a problem. This is not a technical limitation, it just adds more lines into the code. For this reason, I will try to fit the data into 16 bytes, which is still a lot.
Having 16 bytes per block allows me to store least 256 logged entries, meaning either 256 entries every 24 hours or 128 entries twice a day. The initial format for the stored data I have come up looks like this:
1620967937+12345
The data are the following form:
[unix epoch][flipping separator][wight in dekagrams]
This form fits into the 16 bytes. The flipping separator flips its sign every time the entry is overwritten. This is essential, otherwise we would not know which data are the most recent in the circular buffer data structure. I have discussed this topic a few days ago. In reality the data (truncated to just 6 blocks of 16 bytes for illustration) could look like this:
1620967977+10080
1620967987+10090 // the most recent entry
1620967937-10040 // the least recent entry
1620967947-10050
1620967957-10060
1620967967-10070
The data show the entry is created every 10 seconds and the weight is gradually increasing by 10 dekagrams (100 grams). This could work without much problems, but could it be done better?
Possible optimizations
- The library uses some strange epoch time,
starting at 1st of January 2000.
This shaves of a byte, because such epoch time is currently around
674252045
. The library provides tools to manipulate such epoch, so sticking with it for now. - Since I am storing an epoch time already, I could find the division between the least and the most recent entry by comparing these times, this could shave off the flipping separator byte. I would not need any separator as all the data are fixed length, separating them simply by an offset.
- If the data are stored at the fixed time intervals, the epoch would not be needed at all, since it could be calculated backwards in time. Now it would require just the flipping separator and a weight, as there needs to be at least one way to determine where the head of the circular buffer currently it.
- The most importantly, since I am just trying to store integers, they would take much less space than storing them as individual characters. For instance, the epoch would fit into a mere 30 bits and even the full Unix epoch currently sits at 31 bits, which are just 4 bytes, down from the 9 or 10 bytes when stored as characters! Similarly, weight could be stored in just two bytes (a maximum of 65535 dekagrams, which is three times over it's range anyway), giving me the required block size of just 4 + 2 bytes, providing a whopping 682 log entries!
Next steps
With 682 possible entries I could decide to either log entries more often, possibly reducing the significance of occasional measurement errors or choose to log additional data, like temperature. DS3132 has an internal thermometer to calibrate it's oscillator frequency and it's value is available to read. This means I could choose to What's more, it can even be very precise, even down to 0.002°C. Quite interesting for a beehive, as this project is measuring the weight of potential honey remotely.
The library however provides the way to store an array of characters or C strings by default, not integers at specified offset. I have to figure out how to use integers natively wit this library easily if I want to continue being lazy, but it should not be hard.
This is a 65th post of #100daystooffload.