albert einstein
Posts: 7
Joined: Fri Dec 18, 2015 1:25 pm

Re: DS18B20 Low Refresh Rate

Sat Dec 26, 2015 4:05 pm

Is all your sensors at the same tiny location?
10 meters. And we have analogue sensors in the same location, so wiring is not a problem.
Did you ever consider using some little microcomputer which could send the data back to the Raspberry PI using RS-485.
I want to ensure high reliability of communication, as sensors regulate temperature in essential part of production process.

We had AVR previously,that did everything perfectly, but lack of management forced me to move to raspberry Pi.
This way you should be able to break the 100 sensors per second and have your sensor up to hundred meters away.
I'll have about 40 devices, but the wiring conditions could vary and I had bad experience in connection more than 10 sensors on one wire - sometimes connection drops and shit happens. And connection are done in parralel - i.e 40 sensors * 5-15meters for each sensor = 400 meters of cable. And each part of wire could have different capacitance.
So i wanted to connect about 10 sensors on each pin to ensure low capacitance of the 1-wire bus.
The other approach is multiple Raspberry PI with simple UDP packet server. This works quite well and this way you just double or triple the number of sensors you could read and it is very simple to implement in python.
It means that I'd need additional UPS for network equipment.
dsth_list and dsht_list2 give the same output, but dsth_list and w1_list don't.
i mean gives the same error :)

danjperron
Posts: 3031
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: DS18B20 Low Refresh Rate

Sat Dec 26, 2015 4:57 pm

It means that I'd need additional UPS for network equipment.
Not necessarily connect both Raspberry Pi to the same power source. Add another RPi with the same power ups.

I assume that the UPS has enough power for both RPis.

albert einstein
Posts: 7
Joined: Fri Dec 18, 2015 1:25 pm

Re: DS18B20 Low Refresh Rate

Sun Dec 27, 2015 8:56 am

Ok, I'll post this thing when will have more time available.

Thanks for mentioning user-space code, I've fixed timings in it and now it works very nice :)


Btw. Here's simplest possible user-space python wrapper for the danjperron's code for Raspberry 2. Changed delays and GPIO address unputs.

For other Raspberries change GPIO_BASE address, for example for B+ it is 0x20200000.
For my proj, i've created separate r2_ds18b20.c and r1_ds18b20.c modules and dynamically load the needed in Python, so I could use the same SD Card on both B2 and B+ devices.

Code: Select all

import platform
if platform.machine() == "armv7l": #Rpi 2
    print("RASPBERRY 2") 
    import r2_ds18b20 as ds18b20
elif platform.machine() == "armv6l": #Rpi B+
    print("RASPBERRY A+ or B or B+") 
    import r1_ds18b20 as ds18b20
else:
    print("UNSUPPORTED PROCESSOR ON PI BOARD!", GPIO.RPI_INFO)
r2_ds18b20.c

Code: Select all

//  24 May 2014
//  Daniel Perron
//
// Use At your own risk

// 26 Dec 2015
// Gladkikh Artem
// Python Wrapper

#define GPIO_BASE                0x20200000 /* GPIO controller */
//#define GPIO_BASE                0x3f200000 /* GPIO controller */

#include <python2.7/Python.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <sched.h>

#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)

int  mem_fd;
void *gpio_map;

// I/O access
volatile unsigned *gpio;


// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y)
#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
#define OUT_GPIO(g) *(gpio+((g)/10)) |=  (1<<(((g)%10)*3))
#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))

#define GPIO_SET *(gpio+7)  // sets   bits which are 1 ignores bits which are 0
#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0

#define GPIO_READ(g)  (*(gpio + 13) &= (1<<(g)))


#define DS18B20_SKIP_ROM 		0xCC
#define DS18B20_CONVERT_T 		0x44
#define DS18B20_MATCH_ROM               0x55
#define DS18B20_SEARCH_ROM		0XF0
#define DS18B20_READ_SCRATCHPAD         0xBE
#define DS18B20_WRITE_SCRATCHPAD        0x4E
#define DS18B20_COPY_SCRATCHPAD         0x48


unsigned char ScratchPad[9];
double  temperature;
int   resolution;

void setup_io();


unsigned short ArgResolution=0;
unsigned short ArgScan=0;
unsigned short ArgFile=0;
unsigned short ArgWaitTime=750;
char FileName[256];

int  DoReset(unsigned short bus_pin)
{
    int loop;
    
    INP_GPIO(bus_pin);
    
    
    DelayMicrosecondsNoSleep(10);
    
    INP_GPIO(bus_pin);
    OUT_GPIO(bus_pin);
    
    // pin low for 480 us
    GPIO_CLR=1<<bus_pin;
    DelayMicrosecondsNoSleep(480);
    INP_GPIO(bus_pin);
    DelayMicrosecondsNoSleep(60);
    if(GPIO_READ(bus_pin)==0)
    {
        DelayMicrosecondsNoSleep(420);
        return 1;
    }
    return 0;
}

#define DELAY1US  DelayMicrosecondsNoSleep(1);


volatile int loop3;
void  smalldelay(void)
{
    //DelayMicrosecondsNoSleep(1);
    volatile int loop2; 
    for(loop2=0;loop2<300; loop2++);
}

void DelayMicrosecondsNoSleep (int delay_us)
{
	long int start_time;
	long int time_difference;
	struct timespec gettime_now;

	clock_gettime(CLOCK_REALTIME, &gettime_now);
	start_time = gettime_now.tv_nsec;		//Get nS value
	while (1)
	{
		clock_gettime(CLOCK_REALTIME, &gettime_now);
		time_difference = gettime_now.tv_nsec - start_time;
		if (time_difference < 0)
			time_difference += 1000000000;				//(Rolls over every 1 second)
		if (time_difference > (delay_us * 1000))		//Delay for # nS
			break;
	}
}



void WriteByte(unsigned char value, unsigned short bus_pin)
{
    unsigned char Mask=1;
    int loop;
    
    for(loop=0;loop<8;loop++)
    {
        INP_GPIO(bus_pin);
        OUT_GPIO(bus_pin);
        GPIO_CLR= 1 <<bus_pin;
        
        if((value & Mask)!=0)
        {
            DELAY1US
            
            INP_GPIO(bus_pin);
            DelayMicrosecondsNoSleep(60);
            
        }
        else
        {
            DelayMicrosecondsNoSleep(60);
            INP_GPIO(bus_pin);
            DelayMicrosecondsNoSleep(1);
        }
        Mask*=2;
        DelayMicrosecondsNoSleep(60);
    }
    
    
    DelayMicrosecondsNoSleep(100);
}

void WriteBit(unsigned char value, unsigned short bus_pin)
{
    INP_GPIO(bus_pin);
    OUT_GPIO(bus_pin);
    GPIO_CLR=1 <<bus_pin;
    if(value)
    {
        DELAY1US
        INP_GPIO(bus_pin);
        DelayMicrosecondsNoSleep(60);
    }
    else
    {
        DelayMicrosecondsNoSleep(60);
        INP_GPIO(bus_pin);
        DelayMicrosecondsNoSleep(1);
    }
    DelayMicrosecondsNoSleep(60);
}





unsigned char ReadBit(unsigned short bus_pin)
{
    unsigned char rvalue=0;
    INP_GPIO(bus_pin);
    OUT_GPIO(bus_pin);
    // PIN LOW
    GPIO_CLR= 1 << bus_pin;
    DELAY1US
    // set INPUT
    INP_GPIO(bus_pin);
    DELAY1US
    DELAY1US
    DELAY1US
    if(GPIO_READ(bus_pin)!=0)
    rvalue=1;
    DelayMicrosecondsNoSleep(60);
    return rvalue;
}

unsigned char ReadByte(unsigned short bus_pin)
{
    
    unsigned char Mask=1;
    int loop;
    unsigned  char data=0;
    
    int loop2;
    
    
    for(loop=0;loop<8;loop++)
    {
        //  set output
        INP_GPIO(bus_pin);
        OUT_GPIO(bus_pin);
        //  PIN LOW
        GPIO_CLR= 1<<bus_pin;
        DELAY1US
        //  set input
        INP_GPIO(bus_pin);
        // Wait  2 us
        DELAY1US
        DELAY1US
        DELAY1US
        if(GPIO_READ(bus_pin)!=0)
        data |= Mask;
        Mask*=2;
        DelayMicrosecondsNoSleep(60);
    }
    
    return data;
}



int ReadScratchPad(unsigned short bus_pin)
{
    int loop;
    
    WriteByte(DS18B20_READ_SCRATCHPAD, bus_pin);
    for(loop=0;loop<9;loop++)
    {
        ScratchPad[loop]=ReadByte(bus_pin);
    }
}

unsigned char  CalcCRC(unsigned char * data, unsigned char  byteSize)
{
    unsigned char  shift_register = 0;
    unsigned char  loop,loop2;
    char  DataByte;
    
    for(loop = 0; loop < byteSize; loop++)
    {
        DataByte = *(data + loop);
        for(loop2 = 0; loop2 < 8; loop2++)
        {
            if((shift_register ^ DataByte)& 1)
            {
                shift_register = shift_register >> 1;
                shift_register ^=  0x8C;
            }
            else
            shift_register = shift_register >> 1;
            DataByte = DataByte >> 1;
        }
    }
    return shift_register;
}

char  IDGetBit(unsigned long long *llvalue, char bit)
{
    unsigned long long Mask = 1ULL << bit;
    
    return ((*llvalue & Mask) ? 1 : 0);
}


unsigned long long   IDSetBit(unsigned long long *llvalue, char bit, unsigned char newValue)
{
    unsigned long long Mask = 1ULL << bit;
    
    if((bit >= 0) && (bit < 64))
    {
        if(newValue==0)
        *llvalue &= ~Mask;
        else
        *llvalue |= Mask;
    }
    return *llvalue;
}


void SelectSensor(unsigned  long long ID, unsigned short bus_pin)
{
    int BitIndex;
    char Bit;
    
    
    WriteByte(DS18B20_MATCH_ROM,bus_pin);
    
    for(BitIndex=0;BitIndex<64;BitIndex++)
    WriteBit(IDGetBit(&ID,BitIndex), bus_pin);
    
}

int  SearchSensor(unsigned long long * ID, int * LastBitChange, unsigned short bus_pin)
{
    int BitIndex;
    char Bit , NoBit;
    
    
    if(*LastBitChange <0) return 0;
    
    // Set bit at LastBitChange Position to 1
    // Every bit after LastbitChange will be 0
    
    if(*LastBitChange <64)
    {
        
        IDSetBit(ID,*LastBitChange,1);
        for(BitIndex=*LastBitChange+1;BitIndex<64;BitIndex++)
        IDSetBit(ID,BitIndex,0);
    }
    
    *LastBitChange=-1;
    
    if(!DoReset(bus_pin)) return -1;
    
    
    WriteByte(DS18B20_SEARCH_ROM,bus_pin);
    
    for(BitIndex=0;BitIndex<64;BitIndex++)
    {
        NoBit = ReadBit(bus_pin);
        Bit = ReadBit(bus_pin);
        
        if(Bit && NoBit)
        return -2;
        
        if(!Bit && !NoBit)
        {
            // ok 2 possibilities
            //          printf("B");
            if(IDGetBit(ID,BitIndex))
            {
                // Bit High already set 
                WriteBit(1,bus_pin);
            }
            else
            {
                // ok let's try LOW value first
                *LastBitChange=BitIndex;
                WriteBit(0,bus_pin);
            }
        }
        else if(!Bit)
        { 
            //	printf("1");
            WriteBit(1,bus_pin);
            IDSetBit(ID,BitIndex,1);
        }
        else
        {
            //printf("0");
            WriteBit(0,bus_pin);
            IDSetBit(ID,BitIndex,0);
        }
        //   if((BitIndex % 4)==3)printf(" ");
    }
    //
    // printf("\n");
    return 1;
    
    
    
}







float ReadSensor(unsigned long long ID, unsigned short bus_pin)
{
    int RetryCount;
    int loop;
    unsigned char  CRCByte;
    union {
        short SHORT;
        unsigned char CHAR[2];
    }IntTemp;
    
    
    time_t t = time(NULL);
    struct tm tm = *localtime(&t);
    
    temperature=-9999.9;
    
    for(RetryCount=0;RetryCount<10;RetryCount++)
    {
        if(!DoReset(bus_pin)) continue;

        // start a conversion
        SelectSensor(ID,bus_pin);
        
        if(!ReadScratchPad(bus_pin))
        {
            printf("can't read");
            continue;
        }
        
             for(loop=0;loop<9;loop++)
               printf("%02X ",ScratchPad[loop]);
             printf("\n");fflush(stdout);
        
        // OK Check sum Check;
        CRCByte= CalcCRC(ScratchPad,8);
        
        if(CRCByte!=ScratchPad[8])
        {
            printf("CRC error");
            continue;
        }
        
        //Check Resolution
        resolution=0;
        switch(ScratchPad[4])
        {
            
            case  0x1f: resolution=9;break;
            case  0x3f: resolution=10;break;
            case  0x5f: resolution=11;break;
            case  0x7f: resolution=12;break;
        }
        
        if(resolution==0)
        {
           //printf("no resolution");
            continue;
        }
        // Read Temperature
        
        IntTemp.CHAR[0]=ScratchPad[0];
        IntTemp.CHAR[1]=ScratchPad[1];
        
        
        temperature =  0.0625 * (double) IntTemp.SHORT;
        
        ID &= 0x00FFFFFFFFFFFFFFULL;
        //printf("%02llX-%012llX : ",ID & 0xFFULL, ID >>8);
        
        printf("%02d bits  Temperature: %6.2f +/- %4.2f Celsius\n", resolution ,temperature, 0.0625 * (double)  (1<<(12 - resolution)));
        
        break;
    }
    
        return temperature;
    
}



int GlobalStartConversion(unsigned short bus_pin)
{
    DoReset(bus_pin);
    WriteByte(DS18B20_SKIP_ROM,bus_pin);
    WriteByte(DS18B20_CONVERT_T,bus_pin);
    return 1;
}


void WriteScratchPad(unsigned char TH, unsigned char TL, unsigned char config, unsigned short bus_pin)
{
    
    // First reset device
    
    DoReset(bus_pin);
    
    DelayMicrosecondsNoSleep(10);
    // Skip ROM command
    WriteByte(DS18B20_SKIP_ROM,bus_pin);
    
    
    // Write Scratch pad
    
    WriteByte(DS18B20_WRITE_SCRATCHPAD,bus_pin);
    
    // Write TH
    
    WriteByte(TH,bus_pin);
    
    // Write TL
    
    WriteByte(TL,bus_pin);
    
    // Write config
    
    WriteByte(config,bus_pin);
}


void  CopyScratchPad(unsigned short bus_pin)
{
    
    // Reset device
    DoReset(bus_pin);
    DelayMicrosecondsNoSleep(1000);
    
    // Skip ROM Command
    
    WriteByte(DS18B20_SKIP_ROM,bus_pin);
    
    //  copy scratch pad
    
    WriteByte(DS18B20_COPY_SCRATCHPAD,bus_pin);
    DelayMicrosecondsNoSleep(100000);
}



static unsigned long long sensors_arr[64];
static float sensors_vals[64];

void ScanForSensor(unsigned short bus_pin)
{
    unsigned long long  ID=0ULL;
    int  NextBit=64;
    int  _NextBit;
    int  rcode;
    int retry=0;
    int sensor_counter = 0;
    float temper;
    unsigned long long  _ID;
    unsigned char  _ID_CRC;
    unsigned char _ID_Calc_CRC;
    unsigned char  _ID_Family;
    
    int i;
    for(i = 0; i < 64; i++) sensors_arr[i] = 0;  //clear sensors array
    
    while(retry<30){
        _ID=ID;
        _NextBit=NextBit;
        rcode=SearchSensor(&_ID,&_NextBit,bus_pin);
        if(rcode==1)
        {
            _ID_CRC =  (unsigned char)  (_ID>>56);
            _ID_Calc_CRC =  CalcCRC((unsigned char *) &_ID,7);
            if(_ID_CRC == _ID_Calc_CRC)
            {
                printf("found%ull \n", _ID);
                temper = ReadSensor(_ID,bus_pin);
               if(temper > -65.5)
                {
                    sensors_vals[sensor_counter] = temper;
                    sensors_arr[sensor_counter++] = _ID;
                    ID=_ID;
                    NextBit=_NextBit;
                    retry=0;
                }
            }
            else 
            { 
                retry++;
            }
        }
        else if(rcode==0 )
        break;
        else
        retry++;
    }
}



// Adafruit   set_max_priority and set_default priority add-on

