My Cart

Multiprotocol Radio Shield v1.0 Tutorial for Arduino, Raspberry Pi and Intel Galileo

Difficulty Level: Beginner -


In January 2016 Cooking Hacks launched a new version of the Multiprotocol Radio Shield, we have improved the shield with a Digital Switch that allows to enable/disable the different sockets.

In this tutorial we will explain how to work with Multiprotocol Radio Shield V1.0. If you have Multiprotocol Radio Shield V2 you can see how to use it on this link:

Multiprotocol Radio Shield v2.0 Tutorial for Arduino, Raspberry Pi and Intel Galileo

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 modules and LoRa, but also is compatible with the rest of the cooking-hacks modules as RFID, XBee and 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.

Multiprotocol Radio Shield PCB

Version 1 of the shield:

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

UART modules

The UART port is multiplexed and this multiplexer can be managed with the DIGITAL pin 5. First of all, the pin must be configured as an OUTPUT.

pinMode(5, OUTPUT);

If you put this pin HIGH, the communication will be established with the SOCKET1.

digitalWrite(5, HIGH);

Else, the communication will be established with the SOCKET0.

digitalWrite(5, LOW);

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. The SOCKET0 uses the digital pin 6 as selection pin, and the SOCKET1 uses the digital pin 4.

Multiprotocol Radio Shield switch

The Multiprotocol Radio Shield includes a manual switch to switch ON/OFF the multiplexer. This switch is very useful when you are programming the Arduino UNO board and allows the programming of the micro controller without removing the module.

Gateway mode

The Multiprotocol Radio Shield can be used in gateway mode, by retiring the micro controller, or uploading an empty sketch. In this mode, you should put the jumpers in USB position, and the communication can be established only with SOCKET0.

Multiprotocol Radio Shield over Arduino

3. Real applications

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.0
 *  Design:            David GascĂłn 
 *  Implementation:    Ahmad Saad
 */

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);
  pinMode(5, OUTPUT);
  digitalWrite(5, LOW);
  //! 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()
{
  digitalWrite(5, HIGH);
  Serial.begin(57600);
  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);
  
  digitalWrite(5, LOW);
  Serial.begin(115200);
  delay(100);

}

//**********************************************************************
void readTAG()
{
  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]);
}

    

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

We have divided the code in two parts. The first par reads the temperature from Modbus device and the second part, sends the temperature trough the XBee 802.15.4 module. You can see how to connect a Modbus device to the RS-485 module in the corresponding tutorial.

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


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.0
 *  Design:            David GascĂłn 
 *  Implementation:    Ahmad Saad
 */


// Include these libraries for using the RS-485 and Modbus functions
#include <RS485.h>
#include <ModbusMaster485.h>
#include <SPI.h>

// Instantiate ModbusMaster object as slave ID 1
ModbusMaster485 node(254);

// Define one addres for reading
#define address 101

// Define the number of bytes to read
#define bytesQty 2

void setup()
{
  
  // XBee in SOCKET0
  pinMode(5, OUTPUT);
  digitalWrite(5, LOW);

  // Power on the USB for viewing data in the serial monitor
  Serial.begin(115200);
  delay(100);
  // Initialize Modbus communication baud rate
  node.begin(19200);

  // Print hello message
  Serial.println("Modbus communication over RS-485");
  delay(100);
}



void loop()
{
  // This variable will store the result of the communication
  // result = 0 : no errors
  // result = 1 : error occurred
  int result =  node.readHoldingRegisters(address, bytesQty);

  if (result != 0) {
    // If no response from the slave, print an error message
    Serial.println("Communication error");
    delay(1000);
  }
  else {

    // If all OK
    Serial.print("Read value : ");

    // Print the read data from the slave
    Serial.print(node.getResponseBuffer(0));
    delay(1000);
  }

  Serial.print("\n");
  delay(2000);

  // Clear the response buffer
  node.clearResponseBuffer();
}