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

Codatex RFID on EV3 #913

Closed
JorgePe opened this issue Jul 10, 2017 · 19 comments
Closed

Codatex RFID on EV3 #913

JorgePe opened this issue Jul 10, 2017 · 19 comments

Comments

@JorgePe
Copy link
Contributor

JorgePe commented Jul 10, 2017

I would appreciate some hints with the Codatex RF ID Sensor for NXT:
http://www.codatex.com/lego-sensor.html

I'm running
ev3dev 4.4.68-20-ev3dev-ev3
(still jessie, not stretch yet)

I've read some code from LeJOS and RobotC but it seems I don't still understand all of it.

When I have it on Input 1
echo other-i2c > /sys/class/lego-port/port0/mode

dmesg:
i2c-legoev3 i2c-legoev3.3: registered on input port 1

The device needs a dummy I2C write to wake up, I can read the basic information fine:

/usr/sbin/i2cset -y 3 0x02 0x00; /usr/sbin/i2cdump -y 3 0x02
Error: Write failed
No size specified (using byte-data access)
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
00: 56 31 2e 30 00 00 00 00 43 4f 44 41 54 45 58 00    V1.0....CODATEX.
10: 52 46 49 44 00 00 00 00 00 00 00 00 00 00 00 00    RFID............
20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................

But when trying to read the RFID tags I always get five zeros - usually a symptom of improper timings.
As everybody say that timings are important I tried python and smbus:


#!/usr/bin/env python3

from smbus import SMBus
from time import sleep

DELAY_WAKEUP = 0.005
DELAY_FIRMWARE = 0.100
DELAY_ACQUIRE = 0.250

bus = SMBus(3)

#wakeup
bus.write_quick(0x02)
sleep(DELAY_WAKEUP)

#init fw
bus.write_byte_data(0x02,0x41,0x83)
sleep(DELAY_FIRMWARE)

while True:
    sleep(1)

    #wakeup
    bus.write_quick(0x02)
    sleep(DELAY_WAKEUP)

    #read single shot mode
    bus.write_byte_data(0x02,0x41,1)
    sleep(DELAY_ACQUIRE)

    #read tag ID
    data = bus.read_i2c_block_data(0x02,0x42,5)
    print(data)

I'm using same timings as LeJOS and RobotC
https://github.com/SnakeSVx/ev3/blob/master/Lejos/src/main/java/lejos/hardware/sensor/RFIDSensor.java
http://botbench.com/driversuite/codatech-rfid_8h_source.html

but clearly not doing it right. Also tried Continuous Read, same result.

@JorgePe
Copy link
Contributor Author

JorgePe commented Jul 10, 2017

It seems that the Codatex sensor as a LED that turns ON when it detects a tag. Never saw any kind of light on mine and I'm using the two tags that came with it.
I guess I'll have to try it with LeJOS or even a NXT to be sure that it works.

@dlech
Copy link
Member

dlech commented Jul 10, 2017

Did you try starting the firmware?

    /**
     * Start the firmware on the RFID device.
     * NOTES: It seems that you need to issue this command (or some other
     * firmware command), prior to attempting to read the version number etc.
     * Does not wake up the device or contain any delays.
     */
    public void startFirmware()
    {
        sendData(REG_CMD, CMD_STARTFIRMWARE);
    }

@JorgePe
Copy link
Contributor Author

JorgePe commented Jul 10, 2017

Yes, I think so, at least that is the purpose of this part:

bus.write_byte_data(0x02,0x41,0x83)
sleep(DELAY_FIRMWARE)

I can also start the bootloader (writing 0x81 instead of 0x83 to the command register 0x41) and read the serial number (a block of 16 bytes starting at 0xA0), I get

[32, 16, 32, 7, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]

that's probably correct since there is nothing at 0xA0...0xAF in normal operation mode, as seen in my first dump.

i found here that
It seems that this sensor requires 9V to operate
never heard about a 9V mode on EV3 I2C... does this LeJOS line:
myPort.setType(I2CPort.TYPE_LOWSPEED_9V);
make any sense to you?

@dlech
Copy link
Member

