flomio / cordova-plugin-flomio

Flomio SDK plugin for Cordova.
10 stars 8 forks source link

read NDEF fails #32

Closed jeffcrouse closed 6 years ago

jeffcrouse commented 6 years ago

I used the Flomio iOS SDK with a FloBLE Plus to write a URL to a few tags that I have and then I successfully read it back.

FmNdefMessage* message = [FmNdefMessage createURIWithString:@"http://www.cnn.com"];
[tag writeNdef:message success:^(BOOL success)...

and

[tag readNdef:^(FmNdefMessage *ndef) {
        for(FmNdefRecord *record in ndef.ndefRecords) {
            NSLog(@"Record Payload: %@", record.getUriFromPayload);
        }
}];

This successfully writes back out http://www.cnn.com

But when I try to read the NDEF information from these same cards using the cordova plugin, the callbacks are never called.

Here is my TagDiscoveredListener

    flomioTagDiscovered: function(result) {
        console.log("!! discovered tag: ", JSON.stringify(result));

        console.log("!! readNdef");
        flomioPlugin.readNdef(this.deviceId, function(response) {
                console.log("!! onNdefFound", JSON.stringify(response));
            }, function(error) {
                console.log("!! onReadNdefFail", error.message)
            });
    }

The two console.log messages are printed out, but then I just get:

[1245:1386410] Change Status:Present
[1245:1386410] ATR Response: 3B 8F 80 01 80 4F 0C A0 00 00 03 06 03 00 01 00 00 00 00 6A
[1245:1386410] In transmitApdu acsbtreader
[1245:1387268] Command APDU:FF CA 00 00 00
[1245:1386410] Response Apdu: F6 DC 0C E4 90 00
[1245:1386410] In didReturnResponseApdu acsbtreader
[1245:1386410] Found tag UUID: F6 DC 0C E4  from device:RR464-001493
[1245:1386410] !! discovered tag:  {"uid":"F6 DC 0C E4 ","atr":"3B 8F 80 01 80 4F 0C A0 00 00 03 06 03 00 01 00 00 00 00 6A"}
1245:1386410] !! readNdef
[1245:1386464] FmSessionManager sent apdu
[1245:1386464] In transmitApdu acsbtreader
[1245:1387268] Command APDU:FFB0000310
[1245:1386410] Response Apdu: 63 00
[1245:1386410] handleApduResponse: Operation was unsuccessful.
[1245:1386410] FmSessionManager before sem
[1245:1386410] FmSessionManager after sem
[1245:1386410] command: FFB0000310, response: 63 00
[1245:1386410] In didReturnResponseApdu acsbtreader
[1245:1386410] Change Status:Absent

Any ideas why this might happen?

scottire commented 6 years ago

Hi Jeff,

The ATR of your tag indicates you're using a mifare classic 1k (3B 8F 80 01 80 4F 0C A0 00 00 03 06 03 00 01 00 00 00 00 6A). The iOS SDK supports mifare classic 1k tags but the cordova-plugin-flomio doesn't.

If you want to manually send APDUs to read the data in cordova you do this: Load Auth: FF 82 00 00 06 FF FF FF FF FF FF Auth block: FF 86 00 00 05 01 00 + XX + 60 00 - where XX is the block you want to read Read block: FF B0 00 + XX + 10 - same XX as above

Let me know if you have any questions.

jeffcrouse commented 6 years ago

Thanks, Scott. That seems to work. Here is my code, in case anyone else needs it.

It would be helpful to note in the README which cards are supported by the plugin.

var block = "04";
var apdu = [
    `FF 82 00 00 06 FF FF FF FF FF FF`, 
    `FF 86 00 00 05 01 00 ${block} 60 00`, 
    `FF B0 00 ${block} 10`].map(s=>{return s.replace(' ', '')});
flomioPlugin.sendApdu(this.deviceId, apdu[0], (response)=>{
    flomioPlugin.sendApdu(this.deviceId, apdu[1], (response)=>{
        flomioPlugin.sendApdu(this.deviceId, apdu[2], (payload)=>{
            console.log(`!!! ${payload}`);
        }, (error) => { console.log(`failed to send ${apdu[2]} ${error}`); });
    }, (error) => { console.log(`failed to send ${apdu[1]} ${error}`); });
}, (error) => { console.log(`failed to send ${apdu[0]} ${error}`); });
scottire commented 6 years ago

Great, thanks for sharing your code and glad that worked for you.

jeffcrouse commented 6 years ago

Hey Scott -- can you let me know how to read from Mifare Ultralight?

