Problems accessing MCP3008 via SPI - Invalid argument

I want to convert/transfer a program, which reads humidity sensors via a MCP3008 ADC, from Raspberry PI 4 to Le Potato.
The program looks like this:

import time
import tkinter 
from time import gmtime, strftime
import pin_mapping
import libregpio as GPIO
from libregpio import OUT, IN
from tkinter import *
import pathlib
from pathlib import Path
import busio
import smbus
import serial
import adafruit_blinka
#from analogio import analogin
import digitalio
import board
# create the spi bus
spi = busio.SPI((pin.SPI0_SCLK), (pin.SPI0_MOSI), (pin.SPI0_MISO)) 
# create the cs (chip select)
cs = digitalio.DigitalInOut(P26)
# create the mcp object
mcp = MCP.MCP3008(spi, cs)
# create an analog input channel on pin 0
chan = AnalogIn(mcp, MCP.P0)
    print("Raw data: ", chan)
    print ("voltage: "+str(chan.voltage)+"V")

The output of the first print is:
raw data: <adafruit_mcp3xxx.analog_in.AnalogIn object at 0xffffbb859040>
The next print result in the following error:

    ioctl(self.handle, SPI._IOC_MESSAGE, spi_ioc_transfer)
OSError: [Errno 22] Invalid argument

Can anyone help?

Did you enable a Device Tree Overlay for SPI?

It looks like you’re using Blinka, both it and libgpiod need to be installed through apt, don’t install libgpiod through pip. You didn’t show your imports, so I don’t know what the module “pin” is from, I’d use board instead, and implicitly specify pin numbers rather than board.SCLK, etc. I also needed to use adafruit_extended_bus rather than busio to get i2c working, it would be a good idea to use this instead because it allows you to specify the device.

Thank angus for your answer.
I have added my INPORTs in my original question
SPI is enabled as required and libgpiod was installed by: sudo apt-get install python3-libgpiod
I don’t know if “adafruit_extended_bus” can be used for SPI, or how?
Henrik

MCP3008 is already supported by the Linux kernel mcp320x driver. You just need to create a spi overlay with the proper compatible to use the driver. It will show up as a kernel IIO device.

There is sample code in the link I provided, but you might be able to get it working with busio if you’re using spidev 0.0.

I’ve only used SPI in CircuitPython with a microcontroller and a LCD, so I’ll need some time to put together an example.

A few things I’ve noticed right off the bat:

It looks like you have competing libraries imported to handle GPIO. I don’t Imagine this is good practice. Since you’re using Blinka, you can just use board and digitalio to handle the pins.

Also anlogio is used but the import statement is commented out.

You want to “print(chan.value)” otherwise you get the object it’s self, not the variable stored within.

I don’t see “chan1” declared anywhere. I think that should be “chan.voltage”.

The print statements are indented, not sure why.

SPI setup should be something like this:

spi = busio.SPI(board.P23, MISO=board.P21, MOSI=board.P19)
cs = digitalio.DigitalInOut(board.P26)

Writing overlays is high on my list of things to learn, but looking at the examples isn’t cutting it for me. Could anyone recommend a good tutorial?

Answer from “Librecomputer”

MCP3008 is already supported by the Linux kernel mcp320x driver. You just need to create a spi overlay with the proper compatible to use the driver. It will show up as a kernel IIO device.

I understand the first sentence - but not a word of the rest. What a "compatible"? and where will it "show up"?

I added an MCP3008 to my Adafruit order. I’ll see if I can get it working both ways (@librecomputer 's way is something I’ve been trying to teach myself). In the mean time there are two 1.8V ADCs built in to the potato. You’ll have to do some level shifting, but I’ve used it to read a photoresistor. I’ll dig up the code for you, but in a nutshell you just use open() to read the file.

Answer from angus:
If I delete the “Import libregpio” it makes no difference. I use libregpio as an easy way to specify GPIO names to handle relays

You want to “print(chan.value) ” otherwise you get the object it’s self, not the variable stored within.

Yes, but it is the only call that works. As soon as I add an argument [as in (chan.value) or (chan.voltage) ],
I just get the: “Invalid argument” error for that statement.
When I use your proposal: spi = busio.SPI(board.P23, MISO=board.P21,…) I function as well, but I still get the “Invalid argument” error.

It’s just printing the object’s address in memory though, so that’s not really working either.

Just Noticed:
In the sample code, instead of the regular “analogio.AnalogIn()” they use “adafruit_mcp3xxx.analog_in.AnalogIn()”.

The “analogio.AnalogIn()” class only accepts one pin as an argument, and I think is more of a micro-controller thing. That’s probably the invalid argument error.

so try “from adafruit_mcp3xxx.analog_in import AnalogIn” instead of the line that’s commented out in your imports.

I also just noticed AnalogIn is all lowercase in your import. That could muck things up for sure. Classes are usually EachWordUppercse in python (as per style guide anyhow).

https://docs.circuitpython.org/projects/mcp3xxx/en/latest/index.html

https://docs.circuitpython.org/en/latest/shared-bindings/analogio/index.html

I’m afraid that’s the best I can do without one in front of me.

Answer to angus:

so try “from adafruit_mcp3xxx.analog_in import AnalogIn ” instead of the line that’s commented out in your imports.

I have done so, but no change.
However I tried to change "print("ADC value: " chan.value) to chan.VALUE and have then the repons

AttributeError: ‘AnalogIn’ object has no attribute ‘VALUE’

With other words: “value” is a valid attribute. But what is then ment with “invalid argument”? To what “argument” is the system referering.

analogio.AnalogIn() accepts one argument, an analog pin. You are passing it the MPC.MPC3008 object and something else.

Answer to angus
I am not using analogio, but mcp3008.analog_in
Did I mention that my code is working correctly on my Raspberry Pi ? The problem has something to do with the actual board ( AML-S905X-CC)
PS. I want to move this small program to Le Potato in order to use the RPI 4 for something else.

Ok, I guess there’s not much more I can do other than wait for UPS, and try it for myself.

To angus: Thank you anyway for your time.
Henrik

I have reinstalled everything, even flashed the microSD, in order to ensure that there is no conflicting modules or different releases - but I still get the same error related to adafruit_mcp3xxx.mcp3008

File “/home/librecomputer/.local/lib/python3.9/site-packages/Adafruit_PureIO/spi.py”, line 420, in transfer
ioctl(self.handle, SPI._IOC_MESSAGE, spi_ioc_transfer)
OSError: [Errno 22] Invalid argument

As mentioned before, the code works on an RPI 4.
Any ideas?

UPS just dropped mine off. I’ll give it a try.

So I’ve tried a couple things, to no avail. I’m getting the same error.

I’ve reported the issue on github:

Not sure about the Adafruit library but manually probing SPI when there is an upstream kernel driver is not the right way to go about this.

I know, this was the solution I was most familiar with, so I tried it first. I’m preparing to do a deep dive into Device Tree Overlays this week. I still intend to work on that solution too. My family went on holiday an left me to tinker in peace, so maybe I’ll make some quick progress.

My main interest in getting the Adafruit library working is creating code that will run on my RP2040 based microcontrollers as well, without too much modification. I don’t intend to use much circuitpyton on projects that permanantly live on a potato, but there is some value in getting this to work this way, for me at least.

The photoresistor I’m testing this with can easily be read with the adc pins, I just made a simple voltage divider to turn the 3.3V to 1.8V and used pythons built in functions to read the file. That’s not a great solution either though, and wont work for something more sophisticated that runs off 5V.

1 Like