Rocket

Telemetry

louisa

Synopsis

The project of data transmission and reception system has two LoRa1278 modules:

> one inside the rocket and the other one with me <

Communication in North America

Antennas are connected to carry out the transmission with 915MHz, which is the fundamental parameter for communication in the United States.


Down arrow

Programming Language

It's written in C, however, I'll improve it using AVR Assembly for low level optimization.

Arduino Wiring

Down arrow

Arduino Wiring

Vin connects to the Arduino 5V pin. If you're using a 3.3V Arduino, connect to 3.3V;

GND connects to Arduino ground;

SCLK connects to SPI clock. On Arduino Uno/Duemilanove/328-based, thats Digital 13. On Mega's, its Digital 52 and on Leonardo/Due its ICSP-3;

MISO connects to SPI MISO. On Arduino Uno/Duemilanove/328-based, thats Digital 12. On Mega's, its Digital 50 and on Leonardo/Due its ICSP-1;

MOSI connects to SPI MOSI. On Arduino Uno/Duemilanove/328-based, thats Digital 11. On Mega's, its Digital 51 and on Leonardo/Due its ICSP-4;

CS connects to our SPI Chip Select pin. We'll be using Digital 4 but you can later change this to any pin;

RST connects to our radio reset pin. We'll be using Digital 2 but you can later change this pin too;

G0 (IRQ) connects to an interrupt-capable pin. We'll be using Digital 3 but you can later change this pin too. However, it must connect a hardware Interrupt pin. Not all pins can do this! Check the board documentation for which pins are hardware interrupts, you'll also need the hardware interrupt number. For example, on UNO digital 3 is interrupt #1.

temperature and humidity sensor

Code

main.c (rocket transmitter)


/**
 * Rocket data transmitter through RFM9X
 * TODO: currently using Arduino SPI and Library RH_RF95 that uses C++ a lot
 * This isn't a good idea as C++ uses object that uses lot of space and is a waste of cycles
 * rewrite it.
 */

#include 
#include 
#include 
#include 

// custom serial library
// because arduino library sucks
#include "serial.h"

/**
 * Current frequency that we are transmitting information, if you want to change it
 * *please* again read the datasheet to check the supported frequency
 */

#define FREQUENCY 915

/**
 * Layout:
 * SS = Slave Select/Chip Select
 * RST = Reset
 * IRQ = Hardware Interrupt IRQ, ATmega328p uses Pin2 for hardware interrupt
 * if you want to change it, *please* read the datasheet to make sure that the pin
 * has support for hardware interrupt, kthx
 */

#define SS 10
#define RST 9
#define IRQ 2

// global object for rf95 board
RH_RF95 rf95(SS, IRQ);

// constant string should be in flash memory
// don't need to be in SRAM, save memory and is better
// PGM_P: used to declare a variable that is a pointer to a string in program space
// PROGMEM: attribute to use in order to declare an object being located in flash ROM
PGM_P const MESSAGE_LIST[0xB] PROGMEM = {
    "[-] initializing transmissor\n\0",          // 0x0
    "[-] error while initializing\n\0",          // 0x1
    "[-] initialized\n\0",                       // 0x2
    "[-] error while setting frequency\n\0",     // 0x3
    "[-] starting main loop\n\0",                // 0x4
    "[-] sending message to receiver\n\0",       // 0x5
    "[-] delay for packet\n\0",                  // 0x6
    "[-] waiting the reply\n\0",                 // 0x7
    "[-] we got a message back\n\0",             // 0x8
    "[-] failed to receive message\n\0",         // 0x9
    "[-] no reply, anyone around?\n\0"           // 0xA
};