scottire commented 6 years ago

readNdef should work with Mifare Ultralight, so no need to implement the APDUs yourself.

jeffcrouse commented 6 years ago

Here is what I am getting:

[1893:2322219] Change Status:Present
1893:2322219] ATR Response: 3B 8F 80 01 80 4F 0C A0 00 00 03 06 03 00 03 00 00 00 00 68
[1893:2322219] In transmitApdu acsbtreader
[1893:2322603] Command APDU:FF CA 00 00 00
[1893:2322219] Response Apdu: 04 4C 37 1A 71 40 81 90 00
[1893:2322219] In didReturnResponseApdu acsbtreader
[1893:2322219] Found tag UUID: 04 4C 37 1A 71 40 81  from device:RR464-001493
[1893:2322219] !! discovered tag:  {"uid":"04 4C 37 1A 71 40 81 ","atr":"3B 8F 80 01 80 4F 0C A0 00 00 03 06 03 00 03 00 00 00 00 68"}
[1893:2322459] FmSessionManager sent apdu
[1893:2322459] In transmitApdu acsbtreader
[1893:2322603] Command APDU:FFB0000310
[1893:2322219] Response Apdu: E1 10 12 0F 01 03 A0 0C 34 03 12 D1 01 0E 54 02 90 00
[1893:2322219] FmSessionManager before sem
[1893:2322219] FmSessionManager after sem
[1893:2322219] command: FFB0000310, response: E1 10 12 0F 01 03 A0 0C 34 03 12 D1 01 0E 54 02 90 00
[1893:2322219] In didReturnResponseApdu acsbtreader
1893:2322459] FmSessionManager sent apdu
[1893:2322459] In transmitApdu acsbtreader
[1893:2322804] Command APDU:FFB0000410
[1893:2322219] Response Apdu: 01 03 A0 0C 34 03 12 D1 01 0E 54 02 65 6E 6B 69 90 00
[1893:2322219] FmSessionManager before sem
[1893:2322219] FmSessionManager after sem
[1893:2322219] command: FFB0000410, response: 01 03 A0 0C 34 03 12 D1 01 0E 54 02 65 6E 6B 69 90 00
[1893:2322219] In didReturnResponseApdu acsbtreader
[1893:2322219] !! onReadNdefFail Tag is not NDEF formatted

These tags were NDEF formatted and made read-only at the factory, and they work with another reader I have, so I'm not sure what is going on.

scottire commented 6 years ago

Hmm... that sounds like there's an issue with our parser. Do you have a hex dump of your NDEF data?

scottire commented 6 years ago

Not ideal, but to read data from Mifare Ultralight you can send these APDUs

async function readTagData(){
  let numberOfPages = 16
  let deviceId = "RR464-001493"
  for (let page = 4; page <= numberOfPages; page += 4) {
      let n = ''
      page >= 16 ? n = '' + page.toString(16) : n = '0' + page.toString(16)
      const nextApdu = 'FFB000' + n + '10'
      const response = await flomioPlugin.sendApduWithPromise(deviceId, apdu)
  }
}
jeffcrouse commented 6 years ago

Is there a way to get that with the Flomio reader? On the Arduino side I am using https://github.com/don/NDEF and this is what I get when I run "ReadTagExtended"

NFC Forum Type 2
UID: 04 4C 37 1A 71 40 81

This NFC Tag contains an NDEF Message with 1 NDEF Record.

NDEF Record 1
  TNF: 1
  Type: T
  Payload (HEX): 02 65 6E 6B 69 73 73 20 2D 20 63 6F 6C 64  .enkiss - cold
  Payload (as String): enkiss - cold
scottire commented 6 years ago

The problem is that the NDEF Message you want starts later than our parser handles. This is your data: 01 03 A0 0C 34 03 12 D1 01 0E 54 02 65 6E 6B 69 73 73 20 2D 20 63 6F 6C 64. The NDEF parser wants D1 01 0E 54 02 65 6E 6B 69 73 73 20 2D 20 63 6F 6C 64 to behave correctly. Although this is a problem with our parser, in order to get you something working as soon as possible, use the above code to read your data and use const records = flomioPlugin.ndef.decodeMessage(bytes); to solve your issue.

scottire commented 6 years ago

An easier way to send the APDUs is to use flomioPlugin.readPage(deviceId, page) seen here. So you would call it like

