Skip to content

Commit f00e114

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

File tree

1 file changed

+218
-0
lines changed

1 file changed

+218
-0
lines changed

m5stack/libs/unit/vmeter.py

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
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 = 0x49
14+
EEPROM_ADDR = 0x53
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.015918958
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 VMeterUnit:
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("Vmeter unit maybe not connect")
86+
self.eeprom_i2c = i2c
87+
self.eeprom_i2c_addr = EEPROM_ADDR
88+
self._gain = PGA_2048
89+
self._rate = RATE_128
90+
self._mode = MODE_SINGLESHOT
91+
self._calibration_factor = 1
92+
self._resolution = Resolution_list[self._gain]
93+
self._cover_time = Rate_list[self._rate]
94+
self.calibration = True
95+
96+
def set_gain(self, gain):
97+
buf = self.ads_i2c.readfrom_mem(self.ads_i2c_addr, RA_CONFIG, 2)
98+
config = struct.unpack('>h', buf)[0]
99+
config &= ~(0b0111 << 9)
100+
config |= gain << 9
101+
buf = struct.pack('>h', config)
102+
self.ads_i2c.writeto_mem(self.ads_i2c_addr, RA_CONFIG, buf)
103+
self._gain = gain
104+
self._resolution = Resolution_list[self._gain]
105+
expect, real = self.read_calibration(self._gain)
106+
self._calibration_factor = expect / real
107+
108+
def get_gain(self):
109+
buf = self.ads_i2c.readfrom_mem(self.ads_i2c_addr, RA_CONFIG, 2)
110+
config = struct.unpack('>h', buf)[0]
111+
return ((config & (0b0111 << 9)) >> 9)
112+
113+
def set_data_rate(self, rate):
114+
buf = self.ads_i2c.readfrom_mem(self.ads_i2c_addr, RA_CONFIG, 2)
115+
config = struct.unpack('>h', buf)[0]
116+
config &= ~(0b0111 << 5)
117+
config |= rate << 5
118+
buf = struct.pack('>h', config)
119+
self.ads_i2c.writeto_mem(self.ads_i2c_addr, RA_CONFIG, buf)
120+
self._rate = rate
121+
self._cover_time = Rate_list[self._rate]
122+
123+
def get_data_rate(self):
124+
buf = self.ads_i2c.readfrom_mem(self.ads_i2c_addr, RA_CONFIG, 2)
125+
config = struct.unpack('>h', buf)[0]
126+
return ((config & (0b0111 << 5)) >> 5)
127+
128+
def set_operation_mode(self, mode):
129+
buf = self.ads_i2c.readfrom_mem(self.ads_i2c_addr, RA_CONFIG, 2)
130+
config = struct.unpack('>h', buf)[0]
131+
config &= ~(0b0001 << 8)
132+
config |= mode << 8
133+
buf = struct.pack('>h', config)
134+
self.ads_i2c.writeto_mem(self.ads_i2c_addr, RA_CONFIG, buf)
135+
self._mode = mode
136+
137+
def get_operation_mode(self):
138+
buf = self.ads_i2c.readfrom_mem(self.ads_i2c_addr, RA_CONFIG, 2)
139+
config = struct.unpack('>h', buf)[0]
140+
return ((config & (0b0001 << 8)) >> 8)
141+
142+
def get_voltage(self):
143+
if self.calibration:
144+
return self._resolution * self._calibration_factor * self.conversion() * MEASURING_DIR
145+
else:
146+
return self._resolution * self.conversion() * MEASURING_DIR
147+
148+
def get_adc_raw(self):
149+
return self.conversion()
150+
151+
def conversion(self, timeout=125):
152+
if self._mode == MODE_SINGLESHOT:
153+
self.single_conversion()
154+
time.sleep_ms(int(self._cover_time))
155+
time_ms = time.ticks_ms() + timeout
156+
while (time.ticks_ms() < time_ms and self.is_conversion()):
157+
pass
158+
159+
return self.adc_raw()
160+
161+
def single_conversion(self):
162+
buf = self.ads_i2c.readfrom_mem(self.ads_i2c_addr, RA_CONFIG, 2)
163+
config = struct.unpack('>h', buf)[0]
164+
config &= ~(0b0001 << 15)
165+
config |= 0x01 << 15
166+
buf = struct.pack('>h', config)
167+
self.ads_i2c.writeto_mem(self.ads_i2c_addr, RA_CONFIG, buf)
168+
169+
def is_conversion(self):
170+
buf = self.ads_i2c.readfrom_mem(self.ads_i2c_addr, RA_CONFIG, 2)
171+
result = struct.unpack('>h', buf)[0]
172+
if result & (1 << 15):
173+
return False
174+
else:
175+
return True
176+
177+
def adc_raw(self):
178+
buf = self.ads_i2c.readfrom_mem(self.ads_i2c_addr, RA_CONVERSION, 2)
179+
return struct.unpack('>h', buf)[0]
180+
181+
def eeprom_read(self, reg, num):
182+
return self.eeprom_i2c.readfrom_mem(self.eeprom_i2c_addr, reg, num)
183+
184+
def eeprom_write(self, reg, data):
185+
buf = bytearray(data)
186+
self.eeprom_i2c.writeto_mem(self.eeprom_i2c_addr, reg, buf)
187+
188+
def save_calibration(self, gain, expect, real):
189+
if expect == 0 and real == 0:
190+
return
191+
buf = [0] * 8
192+
buf[0] = gain
193+
buf[1] = expect >> 8
194+
buf[2] = expect & 0xff
195+
buf[3] = real >> 8
196+
buf[4] = real & 0xff
197+
198+
for i in range(0, 5):
199+
buf[5] ^= buf[i]
200+
201+
addr = PGA_EEPROM_Addr_list[gain]
202+
self.eeprom_write(addr, buf)
203+
204+
def read_calibration(self, gain):
205+
expect = 1
206+
real = 1
207+
addr = PGA_EEPROM_Addr_list[gain]
208+
read_data = self.eeprom_read(addr, 8)
209+
crc = 0
210+
for i in range(0, 5):
211+
crc ^= read_data[i]
212+
213+
if crc != read_data[5]:
214+
return expect, real
215+
216+
expect = read_data[1] << 8 | read_data[2]
217+
real = read_data[3] << 8 | read_data[4]
218+
return expect, real

0 commit comments

Comments
 (0)