inline void init_transmitter() {
    // i/o init
    init();

    // reset the chip
    pinMode (RST, OUTPUT);
    digitalWrite(RST, HIGH);

    // initialize serial
    usart_start();
    // wait 120ms so we can start
    _delay_ms(120);

    usart_print(MESSAGE_LIST[0x0]);

    digitalWrite(RST, LOW);
    _delay_ms(15);
    digitalWrite(RST, HIGH);
    _delay_ms(15);

    // wait for lora initialize
    while (!rf95.init()) {
        // something bad happened, check the cables
        usart_print(MESSAGE_LIST[0x1]);
    }

    usart_print(MESSAGE_LIST[0x2]);

    // set the communication channel frequency
    if (!rf95.setFrequency(FREQUENCY)) {
        // this is bad, we failed to set the frequency, check datasheet
        usart_print(MESSAGE_LIST[0x3]);
        // endless loop, reboot the board
        while (1) {}
    }

    // c/p from the Lora example, check in the datasheet before
    // any change, *mainly* because we are dealing with "power"
    // it may be related with voltage, careful
    rf95.setTxPower(23, false);
}

int main() {

    const char* test_message = "Hello, I'm alive\0";
    unsigned char buf[RH_RF95_MAX_MESSAGE_LEN];
    unsigned char len = sizeof(buf);

    // initialize serial, pin, rf etc
    init_transmitter();

    usart_print(MESSAGE_LIST[0x4]);

    // main loop
    while (1) {

        // send a message to receiver
        usart_print(MESSAGE_LIST[0x5]);
        _delay_ms(10);
        rf95.send((uint8_t *)test_message, strlen(test_message));

        // wait for the message to be send
        usart_print(MESSAGE_LIST[0x6]);
        _delay_ms(10);
        rf95.waitPacketSent();

        // let's wait now for the reply
        usart_print(MESSAGE_LIST[0x7]);
        _delay_ms(10);

        if (rf95.waitAvailableTimeout(1000)) {

            // read message received into the buffer
            if (rf95.recv(buf, &len)) {
              usart_print(MESSAGE_LIST[0x8]);

              for (int i = 0; i < RH_RF95_MAX_MESSAGE_LEN; i++) {
                  usart_send((char)buf[i]);
              }

              //usart_print("\nRSSI: \n\0");
              //Serial.println(rf95.lastRssi(), DEC);
            } else {
              // something went wrong and we failed to read the message
              usart_print(MESSAGE_LIST[0x9]);
            }
        } else {
            // timeout, no one sending message :(
            usart_print(MESSAGE_LIST[0xA]);
        }
        _delay_ms(1000);
    }

    return 0;
}
                        

Code

transmissor_main.ino


/**
 * Rocket data transmitter through RFM9X
 * TODO: currently using Arduino SPI and Library RH_RF95 that uses C++ a lot
 * This isn't a good idea as C++ uses object that uses lot of space and is a waste of cycles
 * rewrite it.
 */

#include 
#include 
#include 
#include 
#include  //temperature and humidity dht11
#include 
#include 

// custom serial library
// because arduino library sucks
#include "serial.h"

// sensor information
#include 
#include 

// sensor object that will provide information
Adafruit_BMP085 bmp;

/**
 * Current frequency that we are transmitting information, if you want to change it
 * *please* again read the datasheet to check the supported frequency
 */

#define FREQUENCY 915

/**
 * Layout:
 * SS = Slave Select/Chip Select
 * RST = Reset
 * IRQ = Hardware Interrupt IRQ, ATmega328p uses Pin2 for hardware interrupt
 * if you want to change it, *please* read the datasheet to make sure that the pin
 * has support for hardware interrupt, kthx
 */

#define SS 10
#define RST 9
#define IRQ 2

/**
 *  Packet List for communication with server
 */

#define PACKET_ID_SENSORS 0

// global object for rf95 board
RH_RF95 rf95(SS, IRQ);

