louisa
The project of data transmission and reception system has two LoRa1278 modules:
> one inside the rocket and the other one with me <
Antennas are connected to carry out the transmission with 915MHz, which is the fundamental parameter for communication in the United States.
It's written in C, however, I'll improve it using AVR Assembly for low level optimization.
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.
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;
}
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;
}
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;
}
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;
}
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