Point Coordination Function TDMA

PCF TDMA is a simple protocol used to collect data in a contention free manner from neighbors within a 1-hop star topology. A master node sends out a broadcast message that is used to synchronize all of the client nodes. Each node then replies using a dedicated time slot in collision-free manner. Time slots are either pre-defined, or requested at runtime from the master node. The size of the TDMA slots and cycles can be changed dynamically at the start of each cycle by the master making this protocol extremely flexible for applications that require shifting on-demand from high data-rate to low-power operation. Client nodes remain in power-down mode during non-assigned slots making the protocol extremely energy efficient.


Running PCF Demo

There are four main components required to collect data using PCF shown in the block diagram below. The pcf-host and and ff-clients are firmware components that run on each FireFly node. The SLIPstream-server and SLIPstream-clients are applications that run on a host PC (windows or linux, no OS X yet). The SLIPstream-server acts as a USB-serial to UDP forwarding mechanism. The SLIPstream clients either decode messages from the network or package messages that go into the network (for example to actuate devices). There are a variety of ff-clients and SLIPstream-clients depending on which particular pieces of hardware are connected to the system (environmental sensors, power meters, etc).

Step 0: Set fuses for any node that is brand new
  •  cd nano-RK/tools/fuse-conf/firefly3_x
      ./rfa1-fuses-battery-no-bod
       
Step 1: Flash FireFly gateway
  • Flash the gateway node that will be connected to the gateway computer
    cd nano-RK/projects/demos/pcf-demo/pcf-host
    make clean
    make program
    
  • Set the EEPROM like any other FireFly client except with the last two hex digits being '00'
    cd nano-RK/tools/EEPROM_mac_set
    ./config-eeprom /dev/ttyUSB1 00000000 26 -e 00112233445566778899AABBCCDDEEFF
    
    • Alternatively, these values can be hardcoded at the top of the project file's main.c
Step 2: Flash FireFly client nodes
  • First Program the nodes (this example shows the environmental sensor board)
        cd nano-RK/projects/demos/pcf-demo/ff-clients/ff3-sensors
        make clean
        make program
    
  • You will see a red blinking led and the following messages from the serial port:
    * ERROR reading MAC address from EEPROM run eeprom-config utility
    * ERROR reading MAC address from EEPROM run eeprom-config utility
    * ERROR reading MAC address from EEPROM run eeprom-config utility
    
  • Next, set the EEPROM values
    • MAC Address
    • Subnet MAC
    • Encryption Key (optional)
    • Wireless Update image file (optional)
  • Note the following with respect to the MAC address:
    • The first six hex digits in the MAC address denote the subnet
    • All nodes must be part of the same subnet to communicate
    • The gateway node must be set to 0 inside a subnet
    • No other node besides the gateway can be 0
  • Use the config-eeprom tool to set the EEPROM
    cd nano-RK/tools/EEPROM_mac_set
    make (if not already built)
    ./config-eeprom /dev/ttyUSB1 00000001 26 -e 00112233445566778899AABBCCDDEEFF
    
    • Alternatively, these values can be hardcoded at the top of the project file's main.c
Step 3: Run the SLIPstream Server
  • The SLIPstream server forwards messages from the gateway_client to and from the gateway firefly node
  • Connect the gateway node and run the server as follows
    cd /nano-RK/tools/SLIPstream/SLIPstream-server
    make
    ./SLIPstream /dev/ttyUSB0 5000 
    
  • Reset the gateway node and you should see something similar to the following on the SLIPstream server
    Netmask = 0x0 0 0
    MAC = 0x0
    Channel = 26
    slip_init()
    bmac_started()
    Waiting for SLIP data...
    
    • In the above configuration, the node was set to read from the serial port at /dev/ttyUSB0 and forward data to an incoming slip client listening on UDP port 5000
  • Leave this terminal open in its own window so that you can see incoming messages in the next step.
Step 4: Run a slip-client
  • There are a few slip-clients that can be used for various purposes. These are all located in nano-rk/projects/demos/pcf-demo/slip-clients
  • Initially, its best to run the nano-rk/projects/demos/pcf-demo/slip-clients/read-data client
    • First, open a new terminal window so that you can leave the SLIPstream-server from step 3 running.
    • Next, compile and run the read-data slip-client
      cd /nano-RK/projects/demos/pcf-demo/slip-clients/read-data
      make clean
      make
      ./pcf-slip-client localhost 5000 -v
      
  • This will connect to the SLIPstream server that was started in step 3 and now print data received by the nodes.

Application Programming Interface

void tdma_task_config ()

int8_t tdma_init(uint8_t tdma_mode, uint8_t chan, uint16_t my_mac)

int8_t tdma_started()

tdma_info struct

This is the structured passed to and from the tdma_send and tdma_recv communication functions.

typedef struct {
        uint16_t slot;
        uint16_t cycle_size;
        uint8_t slot_len_ms;
        uint16_t dst;
        uint16_t src;
        uint16_t seq_num;
        int16_t rssi;
} tdma_info;

int8_t tdma_send(tdma_info *fd, uint8_t *buf, uint8_t len, uint8_t flags)

Options for flags include TDMA_BLOCKING and TDMA_NONBLOCKING.

int8_t tdma_recv(tdma_info *fd, uint8_t *buf, uint8_t *len, uint8_t flags)

Options for flags include TDMA_BLOCKING and TDMA_NONBLOCKING.

int8_t tdma_tx_slot_add(uint16_t slot)

int8_t tdma_set_slot_len_ms(uint16_t len)

int8_t tdma_set_slots_per_cycle(uint16_t slots_per_cycle)

nrk_sig_t tdma_get_tx_done_signal()

nrk_sig_t tdma_get_rx_pkt_signal()


Host Example

The following examples are snapshots from the simple_msg project. If you wish to run the projects, it is easier to refer to the original source with the associated makefiles etc. T

  1void tx_task ()
  2{
  3  int8_t v;
  4  uint8_t len, cnt;
  5
  6  printf ("Gateway Tx Task PID=%u\r\n", nrk_get_pid ());
  7
  8  while (!tdma_started ())
  9    nrk_wait_until_next_period ();
 10
 11  cnt = 0;
 12
 13  while (1) {
 14
 15    sprintf (tx_buf, "Host data counter %d\n", cnt);
 16    cnt++;
 17    len = strlen (tx_buf) + 1;
 18
 19    // Only transmit data if you want to do so
 20    // Messages from the host are always broadcasts
 21    v = tdma_send (&tx_tdma_fd, &tx_buf, len, TDMA_BLOCKING);
 22    if (v == NRK_OK) {
 23      nrk_kprintf (PSTR ("Host Packet Sent\n"));
 24    }
 25
 26  }
 27}
 28
 29void rx_task ()
 30{
 31  nrk_time_t t;
 32  uint16_t cnt;
 33  int8_t v;
 34  uint8_t len, i;
 35
 36  cnt = 0;
 37  nrk_kprintf (PSTR ("Nano-RK Version "));
 38  printf ("%d\r\n", NRK_VERSION);
 39
 40  printf ("RX Task PID=%u\r\n", nrk_get_pid ());
 41
 42  tdma_init (TDMA_HOST, 13, 0);
 43
 44// Change these parameters at runtime...
 45  tdma_set_slot_len_ms (10);
 46  tdma_set_slots_per_cycle (10);
 47
 48  while (!tdma_started ())
 49    nrk_wait_until_next_period ();
 50
 51  while (1) {
 52    v = tdma_recv (&rx_tdma_fd, &rx_buf, &len, TDMA_BLOCKING);
 53    if (v == NRK_OK) {
 54      nrk_kprintf (PSTR ("Got pkt "));
 55      printf ("src: %u rssi: %d ", rx_tdma_fd.src, rx_tdma_fd.rssi);
 56      printf ("slot: %u ", rx_tdma_fd.slot);
 57      printf ("len: %u\r\npayload: ", len);
 58      for (i = 0; i < len; i++)
 59        printf ("%c", rx_buf[i]);
 60      printf ("\r\n");
 61    }
 62
 63  }
 64}
 65
 66void nrk_create_taskset ()
 67{
 68  nrk_task_set_entry_function (&rx_task_info, rx_task);
 69  nrk_task_set_stk (&rx_task_info, rx_task_stack, NRK_APP_STACKSIZE);
 70  rx_task_info.prio = 1;
 71  rx_task_info.FirstActivation = TRUE;
 72  rx_task_info.Type = BASIC_TASK;
 73  rx_task_info.SchType = PREEMPTIVE;
 74  rx_task_info.period.secs = 0;
 75  rx_task_info.period.nano_secs = 250 * NANOS_PER_MS;
 76  rx_task_info.cpu_reserve.secs = 0;
 77  rx_task_info.cpu_reserve.nano_secs = 100 * NANOS_PER_MS;
 78  rx_task_info.offset.secs = 0;
 79  rx_task_info.offset.nano_secs = 0;
 80  nrk_activate_task (&rx_task_info);
 81
 82  nrk_task_set_entry_function (&tx_task_info, tx_task);
 83  nrk_task_set_stk (&tx_task_info, tx_task_stack, NRK_APP_STACKSIZE);
 84  tx_task_info.prio = 1;
 85  tx_task_info.FirstActivation = TRUE;
 86  tx_task_info.Type = BASIC_TASK;
 87  tx_task_info.SchType = PREEMPTIVE;
 88  tx_task_info.period.secs = 0;
 89  tx_task_info.period.nano_secs = 250 * NANOS_PER_MS;
 90  tx_task_info.cpu_reserve.secs = 0;
 91  tx_task_info.cpu_reserve.nano_secs = 100 * NANOS_PER_MS;
 92  tx_task_info.offset.secs = 0;
 93  tx_task_info.offset.nano_secs = 0;
 94  nrk_activate_task (&tx_task_info);
 95
 96  tdma_task_config ();
 97
 98}
 99

Client Example

  1void tx_task ()
  2{
  3  int8_t v;
  4  uint8_t len, cnt;
  5
  6  printf ("Tx Task PID=%u\r\n", nrk_get_pid ());
  7
  8  while (!tdma_started ())
  9    nrk_wait_until_next_period ();
 10
 11  cnt = 0;
 12
 13  while (1) {
 14
 15    sprintf (tx_buf, "Node MAC: %u cnt: %d\n", mac_address, cnt);
 16    cnt++;
 17    len = strlen (tx_buf) + 1;
 18
 19    v = tdma_send (&tx_tdma_fd, &tx_buf, len, TDMA_BLOCKING);
 20    if (v == NRK_OK) {
 21      nrk_kprintf (PSTR ("App Packet Sent\n"));
 22    }
 23  }
 24}
 25
 26void rx_task ()
 27{
 28  nrk_time_t t;
 29  uint16_t cnt;
 30  int8_t v;
 31  uint8_t len, i;
 32
 33  cnt = 0;
 34  nrk_kprintf (PSTR ("Nano-RK Version "));
 35  printf ("%d\r\n", NRK_VERSION);
 36
 37  printf ("RX Task PID=%u\r\n", nrk_get_pid ());
 38  t.secs = 5;
 39  t.nano_secs = 0;
 40
 41  mac_address = 1;
 42
 43  tdma_init (TDMA_CLIENT, 13, mac_address);
 44
 45  while (!tdma_started ())
 46    nrk_wait_until_next_period ();
 47
 48  v = tdma_tx_slot_add (mac_address);
 49
 50  if (v != NRK_OK)
 51    nrk_kprintf (PSTR ("Could not add slot!\r\n"));
 52
 53  while (1) {
 54    v = tdma_recv (&rx_tdma_fd, &rx_buf, &len, TDMA_BLOCKING);
 55    if (v == NRK_OK) {
 56      printf ("src: %u\r\nrssi: %d\r\n", rx_tdma_fd.src, rx_tdma_fd.rssi);
 57      printf ("slot: %u\r\n", rx_tdma_fd.slot);
 58      printf ("cycle len: %u\r\n", rx_tdma_fd.cycle_size);
 59      printf ("len: %u\r\npayload: ", len);
 60      for (i = 0; i < len; i++)
 61        printf ("%c", rx_buf[i]);
 62      printf ("\r\n");
 63    }
 64
 65  }
 66}
 67
 68void nrk_create_taskset ()
 69{
 70  nrk_task_set_entry_function (&rx_task_info, rx_task);
 71  nrk_task_set_stk (&rx_task_info, rx_task_stack, NRK_APP_STACKSIZE);
 72  rx_task_info.prio = 1;
 73  rx_task_info.FirstActivation = TRUE;
 74  rx_task_info.Type = BASIC_TASK;
 75  rx_task_info.SchType = PREEMPTIVE;
 76  rx_task_info.period.secs = 0;
 77  rx_task_info.period.nano_secs = 250 * NANOS_PER_MS;
 78  rx_task_info.cpu_reserve.secs = 1;
 79  rx_task_info.cpu_reserve.nano_secs = 50 * NANOS_PER_MS;
 80  rx_task_info.offset.secs = 0;
 81  rx_task_info.offset.nano_secs = 0;
 82  nrk_activate_task (&rx_task_info);
 83
 84  nrk_task_set_entry_function (&tx_task_info, tx_task);
 85  nrk_task_set_stk (&tx_task_info, tx_task_stack, NRK_APP_STACKSIZE);
 86  tx_task_info.prio = 1;
 87  tx_task_info.FirstActivation = TRUE;
 88  tx_task_info.Type = BASIC_TASK;
 89  tx_task_info.SchType = PREEMPTIVE;
 90  tx_task_info.period.secs = 0;
 91  tx_task_info.period.nano_secs = 250 * NANOS_PER_MS;
 92  tx_task_info.cpu_reserve.secs = 1;
 93  tx_task_info.cpu_reserve.nano_secs = 50 * NANOS_PER_MS;
 94  tx_task_info.offset.secs = 0;
 95  tx_task_info.offset.nano_secs = 0;
 96  nrk_activate_task (&tx_task_info);
 97
 98  tdma_task_config ();
 99
100}
101

tdma-pcf.png (49.8 kB) Anthony Rowe, 08/20/2010 10:46 am

block-diagram.png (22.8 kB) Anthony Rowe, 08/15/2012 10:51 pm

pcf-demo.png (41.8 kB) Anthony Rowe, 08/15/2012 11:08 pm