简体   繁体   中英

NFC P2P with PN532 module on Arduino and Android Kotlin

I am trying to send a string message from Android to Arduino with the PN532 module. When I approach the smartphone (with the app open) to the module, the "tap to beam" UI shows up, but after I tap on the screen, the phone tells me to approach the two devices again. After I approach them again (with the "approach the devices again" message still displayed on the screen), nothing happens and arduino prints out "failed".

Here is the Kotlin code:

package com.cerowski.nfcclient

import android.nfc.NdefMessage
import android.nfc.NdefRecord
import android.nfc.NfcAdapter
import android.os.Bundle
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.viewpager.widget.ViewPager
import com.cerowski.nfcclient.databinding.ActivityMainBinding
import com.cerowski.nfcclient.ui.main.SectionsPagerAdapter
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayout
import java.io.File
import java.io.UnsupportedEncodingException


class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {config(); sendid();

        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val sectionsPagerAdapter = SectionsPagerAdapter(this, supportFragmentManager)
        val viewPager: ViewPager = binding.viewPager
        viewPager.adapter = sectionsPagerAdapter
        val tabs: TabLayout = binding.tabs
        tabs.setupWithViewPager(viewPager)
        val fab: FloatingActionButton = binding.fab



        fab.setOnClickListener { view ->
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                .setAction("Action", null).show()
        }

    }


fun config(){
    val dir = getExternalFilesDir(null);
    val file = File(dir,"config");
    var msgtext="";
    val msg = TextView (this)

    if (dir!=null) {
        if (!file.exists()) {msgtext = "config not found, attempting to create..."; file.createNewFile(); if(file.exists()) {msgtext = "config created successfully"; file.writeText(idgenerator())} else {msgtext = "problem creating file"}}
        else {msgtext = (file.readText())}
    } else {msgtext = "app directory not found"}
}

    fun idgenerator() : String {
        val allowedChars = ('a'..'z') + ('A'..'Z') + ('0'..'9')
        return (1..16)
            .map { allowedChars.random() }
            .joinToString("")
    }

    fun sendid() {
        val dir = getExternalFilesDir(null);
        val file = File(dir,"config");
        //var bytes = file.readBytes();
        //var nfcmsg = NdefMessage(bytes);
        //var msgtext="";
        var nfcAdapter = NfcAdapter.getDefaultAdapter(this);


        if (nfcAdapter != null) {

            val msg: String = file.readText().toString()
            val languageCode: ByteArray
            val msgBytes: ByteArray
            try {
                languageCode = "en".toByteArray(charset("US-ASCII"))
                msgBytes = msg.toByteArray(charset("UTF-8"))
            } catch (e: UnsupportedEncodingException) {
                return
            }

            val messagePayload = ByteArray(
                1 + languageCode.size
                        + msgBytes.size
            )
            messagePayload[0] = 0x02.toByte() // status byte: UTF-8 encoding and

            // length of language code is 2
            // length of language code is 2
            System.arraycopy(
                languageCode, 0, messagePayload, 1,
                languageCode.size
            )
            System.arraycopy(
                msgBytes, 0, messagePayload, 1 + languageCode.size,
                msgBytes.size
            )

            val message: NdefMessage
            val records = arrayOfNulls<NdefRecord>(1)
            val textRecord = NdefRecord(
                NdefRecord.TNF_WELL_KNOWN,
                NdefRecord.RTD_TEXT, byteArrayOf(), messagePayload
            )
            records[0] = textRecord
            message = NdefMessage(records)

            nfcAdapter.setNdefPushMessage(message, this);

        }
        else {Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show();}

    }
}

And here is the code for my Arduino Uno with the PN532 module (the red one, if you search for photos online):

// Receive a NDEF message from a Peer
// Requires SPI. Tested with Seeed Studio NFC Shield v2

#include "SPI.h"
#include "PN532_SPI.h"
#include "snep.h"
#include "NdefMessage.h"

PN532_SPI pn532spi(SPI, 10);
SNEP nfc(pn532spi);
uint8_t ndefBuf[128];

void setup() {
  Serial.begin(9600);
  Serial.println("NFC Peer to Peer Example - Receive Message");
}

void loop() {
  Serial.println("Waiting for message from Peer");
  int msgSize = nfc.read(ndefBuf, sizeof(ndefBuf));
  if (msgSize > 0) {
      NdefMessage msg  = NdefMessage(ndefBuf, msgSize);
      msg.print();
      Serial.println("\nSuccess");
  } else {
      Serial.println("Failed");
  }
  delay(3000);
}

The little switches on the NFC module are in the right positions and the module is connected to the board as it should be (for SPI), So I do not know what is the thing that causes it to fail.

Any help is much appreciated.

First check if it's even a) available, b) enabled and c) register & implement the callbacks. In Java:

MainActivity extends AppCompatActivity implements
        NfcAdapter.CreateNdefMessageCallback,
        NfcAdapter.OnNdefPushCompleteCallback,
        NfcAdapter.CreateBeamUrisCallback {
        ...

if (
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && 
    Build.VERSION.SDK_INT < Build.VERSION_CODES.Q
) {
     NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
     if (!nfcAdapter.isEnabled()) {
         startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
     } else if (!nfcAdapter.isNdefPushEnabled()) {
         startActivity(new Intent(Settings.ACTION_NFCSHARING_SETTINGS));
     } else {
         nfcAdapter.setNdefPushMessageCallback(this);
         nfcAdapter.setOnNdefPushCompleteCallback(this);
         nfcAdapter.setNdefPushMessage(nfcMessage, this);
     }
}

Deprecated in Build.VERSION_CODES.Q means still available; maybe <= .
These callbacks might also provide you with more detail, why it even fails.
Else you'll send a NDEF message and it will not know what to do next...

Alike this you might also be able to produce a proper error message.
Also see: https://developer.android.com/training/beam-files/send-files

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM