• Trill
  • Trill with ESP32 not working

I'm trying to get my Ring trill to work with an ESP32. I tested with my Bela mini and verified it works. From what I understand, with ESP32 there are a bunch of library calls. So I took this example and modified the I2c class. I added a rawread and rawwrite, and called those instead of ::read/::write in the Trill class.

The first problem I run into is that that write in identify() fails with error 2 (ESP_FAIL Sending command error, slave doesn’t ACK the transfer) when calling i2c_master_cmd_begin. When you write with ESP32 there is an option to request an ACK. If I disable this, the call succeeds, however it fails later. Is the ACK something the RING either supports or it doesn't? Should the RING ACK i2C messages?

if I get around the first problem by not requesting ACKs, identify fails when getting the first 4 bytes. The data will return 207, 207, 207, 0, but it returns error 263 (ESP_ERR_TIMEOUT Operation timeout because the bus is busy). What would a proper response look like?

I've tried adding 4.7k pullup resistors. I've also used the ESP32 tool and I can see the device at address 0x38. I can also use the seti2c and geti2c to return register values, but I don't understand how or if that relates to how the Trill class sends/receives data. But my thought is that it seems to be communicating.

I'll attach my updated i2c.h class. If you have any ideas on how to tshoot further I would appreciate it.

Paul

    BTW, I tried to attach the i2c.h, but it forever spun the Uploading button. I can paste the code, or share another way, if you want.

      I think Trills do send ACKs.

      PaulELong if I get around the first problem by not requesting ACKs, identify fails when getting the first 4 bytes. The data will return 207, 207, 207, 0, but it returns error 263 (ESP_ERR_TIMEOUT Operation timeout because the bus is busy). What would a proper response look like?

      This seems that the communication is not working as expected: an "operation timeout" seems to indicate something wrong.

      Have you tried using the Trill-Arduino library? We haven't tested it, but it should work with the ESP32, too .

        giuliomoro I'm not using Arduino ESP32 because I already have libraries and such setup for the IDF environment. But I tested it out and it works. So that means by board is configure properly. Guess I can play around more.

        When I set a the frequency I use 100000. Does that sound right?

        100kHz is fine, but it can get up to 400kHz, so why not using it at full speed ?

        I found my error, I had mistakenly switch SDA and SCL, so now I get readings. I duplicated the general-print code. It's spitting out a but of stuff even though I'm not touching it. Does that mean I need to setup some calibration?

        For instance I see this:

        T 570 59 3093 85
        B 0 4
        B 1 3
        T 2011 161
        B 0 2
        B 1 5
        T 2067 147
        B 0 3
        B 1 4

          PaulELong I had mistakenly switch SDA and SCL,

          but you it was working with the Arduino IDE!

          The call to Trill::updateBaseline() in Trill::begin() is what sets the calibration, so you should make sure you are not touching the device the program starts. Is that function being called successfully?

          Actually the arduino IDE also outputs data: I'm calling Trill::Setup and I see the call to updateBaseline there. It's just laying on my desk, so nothing is touching it. Should I adjust the sensitivity?

          T 547 50 3130 77 
          B 0 3 
          B 1 6 
          T 652 60 3131 79 
          B 0 1 
          T 648 58 3104 82 
          B 1 4 
          T 2127 145 
          B 1 5 
          T 593 58 3182 81 
          B 0 3 
          B 1 4 
          T 615 42 3168 60 
          B 0 2 
          B 1 5 

          Is it possible that there is some moist on the sensor that keeps it showing a reading? Those touches have a very small size, so you should be able to fix it by changing the detection threshold ... but I am confused be cause they seem to move around ... can you connect it to Bela and see if it behaves the same? Then you can use Trill/general-settings to switch between baseline, diff and raw modes (explained here) and see if you can make sense of what the source of the noise is.

          During troubleshooting, I added pull-up resistors by adding a jumper wire to another location on the breadboard, and then resistor to +V. I did this for both SCL and SDA. I just removed them and now there is no more noise and it's working like I expect. I think I'm all set...for now 🙂

          great then, would you be able to share the code changes needed to run it on ESP32 ?

          PaulELong BTW, I tried to attach the i2c.h, but it forever spun the Uploading button. I can paste the code, or share another way, if you want

          You cannot attach code here, only images. To share code, you have to paste it (enclosed in ```) or (perhaps better) make a github repo and share it here.

          @giuliomoro, seems it working every other time. The Ardunio version works every time. When it fails, it's here in indentify:

          	if(0 == dataBuffer[1]) {
          		device_type_ = NONE;
          		fprintf(stderr, "Buffer is zeros\n");
          		return -1;
          	}

          Any thoughts on why it might work every other time?

          No idea really. What code are you using in the end? That snippet looks like it comes from the Bela/Linux library.

          I'm using the Linux library. The only changes I made was to change the read and write calls to rawwrite and rawread. And then add ifdefs to i2c.h as follows:

          #pragma once
          
          #include <stdio.h>
          #include <unistd.h>
          #include <fcntl.h>
          
          #ifdef USE_ESP32
          #include <driver/i2c.h>
          #define MAX_I2C_TX_BUFFER_LENGTH		0
          #define MAX_I2C_RX_BUFFER_LENGTH		0
          #define CONFIG_I2C_SLAVE_SCL			22
          #define CONFIG_I2C_SLAVE_SDA			23
          #define CONFIG_I2C_MASTER_FREQUENCY		400000
          #define TICKS_TO_WAIT					1000 / portTICK_RATE_MS //TODO from example, how many ticks to wait??
          #define ACK_CHECK_EN i2c_ack_type_t(0x1)                        /*!< I2C master will check ack from slave*/
          #define ACK_CHECK_DIS i2c_ack_type_t(0x0)                       /*!< I2C master will not check ack from slave */
          
          #define ACK_VAL i2c_ack_type_t(0x0)                             /*!< I2C ack value */
          #define NACK_VAL i2c_ack_type_t( 0x1)                            /*!< I2C nack value */
          
          #else
          #include <linux/i2c-dev.h>
          // heuristic to guess what version of i2c-dev.h we have:
          // the one installed with `apt-get install libi2c-dev`
          // would conflict with linux/i2c.h, while the stock
          // one requires linus/i2c.h
          #ifndef I2C_SMBUS_BLOCK_MAX
          // If this is not defined, we have the "stock" i2c-dev.h
          // so we include linux/i2c.h
          #include <linux/i2c.h>
          typedef unsigned char i2c_char_t;
          #else
          typedef char i2c_char_t;
          #endif
          #endif
          
          #include <sys/ioctl.h>
          
          #define MAX_BUF_NAME 64
          
          class I2c
          {
          
          protected:
          	int i2C_bus;
          	int i2C_address;
          	int i2C_file;
          
          public:
          	int initI2C_RW(int bus, int address, int file);
          	virtual int readI2C();
          	int rawread(uint8_t* buf, size_t length);
          	int rawwrite(char* buf, uint8_t length);
          	int closeI2C();
          
          	virtual ~I2c();
          
          };
          
          inline int I2c::rawread(uint8_t* buf, size_t length)
          {
          #ifdef USE_ESP32
          	int ret;
          	// return i2c_master_write_buffer(i2C_bus, buf, length, TICKS_TO_WAIT);
              i2c_cmd_handle_t cmd = i2c_cmd_link_create();
              if((ret = i2c_master_start(cmd)))
          	{
          		printf("read i2c_master_start error %d\n", ret);
          	}
          
          	ret = i2c_master_write_byte(cmd, (i2C_address << 1) | I2C_MASTER_READ, ACK_CHECK_EN);
              if(ret)
          	{
          		printf("read i2c_master_write_byte address error %d\n", ret);
          	}
          
              if (length > 1) {
                  i2c_master_read(cmd, buf, length - 1, ACK_VAL);
              }
          
          	ret = i2c_master_read_byte(cmd, buf + length - 1, NACK_VAL);
              if(ret)
          	{
          		printf("read i2c_master_read_byte data error %d\n", ret);
          	}
          
              i2c_master_stop(cmd);
          
              ret = i2c_master_cmd_begin(i2C_bus, cmd, 1000 / portTICK_RATE_MS);
              if(ret)
          	{
          		printf("rawread i2c_master_cmd_begin error %d\n", ret);
          	}
          
          	i2c_cmd_link_delete(cmd);
          
          	return ret ? ret : length;
          #else
          	return ::read(i2C_file, buf, length);
          #endif
          }
          
          inline int I2c::rawwrite(char* buf, uint8_t length)
          {
          #ifdef USE_ESP32
          	// return i2c_master_read_byte(i2C_bus, (uint8_t*)buf, length, TICKS_TO_WAIT);
          	int ret;
          
              i2c_cmd_handle_t cmd = i2c_cmd_link_create();
              if((ret = i2c_master_start(cmd)))
          	{
          		printf("write i2c_master_start error %d\n", ret);
          	}
          	
          	ret = i2c_master_write_byte(cmd, (i2C_address << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN);
              if(ret)
          	{
          		printf("write i2c_master_read_byte address error %d\n", ret);
          	}
          
          	ret = i2c_master_write(cmd, (uint8_t*)buf, length, ACK_CHECK_EN);
          	if(ret)
          	{
          		printf("write i2c_master_read_byte data error %d\n", ret);
          	}
          
              if((ret = i2c_master_stop(cmd)))
          	{
          		printf("i2c_master_stop error %d\n", ret);
          	}
          	
          	ret = i2c_master_cmd_begin(i2C_bus, cmd, 1000 / portTICK_RATE_MS);
          	if(ret)
          	{
          		printf("rawwrite error %d\n", ret);
          	}
              i2c_cmd_link_delete(cmd);
          
          	return ret ? ret : length;
          #else
          	return ::write(i2C_file, buf, length);
          #endif
          }
          
          inline int I2c::initI2C_RW(int bus, int address, int fileHnd)
          {
          	i2C_bus 	= bus;
          	i2C_address = address;
          	i2C_file 	= fileHnd;
          
          #ifdef USE_ESP32
              i2c_config_t conf;
              conf.sda_io_num = CONFIG_I2C_SLAVE_SDA;
              conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
              conf.scl_io_num = CONFIG_I2C_SLAVE_SCL;
              conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
              conf.mode = I2C_MODE_MASTER;
              //conf.slave.slave_addr = i2C_address;
          	conf.master.clk_speed = CONFIG_I2C_MASTER_FREQUENCY;
          
          	int ret;
              ret = i2c_driver_install(i2C_bus, conf.mode, MAX_I2C_RX_BUFFER_LENGTH, MAX_I2C_TX_BUFFER_LENGTH, 0);
              if(ret)
          	{
          		printf("Install failed %d\n", ret);
          	}
          
          	ret = i2c_param_config(i2C_bus, &conf);
              if(ret)
          	{
          		printf("config failed %d\n", ret);
          	}
          
          	return ret;
          #else
          	open I2C device as a file
          	char namebuf[MAX_BUF_NAME];
          	snprintf(namebuf, sizeof(namebuf), "/dev/i2c-%d", i2C_bus);
          
          	if ((i2C_file = open(namebuf, O_RDWR)) < 0)
          	{
          		fprintf(stderr, "Failed to open %s I2C Bus\n", namebuf);
          		return(1);
          	}
          
          	// target device as slave
          	if (ioctl(i2C_file, I2C_SLAVE, i2C_address) < 0){
          		fprintf(stderr, "I2C_SLAVE address %#x failed...", i2C_address);
          		return(2);
          	}
          
          	return 0;
          #endif
          }
          
          
          
          inline int I2c::closeI2C()
          {
          #ifdef USE_ESP32
          
          	return i2c_driver_delete(i2C_bus);
          
          #else
          	if(close(i2C_file)>0)
          	{
          		fprintf(stderr, "Failed to close  file %d\n", i2C_file);
          		return 1;
          	}
          	return 0;
          #endif
          }
          
          
          inline I2c::~I2c(){}

          I am not familiar with this API but I cannot see much there that would be problematic...

          are you still sleeping 10ms between commands in Trill.cpp ?

          I didn't change trill.cpp, other than to call rawread and rawwrite. Maybe another clue. I hooked it up to a feather ESP32, which is the one I plan to use moving forward. It uses a battery and a I have a power switch. When I first turn it on it it fails. Then I reset it and it works. Reset it again and it fails.

            PaulELong Then I reset it and it works.

            What do you mean by "reset"? Is there a dedicated reset button or is it simply powercycling it? Does the reset power off and on the supply rail that powers the Trill?

            PaulELong When I first turn it on it it fails.

            Just a wild guess: maybe the ESP32 is extremely fast at booting after power on / reset and it sometimes reaches the trill when it is not yet ready to receive commands. Could you try sleeping for 50ms at the beginning of your code before doing anything with the Trill object?

            PaulELong Then I reset it and it works. Reset it again and it fails.

            does this cycle go on repeatedly and consistently? E.g.: at each reset does it keep alternating between working and failing?

            I mean hitting the reset button on the ESP32 feather. Or running the program again by executing the IDF.PY monitor command. It's very consistent I would say. If I turn off and on the power, however, it always starts with a failure. I've attached a video.

              PaulELong the reset button on the ESP32 feather

              yeah I don't know if that turns off/on all the supply rails or not ... Can you try with the 100ms delay after reset before doing anything on the Trill?

              it would also be interesting to simply perform Trill::setup() several times on a timer, maybe every 2 seconds or so: does it still alternate between success and failure?