NeoHWSerial/interrupts

I have made a "workaround" with:

static void smartDelay(unsigned long ms)
{
  unsigned long start = millis();
  do 
  {
    GPS();
  } while (millis() - start < ms);
}

void loop() 
{ 
  MultiAirTemp();
  smartDelay(1000);
}

I know this is not a solution,but works ツ

pylon:
While this is true, the author implemented a call to allows overriding idleTasks(). This is an empty method that may be overwritten overriden by a own derived class inheriting from OBD2UART.

Fixed.

That is certainly another way to do it. This is similar to providing a yield function (when blocking functions/libraries bother to call it...). You should describe this technique to the OP.

That way you don't have to fidle with interrupts (which is quite dangerous as interrupt context is a bit different)

If by "fiddle" you mean that you wouldn't have to call attachInterrupt and provide an ISR, I guess that's true. However, the GPSisr function shown here is quite safe. If the OP starts adding print statements to the ISR, that would be "dangerous". But the same warnings apply to any ISR (e.g., Pin Change Interrupts, see notes here).

Calling the lengthy method "handle()" inside interrupt context (where other interrupts are blocked) is definitely wrong because during that time the hardware serial interfaces are not handled correctly.

While it's true that other interrupts are blocked during GPSisr (or any ISR), I'll have to ask you to clarify why you think "the hardware serial interfaces are not handled correctly." I have measurements that prove otherwise, but I'd like to know why you make this claim.

First, the "lengthy" handle method is highly optimized, spending an average of 10us per character. This will not disturb other ISRs nor the handling of the hardware serial interface. Even at 115200, it would be ~175us before any characters would be lost. NeoGPS can process an entire sentence in 800us.

Second, handling each character during the ISR actually uses less total CPU time, because the character does not get queued into the RX buffer, polled by loop (or idleTasks) and then dequeued by read(). A secondary benefit is that GPS processing is immune to blocking libraries (there are many), as suggested to the OP elsewhere.

Perhaps your experience is with other libraries? I would not suggest calling other libraries' parse functions during an ISR. Unlike other libraries, NeoGPS completely avoids floating-point operations; they are expensive and should not be performed in any ISR.

I have always been a little surprised that the Arduino community shies away from handling the RX char interrupt. In previous versions of the Arduino core, the serialEvent functions were called from the ISR, but now they are called from the invisible main. Oh well.

but I don't notice improvements

I forgot to mention that you also have to enable interrupt-style processing. Uncomment line 144 in NeoGPS/src/NMEAGPS_cfg.h:

    #define NMEAGPS_INTERRUPT_PROCESSING

I have made a "workaround" with [smartDelay]

Oh please don't do that. smartDelay is really stupid. :stuck_out_tongue: It keeps your sketch from doing anything else for a whole second, and you may not get complete GPS data. You might get a lat/lon but no speed. If you ever see the speed not update (freeze), smartDelay caused that.

You should also reduce the NeoGPS configuration items down to just the items you use. In NeoGPS/src/GPSfix_cfg.h, disable everything but the speed:

//#define GPS_FIX_DATE
//#define GPS_FIX_TIME
//#define GPS_FIX_LOCATION
//#define GPS_FIX_LOCATION_DMS
//#define GPS_FIX_ALTITUDE
#define GPS_FIX_SPEED
//#define GPS_FIX_HEADING
//#define GPS_FIX_SATELLITES
//#define GPS_FIX_HDOP
//#define GPS_FIX_VDOP
//#define GPS_FIX_PDOP
//#define GPS_FIX_LAT_ERR
//#define GPS_FIX_LON_ERR
//#define GPS_FIX_ALT_ERR
//#define GPS_FIX_GEOID_HEIGHT

In NMEAGPS_cfg.h, also disable all sentences except RMC:

//#define NMEAGPS_PARSE_GGA
//#define NMEAGPS_PARSE_GLL
//#define NMEAGPS_PARSE_GSA
//#define NMEAGPS_PARSE_GSV
//#define NMEAGPS_PARSE_GST
#define NMEAGPS_PARSE_RMC
//#define NMEAGPS_PARSE_VTG
//#define NMEAGPS_PARSE_ZDA

#define LAST_SENTENCE_IN_INTERVAL NMEAGPS::NMEA_RMC

You could also disable these items, since you don't need them:

//#define NMEAGPS_STATS

//#define NMEAGPS_COMMA_NEEDED

//#define NMEAGPS_RECOGNIZE_ALL

This will make your sketch quite a bit smaller and faster.

I will try, thanks!

