open-degu / USER_COMMUNITY

Deguの使い方に関する疑問を投稿、回答するためのIssueを提供しています。
4 stars 2 forks source link

Python file corrupted after low battery voltage shutdown #33

Open alanp-sporelab opened 4 years ago

alanp-sporelab commented 4 years ago

After letting the Degu unit run on battery power for an extended period, the Degu unit shuts itself down itself due to low battery (output says ~2.2V, read using the battery example code). Battery discharge: Degu Battery

After replacing the battery of the degu unit, main.py file is found to be corrupted. Details:

from machine import Pin
from machine import Signal
from machine import ADC
import zcoap
import time
import ujson
from ustruct import unpack, unpack_from
from array import array
from machine import I2C

# BMP280 default address.
BMP280_I2CADDR = 0x77

# Operating Modes
BMP280_OSAMPLE_1 = 1
BMP280_OSAMPLE_2 = 2
BMP280_OSAMPLE_4 = 3
BMP280_OSAMPLE_8 = 4
BMP280_OSAMPLE_16 = 5

BMP280_REGISTER_CONTROL_HUM = 0xF2
BMP280_REGISTER_CONTROL = 0xF4

class BMP280:

    def __init__(self,
                 mode=BMP280_OSAMPLE_1,
                 address=BMP280_I2CADDR,
                 i2c=None,
                 **kwargs):
        # Check that mode is valid.
        if mode not in [BMP280_OSAMPLE_1, BMP280_OSAMPLE_2, BMP280_OSAMPLE_4,
                        BMP280_OSAMPLE_8, BMP280_OSAMPLE_16]:
            raise ValueError(
                'Unexpected mode value {0}. Set mode to one of '
                'BMP280_ULTRALOWPOWER, BMP280_STANDARD, BMP280_HIGHRES, or '
                'BMP280_ULTRAHIGHRES'.format(mode))
        self._mode = mode
        self.address = address
        if i2c is None:
            raise ValueError('An I2C object is required.')
        self.i2c = i2c

        # load calibration data
        self.i2c.writeto(self.address, 0x88)
        dig_88_a1 = self.i2c.readfrom(self.address, 26)
        self.i2c.writeto(self.address, 0xE1)
        dig_e1_e7 = self.i2c.readfrom(self.address, 7)
        self.dig_T1, self.dig_T2, self.dig_T3, self.dig_P1, \
            self.dig_P2, self.dig_P3, self.dig_P4, self.dig_P5, \
            self.dig_P6, self.dig_P7, self.dig_P8, self.dig_P9, \
            _, self.dig_H1 = unpack("<HhhHhhhhhhhhBB", dig_88_a1)

        self.dig_H2, self.dig_H3 = unpack("<hB", dig_e1_e7)
        e4_sign = unpack_from("<b", dig_e1_e7, 3)[0]
        self.dig_H4 = (e4_sign << 4) | (dig_e1_e7[4] & 0xF)

        e6_sign = unpack_from("<b", dig_e1_e7, 5)[0]
        self.dig_H5 = (e6_sign << 4) | (dig_e1_e7[4] >> 4)

        self.dig_H6 = unpack_from("<b", dig_e1_e7, 6)[0]

        self.i2c.writeto(self.address, bytearray([BMP280_REGISTER_CONTROL, 0x3F]))
        self.t_fine = 0

        # temporary data holders which stay allocated
        self._l1_barray = bytearray(2)
        self._l8_barray = bytearray(8)
        self._l3_resultarray = array("i", [0, 0, 0])

    def read_raw_data(self, result):
        """ Reads the raw (uncompensated) data from the sensor.
            Args:
                result: array of length 3 or alike where the result will be
                stored, in temperature, pressure, humidity order
            Returns:
                None
        """

        self._l1_barray[0] = BMP280_REGISTER_CONTROL_HUM
        self._l1_barray[1] = self._mode
        self.i2c.writeto(self.address, self._l1_barray)
        self._l1_barray[0] = BMP280_REGISTER_CONTROL
        self._l1_barray[1] = self._mode << 5 | self._mode << 2 | 1
        self.i2c.writeto(self.address, self._l1_barray)

        sleep_time = 1250 + 2300 * (1 << self._mode)
        sleep_time = sleep_time + 2300 * (1 << self._mode) + 575
        sleep_time = sleep_time + 2300 * (1 << self._mode) + 575
        time.sleep_us(sleep_time)  # Wait the required time

        # burst readout from 0xF7 to 0xFE, recommended by datasheet
        self.i2c.writeto(self.address, 0xF7)
        self._l8_barray = self.i2c.readfrom(self.address, 9)
        readout = self._l8_barray
        # pressure(0xF7): ((msb << 16) | (lsb << 8) | xlsb) >> 4
        raw_press = ((readout[0] << 16) | (readout[1] << 8) | readout[2]) >> 4
        # temperature(0xFA): ((msb << 16) | (lsb << 8) | xlsb) >> 4
        raw_temp = ((readout[3] << 16) | (readout[4] << 8) | readout[5]) >> 4
        # humidity(0xFD): (msb << 8) | lsb
        raw_hum = (readout[6] << 8) | readout[7]

        result[0] = raw_temp
        result[1] = raw_press
        result[2] = raw_hum

    def read_compensated_data(self, result=None):
        """ Reads the data from the sensor and returns the compensated data.
            Args:
                result: array of length 3 or alike where the result will be
                stored, in temperature, pressure, humidity order. You may use
                this to read out the sensor without allocating heap memory
            Returns:
                array with temperature, pressure, humidity. Will be the one from
                the result parameter if not None
        """
        self.read_raw_data(self._l3_resultarray)
        raw_temp, raw_press, raw_hum = self._l3_resultarray
        # temperature
        var1 = ((raw_temp >> 3) - (self.dig_T1 << 1)) * (self.dig_T2 >> 11)
        var2 = (((((raw_temp >> 4) - self.dig_T1) *
                  ((raw_temp >> 4) - self.dig_T1)) >> 12) * self.dig_T3) >> 14
        self.t_fine = var1 + var2
        temp = (self.t_fine * 5 + 128) >> 8

        # pressure
        var1 = self.t_fine - 128000
        var2 = var1 * var1 * self.dig_P6
        var2 = var2 + ((var1 * self.dig_P5) << 17)
        var2 = var2 + (self.dig_P4 << 35)
        var1 = (((var1 * var1 * self.dig_P3) >> 8) +
                ((var1 * self.dig_P2) << 12))
        var1 = (((1 << 47) + var1) * self.dig_P1) >> 33
        if var1 == 0:
            pressure = 0
        else:
            p = 1048576 - raw_press
            p = (((p << 31) - var2) * 3125) // var1
            var1 = (self.dig_P9 * (p >> 13) * (p >> 13)) >> 25
            var2 = (self.dig_P8 * p) >> 19
            pressure = ((p + var1 + var2) >> 8) + (self.dig_P7 << 4)

        # humidity
        h = self.t_fine - 76800
        h = (((((raw_hum << 14) - (self.dig_H4 << 20) -
                (self.dig_H5 * h)) + 16384)
              >> 15) * (((((((h * self.dig_H6) >> 10) *
                            (((h * self.dig_H3) >> 11) + 32768)) >> 10) +
                          2097152) * self.dig_H2 + 8192) >> 14))
        h = h - (((((h >> 15) * (h >> 15)) >> 7) * self.dig_H1) >> 4)
        h = 0 if h < 0 else h
        h = 419430400 if h > 419430400 else h
        humidity = h >> 12

        if result:
            result[0] = temp
            result[1] = pressure
            result[2] = humidity
            return result

        return array("i", (temp, pressure, humidity))

    @property
    def values(self):
        """ human readable values """

        t, p, h = self.read_compensated_data()

        p = p // 256
        pi = p // 100
        pd = p - pi * 100

        hi = h // 1024
        hd = h * 100 // 1024 - hi * 100
        return ("{}".format(t / 100), "{}.{:02d}".format(pi, pd),
                "{}.{:02d}".format(hi, hd))

def negtive_converter(raw):
    if raw > 32767:
        raw = (65536 - raw) * (-1)
    return raw

def light_sensor():
    ADC_REF = 0.6
    ADC_RESOLUTION=4096 #12bit
    ain = ADC(0)
    ain.gain(ain.GAIN_1_6) #gain set to 1/6

    raw = negtive_converter(ain.read())
    v = (raw / ADC_RESOLUTION) * ADC_REF * 6
    return v

def air_quality_sensor():
    ain = ADC(0)
    return negtive_converter(ain.read())

def sound_sensor():
    ain = ADC(4)
    return negtive_converter(ain.read())

def battery_voltage():
    R6 = 68
    R8 = 100

    ADC_REF = 0.6
    ADC_RESOLUTION=îÅ\¾  ô ^  S     5      @  PäùçÖ3ñý  ýPäùçÖ    ¼ef, #ÜÕa &      degu  «Í                     ÿ          ÿÿ  ô     2ëž]äÖOw  ô $ ªª  ì    N¼   à  T  XZ }£7äYöì  ì Ëéw  ô $ ªª  ì    7À  ðÆ  T  XZ }£7äYöì  ì Ëéw  ô $     ì    :À  ðÆ  T  XZ }£7äYöì  ì Ëéw  ô $ ªª  ì    #Ä  ØÊ  T  XZ }£7äYöì  ì Ëéw  ô $ ªª  ì    È  ÀÎ  T  XZ }£7äYöì  ì Ëéw  ô $ ªª  ì    õË  ¨Ò  T  XZ }£7äYöì  ì Ëéw  ô $ ªª  ì    ÞÏ   Ö  T  XZ }£7äYöì  ì Ëéw  ô $ ªª  ì    ÇÓ  xÚ  T  XZ }£7äYöì  ì Ëéw  ô $ ªª  ì    °×  `Þ  T  XZ }£7äYöì  ì Ëéw  ô $ ªª  ì    ™Û  Hâ  T  XZ }£7äYöì  ì Ëéwÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ

After replacing corrupted main.py, the Degu unit is still unable to connect to the gateway (A6). Suspect that the network information partition is also corrupted. After using “dd” to clear network information partition and re-register unit on gateway, it started to work again.

We've been running two Degu units on batteries. This issue has happened twice on the same Degu unit so far, but hasn't occurred on the other which was also battery powered, and also ran down the batteries to the point of power failure, but with no corruption.

atKeitaroTakahashi commented 4 years ago

The lower limitation voltage of DCDC converter(※1) for using AA battery is 2.2V, so that if the batteries voltage go below 2.2V, the board won't work normally.

In case of you connect directly to nRF52 without using DCDC converter(※2), nRF52 is possible works properly unless the battery voltage goes below 1.7V.

We are scheduled to implement shutdown sequence by nrf52 POFCON when AA batteries voltage dropped to 2.2V.

※1: Short-circuit: JP1.1 <-> JP1.2; Short-circuit: JP2.1 <-> JP2.2 ※2: Short-circuit: JP1.1 <-> JP2.1; Short-circuit: JP1.2 <-> JP2.2, input voltage is up to 3.6V