ESP32-WROOM-32DとMAX6675を組み合わせてK型熱電対の値をSPI経由で取得してみた。

# はじめに

mruby/cを使って温度計測をしてみようと思い、手元にあるデバイスで試そうとしたら意外と手がかかったのでまとめておきます。

# 使ったもの

- ESP32-WROOM-32D
- MAX6675基盤
- K型熱電対センサー

# 接続

|MAX6675基盤|ESP32基盤|
|---|---|
|GND|GND|
|VCC|5V|
|SCK|14ピン|
|CS|15ピン|
|SO|13ピン|

# Arduino IDEを使った場合

まずは情報の多いArduinoを試して回路の配線などを検証してみました。

以下のURLにあるサンプルを流用してそれなりにできたっぽい。
[ESP32 and Max6675 example](http://www.esp32learning.com/code/esp32-and-max6675-example.php)

ピン番号を今回の接続にあわせて変更したものは以下のとおり。

```c:
#include "max6675.h"
 
int thermoDO = 13;
int thermoCS = 15;
int thermoCLK = 14;
 
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
 
void setup()
{
  Serial.begin(9600);
  Serial.println("MAX6675 test");
  delay(500);
}
 
void loop()
{
  // basic readout test, just print the current temp
  
  Serial.print("C = ");
  Serial.println(thermocouple.readCelsius());
  Serial.print("F = ");
  Serial.println(thermocouple.readFahrenheit());
  
  delay(1000);
}
```

# ESP-IDFを使った場合

次に、mruby/cを使うためにESP-IDFの環境を用意してテストしてみました。

以下の本家サイトでセットアップ手順が詳しく記載されています。

[ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html)

手順の中で入手するサンプルコードを改造します。
使ったのは、examplesの中にある以下のサンプルです。

[spi_master](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/spi_master)

また、以下にMAX6675を使ったサンプルがあったのでこちらも参考にさせて頂きました。

[ESP32_MAX6675_thingspeak](https://github.com/pcbreflux/espressif/tree/master/esp32/app/ESP32_MAX6675_thingspeak)

本家のサンプルコードを確認するとLCDモニターを動かすためのサンプルになっているようなので、MAX6675のサンプルを真似て以下のとおり修正。

```c:spi_master_example_main.c
/* SPI Master example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/spi_master.h"
#include "soc/gpio_struct.h"
#include "driver/gpio.h"

#include "pretty_effect.h"

/*
#define PIN_NUM_MISO 25
#define PIN_NUM_MOSI 23
#define PIN_NUM_CLK  19
#define PIN_NUM_CS   22
*/

#define PIN_NUM_MISO 13
#define PIN_NUM_MOSI 12
#define PIN_NUM_CLK  14
#define PIN_NUM_CS   15

#define PIN_NUM_DC   21
#define PIN_NUM_RST  18
#define PIN_NUM_BCKL 5

#ifndef HIGH
    #define HIGH 1
#endif
#ifndef LOW
    #define LOW 0
#endif

//To speed up transfers, every SPI transfer sends a bunch of lines. This define specifies how many. More means more memory use,
//but less overhead for setting up / finishing transfers. Make sure 240 is dividable by this.
#define PARALLEL_LINES 16

uint16_t readMax6675(spi_device_handle_t spi) {

    spi_transaction_t trans_word;
    uint16_t data,rawtemp,temp = 0;
    
    gpio_set_level(PIN_NUM_CS, LOW);
    vTaskDelay(500 / portTICK_RATE_MS);
    
    rawtemp = 0x000;
    data = 0x000;
    
    //trans_word.address   = 0;
    //trans_word.command   = 0;
    trans_word.flags     = 0;
    trans_word.length    = 8 * 2; // Total data length, in bits NOT number of bytes.
    trans_word.rxlength  = 0; // (0 defaults this to the value of ``length``)
    trans_word.tx_buffer = &data;
    trans_word.rx_buffer = &rawtemp;
    
    ESP_ERROR_CHECK(spi_device_transmit(spi, &trans_word));
    
    gpio_set_level(PIN_NUM_CS, HIGH);
    
    temp = ((((rawtemp & 0x00FF) << 8) | ((rawtemp & 0xFF00) >> 8))>>3)*25;
    
    return temp;
    
}

void app_main()
{
    
    uint16_t temp = 0;
    
    esp_err_t ret;
    
    spi_device_handle_t spi;
    
    spi_bus_config_t buscfg={
        .miso_io_num=PIN_NUM_MISO,
        .mosi_io_num=PIN_NUM_MOSI,
        .sclk_io_num=PIN_NUM_CLK,
        .quadwp_io_num=-1,
        .quadhd_io_num=-1,
        .max_transfer_sz=PARALLEL_LINES*320*2+8
    };
    
    spi_device_interface_config_t devcfg={
        .clock_speed_hz=10*1000,                //Clock out at 10 kHz
        .mode=0,                                //SPI mode 0
        .spics_io_num=PIN_NUM_CS,               //CS pin
        .queue_size=100,                        //We want to be able to queue 7 transactions at a time
        .pre_cb=NULL,                           //Specify pre-transfer callback to handle D/C line
    };
    
    //Initialize the SPI bus
    ret = spi_bus_initialize(HSPI_HOST, &buscfg, 1);
    ESP_ERROR_CHECK(ret);
    
    //Attach the LCD to the SPI bus
    ret = spi_bus_add_device(HSPI_HOST, &devcfg, &spi);
    ESP_ERROR_CHECK(ret);
    
    while(1) {
        
        temp = readMax6675(spi);
        printf("%d\n", temp);
        
        vTaskDelay(1000 / portTICK_RATE_MS);
        
    }
    
}
```

変更後、以下のコマンドでコンパイルして書き換え。

```bash:
make flash monitor
```

実行すると以下のようなメッセージが出てきます。

```bash:
make flash monitor
Toolchain path: /Users/[your user name]/esp/xtensa-esp32-elf/bin/xtensa-esp32-elf-gcc
Toolchain version: crosstool-ng-1.22.0-80-g6c4433a
Compiler version: 5.2.0
Python requirements from /Users/[your user name]/esp/esp-idf/requirements.txt are satisfied.

Project is not inside a git repository, will not use 'git describe' to determine PROJECT_VER.
App "spi_master" version: 1
CC build/main/spi_master_example_main.o
AR build/main/libmain.a
Generating libmain.a.sections_info
Generating esp32.project.ld
LD build/spi_master.elf
esptool.py v2.7-dev
Flashing binaries to serial port /dev/cu.SLAB_USBtoUART (app at offset 0x10000)...
esptool.py v2.7-dev
Serial port /dev/cu.SLAB_USBtoUART
Connecting........___
Chip is ESP32D0WDQ5 (revision 1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
MAC: 30:ae:a4:ca:e3:68
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 921600
Changed.
Configuring flash size...
Auto-detected Flash size: 4MB
Flash params set to 0x0220
Compressed 24432 bytes to 14609...
Wrote 24432 bytes (14609 compressed) at 0x00001000 in 0.2 seconds (effective 1125.7 kbit/s)...
Hash of data verified.
Compressed 175936 bytes to 80198...
Wrote 175936 bytes (80198 compressed) at 0x00010000 in 1.3 seconds (effective 1120.6 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 103...
Wrote 3072 bytes (103 compressed) at 0x00008000 in 0.0 seconds (effective 2606.4 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...
MONITOR
--- idf_monitor on /dev/cu.SLAB_USBtoUART 115200 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
ets Jun  8 2016 00:22:57

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:6328
load:0x40078000,len:11308
load:0x40080400,len:6700
entry 0x40080764
I (28) boot: ESP-IDF v4.0-dev-311-g70eda3d22 2nd stage bootloader
I (28) boot: compile time 13:58:14
I (28) boot: Enabling RNG early entropy source...
I (34) boot: SPI Speed      : 40MHz
I (38) boot: SPI Mode       : DIO
I (42) boot: SPI Flash Size : 4MB
I (47) boot: Partition Table:
I (50) boot: ## Label            Usage          Type ST Offset   Length
I (57) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (65) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (72) boot:  2 factory          factory app      00 00 00010000 00100000
I (80) boot: End of partition table
I (84) esp_image: segment 0: paddr=0x00010020 vaddr=0x3f400020 size=0x0bfc4 ( 49092) map
I (110) esp_image: segment 1: paddr=0x0001bfec vaddr=0x3ffb0000 size=0x01eb4 (  7860) load
I (113) esp_image: segment 2: paddr=0x0001dea8 vaddr=0x40080000 size=0x00400 (  1024) load
0x40080000: _WindowOverflow4 at /Users/[your user name]/esp/esp-idf/components/freertos/xtensa_vectors.S:1779

I (117) esp_image: segment 3: paddr=0x0001e2b0 vaddr=0x40080400 size=0x01d60 (  7520) load
I (129) esp_image: segment 4: paddr=0x00020018 vaddr=0x400d0018 size=0x14130 ( 82224) map
0x400d0018: _flash_cache_start at ??:?

I (163) esp_image: segment 5: paddr=0x00034150 vaddr=0x40082160 size=0x06dc4 ( 28100) load
0x40082160: heap_caps_realloc at /Users/[your user name]/esp/esp-idf/components/heap/heap_caps.c:308 (discriminator 1)

I (181) boot: Loaded app from partition at offset 0x10000
I (181) boot: Disabling RNG early entropy source...
I (181) cpu_start: Pro cpu up.
I (185) cpu_start: Application information:
I (190) cpu_start: Project name:     spi_master
I (195) cpu_start: App version:      1
I (200) cpu_start: Compile time:     Apr 12 2019 14:08:24
I (206) cpu_start: ELF file SHA256:  573d8bd8895faef1...
I (212) cpu_start: ESP-IDF:          v4.0-dev-311-g70eda3d22
I (218) cpu_start: Starting app cpu, entry point is 0x40080f00
0x40080f00: call_start_cpu1 at /Users/[your user name]/esp/esp-idf/components/esp32/cpu_start.c:267

I (0) cpu_start: App cpu up.
I (228) heap_init: Initializing. RAM available for dynamic allocation:
I (235) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (241) heap_init: At 3FFB2F00 len 0002D100 (180 KiB): DRAM
I (248) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (254) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (260) heap_init: At 40088F24 len 000170DC (92 KiB): IRAM
I (267) cpu_start: Pro cpu start user code
I (285) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (1285) spi_master: Allocate RX buffer for DMA
2725
I (2785) spi_master: Allocate RX buffer for DMA
2700
I (4285) spi_master: Allocate RX buffer for DMA
2700
```

Ctlキーと「 ] 」キーを同時に押すと終了

値が2700なら1/100して27℃となっていて、熱電対を触ったら値が変わるのでとりあえず動いているはず！

ということで、次はWiFi経由で値をWebアプリケーションに送信するというのを試してみる。