# How can I understand the bytes of sensor memory without having the memory map in I2C bus

I am a data scientist and I do not know much about programming and electronics. However, I have to get some data from an O2 sensor. The sensor (Gravity O2 Sensor 0-25%) is a cheap Chinese one (~ 60 Euros) Unfortunately, the library that the manufacturer has provided reads funny numbers, funny in the way that the decimals seem reasonable in the sense that they do not vary much in short periods of time, but the numbers before decimals jump from 21 to 25 and then 27 so on. You can see the issue in the image below.

I checked their library code and couldn't find an issue with it. In any case, I decided to try and read the data myself using the Python smbus2 library. Now I can read the bytes like below, however, I don't know what they mean.

[115,223,16,85,7,1,0,0,0,0,237,0,0,0,0,255] [115,223,16,86,5,7,0,0,0,0,237,0,0,0,0,255] [115,223,16,85,0,9,0,0,0,0,237,0,0,0,0,255]

I couldn't find a memory map or registry table if that is where I can find the relation between these numbers and the oxygen concentration level. I was even thinking maybe I should look for a memory map of another electronic part on the board. I am lost and I have no one around to ask, but StackExchange. Any help is appreciated.

• Without a datasheet for the sensor, there isn't much we can say about it. Dec 4, 2022 at 0:11
• Thanks for your comment @DaveTweed . Somehow I found my way around right after I posted my question. I just had to read the whole code they had written for their library to figure out what corresponds to what. The problem was that sometimes the one register produced outliner numbers out of the sky that were out of the acceptable limit. Just edited their code and made the calculations without those outliners.
– Leo
Dec 4, 2022 at 0:49
• @Leo in the spirit of Stack Exchange, and the benefit & appreciation of the next poor data scientist or other struggler with the same or similar sensor - could you 'self-answer' your question with a brief description of how you fixed or worked around the problem? Dec 4, 2022 at 1:01
• @brhans I didn't know I could! Thanks for letting me know that I can. I added it.
– Leo
Dec 4, 2022 at 1:51

Here is the part of the python code of the manufacturer which I originally had overlooked.

## I2C address select
## Register for oxygen data
OXYGEN_DATA_REGISTER      = 0x03
## Register for users to configure key value manually
USER_SET_REGISTER         = 0x08
## Register for automatically configuring key value
AUTUAL_SET_REGISTER       = 0x09
## Register for obtaining key value
GET_KEY_REGISTER          = 0x0A


You can say from this section that the Oxygen data register is 3. In reality, register 3,4, and 5 are the Oxygen data register, register 3 being the part that stores the part before the decimal point.

  def get_oxygen_data(self, collect_num):
'''!
@brief Get oxygen concentration
@param collectNum The number of data to be smoothed
@n     For example, upload 20 and take the average value of the 20 data, then return the concentration data
@return Oxygen concentration, unit vol
'''
self.get_flash()
if collect_num > 0:
for num in range(collect_num, 1, -1):
self.__oxygendata[num-1] = self.__oxygendata[num-2]
self.__oxygendata[0] = self.__key * (float(rslt[0]) + float(rslt[1]) / 10.0 + float(rslt[2]) / 100.0)
if self.__count < collect_num:
self.__count += 1
return self.get_average_num(self.__oxygendata, self.__count)
elif (collect_num > 100) or (collect_num <= 0):
return -1


You can infer from the code above that in memory block [115,223,16,85,0,9,0,0,0,0,237,0,0,0,0,255], values 85,0,9 relate to Oxygen level because the function calls for an array of size 3 starting from position 3. I was reading the first 16 bytes of the sensor memory in small intervals when I realized position 3 of the memory array sometimes produces values over 200, while generally, the values are more or less around 85 at home atmosphere where the concentration is supposed to be around 20-21%. Given those, I concluded that something out of my understanding causes that register to produce outliers.

    self.__oxygendata[0] = self.__key * (float(rslt[0]) + float(rslt[1]) / 10.0 + float(rslt[2]) / 100.0)


From the part above you can say that location 3 of the memory array (which is also jumpy), corresponds to the non-decimal part of the O2 concentration level. So the easiest way out would be to get rid of those outlier values. Given that 85 corresponds to 21%, and the sensor is supposed to work up until 25%, I recon that values due to linear calculations we can simply call values over 100 as outliers. I just added some lines to get rid of those values like in the code below, and now as far as I care the values are good enough.

  def get_oxygen_data(self, collect_num):
'''!
@brief Get oxygen concentration
@param collectNum The number of data to be smoothed
@n     For example, upload 20 and take the average value of the 20 data, then return the concentration data
@return Oxygen concentration, unit vol
'''
skipped = 0 # counting the number of outliers
self.get_flash()
if collect_num > 0:
for num in range(collect_num, 1, -1):
self.__oxygendata[num-1] = self.__oxygendata[num-2]
if rslt[0] > 100:
self.__oxygendata[0] = self.__key * (float(rslt[0]) + float(rslt[1]) / 10.0 + float(rslt[2]) / 100.0)
else:
skipped +=1 # add 1 for each outlier
if self.__count < collect_num:
self.__count += 1
return self.get_average_num(self.__oxygendata, self.__count - skipped) # -skipped to compensate for the outliers
elif (collect_num > 100) or (collect_num <= 0):
return -1



In the first run, you get a DivisionByZero error that one can easily handle or just ignore and run again.

To answer your question, you need to have the sensor data sheet, the schematic of the device, and the code. If you want to torture yourself to try and 'reverse engineer' the whole thing, so be it...age will cure that :) [ok that was a little too smug and I do admire your effort].

So, it looks like you can get the schematic here: https://dfimg.dfrobot.com/nobody/wiki/687ba70b4fab48f7d273c4a9b96c3bdd.pdf I would verify that the schematic, as far as it goes, is accurate.

Then I would drill down on the sensor. Look at pictures of the product, many contain some coded numbers - just like an IC (e.g., https://www.electromaker.io/shop/product/gravity-i2c-oxygen-sensor). It could be that your label obscures them - I don't know, but if it was obscurred, I would start asking (after searching) if anyone (especially DFRobot) knows the brand of sensor they are using.

Looking around (there really are not as many gas sensor cans as you might think), I found this one https://www.winsen-sensor.com/d/files/ze03-electrochemical-module-manualv2_8(5).pdf which is a likely, maybe, could be, candidate. I would also see what other sensors that company makes that it could be [the one I linked is the most likely candidate I think].

Armed with what could be the data sheet, I note that it has an analog or serial output and that is a candidate worthy characteristic because the schematic shows a microprocessor that is, basically, translating UART serial to I2C serial (to and fro I guess).

Then I would go through that, frequently cryptic, datasheet and try to see if I could match up what the datasheet says with the observed values. That is where you are going to have to go through the code to completely understand what is happening.

You also have to account for the calibration that you do manually and that results in some key value.

Finally, there are some safety concerns whenever you are working with O2, if the concentrations are high. That sensor is not any good for high concentrations, but still, you may want to read up a bit on that aspect.

I could be very far off the mark here, but hopefully, it may give you some clues.