Skip to content

Commit 689e715

Browse files
committed
feature/unit/ameter: 16-bit ADS1115 analog-to-digital converter
1 parent d338244 commit 689e715

File tree

1 file changed

+219
-0
lines changed

1 file changed

+219
-0
lines changed

m5stack/libs/unit/ameter.py

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
from machine import I2C
2+
from .pahub import PAHUBUnit
3+
from .unit_helper import UnitError
4+
import struct
5+
import sys
6+
7+
if sys.platform != "esp32":
8+
from typing import Union
9+
10+
import time
11+
import struct
12+
13+
ADS1115_ADDR = 0x48
14+
EEPROM_ADDR = 0x51
15+
16+
RA_CONVERSION = 0x00
17+
RA_CONFIG = 0x01
18+
19+
MODE_CONTINUOUS = 0x00
20+
MODE_SINGLESHOT = 0x01
21+
22+
PGA_6144 = 0x00
23+
PGA_4096 = 0x01
24+
PGA_2048 = 0x02
25+
PGA_1024 = 0x03
26+
PGA_512 = 0x04
27+
PGA_256 = 0x05
28+
29+
RATE_8 = 0x00
30+
RATE_16 = 0x01
31+
RATE_32 = 0x02
32+
RATE_64 = 0x03
33+
RATE_128 = 0x04
34+
RATE_250 = 0x05
35+
RATE_475 = 0x06
36+
RATE_860 = 0x07
37+
38+
MV_6144 = 0.187500
39+
MV_4096 = 0.125000
40+
MV_2048 = 0.062500
41+
MV_1024 = 0.031250
42+
MV_512 = 0.015625
43+
MV_256 = 0.007813
44+
45+
MEASURING_DIR = -1
46+
47+
VOLTAGE_DIVIDER_COEFFICIENT = 0.05
48+
49+
PAG_6144_CAL_ADDR = 208
50+
PAG_4096_CAL_ADDR = 216
51+
PAG_2048_CAL_ADDR = 224
52+
PAG_1024_CAL_ADDR = 232
53+
PAG_512_CAL_ADDR = 240
54+
PAG_256_CAL_ADDR = 248
55+
56+
Resolution_list = {0: MV_6144 / VOLTAGE_DIVIDER_COEFFICIENT,
57+
1: MV_4096 / VOLTAGE_DIVIDER_COEFFICIENT,
58+
2: MV_2048 / VOLTAGE_DIVIDER_COEFFICIENT,
59+
3: MV_1024 / VOLTAGE_DIVIDER_COEFFICIENT,
60+
4: MV_512 / VOLTAGE_DIVIDER_COEFFICIENT,
61+
5: MV_256 / VOLTAGE_DIVIDER_COEFFICIENT}
62+
63+
PGA_EEPROM_Addr_list = {0: PAG_6144_CAL_ADDR,
64+
1: PAG_4096_CAL_ADDR,
65+
2: PAG_2048_CAL_ADDR,
66+
3: PAG_1024_CAL_ADDR,
67+
4: PAG_512_CAL_ADDR,
68+
5: PAG_256_CAL_ADDR}
69+
70+
Rate_list = {0: 1000 / 8,
71+
1: 1000 / 16,
72+
2: 1000 / 32,
73+
3: 1000 / 64,
74+
4: 1000 / 128,
75+
5: 1000 / 250,
76+
6: 1000 / 475,
77+
7: 1000 / 860}
78+
79+
class AMeterUnit:
80+
81+
def __init__(self, i2c: Union[I2C, PAHUBUnit], ads_addr=ADS1115_ADDR):
82+
self.ads_i2c = i2c
83+
self.ads_i2c_addr = ads_addr
84+
if not (self.ads_i2c_addr in self.ads_i2c.scan()):
85+
raise UnitError("Ameter unit maybe not connect")
86+
self.eeprom_i2c = i2c
87+
self.eeprom_i2c_addr = EEPROM_ADDR
88+
self.set_gain(PGA_256)
89+
self._gain = PGA_256
90+
self._rate = RATE_128
91+
self._mode = MODE_SINGLESHOT
92+
self._calibration_factor = 1
93+
self._resolution = Resolution_list[self._gain]
94+
self._cover_time = Rate_list[self._rate]
95+
self.calibration = True
96+
97+
def set_gain(self, gain):
98+
buf = self.ads_i2c.readfrom_mem(self.ads_i2c_addr, RA_CONFIG, 2)
99+
config = struct.unpack('>h', buf)[0]
100+
config &= ~(0b0111 << 9)
101+
config |= gain << 9
102+
buf = struct.pack('>h', config)
103+
self.ads_i2c.writeto_mem(self.ads_i2c_addr, RA_CONFIG, buf)
104+
self._gain = gain
105+
self._resolution = Resolution_list[self._gain]
106+
expect, real = self.read_calibration(self._gain)
107+
self._calibration_factor = expect / real
108+
109+
def get_gain(self):
110+
buf = self.ads_i2c.readfrom_mem(self.ads_i2c_addr, RA_CONFIG, 2)
111+
config = struct.unpack('>h', buf)[0]
112+
return ((config & (0b0111 << 9)) >> 9)
113+
114+
def set_data_rate(self, rate):
115+
buf = self.ads_i2c.readfrom_mem(self.ads_i2c_addr, RA_CONFIG, 2)
116+
config = struct.unpack('>h', buf)[0]
117+
config &= ~(0b0111 << 5)
118+
config |= rate << 5
119+
buf = struct.pack('>h', config)
120+
self.ads_i2c.writeto_mem(self.ads_i2c_addr, RA_CONFIG, buf)
121+
self._rate = rate
122+
self._cover_time = Rate_list[self._rate]
123+
124+
def get_data_rate(self):
125+
buf = self.ads_i2c.readfrom_mem(self.ads_i2c_addr, RA_CONFIG, 2)
126+
config = struct.unpack('>h', buf)[0]
127+
return ((config & (0b0111 << 5)) >> 5)
128+
129+
def set_operation_mode(self, mode):
130+
buf = self.ads_i2c.readfrom_mem(self.ads_i2c_addr, RA_CONFIG, 2)
131+
config = struct.unpack('>h', buf)[0]
132+
config &= ~(0b0001 << 8)
133+
config |= mode << 8
134+
buf = struct.pack('>h', config)
135+
self.ads_i2c.writeto_mem(self.ads_i2c_addr, RA_CONFIG, buf)
136+
self._mode = mode
137+
138+
def get_operation_mode(self):
139+
buf = self.ads_i2c.readfrom_mem(self.ads_i2c_addr, RA_CONFIG, 2)
140+
config = struct.unpack('>h', buf)[0]
141+
return ((config & (0b0001 << 8)) >> 8)
142+
143+
def get_current(self):
144+
if self.calibration:
145+
return self._resolution * self._calibration_factor * self.conversion() * MEASURING_DIR
146+
else:
147+
return self._resolution * self.conversion() * MEASURING_DIR
148+
149+
def get_adc_raw(self):
150+
return self.conversion()
151+
152+
def conversion(self, timeout=125):
153+
if self._mode == MODE_SINGLESHOT:
154+
self.single_conversion()
155+
time.sleep_ms(int(self._cover_time))
156+
time_ms = time.ticks_ms() + timeout
157+
while (time.ticks_ms() < time_ms and self.is_conversion()):
158+
pass
159+
160+
return self.adc_raw()
161+
162+
def single_conversion(self):
163+
buf = self.ads_i2c.readfrom_mem(self.ads_i2c_addr, RA_CONFIG, 2)
164+
config = struct.unpack('>h', buf)[0]
165+
config &= ~(0b0001 << 15)
166+
config |= 0x01 << 15
167+
buf = struct.pack('>h', config)
168+
self.ads_i2c.writeto_mem(self.ads_i2c_addr, RA_CONFIG, buf)
169+
170+
def is_conversion(self):
171+
buf = self.ads_i2c.readfrom_mem(self.ads_i2c_addr, RA_CONFIG, 2)
172+
result = struct.unpack('>h', buf)[0]
173+
if result & (1 << 15):
174+
return False
175+
else:
176+
return True
177+
178+
def adc_raw(self):
179+
buf = self.ads_i2c.readfrom_mem(self.ads_i2c_addr, RA_CONVERSION, 2)
180+
return struct.unpack('>h', buf)[0]
181+
182+
def eeprom_read(self, reg, num):
183+
return self.eeprom_i2c.readfrom_mem(self.eeprom_i2c_addr, reg, num)
184+
185+
def eeprom_write(self, reg, data):
186+
buf = bytearray(data)
187+
self.eeprom_i2c.writeto_mem(self.eeprom_i2c_addr, reg, buf)
188+
189+
def save_calibration(self, gain, expect, real):
190+
if expect == 0 and real == 0:
191+
return
192+
buf = [0] * 8
193+
buf[0] = gain
194+
buf[1] = expect >> 8
195+
buf[2] = expect & 0xff
196+
buf[3] = real >> 8
197+
buf[4] = real & 0xff
198+
199+
for i in range(0, 5):
200+
buf[5] ^= buf[i]
201+
202+
addr = PGA_EEPROM_Addr_list[gain]
203+
self.eeprom_write(addr, buf)
204+
205+
def read_calibration(self, gain):
206+
expect = 1
207+
real = 1
208+
addr = PGA_EEPROM_Addr_list[gain]
209+
read_data = self.eeprom_read(addr, 8)
210+
crc = 0
211+
for i in range(0, 5):
212+
crc ^= read_data[i]
213+
214+
if crc != read_data[5]:
215+
return expect, real
216+
217+
expect = read_data[1] << 8 | read_data[2]
218+
real = read_data[3] << 8 | read_data[4]
219+
return expect, real

0 commit comments

Comments
 (0)