Pantech.AI

How to Interface XBee with PIC18F4550

Overview of Xbee

XBee radios are based on the IEEE 802.15.4 standard, which defines the operation of low-rate wireless personal area networks (LR-WPANs). These radios are designed for various communication topologies, including point-to-point and star configurations, and facilitate wireless communication over the air.

Key features of XBee radios include:

  • Operating on the 2.5GHz unlicensed radio band.
  • Supporting low data rates (around 250Kbps).
  • Offering low power consumption with options like 1mW, 6mW, or 250mW.
  • Enabling short-range wireless communication with ranges of 90m, 750m, or up to 1 mile, depending on the module.

As a result, XBee radios are widely used in applications such as home automation, wireless sensor networks, industrial control, medical data collection, and building automation.

For a deeper understanding of how XBee modules work, refer to the XBee Module documentation.

XBee Module

Interfacing of XBee device with PIC18F4550

In this setup, we have connected the following components to the XBee End Device:

  • An LM35 temperature sensor for analog sampling.
  • A switch for digital sampling.

We will request these analog and digital samples from the XBee End Device using the XBee Coordinator.

The XBee modules are configured in API operating mode for communication.

The program for the PIC18F4550 will request the analog and digital samples from the XBee End Device via the XBee Coordinator. The status of the switch (0 for OFF and 1 for ON) and the temperature data from the LM35 will be displayed on a 16×2 LCD connected to the PIC18F4550.

Please note that the ADC Vref varies depending on the XBee model. The model used in this setup is based on the ZigBee protocol and has a fixed Vref of 1.2V. For accurate ADC readings, refer to the documentation for your specific XBee model to find the Vref.

Connection Diagram of Xbee to PIC18F4550

XBee Interfacing with PIC18F4550

Note: In the above example, it is necessary to configure the XBee End Device pins (AD1/DIO1 and AD2/DIO2) as analog and digital inputs. For detailed instructions on how to configure the XBee pins, refer to the “Configuring the XBee Pins” section in the XBee Module documentation.

Need to know

In this example, we are using XBee in API mode, and for basic communication, we are constructing the following frames:

  1. AT COMMAND FRAME: This frame is used to send AT commands to the XBee device.
  2. REMOTE AT COMMAND FRAME: This frame allows us to send AT commands to an XBee device located remotely, with its address specified in the frame.
  3. TRANSMIT REQUEST FRAME: This frame is used to transmit a data string to an XBee device, with the device’s address specified in the frame.
  4. IO DATA SAMPLE FRAME: This frame is used to receive analog and digital data transmitted by an XBee device located remotely, with the device’s address specified in the frame.

In the program below, we are using functions that build the structures for these frames. While the API frame structures may seem lengthy, they are straightforward to understand once we are familiar with each frame’s structure.

To get familiar with the API frames and their structure, refer to the “API Frame Generator” section in the X-CTU documentation in the XBee Module.

Xbee Code for PIC18F4550

/*
 * PIC18F4550 interface with X-Bee 
 * http://www.electronicwings.com
 */


#include <pic18f4550.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "Configuration_header_file.h"
#include "LCD_16x2_Header_File.h"   /* Include LCD header file */
#include "USART_Header_File.h"      /* Include USART header file */

/* Define Required XBee Frame Type and Responses */
#define START_DELIMITER                     0x7E
#define AT_COMMAND_FRAME                    0x08
#define TRANSMIT_REQUEST_FRAME              0x10
#define REMOTE_AT_COMMAND_FRAME             0x17
#define IO_DATA_SAMPLE_FRAME                0x92
#define AT_COMMAND_RESPONSE_FRAME           0x88
#define REMOTE_AT_COMMAND_RESPONSE_FRAME    0x97
#define RECEIVE_PACKET_FRAME                0x90
#define TRANSMIT_STATUS_FRAME               0x8B