void set_max_priority(void) {
    struct sched_param sched;
    memset(&sched, 0, sizeof(sched));
    // Use FIFO scheduler with highest priority for the lowest chance of the kernel context switching.
    sched.sched_priority = sched_get_priority_max(SCHED_FIFO);
    sched_setscheduler(0, SCHED_FIFO, &sched);
}

void set_default_priority(void) {
    struct sched_param sched;
    memset(&sched, 0, sizeof(sched));
    // Go back to default scheduler with default 0 priority.
    sched.sched_priority = 0;
    sched_setscheduler(0, SCHED_OTHER, &sched);
}

//
// Set up a memory regions to access GPIO
//
void setup_io()
{
    /* open /dev/mem */
    if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
        printf("can't open /dev/mem \n");
        exit(-1);
    }
    
    /* mmap GPIO */
    gpio_map = mmap(
    NULL,             //Any adddress in our space will do
    BLOCK_SIZE,       //Map length
    PROT_READ|PROT_WRITE|PROT_EXEC,// Enable reading & writting to mapped memory
    MAP_SHARED,       //Shared with other processes
    mem_fd,           //File to map
    GPIO_BASE         //Offset to GPIO peripheral
    );
    
    close(mem_fd); //No need to keep mem_fd open after mmap
    
    if (gpio_map == MAP_FAILED) {
        printf("mmap error %d\n", (int)gpio_map);//errno also set!
        exit(-1);
    }
    
    // Always use volatile pointer!
    gpio = (volatile unsigned *)gpio_map;
    
    
} // setup_io



static PyObject *ds18b20Error;

static PyObject *
py_search(PyObject *self, PyObject *args)
{
    int bus_pin;
    int i;
    
    if (!PyArg_ParseTuple(args, "i", &bus_pin))
    {
        PyErr_SetString(ds18b20Error, "integer GPIO should be provided.");
        return NULL;
    }    
    
    PyObject * dict = Py_BuildValue("{}");
    ScanForSensor(bus_pin);
    for(i = 0; i < 64; i++)
    {
        if(sensors_arr[i] != 0)
        {
            PyDict_SetItem(dict, Py_BuildValue("l", sensors_arr[i]), 
                                 Py_BuildValue("f", sensors_vals[i]));        
        }
    }
    return dict;
}

static PyObject *
py_convert(PyObject *self, PyObject *args)
{
    int bus_pin;
    unsigned long long id;
    int i;
    
    if (!PyArg_ParseTuple(args, "i", &bus_pin))
    {
        PyErr_SetString(ds18b20Error, "integer GPIO should be provided.");
        return NULL;
    }
    
    GlobalStartConversion(bus_pin);
    Py_INCREF(Py_None);
    return Py_None;
}


static PyMethodDef r1_ds18b20_funcs[] = {
    {"search", (PyCFunction)py_search, 
     METH_VARARGS, "search sensors on given GPIO"},
    {"convert", (PyCFunction)py_convert, 
     METH_VARARGS, "starts global conversion on given GPIO"},
    {NULL}
};

void initr1_ds18b20(void)
{
    setup_io();
    PyObject *m;
    m = Py_InitModule3("r1_ds18b20", r1_ds18b20_funcs,
                   "Extension module example!");
                   
                   
    ds18b20Error = PyErr_NewException("ds18b20.error", NULL, NULL);
    Py_INCREF(ds18b20Error);
    PyModule_AddObject(m, "error", ds18b20Error);
}
/*

static PyObject *
py_read(PyObject *self, PyObject *args)
{
    int gpio;
    
    if (!PyArg_ParseTuple(args, "i", &gpio))
        PyErr_SetString(ds18b20Error, "integer GPIO should be provided.");
            return NULL;

    return Py_BuildValue("i", 2);
    //Py_INCREF(Py_None);
    //return Py_None;
}

static PyObject *
py_convert(PyObject *self, PyObject *args)
{
    int gpio;
    
    if (!PyArg_ParseTuple(args, "i", &gpio))
        PyErr_SetString(ds18b20Error, "integer GPIO should be provided.");
            return NULL;

    return Py_BuildValue("i", 2);
    //Py_INCREF(Py_None);
    //return Py_None;
}




static PyMethodDef ds18b20Methods[] = {
    {"search",  py_search, METH_VARARGS,
     "Search sensors on given GPIO"},
    //{"convert", py_convert, METH_VARARGS,
    // "Search sensors on given GPIO"},
    //{"read",  py_read, METH_VARARGS,
//    "Read sensor with given ID on given GPIO"},
    {NULL}    
};

PyMODINIT_FUNC
initr1_ds18b20(void)
{

    m = Py_InitModule("ds18b20", ds18b20Methods);
    if (m == NULL)
        return;

        
    
}
*/
and Setup script:

setup.py

Code: Select all

from distutils.core import setup, Extension

module2 = Extension('r2_ds18b20',
                    libraries= ['rt'],
                    sources = ['r2_ds18b20.c'])
                    
setup (name = 'r2_ds18b20',
       version = '1.0',
       description = 'This is a r2_ds18b20 package',
       ext_modules = [module2])
       

you'd need to run "sudo python setup.py install"

Code: Select all

import r2_ds18b20
while True:
    r2_ds18b20.convert(pin)
    time.sleep(0.750)
    for id, temp in r2_ds18b20.search(pin).iteritems():
        print("temp:", hex(id & (2**32-1)))       

danjperron
Posts: 3031
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: DS18B20 Low Refresh Rate

Sun Dec 27, 2015 1:00 pm

Thank's Albert Einsten to upgrade my code.

looks good.

Too bad that I don't use ds18B20 on my Pi anymore. All on RF module with arduino! ;-)

Daniel

