abobija/esp-idf-rc522

Support I2C

michaelwittig opened this issue · 6 comments

I wonder if it is possible to support both SPI and I2C.

I try to connect a ESP32-LyraT V4.3 which does not support SPI.

Currently only SPI is supported.
I will try to support I2C as well, soon.

I can post my port here

rc522.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "i2c_bus.h"
#include "board.h"

#include "rc522.h"

static i2c_bus_handle_t i2c_handle;

static int i2c_addr = (0x28 << 1) | I2C_MASTER_WRITE;

esp_err_t rc522_write_n(uint8_t reg, uint8_t n, uint8_t *data) {
    return i2c_bus_write_bytes(i2c_handle, i2c_addr, &reg, sizeof(reg), data, n);
}

esp_err_t rc522_write(uint8_t reg, uint8_t val) {
    return rc522_write_n(reg, 1, &val);
}

uint8_t rc522_read(uint8_t reg) {
    uint8_t data = 0xFF;
    ESP_ERROR_CHECK(i2c_bus_read_bytes(i2c_handle, i2c_addr, &reg, sizeof(reg), &data, 1));
    return data;
}

esp_err_t rc522_set_bitmask(uint8_t reg, uint8_t mask) {
    return rc522_write(reg, rc522_read(reg) | mask);
}

esp_err_t rc522_clear_bitmask(uint8_t reg, uint8_t mask) {
    return rc522_write(reg, rc522_read(reg) & ~mask);
}

esp_err_t rc522_antenna_on() {
    esp_err_t ret = ESP_OK;

    if(~ (rc522_read(0x14) & 0x03)) {
        ret = rc522_set_bitmask(0x14, 0x03);
        if(ret != ESP_OK) {
            return ret;
        }
    }

    return rc522_write(0x26, 0x60); // 43dB gain
}

esp_err_t rc522_init() {
    esp_err_t ret = ESP_OK;

    i2c_config_t i2c_cfg = {
        .mode = I2C_MODE_MASTER,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = 100000,
    };
    ret = get_i2c_pins(I2C_NUM_0, &i2c_cfg);
    if (ret != ESP_OK) {
        return ret;
    }
    i2c_handle = i2c_bus_create(I2C_NUM_0, &i2c_cfg);

    // ---------- RW test ------------
    ret = rc522_write(0x24, 0x25);
    if (ret != ESP_OK) {
        return ret;
    }
    assert(rc522_read(0x24) == 0x25);
    ret = rc522_write(0x24, 0x26);
    if (ret != ESP_OK) {
        return ret;
    }
    assert(rc522_read(0x24) == 0x26);
    
    // ------- Config --------
    ret = rc522_write(0x01, 0x0F);
    if (ret != ESP_OK) {
        return ret;
    }
    ret = rc522_write(0x2A, 0x8D);
    if (ret != ESP_OK) {
        return ret;
    }
    ret = rc522_write(0x2B, 0x3E);
    if (ret != ESP_OK) {
        return ret;
    }
    ret = rc522_write(0x2D, 0x1E);
    if (ret != ESP_OK) {
        return ret;
    }
    ret = rc522_write(0x2C, 0x00);
    if (ret != ESP_OK) {
        return ret;
    }
    ret = rc522_write(0x15, 0x40);
    if (ret != ESP_OK) {
        return ret;
    }
    ret = rc522_write(0x11, 0x3D);
    if (ret != ESP_OK) {
        return ret;
    }

    ret = rc522_antenna_on();
    if (ret != ESP_OK) {
        return ret;
    }

    printf("RC522 Firmware 0x%x\n", rc522_fw_version());

    return ret;
}

esp_err_t rc522_clear() {
    return i2c_bus_delete(i2c_handle);
}

uint8_t* rc522_card_write(uint8_t cmd, uint8_t *data, uint8_t n, uint8_t* res_n) {
    uint8_t *result = NULL;
    uint8_t irq = 0x00;
    uint8_t irq_wait = 0x00;
    uint8_t last_bits = 0;
    uint8_t nn = 0;
    
    if(cmd == 0x0E) {
        irq = 0x12;
        irq_wait = 0x10;
    }
    else if(cmd == 0x0C) {
        irq = 0x77;
        irq_wait = 0x30;
    }

    rc522_write(0x02, irq | 0x80);
    rc522_clear_bitmask(0x04, 0x80);
    rc522_set_bitmask(0x0A, 0x80);
    rc522_write(0x01, 0x00);

    rc522_write_n(0x09, n, data);

    rc522_write(0x01, cmd);

    if(cmd == 0x0C) {
        rc522_set_bitmask(0x0D, 0x80);
    }

    uint16_t i = 1000;

    for(;;) {
        nn = rc522_read(0x04);
        i--;

        if(! (i != 0 && (((nn & 0x01) == 0) && ((nn & irq_wait) == 0)))) {
            break;
        }
    }

    rc522_clear_bitmask(0x0D, 0x80);

    if(i != 0) {
        if((rc522_read(0x06) & 0x1B) == 0x00) {
            if(cmd == 0x0C) {
                nn = rc522_read(0x0A);
                last_bits = rc522_read(0x0C) & 0x07;

                if (last_bits != 0) {
                    *res_n = (nn - 1) + last_bits;
                } else {
                    *res_n = nn;
                }

                result = (uint8_t*) malloc(*res_n);

                for(i = 0; i < *res_n; i++) {
                    result[i] = rc522_read(0x09);
                }
            }
        }
    }

    return result;
}

/* Returns pointer to dynamically allocated array of two element */
uint8_t* rc522_calculate_crc(uint8_t *data, uint8_t n) {
    rc522_clear_bitmask(0x05, 0x04);
    rc522_set_bitmask(0x0A, 0x80);

    rc522_write_n(0x09, n, data);

    rc522_write(0x01, 0x03);

    uint8_t i = 255;
    uint8_t nn = 0;

    for(;;) {
        nn = rc522_read(0x05);
        i--;

        if(! (i != 0 && ! (nn & 0x04))) {
            break;
        }
    }

    uint8_t* res = (uint8_t*) malloc(2); 
    
    res[0] = rc522_read(0x22);
    res[1] = rc522_read(0x21);

    return res;
}

uint8_t* rc522_request(uint8_t* res_n) {
    uint8_t* result = NULL;
    rc522_write(0x0D, 0x07);

    uint8_t req_mode = 0x26;
    result = rc522_card_write(0x0C, &req_mode, 1, res_n);

    if(*res_n * 8 != 0x10) {
        free(result);
        return NULL;
    }

    return result;
}

uint8_t* rc522_anticoll() {
    uint8_t* result = NULL;
    uint8_t res_n;
    uint8_t serial_number[] = { 0x93, 0x20 };

    rc522_write(0x0D, 0x00);

    result = rc522_card_write(0x0C, serial_number, 2, &res_n);

    if(result != NULL && res_n != 5) {
        free(result);
        return NULL;
    }

    return result;
}

uint8_t* rc522_get_tag() {
    uint8_t* result = NULL;
    uint8_t* res_data = NULL;
    uint8_t res_data_n;

    res_data = rc522_request(&res_data_n);

    if(res_data != NULL) {
        free(res_data);

        result = rc522_anticoll(&res_data_n);

        if(result != NULL) {
            uint8_t buf[] = { 0x50, 0x00, 0x00, 0x00 };
            uint8_t* crc = rc522_calculate_crc(buf, 2);

            buf[2] = crc[0];
            buf[3] = crc[1];

            free(crc);

            res_data = rc522_card_write(0x0C, buf, 4, &res_data_n);
            free(res_data);

            rc522_clear_bitmask(0x08, 0x08);

            return result;
        }
    }

    return NULL;
}

So, you managed to adapt code to communicate via I2C. I guess it works for you.
Nice. This can be reference for me.
Thanks!

Yes, it works. And I only had to make very few changes.

It was not obvious to me, so quick note for future readers:

It seems that the RC5221 is hardwired to use SPI, therefore using I2C (or UART) requires a (not so easy, in the case if I2C) physical modification of the pcb to cut one or two copper trace(s): https://forum.arduino.cc/index.php?topic=442750.0

Footnotes

  1. Edit: at least the one I have. YMMV :)

Hi @Nodraak
Yes, that's true. I have ordered this one https://ebay.to/3ceqSZr which is hardwired for I2C.
When it arrives I will modify this library to support both, SPI and I2C.