danjperron / BitBangingDS18B20

Method to access the DS18B20 sensor using Rapsberry Pi GPIO
41 stars 29 forks source link

Raspberry Pi 3 and MAX31850 (now working!) #5

Closed RpiController closed 5 years ago

RpiController commented 7 years ago

Hi, I just signed up on GitHub so I could ask you some questions.

I've been reading just about every post you've made on this topic everywhere I could find on the internet. In a comment you made here... https://www.raspberrypi.org/forums/viewtopic.php?f=37&t=77611&start=25 You show an example of using ./DS18B20Scan on -gpio 4. I would like to know how you got that to work.

-Jacob

danjperron commented 7 years ago

Simple! I read the datasheet and figure out how it works.

Everything you need to know about the DS18B20 is on the datasheet. https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf

The rest is to figure out how the GPIO on the Raspberry Pi works.

It is a 32 bits bus which you could read the full 32 bits in one dword read. You need to write to a 32 bits register to set one of the bit high and to another register to clear the bit.

BCM GPIO0 correspond to bit 0 ,...., and BCM GPIO31 to bits 31.

You will have more explicit about your question.

Daniel

Le 27 avr. 2017 à 16:30, NCSUphytotron notifications@github.com a écrit :

Hi, I just signed up on GitHub so I could ask you some questions.

I've been reading just about every post you've made on this topic everywhere I could find on the internet. In a comment you made here... https://www.raspberrypi.org/forums/viewtopic.php?f=37&t=77611&start=25 https://www.raspberrypi.org/forums/viewtopic.php?f=37&t=77611&start=25 You show an example of using ./DS18B20Scan on -gpio 4. I would like to know how you got that to work.

-Jacob

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/danjperron/BitBangingDS18B20/issues/5, or mute the thread https://github.com/notifications/unsubscribe-auth/AEJf3KHDanIsTsVmjV1s53hDW3RdMXcAks5r0PrYgaJpZM4NKuu3.

RpiController commented 7 years ago

Haha, it's not so simple for some of us.

We have 4 thermocouples (1 host and 3 in parasite mode) on GPIO4. They are MAX31850 boards. Running on Raspberry Pi 3. But they run at the slow 750ms speed.

I have download your code and tried modifying it for Pi3, but I'm not sure where exactly to change "BCM2708_PERI_BASE "0x20000000" to BCM2708_PERI_BASE "0x3F000000". There is a "0x20000000" in a couple of places in the file. I've tried it different ways but haven't had any success. Is it still necessary to make that change? (You've updated your code a few times) Do I need to modify all three of your code files for Pi 3? (configDS18B20.c, DS18B20Scan.c, and DS18B20V2.c)

danjperron commented 7 years ago

I never intend my code to work on parasite mode.

The PI3 BCM2708_PERI_BASE is 0x3F000000

Thing to check.

First use a GPIO which is only INPUT/OUTPUT. You can't have a GPIO with other type of function. Ex: disable the current 1wire if you want GPIO4 to work with my code. Second try with only one ds18B20 in normal mode (Not parasite). And third Don't forget the 4k7 pull-up resistor.

Then try sudo ./configDS18B20 x

where x is is BCM GPIO pin

Le 27 avr. 2017 à 21:16, NCSUphytotron notifications@github.com a écrit :

Haha, it's not so simple for some of us.

We have 4 thermocouples (1 host and 3 in parasite mode) on GPIO4. They are MAX31850 boards. Running on Raspberry Pi 3. But they run at the slow 750ms speed.

I have download your code and tried modifying it for Pi3, but I'm not sure where exactly to change "BCM2708_PERI_BASE "0x20000000" to BCM2708_PERI_BASE "0x3F000000". There is a "0x20000000" in a couple of places in the file. I've tried it different ways but haven't had any success. Is it still necessary to make that change? (You've updated your code a few times) Do I need to modify all three of your code files for Pi 3? (configDS18B20.c, DS18B20Scan.c, and DS18B20V2.c)

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/danjperron/BitBangingDS18B20/issues/5#issuecomment-297881429, or mute the thread https://github.com/notifications/unsubscribe-auth/AEJf3IwncUCmdL3XUwYKNc9dm7E0Rysgks5r0T35gaJpZM4NKuu3.

