AlejandroHuerta

AlejandroHuerta

Hey everyone, I’m new to working with hardware and decided to learn by interfacing with some MAX31855 thermocouple sensors. I’ve successfully read temperatures from them using a software SPI, basically doing all of the CLK switching manually, and decided to give the hardware SPI a try but I’m unsure how to correctly interface with it.

I’m using the Circuits.SPI library. I’ve connected the MAX31855 pins according to this layout

DO → SPI_MISO
CLK → SPI_SCLK
CS → SPI_CE0

I’ve done Circuits.SPI.bus_name which returns ["spidev0.0", "spidev0.1"] I assume spidev0.0 corresponds to CE0, so I then proceeded to do SPI.open("spidev0.0", speed_hz: 5_000_000, delay_ns: 100) The opts I’ve added by looking at the MAX31855 datasheet (https://datasheets.maximintegrated.com/en/ds/MAX31855.pdf). I’m really not sure what to do from here, I’ve tried, as a test, SPI.transfer(ref, <<0>>) and it just returns {:ok, <<0>>}.

Any help would be appreciated as I’m pretty lost here since the example for Circuits.SPI is interfacing with an ADC.

Showing Posts 1 to 8

ConnorRigby

ConnorRigby

Nerves Core Team

I’ve never used this device myself, but couple things i notice here.. SPI usually requires more wires.

The main signals are

  • MISO - Master In Slave Out
  • MOSI - Master Out Slave In
  • CLK - Clock

Optional signals will be

  • CS - Cable Select
  • CE - Chip Enable

And occasionally you will have
*INT - interups

As far as i know the Linux driver will not operate CS or CE themselves. You usually will need to use
Circuits.GPIO to toggle these. You will probably want to use spidev0.0, but it depends on the bus you used. If you are on Raspberry pi, the 0.0 bus is the top one. You will also want to look at your data sheet to see how to use CS or CE. (It looks like you have CS). CS needs to be set high or low for a SPI device to know that it is it’s turn to use the bus. if you have a CE, it tells the chip to be on or off. You will need to look at the datasheet to tell for sure. You also need to get another data line for MOSI (master out, slave in) this is the wire that will be transmitting data TO the device from Nerves

AlejandroHuerta

AlejandroHuerta OP

Hey thanks for the reply. This device has 6 pins total. The other 3 are 3.3v Vin, GND, and 3Vo. I don’t believe there is anything for MOSI. Here is a pin layout I’ve been able to find for connecing to a RPI, Hardware | MAX31855 Thermocouple Sensor Python Library | Adafruit Learning System.

I was under the impression that Circuits.SPI handled selecting the chip. From the Circuits.SPI example I can see that a CE pin from the RPI is connected to a CS pin and there is no manual toggling with GPIO.

After messing around with the wiring a bit on the breadboard, I’ve discovered that the +/- rails seem to have issues because by connecting the power and ground directly to the breakout board I’m now getting something else out of the transfer

{:ok, <<oc::1, scg::1, scv::1, _::1, ref::12, fault::1, _::1, temp::14>>} = SPI.transfer(t, <<0::32>>)
{:ok, <<1, 68, 20, 224>>}

clayschick

clayschick

I’ve been playing around with a breakout board (from Adafruit) that has a MAX31865 IC which, I think, is just a later iteration of the MAX31855 (I haven’t looked at the datasheet for the MAX31855 yet). Anyway, I also had some trouble figuring out how to communicate with the IC via SPI but I eventually figured it by reading the source code of the Adafruit python library and running the python library with lots of debug statements sprinkled around.

I have my breakout board wired to a Raspberry Pi Zero. On the breakout board I have I’m using the pin outs for power and ground, of course. For the SPI communication I’m using:

CLK → BCM 11 (SCLK)
SDO → BCM 9 (MISO)
SDI → BCM 10 (MOSI)
CS → BCM 8 (CE0)

To open a connection I use:

iex(gadget@nerves.local)1> {:ok, ref} = Circuits.SPI.open("spidev0.0", mode: 1, speed_hz: 500_000)

I set the mode to 1 because the datasheet for the MAX31865 says it works in mode 1 or 3. The speed_hz to 500_000 because that’s what’s used in the Adafruit documentation and source code (I’m kind of assuming I have the value set correctly).

At this point I can read from the config register (0x00 when reading).

iex(gadget@nerves.local)2> Circuits.SPI.transfer(ref, <<0x00, 0x00>>)
{:ok, <<0, 0>>}

And set the config register (0x80 when writing) to the values that correspond to my setup which is a 3-wire PT100 RTD.

iex(gadget@nerves.local)3> Circuits.SPI.transfer(ref, <<0x80, 0x90>>)
{:ok, <<0, 0>>}

And read from the config register again to make sure it is set correctly.

iex(gadget@nerves.local)4> Circuits.SPI.transfer(ref, <<0x00, 0x00>>)
{:ok, <<0, 144>>}

When reading from the data register of the MAX31865 (0x01) I will get back 3 bytes of binary data. The first byte is ignored (same applies when reading from the config register). Then next 2 bytes are the data that I want. The first 15 bits of that data is the actual resistance value of the RTD. The last bit is the fault bit that will be 0 or 1 (if there was a fault you can read from the fault registers to see what it was). To read the resistance from the chip I do a transfer/2 and pattern match.

First, I have to configure the chip for a 1-shot data transfer. This value is in the datasheet.

iex(gadget@nerves.local)5> Circuits.SPI.transfer(ref, <<0x80, 0xB0>>)

Then I can initiate a transfer and get the resistance value back in the binary.

iex(gadget@nerves.local)6> {:ok, <<_::size(8), resistance::size(15), fault_bit::size(1)>>} =
      Circuits.SPI.transfer(ref, <<0x01, 0x00, 0x00>>)

The first byte of the binary that I’m passing into the transfer\2 function is the hex value of the data register on the MAX31865. I also add 2 more bytes because the transfer function is going to return me the same amount of binary data that I pass in. Since I know that I’m going to get 2 bytes worth of data back, I add 2 empty bytes in my binary that I pass into the transfer function. That’s the part that really tripped me up.

Once I have the resistance value I can do the maths to convert it to a temperature value. I created a Raspbian build to run the python libraries for debugging and to also take a measurement of the resistance value so I could test that it was the same when using Circuits.SPI on a Nerves build. The values where the same and after converting the resistance value to a temperature value and comparing it to a normal thermometer the values are only off by 2 degrees. So, I think it’s working but this is all an experiment for me too.

Hope that helps.

clayschick

clayschick

After looking closer at the datasheet for the MAX31855 it doesn’t look like it uses registers like the MAX 31865. I think in your case you get all the data back in one full chunk of 32 (0-31) bits with bits 30 - 18 containing the temp data. Bit 31 is the first one returned. So after dropping the CS pin low you would initiate a transfer of 32 bits. Something like this:

Circuits.SPI.transfer(ref, <<0x00, 0x00, 0x00, 0x00>>)

Then I think you will need to wait a bit before raising the CS pin high again because, according to the datasheet,

A complete serial-interface read of the cold-junction compensated thermocouple temperature requires 14 clock cycles.

So the time you have to wait depends on the clock timing that you are applying at SCK.

Once the timing has completed check the values that you patterned matched on and see if you have data.

AlejandroHuerta

AlejandroHuerta OP

Haha I was just replying to your previous post when you posted again.

It looks like I’ve successfully been able to get data out by doing, {:ok, <<temp_sign::1, temp::13, _::1, fault::1, ref_sign::1, ref::11, _::1, scv::1, scg::1, oc::1>>} = SPI.transfer(t, <<0::32>>). I’m still unsure about the whole needing to manually drop the CS pin low because simply doing the transfer seems to be working. I’ll have to see what happens when I add in a second MAX31855.

Thanks for the help!

clayschick

clayschick

Yeah, I reread your post and it looks like you have it. I think you’re also correct about changing the CS pin value.

Funny because I was working on this when I found your post and thought I would do a brain dump to see if it would help. Like you, I haven’t found a lot of examples demonstrating how to talk to different devices through SPI. Hopefully someone else will find this helpful too.

AlejandroHuerta

AlejandroHuerta OP

Just replying to say that I was able to use 2 sensors without issue, using the same method, with the second sensors CS pin connected to SPI_CS1. It seems like the Circuits.SPI library handles the CS/CE switching.

mmmries

mmmries

Thanks for posting this! I’m still trying to wrap my head around how to talk over an SPI interface and found it helpful to read through your posts

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
apz
I’m new to elixir and just tried to install the elixirLS extension for VScode(ium) and it is throwing some errors that I would like help ...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews