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