Manual
do
Maker
.
com
Estou trabalhando em um projeto cujo hardware não pode ser divulgado. A imagem de destaque é o vazamento desse produto, do qual não posso sequer citar as especificações, mas enfim, é um hat para Raspberry e tem LoRa e fiz o teste inicial do LoRa com Raspberry e Arduino, com um bocado de esforço. Detalho.
Rádio é rádio, certo? O Driver é o mesmo para ambos, estou utilizando RadioHead, instalável pelo gerenciador de biblioteca do Arduino ou você pode pegá-lo nesse link.
Se quiser placas LoRa industriais, visite a AFE e faça sua escolha. Não é custo de hardware chinês, já adianto. É hardware profissional, mas nada impede que hobistas abusados (como eu) as tenham.
Não foi uma configuração transparente por diversos motivos e é importante salientar os pontos aqui.
No Raspberry não basta a biblioteca RadioHead. É necessário instalar previamente a biblioteca BCM2835, que dá acesso aos GPIO e outras funções do chip usado no Raspberry. Pegue-a nesse link. Se seu Raspberry 3 não estiver preparado para desenvolvimento, as dependências surgirão durante o processo a seguir:
tar zxvf bcm2835-1.xx.tar.gz
cd bcm2835-1.xx
./configure
make
sudo make check
sudo make install
Se estiver utilizando Raspberry Pi 2, execute raspi-config e em Advanced Options habilite a opção Enable Device Tree, então salve e reinicie-o antes de proceder com a configuração da biblioteca supracitada.
Os pinos padrão são:
Para multi-rádio, uma implementação extra é necessária, mas isso só poderei mostrar quando a AF Eletrônica tiver um hardware para tal.
Os pinos auxiliares são:
Esse é o segundo SPI do Raspberry (wow).
Mais informações sobre a BMC2835 podem ser encontradas aqui.
Estranho, não? Nem tanto, na verdade.
Através do configurador raspi-config podemos habilitar a interface SPI, entre outras. Porém, se estiver utilizando a biblioteca BCM2835, é necessário desabilitar o módulo do kernel Linux porque a biblioteca BMC2835 interage em baixo nível com o hardware, independente do módulo carregado pelo kernel.
Entre no configurador digitando raspi-config, então vá em Interface Options > SPI e desabilite-a.
Nesse ponto você já deve ter reiniciado seu Raspberry, caso a interface SPI estivesse habilitada. Tendo compilado e instalado a biblioteca BCM2835, agora baixe a biblioteca RadioHead. Por exemplo:
wget -c http://www.airspayce.com/mikem/arduino/RadioHead/RadioHead-1.89.zip
unzip RadioHead-1.89.zip
cd RadioHead/examples/raspi/rf95
Nesse diretório que você deverá entrar após a extração do pacote baixado, estarão contidos os arquivos Makefile, rf95_client.cpp e rf95_server.cpp.
O exemplo de client e server é para comunicação entre dois Raspberry Pi. No primeiro momento, eu precisava provar o conceito, sem endereçar os destinos dos pacotes (e tem muitas razões para isso). Desse modo, o rádio LoRa da placa da AF Eletrônica joga a informação no ar para quem quiser pegar. Para que a recepção ocorresse, foi necessário escrever algumas modificações no exemplo do server (que é o Raspberry).
Antes de expôr o código de exemplo, devemos considerar alguns pontos.
no site do RadioHead foi relatado que existe um bug conhecido e não solucionado, que não permite a utilização de interrupção do rádio. Antes de tomar conhecimento, eu já havia feito as configurações e testado. Com poucos minutos o Raspberry congelava, portanto, não tente usar interrupção até que o bug seja solucionado.
Esse arquivo contém a definição dos pinos para cada tipo de placa. Como a que estou utilizando para desenvolvimento ainda não está no mercado, tive que fazer as declarações. Vou dar como exemplo um dos modelos conhecidos:
// Dragino Raspberry PI hat (no obboard led)
// =========================================
// see https://github.com/dragino/Lora
#elif defined (BOARD_DRAGINO_PIHAT)
#define RF_CS_PIN RPI_V2_GPIO_P1_22 // Slave Select on GPIO25 so P1 connector pin #22
#define RF_IRQ_PIN RPI_V2_GPIO_P1_07 // IRQ on GPIO4 so P1 connector pin #7
#define RF_RST_PIN RPI_V2_GPIO_P1_11 // Reset on GPIO17 so P1 connector pin #11
#define RF_LED_PIN NOT_A_PIN // No onboard led to drive
No Raspberry não devemos utiizar a interrupção, portanto defina o pino como NOT_A_PIN.
O padrão é 434MHz à 13dBm, mas no EUA a regulamentação convenciona o uso de +14dBm, por isso é necessário modificar ou comentar a linha rf95.setTxPower.
Modifique também a frequência no define correspondente para 915.00.
Baseado no código de exemplo, as modificações resultaram em:
#include <bcm2835.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <RH_RF69.h>
#include <RH_RF95.h>
// AFEletronica MR
// see <link unavailable yet>
#define BOARD_AF_ELETRONICA
// Now we include RasPi_Boards.h so this will expose defined
// constants with CS/IRQ/RESET/on board LED pins definition
#include "../RasPiBoards.h"
// Our RFM95 Configuration
#define RF_FREQUENCY 915.00
// Create an instance of a driver
RH_RF95 rf95(RF_CS_PIN, RF_IRQ_PIN);
//RH_RF95 rf95(RF_CS_PIN);
//Flag for Ctrl-C
volatile sig_atomic_t force_exit = false;
void sendAnswer(){
char radiopacket[20] = "Good job! # ";
//itoa(packetnum++, radiopacket+13, 10);
printf("\nAnswering now...\n");
radiopacket[19] = 0;
rf95.setModeTx();
rf95.send((uint8_t *)radiopacket, 20);
rf95.waitPacketSent();
rf95.setModeRx();
}
void sig_handler(int sig)
{
printf("\n%s Break received, exiting!\n", __BASEFILE__);
force_exit=true;
}
//Main Function
int main (int argc, const char* argv[] )
{
unsigned long led_blink = 0;
signal(SIGINT, sig_handler);
//printf( "%s\n", __BASEFILE__);
if (!bcm2835_init()) {
fprintf( stderr, "%s bcm2835_init() Failed\n\n", __BASEFILE__ );
return 1;
}
#ifdef RF_RST_PIN
//printf( ", RST=GPIO%d", RF_RST_PIN );
// Pulse a reset on module
pinMode(RF_RST_PIN, OUTPUT);
digitalWrite(RF_RST_PIN, LOW );
bcm2835_delay(150);
digitalWrite(RF_RST_PIN, HIGH );
bcm2835_delay(100);
#endif
if (!rf95.init()) {
fprintf( stderr, "\nRF95 module init failed, Please verify wiring/module\n" );
} else {
// Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
// The default transmitter power is 13dBm, using PA_BOOST.
// If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then
// you can set transmitter powers from 5 to 23 dBm:
// driver.setTxPower(23, false);
// If you are using Modtronix inAir4 or inAir9,or any other module which uses the
// transmitter RFO pins and not the PA_BOOST pins
// then you can configure the power transmitter power for -1 to 14 dBm and with useRFO true.
// Failure to do that will result in extremely low transmit powers.
// rf95.setTxPower(14, true);
// RF95 Modules don't have RFO pin connected, so just use PA_BOOST
// check your country max power useable, in EU it's +14dB
rf95.setTxPower(13, false);
// You can optionally require this module to wait until Channel Activity
// Detection shows no activity on the channel before transmitting by setting
// the CAD timeout to non-zero:
//rf95.setCADTimeout(10000);
// Adjust Frequency
rf95.setFrequency(RF_FREQUENCY);
// Be sure to grab all node packet
// we're sniffing to display, it's a demo
rf95.setPromiscuous(true);
// We're ready to listen for incoming message
rf95.setModeRx();
//Begin the main body of code
while (!force_exit) {
if (rf95.available()) {
// Should be a message for us now
uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
uint8_t len = sizeof(buf);
uint8_t from = rf95.headerFrom();
uint8_t to = rf95.headerTo();
uint8_t id = rf95.headerId();
uint8_t flags= rf95.headerFlags();;
int8_t rssi = rf95.lastRssi();
if (rf95.recv(buf, &len)) {
printf("Ok!\n");
printbuffer(buf, len);
printf("\n%s\n",buf);
sendAnswer();
} else {
Serial.print("receive failed");
}
printf("\n");
}
// Let OS doing other tasks
// For timed critical appliation you can reduce or delete
// this delay, but this will charge CPU usage, take care and monitor
bcm2835_delay(5);
}
}
printf( "\n%s Ending\n", __BASEFILE__ );
bcm2835_close();
return 0;
}
Deixei o programa original tanto quanto foi possível. Tendo feito essas modificações, agora basta compilar e executar:
make
sudo ./rf95_server
O programa ficará em execução aguardando por qualquer mensagem.
Estou utilizando também uma placa da AF Eletrônica. No caso, essa (link para compra - aproveite porque está absurdamente barata), da imagem abaixo (descrita nesse artigo).
Essa placa utiliza um Arduino Pro-mini 3v3 (pode ser 5v) e tem slot para RF4463Pro e LoRa1276, à sua escolha. Estou utilizando LoRa1276 nela.
Para instalar a biblioteca, vá ao gerenciador de bibliotecas da IDE do Arduino e digite RadioHead. Instale-a. Após, use esse código no sketch:
#include <SPI.h>
#include <RH_RF95.h>
// Change to 434.0 or other frequency, must match RX's freq!
#define RF95_FREQ 915.0
// Singleton instance of the radio driver
RH_RF95 rf95;
void setup()
{
while (!Serial);
Serial.begin(115200);
delay(100);
while (!rf95.init()) {
Serial.println("LoRa radio init failed");
while (1);
}
Serial.println("LoRa radio init OK!");
// Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
if (!rf95.setFrequency(RF95_FREQ)) {
Serial.println("setFrequency failed");
while (1);
}
Serial.print("Set Freq to: "); Serial.println(RF95_FREQ);
// Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
// The default transmitter power is 13dBm, using PA_BOOST.
// If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then
// you can set transmitter powers from 5 to 23 dBm:
rf95.setTxPower(13, false);
}
int16_t packetnum = 0; // packet counter, we increment per xmission
void loop()
{
Serial.println("Sending to rf95_server");
// Send a message to rf95_server
char radiopacket[20] = "Hello World # ";
itoa(packetnum++, radiopacket+13, 10);
Serial.print("Sending "); Serial.println(radiopacket);
radiopacket[19] = 0;
Serial.println("Sending..."); delay(10);
rf95.send((uint8_t *)radiopacket, 20);
Serial.println("Waiting for packet to complete..."); delay(10);
rf95.waitPacketSent();
// Now wait for a reply
uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
uint8_t len = sizeof(buf);
Serial.println("Waiting for reply..."); delay(10);
if (rf95.waitAvailableTimeout(1000))
{
// Should be a reply message for us now
if (rf95.recv(buf, &len))
{
Serial.print("Got reply: ");
Serial.println((char*)buf);
Serial.print("RSSI: ");
Serial.println(rf95.lastRssi(), DEC);
}
else
{
Serial.println("Receive failed");
}
}
else
{
Serial.println("No reply, is there a listener around?");
}
delay(1000);
}
Bem mais simples que o Raspberry, hum? Mas convenhamos que agora trata-se de uma microcontroladora e um driver direto, com tudo já definido. Claro que o mínimo foi necessário; ajustar txPower para 915MHz, 13dBm. Subindo o sketch, a comunicação já deve começar em um ou dois segundos e o resultado deve ser algo como:
Espero que esse artigo lhe seja útil, porque foi muito duro chegar nesse ponto.
Meu ambiente consiste de um Raspberry Pi 3 na sala (piso inferior da casa) e um AFSmartRadio no meu quarto (piso superior). A imagem do terminal do Raspberry mostrada acima é de um acesso por SSH, enquanto a IDE Atom (ao fundo) é local, no notebook.
Inscreva-se no nosso canal Manual do Maker no YouTube.
Também estamos no Instagram.
Autor do blog "Do bit Ao Byte / Manual do Maker".
Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.