I can't make HPMA 115S0 and GPS work at the same time [SOLVED]

Hi everyone,
I’m using an Adfruit Ultimate breakout, a HPMA 115S0 and a RFM95 Lora Radio module on an attiny1604.
after I got proper data from my gps (see https://forum.arduino.cc/index.php?topic=705952.msg4746378#msg4746378) I wanted to add the HPMA 115S0 sensor, but since I had to add a second software serial instance I started to get problems. At the beginning I couldn’t send data, so I added another serial.begin() in the loop (HPMAserial.begin(9600)) and it worked, but now the gps doesn’t work anymore and I only get 0.00 for latitude and longitude.
I tried using my old sketch and I was able to get proper values for latitude and longitude.
I’m using GitHub - Electronza/HPMA115S0: Arduino library for Honeywell HPMA115S0 particle sensor as a library for the sensor.

here’s the code:

#include <hpma115s0.h>
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#define gpsPort ssGPS
#include <Arduino.h>
#include "LoRaWAN.h" 
#include "secconfig.h" 

/******************************
 DEFINIZIONI MODULO LoRa
 ******************************/
#define DIO0 1 //PIN FISICO 3
#define NSS 0 //PIN FISICO 2
RFM95 rfm(DIO0, NSS);
// Definisce la lunghezza del buffer dati
uint8_t Data_Length = 0x10;
// Definisce il layer LoRaWAN
LoRaWAN LoRa = LoRaWAN(rfm);
// Frame Counter per contare ogni Payload inviato
unsigned int Frame_Counter_Tx = 0;

/******************************
 DEFINIZIONI MODULO GPS
 ******************************/
static const int Rx = 4, Tx = 5;
static const uint32_t GPSBaud = 9600;

// The serial connection to the GPS device
SoftwareSerial ssGPS(Rx, Tx);
float lati, longi;
int latid, longid;
float latif, longif;
int pm10d, pm25d;
float pm10f, pm25f;
// The TinyGPS++ object
TinyGPSPlus tinyGPS;

///******************************
// DEFINIZIONI SENSORE PARTICELLARE HPMA115S0
// ******************************/
bool my_status;
float p25;
float p10;
//// Collegamento seriale HPMA115S0
SoftwareSerial HPMAserial(2,3); // RX, TX dell' HPMA115S0
HPMA115S0 my_hpm(HPMAserial);


void setup( ) {
  Serial.begin(9600);
//Inizializzo il modulo RFM
 rfm.init();
/*Inizializza le chiavi di sessione ed applicazione e l'indirizzo del
nodo per il joining tra Nodo e Network Server attraverso tecnica ABP*/
 LoRa.setKeys(NwkSkey, AppSkey, DevAddr);
 // HPMA115S0 setting

    HPMAserial.begin(9600);
    delay(100);
    // Stop autosend
    my_status = my_hpm.stop_autosend();
     
    delay(500);
    //my_status = my_hpm.start_measurement();
    delay(1000);
   
  ssGPS.begin(GPSBaud);
}
void loop( ) {

 
 //ssGPS.begin(GPSBaud);
 ssGPS.listen();
 while (ssGPS.available()){
      char c=ssGPS.read();
      tinyGPS.encode(c);
    
       if (tinyGPS.location.isUpdated()){
 lati=tinyGPS.location.lat();
 break;

 }
 }
 unsigned char Data[Data_Length];

   
//aumento il Frame Counter
 Frame_Counter_Tx++;

 //Provo a stampare e trasmettere distanza come parte intera e decimale --> sprintf per l'attiny ha funzionalità limitate
  char *sgn1 = (lati < 0) ? "-" : "";
  latid = lati;                 
  latif = lati - latid;      
  int latiff = trunc(latif * 100); 

  //Provo a stampare e trasmettere distanza come parte intera e decimale --> sprintf per l'attiny ha funzionalità limitate
  char *sgn2 = (lati < 0) ? "-" : "";
  longid = longi;                 
  longif = longi - longid;      
  int longiff = trunc(longif * 10000); 
   delay(6000);


 // Dati del sensore HPMA (con media pesata)
HPMAserial.begin(9600);

float pm10=0,pm25=0;
//int i;
//for(i=0;i<10;i++)
// {
// 
 my_status = my_hpm.read(&p25,&p10);
 delay(6000);
 pm10 = pm10 + p10;
 pm25 = pm25 + p25;
//
// }
// pm10=pm10/i;
// pm25=pm25/i;

  //Provo a stampare e trasmettere distanza come parte intera e decimale --> sprintf per l'attiny ha funzionalità limitate
  pm10d = pm10;   
  pm25d= pm25;              

 int a;
//Creo la stringa in cui stampo il buffer Data contenente la stringa di invio
Data_Length = sprintf(Data,"pm10:%d pm2.5:%d Lat:%s%d.%02d Long:%s%d.%04d ", pm10d, pm25d, sgn1, latid, latiff,sgn2, longid, longiff ); // 68 BYTE
 //delay(5000);
/*Send_Data già modificato nella libreria; mancavano alcuni parametri che
permettevano la trasmissione a certi SF(Spreading Factor) e BW(Larghezze
di Banda)*/
//A fine sketch sono riportati i canali con SF e BW modificati nella libreria (sono riportati come riferimento per ricordarsene)
//txParameter viene scelto uguale a 5 perchè individua la casistica 5 per
//la trasmissione; quindi trasmetto ad un SF7 con BW di 125 kHz
 int txParameter=5;
/*Controllo sulla quantità di pacchetti inviati; considerando i tempi necessari
a sistemare e recuperare il sistema sotto terra, non volevamo inviare pacchetti fino a che le batterie non si fossero drenate;
si è quindi inserito un controllo sul contatore, in modo che quando esso arriva a
2000 la trasmissione si conclude definitivamente.*/
if (Frame_Counter_Tx < 2000)
 {
 LoRa.Send_Data(Data, Data_Length, Frame_Counter_Tx,txParameter);
 }
}
unsigned char NwkSkey[16] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
 0x09, 0x10, 0x11, 0x12, 0x13, 0x014, 0x15, 0x16 };