// constant string should be in flash memory
// don't need to be in SRAM, save memory and is better
// if you don't know what is PGM_P and PROGMEM is, google is your friend
PGM_P const MESSAGE_LIST[0xD] PROGMEM = {
  "[-] initializing transmissor\n\0",          // 0x0
  "[-] error while initializing\n\0",          // 0x1
  "[-] initialized\n\0",                       // 0x2
  "[-] error while setting frequency\n\0",     // 0x3
  "[-] starting main loop\n\0",                // 0x4
  "[-] sending message to receiver\n\0",       // 0x5
  "[-] delay for packet\n\0",                  // 0x6
  "[-] waiting the reply\n\0",                 // 0x7
  "[-] we got a message back\n\0",             // 0x8
  "[-] failed to receive message\n\0",         // 0x9
  "[-] no reply, anyone around?\n\0",          // 0xA
  "[-] failed to initialize the sensor\n\0",   // 0xB
  "[-] sensor initialized with success\n\0"    // 0xC
};

/**
 *  Create a packet that will be transfered to the server
 *  holding information about temperature and humidity
 */
void packet_sensors(unsigned char* packet) {

   // packet format:
   // byte 0 -- ID
   // byte 1 -- temperature
   // byte 2 -- humidity

   packet[0] = PACKET_ID_SENSORS;
   packet[1] = (char)bmp.readTemperature();
   *(unsigned int*)(packet + 0x2) = (unsigned int)bmp.readPressure();
   *(unsigned int*)(packet + 0x6) = (unsigned int)bmp.readAltitude();
   *(unsigned int*)(packet + 0xA) = (unsigned int)bmp.readSealevelPressure();
   *(unsigned int*)(packet + 0xE) = (unsigned int)bmp.readAltitude(101500);
}

inline void init_transmitter() {
  // i/o init
  init();

  // reset the chip
  pinMode (RST, OUTPUT);
  digitalWrite(RST, HIGH);
  pinMode (7, OUTPUT);

  // initialize serial
  usart_start();
  // wait 120ms so we can start
  _delay_ms(120);

  usart_print(MESSAGE_LIST[0x0]);

  digitalWrite(RST, LOW);
  _delay_ms(15);
  digitalWrite(RST, HIGH);
  _delay_ms(15);

  // wait for lora initialize
  while (!rf95.init()) {
    // something bad happened, check the cables
    usart_print(MESSAGE_LIST[0x1]);
  }

  usart_print(MESSAGE_LIST[0x2]);

  // set the communication channel frequency
  if (!rf95.setFrequency(FREQUENCY)) {
    // this is bad, we failed to set the frequency, check datasheet
    usart_print(MESSAGE_LIST[0x3]);
    // endless loop, reboot the board
    while (1) {}
  }

  // c/p from the Lora example, check in the datasheet before
  // any change, *mainly* because we are dealing with "power"
  // it may be related with voltage, careful
  rf95.setTxPower(23, false);
}

int main() {

  const char* test_message = "Hello, I'm alive\0";
  unsigned char buf[RH_RF95_MAX_MESSAGE_LEN];
  unsigned char len = sizeof(buf);

  // initialize serial, pin, rf etc
  init_transmitter();

  usart_print(MESSAGE_LIST[0x4]);

  // init sensor
  if (!bmp.begin()) {
    usart_print(MESSAGE_LIST[0xB]);
    while(1);
  }

  // main loop
  while (1) {

    // send a message to receiver
   // usart_print(MESSAGE_LIST[0x5]);
    packet_sensors(buf);
    _delay_ms(10);
    rf95.send((uint8_t *)buf, RH_RF95_MAX_MESSAGE_LEN);

    // wait for the message to be send
    usart_print(MESSAGE_LIST[0x6]);
    _delay_ms(10);
    rf95.waitPacketSent();

    // let's wait now for the reply
    usart_print(MESSAGE_LIST[0x7]);
    _delay_ms(10);

    digitalWrite(7, HIGH);

    if (rf95.waitAvailableTimeout(1000)) {

      // read message received into the buffer
      if (rf95.recv(buf, &len)) {
        usart_print(MESSAGE_LIST[0x8]);

        for (int i = 0; i < RH_RF95_MAX_MESSAGE_LEN; i++) {
          usart_send((char)buf[i]);
        }

        //usart_print("\nRSSI: \n\0");
        //Serial.println(rf95.lastRssi(), DEC);
      } else {
        // something went wrong and we failed to read the message
        usart_print(MESSAGE_LIST[0x9]);
      }
    } else {
      // timeout, no one sending message :(
      usart_print(MESSAGE_LIST[0xA]);
    }

    digitalWrite(7, LOW);
    _delay_ms(1000);
  }

  return 0;
}
            

