-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmovement.py
164 lines (138 loc) · 5.3 KB
/
movement.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#!/usr/bin/env python3
"""
"""
from math import log2, fabs
from time import sleep
import pigpio
class Adxl345():
ADDR_DEV_ID = 0x00
ADDR_RATE_BW = 0x2C
ADDR_DATA_FORMAT = 0x31
ADDR_DATA_X_0 = 0x32 # from here on 6 bytes (2 per coordinate) for X,Y,Z
ADDR_OFFSET_X = 0x1E
ADDR_OFFSET_Y = 0x1F
ADDR_OFFSET_Z = 0x20
ADDR_POWER_CTL = 0x2D
def __init__(
self, sensitivity_range=4, data_rate_level=0):
# high range values means lower resolution for small movements!
# low range values means higher resolution for small movements!
self.set_sensitivity_range(sensitivity_range)
self.set_data_rate_level(data_rate_level)
def decode(self, lsb, msb):
accl = (msb << 8) | lsb
adjust = 2 * (2**(self.sensitivity_range + 1)) / (2**13) # given g range with 13 bit precision
correct_accl = (accl - (1 << 16)) * adjust if accl & (1 << 15) else accl * adjust
return correct_accl
def set_sensitivity_range(self, sensitivity_range):
errmsg = f"Sensitivity Range '{sensitivity_range}' needs to integer of 2,4,8,16"
if not isinstance(sensitivity_range, int):
raise TypeError(errmsg)
if sensitivity_range not in (2,4,8,16):
raise ValueError(errmsg)
self.sensitivity_range = int(log2(sensitivity_range))-1
data = self.from_address(Adxl345.ADDR_DATA_FORMAT, 1)[0] & ~0x0F | self.sensitivity_range | 0x08
self.to_address(Adxl345.ADDR_DATA_FORMAT, data)
def set_data_rate_level(self, data_rate_level):
errmsg = f"DataRateLevel '{data_rate_level}' needs to be integer within 0 < DRL <= 16!"
if not isinstance(data_rate_level, int):
raise TypeError(errmsg)
if data_rate_level < 0 or data_rate_level > 16:
raise ValueError(errmsg)
self.data_rate_code = 0b1111-(data_rate_level)
self.data_rate = int(3200/(2**(data_rate_level)))
self.to_address(Adxl345.ADDR_RATE_BW, self.data_rate_code)
def set_offset(self, x, y, z):
offset = {
Adxl345.ADDR_OFFSET_X: x,
Adxl345.ADDR_OFFSET_Y: y,
Adxl345.ADDR_OFFSET_Z: z,
}
for addr in offset.keys():
self.to_address(
addr,
int(offset[addr] / Adxl345.FACTOR_HIGH_RES / 4 ) & 0xFF
)
def set_on(self):
self.to_address(Adxl345.ADDR_POWER_CTL, 0x08)
def set_off(self):
self.to_address(Adxl345.ADDR_POWER_CTL, 0x00)
def calibrate(self, margin=0.1):
self.set_offset(0,0,0)
accel = self.get_acceleration()
x,y,z = accel[0], accel[1], accel[2]
if not all(
(0-margin < fabs(v) < 0+margin) or (1-margin < fabs(v) < 1+margin)
for v in (x, y, z)
):
raise ValueError(
f"WARNING! Please place sensor on appropriate surface; "
f"values should be around 0 or 1 and not {x,y,z}")
if not round(x) ^ round(y) ^ round(z):
raise ValueError(
"WARNING! Please place sensor on appropriate surface; "
"values should be around 0 or 1, with only one value 1 "
f"and not {x,y,z}"
)
calibration = [
round(v) - v
for v in (x,y,z)
]
self.set_offset(*calibration)
def get_acceleration(self,axis=0b111):
byte_ctr = 6 #2 bytes each for x,y,z values
data = self.from_address(Adxl345.ADDR_DATA_X_0, byte_ctr)
return [
self.decode(data[idx], data[idx+1])
for idx in range(0,byte_ctr, 2)
if axis & (1 << (idx//2))
]
class Adxl345Spi(Adxl345):
BITMASK_READ = 0x80
BITMASK_MULTI = 0x40
ADDR_SELECT_MASK = 0x3f
def __init__(self, channel=0, mode=0b11, baudrate=2e6):
self.channel = int(channel)
self.mode = int(mode)
self.baudrate = int(baudrate)
self.pi = pigpio.pi()
self.spi = self.pi.spi_open(self.channel, self.baudrate, self.mode)
super().__init__()
def from_address(self, addr, byte_count):
bit_msg = [
addr | Adxl345Spi.BITMASK_READ | (Adxl345Spi.BITMASK_MULTI * (byte_count > 1))
]
# add some random bytes, read bitmask is set -> no writing!
bit_msg.extend([
0xFF
for _ in range(byte_count)
])
count, data = self.pi.spi_xfer(self.spi, bit_msg)
if count != (byte_count+1) or len(data) != count:
raise ValueError(
f"Returned SPI bytes from {addr} seems not to be correct!\n"
f"Found {[x for x in data]}"
)
return data[1:]
def to_address(self, addr, values):
data_values = values if isinstance(values, list) else [values]
bit_msg = [
addr | (Adxl345Spi.BITMASK_MULTI * (len(data_values) > 1))
]
bit_msg.extend(data_values)
self.pi.spi_xfer(self.spi, bit_msg)
def stop(self):
self.pi.spi_close(self.spi)
self.pi.stop()
class Adxl345I2C(Adxl345):
pass
def main():
test = Adxl345Spi()
test.set_on()
for _ in range(10):
print(", ".join(str(val) for val in test.get_acceleration()))
sleep(1)
test.stop()
if __name__ == "__main__":
# execute only if run as a script
main()