1. Do not share user accounts! Any account that is shared by another person will be blocked and closed. This means: we will close not only the account that is shared, but also the main account of the user who uses another person's account. We have the ability to detect account sharing, so please do not try to cheat the system. This action will take place on 04/18/2023. Read all forum rules.
    Dismiss Notice
  2. For downloading SimTools plugins you need a Download Package. Get it with virtual coins that you receive for forum activity or Buy Download Package - We have a zero Spam tolerance so read our forum rules first.

    Buy Now a Download Plan!
  3. Do not try to cheat our system and do not post an unnecessary amount of useless posts only to earn credits here. We have a zero spam tolerance policy and this will cause a ban of your user account. Otherwise we wish you a pleasant stay here! Read the forum rules
  4. We have a few rules which you need to read and accept before posting anything here! Following these rules will keep the forum clean and your stay pleasant. Do not follow these rules can lead to permanent exclusion from this website: Read the forum rules.
    Are you a company? Read our company rules

Showroom Arduino custom DIY dashboard

Discussion in 'DIY peripherals' started by Pedro Melim, May 29, 2015.

  1. Pedro Melim

    Pedro Melim Smoother Racer

    Joined:
    Nov 12, 2014
    Messages:
    13
    Occupation:
    Programmer
    Location:
    Funchal
    Balance:
    710Coins
    Ratings:
    +8 / 0 / -0
    My Motion Simulator:
    Arduino
    WIP

    Last year i decided to get back again into SimRacing, and one of the things i decided to go for was a dashboard.
    After looking into comercial ones, i found that at the price they were asking i could go for the arduino way and save some money.

    After some time researching, instead of going the way people usually go (TM1638) i went for a more modular approach.

    Part list:

    (Electronics)
    Arduino Uno (official, bought for testing and for the case of the nano not coming with bootloader)
    Arduino Nano (cheap ones, not official)
    2 digit 7 segment module
    8 digit 7 segment module
    2 x neopixel sticks (8 RGB LEDs each)
    1 x rotary encoder module

    (Other)
    Wooden Box
    Tinted Acrylic
    Screws

    Both the 2 digit and the 8 digit module are connected together, since they are MAX7219 they can become 1 by connecting each other.
    The neopixel sticks are alone on that matter as is the knob module.

    First started testing arduino so i could see how it worked, and then envolved to make it show data from rfactor.
    Even after i get it to work more like i wanted, tried some different directions, mainly with python (not done yet with it) as i wanted to show some optional data (trying to make real-time delta show up,have some breaktroughts on it already)

    After i got it +/- working went ahead and made it fit into the wooden box, first without the acrylic, but now with it. All its left on the aspect of the dashboard is black matte paint, inside and outside :)

    Right now its working like this:

    - 1 digit of the 2 digit module for the gears;
    - 8 digit module, half for the RPM (ex: instead of 10000 rpm it shows 10.00, so i can fit it in 4 digits), the other half for speed;
    - Neopixel sticks are for the shift lights, atm they are with a fixed color but i plan on doing some coding to make it configurable on the go, with the knob module;
    - The knob module as i said above, will help on user configuration (will have a menu for it) but also on changing dashboard data, for example, one with RPM and speed, other with oil and water temp, etc etc

    One of the digits from the 2 digit module will eventually have some info, will test more things on that later.

    ATM im using a software made by a member of another comunity.

    Have it fully working with rFactor, still not with everything i want, there are alot of ideas on my head for it but firstly would need some other software to trasmit data to the arduino, and thats where SimTools comes in with the many plugins availabe. That way i can have it working on evey title i play, and will eventually play.

    I didnt have any enough coins to buy them, but since now i have will start soon working on making the dashboard usable with GameDash.

    Will leave you with some pictures and a video showing the dashboard working. Will add more when i do some more progress. Enjoy!



    PS: This is one of my DIY, have modded the G27 wheel as well as you migh see on the video, but will leave it for other thread, as its a seperated thing

    Attached Files:

    • Like Like x 5
    • Creative Creative x 1
  2. noorbeast

    noorbeast VR Tassie Devil Staff Member Moderator Race Director

    Joined:
    Jul 13, 2014
    Messages:
    20,535
    Occupation:
    Innovative tech specialist for NGOs
    Location:
    St Helens, Tasmania, Australia
    Balance:
    145,034Coins
    Ratings:
    +10,776 / 52 / -2
    My Motion Simulator:
    3DOF, DC motor, JRK
    Great job @Pedro Melim, I will be looking forward to your future updates and use with GameDash.

    What code are you running your dash with?
  3. RacingMat

    RacingMat Well-Known Member Gold Contributor

    Joined:
    Feb 22, 2013
    Messages:
    2,234
    Location:
    Marseille - FRANCE
    Balance:
    20,890Coins
    Ratings:
    +2,083 / 21 / -2
    My Motion Simulator:
    2DOF, DC motor, Arduino
    Thanks for sharing your work with pictures and video!
  4. Pedro Melim

    Pedro Melim Smoother Racer

    Joined:
    Nov 12, 2014
    Messages:
    13
    Occupation:
    Programmer
    Location:
    Funchal
    Balance:
    710Coins
    Ratings:
    +8 / 0 / -0
    My Motion Simulator:
    Arduino
    @noorbeast will share my code later...but as i said, its only working with another software, was intending on sharing when i started with simtools :)
    Can do both if you guys want.
  5. noorbeast

    noorbeast VR Tassie Devil Staff Member Moderator Race Director

    Joined:
    Jul 13, 2014
    Messages:
    20,535
    Occupation:
    Innovative tech specialist for NGOs
    Location:
    St Helens, Tasmania, Australia
    Balance:
    145,034Coins
    Ratings:
    +10,776 / 52 / -2
    My Motion Simulator:
    3DOF, DC motor, JRK
  6. Cluclu

    Cluclu Member

    Joined:
    May 26, 2015
    Messages:
    56
    Location:
    France
    Balance:
    443Coins
    Ratings:
    +28 / 0 / -0
    My Motion Simulator:
    2DOF, Arduino
    Great job

    It's possible to look the Arduino code ?
  7. Pedro Melim

    Pedro Melim Smoother Racer

    Joined:
    Nov 12, 2014
    Messages:
    13
    Occupation:
    Programmer
    Location:
    Funchal
    Balance:
    710Coins
    Ratings:
    +8 / 0 / -0
    My Motion Simulator:
    Arduino
    Tks @Cluclu ...yes its possible. As soon as i can, i will share it here.

    Worth to mention again that it wont work with SimTools atm as i have not started doing it for SimTools. Will get to that in the next days/week ;)
    • Like Like x 1
  8. Pedro Melim

    Pedro Melim Smoother Racer

    Joined:
    Nov 12, 2014
    Messages:
    13
    Occupation:
    Programmer
    Location:
    Funchal
    Balance:
    710Coins
    Ratings:
    +8 / 0 / -0
    My Motion Simulator:
    Arduino
    Started to test it with simtools already (based on this guide http://www.xsimulator.net/community/marketplace/arduino-and-tm1638-for-simple-dashboard.79/) but im having a bit of a bash with this, once i start up rfactor, it keeps beeping over and over again on windows, like an alert, but i have no messages or anything. I know its from GameDash as when i close it stops. Anyone got an idea on what migh causing the problem?

    Also my dash is flashing with some "weird data"...its showing mine but on the middle of it shows something else...iand its kinda out of sync it seems

    By the way, here is the current code, working with simtools (with the problems mention above):

    Code:
    #include <LedControl.h>
    #include <Adafruit_NeoPixel.h>
    #include <Encoder.h>
    
    // inputs: DIN pin, CLK pin, LOAD pin. number of chips
    LedControl mydisplay = LedControl(12, 11, 10, 2);
    Adafruit_NeoPixel strip = Adafruit_NeoPixel(16, 5, NEO_GRB + NEO_KHZ800); //Standered Strip function
    Encoder knob(3, 2);
    
    boolean parpadea  = false;
    long previousMillis = 0;
    long interval = 50;
    unsigned long currentMillis;
    int Mode_Buttons;
    int Mode_Buttons_ant;
    unsigned int Gear;
    unsigned int Speed;
    unsigned int Rpm;
    unsigned int Fuel;
    int ultima_vuelta_tiempo = -1;
    int ultima_vuelta_tiempo_m = -1;
    int PitLine = 0;
    int Agua;
    int Oil;
    char wrspeed[4];
    char wrfuel[4];
    char wrrpm[5];
    String waspeed;
    String wafuel;
    String warpm;
    int rpm_maxima = 15000;
    int i;
    int wnx;
    long newposition;
    long knobposition = 0;
    unsigned long currentTime;
    unsigned long loopTime;
    char rev_color[] = "gggrrrbbbbrrrggg";
    uint32_t r = strip.Color(255, 0, 0);
    uint32_t g = strip.Color(0, 255, 0);
    uint32_t b = strip.Color(0, 0, 255);
    uint32_t empty = strip.Color(0, 0, 0);
    
    void setup()
    {
      Serial.begin(115200);
      strip.begin();
      strip.show(); // Initialize all pixels to 'off'
      strip.setBrightness(64);
      mydisplay.shutdown(0, false);  // turns on display
      mydisplay.setIntensity(0, 5); // 15 = brightest
      mydisplay.shutdown(1, false);  // turns on display
      mydisplay.setIntensity(1, 5); // 15 = brightest
      Mode_Buttons = 1;
      currentTime = millis();
      loopTime = currentTime;
    }
    void loop()
    {
      //while(Serial.available() > 1)
      //{
        //char comandos = Serial.read();
       //if (comandos == 'S') Speed = Serial.parseInt();
       //if (comandos == 'R') Rpm = Serial.parseInt();
       //if(comandos == 'G') Gear = Serial.parseInt();
       //if(comandos == 'F') Fuel = Serial.parseInt();
       //if(comandos == 'M') rpm_maxima = Serial.parseInt();
      //}
      // read input data
      readSerialData();
      Read_encoder();
      switch(Mode_Buttons)
      {
      case 1: // Display mode 1
        Read_Gear();
        Read_Speed();
        Read_Rpm();
        rpm_luz(Rpm);
        break;
      case 2: //Display mode 2
        Read_Gear();
        Read_Speed();
        Read_Fuel();
        rpm_luz(Rpm);
        break;
      }
    }
    
            ////////////////////////////////////////////////////////////////////////////////
            // Procedure: read serial data
            ////////////////////////////////////////////////////////////////////////////////
            void readSerialData()
            ////////////////////////////////////////////////////////////////////////////////
            {
              byte Data[5]={'0','0','0','0','0'};
    
              // parse the buffer
              if (Serial.available()>4){
                Data[0]=Serial.read();                   // read command
                if (Data[0]=='M'){                       // if rpm data
                  Data[1]=Serial.read();                 // store high byte
                  Data[2]=Serial.read();                 //
                  Data[3]=Serial.read();                 //
                  Data[4]=Serial.read();                 // store low byte
                  Rpm = NormalizeData(Data);   
                  if (Serial.available()>4){
                    Data[0]=Serial.read();               // re-read command
                  }
                }
                if (Data[0] == 'S' ){                    // if speed data
                  Data[1]=Serial.read();                 // store high byte
                  Data[2]=Serial.read();                 //
                  Data[3]=Serial.read();                 //
                  Data[4]=Serial.read();                 // store low byte
                  Speed = NormalizeData(Data);
                  if (Serial.available()>4){
                    Data[0]=Serial.read();               // re-read command
                  }
                }
                if (Data[0] == 'G' ){                    // if gear data
                  Data[1]=Serial.read();                 // store high byte
                  Data[2]=Serial.read();                 //
                  Data[3]=Serial.read();                 //
                  Data[4]=Serial.read();                 // store low byte
                  Gear = NormalizeData(Data);
                  if (Serial.available()>4){
                    Data[0]=Serial.read();               // re-read command
                  }
                }                    
                if (Data[0] == 'F' ){                    // if fuel data
                  Data[1]=Serial.read();                 // store high byte
                  Data[2]=Serial.read();                 //
                  Data[3]=Serial.read();                 //
                  Data[4]=Serial.read();                 // store low byte
                  Fuel = NormalizeData(Data);
                  if (Serial.available()>4){
                    Data[0]=Serial.read();               // re-read command
                  }
                }                                
                if (Data[0] == 'Z' ){                      // if Shutdown - Output
                  Serial.flush();
                }          
              }
              if (Serial.available()>60) Serial.flush();
            }
           
            ////////////////////////////////////////////////////////////////////////////////
            // Function: convert Hex to Dec
            ////////////////////////////////////////////////////////////////////////////////
            unsigned int NormalizeData(byte x[5])
            ////////////////////////////////////////////////////////////////////////////////
            {
            unsigned int result;
    
            for (int i=1; i<5; i++)
            {
              if (x[i]>47 && x[i]<58 ){   //for xA to xF
                x[i]=x[i]-48;
              }                    
              if (  x[i]>64 && x[i]<71 ){ //for x0 to x9
                x[i]=x[i]-55;            
              }
            } 
            result = (x[1]*16*16*16+x[2]*16*16+x[3]*16+x[4]);
            return result;
            }
    
    //Read encoder
    void Read_encoder()
    {
      newposition = knob.read();
      currentTime = millis();
      if(currentTime >= (loopTime + 400))
      {
        loopTime = currentTime;  // Updates loopTime
        if(newposition != knobposition)
        {
          if(newposition < knobposition)
          {
            if(Mode_Buttons == 1)
            {
              Mode_Buttons = 2;
            }
            else
            {
              Mode_Buttons = Mode_Buttons - 1;
            }
          }
          if(newposition > knobposition)
          {
            if(Mode_Buttons == 2)
            {
              Mode_Buttons = 1;
            }
            else
            {
              Mode_Buttons = Mode_Buttons + 1;
            }
          }
          knobposition = newposition;
          Mode_Buttons_ant = Mode_Buttons;
        }
      }
    }
    // Fuel reading and display
    void Read_Fuel()
    {
      wafuel = String(Fuel);
      wafuel.toCharArray(wrfuel,4);
      if(Fuel >= 100)
      {
        mydisplay.setChar(0, 4, wrfuel[2], false);
        mydisplay.setChar(0, 5, wrfuel[1], false);
        mydisplay.setChar(0, 6, wrfuel[0], false);
      }
      if(Fuel >= 10 && Fuel < 100)
      {
        mydisplay.setChar(0, 4, wrfuel[1], false);
        mydisplay.setChar(0, 5, wrfuel[0], false);
        mydisplay.setChar(0, 6, ' ', false);
      }
      if(Fuel >= 0 && Fuel <10)
      {
        mydisplay.setChar(0, 4, wrfuel[0], false);
        mydisplay.setChar(0, 5, ' ', false);
        mydisplay.setChar(0, 6, ' ', false);
      }
      mydisplay.setChar(0, 7, 'F', true);
    }
    
    // Gear reading and display
    void Read_Gear()
    {
      switch(Gear)
      {
      case 0:
        mydisplay.setRow(1,1,B01110110);
        break;
      case -1:
        mydisplay.setRow(1,1,B00000101);
        break;
      default:
        mydisplay.setDigit(1, 1, Gear, false);
        break;
      }
    }
    
    // Speed reading and display
    void Read_Speed()
    {
      waspeed = String(Speed);
      waspeed.toCharArray(wrspeed,4);
      if(Speed >= 100)
      {
        mydisplay.setChar(0, 0, wrspeed[2], false);
        mydisplay.setChar(0, 1, wrspeed[1], false);
        mydisplay.setChar(0, 2, wrspeed[0], false);
      }
      if(Speed >= 10 && Speed < 100)
      {
        mydisplay.setChar(0, 0, wrspeed[1], false);
        mydisplay.setChar(0, 1, wrspeed[0], false);
        mydisplay.setChar(0, 2, ' ', false);
      }
      if(Speed >= 0 && Speed <10)
      {
        mydisplay.setChar(0, 0, wrspeed[0], false);
        mydisplay.setChar(0, 1, ' ', false);
        mydisplay.setChar(0, 2, ' ', false);
      }
    }
    
    // RPM Reading and display
    void Read_Rpm()
    {
      warpm = String(Rpm/10);
      warpm.toCharArray(wrrpm,5);
      switch(warpm.length())
      {
      case 1:
        mydisplay.setChar(0, 4, wrrpm[0], false);
        mydisplay.setChar(0, 5, 0, false);
        mydisplay.setChar(0, 6, 0, true);
        mydisplay.setChar(0, 7, ' ', false);
        break;
      case 2:
        mydisplay.setChar(0, 4, wrrpm[1], false);
        mydisplay.setChar(0, 5, wrrpm[0], false);
        mydisplay.setChar(0, 6, 0, true);
        mydisplay.setChar(0, 7, ' ', false);
        break;
      case 3:
        mydisplay.setChar(0, 4, wrrpm[2], false);
        mydisplay.setChar(0, 5, wrrpm[1], false);
        mydisplay.setChar(0, 6, wrrpm[0], true);
        mydisplay.setChar(0, 7, ' ', false); 
        break;
      default:
        mydisplay.setChar(0, 4, wrrpm[3], false);
        mydisplay.setChar(0, 5, wrrpm[2], false);
        mydisplay.setChar(0, 6, wrrpm[1], true);
        mydisplay.setChar(0, 7, wrrpm[0], false);
        break;
      }
    }
    
    // Rev Lights
    void rpm_luz(int rpm_actual)
    {
      for (wnx = 0; wnx <= 15; wnx++)
      {
        strip.setPixelColor(wnx, empty);
      }
      int calculo = map(rpm_actual, 0, rpm_maxima, 0, 100);
      if(calculo >= 80)
      {
        int calculo_tmp = map(calculo, 80, 98, 0, 9);
        switch(calculo_tmp)
        {
        case 9:
          currentMillis = millis();
          if(currentMillis - previousMillis > interval)
          {
            previousMillis = currentMillis;  
            if(parpadea == false)
            {
              for (wnx = 0; wnx <= 15; wnx++)
              {
                Set_pixel_color();
              }
              parpadea = true;
            }
          }
          else
          {
            parpadea = false;
          }
          break;
        default:
          for (wnx = 0; wnx < calculo_tmp; wnx++)
          {
            Set_pixel_color();
          }
          for (wnx = 15; wnx > (15 - calculo_tmp); wnx--)
          {
            Set_pixel_color();
          }
          break;
        }
      }
      strip.show();
    }
    
    //Change pixel color
    void Set_pixel_color()
    {
      switch(rev_color[wnx])
      {
      case 'r':
        strip.setPixelColor(wnx, r);
        break;
      case 'g':
        strip.setPixelColor(wnx, g);
        break;
      case 'b':
        strip.setPixelColor(wnx, b);
        break;
      }
    }
    There are alot of things that arent needed, its my WIP code so to speek. It will look alot more clean when i have time to put it and as i progress.
    • Like Like x 1
  9. Pedro Melim

    Pedro Melim Smoother Racer

    Joined:
    Nov 12, 2014
    Messages:
    13
    Occupation:
    Programmer
    Location:
    Funchal
    Balance:
    710Coins
    Ratings:
    +8 / 0 / -0
    My Motion Simulator:
    Arduino
    Ok, fixed the weird data, turns out i need some variable to tell the rotines when there is new data so they only process data that is retreive from the serial. Tought it wouldnt be needed but having that it fixed the weird data...Now the only thing left is the beep sound, over and over again until i close the game. Any help?
  10. RacingMat

    RacingMat Well-Known Member Gold Contributor

    Joined:
    Feb 22, 2013
    Messages:
    2,234
    Location:
    Marseille - FRANCE
    Balance:
    20,890Coins
    Ratings:
    +2,083 / 21 / -2
    My Motion Simulator:
    2DOF, DC motor, Arduino
    this Beep is weird: never heard of this on the forum... sorry
  11. Pedro Melim

    Pedro Melim Smoother Racer

    Joined:
    Nov 12, 2014
    Messages:
    13
    Occupation:
    Programmer
    Location:
    Funchal
    Balance:
    710Coins
    Ratings:
    +8 / 0 / -0
    My Motion Simulator:
    Arduino
    Somehow, after a while it was fixed.

    Been busy as i have a race this week, will get to the coding after the race.

    I have been able to get the data to be sent correctly to the arduino, had a couple of issues but will get to that later. The thing that is annoying me atm is that the Speed variable showed in the GameDash window flickers when the car is stopped, wich makes it show on the display that the car is at x speed when it is stopped, its weird and i dont know what the problem might be.
    Another thing is the speed variable is an integer, so the speed in KPH on the arduino increments as the ms (meters per second) increments, i get 3kph, 7kph, 11kph etc. Its not flowing correctly. I would want it to increment correctly, but for that the speed variable within the GameDash would need to be a float type perhaps?

    Anyway suggestions, opinions, advice are welcome ;)
  12. insanegr

    insanegr !N$@n€

    Joined:
    Jan 14, 2014
    Messages:
    505
    Location:
    Athens
    Balance:
    16,715Coins
    Ratings:
    +499 / 1 / -0
    My Motion Simulator:
    2DOF, DC motor, Arduino
    Heard the beep too and if i remember right it was beeping when i had wrong interface settings or wrong settings in game dash - command editor (output rules).
    Seen the floating values in rFactor too with @soraz dashboard code. if you watch the game dash when the car is stopped you will see the floating values there too.
    i think the floating values and the arduino increments are from the game or the plugin nothing to do with arduino code.
    BTW really like your dashboard
  13. noorbeast

    noorbeast VR Tassie Devil Staff Member Moderator Race Director

    Joined:
    Jul 13, 2014
    Messages:
    20,535
    Occupation:
    Innovative tech specialist for NGOs
    Location:
    St Helens, Tasmania, Australia
    Balance:
    145,034Coins
    Ratings:
    +10,776 / 52 / -2
    My Motion Simulator:
    3DOF, DC motor, JRK
    Please stop making a multitude of pointless posts on other member's threads, it adds nothing to their project, needlessly pushes out relevant posts and will just draw the attention of the Admins @Historiker and @RaceRay.

    Instead please spend your time and become familiar with community etiquette and expectations: https://www.xsimulator.net/community/faq/community-etiquette.82/
    • Agree Agree x 1