Code

rocket_receiver.ino


/**
 * Rocket data transmitter through RFM9X
 * TODO: currently using Arduino SPI and Library RH_RF95 that uses C++ a lot
 * This isn't a good idea as C++ uses object that uses lot of space and is a waste of cycles
 * rewrite it.
 */

#include 
#include 
#include 
#include 

// custom serial library
// because arduino library sucks
#include "serial.h"

/**
 * Current frequency that we are transmitting information, if you want to change it
 * *please* again read the datasheet to check the supported frequency
 */

#define FREQUENCY 915

/**
 * Layout:
 * SS = Slave Select/Chip Select
 * RST = Reset
 * IRQ = Hardware Interrupt IRQ, ATmega328p uses Pin2 for hardware interrupt
 * if you want to change it, *please* read the datasheet to make sure that the pin
 * has support for hardware interrupt, kthx
 */

#define SS 10
#define RST 9
#define IRQ 2
unsigned int max_altitude = 0;

/**
 *  Packet List for communication with server
 */

#define PACKET_ID_SENSORS 0

// global object for rf95 board
RH_RF95 rf95(SS, IRQ);

// constant string should be in flash memory
// don't need to be in SRAM, save memory and is better
// if you don't know what is PGM_P and PROGMEM is, google is your friend
PGM_P const MESSAGE_LIST[0xB] PROGMEM = {
  "[-] initializing receiver\n\0",             // 0x0
  "[-] error while initializing\n\0",          // 0x1
  "[-] initialized\n\0",                       // 0x2
  "[-] error while setting frequency\n\0",     // 0x3
  "[-] starting main loop\n\0",                // 0x4
  "[-] message received\n\0",                  // 0x5
  "[-] delay for packet\n\0",                  // 0x6
  "[-] waiting the reply\n\0",                 // 0x7
  "[-] we got a message back\n\0",             // 0x8
  "[-] failed to receive message\n\0",         // 0x9
  "[-] no reply, anyone around?\n\0"           // 0xA
};

inline void init_transmitter() {
  // i/o init
  init();

  // reset the chip
  pinMode (RST, OUTPUT);
  digitalWrite(RST, HIGH);

  // initialize serial
  usart_start();
  // wait 120ms so we can start
  _delay_ms(120);

  usart_print(MESSAGE_LIST[0x0]);

  digitalWrite(RST, LOW);
  _delay_ms(15);
  digitalWrite(RST, HIGH);
  _delay_ms(15);

  // wait for lora initialize
  while (!rf95.init()) {
    // something bad happened, check the cables
    usart_print(MESSAGE_LIST[0x1]);
  }

  usart_print(MESSAGE_LIST[0x2]);

  // set the communication channel frequency
  if (!rf95.setFrequency(FREQUENCY)) {
    // this is bad, we failed to set the frequency, check datasheet
    usart_print(MESSAGE_LIST[0x3]);
    // endless loop, reboot the board
    while (1) {}
  }

  // c/p from the Lora example, check in the datasheet before
  // any change, *mainly* because we are dealing with "power"
  // it may be related with voltage, careful
  rf95.setTxPower(23, false);
}