#define FRAME_ID                            0x01
#define REMOTE_AT_COMMAND_OPT               0x02
#define TRANSMIT_REQUEST_OPT                0x00
#define TRANSMIT_REQUEST_BROADCAST_RADIUS   0x00

#define Read                                0
#define Write                               1


#define BUFFER_SIZE			    	100
#define DIGITAL_BUFFER_SIZE		    16
#define ANALOG_BUFFER_SIZE		    8

uint8_t		ReceiveBuffer[BUFFER_SIZE];
int8_t		DigitalData[DIGITAL_BUFFER_SIZE];
int16_t		AnalogData[ANALOG_BUFFER_SIZE];
uint16_t	BufferPointer	= 0,
			LastByteOfFrame	= 0; 
uint32_t	Command_Value	= 0;

bool 	_IsContainsDigital	= false,
	 	_IsContainsAnalog	= false;	

bool Is_Data_Received()		/* Check wether data received or not  */
{
	for (uint8_t i = 0; i < BUFFER_SIZE; i++)
	{
		if (ReceiveBuffer[i] != 0 && i > 0)
			return true;
	}
	return false;
}

bool Is_Checksum_Correct()	/* Check wether received data is correct */
{
	uint16_t checksum = 0;
	for(uint8_t i = 3; i < LastByteOfFrame; i++)
		checksum = checksum + ReceiveBuffer[i];
	checksum = 0xFF - checksum;
	if(ReceiveBuffer[LastByteOfFrame] == (uint8_t)checksum &&
		LastByteOfFrame > 0)
		return true;
	else
		return false;
}

/* Sample function for parsing received buffer as per frame type */
void sample()
{
    uint8_t Frame_Type, Is_Analog;
    uint16_t Length, Is_Digital, Digital_Value;
	
    _IsContainsAnalog = false;
    _IsContainsDigital = false;

    /* 2 byte Frame length is at 1st and 2nd position of frame */
    Length = ((int)ReceiveBuffer[1]<<8) + ReceiveBuffer[2];
    /* 1 byte Frame type is at 3rd position of frame */
    Frame_Type = ReceiveBuffer[3];

    switch (Frame_Type)
     {
	case (IO_DATA_SAMPLE_FRAME):	/* Parse received I/O data sample frame */
	  if(Is_Data_Received() == false || Is_Checksum_Correct() == false) break;
	  Is_Digital = ((int)ReceiveBuffer[16]<<8) + ReceiveBuffer[17];
	  Is_Analog = ReceiveBuffer[18];
	  Digital_Value = ((int)ReceiveBuffer[19]<<8) + ReceiveBuffer[20];
	
	  if(Is_Analog != 0)
	   _IsContainsAnalog = true;
	  if(Is_Digital != 0)
	   _IsContainsDigital = true;
/****** Check For whether sample contains Analog/Digital Sample *********/		

	  for (uint8_t i = 0; i < DIGITAL_BUFFER_SIZE; i++)
	   {
		if(((Is_Digital >> i) & 0x01) == 1 && ((Digital_Value>>i) & 0x01) != 0)
		 DigitalData[i] = 1;
		else if(((Is_Digital >> i) & 0x01) == 1 && ((Digital_Value>>i) & 0x01) == 0)
		 DigitalData[i] = 0;
		else
		 DigitalData[i] = -1;
	   }
			
	  for (uint8_t i = 0, j = 0; i < ANALOG_BUFFER_SIZE; i++)
	   {
	    if(((Is_Analog >> i) & 0x01) == 1)
	     {
		if(Is_Digital != 0)
		AnalogData[i] = 256 * ReceiveBuffer[21+(j*2)] + ReceiveBuffer[22+(j*2)];
		else
		AnalogData[i] = 256 * ReceiveBuffer[19+(j*2)] + ReceiveBuffer[20+(j*2)];
		j++;
	     }
	    else
	     {
		AnalogData[i] = -1;
	     }
	   }

	case (TRANSMIT_STATUS_FRAME):/* Parse received Transmit status frame */
	  if(Is_Data_Received() ==false || Is_Checksum_Correct() ==false) break;
	  break;	/* Check whether frame is correctly received or not */
	case (RECEIVE_PACKET_FRAME):
	  if(Is_Data_Received() ==false || Is_Checksum_Correct() ==false) break;
	  break;
	case (REMOTE_AT_COMMAND_RESPONSE_FRAME):
	  if(Is_Data_Received() ==false || Is_Checksum_Correct() ==false) break;
	  break;
	case (AT_COMMAND_RESPONSE_FRAME):
	  if(Is_Data_Received() ==false || Is_Checksum_Correct() ==false) break;
	  break;
	default:
	  break;
     }
}