async function readTagData(){
  const numberOfPages = 16
  const deviceId = "RR464-001493"
  let tagData = ""
  for (let page = 4; page <= numberOfPages; page += 4) {
      const response = await flomioPlugin.readPage(deviceId, page)
      tagData = tagData + response
  }
  const records = flomioPlugin.ndef.decodeMessage(tagData)
}

Let me know if this works for you.

jeffcrouse commented 6 years ago

So I'm calling readTagData() in flomioTagDiscovered right after I print out the result. I added console.log("!! NDEF", records); to the bottom of your function. This is what I get:

[2078:2372602] !! discovered tag:  {"uid":"04 B7 2A 1A 71 40 80 ","atr":"3B 8F 80 01 80 4F 0C A0 00 00 03 06 03 00 03 00 00 00 00 68"}
[2078:2372649] FmSessionManager sent apdu
[2078:2372649] In transmitApdu acsbtreader
[2078:2372640] Command APDU:FFB0000410
[2078:2372602] Response Apdu: 01 03 A0 0C 34 03 12 D1 01 0E 54 02 65 6E 66 61 90 00
[2078:2372602] FmSessionManager before sem
[2078:2372602] FmSessionManager after sem
[2078:2372602] command: FFB0000410, response: 01 03 A0 0C 34 03 12 D1 01 0E 54 02 65 6E 66 61 90 00
[2078:2372602] In didReturnResponseApdu acsbtreader
[2078:2372649] FmSessionManager sent apdu
[2078:2372649] In transmitApdu acsbtreader
[2078:2372641] Command APDU:FFB0000810
[2078:2372602] Response Apdu: 74 65 20 2D 20 62 61 6E 67 FE 00 00 00 00 00 00 90 00
[2078:2372602] FmSessionManager before sem
[2078:2372602] FmSessionManager after sem
[2078:2372602] command: FFB0000810, response: 74 65 20 2D 20 62 61 6E 67 FE 00 00 00 00 00 00 90 00
[2078:2372602] In didReturnResponseApdu acsbtreader
[2078:2372649] FmSessionManager sent apdu
[2078:2372649] In transmitApdu acsbtreader
[2078:2372641] Command APDU:FFB0000c10
[2078:2372602] Response Apdu: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90 00
[2078:2372602] FmSessionManager before sem
[2078:2372602] FmSessionManager after sem
[2078:2372602] command: FFB0000c10, response: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90 00
[2078:2372602] In didReturnResponseApdu acsbtreader
[2078:2372649] FmSessionManager sent apdu
[2078:2372649] In transmitApdu acsbtreader
[2078:2372641] Command APDU:FFB0001010
[2078:2372602] Response Apdu: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90 00
[2078:2372602] FmSessionManager before sem
[2078:2372602] FmSessionManager after sem
[2078:2372602] command: FFB0001010, response: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90 00
[2078:2372602] In didReturnResponseApdu acsbtreader
[2078:2372602] Change Status:Absent
jeffcrouse commented 6 years ago

upon further investigation, it's looking like "sendApduWithPromise" and "readPage" never return.

jeffcrouse commented 6 years ago

So I managed to brute force my way to an answer, but I don't fully understand it, which makes me nervous.

flomioPlugin.sendApdu(this.deviceId, 'FFB0000710', (result)=>{
    var message = hex2a(result.replace(/ /g, "")).substr(2, 11);
    console.log("!! message", message);
},  (error) => { console.log(`failed to send read command ${error}`); });

I was noticing that reading page by page, parts of the response would be repeated, which suggests that the "07" is not really a unique "page", but instead it is an offset, and then it gives you 16 bytes. Is this correct?

In my case (which I realize is somewhat specific), when I ask for "page" 7, I can convert the result to ASCII, and then take characters 2-11, and that is the string I want.

But, like I said, I don't understand why exactly, so if something gles wrong, I won't know how to fix it. Can you shed any light on what is going on here? For instance, why would the manufacturers of these tags (I don't know who they are - they are provided by my client) put the relevant NDEF data at such a weird offset?

Thanks much!

scottire commented 6 years ago

I believe it’s to do with (not often used) lock control TLV structures that the parser doesn’t handle. We will look into fixing it when we get a chance :)

You are correct about the read APDU, that's why we move up 4 pages at a time in the for-loop above. If you want more info about reading data / writing data using APDUs, this is a nice introduction article.

As I was saying before, the NDEF parser wants D1 01 0E 54 02 65 6E 6B 69 73 73 20 2D 20 63 6F 6C 64 from your data to behave correctly. const records = flomioPlugin.ndef.decodeMessage(bytes); will return a NDEF object similar to that returned by the Arduino library.