I've attached a soil hygrometer I bought on ebay to my rpi via an MCP3008 ADC. All I want is a small program that can spit out the current value as an integer. I don't want to use Python...
I must emphasize that I'm not a C programmer at all, so I'm pretty much stumbling in the dark here.
My problem is that my program's output varies a lot. The value varies by about 20 every time I run it. If I use the Python implementation of SPI that everyone refer to I get a pretty stable value. Also, it's a little higher than the largest value my C program returns.
Here's my code:
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#define ARRAY_SIZE(array) sizeof(array)/sizeof(array[0])
static const char *device = "/dev/spidev0.0";
static uint8_t mode = SPI_MODE_0;
static uint8_t bits = 8;
static uint32_t speed = 1000000;
static uint16_t delay = 0;
static int prepare(int fd) {
int ret;
if ((ret = ioctl(fd, SPI_IOC_WR_MODE, &mode)) == -1) {
perror("Can't set mode");
return ret;
}
if ((ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits)) == -1) {
perror("Can't set number of bits");
return ret;
}
if ((ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed)) == -1) {
perror("Can't set write speed");
return ret;
}
if ((ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed)) == -1) {
perror("Can't set read speed");
return ret;
}
return 0;
}
static void transfer(int fd, uint8_t tx[], uint8_t rx[]) {
int ret;
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = ARRAY_SIZE(tx),
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret == 1) {
perror("Can't send API message");
abort();
}
printf("%d\n", ((rx[2] & 3) << 8) + rx[3]);
for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {
if (!(ret % 6))
puts("");
printf("%.2X ", rx[ret]);
}
puts("");
}
int main(int argc, char **argv) {
int i,fd;
int ret = 0;
uint8_t wr_buf[]={0b0000, 0b0001, 0b1000, 0b0000, 0b0000, 0b0000, 0b0000, 0b0000};
uint8_t rd_buf[8];
fd = open(device, O_RDWR);
if (fd<=0) {
printf("Device %s not found\n", device);
exit(1);
}
if ((ret = prepare(fd)) == 0)
transfer(fd, wr_buf, rd_buf);
close(fd);
return ret;
}
This is an example of the Python code that works, taken from here
import spidev
spi = spidev.SpiDev()
spi.open(0,0)
def readadc(adcnum):
if ((adcnum > 7) or (adcnum < 0)):
return -1
r = spi.xfer2([1,(8+adcnum)<<4,0])
adcout = ((r[1]&3) << 8) + r[2]
return adcout
value = readadc(0)
Could somebody give me any hints?
I don't want to use Python...
-- what's wrong with python? it works, gives you good results...wr_buf
wrong? After quick look at datasheet I believe You should send 3 bytes of data, not four. Your buffer seems to have proper values if you remove last byte. There is start bit in tx buffer so chances are that this is not a problem for ADC chip but you should change the address inrx[]
buffer from which you read the data (I believe it should berx[1]&3+rx[2]
).