-dev:
Oh please don't do that. smartDelay is really stupid. :stuck_out_tongue: It keeps your sketch from doing anything else for a whole second, and you may not get complete GPS data. You might get a lat/lon but no speed. If you ever see the speed not update (freeze), smartDelay caused that.

I have seen not update (freeze) before I added smartDelay. One moment was 0kph, next was 40kph. Now it freeze for just a fraction of a second, when read OBD.

Actually, I bet this would also work without interrupt-style processing (like your original sketch):

void loop() 
{ 
  //  Print new fixes when they arrive
  if (gps.available( gpsPort )) {
    fix = gps.read();
    displayGPS();
    MultiAirTemp();
  }
}

Delete the GPS() function, because loop can do all of it very simply.

This loop structure waits for the GPS update to arrive (once per second). When a fix is available, the GPS will not send any more data for almost 1 second. That should be plenty of time for the OBD functions to complete (two 300ms timeouts = 600ms maximum delay).

This will update the display once per second with GPS and OBD data, at the same time.

If you want to try this, start with your original sketch and make sure this is commented out in NMEAGPS_cfg.h:

    //#define NMEAGPS_INTERRUPT_PROCESSING  <-- commented out

Re-ordering the loop to run off the GPS updates is a good way to balance the processing load of the Arduino (read more here).

GPS works with 5HZ, will this not affect on update once per second?

GPS works with 5HZ, will this not affect on update once per second?

Maybe.

The polling style (NOT interrupt style) program above might work at 5Hz. Or it may lose GPS updates if the OBD functions take longer than ~180ms.

But I have to ask: Are you sending a command to set the update rate to 5Hz? I don't see that in your sketch.

You should send a command that turns off all sentences except the RMC, but I don't know what kind of GPS you are using.

But why are you running at 5Hz? You can only run the OBD at 1.67Hz (with timeouts). You seemed to be happy using "smartDelay" to slow down the display to 1Hz.

You could run reliably at 5Hz if the GPS is configured correctly (with commands), and if loop is structured correctly. You may need to use the interrupt processing style, depending on the OBD response time (vs. timeout).

Too many questions.

-dev:
Are you sending a command to set the update rate to 5Hz? I don't see that in your sketch.

No, I don't send and I don't need update rate at 5Hz.

-dev:
You should send a command that turns off all sentences except the RMC, but I don't know what kind of GPS you are using.

I am using this GPS. Probably I can do with U-Center?
If I turns off all sentences except the RMC, what impact will it have? What and why is RMC?

-dev:
But why are you running at 5Hz? You can only run the OBD at 1.67Hz (with timeouts). You seemed to be happy using "smartDelay" to slow down the display to 1Hz.

How to configure that will run at 1.67Hz?

I've tried without interrupt-style processing and withoud smartDelay. It works pretty well. But still there are some "wrong" readings from OBD. This was start happening when I included the NeoGPS library. The same is happening with TinyGPS / TinyGPS ++ library.

P.S. Sorry for noobie questions.

What and why is RMC?

The GPS devices sends information in "sentences".

Different sentences contain different fields (see this table). Since you are only using speed, you only need one of the sentences that contain speed: RMC or VTG.

If I turns off all sentences except the RMC, what impact will it have?

To reduce the number of RX char interrupts the Arduino has to handle, tell the GPS to send the fewest sentences possible.

To reduce the time that NeoGPS spends in recognizing different sentences, minimize the NMEAGPS_cfg.h configuration file as described above. This also reduces RAM and program space.

To reduce the time that NeoGPS spends parsing fields that you don't use, minimize the GPSfix_cfg.h configuration file as described above. This also reduces RAM and program space.

To configure the GPS to send only the RMC sentence, add these lines of code to your setup function:

  gps.send_P( &gpsPort, F("PUBX,40,GLL,0,0,0,0,0,0") );
  gps.send_P( &gpsPort, F("PUBX,40,GSV,0,0,0,0,0,0") );
  gps.send_P( &gpsPort, F("PUBX,40,GSA,0,0,0,0,0,0") );
  gps.send_P( &gpsPort, F("PUBX,40,VTG,0,0,0,0,0,0") );
  gps.send_P( &gpsPort, F("PUBX,40,ZDA,0,0,0,0,0,0") );
  gps.send_P( &gpsPort, F("PUBX,40,RMC,0,1,0,0,0,0") );  // <-- only enabled sentence
  gps.send_P( &gpsPort, F("PUBX,40,GGA,0,0,0,0,0,0") );

This matches the config files above.

To be even more efficient, you could try using just the VTG sentence. Make these changes to NMEAGPS_cfg.h:

//#define NMEAGPS_PARSE_GGA
//#define NMEAGPS_PARSE_GLL
//#define NMEAGPS_PARSE_GSA
//#define NMEAGPS_PARSE_GSV
//#define NMEAGPS_PARSE_GST
//#define NMEAGPS_PARSE_RMC
#define NMEAGPS_PARSE_VTG
//#define NMEAGPS_PARSE_ZDA

#define LAST_SENTENCE_IN_INTERVAL NMEAGPS::NMEA_VTG

And send these commands in setup:

  gps.send_P( &gpsPort, F("PUBX,40,GLL,0,0,0,0,0,0") );
  gps.send_P( &gpsPort, F("PUBX,40,GSV,0,0,0,0,0,0") );
  gps.send_P( &gpsPort, F("PUBX,40,GSA,0,0,0,0,0,0") );
  gps.send_P( &gpsPort, F("PUBX,40,VTG,0,1,0,0,0,0") );  // <-- only enabled sentence
  gps.send_P( &gpsPort, F("PUBX,40,ZDA,0,0,0,0,0,0") );
  gps.send_P( &gpsPort, F("PUBX,40,RMC,0,0,0,0,0,0") );
  gps.send_P( &gpsPort, F("PUBX,40,GGA,0,0,0,0,0,0") );

How to configure that will run at 1.67Hz?

Start with these changes at 1Hz, as it will give the maximum amount of time for the OBD functions to complete. It will also coordinate the OBD functions with the GPS quiet time. No RX char interrupts will occur during the OBD functions.

Likewise, the OBD functions will have completed by the time a new GPS update begins (RX char interrupts).

If this works very reliably, post your code and I can comment on changing the display update rate.

But still there are some "wrong" readings from OBD.

Explain this in more detail.

-dev:
To reduce the time that NeoGPS spends in recognizing different sentences, minimize the NMEAGPS_cfg.h configuration file as described above. This also reduces RAM and program space.

To reduce the time that NeoGPS spends parsing fields that you don't use, minimize the GPSfix_cfg.h configuration file as described above. This also reduces RAM and program space.

To be even more efficient, you could try using just the VTG sentence.

I have minimized the NMEAGPS_cfg.h and GPSfix_cfg.h configuration.
If use just RMC/VTG sentence, this will not affect from which satellites receive signal. Galileo or GPS, will use all of them?

-dev:
Explain this in more detail.

LCDs sometimes show values other than the actual oil temperature. For example shows the value of 70 °C, next time show "random" value (negative, or more than 1000), and then next time it's right again 70 °C.

gospod:
If use just RMC/VTG sentence, this will not affect from which satellites receive signal. Galileo or GPS, will use all of them?

Correct. From the M8 Protocol Specification:

u-blox receivers generally try to combine information from
all available GNSS to create the best possible navigation information.

LCDs sometimes show values other than the actual oil temperature. For example shows the value of 70 °C, next time show "random" value (negative, or more than 1000), and then next time it's right again 70 °C.

I would suggest printing the complete response from the "22 19 4F" command. Try something like:

Serial.println( prebralo_bo_sem );  // show the complete response buffer

    int temperatura = (readMultiAir(prebralo_bo_sem) - 40);

Then you can see why the temperature is not calculated correctly from those characters.