dlech commented Jul 10, 2017

It seems that this sensor requires 9V to operate

Ah, ha! ev3dev does not turn on 9V unless the sensor needs it, so it means you will have to write a kernel driver for it - or we have to a new port mode, e.g. other-i2c-9v - or we just always turn on 9V when the port is in I2C mode.

The input ports are capable of outputting 9V (battery voltage really) on pin 1.

@jabrena
Copy link

jabrena commented Jul 10, 2017

Hi @JorgePe,

it is a classic syntax to define the Port type but you are checking NXT syntax. In LeJOS, the sensors are sorted by the way to connect: i2c, uart, etc... at the end, in the constructor, you set 2 parameters:

  • Physical port: SensorPort.S1, SensorPort.S2, SensorPort.S3 & SensorPort.S4
  • The i2c address, normally 0x02.

Later, once you have connected, you have to review the technical documentation from the manufacturer to play with the registers. In the past, more or less, we had the same way run to with NXC, RobotC & LeJOS.

Example:
https://sourceforge.net/p/lejos/ev3/code/ci/master/tree/ev3classes/src/lejos/hardware/sensor/MindsensorsAbsoluteIMU.java

/** The default I2C address of the sensor */
  public static final int     DEFAULT_I2C_ADDRESS = 0x22;
  protected static final int  ACCEL_DATA          = 0x45;
  protected static final int  COMPASS_DATA        = 0x4b;
  protected static final int  MAG_DATA            = 0x4d;
  protected static final int  GYRO_DATA           = 0x53;
  protected static final int  COMMAND             = 0x41;
  protected static final int  GYRO_FILTER         = 0x5a;
  protected static final byte SENSITIVITY_BASE    = 0x31;
  protected static final byte START_CALIBRATION   = 0x43;
  protected static final byte END_CALIBRATION     = 0x63;
  public static final int     LOW                 = 0;
  public static final int     MEDIUM              = 1;
  public static final int     HIGH                = 2;
  public static final int     VERY_HIGH           = 3;

Example of usage of the method to write a I2C register:

public void setRange(int range) {
    byte cmd = SENSITIVITY_BASE;
    switch (range) {
    case LOW:
    case MEDIUM:
    case HIGH:
    case VERY_HIGH:
      break;
    default:
      throw new IllegalArgumentException("Range setting invalid");
    }
    cmd += range;
    sendData(COMMAND, cmd);
    // update gyro scale to match new setting
    gyroMode.setScale(gyroScale[range]);
  }

To send a register, you use:

//Example
sendData(0x41, 0x31);

How to read a I2CSensor?:

@Override
    public void fetchSample(float[] sample, int offset) {
      // fetch the raw data
      getData(baseReg, buffer, 0, buffer.length);
      for (int i = 0; i < sampleSize; i++) {
        int rawVal = (buffer[i * 2] & 0xff) | ((buffer[i * 2 + 1]) << 8);
        sample[i + offset] = rawVal * scale[i];
      }
    }

You use this method:

getData(baseReg, buffer, 0, buffer.length);

You set the register for the mode, in this case and you store in a buffer:

protected byte[]        buffer;
buffer = new byte[sampleSize * 2];

and finally, you operate with the result from the i2c output.

Technical docs about I2CSensor:
http://www.lejos.org/ev3/docs/lejos/hardware/sensor/I2CSensor.html

Take a look this doc, is a bit old but with this notes, I developed some drivers in the past for NXT:
http://www.juanantonio.info/docs/2008/JAVA-LEJOS-I2C.pdf

Besides, if you check other I2C sensors, you could get common factor:
https://sourceforge.net/p/lejos/ev3/code/ci/master/tree/ev3classes/src/lejos/hardware/sensor/I2CSensor.java
https://sourceforge.net/p/lejos/ev3/code/ci/master/tree/ev3classes/src/lejos/hardware/sensor/DexterGPSSensor.java
https://sourceforge.net/p/lejos/ev3/code/ci/master/tree/ev3classes/src/lejos/hardware/sensor/MindsensorsAbsoluteIMU.java
https://sourceforge.net/p/lejos/ev3/code/ci/master/tree/ev3classes/src/lejos/hardware/sensor/NXTUltrasonicSensor.java

It is not complex, but it is necessary to play a bit with the sensors :D

Cheers

Juan Antonio

@JorgePe
Copy link
Contributor Author

JorgePe commented Jul 10, 2017

Thanks @dlech and @jabrena
A new other-i2c-9v mode would be great if there were other devices that might use it but I'm afraid only DIY devices would benefit from it.
I am not ready for creating kernel drivers but nevertheless any hints I can follow?

And I can also make my own cable with an external 9V source :)

@dlech
Copy link
Member

dlech commented Jul 10, 2017

Hints:

  • Normally, adding a new I2C sensor is as easy as this.
  • modes are something that changes the type of data being read. commands affect the sensor behavior without changing the data type or location.
  • how to enable 9v - set pin1_state = LEGO_PORT_GPIO_HIGH in the sensor definition
  • This sensor will probably need custom ops to handle the quirks.

@JorgePe
Copy link
Contributor Author

JorgePe commented Jul 10, 2017

Thanks. Will read and try not to burn my last neurons.

@JorgePe
Copy link
Contributor Author

JorgePe commented Jul 12, 2017

Still trying to understand, evently I will get there.

At least the Codatex sensor is working - I used LeJOS and could read my 2 tags (and finally see the LED turning on):

package PackageCodatex;

import lejos.hardware.Brick;
import lejos.hardware.port.SensorPort;
import lejos.hardware.sensor.RFIDSensor;

public class Codatex
{
    public static void main(String[] args)
    {
    	RFIDSensor rfid = new RFIDSensor(SensorPort.S1);
		System.out.println(rfid.getProductID());
		System.out.println(rfid.getVendorID());
		System.out.println(rfid.getVersion());
		try {Thread.sleep(2000);}
		catch (InterruptedException e) {}
		long transID = rfid.readTransponderAsLong(true);    
		System.out.println(transID);
		try {Thread.sleep(5000);}
		catch (InterruptedException e) {}
	}
}

It doesn't always work, maybe 3/5 - so even with LeJOS the timings are damn crazy.
My tags ID are
Tag 1: 404725235792
Tag 2: 142916780112
And I can also read my company's ID card, that's great because it is an older RFID protocol that I already could read with the USB RFID readers I was using with trains. So now I know that I have several compatible tags that I can use.

@dlech
Copy link
Member

dlech commented Jul 12, 2017

If the sensor relies on exact timings, that isn't going to happen with Linux (at least not on the EV3 - maybe a faster multi-core processor). What is really needed though is a logic analyzer to see exactly what is really happening.

@JorgePe
Copy link
Contributor Author

JorgePe commented Jul 12, 2017

Yeap, it would be great if I had one but those aren't cheap here in Portugal.

But I have a feeling that the timings don't need to be so exact, I've been reading Daniele Benedettelli "Codatex RFID sensor library for NXC" source and he didn't use half of the timings defined by LeJOS driver.

I'm stubborn so I also made a cable, pin 1 and 2 to a PP3 9V battery. Doesn't work. But it also doesn't work with LeJOS so probably cable is wrong.

I guess I really have to read your hints a few times more and get my hands dirty.

@dlech
Copy link
Member

dlech commented Jul 12, 2017

Pin 2 is not ground on the EV3!!!! Use Pin 3 for ground.

@JorgePe
Copy link
Contributor Author

JorgePe commented Jul 12, 2017

Bad days these we can't even trust the web ... thanks, I was really going to ask for the pinout.

@JorgePe
Copy link
Contributor Author

JorgePe commented Jul 12, 2017

Working!!!
A "DIY cable adapter" with:

  • the WHITE wire cut, connecting just pin1 of the sensor and the positive pole of a PP3 battery (9V) and

  • the RED wire stripped and connected to pin3 of the sensor and pin3 of EV3 and the negative pole of the battery works with LeJOS and ev3dev.

Singleshot reading:

#!/usr/bin/env python3

from smbus import SMBus
from time import sleep
from codecs import encode

