Problems accessing MCP3008 via SPI - Invalid argument

I am glad to see I am not the only one to have problems with mcp3008. I use it to read 4 (or more) earth-humidity sensors in order to control my irrigation system.

I do not understand the answer from “Librecomputer”. Why telling us WHAT NOT to do. Please tell us WHAT TO DO.

It’s a learning curve issue, I think. It would be too much for a forum post.

@librecomputer tends to give very linux stardard advice. I appreciate it, even if it’s not always the easiest path at first. If it’s not in my skill set it’s usually documented somewhere on the interwebs. I figure my Google probably works the same as his.

I usually try to walk both paths. I’ll get things going the hackey circuitpyton way, then refine my approach as I learn. Lately, I’ve been running into issues with Adafruit libraries, Blinka does not work at all on my Odroid, but libgpiod worked flawlessly.

Thanks for the answer. To be honest I don’t know anything about linux. For me a computer is a tool to perform some tasks and I want to get it to solve this tasks in the most easy way.
I am not a programmer, as such, even if I was writing some Assembler programs 54 years ago. And my (limited) knowledge is related to IBM machines.

You have to take a look at how SPI overlays are done. There are numerous examples here: libretech-wiring-tool/libre-computer/aml-s905x-cc/dt at master · libre-computer-project/libretech-wiring-tool · GitHub

You need to clone the repository, write your own overlay, and test it. We use standard Linux mechanism so there’s Linux documentation. We recommend contributing back via a pull request once you have written and tested your overlay.

We understand that not everyone is an embedded engineer but we’re also not embedded consultants who have the bandwidth implement every device. We can only guide you to learn proper methods so that the knowledge you gain is re-usable and not a waste (proprietary) like with other vendors.

Try to decompose an existing overlay so you understand how they are written: libretech-wiring-tool/spicc-ili9486.dts at master · libre-computer-project/libretech-wiring-tool · GitHub

As for the Linux driver, it can be found here: linux/mcp320x.c at master · torvalds/linux · GitHub
You need to use the compatible for the driver.

After you finished, you can access the IIO devices via standard /sys interface for IIO devices.

1 Like

Thanks for the primer. I’ve learned more about the os I daily drive from this sbc than the last 3 desktops I’ve owned. I really appreciate the time you take to point us in the right direction, even if I don’t always listen the first time.

Took some messing around, but this works:

 * Device Tree Overlay to enable MCP3008 ADC


#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/gpio/meson-gxl-gpio.h>

