Sygaldry
Loading...
Searching...
No Matches
sygse-arduino_hack: ESP32 Arduino Hack

Copyright 2023 Travis J. West, https://traviswest.ca, Input Devices and Music Interaction Laboratory (IDMIL), Centre for Interdisciplinary Research in Music Media and Technology (CIRMMT), McGill University, Montréal, Canada, and Univ. Lille, Inria, CNRS, Centrale Lille, UMR 9189 CRIStAL, F-59000 Lille, France

SPDX-License-Identifier: LGPL-2.1-or-later

This document describes the ESP32 implementation of the Arduino APIs supported by Sygaldry's Arduino hack subsystem. Although an Arduino library implementation exists for ESP32, it tends to lag behind the main ESP-IDF version. At the time of writing, the ESP32 Arduino library is not available with a compiler that is compatible with Sygaldry, so Arduino hack is necessary.

An IDF component CMakeLists.txt registers the implementation with the framework, and an implementation of the TwoWire API is provided.

ESP Component CMakeLists.txt

# @#'CMakeLists.txt'
set(lib sygse-arduino_hack)
add_library(${lib} INTERFACE)
target_link_libraries(${lib} INTERFACE sygsp-arduino_hack)
target_link_libraries(${lib} INTERFACE idf::driver idf::esp_timer)
target_sources(${lib} INTERFACE Arduino.cpp Wire.cpp)
# @/

ESP Arduino.h