unsigned char AppSkey[16] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
 0x09, 0x10, 0x11, 0x12, 0x13, 0x014, 0x15, 0x16 };
unsigned char DevAddr[4] = { 0x00, 0x00, 0x00, 0x02 };

I dont understand most of the code and comments so working out what the code is trying to do is difficult. This is an english language forum.

Using two software serial instances is possible, but not straightforward, much easier to use an Arduino that has enough hardware serial ports for the application.

ATtiny1604: ATTINY1604 - 8-bit Microcontrollers.

That is not an Arduino :sob: :sob: :sob:
It has only 1024 bytes of sram an you are trying to put 5 libraries in it :o I think that is impossible.
The SoftwareSerial puts a lot of stress on the microcontroller. If one of those other libraries is using an interrupt or disables the interrupts, then nothing will work reliable anymore.

Try an Arduino board which has two extra Serial port for the GPS and the HPMA115S0.
Maybe that is not what you want, but you are trying to put GPS and the HPMA115S0 on the same board.

Koepel:
ATtiny1604: ATTINY1604 - 8-bit Microcontrollers.

It has only 1024 bytes of sram an you are trying to put 5 libraries in it :o I think that is impossible.

I also have an attiny1614, that has 2048 bytes of sram, could it be enough? Sadly I can only use tinys for my project.

alessandro_capresi:
I also have an attiny1614, that has 2048 bytes of sram, could it be enough? Sadly I can only use tinys for my project.

I tried with the attiny 1614 and it does't change anything

srnet:
I dont understand most of the code and comments so working out what the code is trying to do is difficult. This is an english language forum.

I'm very sorry but I didn't have time to translate the comments, no one said anything about it my other topics/questions I made before, so I thought it wasn't a problem.

srnet:
Using two software serial instances is possible, but not straightforward, much easier to use an Arduino that has enough hardware serial ports for the application.

I know but I can't use them.

Maybe it is not possible :frowning:

Why do you have to use an ATtiny ?
If you really want to make this work in a ATtiny, then you need to be an experienced programmer or have a lot of luck :wink:

Can you give a list with links to all the libraries ? Perhaps they do go along with SoftwareSerial (very unlikely).
Can you also give a schematic or drawing to check how everything is connected ?

In your sketch you can select the RX of a SoftwareSerial port with SoftwareSerial::listen().
However, you use HPMAserial.begin(9600) in the loop(). You should use listen() to select a port.

