1 // *********************************************************************
    2 //   Custom DPA Handler code template                                  *
    3 // *********************************************************************
    4 // Copyright (c) IQRF Tech s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-SensorBeaming-Temperature.c,v $
    7 // Version: $Revision: 1.4 $
    8 // Date:    $Date: 2021/08/18 20:43:06 $
    9 //
   10 // Revision history:
   11 //   2021/08/18  Release for DPA 4.16
   12 //
   13 // *********************************************************************
   14 
   15 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   16 
   17 // Default IQRF include (modify the path according to your setup)
   18 #include "IQRF.h"
   19 
   20 // Default DPA header (modify the path according to your setup)
   21 #include "DPA.h"
   22 // Default Custom DPA Handler header (modify the path according to your setup)
   23 #include "DPAcustomHandler.h"
   24 // IQRF standards header (modify the path according to your setup)
   25 #include "standard/IQRFstandard.h"
   26 #include "standard/IQRF_HWPID.h"
   27 // Uncomment the following includes if the respective component is needed
   28 //#include "NFC.c"
   29 
   30 //############################################################################################
   31 
   32 // Define to pulse LEDG on every Idle event and LEDR after beaming was transmitted. Also regular beaming is every 5 s instead of 60 s
   33 #define DEBUGBeaming
   34 
   35 #define _HWPID_     0x5E7F
   36 #define _HWPIDver_  0x0100 // 1.00
   37 
   38 // Random value. We cannot use DPA random value as it is generated only when event happens
   39 uns8 rand;
   40 // Variable to store sensor value at Get?_????() methods.
   41 uns16 sensorValue;
   42 
   43 void Beaming();
   44 
   45 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   46 //############################################################################################
   47 // https://doc.iqrf.org/DpaTechGuide/pages/custom-dpa-handler.html
   48 bit CustomDpaHandler()
   49 //############################################################################################
   50 {
   51   // Handler presence mark
   52   clrwdt();
   53 
   54   // Detect DPA event to handle (unused event handlers can be commented out or even deleted)
   55   switch ( GetDpaEvent() )
   56   {
   57     // -------------------------------------------------
   58     case DpaEvent_Interrupt:
   59       // Do an extra quick background interrupt work
   60       // ! The time spent handling this event is critical.If there is no interrupt to handle return immediately otherwise keep the code as fast as possible.
   61       // ! Make sure the event is the 1st case in the main switch statement at the handler routine.This ensures that the event is handled as the 1st one.
   62       // ! It is desirable that this event is handled with immediate return even if it is not used by the custom handler because the Interrupt event is raised on every MCU interrupt and the “empty” return handler ensures the shortest possible interrupt routine response time.
   63       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
   64       // ! Make sure race condition does not occur when accessing those variables at other places.
   65       // ! Make sure( inspect.lst file generated by C compiler ) compiler does not create any hidden temporary local variable( occurs when using division, multiplication or bit shifts ) at the event handler code.The name of such variable is usually Cnumbercnt.
   66       // ! Do not call any OS functions except setINDFx().
   67       // ! Do not use any OS variables especially for writing access.
   68       // ! All above rules apply also to any other function being called from the event handler code, although calling any function from Interrupt event is not recommended because of additional MCU stack usage.
   69       // https://doc.iqrf.org/DpaTechGuide/pages/EventInterrupt.html
   70       return Carry;
   71 
   72       // -------------------------------------------------
   73     case DpaEvent_Idle:
   74       // Do a quick background work when RF packet is not received
   75       // https://doc.iqrf.org/DpaTechGuide/pages/idle.html
   76 
   77 #ifdef DEBUGBeaming
   78       // On-line mode indication
   79       setLEDG();
   80 #endif
   81       if ( buttonPressed )
   82         // Go to Beaming mode
   83         Beaming();
   84       break;
   85 
   86       // -------------------------------------------------
   87     case DpaEvent_Reset:
   88       // Called after module is reset
   89       // https://doc.iqrf.org/DpaTechGuide/pages/ResetEvent.html
   90       // Initialize nonzero random value
   91       rand = Random.low8 | 1;
   92 
   93       //goto DpaHandleReturnTRUE; // return TRUE only if you handle node bonding/unbonding
   94       break;
   95 
   96       // -------------------------------------------------
   97     case DpaEvent_BondingButton:
   98       // Called to allow a bonding button customization
   99       // https://doc.iqrf.org/DpaTechGuide/pages/bondingbutton.html
  100       //goto DpaHandleReturnTRUE; // return TRUE to handle bonding button
  101       break;
  102 
  103       // -------------------------------------------------
  104     case DpaEvent_Indicate:
  105       // Called to allow a customization of the device indication
  106       // https://doc.iqrf.org/DpaTechGuide/pages/IndicateEvent.html
  107       //goto DpaHandleReturnTRUE; // return TRUE to skip default indication
  108       break;
  109 
  110       // -------------------------------------------------
  111     case DpaEvent_Init:
  112       // Do a one time initialization before main loop starts
  113       // https://doc.iqrf.org/DpaTechGuide/pages/init.html
  114       break;
  115 
  116       // -------------------------------------------------
  117     case DpaEvent_ReceiveDpaRequest:
  118       // Called after DPA request was received
  119       // https://doc.iqrf.org/DpaTechGuide/pages/receivedparequest.html
  120 
  121       //goto DpaHandleReturnTRUE; // return TRUE to skip default processing
  122       break;
  123 
  124       // -------------------------------------------------
  125     case DpaEvent_BeforeSendingDpaResponse:
  126       // Called before sending DPA response back to originator of DPA response
  127       // https://doc.iqrf.org/DpaTechGuide/pages/beforesendingdparesponse.html
  128       break;
  129 
  130       // -------------------------------------------------
  131     case DpaEvent_Notification:
  132       // Called after DPA request was processed and after DPA response was sent
  133       // https://doc.iqrf.org/DpaTechGuide/pages/notification.html
  134       break;
  135 
  136       // -------------------------------------------------
  137     case DpaEvent_AfterRouting:
  138       // Called after Notification and after routing of the DPA response was finished
  139       // https://doc.iqrf.org/DpaTechGuide/pages/afterrouting.html
  140       break;
  141 
  142       // -------------------------------------------------
  143     case DpaEvent_FrcValue:
  144       // Called to get FRC value
  145       // https://doc.iqrf.org/DpaTechGuide/pages/frcvalue.html
  146       break;
  147 
  148       // -------------------------------------------------
  149     case DpaEvent_FrcResponseTime:
  150       // Called to get FRC response time
  151       // https://doc.iqrf.org/DpaTechGuide/pages/frcresponsetime.html
  152       break;
  153 
  154       // -------------------------------------------------
  155     case DpaEvent_BeforeSleep:
  156       // Called before going to sleep
  157       // https://doc.iqrf.org/DpaTechGuide/pages/beforesleep.html
  158       break;
  159 
  160       // -------------------------------------------------
  161     case DpaEvent_AfterSleep:
  162       // Called after woken up after sleep
  163       // https://doc.iqrf.org/DpaTechGuide/pages/aftersleep.html
  164       break;
  165 
  166       // -------------------------------------------------
  167     case DpaEvent_DisableInterrupts:
  168       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond and run RFPGM)
  169       // https://doc.iqrf.org/DpaTechGuide/pages/eventDisableInterrupts.html
  170       break;
  171 
  172       // -------------------------------------------------
  173     case DpaEvent_PeerToPeer:
  174       // Called when peer-to-peer (non-networking) packet is received
  175       // https://doc.iqrf.org/DpaTechGuide/pages/peertopeer.html
  176       break;
  177 
  178       // -------------------------------------------------
  179     case DpaEvent_UserDpaValue:
  180       // Called when DPA is required to return User defined DPA value in the response
  181       // https://doc.iqrf.org/DpaTechGuide/pages/userdpavalue.html
  182       break;
  183 
  184       // -------------------------------------------------
  185     case DpaEvent_VerifyLocalFrc:
  186       // Called to verify local FRC command
  187       // https://doc.iqrf.org/DpaTechGuide/pages/verifylocalfrc.html
  188 
  189       //goto DpaHandleReturnTRUE; // return TRUE allow FRC command
  190       break;
  191 
  192       // -------------------------------------------------
  193     case DpaEvent_DpaRequest:
  194       // Called to interpret DPA request for peripherals
  195       // https://doc.iqrf.org/DpaTechGuide/pages/EventDpaRequest.html
  196       IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequest()
  197       {
  198         // -------------------------------------------------
  199         // Peripheral enumeration
  200         // https://doc.iqrf.org/DpaTechGuide/pages/enumerate-peripherals.html
  201 
  202         // We implement 1 standard peripheral
  203         _DpaMessage.EnumPeripheralsAnswer.UserPerNr |= 1;
  204         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_SENSORS );
  205         _DpaMessage.EnumPeripheralsAnswer.HWPID |= _HWPID_;
  206         _DpaMessage.EnumPeripheralsAnswer.HWPIDver |= _HWPIDver_;
  207 
  208 DpaHandleReturnTRUE:
  209         return TRUE;
  210       }
  211       else
  212       {
  213         // -------------------------------------------------
  214         // Get information about peripheral
  215         // https://doc.iqrf.org/DpaTechGuide/pages/get-peripheral-info.html
  216 
  217         if ( _PNUM == PNUM_STD_SENSORS )
  218         {
  219           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_STD_SENSORS;
  220           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ;
  221           // Set standard version
  222           _DpaMessage.PeripheralInfoAnswer.Par1 = STD_SENSORS_VERSION;
  223           goto DpaHandleReturnTRUE;
  224         }
  225 
  226         break;
  227       }
  228 
  229       // -------------------------------------------------
  230       // Handle peripheral command
  231       // https://doc.iqrf.org/DpaTechGuide/pages/handle-peripheral-request.html
  232 
  233       // Supported peripheral number?
  234       if ( _PNUM == PNUM_STD_SENSORS )
  235       {
  236         // Supported commands?
  237         switch ( _PCMD )
  238         {
  239           // Invalid command
  240           default:
  241             // Return error
  242             W = ERROR_PCMD;
  243 _ERROR_W:
  244             DpaApiReturnPeripheralError( W );
  245             break;
  246 
  247             // Sensor enumeration
  248           case PCMD_STD_ENUMERATE:
  249             if ( _DpaDataLength != 0 )
  250             {
  251               W = ERROR_DATA_LEN;
  252               goto _ERROR_W;
  253             }
  254 
  255             // Then just enumerate their types
  256             // -------------------------------------------------
  257             _DpaMessage.Response.PData[0] = STD_SENSOR_TYPE_EXTRA_LOW_VOLTAGE;
  258             _DpaMessage.Response.PData[1] = STD_SENSOR_TYPE_TEMPERATURE;
  259             _DpaDataLength = 2;
  260             // -------------------------------------------------
  261             goto DpaHandleReturnTRUE;
  262         }
  263       }
  264 
  265       break;
  266   }
  267 
  268 DpaHandleReturnFALSE:
  269   return FALSE;
  270 }
  271 
  272 //############################################################################################
  273 void DpaSleep()
  274 //############################################################################################
  275 {
  276   // Finalize OS Sleep DPA Request
  277   _DpaDataLength = sizeof( _DpaMessage.PerOSSleep_Request );
  278   _PNUM = PNUM_OS;
  279   _PCMD = CMD_OS_SLEEP;
  280   // Execute sleep
  281   DpaApiLocalRequest();
  282 }
  283 
  284 //############################################################################################
  285 void RandomWait()
  286 //############################################################################################
  287 {
  288 #pragma updateBank default = UserBank_01
  289 
  290   // Perform local DPA Request to go to sleep
  291 
  292   // Time 32.768 ms * ( 30 + rand(0...31) ) i.e. 983 - 1999 ms
  293   rand >>= 1;
  294 #pragma updateBank 0 /* OFF */  // Optimization (avoid duplicate bank setting for RandomValue access)
  295   W = 0b10111000;   // x^8 + x^6 + x^5 + x^4 + 1
  296   if ( Carry )
  297     rand ^= W;
  298 #pragma updateBank 1 /* ON */
  299 
  300   // Prepare sleep parameters
  301   _DpaMessage.PerOSSleep_Request.Time.low8 = ( rand & 0x1F ) + 30;
  302   _DpaMessage.PerOSSleep_Request.Time.high8 = 0;
  303   // 32.768 ms unit
  304   _DpaMessage.PerOSSleep_Request.Control = 0b0001.0000;
  305   // Execute sleep
  306   DpaSleep();
  307 }
  308 
  309 //############################################################################################
  310 void Get_Temperature()
  311 //############################################################################################
  312 {
  313   // https://doc.iqrf.org/IQRF-Standards/StandardSensor/pages/0x01-temperature.html
  314 
  315   // Error reading temperature? Note: param3 holds finer temperature value after calling getTemperature()
  316   if ( getTemperature() == -128 )
  317     // Return standard error value
  318     param3 = FRC_STD_FRC_ERROR_VALUE;
  319   else
  320   {
  321     // Extent minus sign bit #11 to the bits #12-15
  322     if ( param3.11 )
  323       param3 |= 0xF000;
  324     // Make a FRC value from the raw value
  325     sensorValue = param3 ^ 0x8000;
  326   }
  327 }
  328 
  329 //############################################################################################
  330 void Get_ExtraLowVoltage()
  331 //############################################################################################
  332 {
  333   // https://doc.iqrf.org/IQRF-Standards/StandardSensor/pages/0x04-extra-low-voltage.html
  334 
  335   // Voltage [V] = 261.12 / (127 - getSupplyVoltage)
  336   uns8 div = 127 - getSupplyVoltage();
  337   // For expected voltage ~3.0V, the divider is 127-40=87, so we will add 1/2 of it for more precise rounding
  338   sensorValue = (uns24)( 261.120 * 1000 + 87.0 / 2 ) / div;
  339   // Make a FRC value from the raw value
  340   sensorValue ^= 0x8000;
  341 }
  342 
  343 //############################################################################################
  344 void ReleaseButton()
  345 //############################################################################################
  346 {
  347   do
  348   {
  349     clrwdt();
  350   } while ( buttonPressed );
  351 }
  352 
  353 //############################################################################################
  354 void Beaming()
  355 //############################################################################################
  356 {
  357   setLEDR();
  358   // Wait for button release
  359   ReleaseButton();
  360   stopLEDR();
  361 
  362   // Read 1st voltage
  363   Get_ExtraLowVoltage();
  364 
  365   for ( ;; )
  366   {
  367     // Do beaming
  368     clrwdt();
  369     // Required time for the temperature sensor after wake-up from sleep
  370     waitDelay( 31 ); // Note: in real application sleep instead of active waiting to lower the consumption
  371     // Prepare beaming data
  372     // Voltage was read 1st time when the device starts beaming and then after every TX in the beaming when the power consumption was the highest
  373     _DpaMessage.Response.PData[0] = STD_SENSOR_TYPE_EXTRA_LOW_VOLTAGE;
  374     _DpaMessage.Response.PData[1] = sensorValue.low8;
  375     _DpaMessage.Response.PData[2] = sensorValue.high8;
  376 
  377     Get_Temperature();
  378     _DpaMessage.Response.PData[3] = STD_SENSOR_TYPE_TEMPERATURE;
  379     _DpaMessage.Response.PData[4] = sensorValue.low8;
  380     _DpaMessage.Response.PData[5] = sensorValue.high8;
  381     _DpaDataLength = 6;
  382 
  383     // Do simple LBT
  384     uns8 loop = 3;
  385     do
  386     {
  387       if ( !checkRF( DpaApiReadConfigByte( CFGIND_RXFILTER ) + 10 ) )
  388         break;
  389 
  390       RandomWait();
  391     } while ( --loop != 0 );
  392 
  393     // STD TX
  394     // !!! Note: we expect that the aggregating repeaters are STD !!!
  395     setRFmode( _TX_STD );
  396 
  397     // Force not routed packet to be sent from N by DPA API
  398     NonroutedRfTxDpaPacket = TRUE;
  399 
  400     // Prepare off-line sensor beaming packet
  401 
  402     // HW profile ID
  403     _HWPID = _HWPID_;
  404 
  405     // No DPA Params used
  406     _DpaParams = 0;
  407 
  408     // Beaming is broadcast
  409     _NADR = BROADCAST_ADDRESS;
  410     _NADRhigh = 0;
  411 
  412     // Prepare sensor packet type and quantity data
  413     _PNUM = PNUM_STD_SENSORS;
  414     _PCMD = PCMD_STD_SENSORS_READ_TYPES_AND_FRC_VALUES | RESPONSE_FLAG;
  415 
  416     // TX DPA message with zero DPA Value and asynchronous
  417     // Note: Use DpaValue = 0x01 to indicate asynchronous i.e. non-regular beaming
  418     DpaApiRfTxDpaPacket( 0 /*DpaValue*/, 0 );
  419 
  420     // Measure voltage just after TX
  421     Get_ExtraLowVoltage();
  422 
  423     // Do a sleep between two beamings
  424     // Prepare sleep parameters, 2.097 s unit
  425 #ifdef DEBUGBeaming
  426     pulseLEDR();
  427     _DpaMessage.PerOSSleep_Request.Time = (uns16)( 5 / 2.097 ); // Beaming every ~5 sec
  428 #else
  429     _DpaMessage.PerOSSleep_Request.Time = (uns16)( 60 / 2.097 ); // Beaming every ~60 sec
  430 #endif
  431     // Wakeup on negative edge (button press)
  432     _DpaMessage.PerOSSleep_Request.Control = 0b0000.0001;
  433     // Execute sleep
  434     DpaSleep();
  435 
  436     // Go to On-line mode when button is pressed?
  437     if ( buttonPressed )
  438     {
  439       // Wait for button release
  440       setLEDG();
  441       ReleaseButton();
  442       stopLEDG();
  443       // Break the beaming loop
  444       break;
  445     }
  446   }
  447 
  448   // Restore RF settings
  449   DpaApiSetRfDefaults();
  450 }
  451 
  452 //############################################################################################
  453 // Uncomment the following includes if the respective component is needed
  454 //#include "NFC.c"
  455 
  456 // Default Custom DPA Handler header; 2nd include to implement Code bumper to detect too long code of the Custom DPA Handler (modify the path according to your setup)
  457 #include "DPAcustomHandler.h"
  458 //############################################################################################