Custom Digital Dash

Sound and electronic gear in your MR2

Moderator: Moderators

Forum rules
This area is dedicated to technical discussions concerning in-car electronics. Stereos, alarms, GPS etc. Please try to spell correctly because this will help people find information later if they are using search functionality. If you need assistance with your car and want to host a spanner day, please use the appropriate section of the forum: http://mr2.org.nz/phpbb3/viewforum.php?f=35 Thank you.
User avatar
NzLHD
Club Member - MR2OCNZ
Posts: 113
Joined: Sun Feb 22, 2015 12:25 pm
Stomping Ground: Auckland
Prime Mover: AW11
First name: David

Custom Digital Dash

Post by NzLHD »

Thought id put up a post to document what I'm doing if anyone else would like to copy, improve on or have any suggestions on how to improve.

I'm Having a go at making a digital dash with Arduino's and and small 1" OLED displays. The goal is to have the gauge information but without having gauge pods/clusters.

Wanting to be able to view 3 different sensors data:
#1 - Manifold Pressure
#2 - Oil Pressure
#3 - WideBand Lambda

Manifold Pressure:
This was easy, Most cars already have a 5v pressure sensor on them, a MAP sensor :) picked a Toyota one up at the wreckers for a couple of bucks.
P/N: 89420-12080
I used a Small pressure gauge and calibrated a scale equation on the Arduino to follow the linear scale that the sensor outputs. This paticular one senses from -8.8Psi to 16.6psi.
Perfect for a small amount of boost. Could always get a 3 bar MAP and adjust the scale if needing to go higher. Added a max boost hold that resets after 1 minute.

Here's what it looks like:
IMG_20171201_074121 (Medium).jpg
IMG_20171201_074121 (Medium).jpg (154.03 KiB) Viewed 8596 times
Oil Pressure is not done yet but it is exactly the same principle as the manifold pressure but a 5v pressure transducer will be used instead of the MAP.

WideBand:
WideBand was interesting, after some research I stumbled across an open source lambda controller by the name of SlC Free, made by a company call 14point7.com in the us.
it was $60 shipped to NZ. It has a LCD screen already and just needs the header pins soldering on. comes in a cool 3D printed case too. It works with the Bosch LSU 4.9.
But what made it a really easy buy was that not only did it have an narrow band output that could be wired into the ECU but it also had a 5v output! perfect for the arduino.

And here's what the OLED will look like for that:
IMG_20171201_073959 (Medium).jpg
IMG_20171201_073959 (Medium).jpg (148.46 KiB) Viewed 8596 times
Had a spare AW dash laying around so test fit one of the screens into it to see how it would look... not too bad :D
IMG_20171121_195933 (Medium).jpg
IMG_20171121_195933 (Medium).jpg (144.25 KiB) Viewed 8596 times
Still building this and plan to install it over the Christmas holidays so any criticism or ideas are appreciated :D
'86 AW10 2ZZ-GTE

User avatar
chief1eye
Club Member - MR2OCNZ
Posts: 527
Joined: Sun Mar 20, 2016 2:33 pm
Stomping Ground: Bay Of Plenty
Prime Mover: SW20
First name: Ross

Re: Custom Digital Dash

Post by chief1eye »

That's looks great. Keen to hear more about it David when we next catch up. Will it be installed in time for the Thundercats event?

mknz
Site Admin - MR2OCNZ
Posts: 1182
Joined: Thu Apr 03, 2014 1:17 am
Stomping Ground: Auckland
Prime Mover: More than one MR2
First name: Michael
Contact:

Re: Custom Digital Dash

Post by mknz »

If you invert which pixels are on and off it can act as a warning flasher when it sees something you don't like. Instead of getting rid of those warning lights where you're putting these OLEDs, connect the to an arduino which would turn the screen on for orange and flash for red and you naturally display text for what went wrong. The only catch case is when the three lights of death come on.

User avatar
Benckj
Forum Moderator - MR2OCNZ
Posts: 7149
Joined: Thu Nov 24, 2005 12:44 pm
Stomping Ground: Otago
Prime Mover: More than one MR2
First name: Jim

Re: Custom Digital Dash

Post by Benckj »

Very impressed with your idea and implementation. Someone in the Megasquirt forum is doing something similar but using a small tablet for display . I like your idea though as it uses seperate sensors and does not eliminate OEM gauges. Would think temperatures would be next on the list for expansion including coolant, IAT and an IC sensor.

