What methods can I use to return a struct to a Python Ctypes call to the function in a shared object?

StackOverflow https://stackoverflow.com/questions/22443781

  •  15-06-2023
  •  | 
  •  

문제

I have the following C file that I am compiling to a shared object. I then load the .so shared object via ctypes in python. I can call the function from ctypes, and the function prints the correct temp and humidity, however I can't seem to get the struct back from the main code. How can I get the struct back from the C function and how can I retrieve the fields from it within python.

#!/bin/python

from ctypes import *
class HMTEMP(Structure):
 _fields_ = [ ("temp", c_double) , ("humidity", c_double) ]
dhtlib = 'libdht4py.so'
hlibc = CDLL(dhtlib)
HMTEMP = hlibc.readDHT()
print HMTEMP.temp

#define BCM2708_PERI_BASE        0x20000000
#define GPIO_BASE                (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <assert.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <bcm2835.h>
#include <unistd.h>

#define MAXTIMINGS 100

struct DHStruct {    
 double temp;
 double humidity;
}  ;


struct DHStruct readDHT();


int bits[250], data[100];
int bitidx = 0;

struct DHStruct readDHT() {
  bcm2835_init() ; 
  int type = 11 ; 
  int pin = 4 ;
  struct DHStruct dhts;
  int counter = 0;
  int laststate = HIGH;
  int j=0;

  // Set GPIO pin to output
  bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP);

  bcm2835_gpio_write(pin, HIGH);
  usleep(500000);  // 500 ms
  bcm2835_gpio_write(pin, LOW);
  usleep(20000);

  bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_INPT);

  data[0] = data[1] = data[2] = data[3] = data[4] = 0;

  // wait for pin to drop?
  while (bcm2835_gpio_lev(pin) == 1) {
    usleep(1);
  } //while

  // read data!
  for (int i=0; i< MAXTIMINGS; i++) {
    counter = 0;
    while ( bcm2835_gpio_lev(pin) == laststate) {
    counter++;
    //nanosleep(1);     // overclocking might change this?
        if (counter == 1000)
      break;
    }//while
    laststate = bcm2835_gpio_lev(pin);
    if (counter == 1000) break;
    bits[bitidx++] = counter;

    if ((i>3) && (i%2 == 0)) {
      // shove each bit into the storage bytes
      data[j/8] <<= 1;
      if (counter > 200)
        data[j/8] |= 1;
      j++;
    }//if
  } //for

    dhts.temp = data[2] ;
    dhts.humidity = data[0] ; 
    printf("Temp = %5.2f *C, Hum = %5.2f \%\n", dhts.temp , dhts.humidity );

  return dhts;
}//function
도움이 되었습니까?

해결책

Ok I got it - and using ctypes was very fast. The python code:

#!/bin/python
from ctypes import *
# define the struct and it's fields
class DHStruct(Structure):
    _fields_ = [("temp",c_double),("humidity",c_double)]
#reference the library
dhtlib = CDLL("libdht4py.so")
# set the return type as the object above 
dhtlib.readDHT.restype = POINTER(DHStruct)
# dereference the pointer using ctype's -contents and access the struct fields.
print ( dhtlib.readDHT().contents.temp ,   dhtlib.readDHT().contents.humidity  )

The C code : the key was to convert the function to return a pointer.

    #define BCM2708_PERI_BASE        0x20000000
    #define GPIO_BASE                (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <dirent.h>
    #include <fcntl.h>
    #include <assert.h>
    #include <unistd.h>
    #include <sys/mman.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/time.h>
    #include <bcm2835.h>
    #include <unistd.h>

    #define MAXTIMINGS 100

//define the struct
    struct DHStruct {
     double temp;
     double humidity;
    }  ;


    struct DHStruct *readDHT(); // define the function prototype to return the pointer


    int bits[250], data[100];
    int bitidx = 0;

    //make sure to return a POINTER!!
    struct DHStruct *readDHT() {
      bcm2835_init() ;
      int type = 11 ;
      int pin = 4 ;
      struct DHStruct *dhts; // here is the key - define the pointer to the struct
      int counter = 0;
      int laststate = HIGH;
      int j=0;

      // Set GPIO pin to output
      bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP);

      bcm2835_gpio_write(pin, HIGH);
      usleep(500000);  // 500 ms
      bcm2835_gpio_write(pin, LOW);
      usleep(20000);

      bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_INPT);

      data[0] = data[1] = data[2] = data[3] = data[4] = 0;

      // wait for pin to drop?
      while (bcm2835_gpio_lev(pin) == 1) {
        usleep(1);
      } //while

      // read data!
      for (int i=0; i< MAXTIMINGS; i++) {
        counter = 0;
        while ( bcm2835_gpio_lev(pin) == laststate) {
            counter++;
            //nanosleep(1);         // overclocking might change this?
            if (counter == 1000)
              break;
        }//while
        laststate = bcm2835_gpio_lev(pin);
        if (counter == 1000) break;
        bits[bitidx++] = counter;

        if ((i>3) && (i%2 == 0)) {
          // shove each bit into the storage bytes
          data[j/8] <<= 1;
          if (counter > 200)
            data[j/8] |= 1;
          j++;
        }//if
      } //for

            dhts->temp = data[2] ;
            dhts->humidity = data[0] ;
   //for debug  printf("Temp = %5.2f *C, Hum = %5.2f \%\n", dhts->temp , dhts->humidity );

      return dhts;
    }//function

다른 팁

To combine C/C++ and Python I would recommend to use Cython. With Cython you are able to pass objects (eg. numpy arrays) to C/C++, fill it with your data and get it back to your python-code.


Here is a minmal example:

The C-skript: (c_example.c)

#include <stdlib.h>
#include <math.h>

void c_claculate(double *x, int N) {
  int i;
  for (i = 0; i<N;i++) {
    x[i]+=i*i;
  }
}

The python-skript: (example.py)

from numpy import *
from example import *

data=zeros(10)
calculate(data)
print data

The .pyx file: (example.pyx)

import cython
import numpy
cimport numpy

# declare the interface to the C code
cdef extern void c_claculate(double *x, int N)

# Cython interface to C function
def calculate(numpy.ndarray[double, ndim=1, mode='c'] x not None):
    cdef int N = x.shape[0]
    c_claculate(&x[0],N)
    return x

and the setup file: (setup.py)

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
import numpy

setup(
    cmdclass = {'build_ext': build_ext},
    ext_modules = [
        Extension("example",
                  sources=["example.pyx", "c_example.c"],
                  include_dirs=[numpy.get_include()]
                  )
    ],
)

Now you can compile the skript by running

python setup.py build_ext -fi

and then execute the python skript.


Cython should be available via pip on your PI.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top