I have record how looks reading when is not correct (different of: 62 19 4F ")Dropbox - IMG_5453_2.mov - Simplify your life

I have tried with Serial.println( prebralo_bo_sem ); and didn't work. Of course, I need to use NeoSerial. If I'm not wrong, USB works on (Neo)Serial? So I changed OBD on (Neo)Serial2. When I have changed OBD port from (Neo)Serial to (Neo)Serial2 the problem disappeared.

Question is why?

I have record how looks reading...

Just select the text in the Serial Monitor Window with your mouse, copy it (ctrl-C), and then paste it (ctrl-V) into your post, in code tags. In the Serial Monitor Window, uncheck the Autoscroll box to make it easier to control what you select.

BTW, most people will not download from external sites, especially dropbox. Don't post a screen image of the Serial Monitor window, either.

Of course, I need to use NeoSerial.

Only for the interrupt processing style. You probably don't need to do that, but it's ok to try it.

USB works on (Neo)Serial?

Yes, on the Mega, the USB interface (an FTDI chip) is connected to pins 0 & 1.

When I have changed OBD port from (Neo)Serial to (Neo)Serial2 the problem disappeared. Question is why?

Maybe the OBD adapter does not drive RX pin 0 "hard enough" to override the USB connection. Sending data from the Serial Monitor window would also interfere with receiving the OBD response.

Be sure to post (or attach) your code, so others can benefit from your final solution.

-dev:
BTW, most people will not download from external sites, especially dropbox. Don't post a screen image of the Serial Monitor window, either.

I have record a movie of print on LCD. You don't need to download, you can watch online.

-dev:
Only for the interrupt processing style. You probably don't need to do that, but it's ok to try it.

It means don't need to use NeoHWSerial.h?

-dev:
Maybe the OBD adapter does not drive RX pin 0 "hard enough" to override the USB connection. Sending data from the Serial Monitor window would also interfere with receiving the OBD response.

I guess can not be at the same time OBD connected on Serial(0) and computer on USB?

I have in code sleep function and want that GPS will go to sleep too. Will this work?

gospod:
I have record a movie of print on LCD. You don't need to download, you can watch online.

"Watching" it effectively downloads the MOV file to your computer. Uploading it to YouTube would be a "safe" alternative.

It means don't need to use NeoHWSerial.h?

Yes. There two ways you can process characters (i.e., the "processing style"):

1) By POLLING the Serial.available function in loop (or a function called from loop). This does not require NeoHWSerial, and you should comment out the INTERRUPT define in NMEAGPS_cfg.h.

This is easiest to understand for most people. The only disadvantage is that other functions can cause GPS data to be lost when the RX buffer overflows. As long as other functions take less than 64 character times (5.55ms @ 115200), you will not lose GPS characters. The OBD functions have a timeout of 300ms, so they could block for up to 600ms. I do not know how long they actually take (without timeouts).

However, if you wait for the GPS to quit sending data until the next update period, you can try to perform the OBD functions during this "quiet time". The loop structure in reply #11 will only call MultiAirTemp immediately after a fix is available. That fix is the result of parsing the only sentence that is enabled (according to commands sent during setup). The VTG sentence only contains ~50 characters, sent once per second. It takes about 50*10/115200 = 4.35ms to send that sentence. It will be 993.65ms until the GPS sends another VTG sentence. This is long enough to complete the OBD transactions, even with the 600ms timeouts and LCD updates.

Therefore, the POLLING style should work for an update rate of 1Hz.

2) By handling each character during the INTERRUPT. If you cannot coordinate other functions with the GPS quiet time, or other parts of your sketch take longer than 5.55ms, you must use this processing style. This requires NeoHWSerial, and you must uncomment the INTERRUPT define in NMEAGPS_cfg.h.

The main loop will still check for complete fixes to become available and will then read them. The latest fix will always be available, even if older fixes were not read in time. For example, if the OBD functions took 2 seconds to complete, you may have "lost" one complete fix while that was happening. But the latest one will have been parsed during that time (by the ISR), and will be available immediately, the next time gps.available is checked. NeoGPS holds on to the latest fix until it is read. When a newer fix is available, the previous (unread) fix is discarded. Your sketch will not care that a fix was lost.

I guess can not be at the same time OBD connected on Serial(0) and computer on USB?

Normally, it is ok. You must disconnect 0 to upload new sketches, but you should be able to reconnect it after the upload. If no characters are sent over the USB port to the Arduino, the OBD "should" be able to send data to the Arduino on pin 0.

Doesn't matter -- Serial2 is a better choice, anyway.

I have in code sleep function and want that GPS will go to sleep too. Will this work?

Yes. Just write those bytes to the GPS port:

const uint8_t powerdown[] = { 0xB5, 0x62, 0x02, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4D, 0x3B };

void sleep()
{
  lcd.noBacklight();
  lcd.noDisplay();

  gpsPort.write( powerdown, sizeof(powerdown) );
  gpsPort.flush();   // make sure the bytes are fully transmitted before sleeping

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here  
  sleep_enable();    // enables the sleep bit in the mcucr register
  sleep_mode();      // here the device is actually put to sleep!! 
}

(not tested)

To wake the GPS device, send it a character:

    gpsPort.write( 0x00 ); // anything, really

-dev:
1) By POLLING the Serial.available function in loop (or a function called from loop). This does not require NeoHWSerial, and you should comment out the INTERRUPT define in NMEAGPS_cfg.h.

So, if INTERRUPT is not use, NeoHWSerial does not have the advantage of using it?

-dev:
You must disconnect 0 to upload new sketches, but you should be able to reconnect it after the upload. If no characters are sent over the USB port to the Arduino, the OBD "should" be able to send data to the Arduino on pin 0.