bool Get_Sample()	/* Get sample function */
{
	MSdelay(200);	/* Wait for response */
	for (uint16_t count = 0; Is_Data_Received() == false; count++)
	{
		if(count>15000)
		{
			return false;
		}
	}
    	INTCONbits.GIE=0;	/* Disable global interrupt to parse received data */
	sample();		/* Parse data in sample function */
	memset(ReceiveBuffer,0,BUFFER_SIZE);/* Clear ReceiveBuffer */
    	INTCONbits.GIE=1;	/* Enable Global Interrupt */
	return true;		/* Return success value */
}

Remote_AT_Command(uint32_t Long_Address_MSB, uint32_t Long_Address_LSB, 
		  uint16_t Short_Address, const char* ATCommand, bool action)
{
	uint16_t Length,Checksum;
	if (action == Write)
	{
		/* Define parameter value depend frame length */
		if(Command_Value > 0x00FFFFFF) Length = 19;
		else if(Command_Value > 0x00FFFF) Length = 18;
		else if(Command_Value > 0x00FF) Length = 17;
		else Length = 16;
	}
	else
		Length = 15;

	Checksum = REMOTE_AT_COMMAND_FRAME + FRAME_ID;/* Calculate Checksum */
	for (int8_t i = 24; i >= 0; i = i-8) 
		Checksum = Checksum + (Long_Address_MSB >> i);
	for (int8_t i = 24 ; i >= 0; i = i-8) 
		Checksum = Checksum + (Long_Address_LSB >> i);
	
	Checksum = Checksum + (Short_Address >> 8) + Short_Address 
		   + REMOTE_AT_COMMAND_OPT + ATCommand[0] + ATCommand[1];
	if (action == Write)
	Checksum = Checksum + (Command_Value >> 24) + (Command_Value >> 16)
		   + (Command_Value >> 8) + Command_Value ;

	/* Subtract checksum lower byte from 0xFF to get 1 byte checksum */
	Checksum = 0xFF - Checksum;

	USART_TxChar(START_DELIMITER);	/* Send frame start with 1 byte Delimiter */
	USART_TxChar(Length >> 8);	/* Send 2 byte length */
	USART_TxChar(Length);
	USART_TxChar(REMOTE_AT_COMMAND_FRAME);	/* Send 1 byte frame type */
	USART_TxChar(FRAME_ID);			/* Send 1 byte frame ID */
	for (int8_t i = 24 ; i >= 0 ; i = i-8)	/* Send 32-bit long destin address MSB */
		USART_TxChar(Long_Address_MSB >> i);
	for (int8_t i = 24 ; i >= 0 ; i = i-8)	/* Send 32-bit long destin address LSB */
		USART_TxChar(Long_Address_LSB >> i);
	USART_TxChar(Short_Address >> 8);	/* Send 16-bit long destination address */
	USART_TxChar(Short_Address);
	USART_TxChar(REMOTE_AT_COMMAND_OPT);	/* Send Option */
	USART_SendString(ATCommand);		/* Send AT command */

	if(action == Write)
	{
		if(Length == 19)		/* Send value */
		{
			USART_TxChar(Command_Value >> 24);
			USART_TxChar(Command_Value >> 16);
			USART_TxChar(Command_Value >> 8);
		}
		if(Length == 18)
		{
			USART_TxChar(Command_Value >> 16);
			USART_TxChar(Command_Value >> 8);
		}
		if(Length == 17) 
			USART_TxChar(Command_Value >> 8);
		USART_TxChar(Command_Value);
	}
	USART_TxChar(Checksum);			/* Send Checksum */
}