Subscribed
Jim Benck
90 rev 1 parts car
98 rev 5 GT- all the mods

User avatar
NzLHD
Club Member - MR2OCNZ
Posts: 113
Joined: Sun Feb 22, 2015 12:25 pm
Stomping Ground: Auckland
Prime Mover: AW11
First name: David

Re: Custom Digital Dash

Post by NzLHD »

IMG_20171226_193959 (Medium).jpg
IMG_20171226_193959 (Medium).jpg (138.97 KiB) Viewed 8482 times
Here's where we are so far, not looking too bad.

Have run a 4 core braided cable from the dash to the ecu and wired in the Lambda controller, she works a treat! (found out that my base tune was staying lean in the top end) :(

Still have to connect up the manifold pressure and oil pressure.

Would highly recommend the SLC Free lambda controller to anyone, cheap compared to some of the AFR gauges out there and looks to be built with quality components. Also it has
narrow band output to wire back into the ecu for closed loop control.
Benckj wrote: Would think temperatures would be next on the list for expansion including coolant, IAT and an IC sensor.
Yes! this could really easily be done with a thermistor and a resistor. For my application the powerfc already has all that on the hand held controller.
mknz wrote:If you invert which pixels are on and off it can act as a warning flasher when it sees something you don't like.

Good idea :) Yeah, ended up moving the 3 to the right side. Two of the spaces were blank and the other was the charge light. Not ideal but there is the old analog voltage meter in the dash.

Don't think she is gunna be running for thundercats unfortunately Ross :(
'86 AW10 2ZZ-GTE

User avatar
NzLHD
Club Member - MR2OCNZ
Posts: 113
Joined: Sun Feb 22, 2015 12:25 pm
Stomping Ground: Auckland
Prime Mover: AW11
First name: David

Re: Custom Digital Dash

Post by NzLHD »

All finished now, Just a little video of it in action.
'86 AW10 2ZZ-GTE

User avatar
GDII
Forum Moderator - MR2OCNZ
Posts: 5739
Joined: Sun Jul 10, 2011 5:13 pm
Stomping Ground: Wellington
Prime Mover: SW20
First name: Phill

Re: Custom Digital Dash

Post by GDII »

Very clever. I like what you've done. Keeps the dashboard clear of big round things and everything in the same place.
1990 SW20 MR2 G-Limited (GEN4 3SGTE Installed)
2000 AE111R Corolla Wagon NZ New Daily
1996 AE101R Corolla Sprint NZ New Selling Soon
1990 EP81 Starlet XL (Sold)
1990 EE90 Corolla XL (Sold)
Instagram https://www.instagram.com/sw20glimited/

User avatar
mickeyduck
Life Member - MR2OCNZ
Posts: 6144
Joined: Mon Apr 25, 2005 11:30 am
Stomping Ground: Auckland
Prime Mover: More than one MR2
First name: Charlie

Re: Custom Digital Dash

Post by mickeyduck »

Yes indeed, very cool. Love the screaming chickens on startup too. 8)
#8^) Charlie the certified Westie
Retired - President 2012 - 2018
Retired - Committee Member 2009 - 2018
Retired - Auckland Area Coordinator 2009 - 2018
Retired - Webmaster, Forum Host & Admin 2010 - 2018 - Now it's mknz

Financial Club Member since 2004 and thanks to *84vvt and co-conspirators, Life Member since April 2017 8)
100+ MR2OCNZ runs and counting... When going hard, good rubber's your best protection against unwanted accidents. Buy good tyres!
When you're nearing the end of the drag-strip and you have no 'chute, you may as well keep your foot to the floor... Live life. There ain't no second pass. :twisted:

Speedhog
Club Member - MR2OCNZ
Posts: 221
Joined: Wed Oct 27, 2010 12:34 am
Stomping Ground: Auckland
Prime Mover: More than one MR2
First name: Rodney

Re: Custom Digital Dash

Post by Speedhog »

I know this is old now, but looking into something similar to this except I don't need the wideband, Just the Oil pressure, Volts, and want to do a coolant Temp.
I take your using a nano per function?
Got any advice or even code(to start with)?

