Tenho os seguintes códigos no Labrador e no Arduino:
Labrador:
import caninos_sdk as k9
import time
labrador = k9.Labrador()
labrador.i2c.add_device("indefinido", 127)
def mudarEndereco(novo, nome):
print("Mudando endereço")
bd = bytes([138, novo])
labrador.indefinido.write(bd)
labrador.i2c.add_device(nome, novo)
print("Endereço alterado")
s = time.time()
mudarEndereco(0, "sensordelinha")
while True:
resposta = labrador.sensordelinha.read(1)
print (str(time.time() - s)+" - "+str(resposta))
time.sleep(0.1)
Arduino: (Parte do código foi omitida pela irrelevância)
byte address = 0x7F;
byte mode;
char *modeData;
enum regAddr{
READ_SENSORS = 0X00,
I2C_SLAVE_DEVICE_ADDRESS = 0x8A,
};
void readData(){
byte len = Wire.available();
modeData = malloc(len);
for(int i=0; i<len; i++){
modeData[i]=Wire.read();
}
}
void onI2CReceive(){
debugLED();
if(!Wire.available()){
onI2CRequest();
return;
}
Serial.println("Receiving I2C data");
Serial.print("Data length: ");
Serial.println(Wire.available());
byte command = Wire.read();
Serial.print("Command: ");
Serial.println(command);
readData();
switch(command){
case READ_SENSORS:
mode = command;
return;
case I2C_SLAVE_DEVICE_ADDRESS:
address = modeData[0];
Serial.print("Changing address to ");
Serial.println(address);
Wire.end();
Wire.begin(address);
freeData();
return;
}
}
A idéia é que o Arduino sirva como um dispositivo slave e o Labrador master, o Arduino tem um endereço inicial 0x7F, e receberá um comando (0x8A) do Labrador com o novo endereço dele (0). Isso significa que o Labrador envia 2 bytes para o Arduino.
Só que o Arduino recebe 3 bytes, com a seguinte linha: 0x00 0x8A 0x00
Então o Arduino imprime isso:
01:21:04.876 → Receiving I2C data
01:21:04.876 → Data length: 3
01:21:04.876 → Command: 0
Verificando nessa parte do código da caninos_sdk
def write(self, data):
return self.libi2c_device.write(0x0, data)
Vemos que ele envia 2 parâmetros para a libi2c_device. Que nessa linha vemos que os parâmetros são iaddr e buf.
if (!PyArg_ParseTuple(args, "Is#::write", &iaddr, &buf, &size)) {
Que manda para a write_handle, que manda para a i2c_ioctl_write
ssize_t i2c_ioctl_write(const I2CDevice *device, unsigned int iaddr, const void *buf, size_t len)
{
ssize_t remain = len;
size_t size = 0, cnt = 0;
const unsigned char *buffer = buf;
unsigned char delay = GET_I2C_DELAY(device->delay);
unsigned short flags = GET_I2C_FLAGS(device->tenbit, device->flags);
struct i2c_msg ioctl_msg;
struct i2c_rdwr_ioctl_data ioctl_data;
unsigned char tmp_buf[PAGE_MAX_BYTES + INT_ADDR_MAX_BYTES];
while (remain > 0) {
size = GET_WRITE_SIZE(iaddr % device->page_bytes, remain, device->page_bytes);
/* Convert i2c internal address */
memset(tmp_buf, 0, sizeof(tmp_buf));
i2c_iaddr_convert(iaddr, device->iaddr_bytes, tmp_buf);
/* Connect write data after device internal address */
memcpy(tmp_buf + device->iaddr_bytes, buffer, size);
/* Fill kernel ioctl i2c_msg */
memset(&ioctl_msg, 0, sizeof(ioctl_msg));
memset(&ioctl_data, 0, sizeof(ioctl_data));
ioctl_msg.len = device->iaddr_bytes + size;
ioctl_msg.addr = device->addr;
ioctl_msg.buf = tmp_buf;
ioctl_msg.flags = flags;
ioctl_data.nmsgs = 1;
ioctl_data.msgs = &ioctl_msg;
if (ioctl(device->bus, I2C_RDWR, (unsigned long)&ioctl_data) == -1) {
perror("Ioctl write i2c error:");
return -1;
}
/* XXX: Must have a little time delay */
i2c_delay(delay);
cnt += size;
iaddr += size;
buffer += size;
remain -= size;
}
return cnt;
}
Se prestarmos atenção nessa linha:
/* Connect write data after device internal address */
memcpy(tmp_buf + device->iaddr_bytes, buffer, size);
Vemos que ele junta o endereço interno com os dados. Sendo o endereço interno 0x00.
Se essa linha for o problema, significa que o problema pode não ser na caninos_sdk em si, a raíz do problema está na pylibi2c mesmo… Certo?
…
Certo?
ERRADO kkkkkkkkkk
Inicialmente pensei que fosse algum bug, mas na verdade, é um recurso que falta na biblioteca do caninos_sdk.
O endereço interno (iaddr) é o que identifica o endereço de registro que você quer gravar ou acessar o dado, que usamos para realizar certas funções, exatamente como o de alterar seu endereço no barramento, como o que quero implementar. A biblioteca pylibi2c nos permite selecionar o endereço interno, mas a caninos_sdk não, deixando ela sempre na 0x00.
Então quando tento indicar o endereço do registro no próprio buffer pelo caninos_sdk, o pylibi2c fala: “nonononono, você quer o endereço 0x00 e o 0x8A é só um dado”, porque ele já tem o seu jeito de indicar que é passando no parâmetro dele.
A mesma coisa funciona na função de leitura…
Solução é ou implementar um parâmetro para definir o endereço do registro no write() e no read() do caninos_sdk, e com o poder do Python, quando o parâmetro não for especificado quando chamar a função, manter 0x00 como padrão. Ou só usar o pylibi2c.
Vou fazer uma pull request em breve.