void AT_Command(const char* ATCommand, bool action)
{
	uint16_t Length,Checksum;

	if (action == Write)
	{
		if(Command_Value > 0x00FFFFFF) Length = 8;
		else if(Command_Value > 0x00FFFF) Length = 7;
		else if(Command_Value > 0x00FF) Length = 6;
		else Length = 5;
	}
	else
		Length = 4;

	Checksum = AT_COMMAND_FRAME + FRAME_ID + ATCommand[0] + ATCommand[1];
	if (action == Write)
	Checksum = Checksum + (Command_Value >> 24) + (Command_Value >> 16)
		   + (Command_Value >> 8) + Command_Value ;
	Checksum = 0xFF - Checksum;

	USART_TxChar(START_DELIMITER);
	USART_TxChar(Length >> 8);
	USART_TxChar(Length);
	USART_TxChar(AT_COMMAND_FRAME);
	USART_TxChar(FRAME_ID);
	USART_SendString(ATCommand);

	if(action == Write)
	{
		if(Length == 8)		/* Send value */
		{
			USART_TxChar(Command_Value >> 24);
			USART_TxChar(Command_Value >> 16);
			USART_TxChar(Command_Value >> 8);
		}
		if(Length == 7)
		{
			USART_TxChar(Command_Value >> 16);
			USART_TxChar(Command_Value >> 8);
		}
		if(Length == 6) 
			USART_TxChar(Command_Value >> 8);
		USART_TxChar(Command_Value);
	}
	USART_TxChar(Checksum);
}

void Transmit_Request(uint32_t Long_Address_MSB, uint32_t Long_Address_LSB,
		      uint16_t Short_Address, char* str)
{
	uint16_t Length,Checksum;
	Length = 14 + strlen(str);

	Checksum = TRANSMIT_REQUEST_FRAME + FRAME_ID;/* Calculate Checksum */
	for (int8_t i = 24; i >= 0; i = i-8) 
		Checksum = Checksum + (Long_Address_MSB >> i);
	for (int8_t i = 24 ; i >= 0; i = i-8) 
		Checksum = Checksum + (Long_Address_LSB >> i);
	Checksum = Checksum + (Short_Address >> 8) + Short_Address;
	for (int8_t i=0;str[i]!=0;i++)
		Checksum = Checksum + str[i];
	Checksum = 0xFF - Checksum;

	USART_TxChar(START_DELIMITER);
	USART_TxChar(Length >> 8);
	USART_TxChar(Length);
	USART_TxChar(TRANSMIT_REQUEST_FRAME);
	USART_TxChar(FRAME_ID);
	for (int8_t i = 24 ; i >= 0 ; i = i-8)
		USART_TxChar(Long_Address_MSB >> i);
	for (int8_t i = 24 ; i >= 0 ; i = i-8)
		USART_TxChar(Long_Address_LSB >> i);
	USART_TxChar(Short_Address >> 8);
	USART_TxChar(Short_Address);
	USART_TxChar(TRANSMIT_REQUEST_BROADCAST_RADIUS);
	USART_TxChar(TRANSMIT_REQUEST_OPT);
	USART_SendString(str);
	USART_TxChar(Checksum);
}

void Write_AT_Command(char* ATCommand, uint32_t _CommandValue)
{
	Command_Value = _CommandValue;
	AT_Command(ATCommand, Write);
}

void Read_AT_Command(char* ATCommand)
{
	AT_Command(ATCommand, Read);
}

