DGALLENE
Published © GPL3+

Arduino objects with LORA

Things communicate with LORA to a Jeedom Box

IntermediateProtip730
Arduino objects with LORA

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×2
Seeed Studio Grove base shiled for Uno
×2
Seeed Studio Grove temp and pressure sensors
×1
Seeed Studio Grove temp and hulidity sensors
×1
Grove - LoRa Radio 433MHz
Seeed Studio Grove - LoRa Radio 433MHz
×2
Raspberry Pi 3 Model B
Raspberry Pi 3 Model B
The jeedom box
×1
Raspberry PI2
Could be any PI
×1
Adafruit Bonnet Lora 433 for PI
×1

Story

Read more

Schematics

Network schema

The RFM9x datasheet

Code

The barometer

C/C++
It's the code for one of the Arduino device. It's the one at home.
#include <Dps310.h>
#include <Dps422.h>
#include <DpsClass.h>
#include <math.h>

#include <SoftwareSerial.h>
#include <RH_RF95.h>

Dps310 Dps310PressureSensor = Dps310();

// RF95 sur D6
const int RX = 6;
const int TX = 7;
// Singleton instance of the radio driver
SoftwareSerial ss(RX, TX); // RX, TX
RH_RF95<SoftwareSerial> rf95(ss);
//
float pres=0, temp=0;
float resTemp=0, resPres=0;
uint8_t oversampling = 7;
int ret=0;
const int CALIB_PRESS = 7; // Calibrage du baromtre pour Paris 14
//
unsigned int nbcar=0, compt=0, offsetPwr=0, power=6;
// DATUM
// SOURCE : 4 CHAR
// NUM MES : 6 CHAR
// MESURE : 10 CHAR dont CD MES : 4 CHAR et Val Mes : 6 CHAR
// Taille DATUM = SOURCE + NUM MES + N * (MESURES) = 10 + (N * 10) + \0
char msg[31] ;
char SOURCE[5]="BARO";
char MESPRES[5]="PRES";
char MESTEMP[5]="TEMP";

void setup() {
  // put your setup code here, to run once:
    Serial.begin(9600);
    // Pressure sensor
    Dps310PressureSensor.begin(Wire);
    // Radio
    if (!rf95.init())
    {
        Serial.println("init failed");
        while(1);
    }

    // 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.setFrequency(439.0);
    rf95.setModemConfig(rf95.Bw125Cr48Sf4096);
    //Serial.print("Conf : "); Serial.println(rf95.Bw125Cr48Sf4096);
    rf95.setTxPower(power, false);
}

void loop() {
  // put your main code here, to run repeatedly:
  memset(msg,'\0',sizeof(msg));
  // Message de wake up car cot PI ca roupille des fois
  nbcar = sprintf(msg,"%s","WAKE UP from BARO!");
  rf95.send(msg, sizeof(msg));
  delay(20000); // pas trop court le dlai 
  memset(msg,'\0',sizeof(msg));
  // DPS310 - Temp 
  ret = Dps310PressureSensor.measureTempOnce(temp, oversampling);
  if(ret==0){
    resTemp=temp;
    Serial.print("Temp : ");Serial.println(temp);
  }else{
    Serial.print("Err lect Temp : ");Serial.println(ret);
  }
//  Pression
  ret = Dps310PressureSensor.measurePressureOnce(pres, oversampling);
  if(ret==0){
    resPres=round(pres/100)+CALIB_PRESS ;
    Serial.print("Pres : ");Serial.println(pres);
  }else{
    Serial.print("Err lect Pres : ");Serial.println(ret);
  }
  // 
  compt+=1;
  // Formattage message
  // Arduino ne prend pas en charge simplement les float dans les xprintf (%f)
  // On passera des entiers  la place 
    nbcar = sprintf(msg,"%s%6u%s%6d%s%6d",SOURCE,compt,MESPRES,int(resPres),MESTEMP,int(resTemp*100));
    Serial.print(nbcar);Serial.print(" - Msg : ");Serial.println(msg);
  // ce module est client  
  // Envoi radio
    rf95.send(msg, sizeof(msg));
   
    rf95.waitPacketSent();
    
    // Now wait for a reply
    uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
    uint8_t len = sizeof(buf);

    if(rf95.waitAvailableTimeout(5000))
    {
        // Should be a reply message for us now   
        if(rf95.recv(buf, &len))
        {
            Serial.print("Recv OK : "); Serial.println((char*)buf);
        }
        else
        {
            Serial.println("Recv Err");
        }
    }
    else
    {
        Serial.println("Pas de rception");
    }
  
  //
  delay(3602000); // 1 heure + 7s - 5s ci-dessus
  //offsetPwr+=1;
  //if(offsetPwr>=8){
  //  offsetPwr=0;
  //}
  //Serial.print("Pwr : ");Serial.println(power-offsetPwr);
  //rf95.setTxPower(power-offsetPwr, false);
}

