seandheath / PyADS1256

Python class for interacting with the ADS1256 analog to digital converter with the Raspberry Pi
19 stars 12 forks source link

SPI setup bugs #1

Open jrosenstein opened 8 years ago

jrosenstein commented 8 years ago

This was a helpful starting point, but it only works after fixing a couple of important SPI setup problems.

  1. Instead of wiringPiSPISetup, it needs to use wiringPiSPISetupMode, with SPI_MODE=1
  2. It suffers from this WiringPi-Python bug, and needs the same workaround of creating a new string inside the call to wiringPiSPIDataRW: https://github.com/WiringPi/WiringPi-Python/issues/25
GrahamOB commented 7 years ago

This code could be a gem. Anyone fancy getting it working? If I had the expertise I would pick it up immediately.

jrosenstein commented 7 years ago

I don't have write access to this, but I forked and uploaded my version of pyads1256.py here: https://github.com/jrosenstein/PyADS1256

ul-gh commented 7 years ago

I have just now tried https://github.com/jrosenstein/PyADS1256 on Raspbian Jessie (Linux 4.4.21-v7+) with the "Waveshare Precision" ADC hardware.

I can confirm, this works.

jrosenstein: Thank you very much!

Now, the abovementioned hardware is of course no good for real 24-bit precision, but should my current project, a simple water flow calorimeter (Wärmebilanzkalorimeter) ever evolve into a precision instrument, (50% likelyhodd, my guess), I will see if I can make public a proper analog frontend design.

ul-gh commented 7 years ago

Test example attached.

ADS1256_example.py.txt

GrahamOB commented 7 years ago

Thank you very much for getting this working. I do have one small question regarding the conversion of the raw output to volts. Why is the result divided by 167? Thanks again for your help.

ul-gh commented 7 years ago

Hi Graham,