I thought OBD would read on serial(0) and then (at the same time) print it on USB. If this will work? Otherwise, I can not read in Serial Monitor Window.

-dev:

const uint8_t powerdown[] = { 0xB5, 0x62, 0x02, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4D, 0x3B };

It works. Is this the most power saving mode? I don't need hot start, I just want to turn off all I can when car is turned off.
Do you have any idea how to wake the whole system from sleep when I start the car? If is there any software "tweak"? I want to avoid additional cables/connections.

So, if INTERRUPT is not use, NeoHWSerial does not have the advantage of using it?

NeoHWSerial is HardwareSerial, with one addition: attachInterrupt. I started with the Arduino core source code and made this modification. If you don't need attachInterrupt, you might as well use the built-in HardwareSerial (i.e., Serial, Serial1, etc.).

I thought OBD would read on serial(0) and then (at the same time) print it on USB. If this will work? Otherwise, I can not read in Serial Monitor Window.

Yes, that will send debug messages to the OBD device as well as the Serial Monitor window. Because the device requires a certain AT command format, it should ignore your debug messages... unless they start with "AT". Then it tries to interpret your debug messages. :stuck_out_tongue: Don't do that.

You will also see the AT commands in the Serial Monitor window. But maybe you should connect the OBD device to Serial1, Serial2 or Serial3, like I suggested earlier.

You could put the GPS on Serial, if you want. Again, Serial1, Serial2 or Serial3 would be more convenient, because you wouldn't have to disconnect pin 0 everytime you want to upload a new sketch.

It works. Is this the most power saving mode? I don't need hot start, I just want to turn off all I can when car is turned off.

Yes, this is the lowest power mode for the ublox devices: 35uA is a very small drain. Other system components probably use more than this (e.g., regulators' quiescent currents). You'll have to measure the actual usage of your system.

Be sure to read reply #5 and reply #20 in that other thread.

Do you have any idea how to wake the whole system from sleep when I start the car? If is there any software "tweak"? I want to avoid additional cables/connections.

No clue. How would your system know the car is "on"?

Maybe a tilt switch or vibration/sound sensor hooked to a Pin Change Interrupt? Sensing a voltage on something must be done very carefully, because automotive power if very noisy, even dangerous to the components.

The Watch Dog Timer can be used to periodically wake up, but it increases the sleep current.

-dev:
But maybe you should connect the OBD device to Serial1, Serial2 or Serial3, like I suggested earlier.

OBD is connected on Serial2 and wokrs. But I am trying to figure out why it does not work on Serial(0).

-dev:
You could put the GPS on Serial, if you want.

I would like OBD (not GPS), as I already wrote because of the length of the cables.

-dev:
No clue. How would your system know the car is "on"?

Maybe a tilt switch or vibration/sound sensor hooked to a Pin Change Interrupt? Sensing a voltage on something must be done very carefully, because automotive power if very noisy, even dangerous to the components.

The Watch Dog Timer can be used to periodically wake up, but it increases the sleep current.

I was thinking about something like that. It could be watching for changes in motion (gyro in OBD adapter) or increased Voltage (also from OBD adapter - car battery voltage, not arduino VIN).

P.S. What I need to enable if I would like read number of seen satellites? Will this works if fix is not valid?
I have tried:

gps.send_P( &gpsPort, F("PUBX,40,GSA,0,1,0,0,0,0") );

in GPSfix_cfg.h enable

#define GPS_FIX_SATELLITES

in NMEAGPS_cfg.h enable:

#define NMEAGPS_PARSE_GSA 
#define NMEAGPS_PARSE_SATELLITES

I am trying to figure out why it does not work on Serial(0).

Be sure to read my warning: don't print something that looks like an AT command to Serial for debugging. It will also be sent to the OBD device.

What I need to enable if I would like read number of seen satellites?

Look at this table. If you need speed and satellites, the RMC sentence would give you both. That is the only sentence you would need to enable and parse. Enable RMC (and set LAST_SENTENCE to RMC) in NMEAGPS_cfg and add SATELLITES to GPSfix_cfg.h.

All fields have a validity flag, so you can make sure the satellites field is valid with this:

    if (valid.satellites)

This can be valid even if other fields are not valid. The RMC sentence may have a satellite count without a location or speed.

-dev:
Look at this table. If you need speed and satellites, the RMC sentence would give you both. That is the only sentence you would need to enable and parse. Enable RMC (and set LAST_SENTENCE to RMC) in NMEAGPS_cfg and add SATELLITES to GPSfix_cfg.h.

In this table RMC sentence does not provide satellites data. Also until I enable GGA does not show satellites data.