viper_lasser
Posts: 2
Joined: Sat Sep 28, 2013 12:24 pm

Re: DS18B20 Low Refresh Rate

Tue Jun 20, 2017 8:52 am

I have little problem.
When I have configured 10 sensor and all are connected property then all values are displayed ok.
But when i.e one sensor are disconnected and I tried to read 10 sensor then all values are displayed as 125C.
I run program on RPI v1.

Second question: Will this program run on orangepi pc ?

NCSUphytotron
Posts: 1
Joined: Sun Aug 06, 2017 10:54 pm

Re: DS18B20 Low Refresh Rate

Mon Aug 07, 2017 3:09 pm

Hello Albert, danjperron, and pstolarz! I've enjoyed reading your posts on this topic. I'm glad some smart people like you dug into this issue. I know it's been a couple years since you've worked on this, but even now in August 2017 there still haven't been any updates to the 1-wire libraries to make interfacing with these temperature sensors any easier or versatile on a Raspberry Pi.

I work in research lab where we are building our own controllers with Raspberry Pi 2 Model B's to control temperature, humidity, CO2, etc in our growth chambers. We are using 2 types of sensors... the MAX31850 thermocouple, and of course the DS18B20... both of which use the 1-wire protocol. We have the sensors mounted in various parts of the chamber (the lightcap, the chilled water supply & return, the plenum where the air is heated and cooled, and within the chamber itself.) To maintain precise control we need the response time on the sensors to be as quick as possible.

The MAX31850's are hardcoded into a .25C resolution and a response/conversion time of 100ms. As you know, and as is the whole purpose of this discussion thread, a lower response time is of no use with the w1_therm kernel module locked in at a cycle time of 750ms. So for a while now, we've been using the solution found here and here to change the hard-coded cycle to ~150ms which involves modifying and recompiling the kernel... and which is not the easiest or quickest thing in the world to do. This method is an improvement, but it still only reads and updates the sensors one at a time so that the refresh time on any one sensor is still the cumulative sum of all the sensors in the cycle.

I looked into the option of enabling multiple gpios as 1-wire inputs which I have been successful at doing, but the downside is that I would like to minimize the number of occupied gpios so they can be free to use for other things such as controlling the lighting. And this method would still involve the dreaded kernel recompiling in order to get a faster than 750ms response time on each pin.
Interestingly pstolarz made a post in a different discussion thread on this topic where he says...
if you are still interested in setting w1 on multiple buses check out my w1-gpio alternative:
https://github.com/pstolarz/w1-gpio-cl
No DT patching, kernel ver dependencies or other nuisance. Simply insmod and wroks. Full parasire power support w/o any touch of DT.

Enjoy
But I have no idea what "simply insmod" means or how to use the materials he has at that link. It looks like his method still involves messing around with the kernel. I'm not a complete noob in working with software, but my knowledge is pretty much limited to python code and basic linux commands. If pstolarz is still monitoring this discussion, I would like to ask him if he could explain in a step by step way with examples how to use his method. I would similarly like to ask pstolarz how to use the code he talked about in his earlier post in this discussion thread...
Good new - there is already existing userland API dedicated for 1-wire communication. Look at "librasp" library
https://github.com/pstolarz/librasp

The library provides simple C API to reach for the full power over 1-wire buse (see /w1.h header for API spec). As en example of usage there is provided API to talk with Dallas family of sensor (look at /inc/librasp/devices/ds_therm.h for details).

Finally look at two examples in /examples/dsth_list.c and /examples/dsth_list2.c. The 1st eample probes DS sensors one by one (as in the problematic case), the second one uses yor approach (one write many reads). Additionally both examples are able to scan the platform agains installed w1 bus masters, search them for installed DS sensors and probe their temperature. All in just few lines of code. If you want to check them make sure you have loaded wire and w1_gpio modules, unloaded w1_therm + w1 enabled in the DT of course.

Enjoy.
Recently I have finally figured out how to get danjperron's code working with PERI_BASE=0x3F000000 and it actually works quite well. Seeing as how I only know python, here's what I had to do for it to be useful... I had to edit the DS18B20Scan.c file so that it will display 4 digit precision decimals (Temperature: %6.4f +/- %4.4f Celsius\n") then I use a subprocess.check_output command to get the results into the python work space.

Code: Select all

Ttext = subprocess32.check_output(['sudo','/ChamberData/DS18B20Scan', '-gpio', '23'],timeout=3).strip().split('\n')
for i in range(len(Ttext)):
       OWtemps[i]=float(Ttext[i].split()[5])
This seems like kind of a hacky way to do it, and I'd like to know if anyone knows of a smoother way to get the temperatures into the python workspace. (In the example above, the DS18B20Scan executable file is located in folder named ChamberData)

Finally, I tried to use Albert's code that he shared in his last post. I changed the GPIO_BASE as directed, but there were errors in building the extension until I changed some other things in the r2_ds18b20.c code. Apparently he forgot to change "r1_ds18b20" to "r2_ds18b20" in the init section at the end of the file. Also I discovered I needed to install the python-dev package in order to build the extension. But even after doing all of that, python will still lock up when it gets to the r2_ds18b20.search command in his example... I'm guessing there's something else he overlooked in the r2_ds18b20.c file that he posted.

In conclusion, I would just like to ask any of the geniuses that might still be monitoring this discussion if you could "dumb-down" :) your methods and explain them in the easiest way for noobs like me to be able to follow and implement. As of now, danjperron's Scan code using my hacky method seems like the simplest option to get fast read times on multiple sensors which doesn't involve compiling any kernels, modules, or extensions. However the way pstolarz describes the functionality of his librasp method sounds really good too if I knew how to use it, and I like the prettiness of Albert's idea of making an importable python module.

