Problems accessing MCP3008 via SPI - Invalid argument

Took some messing around, but this works:


/*
 * Device Tree Overlay to enable MCP3008 ADC
 */

/dts-v1/;
/plugin/;

#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:
		return(f.read())

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
	return(n*ref_voltage)
	

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

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.

EDIT:
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.

My ch0 is at 3.3V, ch1 = 0v, ch2 = potentiometer, ch3-ch7 are all grounded.
If I read from device 1 i get 1023 on all channels (0 -7)

It should be a simple task to read a value from a pin. I don’t understand the complexity in the basic code. I think it is so complex that nobody really understand how all the components works together (or not work together).
I have an Anduino board. I will try if it can run my application. Of cause I will need to learn that box and to reprogram everything, but it might be easier.
Thank you for all the time you have used trying to help me.
Henrik.

Fair enough, I guess.

I’m not having any issues reading the device now. I’ve tried a pot, voltage divider, thermistor, photocell and vacuum pressure sensor all to good effect, so it can work. I don’t call using the python’s open() built-in to read a file difficult, especially compared to what Adafruit’s code is doing under the hood.

Thanks for presenting this challenge, anyhow. I was overjoyed when I saw the device on my breadboard show up in my filesystem.

You are right about the open().
I have found an error in my breadboard(sic) - no power or no GND to the MCP3008. Will try tomorrow with another breadboard.

1 Like

Hurrah.
I went through all my wiring and changed the MCP3008 chip and now it works (using device(1))
Henrik

1 Like

Glad it worked! I’m new to this, so I’m sorry if it was a long road getting here. I know it’s not the best situation when the guy helping you is also learning things for the first time.

My main interest in embedded Linux is retro gaming agricultural so this was a good way to justify buying more ic’s to play with learning exercise. As soon as all the dust settles, and all the PRs are merged I’ll write a tutorial with some cleaner looking sample code and clear instructions.

I made a new AnalogIn class for use with iio devices I’m going to submit to Adafruit soon, and I’m also looking into the problem with Blinka that started all this. As strange as this may be to say on post # 45 on this thread, it turns out the Libre “just learn to do it the right way” solution was really the “easy” solution this time.

Blinka is a mess under the hood, thanks in part to soc/SBC makers not having any common standards. What should be a simple if/then is turned into this crazy soup of pin mapping and board-specific functions. Just for a taste, an instance of this monstrosity is created every time you import board:

I had 7 browser tabs open just trying to figure out what it does.

Anyhow, Good luck with your project!
-Angus