User avatar
Malcolm
Club Member - MR2OCNZ
Posts: 638
Joined: Mon May 24, 2010 8:24 pm
Stomping Ground: Auckland
Prime Mover: AW11
First name: Malcolm

Re: Custom Digital Dash

Post by Malcolm »

Speedhog wrote:I know this is old now, but looking into something similar to this except I don't need the wideband, Just the Oil pressure, Volts, and want to do a coolant Temp.
I take your using a nano per function?
Got any advice or even code(to start with)?
Since you haven't had a response, I thought I'd answer what I can.

The displays look to be the ssd1306, which from what I can tell uses i2c for communication - this means you can conceivably run multiple displays off a single arduino, provided there is a way to change the receive address in the display (I haven't really looked into this).

Oil pressure is easy, just get a sensor such as a Honeywell MLH (or similar) which has a 0.5-4.5V ratiometric output and runs off 5V
Voltage is also easy, just use a 2:1 voltage divider (e.g. 20k and 10k) wired in that order between battery voltage and ground, and measure the voltage between them - multiply that number by 3 and you have battery voltage
For coolant temp use a sensor with a known calibration such as a Bosch 026 and a 1k pull up resistor to 5V. You then just need to calculate resistance using the measured voltage and a voltage divider equation, then use the calibration table to look up the temperature - this doesn't give the greatest accuracy (probably +/- 2 degrees using linear interpolation between table values), but it's a good start and you can always improve it later by calculating a better interpolation using the proper thermistor characteristic equation.

I have code for most of the above if you get stuck, I'll see if I can find it and separate it out (it might be quite buried in a more complex program)

User avatar
Malcolm
Club Member - MR2OCNZ
Posts: 638
Joined: Mon May 24, 2010 8:24 pm
Stomping Ground: Auckland
Prime Mover: AW11
First name: Malcolm

Re: Custom Digital Dash

Post by Malcolm »

Ok I do have some code and hopefully it is well commented enough that you can figure out what's going on. There's a lot of extra functionality going on here (wheel speed input, electronic speedo drive output, output to drive the standard current-based oil pressure gauge via a transistor, CAN data reading etc), and some of that stuff is incomplete or just there for future expansion. Anyway I think you should be able to pick out the various bits that you might need. The i2c communication would need to be changed totally to work with those displays, and I think you'll need to add one or more libraries to get them working

Code: Select all

/*
  MR2 Brains
  Written by Malcolm Graham
  April 2017

  This is intended to read sensors and control some outputs - e.g. speedo, possibly other gauges.
  Data sent over i2c for an LCD display, possibly other uses (e.g. logger?)
  CAN bus - able to send and receive

  Functions performed:
  - converting pulses into wheel speed
  - conversion of voltage to pressure for oil/boost
  - conversion of voltage to temperature for NTC temperature sensors, using lookup table

  ******** To do list **********
  * add oil pressure gauge control
  * add i2c comms
  * finish adding analogue input measurements
  * decoding of CAN data - e.g. lambda
  * Get GPS speed working
  * reduce sample/update rate for temperatures
  * change speed reading to rolling average
  
  ########## HARDWARE ###########
  Analog Inputs:
  A0 - Oil pressure
  A1 - Boost pressure
  A2 - Temp 1
  A3 - Temp 2
  A4&5 - used for i2c
  
  Digital Pins:
  0
  1
  2 - Wheel speed sensor
  3 - CAN interrupt *** possibly relocate to another pin and use as flag rather than interrupt
  4 - Software serial Rx (GPS)
  5 - reserved for possible software serial Tx
  6 - Oil pressure gauge output
  7
  8
  9 - Speed out to instrument cluster
  10 - CAN CS
  11 - CAN MOSI
  12 - CAN MISO
  13 - CAN SCK
  
  
  Incrementing of release number:
  Final digit designates changes only for bug fixes, altering numerical factors etc
  Second digit increment for functionality changes
  First digit increment for major functional changes

  

*/

#include <Wire.h> // for i2c
#include <SPI.h> // for CAN
#include <mcp_can.h>
#include <mcp_can_dfs.h>

// ******** CAN related **********
#define CAN0_INT 3
MCP_CAN CAN0(10);                                    // Set CS pin
byte flagRecv = 0;
byte len = 0;
byte rxBuf[8];
long unsigned int rxId;;
char msgStr[128];

// ******** i2c related **********
byte i2cb0 = 0xFF;
byte i2cb1 = 0xFF;
byte i2cb2 = 0xFF;
byte i2cb3 = 0xFF;
byte i2cb4 = 0xFF;
byte i2cb5 = 0xFF;
byte i2cb6 = 0xFF;

// ******** speed related *********
#define SPD_IN_PIN 2
#define SPD_OUT_PIN 9
const float wheelCirc = 1.913;
const float toothCount = 6;
const float speedoRatio = 1.46;
//const int spdPin = 10;
volatile byte pulseCount = 0;
unsigned long prevTime = 0;
int vehicleSpeed = 50;
float i, j, k, spdFreq;

// ********** analogue inputs **********
#define OIL_P_PIN A1
#define MAP_PIN A0
#define TEMP1_PIN A2
#define TEMP2_PIN A3
#define SERIESRESISTOR 1000 
unsigned int oilP_in, MAP_in;
float oilP_Out, MAP_Out;
unsigned long oilP_offset = 500; // mV
unsigned long MAP_offset = 10; // kPa
float oilP_mult = 0.025; // psi per mV
float MAP_mult = 250; // kPa per mV (this is not correctly scaled yet)
// array with values for temperatures from 0 to 60,
unsigned int tableOhms[] =  {5894, 3797, 2498, 1706, 1177, 834, 596, 436, 324, 243, 187, 144, 113,  89};
unsigned int tableC[] =     {   0,   10,   20,   30,   40,  50,  60,  70,  80,  90, 100, 110, 120, 130};
unsigned int temp1[10] = {0,0,0,0,0,0,0,0,0,0};
unsigned int temp2[10] = {0,0,0,0,0,0,0,0,0,0};
float t, tempReading;
int ii, tempVal, therm1x10, therm2x10;
int kk = 0; 
unsigned long tempTimer = 0;

// ********** outputs **********
#define OIL_GAUGE_PIN 6
float oilDCperPSI = 1.1;
byte oilDCout = 0;


void setup() 
{
    // begin serial monitor data
    Serial.begin(115200);

    // ************ CAN related ***********
    if(CAN0.begin(MCP_ANY, CAN_1000KBPS, MCP_16MHZ) == CAN_OK)
      Serial.println("MCP2515 Initialized Successfully!");
    else
      Serial.println("Error Initializing MCP2515...");

    CAN0.setMode(MCP_NORMAL);
    // attach CAN interrupt
    attachInterrupt(digitalPinToInterrupt(CAN0_INT), MCP2515_ISR, FALLING);

//    Serial.println("Checkpoint #1");
    // ************ i2c related *************
    Wire.begin();

    // ************ speed related ************
    // assign interrupt pin 2 as hall effect input and hold it high
    pinMode(SPD_IN_PIN, INPUT);  
    digitalWrite(SPD_IN_PIN, HIGH);
    pinMode(SPD_OUT_PIN, OUTPUT);
    
    //attach the readspeed function as interrupt 0
    attachInterrupt(digitalPinToInterrupt(SPD_IN_PIN), ReadSpeed, RISING);

    // oil pressure gauge output setup
    pinMode(OIL_GAUGE_PIN, OUTPUT);
    digitalWrite(OIL_GAUGE_PIN, LOW);
}

// ISR CAN - set flag when message received
void MCP2515_ISR()
{
    flagRecv = 1;
}

// ISR for incrementing pulse count
void ReadSpeed()
{
  pulseCount++;
}


void loop() 
{
//  Serial.println("Checkpoint #2");
    
    // #############  CAN data processing   ##################
    if(flagRecv) 
    {                                   // check if get data

        flagRecv = 0;                   // clear flag

        // iterate over all pending messages
        // If either the bus is saturated or the MCU is busy,
        // both RX buffers may be in use and reading a single
        // message does not clear the IRQ conditon.
        while (CAN_MSGAVAIL == CAN0.checkReceive()) 
        {
            // read data,  len: data length, buf: data buf
            CAN0.readMsgBuf(&rxId, &len, msgStr);

/*
            // print the data
            for(int i = 0; i<len; i++)
            {
                Serial.print(buf[i]);Serial.print("\t");
            }
            Serial.println();*/
            if (rxId == 0x0460)
            {
              Serial.print("Correct ID found \n");
              Serial.print("Data byte 6: "); //not sure whether this is actually byte 6!
              Serial.print(msgStr[6]);
              Serial.println();
            }
            
        }
    }

//    Serial.println("Checkpoint #3");
    // #############  i2c data processing   ##################
    // prepare message bytes
    i2cb0 = (byte)vehicleSpeed; // 1kmh/b
    i2cb1 = (byte)(oilP_Out * 2.0); // 0.5psi/b
    i2cb2 = (byte)MAP_Out; // 1kpa/b
    i2cb3 = (byte)(therm1x10 * 0.5); // 0.5degC/b
    i2cb4 = (byte)(therm2x10 * 0.5); // 0.5degC/b

//    Serial.println("Checkpoint #4");
/*
    Wire.beginTransmission(2); // transmit on address 2
    Wire.write(i2cb0); // send byte 0
    Wire.write(i2cb1); // send byte 1
    Wire.write(i2cb2); // send byte 2
    Wire.write(i2cb3); // send byte 3
    Wire.write(i2cb4); // send byte 4
    Wire.endTransmission();*/
    // need to add a counter so messages are only sent at say 10Hz
    
//    // Serial print
//    Serial.println("Data for i2c:");
//    Serial.println(i2cb0);
//    Serial.println(i2cb1);
//    Serial.println(i2cb2);
//    Serial.println(i2cb3);
//    Serial.println(i2cb4);

//    Serial.println("Checkpoint #5");
    
    // #############  Speed data processing   ##################
    if (vehicleSpeed > 10)
    {
      // calculate output frequency for speedo
      spdFreq = speedoRatio * vehicleSpeed;
      tone(SPD_OUT_PIN, (unsigned int)spdFreq);

    } 
    

    // when enough speed input pulses have been counted, begin calculation of speed  
    if (pulseCount >=5 ) 
    {

      // calculation is as follows: speed (kmh) = 3.6 * distance (m) / time (s) ---- however this is broken into several steps below, to be unified later after full debugging
      i = pulseCount/toothCount;
      j = (millis()-prevTime);
      k = j/1000;
    
      vehicleSpeed = (unsigned int)(3.6 * ((wheelCirc * i) / k));

      // set current time as prevTime for calculation of next 3-pulse period
      prevTime = millis();
      pulseCount = 0;
    }
    Serial.print("Speed: ");
    Serial.println(vehicleSpeed);

    // #############  Temperature processing   ##################

    // only check temperatures every 500ms (gives 1Hz sample rate per sensor)
    if ((millis() - tempTimer) > 500)
    { 
      // store time of current sample/calculation
      tempTimer = millis();
      
      //kk is index variable for thermistor number
      if (kk == 0)
      {
        tempReading = analogRead(TEMP1_PIN);
      }
      else
      {
        tempReading = analogRead(TEMP2_PIN);
      }
   
//        Serial.print("Analog reading "); 
//        Serial.println(tempReading);
   
      // convert the value to resistance
      tempReading = (1023 / tempReading)  - 1;
      tempReading = SERIESRESISTOR / tempReading;
      // create integer of resistance
      tempVal = (int)tempReading;
//        Serial.print("Thermistor resistance "); 
//        Serial.println(tempVal);
    
      // i is index variable for table lookup
      ii = 0;
      while (tempVal < tableOhms[ii] && ii < sizeof(tableOhms))
      {
        // Serial.println(*tableC[ii,1]);
        ii++;
      }
    
      // interpolation calculation, define variables to tidy up calc
      int ohmsA = tableOhms[ii-1];
      int ohmsB = tableOhms[ii];
      int tempA = tableC[ii-1];
      int tempB = tableC[ii];
   
      t = ((tempVal-ohmsA) * ( (float)(tempB-tempA) / (float)(ohmsB-ohmsA) )) + tempA;
    
      // rolling average calculation, left shift each stored value
      for (int jj = 0; jj < 10; jj++)
      {
        if (kk == 0)
        {
          temp1[jj] = temp1[jj+1];
        }
        else
        {
          temp2[jj] = temp2[jj+1];
        }
      }
    
      // store latest temperature reading and calculate rolling average
      if (kk ==0)
      {
        temp1[9] = (t*100);
        unsigned int t_sum = 0;
        for (int jj = 0; jj < 10; jj++)
        {
          t_sum = t_sum + temp1[jj];
        }
        t = (float)t_sum/1000;
      
        therm1x10 = t*10;
//        Serial.print("Thermistor temperatures: 1 = "); 
//        Serial.print(therm1x10);
      }
      else
      {
        temp2[9] = (t*100);
        unsigned int t_sum = 0;
        for (int jj = 0; jj < 10; jj++)
        {
          t_sum = t_sum + temp2[jj];
        }
        t = (float)t_sum/1000;
        therm2x10 = t*10;
        //Serial.print(", 2 = "); 
        //Serial.print(therm2x10);
        //Serial.println(" degC");
      }
  
      // increment k, then make sure it is less than 2
      kk++;
      kk = kk % 2;
    }

    // #############  Pressure processing   ##################
    oilP_in = analogRead(OIL_P_PIN);
    oilP_Out = (oilP_in * (5000 / 1023.0) - oilP_offset) * oilP_mult; // convert to mV, subtract offset, multiply by scaler

    MAP_in = analogRead(MAP_PIN);
    MAP_Out = (MAP_in / 1023.0) * MAP_mult + MAP_offset;

    Serial.print("Oil P: ");
    Serial.print(oilP_Out);
    Serial.print(" psi, ");
    Serial.print(oilP_in * 5);
    Serial.println("mV (approx)");
    Serial.print("MAP: ");
    Serial.print(MAP_Out);
    Serial.print(" kPa, ");
    Serial.print(MAP_in * 5);
    Serial.println("mV (approx)");

   
    // #############  Oil pressure gauge control   ##################
    oilDCout = (byte)(oilP_Out * oilDCperPSI);
    analogWrite(OIL_GAUGE_PIN,oilDCout);

}

Speedhog
Club Member - MR2OCNZ
Posts: 221
Joined: Wed Oct 27, 2010 12:34 am
Stomping Ground: Auckland
Prime Mover: More than one MR2
First name: Rodney

Re: Custom Digital Dash

Post by Speedhog »

[quote="Malcolm"]Ok I do have some code and hopefully it is well commented enough that you can figure out what's going on. There's a lot of extra functionality going on here (wheel speed input, electronic speedo drive output, output to drive the standard current-based oil pressure gauge via a transistor, CAN data reading etc), and some of that stuff is incomplete or just there for future expansion. Anyway I think you should be able to pick out the various bits that you might need. The i2c communication would need to be changed totally to work with those displays, and I think you'll need to add one or more libraries to get them working

Your the Man Malc!
I have been doing some research in the background, and building the units isn't a massive problem for me, but the coding is all Greek. I better start learning tho, ha!

User avatar
Malcolm
Club Member - MR2OCNZ
Posts: 638
Joined: Mon May 24, 2010 8:24 pm
Stomping Ground: Auckland
Prime Mover: AW11
First name: Malcolm

Re: Custom Digital Dash

Post by Malcolm »

the awesome thing with Arduino stuff is it's all so cheap that it's very easy to throw some stuff together to test out. The Arduino IDE isn't the greatest for debugging code (given it has virtually no debugging capability), but using the serial monitor and just printing stuff to serial through the program works pretty well (you can see in my code the "checkpoints" that I print to serial, just for checking for bits of code that might be hanging up or confirming whether functions are getting called etc). You can also figure out nearly everything you need just from the examples that come with most libraries

User avatar
NzLHD
Club Member - MR2OCNZ
Posts: 113
Joined: Sun Feb 22, 2015 12:25 pm
Stomping Ground: Auckland
Prime Mover: AW11
First name: David

Re: Custom Digital Dash

Post by NzLHD »

Apologies for not seeing the post come up again.

Here's a link to the arduino files i used in the video. I didn't really write them, just sort of hashed codes together from stuff online.

https://drive.google.com/file/d/1o1U8cF ... sp=sharing
'86 AW10 2ZZ-GTE

User avatar
Malcolm
Club Member - MR2OCNZ
Posts: 638
Joined: Mon May 24, 2010 8:24 pm
Stomping Ground: Auckland
Prime Mover: AW11
First name: Malcolm

Re: Custom Digital Dash

Post by Malcolm »

I wondered how the images were drawn - it really is just a monochrome bitmap embedded directly in code! Is there some website where you found the logos you used for things like the snail/boost image, or did you use some software to generate the array?

Post Reply