// @#'Arduino.cpp'
/*
Copyright 2023 Travis J. West, https://traviswest.ca, Input Devices and Music Interaction Laboratory
(IDMIL), Centre for Interdisciplinary Research in Music Media and Technology
(CIRMMT), McGill University, Montréal, Canada, and Univ. Lille, Inria, CNRS,
Centrale Lille, UMR 9189 CRIStAL, F-59000 Lille, France
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "Arduino.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "esp_timer.h"
void pinMode(uint8_t pin, uint8_t mode)
{
}
void digitalWrite(uint8_t pin, uint8_t val)
{
}
unsigned long micros()
{
return static_cast<unsigned long>(esp_timer_get_time());
}
void delay(unsigned long ms)
{
vTaskDelay(pdMS_TO_TICKS(ms));
}
// @/
void delay(unsigned long ms)
Definition sygsa-delay.cpp:14

ESP TwoWire API

We define a very simple implementation of this API in terms of the ESP-IDF. It remains as future work to properly document this implementation, but it is hoped that the reader will find it reasonably straightforward.

// @#'Wire.cpp'
/*
Copyright 2023 Travis J. West, https://traviswest.ca, Input Devices and Music Interaction Laboratory
(IDMIL), Centre for Interdisciplinary Research in Music Media and Technology
(CIRMMT), McGill University, Montréal, Canada, and Univ. Lille, Inria, CNRS,
Centrale Lille, UMR 9189 CRIStAL, F-59000 Lille, France
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "Wire.h"
#include "driver/i2c.h"
#include <cstdio>
namespace
{
static constexpr i2c_port_t _port = I2C_NUM_0;
static uint8_t _tx_address = 0;
static uint8_t _tx_idx = 0;
static uint8_t _tx_buffer[16] = {0};
static uint8_t _rx_idx = 0;
static uint8_t _rx_length = 0;
static uint8_t _rx_buffer[BUFFER_LENGTH] = {0};
static bool _repeated_start = false;
// static i2c_cmd_handle_t _cmd = nullptr;
static constexpr TickType_t _timeout = pdMS_TO_TICKS(2000);
// bool cmd_link_error_check(esp_err_t err, const char * context)
// {
// switch (err)
// {
// case ESP_OK: return false;
// case ESP_ERR_INVALID_ARG:
// printf("%s: invalid argument\n", context);
// break;
// case ESP_ERR_NO_MEM:
// printf("%s: not enough memory in static buffer\n", context);
// break;
// case ESP_FAIL:
// printf("%s: not enough memory on heap\n", context);
// break;
// }
// return true;
// }
//
// void start_cmd_list(auto address, auto readwrite)
// {
// _cmd = i2c_cmd_link_create();
// if (_cmd == nullptr)
// {
// printf("TwoWire::start_cmd_list: unable to allocated command link\n");
// return;
// }
// auto err = i2c_master_start(_cmd);
// if (cmd_link_error_check(err, "TwoWire::start_cmd_list start bit")) return;
// err = i2c_master_write_byte(_cmd, address << 1 | readwrite, true);
// if (cmd_link_error_check(err, "TwoWire::start_cmd_list write address")) return;
// }
//
//
// void send_cmd_list()
// {
// esp_err_t err = i2c_master_cmd_begin(_port, _cmd, _timeout);
// _rx_idx = 0;
// switch (err)
// {
// case ESP_OK:
// break;
// case ESP_ERR_INVALID_ARG:
// printf("TwoWire::send_cmd_list: invalid argument\n");
// break;
// case ESP_FAIL:
// printf("TwoWire::send_cmd_list: failure; no subnode NACK\n");
// break;
// case ESP_ERR_INVALID_STATE:
// printf("TwoWire::send_cmd_list: invalid state; was TwoWire::begin() called successfully?\n");
// break;
// case ESP_ERR_TIMEOUT:
// printf("TwoWire::send_cmd_list: i2c bus timeout\n");
// break;
// }
// i2c_cmd_link_delete(_cmd);
// _cmd = nullptr;
// }
}
TwoWire::TwoWire()
{
};
void TwoWire::begin()
{
// there's no way without arduino to know what pins to use...
// so the user should really call begin() manually before calling
// Trill::begin() or anything else that calls Wire::begin()...
}
void TwoWire::begin(int sda_pin, int scl_pin, uint32_t frequency)
{
printf("Wire: begin(%d, %d, %ld)\n", sda_pin, scl_pin, frequency);
if (frequency > 400000) frequency = 400000;
i2c_config_t config = {
.mode = I2C_MODE_MASTER,
.sda_io_num = sda_pin,
.scl_io_num = scl_pin,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master = { .clk_speed = frequency, },
.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL,
};
esp_err_t err = i2c_param_config(_port, &config);
if (err != ESP_OK)
{
printf("Wire error: i2c_param_config invalid argument\n");
return;
}
err = i2c_driver_install(_port, config.mode, 0, 0, 0);
if (err != ESP_OK) switch (err)
{
case ESP_ERR_INVALID_ARG:
printf("Wire error: i2c_driver_install invalid argument\n");
return;
case ESP_FAIL:
printf("Wire error: i2c_driver_install failure\n");
return;
}
}
void TwoWire::beginTransmission(uint8_t address)
{
//printf("beginTransmission %x\n", address);
_tx_address = address;
}
void TwoWire::write(uint8_t b)
{
//printf("write %x\n", b);
_tx_buffer[_tx_idx++] = b;
}
void TwoWire::write(uint8_t * buffer, uint8_t length)
{
//printf("write "); for (int i = 0; i < length; ++i) printf("%x ", buffer[i]); printf("\n");
for (std::size_t i = 0; i < length; ++i) write(buffer[i]);
}
void TwoWire::endTransmission(bool sendStop)
{
//printf("endTransmission %d\n", sendStop);
if (sendStop)
{
_repeated_start = false;
esp_err_t err = i2c_master_write_to_device(_port, _tx_address, _tx_buffer, _tx_idx, _timeout);
//printf("justwrite: tx - ");
//for (int i = 0; i < _tx_idx; ++i)
// printf("%d ", _tx_buffer[i]);
//printf("\n");
_tx_idx = 0;
if (err == ESP_OK) return;
switch (err)
{
case ESP_ERR_INVALID_ARG:
printf("TwoWire::endTransmission: invalid argument\n");
return;
case ESP_FAIL:
printf("TwoWire::endTransmission: failure; no subnode ACK\n");
return;
case ESP_ERR_INVALID_STATE:
printf("TwoWire::endTransmission: invalid state; was TwoWire::begin() called successfully?\n");
return;
case ESP_ERR_TIMEOUT:
printf("TwoWire::endTransmission: i2c bus timeout\n");
return;
}
}
else
{
_repeated_start = true;
return;
}
}
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t length)
{
//printf("requestFrom %x %d %d\n", address, length, _repeated_start);
esp_err_t err;
if (_repeated_start)
{
_repeated_start = false;
err = i2c_master_write_read_device(_port, address, _tx_buffer, _tx_idx, _rx_buffer, length, _timeout);
_tx_idx = 0;
}
else
{
err = i2c_master_read_from_device(_port, address, _rx_buffer, length, _timeout);
}
_rx_idx = 0;
switch(err)
{
case ESP_OK:
_rx_length = length;
return length;
case ESP_ERR_INVALID_ARG:
printf("TwoWire::requestFrom: invalid argument\n");
break;
case ESP_FAIL:
printf("TwoWire::requestFrom: failure; no subnode ACK\n");
break;
case ESP_ERR_INVALID_STATE:
printf("TwoWire::requestFrom: invalid state; was TwoWire::begin() called successfully?\n");
break;
case ESP_ERR_TIMEOUT:
printf("TwoWire::requestFrom: i2c bus timeout\n");
break;
}
return 0;
}
uint8_t TwoWire::available()
{
return _rx_length - _rx_idx;
}
uint8_t TwoWire::read()
{
if (_rx_idx < _rx_length)
return _rx_buffer[_rx_idx++];
else return 0;
}
TwoWire Wire{};
// @/