the full scale reading of course depends on the hardware as well as the setting of the ADS1256 internal programmable gain amplifier. If you run above example without any changes to the default values, the setting is PGA=1. If Vref is connected to 2.5V as with the "Waveshare" hardware, and if you look on page three of the ADS1256 datasheet, the full-scale input voltage is then 2*Vref/PGA = 5V. I.e. 5V input corresponds to 23-Bit full-scale (neglecting any offset and gain errors, and correction: output is in 24-Bit two's complement format, see table 16 on page 23 of the ADS1256 datasheet). From this you can check your output values.

I don't have the harware at hand, but I can check if this is correct with my board tomorrow.

ul-gh commented 7 years ago

Hi guys, I forked the original project just now. This is a complete rework - I worked through the Ti ADS1255/ ADS1256 datasheet from beginning through end. HTH.

ul-gh commented 7 years ago

https://github.com/ul-gh/PyADS1256

renderit commented 7 years ago

@ul-gh Thanks for this but when I try the attached test above with the code in your repo, I get error:

Traceback (most recent call last):
  File "test.py", line 7, in <module>
    idNumber = ads.ReadID()
  File "/home/pi/Downloads/High-Precision-AD-DA-Board-Code/Raspberry/PyADS1256/pyads1256.py", line 518, in ReadID
    myid = self.ReadReg(self.REG_STATUS, 1)
TypeError: ReadReg() takes exactly 2 arguments (3 given)

I am using, this High precision AD/DA Board with Raspberry Pi 3 which has a ADS1256 on it.. Also, is @jrosenstein repo code exactly same as @ul-gh code?

ul-gh commented 7 years ago

Hi,

thanks for your message! I fixed above error just now. and commited to: https://github.com/ul-gh/PyADS1256/.

Will upload some example files tomorrow. Best wishes.

renderit commented 7 years ago

@ul-gh Example files would be awesome! Will look forward to that!! Quick question, when i run test.py I am seeing output like:

ADS1256 reported ID value: 3

Press CTRL-C to interrupt..
AIN_0 value: 3009986
AIN_1 value: 1334144

Is it possible to convert those readings into voltage? like in the example C code on the website? I want to see output like This C example:

ID=
Ok, ASD1256 Chip ID = 0x3
0=131349,  1250121 ( 0.748 575 V) 
1=132D81,  1256833 ( 0.752 594 V) 
2=1D62D4,  1925844 ( 1.153 200 V) 
3=1D64DB,  1926363 ( 1.153 510 V) 
4=1D648C,  1926284 ( 1.153 463 V) 
5=1D6562,  1926498 ( 1.153 591 V) 
6=1D6534,  1926452 ( 1.153 564 V) 
7=1D637D,  1926013 ( 1.153 301 V) 

Honestly, I'm sure what those reading in the brackets imply. Would anybody know what exactly they mean? Is it "575 volts"?

ul-gh commented 7 years ago

Hi Jim,

just to let you know: I said "tomorrow", but I decided to do a complete rewrite of the class definition for a true object-based interface, using Python properties to access the ADC settings and results.

Since that changes the whole API, I do not want to encourage you to use the current state code.

Sorry, this will take a few days. I am already using the ADC using the code I uploaded, and yes, of course you can read all eight input pins (nine even, if you count the AINCOM).

Give me until the end of next week - I promise you a working example with the new extended code!

ul-gh commented 7 years ago

BTW, yes, the output of the manufacturer example code in the parentheses of course means "volts", which is more exactly the voltage difference between the respective AIN pin number and the AINCOM pin.

However, the manufacturer code has some serious bugs. You would have to read the datasheet thoroughly and fix those bugs if you wanted to change any of the settings. Again, I promise you an example next week.

renderit commented 7 years ago

@ul-gh Excellent! Looking forward to that!!

ul-gh commented 7 years ago

Just now uploaded on GitHub the promised "working" example for the ADS1256 chip using the extended API with Python properties. Has only seen rudimentary testing though..

Because Ti have some very similar ADC hardware chips with slightly different functionality, I decided to make the ADS1256 class part of a new Python module where adding new classes for different chips is easy.

Have a look and feel free to share any issues there might be! https://github.com/ul-gh/PiPyADC

donmatth commented 7 years ago

Hello @ul-gh,

Firstly, thank you for the implementation. Very well done! About the hardware limitation, is there anyway to walk around the 0.298 uV/bit imposed by the 2.5V ref?

Since I'd like to use it in a thermal controller PID, I am not sure if this resolution is accurate enough. Thanks for your attention and support.

Sincerely, Math~

ul-gh commented 7 years ago

Dear Math,

Thank your for your feedback!

Yes, the ADS1256 has an on-chip programmable gain amplifier that can be configured for fixed amplifications up to a factor of 64.

You can set the factor via the respective ADS1256 property:

@property
    def pga_gain(self):
        """Get/Set ADC programmable gain amplifier setting.

        The available options for the ADS1256 are:
        1, 2, 4, 8, 16, 32 and 64. [...]

This is also settable via the gain_flags value in the config file.

The resulting resolution even has a readable property: v_per_digit, you can find this in the file example_2.py.

On the other hand, for a thermocouple, you really do not need a higher resolution, except for differential measurements.

E.g. using a type K thermocouple which has approx. 40µV per °C, the 0.3µV correspond to less than 0.01°C - this is well beyond the accuracy that you can achieve with any regular thermocouple measurement.

What is usually more important is a proper layout and hardware design for the results to be free from noise and thermal voltage drift. E.g. make use of the fully differential input in order to compensate for DC ground leakage current and use an averaging time period that is a multiple of the 50Hz or 60Hz line frequency (e.g. 10 samples per second).

And of course it never hurts to maximise signal-to-noise ration by using the highest possible hardware amplification setting.

Again, please let me know if this helps.

donmatth commented 7 years ago

Dear ul-gh,

Thank you very much for your reply. I am actually willing to use the same board you have used before (High precision AD/DA Board) in order to get the temperature from some RTDs (pt100) in differential mode.

The hardware limitation I've asked previously was concerning this High precision AD/DA Board. You mentioned that it is no good for the 24 bits precision offered by the ADS1256. I don't know if I really get why !

I am also not sure about how to wire the pt100s with this hardware. I imagine that I am supposed to feed the sensors with another power supply and arrange it in a voltage divisor (or a Wheatstone bridge) and then connect its terminals to the hardware. But again, I am not sure at all, therefore some insight would help me a lot.

Thank you again for your help and for your attention.

Sincerely, Math~

ul-gh commented 7 years ago

Hi,

the Waveshare brand "High Precision AD/Da Board" has a relatively high noise level. This depends on the printed circuit layout and on the quality of the on-board reference voltage source.

You can directly equate the circuit noise level to the usable resolution, or the Effective Number of Bits of the ADC: ENOB = (SINAD - 1.76dB) / 6.02dB, with SINAD being the signal-to-noise-and-distortion level in decibels.

You can reduce the SINAD by using a software filter, where fortunately the easiest software filter is also the most effective one in terms of noise reduction: The simple (moving) average filter.

Basically, if you use a low enough sampling frequency, (the ADS1256 has on-chip averaging) this should be sufficient for a temperature sensor.

For real 24-bit precision and in order to eliminate systematic errors like temperature drift, you need to go the extra mile, e.g. by using a circuit board which avoids temperature differentials causing thermoelectric drift or by using a metal shield against electromagnetic influence.

However, for a PT100 sensor, this board is perfectly usable with a small external front-end.

In fact, I designed a precision differential temperature measurement using PT1000 sensors and the Waveshare ADS1256 board for a flow-type Heat Balance Calorimeter.

I also wrote some python code and a NodeRED application and web browser interface for this application.

Give me a few weeks, I have to write some documentation, then I can publish the software.

For the PT100 measurement, btw., this is what I use for the resistance-to-temperature calculation:

def ptRTD_temperature(r_x, r_0=1000.0):
    """Quadratic equation for the temperature of platinum RTDs.
    This is the inversion of the H.L.Callendar polynomial for positive
    temperatures.

    Result is temperature in °C according to the ITS-90 scale.

    For negative temperatures, we are adding a correction term. This is
    a fifth-order polynomial fit of the deviation of the numerically
    inverted ITS-90 standard Callendar-Van Dusen equation, using coef-
    ficient "C" for T < 0, from the second-order equation without
    coefficient "C". Source for the ITS-90 standard polynomial:

    http://de-de.wika.de/upload/DS_IN0029_en_co_59667.pdf
    (Verified with DIN EN 60751:2009, 2017-02-21)

    Source for the correction term:
    https://github.com/ulikoehler/UliEngineering
    """
    PT_A =  3.9083E-3
    PT_B = -5.775E-7
    # Uncorrected solution which is exact for positive temperatures
    r_norm = r_x / r_0
    theta = (- PT_A + np.sqrt(PT_A**2 - 4*PT_B*(1 - r_norm))
            ) / (2*PT_B)
    if r_norm < 1.0:
        # Polynomial correction only for negative temperatures
        correction = np.poly1d(
            [1.51892983e+00, -2.85842067e+00, -5.34227299e+00,
             1.80282972e+01, -1.61875985e+01,  4.84112370e+00]
        )
        return theta + correction(r_norm)
    else:
        return theta

For the hardware circuit, you can use a wheatstone bridge circuit and the following code. R1 is the PT100 or PT1000 sensor:

def wheatstone(ud, u0, nref, rs1):
    """ Return wheatstone bridge unknown resistance r1.
    Arguments:
        ud:   Bridge voltage differential
        u0:   Reference bridge leg absolute voltage
        nref: Reference bridge leg resistance ratio
        rs1:  Measurement bridge leg series resistor
      _______
      |      |
     rs1    rs0       nref=rs0/r0
      |--ud->|..u0
     r1     r0
      |      |
      _______ ..0V
    """
    return rs1 * (u0 + ud)/(u0*nref - ud)
alessandropignatelli commented 7 years ago

Hi, sorry for possible very stupid question but I'm very new in using the device. When I try to run the test.py, I get the error Unable to open SPI device: No such file or directory. I would be very grateful if anyone could help me. Thanks.

Alessandro.

mroscarwilde commented 6 years ago

Hi, is there any Solution for the High Precision AD/DA Board to get the Voltage Data in Node-Red? I have a Raspberry Pi 3 and the High Precision AD/DA Board to which are 8 Sensors connected and I want to get the Voltage Data into Node-Red and show them in a Dashboard. Can anybody help me with this?

Oscar