LoginSignup
4
3

More than 3 years have passed since last update.

ESP32のI2C slave側サンプルコード(送信のみ)を作ってみた

Last updated at Posted at 2019-04-01

はじめに

ESP32をI2C通信のスレーブとして動作させるための良いサンプルコードを見つけることができかったため作成しました。

espressifのgithubのコードをベースにして、シンプルなコードに変更したものです。

※espressifのgithubのコードはESP-IDF(≠Arduino)ですので、その環境のお話となります(Arduinoでは使用できないサンプルコードです)

使用したもの

ESP-WROOM-32ピッチ変換済みモジュール《フル版》

サンプルコード概仕様

  • I2CマスターとI2Cスレーブ用に1つずつタスクを動作させる
  • I2Cスレーブ用タスク(i2c_test_task_0):2秒間隔でI2C送信を行う
  • I2Cマスター用タスク(i2c_test_task_1):1秒間隔でスレーブ用タスクからI2C受信を行う
    ※ I2C送信するデータの先頭に0x55(SYNC_BYTE)の1バイトを付けています(スレーブがデータ更新したことを判定するため)

ESP32の結線

下記のように結線します。

SDA SCL
I2C Master GPIO18 GPIO19
I2C Slave GPIO25 GPIO26

結線図: 
ESP32&ESP32.png

コード

変更概要

espressifのサンプルプロジェクトのi2c_example_main.cに対して以下の変更を加えます。

  • タスク用関数i2c_test_task():削除
  • i2c_master_task()とi2c_master_task():新規作成(i2c_test_taskの代わりにタスク用関数として作成する)
  • app_main():上記タスク用関数の変更に伴い変更

変更後コード(上記の「変更概要」の部分のみ記載)

#define DELAY_TIME_MASTER_MS 1000
#define DELAY_TIME_SLAVE_MS 2000
#define SYNC_BYTE 0x55

static void i2c_master_task(void *arg)
{
  int ret;
  uint32_t task_idx = (uint32_t)arg;
  uint8_t *data_rd = (uint8_t *)malloc(DATA_LENGTH);
  int cnt = 0;
  while (1) {
      ESP_LOGI(TAG, "TASK[%d](master) test cnt: %d", task_idx, cnt++);
      //---------------------------------------------------
      xSemaphoreTake(print_mux, portMAX_DELAY);
      ret = ESP_OK;
      while(ret == ESP_OK) {
        ret = i2c_master_read_slave(I2C_MASTER_NUM, data_rd, RW_TEST_LENGTH);
          if (ret == ESP_ERR_TIMEOUT) {
              ESP_LOGE(TAG, "I2C Timeout");
          } else if (ret == ESP_OK) {
              if (data_rd[0] == SYNC_BYTE) {
                printf("*******************\n");
                printf("TASK[%d]  MASTER READ FROM SLAVE\n", task_idx);
                printf("*******************\n");
                printf("====TASK[%d] Master read ====\n", task_idx);
                disp_buf(data_rd, RW_TEST_LENGTH);
              }
              else {
                printf("*******************\n");
                printf("TASK[%d]  MASTER READ FROM SLAVE\n", task_idx);
                printf("*******************\n");
                printf("NO DATA\n\n");
                ret = ESP_FAIL;
              }
          } else {
              ESP_LOGW(TAG, "TASK[%d] %s: Master read slave error, IO not connected...\n",
                       task_idx, esp_err_to_name(ret));
          }
      }
      xSemaphoreGive(print_mux);
      vTaskDelay((DELAY_TIME_MASTER_MS * (task_idx + 1)) / portTICK_RATE_MS);
    }
    vSemaphoreDelete(print_mux);
    vTaskDelete(NULL);
}

static void i2c_slave_task(void *arg)
{
  int i = 0;
  uint32_t task_idx = (uint32_t)arg;
  uint8_t *data = (uint8_t *)malloc(DATA_LENGTH);
  int cnt = 0;
  while (1) {
      ESP_LOGI(TAG, "TASK[%d](slave) test cnt: %d", task_idx, cnt++);

      //---------------------------------------------------
      data[0] = SYNC_BYTE;
      for (i = 1; i < DATA_LENGTH; i++) {
          data[i] = i;
      }
      xSemaphoreTake(print_mux, portMAX_DELAY);
      size_t d_size = i2c_slave_write_buffer(I2C_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS);
      if (d_size == 0) {
          ESP_LOGW(TAG, "i2c slave tx buffer full");
      }
      else {
        printf("slave send size: %d\n", d_size);
      }
      printf("*******************\n");
      printf("TASK[%d]  SLAVE SEND TO MASTER\n", task_idx);
      printf("*******************\n");
      disp_buf(data, d_size);
      xSemaphoreGive(print_mux);
      vTaskDelay((DELAY_TIME_SLAVE_MS * (task_idx + 1)) / portTICK_RATE_MS);
  }
  vSemaphoreDelete(print_mux);
  vTaskDelete(NULL);

}

void app_main()
{
    print_mux = xSemaphoreCreateMutex();
    ESP_ERROR_CHECK(i2c_slave_init());
    ESP_ERROR_CHECK(i2c_master_init());
    xTaskCreate(i2c_slave_task, "i2c_test_task_0", 1024 * 2, (void *)0, 10, NULL);
    xTaskCreate(i2c_master_task, "i2c_test_task_1", 1024 * 2, (void *)1, 10, NULL);
}

終わりに

上記のサンプルコードではI2Cスレーブは送信のみでしたが、i2c_slave_read_buffer()といった関数を使用することで、I2Cスレーブで受信することもできます。

また、I2Cスレーブの送信ということは過去の記事のヌンチャク側と同じです・・・ということは、このESP32でヌンチャクと同じようにI2C通信で振る舞うことができれば、某ゲーム機のヌンチャクとして使用することができそうです(ΦωΦ)フフフ…

見ていただいてありがとうございました。
тнайк чoμ_〆(・ω・。)

おまけ

I2Cスレーブの送信を割込み方式で出来たらいいなーというお話

今回作ったサンプルコードはベースコードと同じくポーリングの方式でI2Cの送信を行っていますが、その部分は使いにくく感じます。

そこで、ArduinoのWire.hの割込みハンドラ登録のように使うことができないかと思って少し調べると、「関数i2c_isr_registerを使ったら良いよ」という記事を所々で見つけました。

しかし、i2c_driver_installという初期設定を行う関数内でi2c_isr_handler_defaultというデフォルトのハンドラが既に登録されていたりして、「みだりに登録していいもんじゃないしなー」と少し調べる必要を感じつつも、その手間を現在放棄中です・・・。

更新履歴

  • 2019-04-01:新規作成
4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3