My Cart

Multiprotocol Radio Shield v2.0 Tutorial for Arduino.

Difficulty Level: Beginner -


The Multiprotocol Radio Shield is an interconnection shield for Arduino, and was designed to connect two communication modules at the same time. This that means a lot of possibilities and applications can be made using the cooking-hacks modules. The Multiprotocol Radio Shield includes the SPI bus connections that allows the use of the RS-485, CAN Bus, LoRa modules and LoRaWAN, but also is compatible with the rest of the cooking-hacks modules as RFID, XBee or Bluetooth.

As an example in the pictures below we can see several examples of double radio integration: RS232 + Bluetooth (left), RS485 + RFID (center), Can Bus + 802.15.4 / ZigBee (right).

Let's summarize what we can do using this shield:

1. The shield

The Multiprotocol Radio Shield includes two sockets. In these two sockets can be connect any module that uses UART. As for using SPI modules, there are two possibilities. If SPI uses 3V3 voltage levels, you must connect it in the SOCKET1, and if it uses 5V levels, in SOCKET0.

1.1. Multiprotocol Radio Shield PCB

Version 2 of the shield:

  • This version of the shield includes a Digital Switch to enable/disable the different sockets.

1.2. Electrical Features

The Multiprotocol Radio Shield can be powered by the PC or by an external power supply. Some of the USB ports on computers are not able to give all the current the module needs to work, if your module have problems when it work, you can use an external power supply (12V - 2A) on the Arduino.

2. Features

2.1. The Library

The Multiprotocol Radio Shield v2 counts with a C++ library that lets you manage the shield in a simple way. This library offers a simple-to-use open source system.

The Multiprotocol Radio Shield v2 includes a high level library functions for an easy manage. Before start using this functions you should download the library. The next zip includes all the files needed in a folder.

Download the Multiprotocol Radio Shield v2 Libraries for Arduino.

Libraries are often distributed as a ZIP file or folder. The name of the folder is the name of the library. Inside the folder will be the .cpp files, .h files and a examples folder.

To install the library, first quit the Arduino application. Then uncompress the ZIP file containing the library. For installing libraries, uncompress zip file. It should contain a folder called multiprotocolShield. Drag this folder into your libraries folder. Under Windows, it will likely be called "My Documents\Arduino\libraries". For Mac users, it will likely be called "Documents/Arduino/libraries". On Linux, it will be the "libraries" folder in your sketchbook.

The library won't work if you put the .cpp and .h files directly into the libraries folder or if they're tested in an extra folder. Restart the Arduino application. Make sure the new library appears in the Sketch->Import Library menu item of the software.

Functions of the library

The main functions are listed here:

disableMUX();			//Disables the multiplexor

ON();				//Switches ON the power supply of the SOCKET1

OFF();				//Switches OFF the power supply of the SOCKET1

setMUX();			//Configures the multiplexor in the SOCKET1

setCS();			//Enables the SPI of the SOCKET1 (5V Level)

unsetCS();			//Disables the SPI of the SOCKET1

2.2. SPI modules

The Multiprotocol Radio Shield includes SPI bus connection in the two sockets, but the voltage levels of the sockets are not the same. The SOCKET0 of the Multiprotocol Radio Shield uses 5V voltage levels and the SOCKET1 uses 3V3 voltage levels.

These two sockets use different selection pins. The selection pin enables the communication with the module and must be configured previously as an OUTPUT. In the RS-485 and CAN Bus modules, this configuration is made in the library.

2.3. Multiprotocol Radio Shield over Arduino

3. Real applications

3.1. Two UART modules

