|
| 1 | +import math |
| 2 | +import time |
| 3 | +import struct |
| 4 | +from machine import Pin |
| 5 | + |
| 6 | + |
| 7 | +FULL_SCALE_2G = const(0) |
| 8 | +FULL_SCALE_4G = const(2) |
| 9 | +FULL_SCALE_8G = const(3) |
| 10 | + |
| 11 | +ODR_POWER_DOWN = const(0) |
| 12 | +ODR_10_HZ = const(1) |
| 13 | +ODR_50_HZ = const(2) |
| 14 | +ODR_100_HZ = const(3) |
| 15 | +ODR_200_HZ = const(4) |
| 16 | +ODR_400_HZ = const(5) |
| 17 | +ODR_800_HZ = const(6) |
| 18 | + |
| 19 | +ACC_G_DIV = 1000 * 65536 |
| 20 | + |
| 21 | + |
| 22 | +class LIS2HH12: |
| 23 | + |
| 24 | + ACC_I2CADDR = const(30) |
| 25 | + |
| 26 | + PRODUCTID_REG = const(0x0F) |
| 27 | + CTRL1_REG = const(0x20) |
| 28 | + CTRL2_REG = const(0x21) |
| 29 | + CTRL3_REG = const(0x22) |
| 30 | + CTRL4_REG = const(0x23) |
| 31 | + CTRL5_REG = const(0x24) |
| 32 | + ACC_X_L_REG = const(0x28) |
| 33 | + ACC_X_H_REG = const(0x29) |
| 34 | + ACC_Y_L_REG = const(0x2A) |
| 35 | + ACC_Y_H_REG = const(0x2B) |
| 36 | + ACC_Z_L_REG = const(0x2C) |
| 37 | + ACC_Z_H_REG = const(0x2D) |
| 38 | + ACT_THS = const(0x1E) |
| 39 | + ACT_DUR = const(0x1F) |
| 40 | + |
| 41 | + SCALES = {FULL_SCALE_2G: 4000, FULL_SCALE_4G: 8000, FULL_SCALE_8G: 16000} |
| 42 | + ODRS = [0, 10, 50, 100, 200, 400, 800] |
| 43 | + |
| 44 | + def __init__(self, pysense = None, sda = 'P22', scl = 'P21'): |
| 45 | + if pysense is not None: |
| 46 | + self.i2c = pysense.i2c |
| 47 | + else: |
| 48 | + from machine import I2C |
| 49 | + self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) |
| 50 | + |
| 51 | + self.odr = 0 |
| 52 | + self.full_scale = 0 |
| 53 | + self.x = 0 |
| 54 | + self.y = 0 |
| 55 | + self.z = 0 |
| 56 | + self.int_pin = None |
| 57 | + self.act_dur = 0 |
| 58 | + self.debounced = False |
| 59 | + |
| 60 | + whoami = self.i2c.readfrom_mem(ACC_I2CADDR , PRODUCTID_REG, 1) |
| 61 | + if (whoami[0] != 0x41): |
| 62 | + raise ValueError("LIS2HH12 not found") |
| 63 | + |
| 64 | + # enable acceleration readings at 50Hz |
| 65 | + self.set_odr(ODR_50_HZ) |
| 66 | + |
| 67 | + # change the full-scale to 4g |
| 68 | + self.set_full_scale(FULL_SCALE_4G) |
| 69 | + |
| 70 | + # set the interrupt pin as active low and open drain |
| 71 | + self.set_register(CTRL5_REG, 3, 0, 3) |
| 72 | + |
| 73 | + # make a first read |
| 74 | + self.acceleration() |
| 75 | + |
| 76 | + def acceleration(self): |
| 77 | + x = self.i2c.readfrom_mem(ACC_I2CADDR , ACC_X_L_REG, 2) |
| 78 | + self.x = struct.unpack('<h', x) |
| 79 | + y = self.i2c.readfrom_mem(ACC_I2CADDR , ACC_Y_L_REG, 2) |
| 80 | + self.y = struct.unpack('<h', y) |
| 81 | + z = self.i2c.readfrom_mem(ACC_I2CADDR , ACC_Z_L_REG, 2) |
| 82 | + self.z = struct.unpack('<h', z) |
| 83 | + _mult = self.SCALES[self.full_scale] / ACC_G_DIV |
| 84 | + return (self.x[0] * _mult, self.y[0] * _mult, self.z[0] * _mult) |
| 85 | + |
| 86 | + def roll(self): |
| 87 | + x,y,z = self.acceleration() |
| 88 | + rad = math.atan2(-x, z) |
| 89 | + return (180 / math.pi) * rad |
| 90 | + |
| 91 | + def pitch(self): |
| 92 | + x,y,z = self.acceleration() |
| 93 | + rad = -math.atan2(y, (math.sqrt(x*x + z*z))) |
| 94 | + return (180 / math.pi) * rad |
| 95 | + |
| 96 | + def set_register(self, register, value, offset, mask): |
| 97 | + reg = bytearray(self.i2c.readfrom_mem(ACC_I2CADDR, register, 1)) |
| 98 | + reg[0] &= ~(mask << offset) |
| 99 | + reg[0] |= ((value & mask) << offset) |
| 100 | + self.i2c.writeto_mem(ACC_I2CADDR, register, reg) |
| 101 | + |
| 102 | + def set_full_scale(self, scale): |
| 103 | + self.set_register(CTRL4_REG, scale, 4, 3) |
| 104 | + self.full_scale = scale |
| 105 | + |
| 106 | + def set_odr(self, odr): |
| 107 | + self.set_register(CTRL1_REG, odr, 4, 7) |
| 108 | + self.odr = odr |
| 109 | + |
| 110 | + def set_high_pass(self, hp): |
| 111 | + self.set_register(CTRL2_REG, 1 if hp else 0, 2, 1) |
| 112 | + |
| 113 | + def enable_activity_interrupt(self, threshold, duration, handler=None): |
| 114 | + # Threshold is in mg, duration is ms |
| 115 | + self.act_dur = duration |
| 116 | + |
| 117 | + if threshold > self.SCALES[self.full_scale]: |
| 118 | + error = "threshold %d exceeds full scale %d" % (thresold, self.SCALES[self.full_scale]) |
| 119 | + print(error) |
| 120 | + raise ValueError(error) |
| 121 | + |
| 122 | + if threshold < self.SCALES[self.full_scale] / 128: |
| 123 | + error = "threshold %d below resolution %d" % (thresold, self.SCALES[self.full_scale]/128) |
| 124 | + print(error) |
| 125 | + raise ValueError(error) |
| 126 | + |
| 127 | + if duration > 255 * 1000 * 8 / self.ODRS[self.odr]: |
| 128 | + error = "duration %d exceeds max possible value %d" % (duration, 255 * 1000 * 8 / self.ODRS[self.odr]) |
| 129 | + print(error) |
| 130 | + raise ValueError(error) |
| 131 | + |
| 132 | + if duration < 1000 * 8 / self.ODRS[self.odr]: |
| 133 | + error = "duration %d below resolution %d" % (duration, 1000 * 8 / self.ODRS[self.odr]) |
| 134 | + print(error) |
| 135 | + raise ValueError(error) |
| 136 | + |
| 137 | + _ths = int(127 * threshold / self.SCALES[self.full_scale]) & 0x7F |
| 138 | + _dur = int((duration * self.ODRS[self.odr]) / 1000 / 8) |
| 139 | + |
| 140 | + self.i2c.writeto_mem(ACC_I2CADDR, ACT_THS, _ths) |
| 141 | + self.i2c.writeto_mem(ACC_I2CADDR, ACT_DUR, _dur) |
| 142 | + |
| 143 | + # enable the activity/inactivity interrupt |
| 144 | + self.set_register(CTRL3_REG, 1, 5, 1) |
| 145 | + |
| 146 | + self._user_handler = handler |
| 147 | + self.int_pin = Pin('P13', mode=Pin.IN) |
| 148 | + self.int_pin.callback(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=self._int_handler) |
| 149 | + |
| 150 | + # return actual used thresold and duration |
| 151 | + return (_ths * self.SCALES[self.full_scale] / 128, _dur * 8 * 1000 / self.ODRS[self.odr]) |
| 152 | + |
| 153 | + def activity(self): |
| 154 | + if not self.debounced: |
| 155 | + time.sleep_ms(self.act_dur) |
| 156 | + self.debounced = True |
| 157 | + if self.int_pin(): |
| 158 | + return True |
| 159 | + return False |
| 160 | + |
| 161 | + def _int_handler(self, pin_o): |
| 162 | + if self._user_handler is not None: |
| 163 | + self._user_handler(pin_o) |
| 164 | + else: |
| 165 | + if pin_o(): |
| 166 | + print('Activity interrupt') |
| 167 | + else: |
| 168 | + print('Inactivity interrupt') |
0 commit comments