-JS

danjperron
Posts: 3031
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: DS18B20 Low Refresh Rate

Fri Dec 15, 2017 12:28 pm

OK I update my Bitbanging GPIO for DS18B20 by including a python Module.

Everything is on user mode space.

This is a beta version. I'm not sure if all the PY_DECREF need to be added or removed but essentially everything works!

https://github.com/danjperron/BitBangingDS18B20

I include 2 methods,

The first one,pinsread(), is the fastest because I use parallel in/out on the GPIO. This means that it is one DS18B20 per GPIO and the system use the 'skip rom' comment to disable the serial ID.

The second method is like the 1wire driver but you could select any number of GPIO pins.

The advantage of my module is that you could start a conversion in sync on all the DS18B20 and then read each sensor. This way you have only one conversion to do.

ex: Two DS18B20 on GPIO20 and two DS18B20 on GPIO21

Code: Select all

[email protected]:~ $ cat lireCapteurs.py 
#!/usr/bin/python
# -*- coding: utf-8 -*-
import time
import DS18B20 as DS

# creer un dictionnaire des capteurs
dict = {}

# creer une liste des GPIOs qui ont des capteurs
pinsValide= [ 20 , 21]

# scanner Capteur sur les pins
for pin in pinsValide:
   detecte = DS.scan(pin)
   # ajouter dans dictionaire avec le GPIO
   for i in detecte:
       dict[i]=pin

#demarrer une conversion sur  tout les capteurs sur GPIO 20 et 21
DS.pinsStartConversion(pinsValide)

#attendre 0.75 sec
#12 bits time.sleep(0.75)
#11 bits
#10 bits
#9  bits time.sleep(0.094)
time.sleep(0.75)

#afficher les résultats
for Id,pin in dict.items():
    print("GPIO#{} Capteur:{} = {:0.3f} Celsius".format(pin,Id,DS.read(False,pin,Id)))
and this will output the result in 1.2 seconds

Code: Select all

[email protected]:~ $ time sudo python3 lireCapteurs.py
GPIO#21 Capteur:28-000006F058C9 = 15.875 Celsius
GPIO#21 Capteur:28-0215C26F41FF = 15.812 Celsius
GPIO#20 Capteur:28-000006EF85D6 = 16.000 Celsius
GPIO#20 Capteur:28-800000014CC0 = 15.438 Celsius

real	0m1,287s
user	0m0,430s
sys	0m0,030s

zomberry
Posts: 9
Joined: Sat Dec 23, 2017 10:21 am

Re: DS18B20 Low Refresh Rate

Sat Dec 23, 2017 10:44 am

Hi @danjperron,

I've been trying to run your code on my RPi, using 6 x DS18B20 sensors on a (single) GPIO pin. Unfortunately, it only works if just one sensor is left in the circuit. As long as I connect the other sensors, I get a "000000000000" when I run a search and no response at all when I try to get the temperatures.

Anyway, I've been trying to debug/modify your code to find the problem (it might need tweaking some delays) but it was hard to understand your code at the frst glance. ;)

