Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't read from I2C #15

Open
khogeland opened this issue Nov 19, 2021 · 1 comment
Open

Can't read from I2C #15

khogeland opened this issue Nov 19, 2021 · 1 comment

Comments

@khogeland
Copy link

khogeland commented Nov 19, 2021

I'm trying to use a peripheral from Adafruit which communicates over I2C. The same setup works fine with their Arduino library. I copied the protocol from there, and I can't see anything wrong with my implementation, but it doesn't work via Nesper 🤷‍♂️ I've tried messing with the clock speed, different pins, different pullups, etc. I can tell there's some communication happening, because any other address hangs the system (wish I had an oscilloscope), but all I get is 0xFF back for every byte for every register I try.

Any ideas? Am I doing something wrong?

import nesper/timers
import nesper/i2cs
import nesper
#

const
  TAG*: cstring = "input"
  SCL_PIN = gpio_num_t(22)
  SDA_PIN = gpio_num_t(23)
  SEESAW_ADC_BASE = 0x09'u8
  SEESAW_GPIO_BASE = 0x01'u8
  SEESAW_ADC_CHANNEL_OFFSET = 0x07'u8
  SEESAW_GPIO_DIRSET_BULK = 0x02'u8
  SEESAW_GPIO_DIRCLR_BULK = 0x03'u8
  SEESAW_GPIO_BULK = 0x04'u8
  SEESAW_GPIO_BULK_SET = 0x05'u8
  SEESAW_GPIO_BULK_CLR = 0x06'u8
  SEESAW_GPIO_BULK_TOGGLE = 0x07'u8
  SEESAW_GPIO_INTENSET = 0x08'u8
  SEESAW_GPIO_INTENCLR = 0x09'u8
  SEESAW_GPIO_INTFLAG = 0x0A'u8
  SEESAW_GPIO_PULLENSET = 0x0B'u8
  SEESAW_GPIO_PULLENCLR = 0x0C'u8
  BUTTON_RIGHT= 6
  BUTTON_DOWN = 7
  BUTTON_LEFT = 9
  BUTTON_UP   = 10
  BUTTON_SEL  = 14
  button_mask: uint32 = (1 shl BUTTON_RIGHT) or (1 shl BUTTON_DOWN) or 
                  (1 shl BUTTON_LEFT) or (1 shl BUTTON_UP) or (1 shl BUTTON_SEL)

let port1 = newI2CMaster(port = I2C_NUM_0,
                          sda_io_num = SDA_PIN, ## !< GPIO number for I2C sda signal
                          scl_io_num = SCL_PIN, ## !< GPIO number for I2C scl signal
                          clk_speed = 100_000.Hertz,
                          sda_pullup_en = false, ## !< Internal GPIO pull mode for I2C sda signal
                          scl_pullup_en = false, ## !< Internal GPIO pull mode for I2C scl signal
                          intr_alloc_flags = {})


proc seesawWrite(bs: seq[uint8]) =
  var cmd = port1.newCmd()
  cmd.start()
  cmd.writeTo(0x49)
  for b in bs:
    cmd.writeByte(b)
  cmd.stop()
  port1.submit(cmd, 10.Millis)

proc seesawRead(bs: seq[uint8], size: uint): seq[uint8] =
  result = newSeq[uint8](size)
  seesawWrite(bs)
  delayMillis(1)
  var readCmd = port1.newCmd()
  readCmd.start()
  readCmd.readFrom(0x49)
  readCmd.read(result, ACK)
  readCmd.stop()
  port1.submit(readCmd, 10.Millis)

proc initInput*() = 
  let maskcmd = @[uint8(button_mask shr 24), uint8(button_mask shr 16),
                   uint8(button_mask shr 8), uint8(button_mask and 0xFF)]
  seesawWrite(@[SEESAW_GPIO_BASE, SEESAW_GPIO_DIRCLR_BULK] & maskCmd)
  seesawWrite(@[SEESAW_GPIO_BASE, SEESAW_GPIO_PULLENSET] & maskCmd)
  seesawWrite(@[SEESAW_GPIO_BASE, SEESAW_GPIO_BULK_SET] & maskCmd)
  seesawWrite(@[SEESAW_GPIO_BASE, SEESAW_GPIO_INTENSET] & maskCmd)

proc printButtons*() =
  echo(seesawRead(@[SEESAW_GPIO_BASE, SEESAW_GPIO_BULK], 8))
  echo(seesawRead(@[SEESAW_ADC_BASE, SEESAW_ADC_CHANNEL_OFFSET], 2))
@elcritch
Copy link
Owner

The esp-lib has given me lots of headaches with I2C. It looks like you have sda_pullup_en and scl_pullup_en disabled. NVM, you said you tried out different configurations.

There's a few things to try. I had a lot of difficulty with I2C on esp-lib which is what Nesper wraps around. AFAICT, it's with the esp-lib itself and not the Nesper wrappers. The Arduino libraries define a pretty sane set of defaults for dealing with NACK and ACK's while the esp-lib version are much lower level.

I actually moved to using esp-idf-lib I2C drivers in my latest Nesper projects because it was more reliable. They wrap the ACK/NACK's for you and it works for most proper I2C devices. You can see an example on the devel branch here.

  • the clock speed is set to 100_000.Hertz which is a bit slow for some devices, they sometimes prefer 400_000.Hertz
  • sometimes the ACK vs LAST_NACK can be the difference. Most devices do a NACK on the final read byte
  • similar thing for the last writeByte where you may need to do a writeByte(ack=false)

Also, you might consider trying Nephyr as it supports a lot of ESP32 features but is much more stable. It's what I'm using these days for new projects. :-)

Though for Nesper I'd recommend the esp-idf-lib I2C driver as the way to go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants