1 // *********************************************************************
    2 //   Sketch to cooperate with CustomDpaHandler-Bridge-SPI.c            *
    3 // *********************************************************************
    4 // Copyright (c) IQRF Tech s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-Bridge-SPI.ino,v $
    7 // Version: $Revision: 1.7 $
    8 // Date:    $Date: 2021/11/05 10:25:14 $
    9 //
   10 // Revision history:
   11 //   2019/03/01  Release for DPA 4.01
   12 //   2018/10/25  Release for DPA 3.03
   13 //
   14 // *********************************************************************
   15 
   16 // Please see CustomDpaHandler-Bridge-SPI.c for implementation details.
   17 
   18 // This Sketch implements one standard IQRF sensor
   19 // Type of the sensor is Binary7
   20 
   21 // Include IQRF DPA headers
   22 #include "DPA.h"
   23 #include "IQRFstandard.h"
   24 #include "IQRF_HWPID.h"
   25 
   26 // The device communicates using SPI, so include the library
   27 #include <SPI.h>
   28 
   29 // Signal pin from slave. When going low it might wake up the device and when low it signals the SPI transaction is on
   30 #define SIGNAL_PIN    7
   31 
   32 // Gap [us] between SPI bytes to let slave do some work
   33 #define SPI_BYTES_GAP 40
   34 
   35 //############################################################################################
   36 void setup()
   37 //############################################################################################
   38 {
   39   // Set pin from slave (SS) as input
   40   pinMode( SIGNAL_PIN, INPUT );
   41 
   42   // Setup SPI
   43   // 1 MHz SPI Clock
   44   SPI.setClockDivider( SPI_CLOCK_DIV16 );
   45   // Set SPI mode
   46   SPI.setDataMode( SPI_MODE1 );
   47   SPI.begin();
   48 }
   49 
   50 //############################################################################################
   51 // Returns sensor value
   52 byte GetSensor0Value()
   53 //############################################################################################
   54 {
   55   // Remap values from Alcohol Gas Sensor MQ-3 connected to the analog input 0
   56   return map( analogRead( 0 ), 500, 1023, 0, 127 );
   57 }
   58 
   59 //############################################################################################
   60 
   61 // HDLC byte stuffing bytes
   62 // Flag Sequence
   63 #define   HDLC_FRM_FLAG_SEQUENCE    0x7e
   64 // Asynchronous Control Escape
   65 #define   HDLC_FRM_CONTROL_ESCAPE   0x7d
   66 // Asynchronous transparency modifier
   67 #define   HDLC_FRM_ESCAPE_BIT       0x20
   68 
   69 // Flag to DpaEvent_DpaRequest event value to indicate return TRUE not FALSE
   70 #define EVENT_RETURN_TRUE           0x80
   71 // Flag to DpaEvent_DpaRequest event value to report error, error value is the 1st data byte
   72 #define EVENT_RESPONSE_ERROR        0x40
   73 
   74 // Received data from IQRF
   75 byte RxBuffer[2 * sizeof( byte ) + sizeof( TDpaIFaceHeader ) + sizeof( TDpaMessage )];
   76 
   77 // Data from IQRF length limits
   78 #define MIN_RX_PACKET_DATA_LENGTH  2
   79 #define MAX_RX_PACKET_DATA_LENGTH  sizeof( RxBuffer )
   80 
   81 //############################################################################################
   82 // Sends one byte to IQRF
   83 void TxByte( byte data )
   84 //############################################################################################
   85 {
   86   SPI.transfer( data );
   87   delayMicroseconds( SPI_BYTES_GAP );
   88 }
   89 
   90 //############################################################################################
   91 // Sends one HDLC byte to IQRF
   92 void TxHdlcByte( byte data )
   93 //############################################################################################
   94 {
   95   switch ( data )
   96   {
   97     default:
   98       TxByte( data );
   99       return;
  100 
  101     case HDLC_FRM_FLAG_SEQUENCE:
  102     case HDLC_FRM_CONTROL_ESCAPE:
  103     {
  104       TxByte( HDLC_FRM_CONTROL_ESCAPE );
  105       TxByte( data ^ HDLC_FRM_ESCAPE_BIT );
  106       return;
  107     }
  108   }
  109 }
  110 
  111 //############################################################################################
  112 // Returns FRC value back to IQRF
  113 void ResponseFRCvalue( unsigned long frcValue )
  114 //############################################################################################
  115 {
  116   // Start packet
  117   TxByte( HDLC_FRM_FLAG_SEQUENCE );
  118   // Send event value
  119   TxHdlcByte( DpaEvent_FrcValue );
  120   // Send FRC value up to 4 bytes
  121   TxHdlcByte( frcValue & 0xFF );
  122   TxHdlcByte( ( frcValue >> 8 ) & 0xFF );
  123   TxHdlcByte( ( frcValue >> 16 ) & 0xFF );
  124   TxHdlcByte( ( frcValue >> 24 ) & 0xFF );
  125   // Stop packet
  126   TxByte( HDLC_FRM_FLAG_SEQUENCE );
  127 }
  128 
  129 //############################################################################################
  130 // Return DPA response back to IQRF
  131 void ResponseCommand( byte returnFlags, byte _DpaDataLength, byte dataLength, byte *pData )
  132 //############################################################################################
  133 {
  134   // Start packet
  135   TxByte( HDLC_FRM_FLAG_SEQUENCE );
  136   // Send event value
  137   TxHdlcByte( DpaEvent_DpaRequest | returnFlags );
  138   // Send DPA variable data length (must not equal to the actual data length sent)
  139   TxHdlcByte( _DpaDataLength );
  140   // Send DPA response data
  141   for ( ; dataLength != 0; dataLength-- )
  142     TxHdlcByte( *pData++ );
  143   // Stop packet
  144   TxByte( HDLC_FRM_FLAG_SEQUENCE );
  145 }
  146 
  147 //############################################################################################
  148 // Packet from Custom DPA Handler was received
  149 void CustomDpaHandler( byte dataLength )
  150 //############################################################################################
  151 {
  152   // Which Custom DPA Handler event to handle?
  153   switch ( RxBuffer[0] )
  154   {
  155     // Prepare DPA response to DPA request
  156     case DpaEvent_DpaRequest:
  157       if ( dataLength >= ( 2 * sizeof( byte ) + sizeof( TDpaIFaceHeader ) ) )
  158       {
  159         // Fake important DPA variables for the DPA Request/Response so the Custom DPA handler code will can be written almost same way on both platforms
  160 #define _DpaDataLength  (RxBuffer[1])
  161 #define _NADR           (RxBuffer[2])
  162 #define _NADRhigh       (RxBuffer[3])
  163 #define _PNUM           (RxBuffer[4])
  164 #define _PCMD           (RxBuffer[5])
  165 #define _HWPIDlow       (RxBuffer[6])
  166 #define _HWPIDhigh      (RxBuffer[7])
  167 #define _DpaMessage     (*((TDpaMessage*)(RxBuffer+8)))
  168 
  169       // Fake Custom DPA Handler macro to return DPA error (this macro does not do return the same way the DPA original macro)
  170 #define DpaApiReturnPeripheralError(error) do { \
  171   _DpaMessage.ErrorAnswer.ErrN = error; \
  172   returnDataLength = _DpaDataLength = sizeof( _DpaMessage.ErrorAnswer.ErrN ); \
  173   returnFlags = EVENT_RESPONSE_ERROR | EVENT_RETURN_TRUE; \
  174   } while( 0 )
  175 
  176         // Value or error flag to return from Custom DPA handler
  177         byte returnFlags = 0;
  178         // Length data to return (may not equal to _DpaDataLength)
  179         byte returnDataLength = 0;
  180         // Device enumeration?
  181         if ( IsDpaEnumPeripheralsRequest() )
  182         {
  183           // We implement 1 user peripheral
  184           _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1;
  185           FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_SENSORS );
  186           _DpaMessage.EnumPeripheralsAnswer.HWPID = 0x123F;
  187           _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0xABCD;
  188 
  189           // Return the enumeration structure but do not modify _DpaDataLength
  190           returnDataLength = sizeof( _DpaMessage.EnumPeripheralsAnswer );
  191           // Return TRUE
  192           returnFlags = EVENT_RETURN_TRUE;
  193         }
  194         // Get information about peripherals?
  195         else if ( IsDpaPeripheralInfoRequest() )
  196         {
  197           if ( _PNUM == PNUM_STD_SENSORS )
  198           {
  199             _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_STD_SENSORS;
  200             _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE;
  201             // Set standard version
  202             _DpaMessage.PeripheralInfoAnswer.Par1 = STD_SENSORS_VERSION;
  203 
  204             // Return the information structure but do not modify _DpaDataLength
  205             returnDataLength = sizeof( _DpaMessage.PeripheralInfoAnswer );
  206             // Return TRUE
  207             returnFlags = EVENT_RETURN_TRUE;
  208           }
  209         }
  210         else
  211         {
  212           // Handle peripheral command
  213 
  214           // Supported peripheral number?
  215           if ( _PNUM == PNUM_STD_SENSORS )
  216           {
  217             // Supported commands?
  218             switch ( _PCMD )
  219             {
  220               // Invalid command
  221               default:
  222                 // Return error
  223                 DpaApiReturnPeripheralError( ERROR_PCMD );
  224                 break;
  225 
  226                 // Sensor enumeration
  227               case PCMD_STD_ENUMERATE:
  228                 // Check data request length
  229                 if ( _DpaDataLength != 0 )
  230                 {
  231                   DpaApiReturnPeripheralError( ERROR_DATA_LEN );
  232                   break;
  233                 }
  234 
  235                 // 1st byte is sensor type
  236                 _DpaMessage.Response.PData[0] = STD_SENSOR_TYPE_BINARYDATA7;
  237 
  238                 // Return just one sensor type
  239                 returnDataLength = _DpaDataLength = sizeof( _DpaMessage.Response.PData[0] );
  240                 // Return TRUE
  241                 returnFlags = EVENT_RETURN_TRUE;
  242                 break;
  243 
  244                 // Supported commands. They are handled almost the same way
  245               case PCMD_STD_SENSORS_READ_VALUES:
  246               case PCMD_STD_SENSORS_READ_TYPES_AND_VALUES:
  247               {
  248                 // No sensor bitmap specified?
  249                 if ( _DpaDataLength == 0 )
  250                 {
  251                   // Bitmap is 32 bits long = 4
  252                   _DpaDataLength = 4;
  253                   // Simulate 1st sensor in the bitmap (states of the other unimplemented sensors do not care)
  254                   _DpaMessage.Request.PData[0] |= 0x01; // Note: must not modify W
  255                 }
  256                 // Valid bitmap length?
  257                 else if ( _DpaDataLength != 4 )
  258                 {
  259                   // Return error
  260                   DpaApiReturnPeripheralError( ERROR_DATA_LEN );
  261                   break;
  262                 }
  263 
  264                 // Pointer to the response data
  265                 byte *pResponseData = _DpaMessage.Response.PData;
  266                 // Is my only sensor selected?
  267                 if ( ( _DpaMessage.Request.PData[0] & 0x01 ) != 0 )
  268                 {
  269                   // Return also sensor type?
  270                   if ( _PCMD == PCMD_STD_SENSORS_READ_TYPES_AND_VALUES )
  271                     *pResponseData++ = STD_SENSOR_TYPE_BINARYDATA7;
  272 
  273                   // Return sensor data
  274                   *pResponseData++ = GetSensor0Value();
  275                 }
  276 
  277                 // Returned data length
  278                 returnDataLength = _DpaDataLength = ( pResponseData - _DpaMessage.Response.PData );
  279                 // Return TRUE
  280                 returnFlags = EVENT_RETURN_TRUE;
  281                 break;
  282               }
  283             }
  284           }
  285         }
  286 
  287         // Return DPA response
  288         ResponseCommand( returnFlags, _DpaDataLength, returnDataLength, (byte*)&_DpaMessage );
  289       }
  290       break;
  291 
  292       // Return FRC Value
  293     case DpaEvent_FrcValue:
  294       // Check for the minimum length (FRC command and at least 2 bytes of data)
  295       if ( dataLength >= ( 2 + 1 + 2 ) )
  296       {
  297         // Fake important DPA variables for the DPA FRC handling
  298 #define FrcCommand               (RxBuffer[1])
  299 #define DataOutBeforeResponseFRC ((byte*)( &RxBuffer[2] ))
  300 
  301       // Check the correct FRC request
  302         if ( DataOutBeforeResponseFRC[0] == PNUM_STD_SENSORS &&
  303           ( DataOutBeforeResponseFRC[1] == 0x00 || DataOutBeforeResponseFRC[1] == STD_SENSOR_TYPE_BINARYDATA7 ) &&
  304              ( DataOutBeforeResponseFRC[2] & 0x1f ) == 0 )
  305         {
  306           // Which FRC command to handle?
  307           switch ( FrcCommand )
  308           {
  309             case FRC_STD_SENSORS_1B:
  310               ResponseFRCvalue( GetSensor0Value() + 4 );
  311               break;
  312 
  313             case FRC_STD_SENSORS_BIT:
  314               ResponseFRCvalue( ( GetSensor0Value() & ( 0x01 << ( DataOutBeforeResponseFRC[2] >> 5 ) ) ) != 0 ? 0x03 : 0x01 );
  315               break;
  316           }
  317         }
  318       }
  319       break;
  320   }
  321 }
  322 
  323 //############################################################################################
  324 void loop()
  325 //############################################################################################
  326 {
  327   // HDLC machine states
  328   typedef enum { RXstateWaitHead, RXstatePacket, RXstateEscape } TState;
  329 
  330   // HDLC state
  331   byte state = RXstateWaitHead;
  332   // Length of the already received data
  333   byte rxLength;
  334   // Pointer to the received data
  335   byte *pRxBuffer;
  336 
  337   // Is SPI transaction running?
  338   while ( digitalRead( SIGNAL_PIN ) == LOW )
  339   {
  340     // Read the byte from IQRF
  341     byte oneByte = SPI.transfer( HDLC_FRM_FLAG_SEQUENCE );
  342     delayMicroseconds( SPI_BYTES_GAP );
  343     switch ( state )
  344     {
  345       // Waiting for the HDLC header
  346       case RXstateWaitHead:
  347       {
  348         if ( oneByte == HDLC_FRM_FLAG_SEQUENCE )
  349         {
  350 _SetRXstatePacket:
  351           rxLength = 0;
  352           pRxBuffer = RxBuffer;
  353           state = RXstatePacket;
  354         }
  355         break;
  356       }
  357 
  358       // Handling packet data byte
  359       case RXstatePacket:
  360       {
  361         switch ( oneByte )
  362         {
  363           case HDLC_FRM_CONTROL_ESCAPE:
  364             // RXstateEscape
  365             state++;
  366             goto _ExitMachine;
  367 
  368           case HDLC_FRM_FLAG_SEQUENCE:
  369           {
  370             if ( rxLength >= MIN_RX_PACKET_DATA_LENGTH )
  371             {
  372               // Packet received, handle it
  373               CustomDpaHandler( rxLength );
  374               // Exit loop
  375               return;
  376             }
  377 
  378             goto _SetRXstatePacket;
  379           }
  380         }
  381 
  382 _StoreByte:
  383         if ( rxLength == ( MAX_RX_PACKET_DATA_LENGTH + 2 ) )
  384           goto _SetRXstateWaitHead;
  385 
  386         *pRxBuffer++ = oneByte;
  387         rxLength++;
  388 
  389 _ExitMachine:
  390         break;
  391       }
  392 
  393       // Handle escaped byte
  394       case RXstateEscape:
  395       {
  396         switch ( oneByte )
  397         {
  398           case HDLC_FRM_FLAG_SEQUENCE:
  399             goto _SetRXstatePacket;
  400 
  401           case HDLC_FRM_CONTROL_ESCAPE:
  402 _SetRXstateWaitHead:
  403             state = RXstateWaitHead;
  404             break;
  405 
  406           default:
  407             // RXstatePacket
  408             state--;
  409             oneByte ^= HDLC_FRM_ESCAPE_BIT;
  410             goto _StoreByte;
  411         }
  412         break;
  413       }
  414     }
  415   }
  416 }
  417 //############################################################################################