The cellar

C/C++
This prog is for the Arduino located into the cellar, out of the home
#include <ATH20.h>
#include <math.h>

#include <SoftwareSerial.h>
#include <RH_RF95.h>

ATH20 ATH;

// RF95 sur D6
const int RX = 6;
const int TX = 7;
// Singleton instance of the radio driver
SoftwareSerial ss(RX, TX); // RX, TX
RH_RF95<SoftwareSerial> rf95(ss);
//
float humi=0, temp=0;
float resTemp=0, resHumi=0;

//
unsigned int nbcar=0, compt=0, power=13, offsetPwr=0, OFFSET_PWR_MAX=10;
// DATUM
// SOURCE : 4 CHAR
// NUM MES : 6 CHAR
// MESURE : 10 CHAR dont CD MES : 4 CHAR et Val Mes : 6 CHAR
// Taille DATUM = SOURCE + NUM MES + N * (MESURES) = 10 + (N * 10) + \0
char msg[31] ;
char SOURCE[5]="CAVE";
char MESHUMI[5]="HUMI";
char MESTEMP[5]="TEMP";

void setup() {
  // put your setup code here, to run once:
    Serial.begin(9600);
    // Setup ATH20
    ATH.begin();
    // Radio
    if (!rf95.init())
    {
        Serial.println("init failed");
        while(1);
    }

    // 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.setFrequency(439.0);
    rf95.setModemConfig(rf95.Bw125Cr48Sf4096);
    //Serial.print("Conf : "); Serial.println(rf95.Bw125Cr48Sf4096);
    rf95.setTxPower(power, false);
}

void loop() {
  // put your main code here, to run repeatedly:
  memset(msg,'\0',sizeof(msg));
  // Message de wake up car cot PI ca roupille des fois
  nbcar = sprintf(msg,"%s","WAKE UP from CAVE !");
  rf95.send(msg, sizeof(msg));
  delay(20000); // pas trop court le dlai 
  memset(msg,'\0',sizeof(msg));
  // ATH20 - Temp and Humi
  int retATH = ATH.getSensor(&humi, &temp);
  // 
  if (retATH){
    resTemp=temp; resHumi=humi;
    Serial.print("Temp : ");Serial.println(resTemp); 
    Serial.print("Humi : ");Serial.println(resHumi*100);
  }
  compt+=1;
  // Formattage message
  // Arduino ne prend pas en charge simplement les float dans les xprintf (%f)
  // On passera des entiers  la place 
//    int nbcar = sprintf(msg,"%s%6u%s%6d%s%6d",SOURCE,compt,MESHUMI,int(resHumi*100),MESTEMP,int(resTemp*100));
    nbcar = sprintf(msg,"%s%2u%4u%s%6d%s%6d",SOURCE,power+offsetPwr,compt,MESHUMI,int(resHumi*100),MESTEMP,int(resTemp*100));
    Serial.print(nbcar);Serial.print(" - Msg : ");Serial.println(msg);
  // ce module est client  
  // Envoi radio
    rf95.send(msg, sizeof(msg));
   
    rf95.waitPacketSent();
    
    // Now wait for a reply
    uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
    uint8_t len = sizeof(buf);

    if(rf95.waitAvailableTimeout(5000))
    {
        // Should be a reply message for us now   
        if(rf95.recv(buf, &len))
        {
            Serial.print("Recv OK : "); Serial.println((char*)buf);
        }
        else
        {
            Serial.println("Recv Err");
        }
    }
    else
    {
        Serial.println("Pas de rception");
    }
  
  //
  delay(1796000); // 1/2 heure + 1s - 5s ci-dessus 
  // Gestion de la puissance
  offsetPwr+=1;
  if(offsetPwr>=OFFSET_PWR_MAX){
    offsetPwr=0;
  }
  rf95.setTxPower(power+offsetPwr, false);
}

The radio server

Python
A python prog that receive the radio Lora message from the Arduino and send the value of the sensor to the Jeedom box via HTTP. Pycurl is used for HTTP.
# Import Python System Libraries
import time
# Import Blinka Libraries
import busio
from digitalio import DigitalInOut, Direction, Pull
import board
# Import the SSD1306 module.
import adafruit_ssd1306
# Import RFM9x
import adafruit_rfm9x
#
import pycurl
from io import BytesIO
from datetime import datetime

# Create the I2C interface.
i2c = busio.I2C(board.SCL, board.SDA)

# 128x32 OLED Display
reset_pin = DigitalInOut(board.D4)
display = adafruit_ssd1306.SSD1306_I2C(128, 32, i2c, reset=reset_pin)
# Clear the display.
display.fill(0)
display.show()
width = display.width
height = display.height

# Configure LoRa Radio
CS = DigitalInOut(board.CE1)
RESET = DigitalInOut(board.D25)
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
rfm9x = adafruit_rfm9x.RFM9x(spi, CS, RESET, 439.0)
rfm9x.tx_power = 13
rfm9x.enable_crc
# la valeur du TO st a regler precisement ... ne pas bidouiller 
rfm9x.receive_timeout=2
# SF=12 ==> Chips=4096
rfm9x.spreading_factor=12
#CR=4/8
rfm9x.coding_rate=8
# Power selon le destinataire
PWR_CAVE=13
PWR_BARO=6
# DATAGRAMME
SOURCE="NONE"
NUMMES="000000"
CDMES="CODE"
VALMES="0000.0"

def main():
  colMess=35
  scroll=0
  power=6
  #
  while True:
    packet = None
    # draw a box to clear the image
    display.fill(0)
    # display(texte,col,lg,1)
    display.text('RasPi LoRa', colMess-scroll, 0, 1)

    # check for packet rx
    #print("av rec ",datetime.now().isoformat()[:19])
    packet = rfm9x.receive()
    #print("ap rec ",datetime.now().isoformat()[:19])
    #print(datetime.now().isoformat()[:19]," - ",packet)
    if packet is None:
        display.show()
        display.text('- Waiting for PKT -', colMess-scroll, 20, 1)
    else:
        # Display the packet text and rssi
        display.fill(0)
        #
        # Grer les pb d'encodage dus aux interferences
        try:
          packet_text = packet.decode(encoding="utf-8", errors="replace")
          print(datetime.now().isoformat()[:19]," - ",packet_text,flush='True')
          # Decoupe DATAGRAMME
          SOURCE=packet_text[0:4]
          NUMMES=packet_text[4:10]
          if SOURCE=="CAVE":
             labHumi=packet_text[10:14]
             valHumi=packet_text[18:20]
             labTemp=packet_text[20:24]
             valTemp=packet_text[26:30]
             display.text(SOURCE,0,0,1)
             display.text(labHumi,20,8,1)
             display.text(valHumi,50,8,1)
             display.text(labTemp,20,16,1)
             display.text(valTemp,50,16,1)
             msg=bytes(SOURCE+"OK guy !\r\n\0","utf-8")
             # Envoi  Jeedom
             if(valHumi.strip().isnumeric()==True):
                envoieValeur(471,int(valHumi.strip()))
             if(valTemp.strip().isnumeric()==True):
                envoieValeur(472,float(valTemp.strip())/100)
             envoieValeur(478,datetime.now().isoformat()[:19])
             power=PWR_CAVE
          elif SOURCE=="BARO":
             labPres=packet_text[10:14]
             valPres=packet_text[16:20]
             labTemp=packet_text[20:24]
             valTemp=packet_text[26:30]
             display.text(SOURCE,0,0,1)
             display.text(labPres,20,8,1)
             display.text(valPres,50,8,1)
             display.text(labTemp,20,16,1)
             display.text(valTemp,50,16,1)
             msg=bytes(SOURCE+"OK guy !\r\n\0","utf-8")
             # Envoi  Jeedom
             if(valPres.strip().isnumeric()==True):
               envoieValeur(476,int(valPres.strip()))
             if(valTemp.strip().isnumeric()==True):
               envoieValeur(477,float(valTemp.strip())/100)
             envoieValeur(479,datetime.now().isoformat()[:19])
             power=PWR_BARO
          else:
             display.text("SOURCE INCONNUE !",0,0,1)
             msg=bytes("SOURCE INCONNUE !\r\n\0","utf-8")
        except Exception as e:
          print("Err : ",packet,e,sep=' - ',flush='True')
          #print(packet)
          packet_text = "Err encodage"
          display.text(packet_text,0,0,1)
          msg=bytes(SOURCE+"KO guy, pb encodage !\r\n\0","utf-8")
        #
        time.sleep(0.1)
        rfm9x.tx_power = power
        rfm9x.send(msg)
        display.text('Msg sent !', 0, 24, 1)

    display.show()
    time.sleep(5)
    scroll=scroll+1
    if scroll>=30:
       scroll=0

def envoieValeur(id,valeur):
  u=""
  status=""
  try:
    u='http://<an IP adress>/core/api/jeeApi.php?apikey=<the API of the jeedom>&type=virtual&id='+str(id)+'&value='+str(valeur)
    #print(u)
    buffer = BytesIO()
    c = pycurl.Curl()
    c.setopt(c.WRITEFUNCTION, buffer.write)
    c.setopt(c.URL,u)
    c.perform()
    status = c.getinfo(pycurl.HTTP_CODE)
  except Exception as e:
    print(datetime.now().isoformat()," - Erreur lors de l envoi")
    print("URL : ",u)
    print("Status : ",status)
  finally:
    c.close()

if __name__ == "__main__":
    main()

Credits

DGALLENE
0 projects • 0 followers

Comments