int main() {
  unsigned char buf[RH_RF95_MAX_MESSAGE_LEN];
  unsigned char len = sizeof(buf);
  char output_buffer[0x10];
  memset(output_buffer, 0x0, 0x10);

  // initialize serial, pin, rf etc
  init_transmitter();

  usart_print(MESSAGE_LIST[0x4]);

  // main loop
  while (1) {
    if (rf95.available()) {
      // read message received into the buffer
      if (rf95.recv(buf, &len)) {
        switch (buf[0]) {
           case PACKET_ID_SENSORS: {
              unsigned int aux = 0;

              // temperature print
              usart_print((const char*)"\n\n\ntemperature:\0");
              itoa(buf[1], output_buffer, 10);
              usart_print((const char*) output_buffer);
              memset(output_buffer, 0x0, 0x10);

              // pressure print
              usart_print((const char*)"\nPressure:\0");
              aux = *(unsigned int*)(buf + 0x2);
              utoa(aux, output_buffer, 10);
              usart_print((const char*) output_buffer);
              memset(output_buffer, 0x0, 0x10);

              // atitude 1
              usart_print((const char*)"\nAltitude:\0");
              aux = *(  unsigned int*)(buf + 0x6);
              itoa(aux, output_buffer, 10);
              usart_print((const char*) output_buffer);
              memset(output_buffer, 0x0, 0x10);

              // pressure at sea level
              usart_print((const char*)"\nPressure Sea Level:\0");
              aux = *(unsigned int*)(buf + 0xA);
              utoa(aux, output_buffer, 10);
              usart_print((const char*) output_buffer);
              memset(output_buffer, 0x0, 0x10);

              // Real atitude
              usart_print((const char*)"\nReal Altitude:\0");
              aux = *(unsigned int*)(buf + 0xE);
              itoa(aux, output_buffer, 10);
              usart_print((const char*) output_buffer);
              memset(output_buffer, 0x0, 0x10);

              if (max_altitude < aux) {
                max_altitude = aux;
              }

              usart_print((const char*)"\nMaximum Altitude:\0");
              itoa(max_altitude, output_buffer, 10);
              usart_print((const char*) output_buffer);
              memset(output_buffer, 0x0, 0x10);
           }
           break;
        }

        // send message back
        uint8_t data[] = "And hello back to you";
        rf95.send(data, sizeof(data));
        rf95.waitPacketSent();

        //usart_print("\nRSSI: \n\0");
        //Serial.println(rf95.lastRssi(), DEC);
      } else {
        // something went wrong and we failed to read the message
        usart_print(MESSAGE_LIST[0x9]);
      }
    }
    _delay_ms(10);
  }
  return 0;
}
                

Code

serial.c


#include "serial.h"

void usart_start() {

/**
 * Configure the baud rate prescale
 */
 UBRR0H  = (unsigned char)(USART_PRESCALE >> 8);
 UBRR0L  = (unsigned char)(USART_PRESCALE & 0xFF);

/**
 * Enable USART hardware to recv and send data
 */
 UCSR0B |= (1 << RXEN0)  | (1 << TXEN0);

 UCSR0C |= (1 << UCSZ00) | (1 << UCSZ01);
}

void usart_send(char data) {
    while (!(UCSR0A & (1 << UDRE0))) {}

    UDR0 = data;
}

void usart_print(const char *data) {
    unsigned char i = 0;

    while (data[i] != '\0') {
        usart_send(data[i++]);
    }
}

char usart_recv() {
    while (!(UCSR0A & (1 << RXC0))) {}

    return UDR0;
}
                

Code

serial.h


#ifndef _SERIAL_H_
#define _SERIAL_H_

#include 

#define USART_PRESCALE (((F_CPU/(9600 * 16UL))) - 1)

#ifdef __cplusplus
    extern "C" {
#endif
void usart_start();
#ifdef __cplusplus
}
#endif

#ifdef __cplusplus
    extern "C" {
#endif
void usart_send(char data);
#ifdef __cplusplus
}
#endif

#ifdef __cplusplus
    extern "C" {
#endif
void usart_print(const char *data);
#ifdef __cplusplus
}
#endif

#ifdef __cplusplus
    extern "C" {
#endif
char usart_recv();
#ifdef __cplusplus
}
#endif

#endif