RpiController commented 7 years ago

Ok I think I might give up on this. According to this page the MAX31850 accepts the same commands as the DS18B20. But the MAX31850 datasheet doesn't mention anything about being able to adjust the temperature resolution and seems to indicate that the configuration register is hard-coded.

We have successfully used the method of recompiling the driver file found here to speed up our read time. (It seems like there should be an easier way to modify the driver without recompiling the whole kernel.) But I was hoping to also change the temperature resolution which is locked in at .25 degree Celsius to the .125 setting.

It would be nice if someone could figure out how to hack the MAX31850 so that you can adjust its settings. I was hoping your code might be able to do it.

danjperron commented 7 years ago

Then configDS18B20 is not needed for you.

But DS18B20Scan could work with some modification.

The way DS18B20Scan works is

Send a global start of conversion (All sensors using SKIP_ROM).
Scan or read the specific sensor from a file and read the temperature.

I should buy one and see how they perform. Next time that I need some Adafruit stuff I will buy one.

RpiController commented 6 years ago

I finally got the DS18B20Scan code to work with MAX31850 thermocouple sensors! (I can get it to read them, but it still doesn't seem possible to change the resolution.)

The MAX31850 scratchpad value for resolution is always "0xf0" so I added the line (around #456ish) in the ReadSensor function... case 0xf0: resolution=10;break; (you can actually set it to 11 or 12 but it will still only return a .25 degree resolution because that's all the MAX31850 seems capable of) Which makes me wonder, why not just leave it at 12 for everything and it will return the most precise value available?

Additionally I changed the print output to show 4 decimal places for my other DS18B20 sensors that have higher resolution.... printf("%02d bits Temperature: %6.4**f +/- %4.4f Celsius\n",......

I'm not sure why Dan has it only set to show 2.

The code will still lock up infinitely if you try to use the change resolution parameter (-xbits) with a MAX31850. It would be nice to add some code to detect if the sensor is family code 0x3B and bypass sending a write command if this is true. I tried adding a line to the ChangeSensorResolution function (line #580ish) if((ID & 0xFF)==0x3B)..... but it gives an error because apparently the "ID" variable is not defined in this block. I don't know how to program in C to adjust for this, but maybe I'll figure it out someday.

danjperron commented 6 years ago

Thumbs up! You were able to get the MAX31850 working with my code!

Something that you should know. I read the spec on the MAX31850 and it is 14 bits and you can't change the resolution. One of my previous post specified that the resolution is fixed on MAX31850. Don't forget that the code was done for the DS18B20.

One digital value is equivalent to 0.25 Celsius then the precision is no more than that. Display the temperature with four decimal digits is not good at all since they mean nothing. Even one decimal is too much . Depending on the type of metal used the error is between +/- 1 to +/- 6 Celsius. Why display value at 4 decimals point when the error is at least +/- 1 Celsius.

And for the DS18B20 the resolution is 0.0625 per digital value. This is why I used 2 digits but in reality I should used only 1 digits. The DS18B20 error is +/- 0.5 Celsius.

RpiController commented 6 years ago

Lol, that is the whole point of having a sensor with a small/tight resolution! There is a difference between error and precision.

Error: If the actual temperature according to the laws of physics, the universe, and the International Bureau of Weights and Measures is off +/- 1 degree... I don't really care that much. I can always add or subtract a calibration factor if needed.

Precision: The most important thing to me is CHANGE in temperature. If the temperature changes by 0.0625 of a degree, I want to know about it and I want to know it immediately. The goal is to hold a CONSTANT temperature with no fluctuation.

So yes, I most definitely need 4 decimal places of precision on my readings. If you have any thoughts on how I could add the functionality I mentioned to detect if the sensor is of the MAX31850 family (0x3B) to then skip any write instructions, I would appreciate it.

danjperron commented 6 years ago

The familly code is 3B instead of 28 for the 31850 then if the sensor familly is 3B just disable the resolution change.

DS18B20 resolution is 0.0625 at best ! You will notice the change even with a 2 decimals numbers. Four decimal values will display the direct digital to engineering value but it will be off by +/- 1 digital with a theoretical perfect sensor.

RpiController commented 6 years ago

Ok I suppose you are technically correct. The change will be noticeable even with only 2 decimal places. You will get... 0.06, 0.12, 0.18, 0.25, 0.31, 0.37, 0.43, 0.5, 0.56, 0.62, 0.68, 0.75, 0.81, 0.87, 0.93, 1 instead of 0.0625, 0.125, 0.1875, 0.25, 0.3125, 0.375, 0.4375, 0.5, 0.5625, 0.625, 0.6875, 0.75, 0.8125, 0.875, 0.9375, 1

However, for the various math equations I use in my program where I make decisions based on tiny fluctuations over time, I like having the more precise numbers.... a 0.07 change in average temperature might trigger a certain event that a 0.06 change would not.

Honestly the resolution changeability is not a major issue, since I probably have no need to adjust it after an initial install, but it would still be nice to have if it's only a matter of a line or two of code that would provide functionality for both types of sensors. I have a mixture of DS18B20 and MAX31850 on one gpio pin.

The test condition if((ID & 0xFF)==0x3B) works within the ReadSensor function but it errors within the ChangeSensorResolution function because the ID variable doesn't exist there.

danjperron commented 6 years ago

Yes the resolution is written into the eerom of the DS18B20. You do it only once.

I used a table which contains all the sensors ID and their location. From that table I know exactly which sensor I'm using. I didn't have any sensor other than DS18B20 when I made that code. Maybe you should start with a function to read the sensors ID first and then check if that sensor allow the change.

I made this code to prove that you could increase measurement speed using lower resolution or by reading more than one sensor in one time with multiple GPIOs individually connected.

B.T.W. Values into a variable and values display on a screen is two different things. The variable will hold the floating point value and it doesn't care about the number of decimal unless you need more resolution than a 'double' which gives you ~28 decimals(numerator dependent). Your equation will use the floating point variable.

I suppose that you will have a P.I.D. control there. I use a DS18B20 with a simple P.I. controller in Python, I removed the derivative part because I didn't need it and I was doing step increase temperature to extract the compensation factor of an instrument. I was able to get the temperature stable within +/- 0.1 Celsius . That was a four hours process, increasing the temperature by 5 Celsius every half hour.

Good luck with your project

RpiController commented 6 years ago

Dear Dan, I don't understand C code and I need help from someone that does. I only know python. It looks like there is already a function that reads the sensor ID in your code.

Yes, I am very very very appreciative of your work on this! I keep checking the internet and you are still the only person in the whole world that has a (independent of drivers) way to quickly read DS18B20 sensors!

B.T.W. this is the other thing I asked you in the raspberrypi.org DS18B20 Low Refresh Rate forum but I never got a response. The only way I have of getting results from your code into Python is to use those "on-screen" values by means of a subprocess.check_output("./DS18B20Scan -gpio 20", cwd='/ChamberData', shell=True).strip() command. There are bugs in the subprocess module (even the new subprocess32 one) and it would be nice if there was a more reliable way to get the output from your code into Python.

Yep yep, it is a P.I.D. of sorts. It's working well enough with the combined read cycle time of all sensors at about 2~3 seconds using RPis built-in drivers, but I would like to get it even shorter/quicker by using your code. I would be curious to see the Python code for your project.

danjperron commented 6 years ago

I'll see if I could take some time this week to implement a python module that could read the specific sensor using my binary C code instead of a subprocess. This way it will transfer the full variable value.

It won't be python code in a sense. It will be an interface to use python directly.

I did something similar with https://github.com/danjperron/PiFreeRunningCounter

danjperron commented 6 years ago

Ok Python module Done!

Parallel mode (One sensor per GPIO) use pinsRead(). Multiple sensors per GPIO use read()

Please check the Help inside python and look at the ReadMe to install the module into python!

By default the acquisition delay is set to 0.75 sec. The pinsStartConversion() function doesn't have a wait delay. Don't forget to put a delay before a read(False)

The First argument inside the read() and pinsRead() function is a enable start conversion flag. It will enable enable the start conversion + a delay of 0.75 second or won't start a conversion and no delay. This permit to start pinsStartConversion() function and synchronize all sensor to start a conversion. This way you could start a conversion for all sensors then read each sensor very fast.

Please send me your first impression about the python version.

Daniel

RpiController commented 6 years ago

I'm using Raspberry Pi 2 Model B. Do I still have to do the change to PERI_BASE=0x3F000000? I've tried both ways. I still get.... (in the shell)

Traceback (most recent call last):
  File "<pyshell#0>", line 1, in <module>
    import DS18B20 as DS
error: Unable to open /dev/mem. Failed

Ok, I got it to work in a terminal window with

sudo python
import DS18B20
DS18B20.scan(20)
danjperron commented 6 years ago

Don't change the PERI_BASE. It is automatic now! I'm using the information in /proc/iomem to figure out the io memory base!

you need to use sudo! Since I'm working using /dev/mem we need to be superuser!

Check the example that I just post on the Raspberry Pi forum. https://www.raspberrypi.org/forums/viewtopic.php?f=37&t=77611&p=1246895#p1246895

Don't forget to check the help inside python sudo python help('DS18B20') use the space bar to display the rest of the help

RpiController commented 6 years ago

Everything is looking good Dan! Thanks for your work!

I have a MAX31850 and 5 DS18B20's on gpio 20. When I do... DS18B20.scan(20) to my delight I get... ['28-000009414B72', '28-00000941375E', '28-00000941254B', '28-000009414907', '28-00000941391F', '3B-2CDC03887074'] and I can read all the DS18B20's. But DS18B20.read(True,20,'3B-2CDC03887074') or DS18B20.read(False,20,'3B-2CDC03887074') returns nothing :cry: I will try to look through your c file to see if I can figure out what is blocking the MAX31850.

danjperron commented 6 years ago

Could you show me what the scratch pad registers return

import time
import DS18B20 as DS
DS.pinsStartConversion([20])
time.sleep(0.75)
DS.readScratchPad(20,'3B-2CDC03887074')
RpiController commented 6 years ago
>>> DS18B20.readScratchPad(20,'3B-2CDC03887074')
[128, 1, 112, 23, 240, 255, 255, 255, 13]
danjperron commented 6 years ago

ok I will induce the registers scratch pad and figure out why it's not working

RpiController commented 6 years ago

I've notice there are several places in your code that have...

case  0x1f: resolution=9;break;
case  0x3f: resolution=10;break;
case  0x5f: resolution=11;break;
case  0x7f: resolution=12;break;

but do not have a case for 0xf0 which is what the MAX31850 uses.

danjperron commented 6 years ago

I fix the problem by checking the Famiily name in the read function. I can't do the same thing in the pinsread(). I'm adding a readMax31850() function that will return the thermocouple and the internal temperature. It should be ready in 10 minutes roughly.

RpiController commented 6 years ago

I added in case 0xf0: resolution=10;break; to all those areas and now it works!

danjperron commented 6 years ago

I know that it will work but I'm not certain that the config register will be always 0xf0. I just bypass the resolution check when the Family is 0x3B.

danjperron commented 6 years ago

OK I updated the github.

I added a function readMax31850() to return the internal temperature and the Error flag. I also correct the value because bit 0 is the fault for the MAX31850.

Now the code figures out which sensor you have with the Family code (0x28=DS18B20, 0x3B=Max31850)

I can't test my code because I don't have a Max31850. If you encounter a problem just tell me.

RpiController commented 6 years ago

Works great with both families! Thanks!

Ok I got one last request/inquiry/challenge for you and then I promise I will leave you alone. :smiley:

We have a few chambers where I converted MAX31850 thermocouples over to DS18B20 sensors by using the existing 2 conductor wire that was already there (connecting it in parasite mode which only needs 2 wires... data and ground). Parasite formation actually works with the built-in Dallas drivers and all the sensors show up and are readable in the file-tree directory.

I tried your new code with one DS18B20 in parallel and 4 other DS18B20's in parasite on one gpio and the .scan() function detects all 5 sensors! Even the .readScratchPad() .getResolution() and .setResolution()works. However the .read() function only returns the correct temperature on the parallel sensor but returns the default 85.0 for the temperature on the 4 parasite configured ones. Do you have any thoughts on how to read the parasite sensors?

danjperron commented 6 years ago

I never checked the parasite mode.

I add the High current after the start conversion command.(Keep the output high until the start conversion is done). It should work now! I'm not sure if it will work because I'm not near my sensors. I will check it tonight.

I also find a bug in the setResolution. I was changing the resolution but I never wrote to the eerom. Now if you change the resolution it will remember the settings even after power off.

danjperron commented 6 years ago

Ok I got it now! I was able to connect the four sensors I got into parasite mode! All of them on GPIO20 with their ground and power pin to ground!

Please reload the github!

RpiController commented 6 years ago

Omg it works perfectly, you are a genius! Thank you so much! :clap::clap::clap:

I don't understand why you are the only person in the world to have solved this problem. This kind of code should have been available years ago.

danjperron commented 6 years ago

Please note that if one of the sensor doesn't respond well it will return None.

I didn't take time to add exception handling. In the near future I will clean my C code, too much cut and paste, and convert it to C++ to create a encapsulate class.
But right now I won't touch it until next year,

Happy holidays Daniel

RpiController commented 6 years ago

No problemo. I figured out that a if variable is not None:
line of python code will ignore the "None"s.

Just fyi, I noticed the .pinsRead() command doesn't seem to work with the MAX31850s. It's not a problem, I can still read them using the other commands, but I just thought I'd let you know.

danjperron commented 6 years ago

It will have to wait next year! I will need to order a max31850 on my next purchase from adafruit or amazon.

RpiController commented 6 years ago

Good morning Dan! Hope you had an enjoyable winter break. I just wanted to let you know our chambers are running better than they ever have. It is very satisfying to watch all of the sensors update in under one second! They update faster they than the clock ticks :satisfied: !

Just checking, what is your schedule looking like for adding in the error detection and modifying .pinsRead() for MAX31850s?

danjperron commented 6 years ago

Yes my winter break time was fantastic! I really like Ireland! Right now I'm on six different projects. This one will be the lower priority.

For now you could create a loop function which return an error if the pinsRead() return a None!

I will also push a change on this github to allow very long cable to be read! A long cable means a high capacitance. The readByte() and readBit() functions only wait for 2 us. It will now wait for 14 us before reading the bit status. I don't know if that will perturbate the MAX31850.

I will order a MAX31850 from Adafruit but it could take a month before I touch the code again. I will push the 14us read bit delay tonight! eastern time.

RpiController commented 6 years ago

Hey Dan, I just now noticed that you updated your code last month. Were you able to add error handling to your python module or get .pinsRead to work with 0x3B family sensors?

danjperron commented 6 years ago

I didn't do anything about error handling or get pinsRead stuff. I just change the code to use /dev/gpiomem instead of /dev/mem. All the changes are on the python and the C source code. No more sudo prefix on command line!

Jmeyering commented 6 years ago

Hi I stopped by to ask a quick question on wiring the MAX31850 with the RPi 3.

I have the MAX31850 wired up as follows GRD->GRD and Data->GPIO4 and a pullup resistor between data and 3v3 on the MAX31850 but when I run

sudo python
import DS18B20
DS18B20.scan(4)

I get an empty array as a result. I'm pretty new to GPIO in general so I was wondering if this setup is incorrect.

Running DS18B20Scan -gpio 4 yields

*** Error Unable to detect Logic level 1. No pull-up ?
Jmeyering commented 6 years ago

I can view the 1wire device within /sys/bus/devices but when I cat w1_slave I get

nothing but 00's across the board

danjperron commented 6 years ago

@Jmeyering The 1Wire GPIO driver ds18b20 has to be removed or not used on the specific GPIO pin. This a user code program and it does everything by itself. Don't forget to add a pull up resistor.

Jmeyering commented 6 years ago

Okay perfect I got everything working. I actually have the opposite question of NCSUphytotron. I can read the sensors fine with the python library but I am unable to read the sensors using the compiled DS18B20V2 binary. I've ran configDS18B20V2 4 but And DS18B20Scan returns my two sensors, however.

$ ./DS18B20V2 4
===
0.000 sec acquisition time = 0.196 sec
GPIO 4 : Bad CRC!
===
danjperron commented 6 years ago

I'm on vacation right now. The python code is more recent and I added the parasite mode. Check the readbyte function for delay change modification.