A delay of 6 seconds means that you are doing nothing for 6 seconds. Is that okay ?

The problem that you have feels as a sram memory problem. Perhaps there is not enough memory. Perhaps the 2048 bytes is not enough as well. But there is also a memory problem in the sketch:

The ‘Data_Length’ is 16.
You allocate ‘Data’ with size 16.
Then you use that with a sprintf(), and that sprint() uses far more bytes than 16 bytes, overwriting memory of other things.
Then you upgrade the ‘Data_Length’, but you have only 16 bytes allocated in memory.
The next time, you allocate ‘Data_Length’, but if for example the string is one byte longer, then it is still not big enough.
The return value by sprintf() is without the zero-terminator, so the ‘Data’ array is probably never big enough.

When you overwrite memory that you don’t own, then it is possible that the loop() still runs a few times before the code crashes. It is also possible that it runs fine and a very small change suddenly causes it to crash because a very small change could make the compiler decide to make other binary code.

Keep in mind that even with %02d, the ‘2’ is a minimal size.
It is therefor never okay to make a buffer the exact length for sprintf().
If the sprintf() requires a buffer of 40 bytes, then make a buffer of 60 or 100 bytes or so.
See printf - C++ Reference.
Look for the notes for the “width”, it is always the minimum number of characters to be printed.

Can you get the same result with plain old ‘c’ code instead of sprintf() ? I don’t know how much work it is, but perhaps that is worth a try.
Building the data with char *sgn1 = (lati < 0) ? “-” : “” can be:

if( lati < 0)
{
  strcat( Data, "-");
}

Sorry for the long post. I hope that you understand the memory better with all the extra information.

Koepel:
Why do you have to use an ATtiny ?

Because it's small and doesn't need too much power.

Koepel:
Can you give a list with links to all the libraries ? Perhaps they do go along with SoftwareSerial (very unlikely)

GitHub - mikalhart/TinyGPSPlus: A new, customizable Arduino NMEA parsing library tiny gps++

https://gitlab.thethingsnetwork.de/RainerWieland/chirpnode/blob/29d61b6587ef62dbff8950b07827fda3e97ab0f9/lib/LoRaWAN/LoRaWAN.h LoRaWAN.h

GitHub - Electronza/HPMA115S0: Arduino library for Honeywell HPMA115S0 particle sensor hpma115s0.h

Koepel:
In your sketch you can select the RX of a SoftwareSerial port with SoftwareSerial::listen().
However, you use HPMAserial.begin(9600) in the loop(). You should use listen() to select a port.

thanks I replaced .begin() with .listen() but it doesn't change anything.

Koepel:
A delay of 6 seconds means that you are doing nothing for 6 seconds. Is that okay ?

yes, the sensor needs 6s of delay.

Koepel:
The 'Data_Length' is 16.
You allocate 'Data' with size 16.
Then you use that with a sprintf(), and that sprint() uses far more bytes than 16 bytes, overwriting memory of other things.
Then you upgrade the 'Data_Length', but you have only 16 bytes allocated in memory.
The next time, you allocate 'Data_Length', but if for example the string is one byte longer, then it is still not big enough.
The return value by sprintf() is without the zero-terminator, so the 'Data' array is probably never big enough.

When you overwrite memory that you don't own, then it is possible that the loop() still runs a few times before the code crashes. It is also possible that it runs fine and a very small change suddenly causes it to crash because a very small change could make the compiler decide to make other binary code.

Keep in mind that even with %02d, the '2' is a minimal size.
It is therefor never okay to make a buffer the exact length for sprintf().
If the sprintf() requires a buffer of 40 bytes, then make a buffer of 60 or 100 bytes or so.
See printf - C++ Reference.
Look for the notes for the "width", it is always the minimum number of characters to be printed.

ok but that part of the code works fine so I'll wait before changing it.

If the Arduino is powered from 5V, do remember that the RFM95 is a 3.3V device.

The ATmega328P is also available in small packages.
You could use a "Pro Mini" board. That is a ATmega328P with a few extra components to make it work.

Are you running the ATtiny without crystal ? Then its baudrate is very inaccurate. You might not be able to communicate with the serial devices.
Do you use a decoupling capacitor between VCC and GND ?

I hope you have a serious RFM95 module. For example from Adafruit: Adafruit RFM95W LoRa Radio Transceiver Breakout - 868 or 915 MHz [RadioFruit] : ID 3072 : $19.95 : Adafruit Industries, Unique & fun DIY electronics and kits.
The Adafruit module is compatible with 3.3V and 5V boards.

The "HPMA115S0" library is not so memory hungry.
The "TinyGPSPlus" library does not allocate a lot of memory, but the library itself is so many classes, that adds up.
The LoRaWAN library uses buffers. The PROGMEM buffer does not use sram, but the other buffers do.
I don't know what the "secconfig.h" is.
All together I think this is too much for 1024 byte ram.

It is not allowed to write beyond the borders of an array :stuck_out_tongue_closed_eyes: Not even a single byte may be written to a memory location that you don't own. Not even once in 10 years. In your sketch, you write about 20 or more bytes beyond the array. That is very wrong. You must fix it. I don't care that it worked. It is very wrong.
A bug that causes a memory problem can cause a problem in another section of the sketch. That is typical for a memory problem.

Ask anyone if it is allowed in 'C' and 'C++' to write data beyond the borders of an array, because it worked in the past. They will tell you that it is absolutely forbidden, that you should learn the basics of 'C' and 'C++' or find a new hobby.

alessandro_capresi:
Sadly I can only use tinys for my project.

That seems to be a complety artificial limitation, for a real project.

If a project wont work because the processor does not have enough memory, it wont work.

Are you able to tell us why this project is limited to tinys ?

Koepel:
Are you running the ATtiny without crystal ? Then its baudrate is very inaccurate. You might not be able to communicate with the serial devices.
Do you use a decoupling capacitor between VCC and GND ?

No, I thought the internal 20Mhz internal clock was enough. No I'm not using a decoupling capacitor.

Koepel:
I hope you have a serious RFM95 module. For example from Adafruit: Adafruit RFM95W LoRa Radio Transceiver Breakout - 868 or 915 MHz [RadioFruit] : ID 3072 : $19.95 : Adafruit Industries, Unique & fun DIY electronics and kits.
The Adafruit module is compatible with 3.3V and 5V boards.

That's what I'm using.

Koepel:
The "HPMA115S0" library is not so memory hungry.
The "TinyGPSPlus" library does not allocate a lot of memory, but the library itself is so many classes, that adds up.

I chose to use them because they don't need too much memory.

Koepel:
I don't know what the "secconfig.h" is.

It defines NwkSkey, AppSkey and the node address (in this case node 2) for the LoraWAN transmission to the lab server.

Koepel:
It is not allowed to write beyond the borders of an array :stuck_out_tongue_closed_eyes: Not even a single byte may be written to a memory location that you don't own. Not even once in 10 years. In your sketch, you write about 20 or more bytes beyond the array. That is very wrong. You must fix it. I don't care that it worked. It is very wrong.
A bug that causes a memory problem can cause a problem in another section of the sketch. That is typical for a memory problem.

Ask anyone if it is allowed in 'C' and 'C++' to write data beyond the borders of an array, because it worked in the past. They will tell you that it is absolutely forbidden, that you should learn the basics of 'C' and 'C++' or find a new hobby.

Ok, I think I fixed it.

srnet:
That seems to be a complety artificial limitation, for a real project.

If a project wont work because the processor does not have enough memory, it wont work.

Are you able to tell us why this project is limited to tinys ?

I 100% agree with you, tinys are giving me a lot of problems, but my professor is forcing me to use them. After I tried with several tinys, I ended up using a 1604 (or 1614) because of the larger memory, but you can't find too much about how to use them on internet.

I'm now using another library for the hpma115S0: GitHub - felixgalindo/HPMA115S0: Arduino Library for Honeywell's Particle Sensor (HPMA115S0-XXX) and I have the opposite problem.

I solved the problem by moving hmpa functions (electronza library) inside the while (ssGPS.available()).

#include <hpma115s0.h>
#include <TinyGPS++.h>
#include <SoftwareSerial.h>

#include <Arduino.h>
#include "LoRaWAN.h"
#include "secconfig.h"

/******************************
  DEFINIZIONI MODULO LoRa
 ******************************/
#define DIO0 1 //PIN FISICO 3
#define NSS 0 //PIN FISICO 2
RFM95 rfm(DIO0, NSS);
// Definisce la lunghezza del buffer dati
uint8_t Data_Length = 0x50;
// Definisce il layer LoRaWAN
LoRaWAN LoRa = LoRaWAN(rfm);
// Frame Counter per contare ogni Payload inviato
unsigned int Frame_Counter_Tx = 0;

/******************************
  DEFINIZIONI MODULO GPS
 ******************************/
static const int Rx = 4, Tx = 5;
static const uint32_t GPSBaud = 9600;

// The serial connection to the GPS device
SoftwareSerial ssGPS(Rx, Tx);
float lati, longi;
int pm10d, pm25d;
float pm10f, pm25f;
// The TinyGPS++ object
TinyGPSPlus tinyGPS;

///******************************
// DEFINIZIONI SENSORE PARTICELLARE HPMA115S0
// ******************************/
bool my_status;
float p25;
float p10;
//// Collegamento seriale HPMA115S0
SoftwareSerial HPMAserial(2, 3); // RX, TX dell' HPMA115S0
HPMA115S0 my_hpm(HPMAserial);


void setup( ) {
  Serial.begin(9600);
  //Inizializzo il modulo RFM
  rfm.init();
  /*Inizializza le chiavi di sessione ed applicazione e l'indirizzo del
    nodo per il joining tra Nodo e Network Server attraverso tecnica ABP*/
  LoRa.setKeys(NwkSkey, AppSkey, DevAddr);
  // HPMA115S0 setting

  HPMAserial.begin(9600);
  delay(100);
  // Stop autosend
  my_status = my_hpm.stop_autosend();

  delay(500);
  //my_status = my_hpm.start_measurement();
  delay(1000);


  ssGPS.begin(GPSBaud);
}
void loop( ) {




  ssGPS.listen();
  while (ssGPS.available()) {
    char c = ssGPS.read();
    tinyGPS.encode(c);

    if (tinyGPS.location.isUpdated()) {
      lati = tinyGPS.location.lat();
      longi = tinyGPS.location.lng();
      //        // Dati del sensore HPMA (con media pesata)
      
      HPMAserial.listen();
      //
      float pm10 = 0, pm25 = 0;
      int i = 1;
      //for(i=0;i<10;i++)
      // {
      //
      my_status = my_hpm.read(&p25, &p10);
      delay(6000);
      pm10 = pm10 + p10;
      pm25 = pm25 + p25;
      ////
      //// }
      pm10 = pm10 / i;
      pm25 = pm25 / i;
      pm10d = pm10;
      pm25d = pm25;
      break;

    }
  }
  unsigned char Data[Data_Length];


  //aumento il Frame Counter
  Frame_Counter_Tx++;

  unsigned char Lat[8];
  dtostrf(lati, 6, 4, Lat);
  unsigned char Lng[8];
  dtostrf(longi, 6, 4, Lng);

  //Creo la stringa in cui stampo il buffer Data contenente la stringa di invio
  Data_Length = sprintf(Data, "pm10:%d pm2.5:%d Lat:%s Lng:%s ", pm10d, pm25d, Lat, Lng); 
  //delay(5000);
  /*Send_Data già modificato nella libreria; mancavano alcuni parametri che
    permettevano la trasmissione a certi SF(Spreading Factor) e BW(Larghezze
    di Banda)*/
  //A fine sketch sono riportati i canali con SF e BW modificati nella libreria (sono riportati come riferimento per ricordarsene)
  //txParameter viene scelto uguale a 5 perchè individua la casistica 5 per
  //la trasmissione; quindi trasmetto ad un SF7 con BW di 125 kHz
  int txParameter = 5;
  /*Controllo sulla quantità di pacchetti inviati; considerando i tempi necessari
    a sistemare e recuperare il sistema sotto terra, non volevamo inviare pacchetti fino a che le batterie non si fossero drenate;
    si è quindi inserito un controllo sul contatore, in modo che quando esso arriva a
    2000 la trasmissione si conclude definitivamente.*/
  if (Frame_Counter_Tx < 2000)
  {
    LoRa.Send_Data(Data, Data_Length, Frame_Counter_Tx, txParameter);
    delay(5000);
  }
}