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.
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.
#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 constexpr TickType_t _timeout = pdMS_TO_TICKS(2000);
}
TwoWire::TwoWire()
{
};
void TwoWire::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)
{
_tx_address = address;
}
void TwoWire::write(uint8_t b)
{
_tx_buffer[_tx_idx++] = b;
}
void TwoWire::write(uint8_t * buffer, uint8_t length)
{
for (std::size_t i = 0; i < length; ++i) write(buffer[i]);
}
void TwoWire::endTransmission(bool sendStop)
{
if (sendStop)
{
_repeated_start = false;
esp_err_t err = i2c_master_write_to_device(_port, _tx_address, _tx_buffer, _tx_idx, _timeout);
_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)
{
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{};