Here is the part of the python code of the manufacturer which I originally had overlooked.
## I2C address select
ADDRESS_0 = 0x70
ADDRESS_1 = 0x71
ADDRESS_2 = 0x72
ADDRESS_3 = 0x73
## 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]
rslt = self.read_reg(OXYGEN_DATA_REGISTER, 3)
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]
rslt = self.read_reg(OXYGEN_DATA_REGISTER, 3)
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.