TD1208 – How to implement RF LAN

2 TD1208 configured as RF Lan
2 TD1208 configured as RF Lan

TD1208, SigFox chips, include a RF LAN fonction, this allow you to communicate between different TD1208 chip, locally, without using the SigFox network. This is really interesting if you have a network of sensor, you can connect them this way to a gateway. This gateway will then transmit the information to the SigFox network to reach your IoT service.

This post will describe how the rf_lan work and how to implement it.

I’m not fully sure I implement it the right way as the given example are really poor, the documentation is a mess and the source code of this part is not accessible… More over, my feeling is that this part of the SDK is still running bugs. What I mean is that in a future post you should find an update and a better way to do it than the one I’m going to describe. By-the-way, what is here is working, have fun with it !

Let’s start with some background elements you have to know

The TD1208 is able to transmit and receive short frame over a 868Mhz to 869.7Mhz channel for a short range. The transmission power can be controlled from -35dB to 14dB .

Each of the frame are following this structure :

/** LAN RF frame structure
 *
 * @details
 *   The TD_LAN frame consists in a a header and a user payload field:
 *
 *   | Header  | Payload  |
 *   | :-----: | :------: |
 *   | 4 bytes | 17 bytes |
 *
 *   The header field is further made up of several fields:
 *
 *   | Addr Low | Addr Med | Addr High | Ack   | Frame Counter |
 *   | :------: | :------: | :-------: | :---: | :-----------: |
 *   | 8 bits   | 8 bits   | 8 bits    | 1 bit | 7 bits        |
 *
 *   The 24-bit address field contains the sender source address for a message
 *   or the destination address for an answer.
 *
 *   The Ack flag is set by the receiver to acknowledge a received message.
 *
 *   The frame counter is a monotonic downwards counter that is decremented for
 *   each retransmission of a message frame during a single retry. This counter
 *   is used by network devices to compute the delay until the end of the
 *   current message.
 */

There is a 24b address corresponding to the TD1208 address. You are free to choose your own address, no specific recommendation is given. By-the-way, there is a function to create an address from the sigfox ID : TD_LAN_ComputeAddress(sigFoxId). This is a kind of magic as there not a large link between initial ID and calculated address. The main point si to ensure the address is unique.

Each frame sent over the air will be acknowledge by the receiver or resent. The ACK bit and the following Frame Counter play this role in the communication.

Then your message will be up to 17 bytes ; feel free to include a crc or checksum in this payload.

Identify emitter and receiver

When you set an address for a device, you also set a mask. The device will then only receive frame matching the address & mask set. It means that if you set address 0xAABBCC and mask 0xFFFF00, the device will send message with address 0xAABBCC and will receive only frame with, in the header address 0xAABB??

For a point to point communication between a device and a gateway, you will configure device with address calculated from SigFox Id and mask 0xFFFFFF. It means the device will only receive frame containing its own address. The gateway will be configured with Id 0x000000 and mask 0x000000 to receive everything (kind of promiscuous mode). In the given example you will see a notion of network, this is an arbitrary part of the address used as a common value to be shared between the devices. It could be a good way to broadcast response to a group of devices or to communicate between devices using the network Id. This mainly make sense when you have different group of sensor in the same area but not working together.

There is no notion of source and destination, we can consider than as the system is mainly a communication from many to one, the gateway address do not have to be show. What you will have to manage is the coexistence of different group of devices sharing the same frequency. A pairing system could be imagine, we will see this later.

These mechanism to filter frame in reception is basic but efficient. As these is no defined protocol you will define your own. TD_SENSOR is providing a couple of functionalities on top of it but personally I had a preference for rewriting my own.

Transmit a Frame

Transmitting a frame is a simple exercise : you put your own address on a header, add your data and send it over the air. The system will retry if no ack has been received in the given period.

#define FREQUENCY        869000000  /**< Channel frequency - 868000000 <= f <= 869700000 */
#define POWER_LEVEL      14         /**< Transmit power level  -35 <= p <= 14 */
#define RETRIES          2          /**< Number of transmit retries */

uint32_t sigFoxId = 0;
if (TD_FLASH_DeviceRead(&device)) {
   sigFoxId = device.Serial;
   if (TD_LAN_Init(true, TD_LAN_ComputeAddress(sigFoxId), 0x00FFFFFF) == true) {
      // Set the operating frequency and power level
      if (TD_LAN_SetFrequencyLevel(FREQUENCY, POWER_LEVEL) == true) {
                tfp_printf("RF LAN Init OK !");
      }
  }
}
// Now we are ready to transmit a Frame
TD_LAN_frame_t RX, TX;
TX.header = 0;
SET_ADDRESS(TX.header, TD_LAN_ComputeAddress(sigFoxId));
int i;
for (i = 0; i < 8; i++) 
    TX.payload[i] = i;
       
if (!TD_LAN_SendReceive(-1, 1, &TX, &RX)) {
   tfp_printf("NACK\r\n");
} else {
   tfp_printf("ACK\r\n");
   tfp_dump("RX ACK=", (uint8_t *) &RX, TD_LAN_FRAME_SIZE);
}

This is typically what you have in the lan_tx example code. TD_LAN_Init is set with the address of the device and a 24bits mask. The device will only receive frame matching exactly its own address. Frequency is set to 869Mhz and power is set to the maximum.

A frame to transfer is built by putting in the header the id of the device itself (the source of the message) and the data to transfer (payload). The TD_LAN_SendReceive is call with the Frame to transfer. This function is emitting the data and then waiting for the acknowledgment. The function also manage retry (second parameter).  It returns true when a ack has been received. Each try have a duration about 5 seconds so to cover a 1 second reception cycle loop a retry of 3 seems to be enough.

Receiving frame

The receiving part is more complex. If you take a look to the example, there is no difficulty but the reception is synchronous (blocking). Personally I do not want my system block until reception of a message, I’m expecting it to proceed something else rest of the time. So I decide to implement a non blocking solution. The documentation is showing a non blocking function, that sounds good…

First we have to initialize the receiver (the gateway):

#define FREQUENCY        869000000  /**< Channel frequency - 868000000 <= f <= 869700000 */
#define POWER_LEVEL      14         /**< Transmit power level  -35 <= p <= 14 */
#define RETRIES          2          /**< Number of transmit retries */

uint32_t sigFoxId = 0;
if (TD_FLASH_DeviceRead(&device)) {
   sigFoxId = device.Serial;
   if (TD_LAN_Init(true, 0x000000, 0x000000) == true) {
      // Set the operating frequency and power level
      if (TD_LAN_SetFrequencyLevel(FREQUENCY, POWER_LEVEL) == true) {
                tfp_printf("RF LAN Init OK !");
      }
  }
}

By setting address and mask to 0x0 any frame will be received by the device. Now, in TD_USER_LOOP, we need to wait for a frame. I was expected the asynchronous mode to work easily but, as much as I know it looks like differently. I do not know if I’m an idiot or if the documentation is wrong or if I face a bug but … when TD_LAN_ReceiveFrame should return false when no frame is received it always return true in non blocking mode or always return false when in a timeout synchronous mode where the timeout is not respected…

After a large number of different tries, I got a solution by using as parameters TD_LAN_ReceiveFrame(0,20000,&RX). That way the system is not blocking and you get a true as return when a frame is received. In fact it seems the second parameter is blocking for a couple of ms but 200K do not change this time… and 2000 is not enought… need clarification. Regarding the measure I did (thanks to RTC->CNT value) the blocking time is about 20ms, this do not change if the parameter is set to a higher number.

By-the-way ! it works.

Now once the frame is received we have to send back the acknowledgment. This have to be done shortly otherwise the timing is not respected on the sender side. You have, according to the documentation 80ms to ack. You have to be careful with printf trace in the code that create delay and will miss the timing.

Here is an example of reception and ack running not blocking.

TD_LAN_frame_t RX;
RX.header = 0;
int i;
for ( i=0 ; i < 17 ; i++) RX.payload[i] = 0;
if (TD_LAN_ReceiveFrame(0,20000,&RX)) {
   // Reply frame to sender with acknowledge flag set
   SET_ACK(RX.header, TD_LAN_ACK);
   TD_LAN_SendFrame(RETRIES, &RX, &RX);

   tfp_dump("FRAME=", (uint8_t *) &RX, TD_LAN_FRAME_SIZE);
}

Managing reception in a sched

Now the objective is to manage a reception sched working a couple of ms every second to capture a message sent by a device. I’ll also use a callback to proceed the received message instead of managing it in the main:

