C++ SPI for Max 31855 Thermocouple Amp

User avatar
javaben
Posts: 35
Joined: Mon Mar 19, 2018 11:49 pm

Re: C++ SPI for Max 31855 Thermocouple Amp

Postby javaben » Sat May 12, 2018 4:03 am

Chegewara,

I appreciate your response, but you are missing the point of my question.

I'm familiar with how to work with the data, once I have obtained it.

The difficulty is in obtaining it, while using C++ and explicitly the ESP32 API functions while performing ESP32 hardware SPI. So, providing code for other scenarios doesn't address this question.

The question is: how to use C++ and the ESP32 API functions to do a hardware SPI read of the MAX31855 thermocouple amplifier.

Thanks for the assistance and willingness to help! It is much appreciated!

JavaBen

User avatar
javaben
Posts: 35
Joined: Mon Mar 19, 2018 11:49 pm

Re: C++ SPI for Max 31855 Thermocouple Amp

Postby javaben » Sat May 12, 2018 4:28 am

Here is some pseudo-code of what I'm trying to do:

Code: Select all

//use the ESP32 hardware SPI to obtain data from a MAX31855 thermocouple amplifier.

1.  Initialize the ESP32, informing it of the HSPI standard pins to be used, clock rate, MISO, CS, CLK pins, setting MOSI -1.
2.  Add the device and get a handle to it.
3. in a loop
    3.1 put the Chip Select (CS) pin high.  
    3.2 delay to settle
    3.3 put the CS pin low.  This causes the MAX31855 to provide 32 bits of data on the MISO line.
    3.4 Read this data from obtained from the MISO.  Save it as 'value' var.
    3.5 bit and 0x80000000 to get the sign bit and save it in 'sign' var.
    3.6 if sign bit not set then proceed, otherwise convert to negative values.  I won't have negative values in my project, so not an issue for me.
    3.7 strip the sign bit by anding 'value' with 0x7FF00000, then shift >> 20 and save as 'CelsiusRawData' var (I'm not concerned with fractions of temperature, so shift them out too; 2 extra bits)
    3.8 get the cold juction temperature sign by anding with 0x8000 and save as 'CJSign' var
    3.9 get the internal cold juction temperature by anding 'value' with 0x7ff0 and shift >> 4 and storing in 'CJRaw' var
    3.10 get error occurred by anding with 0x7 and storing in 'Error' var; take appropriate action if not 0.
    3.11 multiply the CelsiusRawData by .25 (? - not yet sure of this value) to convert to Celsius value, store in 'Celsius' var
    3.12 multiply the CJRaw by .25 (?) to convert to Celcius CJ temperature, store in 'CJ' var.
    3.13 send results to MQTT broker
    3.14 repeat loop to step 3.
I did this off the top of my head, so it's possible I got some of the >> and & adding off, but this is the basic approach.

Steps 3.3 and 3.4 are where I'm currently having difficulty; I don't understand how to get the MISO data that the chip is sending to me, while utilizing C++ and the ESP32 hardware SPI for HSPI.

Thanks!

JavaBen

chegewara
Posts: 2207
Joined: Wed Jun 14, 2017 9:00 pm

Re: C++ SPI for Max 31855 Thermocouple Amp

Postby chegewara » Sat May 12, 2018 4:34 am

Neil has posted this link before, but i want to give you another clue, because some time ago (months ago) ive been playing with it myself (this class, not max31855). Just try to play with those lines(especialy 122):
https://github.com/nkolban/esp32-snippe ... #L121-L122

Example code:

Code: Select all

uint8_t data;
spi.transfer(&data, 32 );
<--- data is keeping stored value 

User avatar
javaben
Posts: 35
Joined: Mon Mar 19, 2018 11:49 pm

Re: C++ SPI for Max 31855 Thermocouple Amp

Postby javaben » Sat May 12, 2018 1:39 pm

Chegewara,

Thanks for taking time to reply with your post.

I tried using this previously, but while it compiled, flashed, ran, and didn't report any errors, my data was always zero - all 32 bits.

However, my knowledge level has increased "somewhat" so now maybe I'll be able to find a setting that will work, so I'm trying again, using different spi_transacton_t value settings.

The MAX31577 requires the CS (or SS) pin to go low for it to return data, then back to high. It still isn't clear to me whether I need to set the CS (or SS) pin high and low manually, or whether the spi_device_tranmist() function will perform this activity. So far, I've tried doing it manually as well as not doing anything and all return zero for data.

It's not communicating with the MAX31855 because, even if I open the thermocouple lead up (open circuit), no error value is returned in the first 7 bits of the response.

I'll keep trying for a while on this approach before I postpone a solution and go to the Adafruit library approach of non-hardware SPI.

Using the non-hardware SPI approach does yield accurate results, so the MAX31855 chip is functioning, and the problem lies in my attempt to setup and send a proper transaction, it appears.

Thanks again.

JavaBen

User avatar
javaben
Posts: 35
Joined: Mon Mar 19, 2018 11:49 pm

Re: C++ SPI for Max 31855 Thermocouple Amp

Postby javaben » Sat May 12, 2018 1:46 pm

These are my current settings:

Code: Select all

        uint32_t thermocoupleData = 0;

        buscfg.mosi_io_num = HSPI_MOSI;
        buscfg.miso_io_num = HSPI_MISO;
        buscfg.sclk_io_num = HSPI_SCK;
        buscfg.quadwp_io_num = -1;
        buscfg.quadhd_io_num = -1;
        buscfg.max_transfer_sz = 4;  //4 BYTES???
        buscfg.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_MISO | SPICOMMON_BUSFLAG_SCLK;

        devcfg.command_bits = 0;
        devcfg.address_bits = 0;
        devcfg.dummy_bits = 0;      //note this; if dropping bits at start of cycle
        devcfg.mode = 0;           //0,1,2,3.  has to do with clock polarity, phase, and edge.  See wikipedia.org for spi mode numbers
        devcfg.duty_cycle_pos = 0; // setting to 0 defaults to 128, 50% duty cycle
        devcfg.cs_ena_pretrans = 0;  //amount of spi bit-cycles cs activated before transmission.
        devcfg.cs_ena_posttrans = 0;  //sim to pretrans, but trailing
        devcfg.clock_speed_hz = 10000; //10 khz; clock speed in hz
        devcfg.spics_io_num = HSPI_SS;  //CS GPIO pin, -1 if not used
        devcfg.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_MISO | SPICOMMON_BUSFLAG_SCLK;  //spi_device_ flags
        devcfg.queue_size = 1; //queue depth.  0 will cause crash
        devcfg.pre_cb = NULL; //callback before trans ========not sure here
        devcfg.post_cb = NULL; //callback after trans ========not sure here

        trans_desc.addr =0;
        trans_desc.cmd = 0;
        trans_desc.flags = SPI_TRANS_USE_RXDATA;
        trans_desc.length = 32;
        trans_desc.rxlength = 32;
        trans_desc.tx_buffer = NULL;  //docs specify NULL for no MOSI phase.
        trans_desc.rx_buffer = &thermocoupleData;


User avatar
javaben
Posts: 35
Joined: Mon Mar 19, 2018 11:49 pm

Re: C++ SPI for Max 31855 Thermocouple Amp

Postby javaben » Sat May 12, 2018 8:04 pm

Chegewara,

I re-cloned Kolban's cpp_util, and copied the SPI.h and SPI.cpp into my project.

I ensured my pin configuration were correct for HSPI. Running it, didn't produce any data, except a constant 1.
In SPI.cpp I un-comented the #define DEBUG 1 so it would print out.
I don't get any errors from SPI.cpp per the ESP_LOG* statements.

Here's the code I drove it with (NOTE: I tried both spi.DEFAULT_MISO_PIN and -1; neither changed the data. If you see an issue with my main.cpp code below, let me know and I'll change it!

Code: Select all

#include "SPI.h"
#include <iostream>


extern "C" {
    void app_main(void);
};

void app_main(void) {
    std::cout << "MAIN LOOP START\n\n\n";

    SPI spi = SPI();

    spi.setHost(HSPI_HOST);
    spi.init(spi.DEFAULT_MOSI_PIN, spi.DEFAULT_MISO_PIN, spi.DEFAULT_CLK_PIN, spi.DEFAULT_CS_PIN);


    uint8_t v = 'A';
    uint8_t r;

    while(1) {
        r = spi.transferByte(v);
        printf("Returned data: %u \n",r);  //always 1
    };
};

Then, after not working:
- I placed an led and resistor on the MISO pin and changed the dev_config.clock_speed_hz to 1 so I could watch it. It pulses, indicating the MAX31855 is sending data back to the ESP32.

I double checked that the MAX31855 was still functioning, by going to the Arduino IDE, and running Adafruit's serialthermocouple.ino. It worked fine; it doesn't appear to me that it uses the hardware SPI capabilities of the ESP32.

At this point, I think there is something wrong with the ESP-IDF code that is preventing it from working properly with the MAX31855.

Thanks for your assistance!

JavaBen

chegewara
Posts: 2207
Joined: Wed Jun 14, 2017 9:00 pm

Re: C++ SPI for Max 31855 Thermocouple Amp

Postby chegewara » Sat May 12, 2018 8:58 pm

SPI is not my domain, anything im saying here is how i would try to make it working. i think loboris expertise would be helpful here.

You can try to change clock to something closer to 5MHz. Next i would try to read more than 32 bits to see if you can read anything more than values you getting now.
https://github.com/nkolban/esp32-snippe ... I.cpp#L109
Next, here you can see that spi is initialized with DMA:
https://github.com/nkolban/esp32-snippe ... PI.cpp#L63
this can help:
http://esp-idf.readthedocs.io/en/latest ... ead-phases

Also just in case (most likely its not required) set pins direction. MOSI, CLK and CS to output and MISO to input.
But most important i will advise you something someone else advised me long time ago: buy cheap logic analyzer, its less than $10, but it will save you a lot time and headaches.

User avatar
javaben
Posts: 35
Joined: Mon Mar 19, 2018 11:49 pm

Re: C++ SPI for Max 31855 Thermocouple Amp

Postby javaben » Sun May 13, 2018 5:53 pm

I wrote my own code, which follows.

It works for me.

Thanks everyone for your assistance!

BTW - this is being used as a 4 channel temperature monitor/sensor for smoking BBQ.

It will later be used with a controller

Output is after code section.

JavaBen

Code: Select all

/* 
* MAX31855 Thermocouple Temperature Software - for type K
* Author: BBQBailey, see //github.com/bbqbailey for updates
* Date: May 13, 2018
* Rev: 1.0
* Purpose: Software for MAX31855 on ESP32
*   I was not able to use the Espressif SPI code to work
*   correctly with my MAX31855 thermocouple amplifier for 
*   type K thermocouples, so I wrote this code.  
*   It does not use the hardware SPI capabilities. 
*   Instead, it generates its own clock pulses, and reads the bit 
*   responses.
*
*   It works correctly for me without any vTaskDelay() calls between
*   CLK, CS, MISO signals.
*
* Future Releases:
*   - Add calculations for negative temperatures
*   - Use interrupt callbacks instead of vTaskDelay(), which is a blocking call.
*   - Add web page host
*   - Add MQTT broker communications
*/

#include <iostream>
#include <driver/gpio.h>
#include <freertos/FreeRTOS.h> //for vTaskDelay();
#include <freertos/task.h>     //for vTaskDelay();

//NOTE: the following pins all have an LED attached to them.  Low causes light
#define CS   16  //output - CHIP SELECT
#define CLK  17  //output - CLOCK
#define DATA 18  //input  - DATA AKA MISO

extern "C" {
    void app_main(void);
}

void app_main(void) {
    int valueBit = 0;
    uint32_t rawData = 0;
    uint32_t  error = 0;
    uint8_t  signInt = 0;
    uint8_t  signC = 0;

    float tempIntC = 0.0;
    float tempIntF = 0.0;
    float tempC = 0.0;
    float tempF = 0.0;

    //assign GPI functions
    gpio_set_direction(gpio_num_t(CS), GPIO_MODE_OUTPUT);
    gpio_set_direction(gpio_num_t(CLK), GPIO_MODE_OUTPUT);
    gpio_set_direction(gpio_num_t(DATA), GPIO_MODE_INPUT);

    //start doing it!
    gpio_set_level(gpio_num_t(CS), 1);
    gpio_set_level(gpio_num_t(CLK), 0);

    //create our own clock pulses, then read data bits
    while(1) {
        gpio_set_level(gpio_num_t(CS), 0);  //enable chip
//        vTaskDelay(10);  //uncomment if you are having problems to give chip time to synch with clock

        //get the 32 bits from MAX31855
        //   generate clock pulse high, then read data bit, then clock pulse low
        std::cout << "Binary data received from MAX31855: ";
        for(int i=0;i<31;i++) {
            gpio_set_level(gpio_num_t(CLK), 1);  //clock pulse going high - start pulse
//            vTaskDelay(10);  //uncomment if synch problem
            valueBit = gpio_get_level(gpio_num_t(DATA));  //get bit
            rawData <<= 1;
            rawData += 0x01 & valueBit;  
//            vTaskDelay(10);  //uncomment if synch problem
            gpio_set_level(gpio_num_t(CLK), 0);  //clock pulse going low - end pulse
//            vTaskDelay(10);  //uncomment if synch problem
            std::cout << valueBit;
        }
        gpio_set_level(gpio_num_t(CS), 1);
        std::cout << "\n";

        printf("rawData: 0x%x\n", rawData);
        error = rawData & 0x7;
        if(error) {
            printf("-----Error reported by chip.  Value is: 0x%x\n", error);
            printf("     Following values are probably bad.\n");
        }

        signInt = (rawData & 0x8000) >> 4;
        signC   = (rawData & 0x80000000) >> 31;


        if((signInt == 0) && (signC == 0)) {

            tempIntC = float((rawData >> 4) & 0x7FF) * 0.0625;
            tempIntF = tempIntC * 1.8 + 32.0;
            tempC   = (float((rawData >> 18) & 0x7FFC) * .25 + tempIntC);
            tempF   = tempC * 1.8 + 32.0;
            printf("error: ox%x\n", error);  //will not be zero if error returned from chip
            printf("signInt: ox%x\n", signInt);
            printf("signC: ox%x\n", signC);
            printf("tempIntC: %6.2f \n", tempIntC);
            printf("tempIntF: %6.2f \n", tempIntF);
            printf("tempC: %6.2f \n", tempC);
            printf("TempF: %6.2f \n", tempF);
        }   else  {
            std::cout << "-----negative temperatures not yet implemented.-----\n";
        }

        vTaskDelay(15000);  //or what ever you want.
    }
}

OUTPUT
Binary data received from MAX31855: 0000000111000100000111010010000
rawData: 0xe20e90
error: ox0
signInt: ox0
signC: ox0
tempIntC: 14.56
tempIntF: 58.21
tempC: 28.56
TempF: 83.41
Binary data received from MAX31855: 0000000111000000000111010010000
rawData: 0xe00e90
error: ox0
signInt: ox0
signC: ox0
tempIntC: 14.56
tempIntF: 58.21
tempC: 28.56
TempF: 83.41
Binary data received from MAX31855: 0000000111000000000111010010000
rawData: 0xe00e90
error: ox0
signInt: ox0
signC: ox0
tempIntC: 14.56
tempIntF: 58.21
tempC: 28.56
TempF: 83.41

Deouss
Posts: 425
Joined: Tue Mar 20, 2018 11:36 am

Re: C++ SPI for Max 31855 Thermocouple Amp

Postby Deouss » Tue May 22, 2018 2:06 am

I actually need help with reading MAX31855

Here is SPI code that works but readings are a bit off.
What am I doing wrong?

Code: Select all

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/gpio_struct.h"
#include "driver/gpio.h"
#include "driver/spi_common.h"
#include "driver/spi_master.h"
#include "sdkconfig.h"

#define DO 12           // slave Data Out
#define CSL 5            // chip select
#define CLK 14          // spi clock
#define SAMPLE_COUNT 10 // consecutive readouts

typedef struct
{
    float internal_C;
    float thermocouple_C;
    float internal_F;
    float thermocouple_F;
    bool fault;
    bool short_to_VCC;
    bool short_to_GND;
    bool open_circuit;
} max31855_data_t;

xQueueHandle callback_queue;

esp_err_t ret;
spi_device_handle_t spi;
spi_bus_config_t buscfg =
{
    .flags              = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_MISO | SPICOMMON_BUSFLAG_SCLK,
    .miso_io_num        = DO,
    .mosi_io_num        = -1,
    .sclk_io_num        = CLK,
    .quadwp_io_num      = -1,
    .quadhd_io_num      = -1,
    .max_transfer_sz    = 4
};
spi_device_interface_config_t devcfg =
{
    .command_bits       = 0,
    .address_bits       = 0,
    .dummy_bits         = 0,        //note this; if dropping bits at start of cycle
    .duty_cycle_pos     = 0,        // setting to 0 defaults to 128, 50% duty cycle
    .cs_ena_pretrans    = 0,        //amount of spi bit-cycles cs activated before transmission.
    .cs_ena_posttrans   = 0,        //sim to pretrans, but trailing
    .mode               = 0,        //0,1,2,3.  has to do with clock polarity, phase, and edge.  See wikipedia.org for spi mode numbers
    .clock_speed_hz = 4*1000*1000,  //Clock out at  MHz
    .spics_io_num   = CSL,           //CS pin
    .queue_size     = 1, // nr transactions at a time
    .pre_cb         = NULL,//enable_thermocouple_chip,         //callback before trans
    .post_cb        = NULL//disable_thermocouple_chip          //callback after trans
};


void app_main()
{
    gpio_set_direction(CSL, GPIO_MODE_INPUT);

    ret = spi_bus_initialize(HSPI_HOST, &buscfg, 0);
    ret = spi_bus_add_device(HSPI_HOST, &devcfg, &spi);

    uint32_t thermocouple_data;
    max31855_data_t dt;


    for(;;)
    {
    spi_transaction_t t =
    {
        .addr = 0,
        .cmd = 0,
        .flags = SPI_TRANS_USE_RXDATA,
        .length = 32,
        .rxlength = 32,
        .user = NULL,
        .tx_buffer = NULL,
        .rx_buffer = NULL,
    };

    spi_device_transmit(spi, &t);

    thermocouple_data = ((uint32_t)t.rx_data[0] << 24) | ((uint32_t)t.rx_data[1] << 16) | ((uint32_t)t.rx_data[2] << 8) | (uint32_t)t.rx_data[3];

    int16_t internal_data= (int16_t)(thermocouple_data & 0xFFFF);
    dt.internal_C = (internal_data/256) + (((uint8_t)internal_data>>4) & 0xF) * 0.0625f;
   
    int16_t thermal_data = (int16_t)(thermocouple_data >> 18);
    dt.thermocouple_C=(thermal_data/4) + (((uint8_t)thermal_data) & 3) * 0.25f + dt.internal_C;
    
    dt.internal_F = dt.internal_C * 1.8f + 32.0f;
    dt.thermocouple_F  = dt.thermocouple_C * 1.8f + 32.0f;
    
    dt.fault = (thermocouple_data >> 16) & 1;
    dt.short_to_VCC = internal_data & 4;
    dt.short_to_GND = internal_data & 2;
    
    printf("C: %6.2f \n", dt.thermocouple_C);
    printf("F: %6.2f \n", dt.thermocouple_F);
    printf("Ci: %6.2f \n", dt.internal_C);
    printf("Fi: %6.2f \n", dt.internal_F);

    printf("error: 0x%x\n", dt.fault);
    printf("VCC: 0x%x\n", dt.short_to_VCC);
    printf("GND: 0x%x\n", dt.short_to_GND);
    
    vTaskDelay(100 / portTICK_RATE_MS);
    }
}
example readout:
C: 37.56
F: 99.61
Ci: 28.06
Fi: 82.51
error: 0x1
VCC: 0x0
GND: 0x1
C: 37.62
F: 99.72
Ci: 28.12
Fi: 82.62
error: 0x1
VCC: 0x0
GND: 0x1
C: 38.56
F: 101.41
Ci: 28.06
Fi: 82.51
error: 0x1
VCC: 0x0
GND: 0x1
C: 38.06
F: 100.51
Ci: 28.06
Fi: 82.51
error: 0x1
VCC: 0x0
GND: 0x1
C: 37.56
F: 99.61
Ci: 28.06
Fi: 82.51
error: 0x1
VCC: 0x0
GND: 0x1
C: 38.31
F: 100.96
Ci: 28.06
Fi: 82.51
error: 0x1
VCC: 0x0
GND: 0x1
C: 38.06
F: 100.51
Ci: 28.06
Fi: 82.51
error: 0x1
VCC: 0x0
GND: 0x1
Also - fundamental question - rx_data buffer - how data is laid out??
highest bits go from rx_data[0] then rx_data[1]...etc?
or is it other way or bits are reversed???

How SPI stores the bits?

Who is online

Users browsing this forum: No registered users and 109 guests