6
6
import logging
7
7
import pins
8
8
from . import bus
9
- from . import adc_temperature
10
9
11
10
# Supported chip types
12
11
ADS1X1X_CHIP_TYPE = {
@@ -173,22 +172,10 @@ def __init__(self, config):
173
172
if config .get ('address_pin' , None ) is not None :
174
173
address = config .getchoice ('address_pin' , ADS1X1X_CHIP_ADDR )
175
174
176
- if isADS101X (self .chip ):
177
- self .samples_per_second = config .getchoice ('samples_per_second' ,
178
- ADS101X_SAMPLES_PER_SECOND , '128' )
179
- self .samples_per_second_numeric = config .getint (
180
- 'samples_per_second' , 128 )
181
- else :
182
- self .samples_per_second = config .getchoice ('samples_per_second' ,
183
- ADS111X_SAMPLES_PER_SECOND , '128' )
184
- self .samples_per_second_numeric = config .getint (
185
- 'samples_per_second' , 128 )
186
-
187
175
self ._ppins = self ._printer .lookup_object ("pins" )
188
176
self ._ppins .register_chip (self .name , self )
189
177
190
178
self .pga = config .getchoice ('pga' , ADS1X1X_PGA , '4.096V' )
191
- self .mode = config .getchoice ('mode' , ADS1X1X_MODE , 'single' )
192
179
# Comparators are not implemented, they would only be useful if the
193
180
# alert pin is used, which we haven't made configurable.
194
181
# But that wouldn't be useful for a normal temperature sensor anyway.
@@ -201,55 +188,112 @@ def __init__(self, config):
201
188
self .mcu = self ._i2c .get_mcu ()
202
189
203
190
self ._printer .add_object ("ads1x1x " + self .name , self )
204
- self ._printer .register_event_handler ("klippy:ready" , \
205
- self ._handle_ready )
206
- self ._printer .register_event_handler ("klippy:shutdown" , \
207
- self .reset_all_devices )
191
+ self ._printer .register_event_handler ("klippy:connect" , \
192
+ self ._handle_connect )
208
193
209
194
self ._pins = {}
210
195
self ._mutex = self ._reactor .mutex ()
211
196
212
197
def setup_pin (self , pin_type , pin_params ):
198
+ pin = pin_params ['pin' ]
213
199
if pin_type == 'adc' :
214
- pin = ADS1X1X_pin (self , pin_params )
215
- if pin .pin in self ._pins :
200
+ if (pin not in ADS1X1X_MUX ):
201
+ raise pins .error ('ADS1x1x pin %s is not valid' % \
202
+ pin_params ['pin' ])
203
+
204
+ config = 0
205
+ config |= (ADS1X1X_OS ['OS_SINGLE' ] & \
206
+ ADS1X1X_REG_CONFIG ['OS_MASK' ])
207
+ config |= (ADS1X1X_MUX [pin_params ['pin' ]] & \
208
+ ADS1X1X_REG_CONFIG ['MULTIPLEXER_MASK' ])
209
+ config |= (self .pga & ADS1X1X_REG_CONFIG ['PGA_MASK' ])
210
+ # Have to use single mode, because in continuous, it never reaches
211
+ # idle state, which we use to determine if the sampling is done.
212
+ config |= (ADS1X1X_MODE ['single' ] & \
213
+ ADS1X1X_REG_CONFIG ['MODE_MASK' ])
214
+ # lowest sample rate per default, until report time has been set in
215
+ # setup_adc_sample
216
+ config |= (self .comp_mode \
217
+ & ADS1X1X_REG_CONFIG ['COMPARATOR_MODE_MASK' ])
218
+ config |= (self .comp_polarity \
219
+ & ADS1X1X_REG_CONFIG ['COMPARATOR_POLARITY_MASK' ])
220
+ config |= (self .comp_latching \
221
+ & ADS1X1X_REG_CONFIG ['COMPARATOR_LATCHING_MASK' ])
222
+ config |= (self .comp_queue \
223
+ & ADS1X1X_REG_CONFIG ['COMPARATOR_QUEUE_MASK' ])
224
+
225
+ pin_obj = ADS1X1X_pin (self , config )
226
+ if pin in self ._pins :
216
227
raise pins .error (
217
- '%s pin %s for chip %s is used multiple times' \
218
- % (self .chip , pin .pin , self .name ))
219
- self ._pins [pin .pin ] = pin
220
- return pin
228
+ 'pin %s for chip %s is used multiple times' \
229
+ % (pin , self .name ))
230
+ self ._pins [pin ] = pin_obj
231
+
232
+ return pin_obj
221
233
raise pins .error ('Wrong pin or incompatible type: %s with type %s! ' % (
222
- pin_params [ ' pin' ] , pin_type ))
234
+ pin , pin_type ))
223
235
224
- def _handle_ready (self ):
225
- self .reset_all_devices ()
236
+ def _handle_connect (self ):
237
+ try :
238
+ # Init all devices on bus for this kind of device
239
+ self ._i2c .i2c_write ([0x06 , 0x00 , 0x00 ])
240
+ except Exception :
241
+ logging .exception ("ADS1X1X: error while resetting device" )
226
242
227
243
def is_ready (self ):
228
244
config = self ._read_register (ADS1X1X_REG_POINTER ['CONFIG' ])
229
245
return bool ((config & ADS1X1X_REG_CONFIG ['OS_MASK' ]) == \
230
246
ADS1X1X_OS ['OS_IDLE' ])
231
247
232
- def sample (self , sensor ):
248
+ def calculate_sample_rate (self ):
249
+ pin_count = len (self ._pins )
250
+ lowest_report_time = 1
251
+ for pin in self ._pins .values ():
252
+ lowest_report_time = min (lowest_report_time , pin .report_time )
253
+
254
+ sample_rate = 1 / lowest_report_time * pin_count
255
+ samples_per_second = ADS111X_SAMPLES_PER_SECOND
256
+ if isADS101X (self .chip ):
257
+ samples_per_second = ADS101X_SAMPLES_PER_SECOND
258
+
259
+ # make sure the samples list is sorted correctly by number.
260
+ samples_per_second = sorted (samples_per_second .items (), \
261
+ key = lambda t : int (t [0 ]))
262
+ for rate , bits in samples_per_second :
263
+ rate_number = int (rate )
264
+ if sample_rate <= rate_number :
265
+ return (rate_number , bits )
266
+ logging .warning (
267
+ "ADS1X1X: requested sample rate %s is higher than supported by %s." \
268
+ % (sample_rate , self .name ))
269
+ return (rate_number , bits )
270
+
271
+ def handle_report_time_update (self ):
272
+ (sample_rate , sample_rate_bits ) = self .calculate_sample_rate ()
273
+
274
+ for pin in self ._pins .values ():
275
+ pin .config = (pin .config & ~ ADS1X1X_REG_CONFIG ['DATA_RATE_MASK' ]) \
276
+ | (sample_rate_bits & ADS1X1X_REG_CONFIG ['DATA_RATE_MASK' ])
277
+
278
+ self .delay = 1 / float (sample_rate )
279
+
280
+ def sample (self , pin ):
233
281
with self ._mutex :
234
- pin_object = self ._pins [sensor .pin ]
235
- sample = 0
236
282
try :
237
- self ._write_register (ADS1X1X_REG_POINTER ['CONFIG' ],
238
- pin_object .config )
239
- # The report time is 1 / sample_count * 4 to account for the 4
240
- # possible inputs. So sample_count 16 on 1 input will result
241
- # in 4 samples per second.
242
- delay = 1 / self .samples_per_second_numeric
243
- self ._reactor .pause (self ._reactor .monotonic () + delay )
283
+ self ._write_register (ADS1X1X_REG_POINTER ['CONFIG' ], pin .config )
284
+ self ._reactor .pause (self ._reactor .monotonic () + self .delay )
285
+ start_time = self ._reactor .monotonic ()
244
286
while not self .is_ready ():
245
287
self ._reactor .pause (self ._reactor .monotonic () + 0.001 )
246
- sample = self ._read_register (ADS1X1X_REG_POINTER ['CONVERSION' ])
247
- except Exception :
248
- logging .exception ("ADS1X1X: error while sampling" )
288
+ # if we waited twice the expected time, mark this an error
289
+ if start_time + self .delay < self ._reactor .monotonic ():
290
+ logging .warning ("ADS1X1X: timeout during sampling" )
291
+ return None
292
+ return self ._read_register (ADS1X1X_REG_POINTER ['CONVERSION' ])
293
+ except Exception as e :
294
+ logging .exception ("ADS1X1X: error while sampling: %s" % str (e ))
249
295
return None
250
296
251
- return sample
252
-
253
297
def _read_register (self , reg ):
254
298
# read a single register
255
299
params = self ._i2c .i2c_read ([reg ], 2 )
@@ -264,89 +308,63 @@ def _write_register(self, reg, data):
264
308
]
265
309
self ._i2c .i2c_write (data )
266
310
267
- def reset_all_devices (self ):
268
- try :
269
- # Init all devices on bus for this kind of device
270
- self ._i2c .i2c_write ([0x06 , 0x00 , 0x00 ])
271
- except Exception :
272
- logging .exception ("ADS1X1X: error while resetting device" )
273
-
274
311
class ADS1X1X_pin :
275
- def __init__ (self , chip , pin_params ):
312
+ def __init__ (self , chip , config ):
276
313
self .mcu = chip .mcu
277
314
self .chip = chip
278
- self .pin = pin_params ['pin' ]
279
-
280
- if (self .pin not in ADS1X1X_MUX ):
281
- raise pins .error ('ADS1x1x pin %s is not valid' % self .pin )
282
-
283
- self .mux = ADS1X1X_MUX [self .pin ]
284
- # Set up 2-byte configuration that will be used with each request
285
- self .config = 0
286
- self .config |= (ADS1X1X_OS ['OS_SINGLE' ] \
287
- & ADS1X1X_REG_CONFIG ['OS_MASK' ])
288
- self .config |= (self .mux & ADS1X1X_REG_CONFIG ['MULTIPLEXER_MASK' ])
289
- self .config |= (chip .pga & ADS1X1X_REG_CONFIG ['PGA_MASK' ])
290
- self .config |= (chip .mode & ADS1X1X_REG_CONFIG ['MODE_MASK' ])
291
- self .config |= (chip .samples_per_second & \
292
- ADS1X1X_REG_CONFIG ['DATA_RATE_MASK' ])
293
- self .config |= (chip .comp_mode \
294
- & ADS1X1X_REG_CONFIG ['COMPARATOR_MODE_MASK' ])
295
- self .config |= (chip .comp_polarity \
296
- & ADS1X1X_REG_CONFIG ['COMPARATOR_POLARITY_MASK' ])
297
- self .config |= (chip .comp_latching \
298
- & ADS1X1X_REG_CONFIG ['COMPARATOR_LATCHING_MASK' ])
299
- self .config |= (chip .comp_queue \
300
- & ADS1X1X_REG_CONFIG ['COMPARATOR_QUEUE_MASK' ])
301
-
302
- self .report_time = 1.0 / chip .samples_per_second_numeric * 4
315
+ self .config = config
316
+
303
317
self .invalid_count = 0
304
318
305
319
self .chip ._printer .register_event_handler ("klippy:connect" , \
306
- self ._handle_connect )
320
+ self ._handle_connect )
307
321
308
322
def _handle_connect (self ):
309
323
self ._reactor = self .chip ._printer .get_reactor ()
310
324
self ._sample_timer = \
311
- self ._reactor .register_timer (self ._timer )
312
- self . _reactor . update_timer ( self . _sample_timer , self ._reactor .NOW )
325
+ self ._reactor .register_timer (self ._process_sample , \
326
+ self ._reactor .NOW )
313
327
314
- def _timer (self , eventtime ):
328
+ def _process_sample (self , eventtime ):
315
329
sample = self .chip .sample (self )
316
330
if sample is not None :
317
- self ._process_sample (sample )
318
-
319
- return self ._reactor .monotonic () + self .report_time
320
-
321
- def _process_sample (self , sample ):
322
- # The sample is encoded in the top 12 or full 16 bits
323
- # Value's meaning is defined by ADS1X1X_REG_CONFIG['PGA_MASK']
324
- if isADS101X (self .chip .chip ):
325
- sample >>= 4
326
- target_value = sample / ADS101X_RESOLUTION
327
- else :
328
- target_value = sample / ADS111X_RESOLUTION
331
+ # The sample is encoded in the top 12 or full 16 bits
332
+ # Value's meaning is defined by ADS1X1X_REG_CONFIG['PGA_MASK']
333
+ if isADS101X (self .chip .chip ):
334
+ sample >>= 4
335
+ target_value = sample / ADS101X_RESOLUTION
336
+ else :
337
+ target_value = sample / ADS111X_RESOLUTION
329
338
330
- if self .maxval > self .minval :
331
339
if target_value > self .maxval or target_value < self .minval :
332
340
self .invalid_count = self .invalid_count + 1
333
- if self .invalid_count > self .range_check_count :
334
- self .chip ._printer .invoke_shutdown (
335
- "ADS1X1X temperature outside range" )
341
+ logging .warning ("ADS1X1X: temperature outside range" )
342
+ self .check_invalid ()
336
343
else :
337
344
self .invalid_count = 0
338
345
339
- # Publish result
340
- measured_time = self ._reactor .monotonic ()
341
- self .callback (self .chip .mcu .estimated_print_time (measured_time ),
342
- target_value )
346
+ # Publish result
347
+ measured_time = self ._reactor .monotonic ()
348
+ self .callback (self .chip .mcu .estimated_print_time (measured_time ),
349
+ target_value )
350
+ else :
351
+ self .invalid_count = self .invalid_count + 1
352
+ self .check_invalid ()
353
+
354
+ return eventtime + self .report_time
355
+
356
+ def check_invalid (self ):
357
+ if self .invalid_count > self .range_check_count :
358
+ self .chip ._printer .invoke_shutdown (
359
+ "ADS1X1X temperature check failed" )
343
360
344
361
def get_mcu (self ):
345
362
return self .mcu
346
363
347
364
def setup_adc_callback (self , report_time , callback ):
348
365
self .report_time = report_time
349
366
self .callback = callback
367
+ self .chip .handle_report_time_update ()
350
368
351
369
def setup_adc_sample (self , sample_time , sample_count ,
352
370
minval = 0. , maxval = 1. , range_check_count = 0 ):
0 commit comments