#define FREQUENCY        869000000     /**< Channel frequency - 868000000 <= f <= 869700000 */
#define POWER_LEVEL      14            /**< Transmit power level  -35 <= p <= 14 */
#define RETRIES          2             /**< Number of transmit retries */
#define BROADCAST_MASK   0x00000000
/* ----------------------------------------------------------------------------
 * LAN RX callback function
 * int (*TD_LAN_callback_t)(TD_LAN_frame_t *tx_frame, TD_LAN_frame_t *rx_frame);
 */
int lanRxFrameCallback(TD_LAN_frame_t *tx, TD_LAN_frame_t *rx) {
    // Reply frame to sender with acknowledge flag set
    SET_ACK(rx->header, TD_LAN_ACK);
    TD_LAN_SendFrame(RETRIES, rx, rx);
    return 1;
}

******************************************************************************
 * @brief
 *   User setup function.
 ******************************************************************************/
void TD_USER_Setup(void)
{
    // Initialize the LEUART
    init_printf(TD_UART_Init(9600, true, false),
                TD_UART_Putc,
                TD_UART_Start,
                TD_UART_Stop);
    resetStringPtr = resetString;

    sigFoxId = 0;
    TD_DEVICE device;
    if (TD_FLASH_DeviceRead(&device)) {
        sigFoxId = device.Serial;
    }
    if ( sigFoxId > 0) {
        // Main station is receiving everything ... MASK is 0
        if (TD_LAN_Init(true, BROADCAST_MASK, BROADCAST_MASK)) {
            if (TD_LAN_SetFrequencyLevel(FREQUENCY, POWER_LEVEL) ) {
                // Initialise un callback sur reception de messages
                TD_LAN_SetUserCallback( lanRxFrameCallback );
            }
        }
    }
    // Lance un sched sur la base de temps LOOP_SLEEP_SEC
    TD_SCHEDULER_Append(LOOP_SLEEP_SEC, 0, 0, TD_SCHEDULER_INFINITE, mainLoop, 0);
}
static void mainLoop(uint32_t arg, uint8_t repetition) {
    // Manage LAN Reception - about 20ms when nothing is received
    TD_LAN_frame_t RX;
    TD_LAN_ReceiveFrame(0,20000,&RX);

    // then do what you have to do ...
}

/*******************************************************************************
 * @brief
 *   User loop function. - 32 KHz loop
 ******************************************************************************/
void TD_USER_Loop(void)
{
    int c;
    // This is to ensure you will be able to reflash your device easily
    while ((c = TD_UART_GetChar()) >= 0) {
        if ( (char)c == *resetStringPtr ) resetStringPtr++;
        else resetStringPtr = resetString;
        if ( *resetStringPtr == 0 ) {
           NVIC_SystemReset();
        }
    }
}


Pairing of device

The next question is to find a way to verify that the data you received has been sent by a known device, not the one of one of your neighborhood.

Post to be continued in the next days … come back !

 

 

 

 

 

 

This entry was posted in Hardware, Sigfox and tagged , , . Bookmark the permalink.

5 Responses to TD1208 – How to implement RF LAN

  1. Manu PILLANT says:

    Recherche google “sigfox sender”…1er résultat m’indique http://www.disk91.com ça me parle…c’est quoi ce truc déjà ?
    Ah mais oui ! C’est Paul ! Avec un article bien complet sur la mise en oeuvre du machin… pas de doute c’est bien lui 🙂
    Je vois que tu continues à t’intéresser à la domotique. C’est cool.

    As-tu fais des tests de portée, et/ou robustesse aux obstacles ?

    A+

    • Paul says:

      salut manu, oui je fais pas mal de tests là dessus, d’ailleurs si tu checks http://www.myteepi.fr tu verras qu’il y a un autre nom qui devrait te parler …
      Si tu as envie d’echanger sur sigfox, n’hesite pas à m’appeler (pm mois sur facebook pour avoir mon tel si tu ne l’as plus)
      Paul

  2. George says:

    Hi Paul,
    Really excellent work here with the TD1208s. You’re the only person I know working with these things in an experimental/ DIY way. Anyway, I was wondering, is the communication protocol used for the LAN sanctioned in anyway by SIGFOX? Would it be possible to send messages from a TD 1208 to a Telit LE51-868s module? or is it only for TD’s modules?

    • Paul says:

      Can’t answer you question, my point of view is that LAN does not use the same modulation as sigfox, but I found no documentation on it. I assume the LAN protocol is some kind of standard and could be received by different chips. But concider that basically the LAN mode comes with a protocol soft stack and you would have to developp the same stack on the other device.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.