void Write_Remote_AT_Command(uint32_t Long_Address_MSB, uint32_t Long_Address_LSB,
		  uint16_t Short_Address, char* ATCommand, uint32_t _CommandValue)
{
	Command_Value = _CommandValue;
	Remote_AT_Command(Long_Address_MSB, Long_Address_LSB, Short_Address, ATCommand, Write);
}

void Read_Remote_AT_Command(uint32_t Long_Address_MSB, uint32_t Long_Address_LSB,
		  uint16_t Short_Address, char* ATCommand, uint32_t _CommandValue)
{
	Remote_AT_Command(Long_Address_MSB, Long_Address_LSB, Short_Address, ATCommand, Read);
}

void interrupt ISR()		/* Receive ISR routine */
{
    char received_char;
    if(RCIF==1){
        received_char = RCREG;
	/* check if any overrun occur due to continuous reception */
        if(RCSTAbits.OERR)
        {           
            CREN = 0;
            NOP();
            CREN=1;
        }
        if (received_char == START_DELIMITER)
        {
            LastByteOfFrame = BufferPointer;
            BufferPointer = 0;
            ReceiveBuffer[BufferPointer] = received_char;
        }
        else
        {
            BufferPointer++;
            ReceiveBuffer[BufferPointer] = received_char;
        }
    }
}

void SetTo_Broadcast()
{
	MSdelay(500);
	Write_AT_Command("DH", 0x00000000);
	MSdelay(500);
	Write_AT_Command("DL", 0x0000FFFF);
	MSdelay(500);
	Read_AT_Command("WR");
	MSdelay(1000);	
}

int main(void)
{
    char _buffer[25];
    double Temperature;
    uint32_t Remote_Address_DH = 0x0013A200;
    uint32_t Remote_Address_DL = 0x41241CB2;

    OSCCON=0x72;		/* set internal clock to 8MHz */
    USART_Init(9600);		/* Initiate USART with 9600 baud rate */
    LCD_Init();			/* Initialize LCD */
    LCD_String_xy(1, 0, "X-Bee Network ");
    LCD_String_xy(2, 0, "Demo..!!");
    MSdelay(20000);
    LCD_Clear();
    INTCONbits.GIE=1;		/* enable Global Interrupt */
    INTCONbits.PEIE=1;		/* enable Peripheral Interrupt */
    PIE1bits.RCIE=1;		/* enable Receive Interrupt */	

    LCD_String_xy(1, 0, "Setting X-Bee to");
    LCD_String_xy(2, 0, "Broadcast mode  ");
    SetTo_Broadcast();		/* Set XBee coordinator to broadcast mode */
    LCD_Clear();
	
    LCD_String_xy(1, 0, "Request Samples ");
    /* Request Samples from remote X-Bee device at 100ms Sample rate */
    Write_Remote_AT_Command(Remote_Address_DH, Remote_Address_DL, 0xFFFE, "IR", 100);
    MSdelay(1500);
    LCD_Clear();

    while (1)
	{
		Get_Sample();
		if (_IsContainsDigital)
		{
			if(DigitalData[2] >= 0)/* Switch status on DIO2 pin */
			{
			  sprintf(_buffer, "Switch = %d   ", DigitalData[2]);
			  LCD_String_xy(1, 0, _buffer);	/* print on 1st row */
			  memset(_buffer, 0, 25);	/* Clear Buffer */
			}
		}
		if (_IsContainsAnalog)
		{
            		Temperature = (double)AnalogData[1] * 0.1171875;
			if(AnalogData[1] >= 0)/* LM35 value on AIN1 pin */
			{
			  sprintf(_buffer, "Temp = %0.1f C  ", Temperature);
			  LCD_String_xy(2, 0, _buffer);/* print on 2nd row */
			  memset(_buffer, 0, 25);	/* Clear Buffer */
			}
		}
	}
}

Leave a Comment

Your email address will not be published. Required fields are marked *