---
# System prepended metadata

title: Compte rendu TP2

---

![](https://i.imgur.com/jrNK2Pn.png =250x65)

# Compte rendu TP2
```
NIANG Elhadji, SAMPIC Tom, MISKU Eigis
```

## Objectives

### Question 1: For each sentence below, indicate if it is true or false.

1. LoRa has a long communication range, so it is likely that end-nodes deployed in the parcels will be able to communicate directly with the sink located in the town.

**True**

2. In the worst case, the end-nodes will communicate with the sink with SF=7.

**False, It will use SF12**

3. LoRa communications require low energy consumption, so the batteries of the end-nodes won’t have to be changed frequently.

**True, this is what LowPowerWAN are about**

## Initial Setup

### Question 2

client.cpp
```cpp!
#include <Arduino.h>

#include <RH_RF95.h>
#include <DHT.h>

#include <RHDatagram.h>

const uint8_t CLIENT_ADDRESS = 4;
const uint8_t SERVER_ADDRESS = 3;

RH_RF95 rf95;
RHDatagram rhd(rf95, CLIENT_ADDRESS);

void setup()
{

  Serial.begin(9600);

  if (!rhd.init())
    Serial.println("Init rhd failed");

  rf95.setFrequency(868);                       // Set the frequency in MHz.
  rf95.setTxPower(0);                           // Set Tx power in dBm
  rf95.setModemConfig(RH_RF95::Bw125Cr45Sf128); // Set the modem configuration.sf7 bw 125 CR 4/5

  Serial.println("Setup finished.");
}

void loop()
{

  uint8_t data[] = "12345678"; // Add here the message to send
  uint8_t len = sizeof(data);

  rhd.sendto(data, len, SERVER_ADDRESS);

  rhd.waitPacketSent();
  Serial.println("Message sent!");
  Serial.println((char *)data);

  delay(2000);
}

```
server.cpp
```cpp!
#include <Arduino.h>
#include <RH_RF95.h>
#include <RHDatagram.h>

const uint8_t CLIENT_ADDRESS = 4;
const uint8_t SERVER_ADDRESS = 3;

RH_RF95 rf95;
RHDatagram rhd(rf95, SERVER_ADDRESS);


void setup()
{
  Serial.begin(9600);
  
  if (!rhd.init())
    Serial.println("Init rhd failed");

  rf95.setFrequency(868); // Set the frequency in MHz.
  rf95.setTxPower(13);    // Set Tx power in dBm

  Serial.println("Setup finished.");
}

void loop()
{
  uint8_t bufferReception[RH_MAX_MESSAGE_LEN];
  uint8_t lenReception = sizeof(bufferReception);
  uint8_t senderAddress;

  if (rhd.waitAvailableTimeout(2000))
  {
    
    if (rhd.recvfrom(bufferReception, &lenReception, &senderAddress))
    {
      uint8_t data[] = "ack elhadji";
      uint8_t len_ack = sizeof(data);

      blinkLedLong();
      
      Serial.print("\n");
      Serial.print("\n");

      Serial.print("Message from the client  ");
      Serial.print(senderAddress);
      Serial.print(": ");
      bufferReception[lenReception]=0;

      Serial.println((char *)bufferReception);

      delay(1000);
      
    }
    else
    {
      Serial.println("Reception fail");
    }
    Serial.print("\n");
    Serial.print("\n");

  }
  else
  {
    Serial.println("No data received !!!"); // remplir le message qui semble approprié
  }
}
```


Here we can visualize in the capture of the serial monitor that the message sent by the end-device which it's adress is 4 is well received by the sever 

![](https://i.imgur.com/FH2jACK.png)





## Implementing a duty cycle

> In Europe for instance, the ETSI regulation instance specifies that any device using the 868 MHz band is forbidden to send for more than 1% of the time, unless a listen-before talk mechanism is implemented.

### Question 3

ETSI: European Telecommunications Standards Institute

### Question 4, 5, 6 & 7

```python!

# Calculation functions
def rawDataRate(sf, bw):
    return (sf * bw) / 2**sf

def physicalDataRate(sf, bw, cr):
    return rawDataRate(sf, bw) * (4 /(4 + cr))

def symbolDuration(sf, bw):
    return 2**sf / bw

preamble = 12.25 # symbols
header = 8 # symbols
app_header = 3 # bytes
payload = 1 # bytes
crc = 2 # bytes

cr = 1 # 4/5

frame_duration = symbolDuration(7, 125e3) * (preamble + header) + (app_header + payload + crc) / physicalDataRate(7, 125e3, cr)
f'one frame last {frame_duration:.2e} seconds'

# > one frame last 2.18e-02 seconds

# Let's take a 1 byte payload

dc = 1 / 100
t_off = frame_duration / dc

throughput = payload / ( t_off + frame_duration)

f'useful transfer rate is  {throughput:.2e} bytes/s'

# > useful transfer rate is 4.53e-01 bytes/s

# We know use a 20 bytes payload

payload = 20 # bytes

frame_duration = symbolDuration(7, 125e3) * (preamble + header) + (app_header + payload + crc) / physicalDataRate(7, 125e3, cr)
f'one frame last {frame_duration:.2e} seconds'

# > one frame last 2.53e-02 seconds

dc = 1 / 100
t_off = frame_duration / dc

throughput = payload / ( t_off + frame_duration)

f'useful transfer rate is {throughput:.2e} bytes/s'

# > useful transfer rate is 7.82e+00 bytes/s
```

### Question 8

We demonstrated that aggregating packets into larger payloads is important because it reduces the ratio on control bytes among the total message length. Thus the transfer rate is better when taking only data bytes into account.

Aggregating data in the code could be done the way we show below

client.cpp
```cpp!
#include <Arduino.h>

#include <RH_RF95.h>
#include <DHT.h>

#include <RHDatagram.h>

const uint8_t CLIENT_ADDRESS = 4;
const uint8_t SERVER_ADDRESS = 3;
const uint8_t MAX_PAYLOAD = 50;

RH_RF95 rf95;
RHDatagram rhd(rf95, CLIENT_ADDRESS);

uint8_t *buf = (uint8_t *)malloc(MAX_PAYLOAD + 1);

int counter = 0;

void setup()
{
  buf[MAX_PAYLOAD] = 0;

  Serial.begin(9600);

  if (!rhd.init())
    Serial.println("Init rhd failed");

  rf95.setFrequency(868);                       // Set the frequency in MHz.
  rf95.setTxPower(0);                           // Set Tx power in dBm
  rf95.setModemConfig(RH_RF95::Bw125Cr45Sf128); // Set the modem configuration.sf7 bw 125 CR 4/5

  Serial.println("Setup finished.");
}

void loop()
{

  // Allocate a buffer of size MAX_PAYLOAD

  // generate 8 bits of random data and put it in the buffer at counter position
  buf[counter] = random(0, 25) + 'a';

  // if the buffer is full, send it to the server
  if (counter == MAX_PAYLOAD - 1)
  {
    Serial.println("Sending data to server");
    Serial.println((char *)buf);
    rhd.sendto(buf, MAX_PAYLOAD, SERVER_ADDRESS);
    rhd.waitPacketSent();
    Serial.println("Data sent");
    rf95.sleep();
    counter = 0;
  }
  else
  {
    counter++;
  }

  delay(100);
}
```

### Question 12

We empirically tried to use the `sleep()` function. However in our test it appears that we got strange undefined behavior. We removed the `sleep()` part since it would have required further investigations that we did not have time to do.

## Implementing acknowledgments

### Question 13

For the confirmation of the reception of the message that is commonly called ack mechnism, for the end device we can decide either to send and wait the ack message from the server or repeating the same message multiple times to ensure that the server will at leat receive on among them. For that, a choice must be made. By comparing both of the mechanisms, it is clear that the latter can be energy consuming beacause for the first, the end-device, after sending the message will wait for the ack from the server.

In the server, we have implemented the ack mechanism that will allows to send a reception confirmation to the end-device as soon as it's message is received.

server.cpp

```cpp!
#include <Arduino.h>
#include <RH_RF95.h>
#include <RHDatagram.h>

const uint8_t CLIENT_ADDRESS = 4;
const uint8_t SERVER_ADDRESS = 3;

RH_RF95 rf95;
RHDatagram rhd(rf95, SERVER_ADDRESS);


void setup()
{
  Serial.begin(9600);
  
  if (!rhd.init())
    Serial.println("Init rhd failed");

  rf95.setFrequency(868); // Set the frequency in MHz.
  rf95.setTxPower(13);    // Set Tx power in dBm

  Serial.println("Setup finished.");
}

void loop()
{
  uint8_t bufferReception[RH_MAX_MESSAGE_LEN];
  uint8_t lenReception = sizeof(bufferReception);
  uint8_t senderAddress;

  if (rhd.waitAvailableTimeout(2000))
  {
    
    if (rhd.recvfrom(bufferReception, &lenReception, &senderAddress))
    {
      uint8_t data[] = "ack elhadji";
      uint8_t len_ack = sizeof(data);

      blinkLedLong();
      
      Serial.print("\n");
      Serial.print("\n");

      Serial.print("Message from the client  ");
      Serial.print(senderAddress);
      Serial.print(": ");
      bufferReception[lenReception]=0;

      Serial.println((char *)bufferReception);

      delay(1000);
      
      rhd.sendto((char*)data,  len_ack, senderAddress); 
      rhd.waitPacketSent();
      Serial.print("--------------------------ACK sent to Client  ");
      Serial.print(senderAddress);
      Serial.print(" --------------------------------\n");
      
    }
    else
    {
      Serial.println("Reception fail");
    }
    Serial.print("\n");
    Serial.print("\n");

  }
  else
  {
    Serial.println("No data received !!!"); // remplir le message qui semble approprié
  }
}

```
### Question 14

The ack message may contain a flag that will allow the end-device to know that the message is received. In our case, we have just specified a text message "ack" to inform the end-device that the message is received by the server.

### Question 15

If the ACK is sent and the end-device did not receive it, the latter, after waiting for a delay can decide to retransmit the message because it consider that the sink lost them. And in the sink we can have duplicated messages.

### Question 16


Limited by many constraints such as duty cycle, the large number of end-devices it serves, number of messages sent by an end-device per time the sink will not be able to send an AKC for a frame, it is quitely impossible.

### Question 17

If the sink can not send an ACK for a frame, the end-device can consider this lost and can also try to send the message again.

### Here below client code with ACK implementation :

client.cpp

```cpp!
#include <Arduino.h>

#include <RH_RF95.h>
#include <DHT.h>

#include <RHDatagram.h>

const uint8_t CLIENT_ADDRESS = 4;
const uint8_t SERVER_ADDRESS = 3;
const uint8_t MAX_PAYLOAD = 50;

RH_RF95 rf95;
RHDatagram rhd(rf95, CLIENT_ADDRESS);

uint8_t buf[MAX_PAYLOAD + 1];
uint8_t resend_buf[MAX_PAYLOAD + 1];

// ack timeout in ms
const uint16_t ACK_TIMEOUT = 3000;

// timer used for ack timeout
unsigned long start;

// ack received flag
bool ack = true;

int counter = 0;

void setup()
{
  buf[MAX_PAYLOAD] = 0;
  resend_buf[MAX_PAYLOAD] = 0;

  Serial.begin(9600);

  if (!rhd.init())
    Serial.println("Init rhd failed");

  rf95.setFrequency(868);                       // Set the frequency in MHz.
  rf95.setTxPower(0);                           // Set Tx power in dBm
  rf95.setModemConfig(RH_RF95::Bw125Cr45Sf128); // Set the modem configuration.sf7 bw 125 CR 4/5

  Serial.println("Setup finished.");

  // fill buf with random bytes of data
  for (int i = 0; i < MAX_PAYLOAD; i++)
  {
    buf[i] = random(0, 25) + 'a';
    counter++;
  }
}

void loop()
{

  // Allocate a buffer of size MAX_PAYLOAD

  // generate 8 bits of random data and put it in the buffer at counter position
  buf[counter] = random(0, 25) + 'a';

  // if the buffer is full, send it to the server
  if (counter >= MAX_PAYLOAD - 1)
  {
    Serial.println("Sending data to server because buffer is full");
    Serial.println((char *)buf);

    // new payload override the old one if ack has not been received at this point
    rhd.sendto(buf, MAX_PAYLOAD, SERVER_ADDRESS);
    rhd.waitPacketSent();
    Serial.println("Data sent");

    ack = false;
    start = millis();
    // copy buf into resend_buf
    memcpy(resend_buf, buf, MAX_PAYLOAD);

    // rf95.sleep();
    counter = 0;
  }
  else
  {
    counter++;
    // every 10th iteration if ack is false, try to recv an ack
    // check if ack is not received after timeout
    if (!ack && millis() - start < ACK_TIMEOUT)
    {
      // reset reception buffer
      // memset(rx_buf, 0, RH_RF95_MAX_MESSAGE_LEN + 1);
      // wait for a message
      if (rf95.available())
      {

        // allocate a reception buffer of max lora size
        uint8_t rx_buf[MAX_PAYLOAD];
        // rx_buf length
        uint8_t rx_len;
        uint8_t sender;
        if (rhd.recvfrom(rx_buf, &rx_len, &sender))
        {
          // check if message is ACK
          if (rx_buf[0] == 'a' && rx_buf[1] == 'c' && rx_buf[2] == 'k')
          {
            Serial.println("Ack received");
            Serial.println("size of buf");
            Serial.println(rx_len);
            Serial.println((char *)rx_buf);
            // print the sender address
            Serial.print("Sender: ");
            Serial.println(sender);
            ack = true;
            rf95.sleep();
          }
          else
          {
            Serial.println("Message received but not an ack");
            // print message
            Serial.println("size of buf");
            Serial.println(rx_len);
            Serial.println((char *)rx_buf);
            // print the sender address
            Serial.print("Sender: ");
            Serial.println(sender);
          }
        }
        // }
        else
        {
          // rf95.sleep();
        }
      }
    }
    // rf95.sleep();
  }
  if (!ack && millis() - start > ACK_TIMEOUT)
  {
    Serial.println("Ack not received");
    Serial.println("Resending data");
    rhd.sendto(resend_buf, MAX_PAYLOAD, SERVER_ADDRESS);
    rhd.waitPacketSent();
    Serial.println("Data sent");
    start = millis();
    // rf95.sleep();
  }
  delay(500);
}
```