In this part of the tutorial we are going to connect two UART modules to see how to use the Multiprotocol Radio Shield multiplexer and how to combine two communication protocols. The modules used are the XBee 802.15.4 module and the RFID13.56 MHz module. We are going to connect the RFID module in the SOCKET0 and the XBee module in the SOCKET1. (In this case, doesn't matter where are connected).

NOTE:
  • The XBee modules must be configured with AP=0 (transparent mode).
  • In this mode, all data sent to the UART will be send wirelessly.
  • In this mode is not advisable to use 115200bps baud rate configuration.

We have divided the code in two parts. The first part reads the UID code of the RFID 13.56 MHz tag, and the second part, sends the UID trough the XBee 802.15.4 module.

In your gateway you should view the response of the module.


Code:


/*  
 *  Multiprotocol Radio Shield with RFID/NFC 13.56MHz
 *  
 *  Copyright (C) Libelium Comunicaciones Distribuidas S.L. 
 *  http://www.libelium.com 
 *  
 *  This program is free software: you can redistribute it and/or modify 
 *  it under the terms of the GNU General Public License as published by 
 *  the Free Software Foundation, either version 3 of the License, or 
 *  (at your option) any later version. 
 *  a
 *  This program is distributed in the hope that it will be useful, 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License 
 *  along with this program.  If not, see http://www.gnu.org/licenses/. 
 *  
 *  Version:           1.1
 *  Design:            David GascĂłn 
 *  Implementation:    Ahmad Saad
 */
 
 
#include <multiprotocolShield.h>
#include <Wire.h>
#include <MCP23008.h>


uint8_t dataRX[35];//Receive buffer.
uint8_t dataTX[35];//Transmit buffer.
uint8_t _UID[4]; // stores the UID (unique identifier) of a card.
uint8_t blockData[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
                       0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
                      };//Data to write (16 bytes).
uint8_t keyAccess[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} ;// stores the key or password.
uint8_t address = 0x04;//Address to read.
uint8_t ATQ[2];//Answer to request
uint8_t state;//state of the process
uint8_t aux[16];//Auxiliary buffer.

void setup()
{
  //Start serial port 115200 bps:
  Serial.begin(115200);
  delay(100);
  Serial.print("RFID/NFC @ 13.56 MHz module started");
  delay(1000);
  
  socket0.ON(); // <------RFID SOCKET  
  delay(100);
  socket0.setMUX(); // <------ Configure the MUX at MUX0

  
  //! It is needed to launch a simple command to synchronize  
  getFirmware();
  configureSAM();
}
void loop()
{
  // Read the RFID TAG
  readTAG();
  delay(1000);

  sendXBee();
  delay(1000);

}
//**********************************************************************

void sendXBee()
{
  socket1.ON();
  delay(100);
  socket1.setMUX(); // <------ Configure the MUX at MUX0
  
  Serial.begin(9600);
  delay(1000);

  Serial.print( "The Card NUID : ");
  print(_UID , 4);
  Serial.print("\n");


  Serial.print("Data read : ");
  print(aux , 16);
  Serial.print("\n");
  
  delay(1000);
  
  Serial.begin(115200);

  delay(100);
  socket0.setMUX(); // <------ Configure the MUX at MUX0

}

//**********************************************************************
void readTAG()
{
  
  socket0.setMUX(); // <------ Configure the MUX at MUX0
  delay(100);
  
  Serial.print("\n");
  Serial.println("Ready to read...");
  /////////////////////////////////////////////////////////////
  //Get the Card Identifier
  init(_UID, ATQ);
  Serial.print("\n");
  Serial.print( "The Card NUID : ");
  print(_UID , 4);
  /////////////////////////////////////////////////////////////
  //Authenticate a block with his keyAccess
  state = authenticate(_UID, address, keyAccess);
  Serial.print("\n");

  if ( state == 0) {
    Serial.println("Authentication block OK");
  } else {
    Serial.println("Authentication failed");
  }
  /////////////////////////////////////////////////////////////
  //Write blockData in a address, after authentication.
  if ( state == 0 ) {
    state = writeData(address, blockData);
    Serial.print("\n");
    if ( state == 0) {
      Serial.println("Write block OK");
    } else {
      Serial.println("Write failed");
    }
    /////////////////////////////////////////////////////////////
    //Read from address after authentication
    state = readData(address, aux);
    Serial.print("\n");

    if (state == 0) {
      Serial.println("Read block OK");
    } else {
      Serial.println("Read failed");
    }

    Serial.print("Data read : ");
    print(aux , 16);
    Serial.print("\n");
  }
  delay(2000);

}


//**********************************************************************
//!The goal of this command is to detect as many targets (maximum MaxTg)
// as possible in passive mode.
uint8_t init(uint8_t *UID , uint8_t *ATQ)   //! Request InListPassive
{
  Serial.flush();

  dataTX[0] = 0x04; // Length
  lengthCheckSum(dataTX); // Length Checksum
  dataTX[2] = 0xD4;
  dataTX[3] = 0x4A; // Code
  dataTX[4] = 0x01; //MaxTarget
  dataTX[5] = 0x00; //BaudRate = 106Kbps
  dataTX[6] = 0x00; // Clear check Sum position
  checkSum(dataTX);

  sendTX(dataTX , 7 , 23);

  for (int i = 17; i < (21) ; i++) {
    _UID[i - 17] = dataRX[i];
    UID[i - 17] = _UID[i - 17];
  }

  ATQ[0] = dataRX[13];
  ATQ[1] = dataRX[14];

  if ((dataRX[9] == 0xD5) & (dataRX[10] == 0x4B) & (dataRX[11] == 0x01)) {
    return 0;
  } else {
    return 1;
  }
}
//**********************************************************************
//!A block must be authenticated before read and write operations
uint8_t authenticate(uint8_t *UID, uint8_t blockAddress, uint8_t *keyAccess)
{
  dataTX[0] = 0x0F;
  lengthCheckSum(dataTX);
  dataTX[2] = 0xD4;
  dataTX[3] = 0x40; // inDataEchange
  dataTX[4] = 0x01; //Number of targets
  dataTX[5] = 0x60; // Authentication code
  dataTX[6] = blockAddress;

  for (int i = 0; i < 6 ; i++) {
    dataTX[i + 7] = keyAccess[i];
  }

  dataTX[13] = UID[0];  dataTX[14] = UID[1];
  dataTX[15] = UID[2];  dataTX[16] = UID[3];
  dataTX[17] = 0x00;
  checkSum(dataTX);
  sendTX(dataTX , 18 , 14);

  if ((dataRX[9] == 0xD5) & (dataRX[10] == 0x41) & (dataRX[11] == 0x00)) {
    return 0;
  } else {
    return 1;
  }
}
//**********************************************************************
//!Write 16 bytes in address .
uint8_t writeData(uint8_t address, uint8_t *blockData)  //!Writing
{
  Serial.print("                ");
  dataTX[0] = 0x15;
  lengthCheckSum(dataTX); // Length Checksum
  dataTX[2] = 0xD4;
  dataTX[3] = 0x40;  //inDataEchange CODE
  dataTX[4] = 0x01;  //Number of targets
  dataTX[5] = 0xA0; //Write Command
  dataTX[6] = address; //Address

  for (int i = 0; i < 16; i++) {
    dataTX[i + 7] = blockData[i];
  }

  dataTX[23] = 0x00;
  checkSum(dataTX);
  sendTX(dataTX , 24 , 14);

  if ((dataRX[9] == 0xD5) & (dataRX[10] == 0x41) & (dataRX[11] == 0x00)) {
    return 0;
  } else {
    return 1;
  }
}
//**********************************************************************
//!Read 16 bytes from  address .
uint8_t readData(uint8_t address, uint8_t *readData) //!Reading
{
  Serial.print("                ");

  dataTX[0] = 0x05;
  lengthCheckSum(dataTX); // Length Checksum
  dataTX[2] = 0xD4; // Code
  dataTX[3] = 0x40; // Code
  dataTX[4] = 0x01; // Number of targets
  dataTX[5] = 0x30; //ReadCode
  dataTX[6] = address;  //Read address
  dataTX[7] = 0x00;
  checkSum(dataTX);
  sendTX(dataTX , 8, 30);
  memset(readData, 0x00, 16);

  if ((dataRX[9] == 0xD5) & (dataRX[10] == 0x41) & (dataRX[11] == 0x00)) {
    for (int i = 12; i < 28; i++) {
      readData[i - 12] = dataRX[i];
    }
    return 0;
  } else {
    return 1;
  }
}
//**********************************************************************
//!The PN532 sends back the version of the embedded firmware.
bool getFirmware(void)  //! It is needed to launch a simple command to synchronize
{
  Serial.print("                ");

  memset(dataTX, 0x00, 35);
  dataTX[0] = 0x02; // Length
  lengthCheckSum(dataTX); // Length Checksum
  dataTX[2] = 0xD4; // CODE
  dataTX[3] = 0x02; //TFI
  checkSum(dataTX); //0x2A; //Checksum

  sendTX(dataTX , 5 , 17);
  Serial.print("\n");
  Serial.print("Your Firmware version is : ");

  for (int i = 11; i < (15) ; i++) {
    Serial.print(dataRX[i], HEX);
    Serial.print(" ");
  }
  Serial.print("\n");
}

//**********************************************************************
//!Print data stored in vectors .
void print(uint8_t * _data, uint8_t length)
{
  for (int i = 0; i < length ; i++) {
    Serial.print(_data[i], HEX);
    Serial.print(" ");
  }
  Serial.print("\n");
}
//**********************************************************************
//!This command is used to set internal parameters of the PN532,
bool configureSAM(void)//! Configure the SAM
{
  Serial.print("               ");

  dataTX[0] = 0x05; //Length
  lengthCheckSum(dataTX); // Length Checksum
  dataTX[2] = 0xD4;
  dataTX[3] = 0x14;
  dataTX[4] = 0x01; //Normal mode
  dataTX[5] = 0x14; // TimeOUT
  dataTX[6] = 0x00; // IRQ
  dataTX[7] = 0x00; // Clean checkSum position
  checkSum(dataTX);

  sendTX(dataTX , 8, 13);
}
//**********************************************************************
//!Send data stored in dataTX
void sendTX(uint8_t *dataTX, uint8_t length, uint8_t outLength)
{
  Serial.print(char(0x00));
  Serial.print(char(0x00));
  Serial.print(char(0xFF));

  for (int i = 0; i < length; i++) {
    Serial.print(char(dataTX[i]));
  }

  Serial.print(char(0x00));
  getACK();
  waitResponse();    // 1C - receive response
  getData(outLength);
}
//**********************************************************************
//!Wait for ACK response and stores it in the dataRX buffer
void getACK(void)
{
  delay(5);
  waitResponse();
  for (int i = 0; i < 5 ; i++) {
    dataRX[i] = Serial.read();
  }
}
//**********************************************************************
//!Wait the response of the module
void waitResponse(void)
{
  int val = 0xFF;
  int cont = 0x00;
  while (val != 0x00) {
    val = Serial.read();
    delay(5);
    cont ++;
  }
}
//**********************************************************************
//!Get data from the module
void getData(uint8_t outLength)
{
  for (int i = 5; i < outLength; i++) {
    dataRX[i] = Serial.read(); // read data from the module.
  }
}
//**********************************************************************
//!Calculates the checksum and stores it in dataTX buffer
void checkSum(uint8_t *dataTX)
{
  for (int i = 0; i < dataTX[0] ; i++) {
    dataTX[dataTX[0] + 2] += dataTX[i + 2];
  }
  byte(dataTX[dataTX[0] + 2] = - dataTX[dataTX[0] + 2]);
}
//**********************************************************************
//!Calculates the length checksum and stores it in the buffer.
uint8_t lengthCheckSum(uint8_t *dataTX)
{
  dataTX[1] = byte(0x100 - dataTX[0]);
}

    


    

3.2. UART and SPI module

The Multiprotocol Radio Shield has been designed to be used with the new industrial protocols modules. Some of these board uses SPI communication. The SPI port allows more speed communication and frees up the Arduino's UART for other purposes. In this part of the tutorial we are going to connect the RS-485 board (Modbus protocol) and send data wirelessly using XBee 805.15.4 modules.

NOTE:
  • If you are using 802.15.4 with the SPI modules, is necessary the use of sockets with pin 4 cut.
  • This is no necessary if you are using ZigBee or DigiMesh communication protocols.
  • If you want more information about RS-485 and Modbus, please see RS-485 tutorial

This sketch shows how to send data through RS-485 standard. This standard defines the electrical characteristics of drivers and receivers for use in digital systems. It does not specify or recommend any communications protocol. For a complete communication protocol, please see the Modbus examples.

Code:
/*  
 *  Multiprotocol Radio Shield with RS-485 ModBus
 *  
 *  Copyright (C) Libelium Comunicaciones Distribuidas S.L. 
 *  http://www.libelium.com 
 *  
 *  This program is free software: you can redistribute it and/or modify 
 *  it under the terms of the GNU General Public License as published by 
 *  the Free Software Foundation, either version 3 of the License, or 
 *  (at your option) any later version. 
 *  a
 *  This program is distributed in the hope that it will be useful, 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License 
 *  along with this program.  If not, see http://www.gnu.org/licenses/. 
 *  
 *  Version:           1.1
 *  Design:            David GascĂłn 
 *  Implementation:    Ahmad Saad
 */

// Include always this library when you are using the RS-485 functions
#include <RS485.h.h>
#include <multiprotocolShield.h.h>
#include <Wire.h.h>
#include <MCP23008.h.h>

//Include the SPI library
#include <SPI.h.h>
// Create an instance
RS485 myDevice = RS485();


void setup() {
  
  
  // Power on the USB for viewing data in the serial monitor
  Serial.begin(9600);
  delay(10); 

  // Initializes the module and assigns the SPI
  if ( myDevice.begin() == 0) {
    Serial.println("RS-485 module started successfully");
  } else {
    Serial.println("RS-485 did not initialize correctly");
  }
  delay(100);  

  // Configure the baud rate of the module
  myDevice.baudRateConfig(9600);
  // Configure the parity bit as disabled
  myDevice.parityBit(DISABLE);
  // Use one stop bit configuration
  myDevice.stopBitConfig(1);
  // Print hello message
  Serial.println("Hello this is RS-485 communication send data example.");
    
  delay(100); 
}

void loop() {  
 
  
  // Reading the analog input 1
  int analog1 = analogRead(A1);
  //Reading the analog input 2
  int analog2 = analogRead(A2);

  // Send data through RS-485 line
  myDevice.send("Data from analog1 input: ");
  myDevice.send(analog1);
  myDevice.send("\n");

  myDevice.send("Data from analog2 input: ");
  myDevice.send(analog2);
  myDevice.send("\n");
  delay(10);  

  socket0.ON();
  delay(100);
  socket0.setMUX();
  delay(100);
  
  Serial.println(F("Data: 0 1 2 3 4 5 6 7 8 9 0 a b c d e f g h i j k l m n o p q r s t u v w x y z"));  
  delay(100);
    
}
    

Device Compatibility

This shield is tested and fully compatible with the following boards:

Arduino Boards

  • Arduino Uno