/ {
	compatible = "libretech,cc", "amlogic,s905x", "amlogic,meson-gxl";

	fragment@0 {
		target = <&spicc>;
		__overlay__ {
			status = "okay";
			#address-cells = <1>;
			#size-cells = <0>;

			mcp3008_00: mcp3008@0 {
				compatible = "mcp3008";
				reg = <0>;
				spi-max-frequency = <30000000>;

With it enabled I can cat /sys/bus/iio/devices/iio:device1/in_voltage0_raw
and get a value between 0 and 1024. Tested channel 0 with a 10k pot and a photoresistor, and it works.

Now I’m going to test it a bit more, submit my pull request and then work out some python code. It should be a simple matter of reading the in_voltage0_raw with open().

1 Like

Great job! You should not need this though as spicc is separate enable:

			status = "okay";
			#address-cells = <1>;
			#size-cells = <0>;
1 Like

I’m new to Git, so I hope I did this right.

Here’s the python code to go with it. I’m getting some inaccurate results, but I had my chip wired wrong and it heated up substantially at one point, so that may be the cause of the problem. Also, for some reason, if I supply 5V to any pin, the rest of them read 1023 as well. Otherwise, it seems to work. The numbers change when I adjust the pot.

#!/usr/bin/env python3

import time

def read_mcp3008(channel=0, device=1):
	'''Reads MCP3008 connected as an iio device.Takes a channel number (0-7)
	 and iio device number as arguments. Returns an integer from 0-1023'''
	channel_path = f"/sys/bus/iio/devices/iio:device{device}/in_voltage{channel}_raw"
	with open(channel_path, 'r') as f:

def adc_to_voltage(reading, ref_voltage=5):
	'''Converts a reading into a voltage. Takes reading and 
	reference voltage as arguments. Default ref_voltage is 5V.'''
	n = float(reading) / 1023

while True:
	for i in range(0, 8):
		r = read_mcp3008(i)
		v = adc_to_voltage(r)
		print(f'channel{i} : {r} - {v}V')

I am impressed. Is the above code a (working) substitut for this code:

spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)
cs = digitalio.DigitalInOut(board.D5)
mcp = MCP.MCP3008(spi, cs)
chan = AnalogIn(mcp, MCP.P0) #(and P1,P2 …P7)

I used on my RPI?
By the way, I think one should only use 3.3V as input to the mcp3008

Ah, that’s probably the issue!
You should probably change to “def adc_to_voltage(reading, ref_voltage=3.3):” and update the docstring accordingly.

That code should read every channel once every second and print to the terminal. Note that the value returned will also now be 0-1023. You’ll need to enable the overlay to use it. To do so you can download the wiring tool from my github (git clone or download the zip, whichever way is easiest for you). Open the directory “libretech-wiring-tool” in terminal and run “make” to compile. From within that same directory run “sudo ./ldto enable spicc spicc-mcp3008”.(If you don’t run it this way you’ll get the installed version, not the one you just maked[sic])

angus@aml-s905x-cc:~$ cd Projects/libretech-wiring-tool/
angus@aml-s905x-cc:~/Projects/libretech-wiring-tool$ make # you'll get more output from make 
make: Nothing to be done for 'all'.
angus@aml-s905x-cc:~/Projects/libretech-wiring-tool$ sudo ./ldto enable spicc spicc-mcp3008
[sudo] password for angus: 
Overlay spicc: applied
Overlay spicc-mcp3008: applied

Here’s my fork of the repo with the new overlay:

I’d also skim this:

You can just run “cat /sys/bus/iio/devices/iio:device1/in_voltage0_raw”" in terminal to read it with the overlay applied. it just show up as a file now. You can check it out using the file manager.

It just occured to me that I should mention this is based directly off the function I use to read the built-in 1.8V adc, which is iio:device0 so you could read channel 0 with “read_mcp3008(0, 0)” or channel 1 with “read_mcp3008(1, 0)” . Guess I should have kept the name “read_adc()”.

I have tested your code and it “appears to work”, but…It gives some random figures and as I am curious I tried to disconnect the mcp3008 pin by pin. The code gives more or less the same random figures whether it is connected or not. I think the readings are a noise signal within the Libre Computer.
I originally connected it to the SPI pins (23, 21, 19 and 29 for the CS). Is that wrong?
This is for cha = 0 dev = 0.

24 for the chip enable, as per the spicc overlay. I still think I fried mine. Replacement is on order.

Nix that!(the part about mine being fried that is, the ce pin is really 24) I just got a wild hair and looked at a few other overlays. I got the sample rate wrong. Works fine now. The corrected overlay is up on my github. I’d download it fresh, there have been other commits since.

I just did the dead obvious thing and looked at the datasheet again. 5V is OK.

You are right. I did look as well. The MCP3008 uses 5V
After your update of the overlay I have a very odd problem with “make”. It processes the overlay (list a spicc-mcp3008.pre.dts) but when I use

$ sudo ldto enable spicc spicc-mcp3008
LDTO_enable: spicc-mcp3008 does not exist and cannot be added

Yesterday I was able to enable the overlay. What can I be doing wrong to day?

You need to cd into the libretech-wiring-tool directory and run “sudo ./ldto enable spicc spicc-mcp3008” the “./” bit tells the shell to run the version in the current directory instead of the installed system version.

Demonstrate my lack of knowledge of Linux. spicc-mcp3008 is now added.
But the test program still reads random values. The same values are read even when MCP3008 is disconnected.

connected … disconnected
ch0 3284… …3186
ch1 986 … … .990
ch2 158… … .158
ch3 206… … .207
ch4 319… … . 321
ch5 383… … 377
ch6 2346… …2307
ch7 1702… …1684

My conclusion is that it is reading from somewhere else than MISO, MOSI, but nothing else is currently connected to the pins so… what.

Hmmm. If nothing is connected to an ADC the pins should “float” or give a random value. That essentially means any pin that’s not being used will return garbage. I’m not sure what the effect of unplugging a device and trying to read from the driver should be.

This is what I get with Ch0 connected to 3.3V and Ch1 to GND, and nothing else:

channel0 : 1023 - 3.3V
channel1 : 0 - 0.0V
channel2 : 9 - 0.02903225806451613V
channel3 : 60 - 0.1935483870967742V
channel4 : 109 - 0.35161290322580646V
channel5 : 118 - 0.38064516129032255V
channel6 : 84 - 0.27096774193548384V
channel7 : 50 - 0.16129032258064516V

**EDIT: **

This is what I get if I unplug mine:

channel0 : 1023 - 3.3V
channel1 : 1023 - 3.3V
channel2 : 1023 - 3.3V
channel3 : 1023 - 3.3V
channel4 : 1023 - 3.3V
channel5 : 1023 - 3.3V
channel6 : 1023 - 3.3V
channel7 : 1023 - 3.3V

Now that I’m thinking, that sounds like the bad sample rate behavior.
Id double check wiring first, I have :

Click for Wiring Diagram
CH0 - | U | - VDD  -> 3.3V
CH1 - |   | - VREF -> 3.3V
CH2 - |   | - AGND -> GND
CH3 - |   | - CLK  -> P23 (SPI_CLK)
CH4 - |   | - DOUT -> P21 (SPI_MISO)
CH5 - |   | - DIN  -> P19 (SPI_MOSI)
CH6 - |   | - CS   -> P24 (CS as per spicc.dto)
CH7 - |   | - DGND -> GND

My PR has been merged, so you might want to try deleting the whole libretech-wiring-tool directory and cloning the official repo fresh. (GitHub - libre-computer-project/libretech-wiring-tool)

I don’t know how often the software is updated, but this overlay should be included in future versions of the tool, so you wont have to jump though these hoops forever. @librecomputer might be able to shed some light as to how that works; I’m pretty green when it comes to contributing. This was my first Pull Request. I don’t know anything about what happens between my PR being merged and downloading the new tool with a package manager.

Meanwhile back at the ranch…

I opened an issue about this on Adafruit’s github. It seems like the tater isn’t being recognized properly by Blinka, and it’s using generic Linux pinmapping. It’s a bit above my head. I offered to implement an iio version of the mp3xxx module. They didn’t seem keen, but are looking into fixing their current implementation. I might get bored and do it anyhow, I don’t know yet.

As much as I like the idea of being able to prototype on a SBC and move to a microcontroller when it’s time, Blinka seems kind of broken, at least if you don’t pay a pi scalper an arm and a leg for a piece of fruit. It doesn’t work right on this board, doesn’t work at all on my odroid. For projects that aren’t going to wind up living on a RP2040, I’m not going to bother with it anymore.