I was reading some Dallas/Microchip app notes regarding software bitbanging and their suggestions (though there's no full code available) are using rather simple timings fo reset/read/write.

Actually, I need a code to perform the following functions: a broadcast command to reset the line, a broadcast command to start conversion (for all sensors at once) and an individual read (scratchpad) for every specific sensor.

That's it, I don't need automatic search & stuff, as I already know all the sensors' IDs.

Could you help me clarify those code blocks to achieve this goal?

Thank you very much for your time (and the good work).

danjperron
Posts: 3031
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: DS18B20 Low Refresh Rate

Sat Dec 23, 2017 7:25 pm

There are 2 modes in the code.

The first one is for a single DS18B20 per GPIO. I hope you didn' t try to put more than one DS18B20 on that mode. This mode is when you are using the pinsRead().
This is the fastest mode since i'm reading all gpios at once.

The other mode will use the serial ID of the sensor instead of the SKIPROM command.


First try to get the serial ID of all your sensors on the GPIO pins

gpiopin=20
sensors = DS18B20.scan(20)

'sensors' should include all the serial Id of your sensors.

Now that 'sensors' array contains the valid serial Id you could specify the system to read the sensor.

for i in sensors:
print(DS18B20.read(True,gpiopin,i))

If you want to read it faster you should just start the conversion for all and read each DS18B20 individually. You will save .75 sec per readings.

pinsStartConversion([gpiopin])
time.sleep(0.75)
for i in sensors:
print(DS18B20.read(False,gpiopin,i))


P.S. you can't use GPIO 4 if 1wire driver is in! Any GPIO need to be in IN/OUT mode.
Also the 4K7 resistor is still needed!

danjperron
Posts: 3031
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: DS18B20 Low Refresh Rate

Sat Dec 23, 2017 7:48 pm

Actually, I need a code to perform the following functions: a broadcast command to reset the line, a broadcast command to start conversion (for all sensors at once) and an individual read (scratchpad) for every specific sensor.

That's it, I don't need automatic search & stuff, as I already know all the sensors' IDs.
The broadcast command to reset and start conversion is

pinsStartConversion([gpio pin list])
time.sleep(0.75)

ex:
import DS18B20 as ds
import time

gpiopin=20
ds.startConversion([gpiopin])
ds.print('Sensor 1 : {}'.format(ds.read(False,gpiopin,'28-000006EF85D6')))
ds.print('Sensor 2 : {}'.format(ds.read(False,gpiopin,'28-800000014CC0')))

if your sensors are on the same gpio you need to read them individually using the serial ID. The broadcast command to read scratch pad doesn't work since they will output data all together.The scan needs to be done once to figure out the serial ID. You could manually insert the serial ID on your code if you know it without using the scan.

danjperron
Posts: 3031
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: DS18B20 Low Refresh Rate

Sat Dec 23, 2017 8:03 pm

Also I forget to specify that my python module is able to read the MAX31850 sensor with the read() function.

When the ID familly is 0x3B the system will output the correct value for the MAX31850.

Also I do have readMax31850() function

zomberry
Posts: 9
Joined: Sat Dec 23, 2017 10:21 am

Re: DS18B20 Low Refresh Rate

Sat Dec 23, 2017 9:06 pm

Thank you very much for your quick response!

I guess I forgot to mention that I'm interested in the c++ (not python) version. Actually, I need to integrate it in a node.js script (which acts as a websocket server).

I'm using the latest code version from your github and I believe(?) it's the multi-drop one. Anyway, if I let a single sensor in place, it works ok. If I let all (5) of them, I get a "000000000000" search result.

For now, I'm using the raspbian "built-in" kernel module (on gpio 4) and it works fine (but very slow). Btw, I'm running your code on pin 4 (while the kernel module is still active) it works! (with just one sensor though)

I have tried with the kernel mod disabled, using a different gpio pin and so on but I was getting the same result (it works with one sensor only).

Could you post a complete code with just two options (arguments): a broadcast "begin conversion" command and a per ID "read scratchpad" one? I only need these two functions.

Thank you very much for your time.

zomberry
Posts: 9
Joined: Sat Dec 23, 2017 10:21 am

Re: DS18B20 Low Refresh Rate

Sat Dec 23, 2017 9:56 pm

danjperron wrote:
Sat Dec 23, 2017 7:48 pm
gpiopin=20
ds.startConversion([gpiopin])
ds.print('Sensor 1 : {}'.format(ds.read(False,gpiopin,'28-000006EF85D6')))
ds.print('Sensor 2 : {}'.format(ds.read(False,gpiopin,'28-800000014CC0')))
That's exactly what I need but in C language (to get a faster binary).

User avatar
DougieLawson
Posts: 33787
Joined: Sun Jun 16, 2013 11:19 pm
Location: Basingstoke, UK
Contact: Website

Re: DS18B20 Low Refresh Rate

Sat Dec 23, 2017 10:22 pm

zomberry wrote:
Sat Dec 23, 2017 9:56 pm
danjperron wrote:
Sat Dec 23, 2017 7:48 pm
gpiopin=20
ds.startConversion([gpiopin])
ds.print('Sensor 1 : {}'.format(ds.read(False,gpiopin,'28-000006EF85D6')))
ds.print('Sensor 2 : {}'.format(ds.read(False,gpiopin,'28-800000014CC0')))
That's exactly what I need but in C language (to get a faster binary).
http://bradsmc.blogspot.co.uk/2014/06/c ... b20-1.html
has a minor bug (because it's written for a BeagleBone) in it, but appears to work with my DS18B20.
Microprocessor, Raspberry Pi & Arduino Hacker
Mainframe database troubleshooter
MQTT Evangelist
Twitter: @DougieLawson

2012-18: 1B*5, 2B*2, B+, A+, Z, ZW, 3Bs*3, 3B+

Any DMs sent on Twitter will be answered next month.

zomberry
Posts: 9
Joined: Sat Dec 23, 2017 10:21 am

Re: DS18B20 Low Refresh Rate

Sat Dec 23, 2017 11:03 pm

Unfortunately, that BB implementation it's based on the 1-wire kernel driver (it looks for the /sys/bus/w1/devices) hence it won't be any faster (I'm currently using a similar node.js package).

@danjperron has a much cleaner implementation but I'm still a little bit confused about the right way to filter the unwanted pieces of code.

I need a bug free implementation as I can't afford an OS (or the program itself) crash - I'm running some sensitive apps on that RPi.

User avatar
DougieLawson
Posts: 33787
Joined: Sun Jun 16, 2013 11:19 pm
Location: Basingstoke, UK
Contact: Website

Re: DS18B20 Low Refresh Rate

Sat Dec 23, 2017 11:36 pm

Try it. Wrap the call to the python, node and C programs with a time command.
Microprocessor, Raspberry Pi & Arduino Hacker
Mainframe database troubleshooter
MQTT Evangelist
Twitter: @DougieLawson

2012-18: 1B*5, 2B*2, B+, A+, Z, ZW, 3Bs*3, 3B+

Any DMs sent on Twitter will be answered next month.

danjperron
Posts: 3031
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: DS18B20 Low Refresh Rate

Sun Dec 24, 2017 4:08 am

That's exactly what I need but in C language (to get a faster binary)
If you look inside my python folder there is a C file DS18B20.c.
In reality the code is in C.

You just need to reformulate some of the functions by removing the python calls arguments.

After the holidays I will make a C++ class.

zomberry
Posts: 9
Joined: Sat Dec 23, 2017 10:21 am

Re: DS18B20 Low Refresh Rate

Sun Dec 24, 2017 10:15 am

Many thanks for your kind support. Merry Christmas, everyone!
danjperron wrote:
Sat Dec 23, 2017 7:48 pm
if your sensors are on the same gpio you need to read them individually using the serial ID. You could manually insert the serial ID on your code if you know it without using the scan.
How to convert the sensor hex ID (like "28-000006EF85D6") to a long long value (used in ReadSensor function, by example)?

zomberry
Posts: 9
Joined: Sat Dec 23, 2017 10:21 am

Re: DS18B20 Low Refresh Rate

Sun Dec 24, 2017 10:22 am

DougieLawson wrote:
Sat Dec 23, 2017 11:36 pm
Try it. Wrap the call to the python, node and C programs with a time command.
Ok, both Python and JS are interpreted languages hence there should be no difference (performance wise) in this particular application.

But, then again, that implementation still makes use of the kernel driver thus it has no other option but to call the temperature convert function (for one sensor at a time) when it's doing a temperature reading.

zomberry
Posts: 9
Joined: Sat Dec 23, 2017 10:21 am

Re: DS18B20 Low Refresh Rate

Sun Dec 24, 2017 12:58 pm

zomberry wrote:
Sun Dec 24, 2017 10:15 am
How to convert the sensor hex ID (like "28-000006EF85D6") to a long long value (used in ReadSensor function, by example)?
Got it (from @danjperron python/c source code). But I still can't make it work with more than one sensor.

Actually, I have one separate sensor and a group of 4 other sensors (they are hardwired together) hence I can't run any test with two or three sensor (by example) but only with one or five of them. Btw, they work ok all together with the 1-wire kernel driver.

danjperron
Posts: 3031
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: DS18B20 Low Refresh Rate

Sun Dec 24, 2017 2:37 pm

Ok the python code is a mix of my C code at the root of the github

What about DS18B20Scan? Does it works?
Btw, I'm running your code on pin 4 (while the kernel module is still active) it works! (with just one sensor though)
It's not suppose to work. You are lucky! you ran the code in between two readouts!
using a different gpio pin and so on but I was getting the same result (it works with one sensor only).
This is a beta version I know it works with the original PIB and the Pi2. I will test the Pi3 more it will take times because I'm leaving for a week in Ireland. Vacation times!

What is your Raspberry Pi? Did you increase the clock speed ? How many application your raspberry Pi run ? is it debian?

Yes 'SensorIdToLLong(char * ID)' is the conversion function from Ascii to unsigned long long.

zomberry
Posts: 9
Joined: Sat Dec 23, 2017 10:21 am

Re: DS18B20 Low Refresh Rate

Sun Dec 24, 2017 6:39 pm

I have a RPi 3 running raspbian 8 (jessie). It has no GUI (graphical interface) and I'm mainly running a node.js server (websocket) to broadcast some infos from an arduino-like board connected to one of the RPi USB ports.

For now, I'm testing the 1-wire connection on an identical setup (back-up) but I want to move it on the main RPi, to include the sensor data on that node.js server.

Btw, the 1-wire network is quite large (there's not so many sensors - just 5, for now - but they are placed on various remote locations - the network weight is about 40m or so). I'm using pretty thick wires but I had to use a strong pull-up resistor, too (2.2kOhm) to get a reliable response from all sensors. I'm not using parasite power, I have a dedicated 3.3V power supply.

It might be due to those long distances (reflections, parasitic capacitances) that I'll need to adjust/tweak the 1-wire communication timings (during read/write bits and so on). The only sensor that is working is mounted right across the RPi pins (the other ones, as I've already mentioned, are hardwired and I can't split them for testing).

And no, the DS18B20Scan didn't work either (with all the sensors in place): I got a "0000000000" response.

Anyway, the whole setup is working fine (but slow - 0.8 seconds per sensor) with the 1-wire kernel driver. And you're right, I was confused too that your software works on top of the built-in kernel driver - I guess it runs a similar bitbanging protocol hence both apps could share the same 1-wire network (not simultaneous, of course).

I've read that you were using RF24 boards for your remote sensors and I was (coincidentally) already testing the same setup few weeks ago (though I don't have time for now to replace the wired configuration).

Speaking of vacations, I need to have this sensors up & running in a few days as I'm going to travel abroad (too!) and my boiler furnace control depends on these (room) temperature sensors.

PS: Welcome to Europe! ;)

zomberry
Posts: 9
Joined: Sat Dec 23, 2017 10:21 am

Re: DS18B20 Low Refresh Rate

Sun Dec 24, 2017 9:22 pm

I was playing with the "read bits/bytes" timeslots and I managed to get some output from the remote sensors. The received data has a large degree of inconsistency for now but I think I'll manage to pair the sensors with the right timings.

I don't blame your softwware, anyway. Looks like you have been used the recommended datasheet timings (though their ranges are quite wide) but I have to compensate my (bad) network behaviour (reflections, mismatched impedances, parasitic capacitances and so on) with the appropriate timings..

Anyway, I think I'll finish my "homework" after the holidays (it's almost Christmas Eve here!). I'll rely on the kernel driver for now (it works pretty reliable though - looks like I'll have to put the oscilloscope on the wires to sniff some timings!)

Merry Christmas, once again!

danjperron
Posts: 3031
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: DS18B20 Low Refresh Rate

Mon Dec 25, 2017 2:24 am

I was playing with the "read bits/bytes" timeslots and I managed to get some output from the remote sensors. The received data has a large degree of inconsistency for now but I think I'll manage to pair the sensors with the right timings.
I did check the timing and I think you are on something.

The timing for the read byte should be less than 15 µs to acquire the bit status. Then the 2 µs is maybe way to fast if you have any capacitive load.

Please change it to 14 µs and tell me if it is working!

Code: Select all

void  ReadByte(unsigned long *datatable)
{
   int loop;

   for(loop=0;loop<8;loop++)
     {
       //  set output
       SetOutputMode();
       //  PIN LOW
       GPIO_CLR= PinMask;
       DELAY1US
       //  set input
       SetInputMode();
       // Wait  14 us
       DelayMicrosecondsNoSleep(14);
       *(datatable++)= GPIO_READ;
       DelayMicrosecondsNoSleep(60);
      }
}

Return to “Automation, sensing and robotics”

Who is online

Users browsing this forum: baetis and 8 guests