I am trying to write an I2C Communication layer for my PN532 NFC Module.
I have the Arduino code working fine, but I am trying to implement it for Android things. In Arduino my output is as follows:
WriteCommand
Write: 0
Write: 0
Write: 255
Write: 2
Write: 254
Write: 212
Write: 2
Write: 42
Write: 0
/WriteCommand
Read ack frame
Read 1
Read 0
Read 0
Read 255
Read 0
Read 255
Read 0
/Read ack frame
With the following code (copied and modified from a CPP library):
#include <Wire.h>
TwoWire* wire = &Wire;
uint8_t command;
uint8_t pn532_packetbuffer[64];
uint8_t write(uint8_t data) {
Serial.println(" Write: " + String(data));
return wire->write(data);
}
uint8_t read() {
uint8_t value = wire->read();
Serial.println(" Read " + String(value));
return value;
}
uint8_t writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0) {
Serial.println("WriteCommand");
command = header[0];
wire->beginTransmission(PN532_I2C_ADDRESS);
write(PN532_PREAMBLE);
write(PN532_STARTCODE1);
write(PN532_STARTCODE2);
uint8_t length = hlen + blen + 1; // length of data field: TFI + DATA
write(length);
write(~length + 1); // checksum of length
write(PN532_HOSTTOPN532);
uint8_t sum = PN532_HOSTTOPN532; // sum of TFI + DATA
for (uint8_t i = 0; i < hlen; i++) {
if (write(header[i])) {
sum += header[i];
} else {
return PN532_INVALID_FRAME;
}
}
for (uint8_t i = 0; i < blen; i++) {
if (write(body[i])) {
sum += body[i];
} else {
return PN532_INVALID_FRAME;
}
}
uint8_t checksum = ~sum + 1; // checksum of TFI + DATA
write(checksum);
write(PN532_POSTAMBLE);
wire->endTransmission();
Serial.println("/WriteCommand");
return readAckFrame();
}
uint8_t readAckFrame() {
Serial.println("Read ack frame");
const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0};
uint8_t ackBuf[sizeof(PN532_ACK)];
uint16_t time = 0;
do {
if (wire->requestFrom(PN532_I2C_ADDRESS, sizeof(PN532_ACK) + 1)) {
if (read() & 1) { // check first byte --- status
break; // PN532 is ready
}
}
delay(1);
time++;
if (time > PN532_ACK_WAIT_TIME) {
return PN532_TIMEOUT;
}
} while (1);
for (uint8_t i = 0; i < sizeof(PN532_ACK); i++) {
ackBuf[i] = read();
}
if (memcmp(ackBuf, PN532_ACK, sizeof(PN532_ACK))) {
return PN532_INVALID_ACK;
}
Serial.println("/Read ack frame");
return 0;
}
int16_t getResponseLength(uint8_t buf[], uint8_t len, uint16_t timeout) {
const uint8_t PN532_NACK[] = {0, 0, 0xFF, 0xFF, 0, 0};
uint16_t time = 0;
do {
if (wire->requestFrom(PN532_I2C_ADDRESS, 6)) {
if (read() & 1) { // check first byte --- status
break; // PN532 is ready
}
}
delay(1);
time++;
if ((0 != timeout) && (time > timeout)) {
return -1;
}
} while (1);
if (0x00 != read() || // PREAMBLE
0x00 != read() || // STARTCODE1
0xFF != read() // STARTCODE2
) {
return PN532_INVALID_FRAME;
}
uint8_t length = read();
// request for last respond msg again
wire->beginTransmission(PN532_I2C_ADDRESS);
for (uint16_t i = 0; i < sizeof(PN532_NACK); ++i) {
write(PN532_NACK[i]);
}
wire->endTransmission();
return length;
}
int16_t readResponse(uint8_t buf[], uint8_t len, uint16_t timeout) {
uint16_t time = 0;
uint8_t length;
length = getResponseLength(buf, len, timeout);
do {
if (wire->requestFrom(PN532_I2C_ADDRESS, 6 + length + 2)) {
if (read() & 1) { // check first byte --- status
break; // PN532 is ready
}
}
delay(1);
time++;
if ((0 != timeout) && (time > timeout)) {
return -1;
}
} while (1);
if (0x00 != read() || // PREAMBLE
0x00 != read() || // STARTCODE1
0xFF != read() // STARTCODE2
) {
return PN532_INVALID_FRAME;
}
length = read();
if (0 != (uint8_t)(length + read())) { // checksum of length
return PN532_INVALID_FRAME;
}
uint8_t cmd = command + 1; // response command
if (PN532_PN532TOHOST != read() || (cmd) != read()) {
return PN532_INVALID_FRAME;
}
length -= 2;
if (length > len) {
return PN532_NO_SPACE; // not enough space
}
uint8_t sum = PN532_PN532TOHOST + cmd;
for (uint8_t i = 0; i < length; i++) {
buf[i] = read();
sum += buf[i];
}
uint8_t checksum = read();
if (0 != (uint8_t)(sum + checksum)) {
return PN532_INVALID_FRAME;
}
read(); // POSTAMBLE
return length;
}
uint32_t getFirmwareVersion(void) {
uint32_t response;
pn532_packetbuffer[0] = PN532_COMMAND_GETFIRMWAREVERSION;
if (writeCommand(pn532_packetbuffer, 1, 0, 0)) {
return 0;
}
Serial.println("Wrote the command and got a success acknowledge");
// read data packet
int16_t status = readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer), 0);
if (0 > status) {
return 0;
}
response = pn532_packetbuffer[0];
response <<= 8;
response |= pn532_packetbuffer[1];
response <<= 8;
response |= pn532_packetbuffer[2];
response <<= 8;
response |= pn532_packetbuffer[3];
return response;
}
So in Kotlin I am trying to reproduce this, and while I am writing the correct values I am unable to read the correct values back:
fun Byte.toUnsigned(): Int = when {
(toInt() < 0) -> 255 + toInt() + 1
else -> toInt()
}
fun I2cDevice.read(data: ByteArray) {
read(data, data.size)
data.forEach {
android.util.Log.d("I2C"," read: ${it.toUnsigned()}")
}
}
fun I2cDevice.write(data: ByteArray) {
write(data, data.size)
android.util.Log.d("I2C"," write: ${data[0].toUnsigned()}")
}
fun I2cDevice.write(data: Byte) =
write(ByteArray(1).apply { this[0] = data })
fun I2cDevice.writeCommand(header: ByteArray, body: ByteArray? = null): Int {
android.util.Log.d("I2C","START")
write(PN532_PREAMBLE)
write(PN532_STARTCODE1)
write(PN532_STARTCODE2)
val len = (1+header.size + (body?.size?:0)).toByte()
write(len)
write((len.inv() + 1).toByte()) // checksum of length
write(PN532_HOSTTOPN532)
var sum: Byte = PN532_HOSTTOPN532 // sum of TFI + DATA
header.forEach {
write(it)
sum = (sum + it).toByte()
}
body?.forEach {
write(it)
sum = (sum + it).toByte()
}
write((sum.inv() + 1).toByte())
write(PN532_POSTAMBLE)
return readAckFrame()
}
fun I2cDevice.readAckFrame(): Int {
val readyCheck = ByteArray(7).apply { this[0] = 0 }
//while(readyCheck[0].toUnsigned() != 1) {
read(readyCheck) // <-- will ALWAYS READ 0, 128, 128
//}
android.util.Log.d("I2C","Ack complete")
return 0
}
The problem seems to be that I need to somehow request the data before reading it (wire->requestFrom(PN532_I2C_ADDRESS, sizeof(PN532_ACK) + 1) in the Arduino code). I am writing the correct values and my wiring is correct.
All the Kotlin code is able to read is 0, 128, 128, even though I am asking for 7 bytes.
