wagiminator / ATtiny412-I2C-Rotary-Encoder

Rotary Encoder with I²C Interface
https://oshwlab.com/wagiminator/attiny412-i2c-rotary-encoder
Other
36 stars 8 forks source link

rpi python - only get state of rotary encoder wheel but not from switch #2

Open mcpat-it opened 10 months ago

mcpat-it commented 10 months ago

Hi,

I tried to get the value with python on raspberry pi via smbus, but I don't know how to get the rotary enoder switch state, could you please help? Here my code for now which is reading the value of the wheel:

from smbus import SMBus
import time

addr = 0x36 # bus address
bus = SMBus(1) # indicates /dev/ic2-1
oldvalue = 0

while True:
    try:
        z = bus.read_byte(addr)
        if z != oldvalue:
            oldvalue = z
            print(z, end = "\r")

        time.sleep(0.25)
    except:
        print('exiting...')
        break

And second question, is there a better way to get the actual value without a while loop?

Thank you in advance, Patrick

wagiminator commented 10 months ago

Hi, I'm not familiar with smbus. However, you have to read at least 3 bytes from the I2C bus. The first 2 bytes are the rotary value, the third byte ist the switch state.

mcpat-it commented 10 months ago

thx for the fast answer, I tried yesterday up to the time to go to bed ;)

I tried this code, which is working if I modify the firmware of the rotary encoder:

from smbus import SMBus
import time

addr = 0x36 # bus address
data_fetch = 0x00

bus = SMBus(1) # indicates /dev/ic2-1
oldvalue = 0
pressed = 0

#not working
#bus.write_i2c_block_data(addr, 0x0, [0x1, 0x20, 0x01, 0x00])

while True:
    try:
        z = bus.read_i2c_block_data(addr, 0x0, 3)
        if z[0] != oldvalue or z[2] != pressed:
            oldvalue = z[0]
            pressed = z[2]
            print("0: " + str(z[0]))
            print("1: " + str(z[1]))
            print("2: " + str(z[2]))

        time.sleep(0.25)
    except:
        print('exiting...')
        break

What I had to do, to get any values, is to prevent to write anything because the read_i2c_block_data is writting a value first, and then it is reading. The value is the second parameter, in my case 0x0.

So I inserted in your firmware:

Last line ENC_set(0, 9, 1, 5, I2C_REG[3]);:

int main(void) {
  // Local variables
  int16_t rval, rmin, rmax, rstep;                // for handling encoder parameters

  // Setup
  _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 1);         // set clock frequency to 10 MHz
  I2C_init();                                     // setup I2C
  ENC_init();                                     // setup rotary encoder  
  sei();                                          // enable interrupts

  ENC_set(0, 9, 1, 5, I2C_REG[3]); 
...

And I "disabled" the I2C_REG_changed "function". Additional, and I don't know why, I had to add on a lower stage I2C_REG[0] = (uint8_t)(rval & 0xff); and I2C_REG[1] = (uint8_t)(rval >> 8); again... Without adding, I can only get the value of the switch which is your code line I2C_REG[2] = !pinRead(PIN_ENC_SW);

// Loop
  while(1) {
    // Update rotary encoder settings if I2C registers have changed
    if(I2C_REG_changed && !I2C_busy) {
      //cli();                                      // disable interrupts for atomic ops
      //rval  = ((uint16_t)I2C_REG[1] << 8) | (uint16_t)I2C_REG[0];
      //rmin  = ((uint16_t)I2C_REG[5] << 8) | (uint16_t)I2C_REG[4];
      //rmax  = ((uint16_t)I2C_REG[7] << 8) | (uint16_t)I2C_REG[6];
      //rstep = ((uint16_t)I2C_REG[9] << 8) | (uint16_t)I2C_REG[8];
      I2C_REG_changed = 0;                        // clear register changed flag
      //sei();                                      // enable interrupts again
      //ENC_set(rmin, rmax, rstep, rval, I2C_REG[3]); // set rotary encoder
    }

    // Update I2C registers if rotary encoder wheel value has changed
    if(ENC_changed && !I2C_REG_changed && !I2C_busy) {
      cli();                                      // disable interrupts for atomic ops
      rval = ENC_count / 2;                       // get encoder counter value
      I2C_REG[0] = (uint8_t)(rval & 0xff);        // store low byte to register
      I2C_REG[1] = (uint8_t)(rval >> 8);          // store high byte to register
      ENC_changed = 0;                            // clear encoder changed flag
      sei();                                      // enable interrupts again
    }

    // Update rotary encoder switch register
    I2C_REG[2] = !pinRead(PIN_ENC_SW);

    I2C_REG[0] = (uint8_t)(rval & 0xff);        // store low byte to register
    I2C_REG[1] = (uint8_t)(rval >> 8);          // store high byte to register
  }

So my main questions are

  1. why do I have to add I2C_REG 0 and 1 again to get values?
  2. How can I write the ENC_set with Python?
  3. How can I prevent the 0x00 is deleting the ENC_set values, which happens if I don't comment out the lines in the beginning of your code
  4. Is there any chance to get a "notice" if something is changing (e.g. switch is pressed or release) whitout looping? I saw in your I2C_RotaryEncoder_Test.ino you are also loop.

Sorry for this questions, I can code in c and other languages, but Pyhton is not my power language and I2C is my first try. So the firmware has a lot of lines like cli(), sei() and ISR which I didn't see before.

Thank you again for your help! Btw, I am from Austria and your name sounds german ;)

Regards Patrick

wagiminator commented 9 months ago

I can't understand your changes at the moment. Have you also changed anything in the interrupt service routines? (sei(), cli() and ISR belong to interrupts, which are triggered whenever the rotary encoder is messed with or I2C communication is running). It's also not entirely clear to me when you're talking about the source code for the ATtiny (it's written in C) and when you're talking about the software on the other side of the I2C connection (in your case, it's probably Python on the Raspberry Pi).