DELAY_WAKEUP = 0.005
DELAY_FIRMWARE = 0.100
DELAY_ACQUIRE = 0.250
DELAY_READ = 0.200

bus = SMBus(3)

# LeJOS makes a single shot read after init
# not shure if needed
bus.write_quick(0x02)
sleep(DELAY_WAKEUP)
bus.write_byte_data(0x02,0x41,1)
sleep(DELAY_ACQUIRE)
bus.write_quick(0x02)
sleep(DELAY_WAKEUP)
print("First Read:", bus.read_i2c_block_data(0x02,0x42,5) )

#wakeup
bus.write_quick(0x02)
sleep(DELAY_WAKEUP)

type = bus.read_i2c_block_data(0x02,0x10,8)
vendor = bus.read_i2c_block_data(0x02,0x08,8)
version = bus.read_i2c_block_data(0x02,0x00,5)

#wakeup and init bootloader to read Serial Number
bus.write_quick(0x02)
sleep(DELAY_WAKEUP)
bus.write_byte_data(0x02,0x41,0x81)
sleep(DELAY_FIRMWARE)
serial=bus.read_i2c_block_data(0x02,0xA0,4)

print('Type:', bytes(type).decode('utf-8'))
print('VendorId:',bytes(vendor).decode('utf-8'))
print('Version:',bytes(version).decode('utf-8'))
print('Serial:',encode(bytes(serial),'hex'))

#wakeup and init firmware for normal operation
bus.write_quick(0x02)
sleep(DELAY_WAKEUP)
bus.write_byte_data(0x02,0x41,0x83)
sleep(DELAY_FIRMWARE)

# From Daniele Benedettelli NXC
# not sure if needed
sleep(2)

while True:

    #wakeup
    bus.write_quick(0x02)
    sleep(DELAY_WAKEUP)

    #read single shot mode
    bus.write_byte_data(0x02,0x41,1)
    sleep(DELAY_ACQUIRE)

    #wakeup again
    #not sure if needed
    bus.write_quick(0x02)
    sleep(DELAY_WAKEUP)

    #read tag ID
    print( bus.read_i2c_block_data(0x02,0x42,5) )

    #not sure if needed
    sleep(DELAY_READ)

tag1: [80, 0, 129, 70, 33] (LED blinks)
tag2: [80, 0, 129, 59, 94] (LED blinks)
no tag: [0, 0, 0, 0, 0] (LED always off)

And it works 5/5!!!!

@JorgePe
Copy link
Contributor Author

JorgePe commented Jul 12, 2017

Now I no longer have a strong reason to learn how to make a driver :)
But I promise I will try.

@JorgePe
Copy link
Contributor Author

JorgePe commented Jul 14, 2017

Closing this issue.
I have singleshot read mode working fine, continuous mode should be easy.
If someone reaches this issue, I'll put my scripts, cab be useful until we have some sort of driver:
https://github.com/JorgePe/CodatexRFID-ev3dev

@JorgePe JorgePe closed this as completed Jul 14, 2017
@dlech
Copy link
Member

dlech commented Jul 14, 2017

Anyone interested in a driver can make further comments on #174.

dlech added a commit to ev3dev/lego-linux-drivers that referenced this issue Jul 25, 2017
dlech added a commit to ev3dev/lego-linux-drivers that referenced this issue Jul 25, 2017
@dlech
Copy link
Member

dlech commented Jul 25, 2017

I modified the other-i2c mode the jessie 21-ev3dev kernel and stretch 1.3.0 kernels. So this should work as expected now.

@JorgePe
Copy link
Contributor Author

JorgePe commented Jul 26, 2017

@dlech David, you are my hero!!! It works!!!

4.4.78-21-ev3dev-ev3 #1 PREEMPT Tue Jul 25 13:30:48 CDT 2017 armv5tejl GNU/Linux

just need to set mode
echo other-i2c > **/sys/class/lego-port/port0/mode

and my scripts work with a standard cable and no code modification at all.

dlech added a commit to ev3dev/lego-linux-drivers that referenced this issue Jul 28, 2017
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

3 participants