EcoModder.com

EcoModder.com (https://ecomodder.com/forum/)
-   OpenGauge / MPGuino FE computer (https://ecomodder.com/forum/opengauge-mpguino-fe-computer.html)
-   -   I have built an in-dash head unit graphical mpguino display (https://ecomodder.com/forum/showthread.php/i-have-built-dash-head-unit-graphical-mpguino-34418.html)

skybolt 10-12-2016 03:29 PM

I have built an in-dash head unit graphical mpguino display
 
I started by modifying a meelis mpguino, since that’s what I’d purchased. I added a tx out cable and soldered pins to the ISP header so I could deploy new sketches.

I’m less interested in hypermiling (this particular vehicle just isn’t capable), and I needed to change the code, and meelis was kind enough to give me his source code. Since then, I’ve modified it heavily, fixed his drag race subroutine …. But … and a big but – I didn’t want to have the traditional mpguino on my dashboard.

Currently I have an alpine iLX-007, which has a video in port. It’s the video in port that’s important.

I changed the mpguino to output a json file, with the parameters I’m interested in, and some formatting.

Code:

[
{"title":"fuel used 2.00/2.61",
"subtitle":"10.99 remaining, 2.20 e-reserve",
"ranges":[3.5, 12.30, 13.80],
"measures":[ 10.99, 12.29],
"markers":[  1.50]},

{"title":"tank distance 46",
"subtitle":"range 175mi, 35 mi e-reserve, 203mi safe range, 242mi dry range",
"ranges":[100,  264,  297],
"measures":[    159,    126],
"markers":[  32.23]},

{"title":"fuel economy",
"subtitle":"0.00 trip, 17.51 tank, .35 ga@idle, 0.05 mi engine-off cruise",
"ranges":[18,24,30],
"measures":[    0,    17.51],
"markers":[ 21]}]

From there, I went to d3js.org and located one of the sample templates, this one:
(link prohibited by eccomodder) bl.ocks.org mbostock 4061961

I then found a version of the same code that would auto-update the data if the json file changed. Since my Arduino is putting out 4 json files a second (or 10, or 100 if it’s drag-race mode), I needed to have something trigger once there was new data.

Unfortunately, the d3.js chart was designed ONLY to uypdate chart elements, so for some time my data titles and subtitles were static. But now I’ve hacked the code to refresh the titles and subtitles as part of the chart update routine.

In a nutshell, I now have all my mpguino data rendering to a video interface in the stereo head unit. (See attached jpg). (jpg removed by eccomodder, *sigh*)

My raspi code can be found here:
(link disabled by ecomodder) www github com skybolt autoUpdate

The easiest way to grab it from a mac or unix box is:
Code:

git clone www github com skybolt autoUpdate
It comes with a couple of scripts. One script is a python scrip that simulates live data, the other is a script that must be run if you want to have it interface with your arduino. it's called serial_update or something.

Things I have added:
1. BINGO fuel, or the point at which you need to get gas, by adding a reserve amount. My tank is 13.8, but anything below 2.2 gallons is risking running out. So, I added a setting somewhere in the code for that reserve level. This lets me calculate distance to bingo as well as distance to empty. If I get 20 mpg in a 13.8 tank, that’s a total range of 276. But, since I want to get gas while I can, my bingo range is 220. So, I show
a. Distance to dry (empty)
b. Distance to bingo (includes reserve)
c. Bingo range (eg, given your current mpg, what’s your unsafe range? 2.2 * 20 is 44, so my bingo range is 44. That’s 44 scary miles. If you want to dip into the danger zone, the interface will let you see that risk.
d. E-reserve level. This is static (for me 2.2) until you dip below it, in which case it counts down to completely dry.
e. Resets trip every time the engine shuts off.
f. Shows your total tank distance, plus a white line that counts down from your maximum range. When it hits the orange line, you should go get gas.
g. Drag race. Meelis had a function but the timing was strange due to how many loops there are a second. I remove the wait state during drag_wait (waiting to start) and drag_active so you get the most accurate timing.
h. A “serial only” screen. Apparently the LCD takes a little over 100ms to paint. This means a max of 10 updates a second. If you NEVER show the screen (by defaulting to “serial only” and never choosing a new screen, the runtime is more like 10ms, or 100 times per second. This starts at boot and if you EVER show an LCD screen other than the serial only, you’re back to 100 or more ms. I have not tested if the “tank saved, sleep active” message triggers the 100ms paint event, but I think it does.


Gotchas, bugs, etc.

1. Many of my added functions are not written to support metric. Some may be so bad as to simply be bypassed if metric is chosen, as my new routine or call to a new function might simply be embedded in the SAE portion of the function.
2. My code is awful. I’m a terrible developer. In addition to sphaghetti code of the worst kind, I know nothing about memory management. Meelis’ sketch was already pretty good sized, but now with my mods it’s very close to 32k. I’ve managed to shrink it enough to run on an Arduino Micro Pro, which has a strange 31.x k cutoff.
3. I added two new parameters, acceleration speed test and acceleration distance test. I added them at the BEGINNING since I wanted to be able to change them. You may wish to do your 0-60 foot times, well, you can. BUT this means if you flash this sketch to your Arduino, all your parameters will be in the wrong location, by two steps. You could also change the code for those parameters to move them to the end.
4. This only compiles under Arduino 1.0.6 or earlier. Sorry! I want to start over with Tavi’s excellently small code, but it’s different that Meelis so I have to learn how it’s built all over again. I may simply add my functions to Tavi’s code and see if that works. If so I’ll probably break it out into multiple files, because I like that. What’s your opinion, do you like projects with multiple files or one giant 8000 line code block?

skybolt 10-12-2016 03:33 PM

Here is a link to the screen demo. https://s22.postimg.org/filt6aovl/output_HQn0xt.gif

oldtamiyaphile 10-12-2016 08:10 PM

I would love to see a histogram in five minute intervals like the Prius G2 has. :)

t vago 10-13-2016 10:16 AM

I am utterly fascinated with what you did with the MPGuino code. This is some nice work!

As you can't yet post pictures, I'll post this one for you ->
https://s22.postimg.org/filt6aovl/output_HQn0xt.gif
(you are allowed to post pictures once you reach 5 posts)

I am more used to working with 8000-line blocks of code, they're easier to work with than trying to hop from file to file to file.

If you want, I can look at your code and suggest ways to make it leaner.

Ardent 10-13-2016 09:02 PM

Cool, thanks for posting!
Quote:

Originally Posted by t vago (Post 524635)
(you are allowed to post pictures once you reach 5 posts)

(I think that was a hint.)

skybolt 10-14-2016 02:25 PM

This source code is not for the faint of heart ....
 
Quote:

Originally Posted by t vago (Post 524635)

I am more used to working with 8000-line blocks of code, they're easier to work with than trying to hop from file to file to file.

If you want, I can look at your code and suggest ways to make it leaner.


I don't recommend it -- but here you go. https://github.com/skybolt/MPGuinoSerialDev. It's based on code by Meelis and I don't believe he wanted it widely distributed, so I won't say anything if you don't. :thumbup:

Right now I'm pulling apart Tavi's code to see if i can retrofit my own mods to it. His starts at a compiled 19k, but ouch, it's way different than what I'm working with now. My first effort (add my own code blocks to his) didn't work, so now I'm starting over, leaving it all in one file, and see if I can understand what in the hell his code is doing. First off is the serial out, ... if I can't figure out how he's setting it to 19,200, then I'm going to have lots of trouble further on. All my stuff is set to 57600 (it was 115200 but I changed to 57 as a test and left it there).

Once I can understand his code, it should be easy to add my functions to his. If you take a look at the serial_send file in my source code, you will see all the new functions I'm calling, most of which are in the mpguinio.ino file, down near the drag race routines.

skybolt 10-14-2016 02:30 PM

More fun with other people's code
 
Quote:

Originally Posted by skybolt (Post 524765)
if I can't figure out how he's setting it to 19,200, then I'm going to have lots of trouble further on.

No code with string 192, no easy math that maps to 19200 given 20/16MHz change, no identifiable myubbr string. **sigh**

There is a nice line
Code:

const unsigned int myubbr = (unsigned int)(processorSpeed * 625ul / 96ul - 1);
, but altering the 96 to any other multiple I can think of hasn't worked yet. I'm busy doing other things, so I haven't divided 625/96 or 96/625 but I will say then when it was compiled for 20MHz on a 16MHz microcontroller, data read properly at 19200 (or was it 9600?). Point is, the 16/20MHz change and relationship to 625 merits a quick run through a calculator. 625 is .625 which is 5/8 I believe, but 96 to 192 is a 1/2.

skybolt 10-14-2016 02:31 PM

Once I get up to 5 posts I'll show it in the actual car's head unit, rather than on my raspi dev mule.

skybolt 10-14-2016 02:32 PM

Now that I go back and actually read the thread ...
 
Oh! I see you're T-vago! Let's work together on this.

skybolt 10-14-2016 02:59 PM

How does your format() work and where is byte?
 
First off, I notice you're using a different format utility, it looks like it combines both intformat and format, and takes a single parameter, int., which looks like it could be a decimal place parameter.

I tried pasting my old format routine, but it uses a c primitive, 'byte' which is either not represented in c++ or has been replaced by superior functions. Rather than fix my the old intformat routine by re-creating byte, I'd rather learn to use yours.

Problem is, as a functionally illiterate developer, I have to run through a number of steps to figure out how yours works. From

Code:

char * format(unsigned long num, uint8_t ndp)
it looks like in addition to the long (uh oh, that's going to be a problem for calculating negative safe fuel remaining, but that's easy enough to fix), it takes and unsigned short, essentially being a byte. But, there's the ndp ..... [error, unrecognized], so I'd step through passing it a number of values and parameters until it did what I expected.

Also, since format takes a long, what do you do to format strings? I'll admit I haven't yet looked for strings, I'm just working on hijacking your serial out string to send it as json.

At one point I experimented with three functions jsonBegin(), jsonPair() and jsonEnd(), so that I could simply call the start, randomly pass keys at any point in the code, then finish. It ended up being easier to create the serialSend monstrosity where it's all hard coded. I despise hard coding more than anyone, but to get Frankenstein standing I had to abandon some of my principles.

Old format function:
Code:

//--------------------------------------------------------
char* intformat(unsigned long num, byte numlength=6)
{
  static char fBuff[7];  //used by format

  //if number doesn't fit, show 9999 (92 bytes)
  if(numlength==6 && num > 999999999){
    num = 999999000ul;
  }
  else if(numlength==5 && num > 99999999){
    num = 99999000ul;
  }
  else if(numlength==4 && num > 9999999){
    num = 9999000ul;
  }
 
 
  num /= 100;
  // Round off the non-printed value.
  if((num % 10) > 4) num += 10;
  num /= 10;
 
  byte x = numlength;
  fBuff[x] = 0;//end string with \0
  while(x > 0){
    x--;
    fBuff[x]= '0' + (num % 10);//poke the ascii character for the digit.
    num /= 10;
  }
 
  //replace leading 0 with ' '
  boolean leadnulls = true;
  while(x < numlength-1 && leadnulls){
    fBuff[x]=='0' ? fBuff[x]=' ' : leadnulls = false;
    x++;
  }
 
  return fBuff; 
}


skybolt 10-14-2016 03:46 PM

Quote:

Originally Posted by oldtamiyaphile (Post 524599)
I would love to see a histogram in five minute intervals like the Prius G2 has. :)

Interesting. I haven't found the histogram to be very useful ... do you rely on it much or is it just eye candy? (Eye candy is a perfectly legitimate reason by the way).

For this project it would requre adding an additional chart type, and given the power of th d3js.org charts, I don't think a simple 2d histogram would cut it ... but then a histogram would be hard to fit on the screen.

I'm thinking something like this:
https://camo.githubusercontent.com/d...61636b2e706e67, but then maybe a simple line BEHIND the existing graphs might work.

I would have a real challenge fitting it all on one screen.

oldtamiyaphile 10-14-2016 11:41 PM

Quote:

Originally Posted by skybolt (Post 524772)
Interesting. I haven't found the histogram to be very useful ... do you rely on it much or is it just eye candy? (Eye candy is a perfectly legitimate reason by the way).

The Prius only has a single economy readout (which I use to monitor the tank to date), so I use the histogram as my 'trip'. So a bit of both, really.

t vago 10-17-2016 11:36 AM

Quote:

Originally Posted by skybolt (Post 524766)
No code with string 192, no easy math that maps to 19200 given 20/16MHz change, no identifiable myubbr string. **sigh**

There is a nice line
Code:

const unsigned int myubbr = (unsigned int)(processorSpeed * 625ul / 96ul - 1);
, but altering the 96 to any other multiple I can think of hasn't worked yet. I'm busy doing other things, so I haven't divided 625/96 or 96/625 but I will say then when it was compiled for 20MHz on a 16MHz microcontroller, data read properly at 19200 (or was it 9600?). Point is, the 16/20MHz change and relationship to 625 merits a quick run through a calculator. 625 is .625 which is 5/8 I believe, but 96 to 192 is a 1/2.

I think I may have oversimplified that myubbr calcuation. Here's what it should look like:

Code:

const unsigned int myubbr = (unsigned int)((processorSpeed * 62500ul / 9600ul) - 1);
With that line re-written as above, you should be able to replace '9600ul' with any legal baud rate that the Arduino can support.

I'll take a look at your other question when I get the time. Thanks again!

t vago 10-17-2016 12:02 PM

Quote:

Originally Posted by skybolt (Post 524769)
Also, since format takes a long, what do you do to format strings? I'll admit I haven't yet looked for strings, I'm just working on hijacking your serial out string to send it as json.

My format() routine calls my SWEET64 pseudoprocessor with a round function/decimal conversion function to round off the unsigned long number according to the decimal place specified by uint8_t ndp, and then to convert the input from hex into a chain of binary-coded decimal bytes for output.

After that, all it does is expand the output of the SWEET64 binary-coded decimal chain into an output ASCII string.

The string output is left in mBuff1

skybolt 10-18-2016 02:24 PM

There is nothing wrong with your myubbr statement -- this particular board (8 MHz 3.3v) can't push the data at anything faster than 38,400. My other board can be read at 57600 but no faster (on either code base, not just yours).

leave the myubbr as it is. I did have to change the processorSpeed to 8, but that would be expected.

skybolt 10-26-2016 02:12 PM

** whew ** new code block added
 
1 Attachment(s)
OK, T, I've added my json functions to your code base. I see how you're calling tFuelRemaing using the index and dfBitShift (some day you can explain how that works, seems like just an index), but some of the parameters don't behave as I expected

dSizeTankSize always returns 24, but dSizeTankSize doesn't look like a pointer so I haven't tried dereferencing it (and I'm not sure what the syntax would be anyway). Also, I'm not sure of the function of dOffsetTankSize, but any math operations on it cause format() to return "-----" which I see is an overflow - is that also called if the number is too small .. i.e. too large of a negative exponent?

I also see dSizeTankSize and dTankOffsetSize are in program arrays. So, to the questions:

-How do I call tank size?

-Is dOffsetTankSize the position of dSizeTankSize in the array, or is it a value such as "reserve fuel?". I'm hoping it's some kind of fuel reserve, but if not that'll be easy for me to add.

-My json send function is big and it's a lot of simpletx. I am seeing my global variables memory footprint gets larger as I add more the sendJson(). This tells me that simpletx is probably building itself in global variable space. If that's true, would making it into several smaller functions drop the footprint? (My guess is no or I would have tried it). I see when you call simpletx you call it once after constructing a big string. Would that be more efficient?

Attached is my set of updates. I will put it in GitHub once I figure out how to create and upload a repository from the command line -- It's time I learned that instead of going to Github, creating the repository, linking the local repository to the remote location ......... I am perfectly comfortable doing all that but I want to do it all from the command line. My GitHub guy will be here in the next hour or so.

Some random thoughts: the drag race function can be useful for hypermilers. All I have to do is add fuel use per run and people can optimize for least fuel use for 0-30, 0-60, etc., as well as least fuel use for first 30 feet, 60 feet, etc. Accelerating away from a stop is a huge efficiency drain and this will help people track it. I'll be renaming it away from "drag" to something more descriptive.

skybolt 10-28-2016 12:30 PM

How do I call tank size ?
 
I need to call tank size/max tank volume in order to show graphically how much is left. How do I call it using your code?

Also, what is the purpose of dOffsetTankSize? Is it the location of dSizeTankSize in an array, or is it a value (like a reserve fuel value?)

t vago 11-01-2016 05:33 PM

Quote:

Originally Posted by skybolt (Post 525777)
I need to call tank size/max tank volume in order to show graphically how much is left. How do I call it using your code?

As it stands now, you don't. All calculations were meant to be performed using SWEET64, as they take up much less program space. 64-bit unsigned integers are not natively supported in the Arduino library, so the orignal 64-bit calculations were performed by many subroutine calls. As I added more and more functionality, I discovered these calls were taking more more and more flash space, so I came up with SWEET64 to combat this issue.

It might be better to code a SWEET64 pseudocode program to generate the bar graph. It so happens, I have done some work on this program. It was originally meant to drive an analog output signal suitable for input to an analog gauge. This was a requested addition to MPGuino that I had worked to add. I just never got around to testing it.

your sendJSON function is straightforward, and the reason why it consumes more RAM is because simpletx is being called with many otherwise-constant strings that are being stored in RAM. Unlike the RAM buffer used to generate decimal representations of numbers, these strings would never ordinarily change. Moving these strings to flash memory should take care of RAM consumption.

Let me work on the code a bit, and I will post what I come up with here.

Quote:

Originally Posted by skybolt (Post 525777)
Also, what is the purpose of dOffsetTankSize? Is it the location of dSizeTankSize in an array, or is it a value (like a reserve fuel value?)

pOffsetTankSize tells MPGuino what address in the EEPROM to read TankSize. pSizeTankSize is the size, in bits, of TankSize.

skybolt 11-02-2016 10:55 AM

Thanks for the info --- I'm going to take a deep look at SWEET64 to see how it works, and I'm looking forward to seeing your new/changed code.

In the meantime I'm able to determine tank size by adding tFuelUsed to tFuelRemaining ... after I better understand SWEET64 that might not be necessary, but I'll look into it anyway.

Keep in mind that when I calculate the 'graph' or 'bar size' all I am doing is passing an integer or float to the raspberry pi via json, all graphical routines are handled in javascript on the pi side.

I will post both repositories to GitHub, today, and will add more meaningful comments.

skybolt 11-02-2016 12:20 PM

Github repositories
 
Arduino code that creates JSON output:
https://github.com/skybolt/Tavi_165_Mpguino


Web GUI to read arduino data from serial port:
https://github.com/skybolt/autoUpdate

t vago 11-02-2016 06:07 PM

Quote:

Originally Posted by skybolt (Post 526110)
Thanks for the info --- I'm going to take a deep look at SWEET64 to see how it works, and I'm looking forward to seeing your new/changed code.

In the meantime I'm able to determine tank size by adding tFuelUsed to tFuelRemaining ... after I better understand SWEET64 that might not be necessary, but I'll look into it anyway.

Keep in mind that when I calculate the 'graph' or 'bar size' all I am doing is passing an integer or float to the raspberry pi via json, all graphical routines are handled in javascript on the pi side.

I will post both repositories to GitHub, today, and will add more meaningful comments.

Ah... it's becoming clearer to me now.

I have started going through the doJsonOutput code. I added a flashTx function that sends flash-based strings to the serial output port. Cleaned up the exception checking for enabling both serial logging and Json output. Added an EEPROM parameter, bingo fuel, with a default value of 2.2 gallons, which will be as editable as the other EEPROM parameters.

Stay tuned...

Edit: I do not do GitHub, mainly because my worksite does not allow GitHub to be accessible any more. F'in dumb, I know.

t vago 11-08-2016 06:00 PM

I have completed going through the code. I've made some modifications and clean-ups to the base code itself, and added some comments. Also coded some SWEET64 code for figuring out reserve and bingo fuel, distance to empty, and auto-ranging fuel economy graph output.

Going to test the modifications tonight, and hopefully will have something to report by tomorrow.

t vago 11-16-2016 06:10 PM

I am finishing up the drag race functionality. Once that is done, I will test all and report back.

I also added a few more flexibility options for compiling the code, cleaned up the SWEET64 code, simplified a few more things, and added a lot more comments.

skybolt 11-18-2016 09:22 PM

I'd have been happy to do the drag race code --- I'd have followed your syntax for the other functions ... once you have that done I'll make improvements to the drag function as follows:

1. rename to acceleration stats
2. Add second line to display (subtitle) to list fuel statistics for accel. This makes the drag useful to all users, as hypermilers will want to know how much fuel it takes them to reach cruising speed, or how much fuel to cover the first n feet/meters.
3. Update distance to show hundredths of miles in decimal format, then feet. Supress showing feet until n mph/kph
4. Update all json functions to be metric/imperial friendly.
5. Add serial config utility (I will hijack the LCD display routines to mirror that over serial, add a cursor designator so that 802576 with a blinking 2 on the LCD will appear as 80_2_576. This will allow serial config using the three main buttons.

This will take me some time, but will pay dividends in the long term. Also, I have new name for the project, it's obvious but it fits. "MPiGuino"


Question: is there a global call for metric/imperial designator? i.e., can I call "units" and have it return miles, mph vs km, kph. If this is obvious in the code I will find it.

t vago 11-18-2016 10:32 PM

Quote:

Originally Posted by skybolt (Post 527441)
I'd have been happy to do the drag race code --- I'd have followed your syntax for the other functions ... once you have that done I'll make improvements to the drag function as follows:

1. rename to acceleration stats
2. Add second line to display (subtitle) to list fuel statistics for accel. This makes the drag useful to all users, as hypermilers will want to know how much fuel it takes them to reach cruising speed, or how much fuel to cover the first n feet/meters.
3. Update distance to show hundredths of miles in decimal format, then feet. Supress showing feet until n mph/kph
4. Update all json functions to be metric/imperial friendly.
5. Add serial config utility (I will hijack the LCD display routines to mirror that over serial, add a cursor designator so that 802576 with a blinking 2 on the LCD will appear as 80_2_576. This will allow serial config using the three main buttons.

This will take me some time, but will pay dividends in the long term. Also, I have new name for the project, it's obvious but it fits. "MPiGuino"


Question: is there a global call for metric/imperial designator? i.e., can I call "units" and have it return miles, mph vs km, kph. If this is obvious in the code I will find it.

There's no global procedure call for "metric or US". Whenever the driver changes between US or Metric, the code goes through all of the EEPROM parameters that require conversion, and performs the necessary unit conversions. There's an accessible global flag that indicates whether US or metric mode is active, and there's a SWEET64 pseudoinstruction that branches on the state of this global flag. This is mainly used by the prgmFuelEconomy routine, since the US MPG and the metric L/100km are reciprocated.

I figure as long as people can recognize it as having roots with "MPGuino," then I would think that "MPiGuino" should work. The original MPGuino author hasn't been seen in many years now.

1. Yah, "Acceleration Stats" sounds better than "drag race" or "drag function."
2. I'll have to re-write #2 a little bit to measure fuel usage. I had repurposed the fuel measurement elements of the one trip variable set aside for acceleration measurement. Not a big deal, though.
3. Intriguing. I suspect you'll have to do something similar to what I had to do for autoranging the decimal point format function.
4. Has already been done, in a manner of speaking. The code fully supports metric/US measurements, including performing parameter conversions when metric or US measurements are selected.
5. Sounds very doable, but will take a lot of work.

t vago 11-21-2016 08:41 AM

1 Attachment(s)
http://ecomodder.com/forum/attachmen...chmentid=20946

Got a good test, but distance (ranges) are wrong. Looking into it.

More later.

t vago 11-22-2016 12:12 AM

1 Attachment(s)
So, here's the updated code. Haven't gotten the drag race functionality working fully yet. Be sure to configure the #defines to match your Arduino before compiling.

There is a chance that the LCD display may not work. Haven't gotten to test the LCD output with the code re-write.

A doOutputJSON serial speed of 38400 baud appears to keep the CPU loading at around 100%. Not a big deal, as all that really means is that the it'll take slightly over 1/2 a second to complete a display update loop, instead of under 1/2 a second. The injector and VSS measurements are not affected at all.

Do not try to swap between US and metric modes - something apparently broke that causes the code to hang when switching between modes is attempted.

Perhaps increasing the serial buffer size, in conjunction with disabling the LCD output, may drop the CPU loading to well under 100%. I may also look at trying to speed up the SWEET64 primitive operations.

Other code improvements center around making various low-level operations more memory efficient, improving readability of some configuration selections, and a good amount of commenting.

http://ecomodder.com/forum/attachmen...chmentid=20951

Update: The current version of my MPGuino code may be found here.

skybolt 11-26-2016 03:11 PM

Thanks for posting the code! I was logging in to check just that ....

Regarding metric: it crashes while running --- but otherwise takes? So live data is lost .... I'm not really asking, I'll check the behavior ...

I'm fairly certain calling the LCD or writing the JSON output takes longer than any of the primary functions, but I haven't measured yet (it's fairly easy to measure using a quick millis() check) --- If I'm correct, then 38.4 should be plenty fast. Let me run a quick calculation:

json file is about 579 bytes, depending. 38.4bps is 4,800Bps, so about an 8th of a second or 125ms to send the payload. the javascript routines on the Pi are set for 100ms by default, adding a total delay from real-time to .225 per second.

Standard mpguino is .250 seconds ..... so I'd say we are well within operating parameters.

In order to accurately measure acceleration functions the standard loop delays needs to be suppressed. I will have more questions about how to deal with LCD delays ... but all in good time.

Also, I notice you support the tinkerkit LCD ... does the tinkerkit use fewer pins than the standard method of using 8 (+ 1 for contrast and 3 grounds) wires to connect to the LCD? Both Adafruit and Osepp have shields that claim to use 2 pins to connect to soft buttons and LCD. The soft buttons are irrelevant here but using 1 or 2 pins for LCD communication would make it very easy to attach an LCD for programming and detach it for a small footprint.

t vago 11-27-2016 10:52 AM

Quote:

Originally Posted by skybolt (Post 527925)
Regarding metric: it crashes while running --- but otherwise takes? So live data is lost .... I'm not really asking, I'll check the behavior ...

I solved the crashing behavior - it was affecting changing any parameter, not just metric mode. It was due to a bug in the post-parameter-saving display output initialization that I was able to provide a stable workaround for.

I did, however, find another bug that is specific to setting metric mode - the metric parameter conversion routine no longer seems to work at all. That is, distances are still given in miles and fuel quantities are still given in gallons, for instance, when metric mode is active. It's very strange.

Quote:

Originally Posted by skybolt (Post 527925)
I'm fairly certain calling the LCD or writing the JSON output takes longer than any of the primary functions, but I haven't measured yet (it's fairly easy to measure using a quick millis() check) --- If I'm correct, then 38.4 should be plenty fast. Let me run a quick calculation:

json file is about 579 bytes, depending. 38.4bps is 4,800Bps, so about an 8th of a second or 125ms to send the payload. the javascript routines on the Pi are set for 100ms by default, adding a total delay from real-time to .225 per second.

No, the buffered LCD or JSON serial output actually provides fairly quick response. Rather, the nature of SWEET64 being a generalized pseudoprocessor for maximum flexibility, also makes it very slow.

Quote:

Originally Posted by skybolt (Post 527925)
In order to accurately measure acceleration functions the standard loop delays needs to be suppressed. I will have more questions about how to deal with LCD delays ... but all in good time.

The loop delay ( ... delay(500); ... ) was something I got rid of, early on. It offended my tender sensibilities.

Delays are now handled by the primary timer, with a tick of about 1 ms. (I say "about" because a 20 MHz MPGuino actually has a 0.8125 ms tick, and the 16 MHz MPGuino has a 1.024 ms tick). The primary timer cycles down a counter from a preset value to generate a 500 ms wait (which is why processor speed is specified early on in the code). Once the counter goes to zero, the main timer sets a flag bit to signal the main loop to do its display update.

The main loop delay "routine" is now little more than the main loop staring at that flag bit that is set by the primary timer, performing its display updating when that flag sets, clearing that flag, and setting another flag to let the primary timer know it should perform another loop delay.

The accel test function uses a similar mechanism, except that 4 flag bits are used instead of just one. There's a target speed bit, target distance bit, an active bit, and a cancelled bit. This enables the code to sense if many separate conditions arise which would cause the accel test function to cancel, like if the vehicle engine were to stop, or if the vehicle speed dropped back down to zero.

Quote:

Originally Posted by skybolt (Post 527925)
Also, I notice you support the tinkerkit LCD ... does the tinkerkit use fewer pins than the standard method of using 8 (+ 1 for contrast and 3 grounds) wires to connect to the LCD? Both Adafruit and Osepp have shields that claim to use 2 pins to connect to soft buttons and LCD. The soft buttons are irrelevant here but using 1 or 2 pins for LCD communication would make it very easy to attach an LCD for programming and detach it for a small footprint.

Yah, the code is able to turn a Tinkerkit LCD module into a standalone MPGuino. It just needs a few components to interface to the VSS and the fuel injector signals, and a few more components for the switches. Internally, the Tinkerkit LCD module uses the same LCD hookup as the legacy MPGuino (4 bits, plus one for contrast and one for brightness). It uses different pins, though, and my code does not yet support the USB functionality of the Tinkerkit LCD module. I don't think the Tinkerkit LCD module has a serial port, but I remember that it does have an I2C bus.

I am currently using a Arduino Mega2560 as the test platform, which has two serial output ports. One port is being used by a Parallax serial LCD module which uses the serial tx pin, and the other is being used by the JSON output routine. Here's a three-year old video showing the setup.

[youtube]Lw0OA-1BWUI[/youtube]

t vago 11-29-2016 12:19 AM

Found the metric conversion bug - when I re-wrote the conversion routine, forgot to change the denominator from loading into register 3, to loading into register 1. It works now. Derp.

Modified the 64-bit left-shift and right-shift routines, and gained a 31% speed improvement, but at the cost of adding 200 bytes to the compiled output code. This is important because the 64-bit division routine performs about 100 shifts on average, for each division operation (depending on the bit patterns of both the numerator and denominator).

Substantially re-wrote the 64-bit multiplication routine. It now takes up 300 more bytes, but also nets about a 6% speed improvement. Also experimented with substantially re-writing the 64-bit addition routine, and while it netted about a 1% speed improvement, it also added 400 bytes. So, the 64-bit shift routine modifications stay, the 64-bit multiplication improvement stays, but the addition modification goes away.

Might look at re-writing the 64-bit integer to string output formatting routine, because each formatted number currently requires four 64-bit divisions, and the JSON output routine alone outputs at least 32 separate numbers.

skybolt 11-29-2016 11:06 AM

1 Attachment(s)
Thanks. I picked up a mega 2560 today as I couldn't find the relevant flags to get it to compile on any of my other boards. The goal for me is to use a pro mini -- see photog with current hardware, still more miniaturization to come. I was using a Pi 0, but the graphics were jerky -- I might still do something based on the Pi0 but the car interface board/shield will need to be redone for that form factor.

Parallax LCD using a single pin is very attractive for portability -- does that mean the tx pin cannot be used for json output to the Pi? Or is it as straightforward as dedicating another pin for output?

skybolt 11-29-2016 11:20 AM

One more item: You aren't required to pass 3 values into the JSON file, n values from 0 to at least 10. I like to keep it simple, so it's 3 ranges for each, 2 measures for the first graph (total and bingo fuel remaining), 1 measure for the 2nd (reserve range) and 2 for the 3rd (trip, tank). (Measures are the orange bars), and a single marker for each (marker is the floating white bar).

I will adjust the colors, for some reason I left the 3rd measure white, which looks awful.

t vago 11-29-2016 04:55 PM

Quote:

Originally Posted by skybolt (Post 528078)
Thanks. I picked up a mega 2560 today as I couldn't find the relevant flags to get it to compile on any of my other boards. The goal for me is to use a pro mini -- see photog with current hardware, still more miniaturization to come. I was using a Pi 0, but the graphics were jerky -- I might still do something based on the Pi0 but the car interface board/shield will need to be redone for that form factor.

What programming environment are you using for programming your Arduino? I'm using the basic IDE, and telling it what board to use. If you're using anything else, you're going to want to tell your programmer to use an Atmel Mega328p to support your Arduino Pro.

Quote:

Originally Posted by skybolt (Post 528078)
Parallax LCD using a single pin is very attractive for portability -- does that mean the tx pin cannot be used for json output to the Pi? Or is it as straightforward as dedicating another pin for output?

Yah, pretty much. You could use a TWI bus LCD module instead, which would leave your serial TX pin free to use for the JSON output.

Quote:

Originally Posted by skybolt (Post 528080)
One more item: You aren't required to pass 3 values into the JSON file, n values from 0 to at least 10. I like to keep it simple, so it's 3 ranges for each, 2 measures for the first graph (total and bingo fuel remaining), 1 measure for the 2nd (reserve range) and 2 for the 3rd (trip, tank). (Measures are the orange bars), and a single marker for each (marker is the floating white bar).

I will adjust the colors, for some reason I left the 3rd measure white, which looks awful.

It's no big deal for me - I just want the MPGuino code to be able to handle being able to output whatever numbers are required for JSON (or any other conceivable) output.

Turns out that each regular number required five 64-bit divides to turn it into an output string. That'd be twenty 64-bit divides to display the numbers, versus between 4 and 8 for the actual value calculations themselves. Going to test some code tonight or tomorrow which will reduce that down to one 64-bit divide for regular numbers. That should give about an 80% speed improvement. CPU loading has already gone down from almost 102% before yesterday's experimentation, to about 63% after last night. I should hopefully see the CPU loading go down to about 13%. This will enable you to use 4 loops per second, instead of the current 2 loops per second.

skybolt 11-29-2016 08:28 PM

I'm using the standard IDE. The original code I based my mods on was only compatible with v.1.0.6 of the IDE, and I didn't bother to try and update it ... just one more reason I like your code is that it's compatible with 1.6.x

If you set your IDE for an Uno, Pro or Pro Mini you'll see that disabling the define flags for the 2620 and two or three of the serial tags, there's also an error message

Quote:

main.cpp:9709: error: 'PINK' was not declared in this scope
lastPINKstate = PINK; // initialize last PINK state value so as to not erroneously detect a keypress on start
It may be something simple, my guess if you haven't tested it for other boards yet. No biggie, that's what pushed me over the edge to buy the mega. Just stumbled across a new board today, the tinycircuits tiny arduino -- it's 8MHz and basically a pro mini board, but it's stackable using a 2cm x 1cm connector -- it'll help me miniaturize but not lose functionality -- and no need for 20+ pins soldered to the 'duino.

skybolt 12-09-2016 07:51 PM

The code is great! I've got it running, configured it to use standard buttons, (although I now have the parallax button as well, which makes more sense for long-term use and mounting remotely.

I plugged it in to the car today, and ... discovered you are simulating data! I've spent a few minutes checking for a flag like "sim" or "sample". I figured this out when I noticed the data approximates 74.9mph and 2.81gph, whether or not the injector and speed sensor are connected to anything.

How to I turn off simulated data? I'm going to check the individual return routines to see if there's evidence of a flag there.

skybolt 12-12-2016 01:15 PM

ah HA!! Found it

Code:

#ifdef useDebugReadings
        tripArray[(unsigned int)(rawIdx)].collectedData[(unsigned int)(rvInjCycleIdx)] = (t2CyclesPerSecond / loopsPerSecond);
        tripArray[(unsigned int)(rawIdx)].collectedData[(unsigned int)(rvInjOpenCycleIdx)] = ((16391ul * processorSpeed) / (loopsPerSecond * 10));
        tripArray[(unsigned int)(rawIdx)].collectedData[(unsigned int)(rvVSScycleIdx)] = (t2CyclesPerSecond / loopsPerSecond);
        tripArray[(unsigned int)(rawIdx)].collectedData[(unsigned int)(rvInjPulseIdx)] = (20ul / loopsPerSecond);
        tripArray[(unsigned int)(rawIdx)].collectedData[(unsigned int)(rvVSSpulseIdx)] = (208ul / loopsPerSecond);

        timerCommand |= tcWakeUpOnEngine; // tell system timer to wake up the main program
#endif

Updates to follow as I run it with live data and start tinkering with the code.

My first order of action will be to modify the loop time to allow for .01 sec resolution during the acceleration times, then see how badly that affects RPM, then look at putting in an RPM buffer so that dropping to zero takes 3 or 4 shortened loops.

t vago 12-12-2016 02:20 PM

updated to latest code
 
Quote:

Originally Posted by skybolt (Post 529243)
My first order of action will be to modify the loop time to allow for .01 sec resolution during the acceleration times, then see how badly that affects RPM, then look at putting in an RPM buffer so that dropping to zero takes 3 or 4 shortened loops.

Why? All acceleration times already have about 0.001 second resolution, as they stand now.

Do you want to change the overall period of the instantIdx trip variable? That is done by changing the loop time. Currently, it's set to 500 ms, but could be set to 250 ms (4 loops/sec) without overloading the loop. However, going beyond 4 loops/sec will not have the desired effect. Data gathering will not be affected, but your output time resolution will suffer as the loops themselves get overloaded.

I guess I really have no idea what you want to accomplish by adding this RPM buffer.

One more thing - you'll probably want to use the attached code. It's the latest version, but it does have some untested modifications that I have to verify.

Update: The current version of my MPGuino code may be found here.

skybolt 12-13-2016 07:07 PM

Quote:

All acceleration times already have about 0.001 second resolution, as they stand now.
Well, then there's no reason to change the loop time. I knew you took out the delay, but I assumed you used the timer to pause execution of the loop ... I never bothered to look. Based on your statement, the main loop is obviously able to repeat very quickly (some multiple of clock speed, of course).

Under the old code, the acceleration time was checked once per loop, during the doDisplay event which in turn called drag::Update. So, a user could hit 30 mph at, say 2.100 seconds, but checking for the condition could be delayed up to the length of looptime, in this case it could read 2.3499.

To improve accuracy I skipped the delay if drag_running or drag_waiting was true. This could drop the looptime to at most 110ms, or if I'd never called the LCD display routine, 7ms.

This caused its own problem!! Mainly, while waiting to start loop time is between 7 and 110ms. The engine is idling at 1200 rpm or 20 rps, or every 50ms. Our #1 injector fires once every other revolution, or once every 100ms. Also, our injector is open for a very short time, has a 500microsecond "settle" time -- long way of saying rpm flashes to 0 when we have a loop without an injector fire, and higher when we have two fires per loop. This also throws of calculation of instant mpg. It doesn't hurt current/tank mpg since those are based on total distance/fuel ticks, all of which remain accurate - but the displays jump around. Based on my empirical testing, anything below 2300 rpm would show this behavior -- so I started working on logic similar to Flip to EOC data when stopped - after 6 or so loops, basically saying "don't return 0 rpm unless 0 rpm has been reported N number of times.

The RPM buffer is to solve a problem that no longer exists as long as acceleration times are accurate to within .001, or actually accurate to looptime, which I'm guessing is greater than .001 second.

t vago 12-14-2016 12:52 AM

Quote:

Originally Posted by skybolt (Post 529389)
Well, then there's no reason to change the loop time. I knew you took out the delay, but I assumed you used the timer to pause execution of the loop ... I never bothered to look. Based on your statement, the main loop is obviously able to repeat very quickly (some multiple of clock speed, of course).

You are correct, in a sense. The main loop is very much dependent on the system timer, but not in the usual sense.

This is what actually happens:
  1. The main loop senses that the loop time count has expired
  2. The main loop then tells the timer to start another loop time count
  3. The main loop then disables interrupts and performs a transfer of trip data from the raw trip index to the instant trip index. It will then re-enable interrupts in order to continue to read injector events and VSS events and keypresses.
  4. The main loop then processes all remaining trip indexes from the instant index. This is the index that has a period equal to the loop time. Change the loop to 4x a second, the loop time goes to 250 ms. It's normally at 500 ms.
  5. The main loop then performs any display screen updating. This is also when the outputJSON routine is called.
  6. Any other housekeeping is performed at this time.
  7. The main loop will then just sit there and wait for the loop time count to expire. The process then begins all over again.

If the main loop detects a keypress, it will immediately stop waiting for the loop time count to expire, and it will immediately execute the related keypress. This may cause the display screen to update instantaneously, but no trip indexes will be touched.

The instant trip index is somewhat misnamed. It is actually an average trip index, but averaged only over a period of the loop time. To our eyes, it's pretty much instantaneous.

The only time the instant trip index might have a period greater than the loop time, is when either the display output, JSON output, logging output, debugging output (if enabled), or housekeeping functions take longer than the loop time. There is no danger to recording the data itself, as the MPGuino code will still accurately gather injector event and VSS event data, and will still generate reliable current trip, tank trip, and other trip functionality. It just might display instant trip functions in a somewhat peculiar manner.

Quote:

Originally Posted by skybolt (Post 529389)
Under the old code, the acceleration time was checked once per loop, during the doDisplay event which in turn called drag::Update. So, a user could hit 30 mph at, say 2.100 seconds, but checking for the condition could be delayed up to the length of looptime, in this case it could read 2.3499.

Yah, that was one of the first things I noticed with the existing 0.86 MPGuino code, and one of the first things I canned by having the system timer take care of doing timing and having the main loop just poll a status flag to see if the loop had completed. It also allowed instantaneous keypress response as described above.

The next thing I did was to have the main loop truly become the main loop. The 0.86 code shifted to a secondary "main" loop whenever a parameter was being cycled for editing, and then went to a third "main" loop whenever the parameter was actually being edited. Trip data was not gathered during the execution of these loops. This would also result in strange readings once the code execution returned to the main loop.

My code, in contrast, always transfers trip index data per loop, even if parameters are being edited. I am in fact thinking about making the parameter edit section a bit more user friendly by allowing the display and editing of numbers that should have a decimal point in them. They currently show up as "(whatever)*1000" in the parameter edit section.

Quote:

Originally Posted by skybolt (Post 529389)
To improve accuracy I skipped the delay if drag_running or drag_waiting was true. This could drop the looptime to at most 110ms, or if I'd never called the LCD display routine, 7ms.

This caused its own problem!! Mainly, while waiting to start loop time is between 7 and 110ms. The engine is idling at 1200 rpm or 20 rps, or every 50ms. Our #1 injector fires once every other revolution, or once every 100ms. Also, our injector is open for a very short time, has a 500microsecond "settle" time -- long way of saying rpm flashes to 0 when we have a loop without an injector fire, and higher when we have two fires per loop. This also throws of calculation of instant mpg. It doesn't hurt current/tank mpg since those are based on total distance/fuel ticks, all of which remain accurate - but the displays jump around. Based on my empirical testing, anything below 2300 rpm would show this behavior -- so I started working on logic similar to Flip to EOC data when stopped - after 6 or so loops, basically saying "don't return 0 rpm unless 0 rpm has been reported N number of times.

That makes sense now.

Quote:

Originally Posted by skybolt (Post 529389)
The RPM buffer is to solve a problem that no longer exists as long as acceleration times are accurate to within .001, or actually accurate to looptime, which I'm guessing is greater than .001 second.

For acceleration testing, I have created 4 separate trip variables, and they all operate independently of the main loop. The first trip variable records the 0-30 MPH portion, the second records the 0-60 MPH portion, the third trip variable records the 1/4 mile portion, and the fourth trip variable records the true instant speed when the vehicle has traveled 1/4 mile.

The first three trip variables can be treated exactly as the current or tank trip variables. You can get the total period, which corresponds to either 0-30 time, 0-60 time, or 1/4 mile time. You can get total fuel consumed for each of these trips, as well as average speed, distance traveled, average engine speed, fuel economy, fuel consumed, fuel consumption rate, and so on.

The fourth trip variable is special, as it actually does capture the instantaneous vehicle speed when the 1/4 mile marker is reached. This is defined as a single vehicle pulse, its corresponding VSS pulse period measurement corresponds to the number of timer2 ticks per pulse. For 75 MPH using 10000 pulses per mile, this VSS pulse period value would be 1200, and for about 128.6 MPH, this value would be 700. For 119.36 MPH, this would be 754.

Differing values of pulses/mile will change the VSS pulse period value, but you should not see any problem.

All of these acceleration test trip variables operate completely independent of the loop time. All the accel test data is gathered in the background, and as long as the main loop does not disable interrupts for too long (say, for 100 microseconds), then the accel test data will not be corrupted. That's to ensure that consecutive vehicle speed pulses and injector event pulses are correctly captured.

skybolt 12-14-2016 11:35 AM

Quote:

Originally Posted by t vago (Post 529419)
Yah, that was one of the first things I noticed with the existing 0.86 MPGuino code, and one of the first things I canned by having the system timer take care of doing timing and having the main loop just poll a status flag to see if the loop had completed.

Excellent work. I always thought the original code was very ham-fisted.

Quote:

Originally Posted by t vago (Post 529419)
The first three trip variables can be treated exactly as the current or tank trip variables. You can get the total period, which corresponds to either 0-30 time, 0-60 time, or 1/4 mile time.

Are you grabbing both 0-30 and 0-60? I originally had it 0-speed, where speed was configurable as a parameter. Catching both removes the need for a parameter -- the only edge case I can think of is calculating 0-100, which I wouldn't even do. I assume for metric you're taking 0-50kph and 0-100kph, which are typically the values quoted by manufacturers.

I still haven't created a working mpguignio shield (car-to-duino interface, basically two resistors and two zener diodes, with a voltage regulator for full compatibility). Once I've figured out how I messed up so simple a design I'll start in-car testing. My other choice is to simply overwrite my production MPiGuino, but it's a pain to deploy code to that one given the short wires involved, and there are benefits to having a dedicated dev mule that can be hooked up to the car.

Quote:

The fourth trip variable is special, as it actually does capture the instantaneous vehicle speed when the 1/4 mile marker is reached.
Only catching instantaneous at the 1/4 mile causes a problem -- many ecomodders won't keep accelerating all the way to 1/4 mile, nor do I when the 1/4 mile time approaches 100mph and I'm testing on a freeway on-ramp. 0-60 is relatively safe and perfectly legal (in a 60mph zone). We don't want to capture end speed if it's less than maximum speed during the run. I'd originally solved this by taking updating trap_speed with MAX(speed, trap_speed), but as I recall you terminate the run when the speed begins to drop.

I believe your approach will work, but real-world driving may result in eco acceleration runs having minor dips in speed, causing early termination of the run. Once I have in-car testing running I will report. Slow shifting in particular can cause a loss of at least 1 mph. I expect to be able to cobble together, today, a breadboard version of a working car-to-duino interface. I've already verified I can push JSON from the new code to my in-car display unit, so in-car testing will be very full-featured.


All times are GMT -4. The time now is 08:15 AM.

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.
Content Relevant URLs by vBSEO 3.5.2
All content copyright EcoModder.com