-
Notifications
You must be signed in to change notification settings - Fork 9
/
nau7802py.py
403 lines (335 loc) · 18.8 KB
/
nau7802py.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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
import smbus
import time
# Register Map
Scale_Registers = {'NAU7802_PU_CTRL': 0x00,
'NAU7802_CTRL1': 1,
'NAU7802_CTRL2': 2,
'NAU7802_OCAL1_B2': 3,
'NAU7802_OCAL1_B1': 4,
'NAU7802_OCAL1_B0': 5,
'NAU7802_GCAL1_B3': 6,
'NAU7802_GCAL1_B2': 7,
'NAU7802_GCAL1_B1': 8,
'NAU7802_GCAL1_B0': 9,
'NAU7802_OCAL2_B2': 10,
'NAU7802_OCAL2_B1': 11,
'NAU7802_OCAL2_B0': 12,
'NAU7802_GCAL2_B3': 13,
'NAU7802_GCAL2_B2': 14,
'NAU7802_GCAL2_B1': 15,
'NAU7802_GCAL2_B0': 16,
'NAU7802_I2C_CONTROL': 17,
'NAU7802_ADCO_B2': 18,
'NAU7802_ADCO_B1': 19,
'NAU7802_ADCO_B0': 20,
'NAU7802_ADC': 0x15, # Shared ADC and OTP 32:24
'NAU7802_OTP_B1': 22, # OTP 23:16 or 7:0?
'NAU7802_OTP_B0': 23, # OTP 15:8
'NAU7802_PGA': 0x1B,
'NAU7802_PGA_PWR': 0x1C,
'NAU7802_DEVICE_REV': 0x1F}
# Bits within the PU_CRTL register
PU_CTRL_Bits = {'NAU7802_PU_CTRL_RR': 0,
'NAU7802_PU_CTRL_PUD': 1,
'NAU7802_PU_CTRL_PUA': 2,
'NAU7802_PU_CTRL_PUR': 3,
'NAU7802_PU_CTRL_CS': 4,
'NAU7802_PU_CTRL_CR': 5,
'NAU7802_PU_CTRL_OSCS': 6,
'NAU7802_PU_CTRL_AVDDS': 7}
# Bits within the CTRL1 register
CTRL1_Bits = {'NAU7802_CTRL1_GAIN': 2,
'NAU7802_CTRL1_VLDO': 5,
'NAU7802_CTRL1_DRDY_SEL': 6,
'NAU7802_CTRL1_CRP': 7}
# Bits within the CTRL2 register
CTRL2_Bits = {'NAU7802_CTRL2_CALMOD': 0,
'NAU7802_CTRL2_CALS': 2,
'NAU7802_CTRL2_CAL_ERROR': 3,
'NAU7802_CTRL2_CRS': 4,
'NAU7802_CTRL2_CHS': 7}
# Bits within the PGA register
PGA_Bits = {'NAU7802_PGA_CHP_DIS': 0,
'NAU7802_PGA_INV': 3,
'NAU7802_PGA_BYPASS_EN': 4,
'NAU7802_PGA_OUT_EN': 5,
'NAU7802_PGA_LDOMODE': 6,
'NAU7802_PGA_RD_OTP_SEL': 7}
# Bits within the PGA PWR register
PGA_PWR_Bits = {'NAU7802_PGA_PWR_PGA_CURR': 0,
'NAU7802_PGA_PWR_ADC_CURR': 2,
'NAU7802_PGA_PWR_MSTR_BIAS_CURR': 4,
'NAU7802_PGA_PWR_PGA_CAP_EN': 7}
# Allowed Low drop out regulator voltages
NAU7802_LDO_Values = {'NAU7802_LDO_2V4': 0b111,
'NAU7802_LDO_2V7': 0b110,
'NAU7802_LDO_3V0': 0b101,
'NAU7802_LDO_3V3': 0b100,
'NAU7802_LDO_3V6': 0b011,
'NAU7802_LDO_3V9': 0b010,
'NAU7802_LDO_4V2': 0b001,
'NAU7802_LDO_4V5': 0b000}
# Allowed gains
NAU7802_Gain_Values = {'NAU7802_GAIN_128': 0b111,
'NAU7802_GAIN_64': 0b110,
'NAU7802_GAIN_32': 0b101,
'NAU7802_GAIN_16': 0b100,
'NAU7802_GAIN_8': 0b011,
'NAU7802_GAIN_4': 0b010,
'NAU7802_GAIN_2': 0b001,
'NAU7802_GAIN_1': 0b000}
# Allowed samples per second
NAU7802_SPS_Values = {'NAU7802_SPS_320': 0b111,
'NAU7802_SPS_80': 0b011,
'NAU7802_SPS_40': 0b010,
'NAU7802_SPS_20': 0b001,
'NAU7802_SPS_10': 0b000}
# Select between channel values
NAU7802_Channels = {'NAU7802_CHANNEL_1': 0,
'NAU7802_CHANNEL_2': 1}
# Calibration state
NAU7802_Cal_Status = {'NAU7802_CAL_SUCCESS': 0,
'NAU7802_CAL_IN_PROGRESS': 1,
'NAU7802_CAL_FAILURE': 2}
class NAU7802():
# Default constructor
def __init__(self, i2cPort = 1, deviceAddress = 0x2A, zeroOffset = False,
calibrationFactor = False):
self.bus = smbus.SMBus(i2cPort) # This stores the user's requested i2c port
self.deviceAddress = deviceAddress # Default unshifted 7-bit address of the NAU7802
# y = mx + b
self.zeroOffset = zeroOffset; # This is b
self.calibrationFactor = calibrationFactor # This is m. User provides this number so that we can output y when requested
# Returns true if Cycle Ready bit is set (conversion is complete)
def available(self): # Returns true if Cycle Ready bit is set (conversion is complete)
return self.getBit(PU_CTRL_Bits['NAU7802_PU_CTRL_CR'], Scale_Registers['NAU7802_PU_CTRL'])
# Check calibration status.
def calAFEStatus(self): # Check calibration status.
if self.getBit(CTRL2_Bits['NAU7802_CTRL2_CALS'], Scale_Registers['NAU7802_CTRL2']):
return NAU7802_Cal_Status['NAU7802_CAL_IN_PROGRESS']
if self.getBit(CTRL2_Bits['NAU7802_CTRL2_CAL_ERROR'], Scale_Registers['NAU7802_CTRL2']):
return NAU7802_Cal_Status['NAU7802_CAL_FAILURE']
# Calibration passed
return NAU7802_Cal_Status['NAU7802_CAL_SUCCESS']
# Call when scale is setup, level, at running temperature, with nothing on it
def calculateZeroOffset(self, averageAmount): # Also called taring. Call this with nothing on the scale
self.setZeroOffset(self.getAverage(averageAmount))
# Calibrate analog front end of system. Returns true if CAL_ERR bit is 0 (no error)
# Takes approximately 344ms to calibrate; wait up to 1000ms.
# It is recommended that the AFE be re-calibrated any time the gain, SPS, or channel number is changed.
def calibrateAFE(self): # Synchronous calibration of the analog front end of the NAU7802. Returns true if CAL_ERR bit is 0 (no error)
self.beginCalibrateAFE()
return self.waitForCalibrateAFE(1)
# Sets up the NAU7802 for basic function
# If initialize is true (or not specified), default init and calibration is performed
# If initialize is false, then it's up to the caller to initalize and calibrate
# Returns true upon completion
def begin(self, initialized = True): # Check communication and initialize sensor
# Check if the device ack's over I2C
if self.isConnected() == False:
# There are rare times when the sensor is occupied and doesn't ack. A 2nd try resolves this.
if self.isConnected() == False:
return False
result = True # Accumulate a result as we do the setup
if initialized:
result &= self.reset() # Reset all registers
result &= self.powerUp() # Power on analog and digital sections of the scale
result &= self.setLDO(NAU7802_LDO_Values['NAU7802_LDO_3V3']) # Set LDO to 3.3V
result &= self.setGain(NAU7802_Gain_Values['NAU7802_GAIN_128']) # Set gain to 128
result &= self.setSampleRate(NAU7802_SPS_Values['NAU7802_SPS_80']) # Set samples per second to 10
result &= self.setRegister(Scale_Registers['NAU7802_ADC'], 0x30) # Turn off CLK_CHP. From 9.1 power on sequencing.
result &= self.setBit(PGA_PWR_Bits['NAU7802_PGA_PWR_PGA_CAP_EN'], Scale_Registers['NAU7802_PGA_PWR']) # Enable 330pF decoupling cap on chan 2. From 9.14 application circuit note.
result &= self.calibrateAFE() # Re-cal analog front end when we change gain, sample rate, or channel
return result
# Begin asynchronous calibration of the analog front end.
# Poll for completion with calAFEStatus() or wait with waitForCalibrateAFE()
def beginCalibrateAFE(self): # Begin asynchronous calibration of the analog front end of the NAU7802. Poll for completion with calAFEStatus() or wait with waitForCalibrateAFE().
self.setBit(CTRL2_Bits['NAU7802_CTRL2_CALS'], Scale_Registers['NAU7802_CTRL2']);
# Call after zeroing. Provide the float weight sitting on scale. Units do not matter.
def calculateCalibrationFactor(self, weightOnScale, averageAmount): # Call this with the value of the thing on the scale. Sets the calibration factor based on the weight on scale and zero offset.
onScale = self.getAverage(averageAmount)
newCalFactor = (onScale - self.zeroOffset) / weightOnScale
self.setCalibrationFactor(newCalFactor)
# Mask & clear a given bit within a register
def clearBit(self, bitNumber, registerAddress): # Mask & clear a given bit within a register
value = self.getRegister(registerAddress)
value &= ~(1 << bitNumber) # Set this bit
return self.setRegister(registerAddress, value)
# Return the average of a given number of readings
# Gives up after 1000ms so don't call this function to average 8 samples setup at 1Hz output (requires 8s)
def getAverage(self, averageAmount): # Return the average of a given number of readings
total = 0
samplesAcquired = 0
startTime = time.time()
while True:
try:
total += self.getReading()
except:
return False
if samplesAcquired == averageAmount:
break # All done
if time.time() - startTime > 1:
return False # Timeout - Bail with error
samplesAcquired += 1
time.sleep(0.001)
total /= averageAmount;
return total
# Return a given bit within a register
def getBit(self, bitNumber, registerAddress): # Return a given bit within a register
value = self.getRegister(registerAddress)
# value &= (1 << bitNumber) # Clear all but this bit
value = value >> bitNumber & 1
return bool(value)
def getCalibrationFactor(self): # Ask library for this value. Useful for storing value into NVM.
return self.calibrationFactor
# Returns 24-bit reading
# Assumes CR Cycle Ready bit (ADC conversion complete) has been checked to be 1
def getReading(self): # Returns 24-bit reading. Assumes CR Cycle Ready bit (ADC conversion complete) has been checked by .available()
while not self.available():
pass
block = self.bus.read_i2c_block_data(self.deviceAddress, Scale_Registers['NAU7802_ADCO_B2'], 3)
valueRaw = block[0] << 16 # MSB
valueRaw |= block[1] << 8 #MidSB
valueRaw |= block[2] # LSB
# the raw value coming from the ADC is a 24-bit number, so the sign bit now
# resides on bit 23 (0 is LSB) of the container. By shifting the
# value to the left, I move the sign bit to the MSB of the container.
# By casting to a signed container I now have properly recovered
# the sign of the original value
valueShifted = valueRaw << 8
# shift the number back right to recover its intended magnitude
value = valueShifted >> 8
return value
# Get contents of a register
def getRegister(self, registerAddress): # Get contents of a register
try:
return self.bus.read_i2c_block_data(self.deviceAddress, registerAddress, 1)[0]
except:
return False # Error
# Get the revision code of this IC
def getRevisionCode(self): # Get the revision code of this IC. Always 0x0F.
revisionCode = self.getRegister(Scale_Registers['NAU7802_DEVICE_REV']);
return revisionCode & 0x0F
# Returns the y of y = mx + b using the current weight on scale, the cal factor, and the offset.
def getWeight(self, allowNegativeWeights = False, samplesToTake = 10): # Once you've set zero offset and cal factor, you can ask the library to do the calculations for you.
onScale = self.getAverage(samplesToTake)
# Prevent the current reading from being less than zero offset
# This happens when the scale is zero'd, unloaded, and the load cell reports a value slightly less than zero value
# causing the weight to be negative or jump to millions of pounds
if not allowNegativeWeights:
if onScale < self.zeroOffset:
onScale = self.zeroOffset # Force reading to zero
try:
weight = (onScale - self.zeroOffset) / self.calibrationFactor
return weight
except:
print('Needs calibrating')
return False
def getZeroOffset(self): # Ask library for this value. Useful for storing value into NVM.
return self.zeroOffset
# Returns true if device is present
# Tests for device ack to I2C address
def isConnected(self): # Returns true if device acks at the I2C address
try:
self.bus.read_byte(self.deviceAddress)
return True
except:
return False
# Puts scale into low-power mode
def powerDown(self): # Puts scale into low-power 200nA mode
self.clearBit(PU_CTRL_Bits['NAU7802_PU_CTRL_PUD'], Scale_Registers['NAU7802_PU_CTRL'])
return self.clearBit(PU_CTRL_Bits['NAU7802_PU_CTRL_PUA'], Scale_Registers['NAU7802_PU_CTRL'])
# Power up digital and analog sections of scale
def powerUp(self): # Power up digital and analog sections of scale, ~2mA
self.setBit(PU_CTRL_Bits['NAU7802_PU_CTRL_PUD'], Scale_Registers['NAU7802_PU_CTRL']);
self.setBit(PU_CTRL_Bits['NAU7802_PU_CTRL_PUA'], Scale_Registers['NAU7802_PU_CTRL']);
# Wait for Power Up bit to be set - takes approximately 200us
counter = 0;
while True:
if self.getBit(PU_CTRL_Bits['NAU7802_PU_CTRL_PUR'], Scale_Registers['NAU7802_PU_CTRL']) != 0:
break # Good to go
time.sleep(0.001)
if counter > 100:
return False # Error
counter += 1
return True
# Resets all registers to Power Of Defaults
def reset(self): # Resets all registers to Power Of Defaults
self.setBit(PU_CTRL_Bits['NAU7802_PU_CTRL_RR'], Scale_Registers['NAU7802_PU_CTRL']) # Set RR
time.sleep(0.001)
return self.clearBit(PU_CTRL_Bits['NAU7802_PU_CTRL_RR'], Scale_Registers['NAU7802_PU_CTRL']) # Clear RR to leave reset state
# Mask & set a given bit within a register
def setBit(self, bitNumber, registerAddress): # Mask & set a given bit within a register
value = self.getRegister(registerAddress)
value |= (1 << bitNumber) # Set this bit
return self.setRegister(registerAddress, value)
# Pass a known calibration factor into library. Helpful if users is loading settings from NVM.
# If you don't know your cal factor, call setZeroOffset(), then calculateCalibrationFactor() with a known weight
def setCalibrationFactor(self, newCalFactor): # Pass a known calibration factor into library. Helpful if users is loading settings from NVM.
self.calibrationFactor = newCalFactor
# Select between 1 and 2
def setChannel(self, channelNumber): # Select between 1 and 2
if channelNumber == NAU7802_Channels['NAU7802_CHANNEL_1']:
return self.clearBit(CTRL2_Bits['NAU7802_CTRL2_CHS'], Scale_Registers['NAU7802_CTRL2']) # Channel 1 (default)
else:
return self.setBit(CTRL2_Bits['NAU7802_CTRL2_CHS'], Scale_Registers['NAU7802_CTRL2']) # Channel 2
# Set the gain
# x1, 2, 4, 8, 16, 32, 64, 128 are available
def setGain(self, gainValue): # Set the gain. x1, 2, 4, 8, 16, 32, 64, 128 are available
if gainValue > 0b111:
gainValue = 0b111 # Error check
value = self.getRegister(Scale_Registers['NAU7802_CTRL1'])
value &= 0b11111000 # Clear gain bits
value |= gainValue # Mask in new bits
return self.setRegister(Scale_Registers['NAU7802_CTRL1'], value)
# Set Int pin to be high when data is ready (default)
def setIntPolarityHigh(self): # # Set Int pin to be high when data is ready (default)
return self.clearBit(CTRL1_Bits['NAU7802_CTRL1_CRP'], Scale_Registers['NAU7802_CTRL1']) # 0 = CRDY pin is high active (ready when 1)
# Set Int pin to be low when data is ready
def setIntPolarityLow(self): # Set Int pin to be low when data is ready
return self.setBit(CTRL1_Bits['NAU7802_CTRL1_CRP'], Scale_Registers['NAU7802_CTRL1']) # 1 = CRDY pin is low active (ready when 0)
# Set the onboard Low-Drop-Out voltage regulator to a given value
# 2.4, 2.7, 3.0, 3.3, 3.6, 3.9, 4.2, 4.5V are available
def setLDO(self, ldoValue): # Set the onboard Low-Drop-Out voltage regulator to a given value. 2.4, 2.7, 3.0, 3.3, 3.6, 3.9, 4.2, 4.5V are available
if ldoValue > 0b111:
ldoValue = 0b111 # Error check
# Set the value of the LDO
value = self.getRegister(Scale_Registers['NAU7802_CTRL1']);
value &= 0b11000111; # Clear LDO bits
value |= ldoValue << 3; # Mask in new LDO bits
self.setRegister(Scale_Registers['NAU7802_CTRL1'], value);
return self.setBit(PU_CTRL_Bits['NAU7802_PU_CTRL_AVDDS'], Scale_Registers['NAU7802_PU_CTRL']) # Enable the internal LDO
# Send a given value to be written to given address
# Return true if successful
def setRegister(self, registerAddress, value): # Send a given value to be written to given address. Return true if successful
try:
self.bus.write_word_data(self.deviceAddress, registerAddress, value)
except:
return False # Sensor did not ACK
return True
# Set the readings per second
# 10, 20, 40, 80, and 320 samples per second is available
def setSampleRate(self, rate): # Set the readings per second. 10, 20, 40, 80, and 320 samples per second is available
if rate > 0b111:
rate = 0b111 # Error check
value = self.getRegister(Scale_Registers['NAU7802_CTRL2'])
value &= 0b10001111 # Clear CRS bits
value |= rate << 4 # Mask in new CRS bits
return self.setRegister(Scale_Registers['NAU7802_CTRL2'], value)
# Sets the internal variable. Useful for users who are loading values from NVM.
def setZeroOffset(self, newZeroOffset):
self.zeroOffset = newZeroOffset # Sets the internal variable. Useful for users who are loading values from NVM.
# Wait for asynchronous AFE calibration to complete with optional timeout.
# If timeout is not specified (or set to 0), then wait indefinitely.
# Returns true if calibration completes succsfully, otherwise returns false.
def waitForCalibrateAFE(self, timeout = 0): # Wait for asynchronous AFE calibration to complete with optional timeout.
begin = time.time()
cal_ready = 0
while cal_ready == NAU7802_Cal_Status['NAU7802_CAL_IN_PROGRESS']:
if (timeout > 0) and ((time.time() - begin) > timeout):
break
time.sleep(0.001)
cal_ready = self.calAFEStatus()
if cal_ready == NAU7802_Cal_Status['NAU7802_CAL_SUCCESS']:
return True
return False