PDA

View Full Version : Making a buttonbox - input wanted



Maskmagog
13-03-2018, 21:16
Hello people,
I'm gonna try to build a buttonbox, and would like some help with the layout or otherwise. Better to get feedback now, than when it's too late to change things :)

This will be the front plate of a slanted box, maybe 45 degrees?

251856

These will be rotary encoders:
Brake = brake bias
FRB = Front AntiRollBar
RARB = Rear AntiRollBar
VOL = FFB volume
TONE = FFB tone

These will be square buttons:
PIT = Pit request
MOT = cycle MOTEC
NAM = cycle names in multiplayer
CAM = change camera view
PLM = Pit limiter
HDL = Headlights
WIP = Wipers

HDL, WIP and PLM might be pull switches, not sure yet. The box might be too light for that.

Top row is 5mm LED. The FLG (flag) LED at least will be RGB LED, to be able to change color.

Thoughts? Anything missing?

Bealdor
14-03-2018, 05:55
Looks nice but KERS and DRS should be activated by buttons on the wheel/controller IMO.

Shepard2603
14-03-2018, 06:23
Nice project, BUT will this work with the XBone? I assume you want to use something like an Arduino Leonardo board?! Is that compatible with consoles? How do you want to read the data from the game and feed it into the controller to activate your LEDs? The pit LEDs can be triggered by the buttons, but anything else needs to be read from the telemetry.

I really do not want to discourage you, I am just curious, if you figured a way out to actually realize this!

Maskmagog
14-03-2018, 06:41
Good point about KERS and DRS, I’ll probably move them to wheel buttons.

About led: I’m using vrHive on a Windows 10 laptop for telemetry, and it can output an easier data stream that will be read by an arduino. It *should* work (I think) :) Long term goal is to get arduino or Raspberry pi to read UDP directly, and make it standalone, if possible.
For the buttons, my plan is to slaughter an old keyboard and rewire that.
With my normal speed, all of it might be done by 2020...

Shepard2603
14-03-2018, 06:47
Very Interesting! So I see a solution with two microcontrollers here. One for sending your inputs to the console, one for receiving telemetry and triggering your displays/LEDs. Is there actually a solution for sending data from the laptop to the microcontroller?

Maskmagog
14-03-2018, 10:46
vrHive has a built-in function that sends serial data (telemetry) through com-ports, to for example an arduino. A PCars user, Gary Swallow, made an application called arDASHuino that reads this stream, and sends different data (rpm, gear, lap times etc) to lcd screens. My plan is to adapt arDASHuino, to display lap times on seven-segment displays, and control leds. Will it work? Well it should work. Can I make it work? Unclear :)

DozUK
14-03-2018, 11:22
Good point about KERS and DRS, I’ll probably move them to wheel buttons.

About led: I’m using vrHive on a Windows 10 laptop for telemetry, and it can output an easier data stream that will be read by an arduino. It *should* work (I think) :) Long term goal is to get arduino or Raspberry pi to read UDP directly, and make it standalone, if possible.
For the buttons, my plan is to slaughter an old keyboard and rewire that.
With my normal speed, all of it might be done by 2020...

I think it's the quickest and the easier way to butcher a keyboard to do this, especially for a console.

One word of (Potential) warning, I'm not 100% but I seem to remember reading on other threads like this that there could be an issue with rotary encoders and consoles. I would check other threads on the same subject to be doubly sure before you invest. Technically there shouldn't be a problem as it's only a mapping but I'm sure I've read something along these lines. Hopefully there won't be and your project will go smoothly

Maskmagog
14-03-2018, 12:30
If I get issues with the rotary encoders, it’s possible to use an arduino for that. Trial and error awaits.

AbeWoz
14-03-2018, 12:50
i used these for my button box. its encoded as a USB joystick so not sure if it will work for your application.

it also seems to be iffy with rotaries. waiting for my new rotaries to come in so i can test.

https://www.amazon.com/gp/product/B0753W8G1P/ref=oh_aui_detailpage_o03_s00?ie=UTF8&psc=1

not sure how/if it will work with LEDs tho.

my BBox doesn't have as many buttons, but i use it for: Headlights, Wipers, Pit Stop request, Mute/Unmute Discord voice, Ignition, TC and ABS, Hud Cycle and MoTeC cycle.
On my wheel I have KERS, DRS, Pit Limiter, Engine Start, Brake Bias, ICM controls.

satco1066
14-03-2018, 22:22
You need a network extension on the Arduino and then can decode the UDP stream to get the LEDs lit.
I tried it long time ago for PC1 on PC and it was no problem.
On the other side, i don't know if an Arduino button box is recognised by the XBX.

foxymop
15-03-2018, 10:27
I think arduino Leonardo can act as a keyboard. Don't know if can act as a normal arduino at the same time to act as a hud.
I've ordered some material also to try some things too.
You can get a list of all the info you can get from hive? Maybe plan the hud from there?
Like, if you can get the fuel value, you can have a buzzer for that, or for engine heating.

Killg0re NL
15-03-2018, 11:46
I recommend the lit limiter also on the wheel.

I will try to make a post on building your own buttonbox.
(Arduino Leonardo sketch)

Maskmagog
15-03-2018, 11:48
Satco1066: sounds interesting! Do you have any details? What network interface? Do you have any code you’re willing to share? :)

Foxymop: Yes, vrHive has a list of supported values. There’s a pdf available on vrhive.co.uk.

Black TimeZzZ
15-03-2018, 20:19
I use a Arduino Pro Micro
acts as standard keyboard, wired like a matrix
i use 15 buttons and 4 encoders on my box

Maskmagog
15-03-2018, 21:12
Yes, just read about the Pro Micro has built-in keyboard emulation. All arduinos with Atmega32u4 maybe?
Just tested, and it seems to work fine. Found two tutorials on youtube:

Rotary encoder: https://www.youtube.com/watch?v=J9cDEef0IbQ
Keyboard emulator: https://www.youtube.com/watch?v=SHIcliL4O14

@Black_TimeZzZ: Do you know how many (and which) of the pins that are available for this on the Pro Micro?
I might need a few of these, but a while ago I ordered a handful from China. Dirt cheap, should arrive in a couple a days.

I also ordered some of these 7-segment displays, that I plan to use for lap times:
251904
That will be served by a separate Arduino, probably a Nano.

Maskmagog
15-03-2018, 21:31
Do you know how many (and which) of the pins that are available for this on the Pro Micro?
I

Gonna answer my own question..
According to this post (https://hackaday.io/project/8282-alpen-clack/log/27475-use-a-pro-micro-in-a-keyboard), 18 pins are available. So a matrix (http://pcbheaven.com/wikipages/How_Key_Matrices_Works/), 9x9 means 81 buttons maximum. Well, that'll have to do :)

Killg0re NL
15-03-2018, 21:43
You can use ' my sketch' for the buttons and encoders

http://forum.projectcarsgame.com/showthread.php?61987-DIY-Buttonbox-V1&p=1490568#post1490568

Reading of the actual status such as ABS ESP is still in progress (with an ESP8266)

The leonardo has more pins available.
i am able to work with an MCP23017, but i don't have the buttonpresses attached yet.
(Working with an MCP23017, uses only 2? inputs on the leonardo, but extends the inputs.
So this leaves more in/outputs available to communicate with the ESP8266 and rotary encoders

satco1066
16-03-2018, 00:19
Satco1066: sounds interesting! Do you have any details? What network interface? Do you have any code youíre willing to share? :)

.

i used an Ethernet Shield W5100, a TM1638 board and Mega 2560R3. Wrote an UDP reader and interpreter to decode car stats for some status leds, gear, speed, fuel etc for the 7-segments and also decoded rpm to display at the led line of the TM1630. Worked fine on the testboard, but never made it to a finished device.
This was 2 years ago and just a test project. Must search for the code in some backups.

belaki
18-03-2018, 13:30
You gotta love a button box, and projects like the one you envision are fabulous learning experiences and creative outlets.

But physical button boxes have significant limitations. For the most part, they are seriously resistant to change. Further, they occupy cockpit space with a one dimensional use case and can be bulky. I could go on...

Save yourself the aggro. Build a virtual button box using something like Roccat Powergrid on an Android tablet. The added benefit is the Android will also run many other tools useful to the driver. Below is a picture of a now deprecated cockpit (meaning I have reconfigured screens - something near impossible with physical dash and button boxes) that shows Powergrid on the right hand glass panel. Note this same panel is also switchable to a telemetry screen running concurrently in the background. With the Powergrid I can also launch PC2 and all associated add-ons directly from the cockpit - and there is also a (rudimentary) keyboard and trackpad. It is very flexible.

251983

Maskmagog
18-03-2018, 13:44
Belaki, you make good points. But one of the reasons I’m doing this (probably the biggest) is that I want to learn more about arduinos, switches, leds, shift registers etc. I just love that mixture of analog and digital! It’s a hobby for me, and I plan to use the knowledge in other, non-pcars2 projects :)

Edit: Nice setup!

Miker
18-03-2018, 13:47
Looks like you are using buttons for ABS and Traction Control, I'd consider changing these to rotary switches, especially if you play other sims which allow a greater range of setting for these functions (maybe swap out the ARB as few cars actually allow these to be adjusted on the fly anyway)

I have rotary switches assigned for seat up/down and back/fwd which makes it very quick and easy to get each car viewing just right, probably my most used function on my button box!

Interesting project though, looking forward to seeing the finished job.

satco1066
19-03-2018, 01:51
Belaki, you make good points. But one of the reasons Iím doing this (probably the biggest) is that I want to learn more about arduinos, switches, leds, shift registers etc. I just love that mixture of analog and digital! Itís a hobby for me, and I plan to use the knowledge in other, non-pcars2 projects :)

Edit: Nice setup!

Hey Maskmagog, do you know that project?
Its also done with Arduino, and this guy uses an original BMW E36 cockpit instead of a digital dash

https://www.sim-pc.de/bmw-e36-mit-rcc-real-cockpit-connector/


https://www.youtube.com/watch?time_continue=1&v=6FNOBRS-7UM

RookieRaceline
19-03-2018, 07:17
Very impresive, but honestly, i don't undestand how you guys can make all this work, but as far as i have figured out, the xbox can't even regognize a premade usb input device made by Logitech:

252006


this more or less has all i need, (even the joystick for ICM navigation and also can be twisted to look left & right)...
But nobody will tell me if it works. Only two people have told me that it doesn't!

Killg0re NL
19-03-2018, 07:50
but as far as i have figured out, the xbox can't even regognize a premade usb input device made by Logitech:


The trick is all USB devices use a PID, in short it is a indentifier who tells what it is.

Sony and MS decide wether to support it, or not.
In the early PS4 days the older firmware supported an logitech wireless headset. Later support of that product was gone.

Back to the question. MS and Sony do support keyboards.
To what a buttonbox or a even a fancy dashboard does, is emulate a plain simple keyboard. It just registers plain keypresses.

RookieRaceline
19-03-2018, 08:25
The trick is all USB devices use a PID, in short it is a indentifier who tells what it is.

Sony and MS decide wether to support it, or not.
In the early PS4 days the older firmware supported an logitech wireless headset. Later support of that product was gone.

Back to the question. MS and Sony do support keyboards.
To what a buttonbox or a even a fancy dashboard does, is emulate a plain simple keyboard. It just registers plain keypresses.

Shouldn't it recognize that logitech sidepanel too then?

Maskmagog
19-03-2018, 18:12
Hey Maskmagog, do you know that project?
Its also done with Arduino, and this guy uses an original BMW E36 cockpit instead of a digital dash



Wow, that’s awesome! Would be great to use some real gauges, but I think I should do one step at the time :)

Killg0re NL
19-03-2018, 19:47
No unfortunatly not. The logitech sidepanel isnt a keyboard.
A keyboard uses default keyboard drivers. For the use of his panel, you'll need to install drivers on a PC.

They could, but then the joystick wouldnt work.

Maskmagog
20-03-2018, 22:49
So, I made some progress. Was a bit worried that the rotary encoders would cause problems. My head started spinning after trying to read about interrupt pins, shift registers and what not. After some intense googling, I found a brilliant arduino sketch, very easy to use. It allows for 5 rotary encoders and 16 buttons, or 4 rotary encoders and 25 buttons (on a Pro Micro). Buttons are wired in a matrix, and rotaries with three pins: common ground + 2 pins.

This is the original sketch, that I later modified slightly: buttsimp.ino (https://github.com/OpenSimHardware/Buttsimp/blob/master/buttsimp.ino)
Don't ask me about how the script works, I'm barely skilled to adapt it to my needs :)

My changes were:
Changed to 5 rotary encoders, and 16 buttons in the #define area. Also changed the "letter matrix" to suit, and added a line to rotariesdef.

I also changed row 73: It didn't seem to like the pin naming, so I changed 21, 20, 19, 18 to A3, A2, A1, A0.

Since I only use 16 buttons, I modified row 73 and 74 to reflect this.

I commented out row 100 & 101, because I want a single key press.

Lastly, I changed row 137, from Keyboard.write to Keyboard.press, and added two lines: delay(150) and Keyboard.releaseAll(). With Keyboard.write, it worked fine on my Win 10 laptop, but on the Xbox, it was hit and miss with the rotaries. Since buttons use Keyboard.press in the script, and those worked on xbox, I tried that, and it worked great on xbox too.

So the technical issues seems ok, regarding buttons and rotaries. Next step is the leds.
Also, my 7-segment displays came today from China :) I'm planning to use a Nano to control these, and show different lap times etc. More googling to come.

Maskmagog
12-06-2018, 09:10
After waiting 3 months for parts from China, I could finally progress. China to Sweden took 1 month. Swedish mail took 2 months.
I ordered a laser cut acrylic top for my box from Danish company cotter.dk. Recommended if you live in these parts of Europe. Took about 1 week from order to delivery.

It's been a while since I used the soldering iron. Progress was slow, 1 step forward, 1 back. Had to redo a few solderings. Then I over used the hot glue and got some glue inside the buttons. Argh. But finally, everything works! 😄
Next phase is an Arduino Nano to read UDP and control leds and fuel meter.
255993255994255995255996255997255998

Maskmagog
12-06-2018, 12:49
If anybody have any questions, or want to have my Frankenstein code, just ask. I've pieced other people's code together to suit my needs, and it's easy to tweak the code. It's very doable, if you have some time and patience. When I started this, I had dabbled with Raspberry Pi for a while, but this is my first real Arduino project.

Next part is a littlie trickier, I think. I have Gary Swallows ArDASHuino code working, which connects to vrHive and outputs to 16x2 LCD screens. My first plan was to adapt that to output to seven-segment displays instead, which would work. But I'm gonna try to get the Arduino Nano to read the UDP stream directly. Google, here I come.

Maskmagog
24-06-2018, 08:48
Time to start the next phase, which requires UDP to be read. Thankfully, Zeratall has released a C# library, which makes things a lot easier for hacks like me! I've just started, and there's a long way to go, but I might as well document things as they go down. Someone might help me, and the info might be helpful to someone else.
First install Visual Studio Community, the free version.
Zeratalls library can be found here: https://github.com/Zdetier/RST_UDP
Forum thread here: http://forum.projectcarsgame.com/showthread.php?63374-C-Udp-V2-library
I had some issues at first, solved by going to Tools->Get tools and features. Select .NET desktop develoment, and press Modify.
Open Program.cs in UDP Example map, and modify it to suit your needs.

The uDP.Speed is in m/s, which I didn't realize. Convert it by using: var speedkmh = uDP.Speed * 3.6;
As a first step, I'm just writing things in the console window, for example Oil and Water temp warning:


I've never used C#, and I'm not a programmer, so I'm hacking my way throug this with plenty of googling. Feel free to suggest improvements!

In the Main area, I declare som variables such as:
int OilWarning = 0;
int OldOilWarning = 0;

Then inside the While-loop:
/***************************************
* Water and Oil Temp warnings
***************************************/
OldOilWarning = OilWarning; // Remember what value OilWarning have
if (uDP.WaterTempCelsius > 100 || uDP.OilTempCelsius > 100) // Check if either temp is above 100
{
OilWarning = 1; // If either temp is above 100, set OilWarning to 1
}
if (OilWarning > OldOilWarning) // Basically if OilWarning is 1, and OldOilWarning is 0, then this is a new state
{
Console.WriteLine("Oil and/or water temp is too high"); // If it is a new state, do stuff. Right now, write a message in console window
}
/***************************************

Without the OldOilWarning-variable, the message would trigger everytime, flooding the console window. It can probably be done in a better way, but this seems to work at least.

For Suspension Damage I have this:

variables in Main area as above:
int SuspDamage = 0;

Inside While loop:
/***************************************
* Brake and suspension damage
***************************************/

byte bytevalue1 = uDP.SuspensionDamage[0]; // These are in the byte datatype, which is completely new to me. I think it's an integer beetween 0-255.
byte bytevalue2 = uDP.SuspensionDamage[1]; // As with tyres, 0 is FL (Front Left), 1 is FR, 2 is RL, 3 is RR
byte bytevalue3 = uDP.SuspensionDamage[2];
byte bytevalue4 = uDP.SuspensionDamage[3];

//Check suspension damage
if (SuspDamage == 0) //Check if we've already written a message about this
{
if (uDP.SuspensionDamage[0] > 100) // A value above 100 (out of 255) equals about 40 in damage on in-game HUD.
{
Console.WriteLine("Front left suspension damaged."); //Write a message
Console.WriteLine(uDP.SuspensionDamage[0]); // Write the value for front left suspension damage (0-255)
SuspDamage = 1; //Set a variable so we know we already written a message about this, to keep from flooding console window
}
}

/***************************************

For the fuel meter I plan to use a 10-segment led bar graph, so I need to check how many leds to light up.

Variables in Main area:
int FuelLED = 0; // Value to store how many LEDs, out of 10, that should be lit.
int OldFuelLED = 0; // Stores old value, to check if value has changed

And in While-loop:
/***************************************
* Fuel level meter
***************************************/
OldFuelLED = FuelLED; // "Remember" the current value
if (uDP.FuelLevel > 0.95) // uDP.FuelLevel gives value between 0-1. Value above 0.95 means 10 leds lit
{
FuelLED = 10; // Value to remember how many leds that should currently be lit
}
else if (uDP.FuelLevel > 0.85) // Value between 0.85-0.95 means 9 leds lit
{
FuelLED = 9;
}
else if (uDP.FuelLevel > 0.75)
{
FuelLED = 8;
}
else if (uDP.FuelLevel > 0.65)
{
FuelLED = 7;
}
else if (uDP.FuelLevel > 0.55)
{
FuelLED = 6;
}
else if (uDP.FuelLevel > 0.45)
{
FuelLED = 5;
}
else if (uDP.FuelLevel > 0.35)
{
FuelLED = 4;
}
else if (uDP.FuelLevel > 0.25)
{
FuelLED = 3;
}
else if (uDP.FuelLevel > 0.15)
{
FuelLED = 2;
}
else if (uDP.FuelLevel > 0.05)
{
FuelLED = 1;
}
else if (uDP.FuelLevel < 0.05)
{
FuelLED = 0;
}
//Check if Fuel level has changed
if (OldFuelLED != FuelLED) //Only write message if number of leds have changed, ie gone from 8 to 7 leds for example
{
Console.WriteLine("Fuel level now around " + FuelLED +"0 %");
}
******************************************

Next step is to try this on a Raspberry Pi 3, or even better, Raspberry Pi Zero W. With the Mono package, it might be possible to run C# on RPi. Time will tell!
Hopefully Zeratall (or anyone else) will soon add more UDP parameters such as lap times, sector times, weather etc :)

Zeratall
24-06-2018, 15:56
This is going to be really cool when your done!

Maskmagog
24-06-2018, 16:57
^^Well I hope so!
By neglecting my kids (it's possible to reconnect with them when thery're older, right? :)) I did a quick test on a RPi Zero W (very small, with built in wifi). I installed mono-runtime, then managed to export from Visual Studio, moved the files to Rpi and executed them with sudo mono UDP_Example.exe. And it worked! The program listened and reacted to UDP, and I even got it to write some lines to a txt file (not sure why yet, but good to know :)).
This was just a quick test, but it looks promising. The possibility to have a Raspberry Pi Zero W listening to UDP, and controlling LEDs, LCD displays, 7-segment displays, or whatever, is very exciting to me.

I would never have come this far with the UDP part if not for your library Zeratall, so thanks again!

Killg0re NL
24-06-2018, 16:57
I'll check this later on aswell.
For now i got ABS, ESP, TCS and Lights reflecting the actual state on leds.

But working on other people's code is hard...

Zeratall
24-06-2018, 19:01
^^Well I hope so!
By neglecting my kids (it's possible to reconnect with them when thery're older, right? :)) I did a quick test on a RPi Zero W (very small, with built in wifi). I installed mono-runtime, then managed to export from Visual Studio, moved the files to Rpi and executed them with sudo mono UDP_Example.exe. And it worked! The program listened and reacted to UDP, and I even got it to write some lines to a txt file (not sure why yet, but good to know :)).
This was just a quick test, but it looks promising. The possibility to have a Raspberry Pi Zero W listening to UDP, and controlling LEDs, LCD displays, 7-segment displays, or whatever, is very exciting to me.

I would never have come this far with the UDP part if not for your library Zeratall, so thanks again!

Hahah awesome I'm glad your enjoying it, my next project is a motion rig haha.

Maskmagog
25-06-2018, 09:04
^^That sounds awesome! I'll have to settle with rocking my chair myself :)

Killg0re NL
28-06-2018, 17:14
Did you get this to work on a arduino?

I got some code working telling me the ABS, ESP, TCS and light status. Gear should also work.

But i can't seem to get the other data working. And i dont have a clue on the UDP stream so speed and RPM. And ie. Flag situations are at the moment not programmable.

I used the http://forum.projectcarsgame.com/showthread.php?43111-How-to-import-data-from-wifi-into-arduino&highlight=buttonbox and got that working.

See my thread for the progress. (I see i should update it)
http://forum.projectcarsgame.com/showthread.php?61987-DIY-Buttonbox-V1a-with-8-encoder-functions&highlight=buttonbox

Maskmagog
28-06-2018, 18:59
^^I've just done a quick test on a Raspberry Pi Zero W, about the size of an Arduino, and just as cheap, with built-in wifi and GPIO pins. With Mono-runtime installed, I could run the C# program, and read and interpret UDP data. Since then I've tried to expand Zeratalls library to include more info, but it's an uphill struggle. Fun (and a bit frustrating:)) to learn though, but I'm hoping that Zeratall adds more info to the library later. It's pretty easy to use. I've had success with things like speed, odometer, tyre name, fuel level but failed with others such as turbo boost pressure and gears. Probably because I don't fully understand the different data types enough (yet) such as byte, char, unsigned int etc.

Nice work on the buttonbox, looks great!

Zeratall
28-06-2018, 23:14
I'll try to update the library this weekend, as I work on an update for PC2Tuner, also for gearing convert the value to hex.

Maskmagog
28-06-2018, 23:47
Brilliant, thanks!

Maskmagog
02-07-2018, 16:20
Progress with UDP on Raspberry Pi! I have a C# program running on my Rpi3, that reads UDP from my Xbox. At the moment I have the RPi and Xbox in different rooms, so I don't know if there's a lot of lag.
Right now I have one red LED connected to the RPi, which turns on if certain criteria is met, from reading the UDP stream. So far so good!

This is the tutorial I followed to control GPIO pins from C#. Excellent, just follow the instructions: https://gitlearning.wordpress.com/2016/05/27/accessing-gpio-ports-with-c/
Here is the follow-up post with an example: https://gitlearning.wordpress.com/2016/06/01/single-led-circuit-with-the-pi/
Only difference for me is that the LED is off when digitalWrite sends .LOW, and the LED turns on when sending .HIGH.

I code in Visual Studio on PC, then copy the files using WinSCP. Right now, thanks to Zeratalls library, I can interpret UDP regarding current lap time, current sector time, current sector, my race position, number of participants, current lap, current gear, available gears, tyre temps, speed, tyre compound, odometer, water & oil temp & pressure, fuel level.

Still struggling with brake bias, value seems to fluctuate wildly up and down? Probably cause I'm still confused by all these bytes, chars, arrays etc :)

Next step is to connect the LEDs in my button box. After that, I'll start with displaying lap times etc on 8 digit 7-segment displays.

Here's my code example for current total time:

double CurrentTotalTime = (uDP.ParticipantInfo[uDP.ViewedParticipantIndex, 14]) / 60;
var timeSpanTotalTime = TimeSpan.FromMinutes(CurrentTotalTime);
int hh1 = timeSpanTotalTime.Hours;
int mm1 = timeSpanTotalTime.Minutes;
int ss1 = timeSpanTotalTime.Seconds;
int ms1 = timeSpanTotalTime.Milliseconds;
Console.WriteLine("Current lap time: {0:00}:{1:00}:{2:00}:{3:000}", hh1, mm1, ss1, ms1);

Here's my code for gears:

string gearhex = uDP.GearNumGears.ToString("X2");
char CurrentGear = gearhex[1];
char AvailableGears = gearhex[0];
Console.WriteLine("Current gear is " + CurrentGear);
Console.WriteLine("Total available gear is " + AvailableGears);

Tyre compound:

string[] TireNameArray = { "Front Left", "Front Right", "Rear Left", "Rear Right" };
for (int i = 0; i < 4; i++)
{
tireName = System.Text.Encoding.UTF8.GetString(uDP.TyreCompound[i]);
Console.WriteLine(TireNameArray[i] + " has " + tireName);
tireShown = 1;
}


EDIT: Just moved the SD card to a Raspberry Pi Zero W, and it works on that as well. Good news, cause Pi Zero W is really small, and pretty cheap! Only issue is that my network seems to bog down because of the UDP traffic, and the RPi (and Xbox) sometimes loses the connection. We'll see how that evolves.

Maskmagog
03-07-2018, 11:57
After a lot of trial and error, and studying Zeratalls library, I managed to add some more data. I now have split times, world record, my fastest lap in race, my last lap in race, track temp, ambient temp, rain density, sector times, wind speed, suspension damage and a few more. So now I pretty much have the data I need for my 7-segment displays.

Zeratall
03-07-2018, 13:56
After a lot of trial and error, and studying Zeratalls library, I managed to add some more data. I now have split times, world record, my fastest lap in race, my last lap in race, track temp, ambient temp, rain density, sector times, wind speed, suspension damage and a few more. So now I pretty much have the data I need for my 7-segment displays.

Be sure to commit to the Library Git! And that's awesome!

Maskmagog
03-07-2018, 22:13
Well, I'll try to upload to the library git, but I really don't know how :) I'll google that as well :)
I have to check it some more for errors, since I don't really know what I'm doing... But I guess that you would review my additions before adding them?

Maskmagog
03-07-2018, 22:19
It seems it's a lot harder than I thought to control 7-segment displays, or 16x2 LCD displays, than I thought. I have used them before, but in Python, not in C#. Turning pins high and low to control leds works alright, but the displays evade me for now. At least what I got now will allow me to finish the buttonbox, with LEDs. Displays will be a separate project.

Maskmagog
05-07-2018, 19:58
Well, the button box is finished! Almost, anyway.
It took a lot of soldering! The fuel meter bar graph is actually 10 LEDs, each requiring a resistor. So, lots of cables, and lots of soldering. But it works! :)
I'm waiting for a powered USB hub to power the arduino and the raspberry pi (and maybe a usb reading lamp so I can see the buttons in a dark room). When that arrives, I can shove everything inside the box!

The code can most certainly be improved, but it works at least. Fuel meter is active even on Ready screen, and updates when a setup is loaded/saved, so it's easy to see roughly how much fuel I have.
RGB LED changes colour based on current flag, though I've only tested green flag and blue flag (and blinking for chequered).
Top left blue led lights up when a pitstop is requested. The other lights up when certain criteria is met: Water temp above 100, Suspension damage above 40, Oil temp above 100. These might change later to pretty much anything at all. The program reads a lot of UDP data, so it's just a matter of taste. Light a red LED when someone is less than 3 seconds behind you? Light blue led if your tires are cold? Anything's possible. :)
Fun project, feels great to have finished it! Not the best button box, or the cheapest, but I made it (and learned a LOT at the same time). Thanks again to Zeratall.

Next project will be 7-segment displays and 16x2 lcd screens displaying lap times etc, based on ESP8266 and an Arduino. But that's another story.

257289257288

Killg0re NL
05-07-2018, 21:30
Next project will be 7-segment displays and 16x2 lcd screens displaying lap times etc, based on ESP8266 and an Arduino. But that's another story.


For @Maskmagog and other PC2 users, i got some stuff working from @cyclicscooby, but for me it stops ar the ABS, ESP TCS and Light.
i can share what i'fe managed to get working. (But sometimes the Led representing the light goes on. so must be bit/byte thing.)

Perhaps we can team up?

i'fe updated my Buttonbox to V1b

Maskmagog
05-07-2018, 21:49
@Killg0re_NL, that sounds great! I've read about yours and cyclicscoobys progress, and it seems very promising, good job! I have some arduino experience but I haven't used ESP8266 before. On the other hand, I'm a great googler (is that a word? :)) and I'm equipped with great patience and perseverance. If that's of any help :)

So yes, it would be awesome to team up, along with anyone interested in this. I'll check out your buttonbox thread!

Zeratall
06-07-2018, 14:37
@maskmagog, congrats on the button box, that's awesome man, looks good not to mention it feels so much cooler when you build it yourself. Here's the progress on my project, was very excited when I discovered I didn't ruin my board during soldering lol. 257317]

Maskmagog
06-07-2018, 16:16
@Zeratall, thanks, and wow that's going to be truly awesome! :)
Yes, it's a very nice feeling of accomplishment to finish a selfmade project (well, almost selfmade, I got stellar help from you).

This is the finished button box, with a nice finishing touch :)
I'll probably do something different for the labels later.

257318257319

Killg0re NL
06-07-2018, 18:34
@Zeratall, i see youre working on arduino aswell. Do you fancy teaming up with us? I am not so handy with the UDP stream, but on the electronic and arduino i got some more experience.

Zeratall
06-07-2018, 18:58
Yep I use uno for all my. Embedded systems stuff. Using arduino uno and then speed controllers to handle about 1200W (got beefy motors). Arduino reads lat and long acceleration from the game using udp.

Id love to help out in any way, always enjoy working on this kinda stuff.

Zeratall
09-07-2018, 21:58
And I finished it this weekend!!! Check it out in the lower right.


https://youtu.be/Rg9vYtkrmaw

Maskmagog
28-10-2018, 10:56
This is the code for my buttonbox, in case anyone can benefit from it. As I said before, I'm using Zeratall's (old) library in C#. You can find on Github here: https://github.com/Zdetier/RST_UDP

Some things work, some don't. The parts I really needed was fuel, damage, oil/water temp and flags. The game is a bit inconsistent with sending flags, it seems.

The parts I changed is, first I added a few more definitions in the PCars2_UDP.cs file, Here is my complete file:


using System;
using System.IO;
using System.Net;
using System.Net.Sockets;


namespace PcarsUDP
{

class PCars2_UDP
{
const int UDP_STREAMER_CAR_PHYSICS_HANDLER_VERSION = 2;
const int TYRE_NAME_LENGTH_MAX = 40;
const int PARTICIPANT_NAME_LENGTH_MAX = 64;
const int PARTICIPANTS_PER_PACKET = 16;
const int UDP_STREAMER_PARTICIPANTS_SUPPORTED = 32;
const int TRACKNAME_LENGTH_MAX = 64;
const int VEHICLE_NAME_LENGTH_MAX = 64;
const int CLASS_NAME_LENGTH_MAX = 20;
const int VEHICLES_PER_PACKET = 16;
const int CLASSES_SUPPORTED_PER_PACKET = 60;
const int UDP_STREAMER_TIMINGS_HANDLER_VERSION = 1;
const int UDP_STREAMER_TIME_STATS_HANDLER_VERSION = 1;
const int UDP_STREAMER_PARTICIPANT_VEHICLE_NAMES_HANDLER_VERSION = 2;
const int UDP_STREAMER_GAME_STATE_HANDLER_VERSION = 2;


private UdpClient _listener;
private IPEndPoint _groupEP;

private UInt32 _PacketNumber;
private UInt32 _CategoryPacketNumber;
private byte _PartialPacketIndex;
private byte _PartialPacketNumber;
private byte _PacketType;
private byte _PacketVersion;
private sbyte _ViewedParticipantIndex;
private byte _UnfilteredThrottle;
private byte _UnfilteredBrake;
private sbyte _UnfilteredSteering;
private byte _UnfilteredClutch;
private byte _CarFlags;
private Int16 _OilTempCelsius;
private UInt16 _OilPressureKPa;
private Int16 _WaterTempCelsius;
private UInt16 _WaterPressureKpa;
private UInt16 _FuelPressureKpa;
private byte _FuelCapacity;
private byte _Brake;
private byte _Throttle;
private byte _Clutch;
private float _FuelLevel;
private float _Speed;
private UInt16 _Rpm;
private UInt16 _MaxRpm;
private sbyte _Steering;
private byte _GearNumGears;
private byte _BoostAmount;
private byte _CrashState;
private float _OdometerKM;
private float[] _Orientation = new float[3];
private float[] _LocalVelocity = new float[3];
private float[] _WorldVelocity = new float[3];
private float[] _AngularVelocity = new float[3];
private float[] _LocalAcceleration = new float[3];
private float[] _WorldAcceleration = new float[3];
private float[] _ExtentsCentre = new float[3];
private byte[] _TyreFlags = new byte[4];
private byte[] _Terrain = new byte[4];
private float[] _TyreY = new float[4];
private float[] _TyreRPS = new float[4];
private byte[] _TyreTemp = new byte[4];
private float[] _TyreHeightAboveGround = new float[4];
private byte[] _TyreWear = new byte[4];
private byte[] _BrakeDamage = new byte[4];
private byte[] _SuspensionDamage = new byte[4];
private Int16[] _BrakeTempCelsius = new Int16[4];
private UInt16[] _TyreTreadTemp = new UInt16[4];
private UInt16[] _TyreLayerTemp = new UInt16[4];
private UInt16[] _TyreCarcassTemp = new UInt16[4];
private UInt16[] _TyreRimTemp = new UInt16[4];
private UInt16[] _TyreInternalAirTemp = new UInt16[4];
private UInt16[] _TyreTempLeft = new UInt16[4];
private UInt16[] _TyreTempCenter = new UInt16[4];
private UInt16[] _TyreTempRight = new UInt16[4];
private float[] _WheelLocalPositionY = new float[4];
private float[] _RideHeight = new float[4];
private float[] _SuspensionTravel = new float[4];
private float[] _SuspensionVelocity = new float[4];
private UInt16[] _SuspensionRideHeight = new UInt16[4];
private UInt16[] _AirPressure = new UInt16[4];
private float _EngineSpeed;
private float _EngineTorque;
private byte[] _Wings = new byte[2];
private byte _Handbrake;
private byte _AeroDamage;
private byte _EngineDamage;
private UInt32 _Joypad0;
private byte _DPad;
private byte[][] _TyreCompound = new byte [4][];
private float _TurboBoostPressure;
private float _FullPosition;
private byte _BrakeBias;
private UInt32 _TickCount;

//RaceData
private float _WorldFastestLapTime;
private float _PersonalFastestLapTime;
private float _PersonalFastestSector1Time;
private float _PersonalFastestSector2Time;
private float _PersonalFastestSector3Time;
private float _WorldFastestSector1Time;
private float _WorldFastestSector2Time;
private float _WorldFastestSector3Time;
private float _TrackLength;
//private char[] _TrackLocation;// Can't get track names to work
//private char[] _TrackVariation;
// private char[] _TranslatedTrackLocation;
// private char[] _TranslatedTrackVariation;
private UInt16 _LapsTimeInEvent;
private sbyte _EnforcedPitStopLap;

//Timing
private sbyte _NumberParticipants;
private UInt32 _ParticipantsChangedTimestamp;
private float _EventTimeRemaining;
private float _SplitTimeAhead;
private float _SplitTimeBehind;
private float _SplitTime;
private double[,] _ParticipantInfo = new double[32, 16];

//Game Data
private UInt16 _BuildVersionNumber;
private byte _GameState;
private sbyte _AmbientTemperature;
private sbyte _TrackTemperature;
private double _RainDensity;
private double _SnowDensity;
private sbyte _WindSpeed;
private sbyte _WindDirectionX;
private sbyte _WindDirectionY;

// Participant Stats Info
private double[,] _ParticipantStatsInfo = new double[32, 16];


public PCars2_UDP(UdpClient listen, IPEndPoint group)
{
_listener = listen;
_groupEP = group;
}

public void readPackets()
{
byte[] UDPpacket = listener.Receive(ref _groupEP);
Stream stream = new MemoryStream(UDPpacket);
var binaryReader = new BinaryReader(stream);

ReadBaseUDP(stream, binaryReader);
if (PacketType == 0)
{
ReadTelemetryData(stream, binaryReader);
}
else if (PacketType == 1)
{
ReadRaceData(stream, binaryReader);
}
else if (PacketType == 3)
{
ReadTimings(stream, binaryReader);
}
else if (PacketType == 4)
{
ReadGameData(stream, binaryReader);
}
else if (PacketType == 7)
{
ReadParticipantsStatsInfo(stream, binaryReader);
}
}

public void ReadBaseUDP(Stream stream, BinaryReader binaryReader)
{
stream.Position = 0;
PacketNumber = binaryReader.ReadUInt32();
CategoryPacketNumber = binaryReader.ReadUInt32();
PartialPacketIndex = binaryReader.ReadByte();
PartialPacketNumber = binaryReader.ReadByte();
PacketType = binaryReader.ReadByte();
PacketVersion = binaryReader.ReadByte();
}

public void ReadTelemetryData(Stream stream, BinaryReader binaryReader)
{
stream.Position = 12;

ViewedParticipantIndex = binaryReader.ReadSByte();
UnfilteredThrottle = binaryReader.ReadByte();
UnfilteredBrake = binaryReader.ReadByte();
UnfilteredSteering = binaryReader.ReadSByte();
UnfilteredClutch = binaryReader.ReadByte();
CarFlags = binaryReader.ReadByte();
OilTempCelsius = binaryReader.ReadInt16();
OilPressureKPa = binaryReader.ReadUInt16();
WaterTempCelsius = binaryReader.ReadInt16();
WaterPressureKpa = binaryReader.ReadUInt16();
FuelPressureKpa = binaryReader.ReadUInt16();
FuelCapacity = binaryReader.ReadByte();
Brake = binaryReader.ReadByte();
Throttle = binaryReader.ReadByte();
Clutch = binaryReader.ReadByte();
FuelLevel = binaryReader.ReadSingle();
Speed = binaryReader.ReadSingle();
Rpm = binaryReader.ReadUInt16();
MaxRpm = binaryReader.ReadUInt16();
Steering = binaryReader.ReadSByte();
GearNumGears = binaryReader.ReadByte();
BoostAmount = binaryReader.ReadByte();
CrashState = binaryReader.ReadByte();
OdometerKM = binaryReader.ReadSingle();

Orientation[0] = binaryReader.ReadSingle();
Orientation[1] = binaryReader.ReadSingle();
Orientation[2] = binaryReader.ReadSingle();

LocalVelocity[0] = binaryReader.ReadSingle();
LocalVelocity[1] = binaryReader.ReadSingle();
LocalVelocity[2] = binaryReader.ReadSingle();

WorldVelocity[0] = binaryReader.ReadSingle();
WorldVelocity[1] = binaryReader.ReadSingle();
WorldVelocity[2] = binaryReader.ReadSingle();

AngularVelocity[0] = binaryReader.ReadSingle();
AngularVelocity[1] = binaryReader.ReadSingle();
AngularVelocity[2] = binaryReader.ReadSingle();

LocalAcceleration[0] = binaryReader.ReadSingle();
LocalAcceleration[1] = binaryReader.ReadSingle();
LocalAcceleration[2] = binaryReader.ReadSingle();

WorldAcceleration[0] = binaryReader.ReadSingle();
WorldAcceleration[1] = binaryReader.ReadSingle();
WorldAcceleration[2] = binaryReader.ReadSingle();

ExtentsCentre[0] = binaryReader.ReadSingle();
ExtentsCentre[1] = binaryReader.ReadSingle();
ExtentsCentre[2] = binaryReader.ReadSingle();

TyreFlags[0] = binaryReader.ReadByte();
TyreFlags[1] = binaryReader.ReadByte();
TyreFlags[2] = binaryReader.ReadByte();
TyreFlags[3] = binaryReader.ReadByte();

Terrain[0] = binaryReader.ReadByte();
Terrain[1] = binaryReader.ReadByte();
Terrain[2] = binaryReader.ReadByte();
Terrain[3] = binaryReader.ReadByte();

TyreY[0] = binaryReader.ReadSingle();
TyreY[1] = binaryReader.ReadSingle();
TyreY[2] = binaryReader.ReadSingle();
TyreY[3] = binaryReader.ReadSingle();

TyreRPS[0] = binaryReader.ReadSingle();
TyreRPS[1] = binaryReader.ReadSingle();
TyreRPS[2] = binaryReader.ReadSingle();
TyreRPS[3] = binaryReader.ReadSingle();

TyreTemp[0] = binaryReader.ReadByte();
TyreTemp[1] = binaryReader.ReadByte();
TyreTemp[2] = binaryReader.ReadByte();
TyreTemp[3] = binaryReader.ReadByte();

TyreHeightAboveGround[0] = binaryReader.ReadSingle();
TyreHeightAboveGround[1] = binaryReader.ReadSingle();
TyreHeightAboveGround[2] = binaryReader.ReadSingle();
TyreHeightAboveGround[3] = binaryReader.ReadSingle();

TyreWear[0] = binaryReader.ReadByte();
TyreWear[1] = binaryReader.ReadByte();
TyreWear[2] = binaryReader.ReadByte();
TyreWear[3] = binaryReader.ReadByte();

BrakeDamage[0] = binaryReader.ReadByte();
BrakeDamage[1] = binaryReader.ReadByte();
BrakeDamage[2] = binaryReader.ReadByte();
BrakeDamage[3] = binaryReader.ReadByte();

SuspensionDamage[0] = binaryReader.ReadByte();
SuspensionDamage[1] = binaryReader.ReadByte();
SuspensionDamage[2] = binaryReader.ReadByte();
SuspensionDamage[3] = binaryReader.ReadByte();

BrakeTempCelsius[0] = binaryReader.ReadInt16();
BrakeTempCelsius[1] = binaryReader.ReadInt16();
BrakeTempCelsius[2] = binaryReader.ReadInt16();
BrakeTempCelsius[3] = binaryReader.ReadInt16();

TyreTreadTemp[0] = binaryReader.ReadUInt16();
TyreTreadTemp[1] = binaryReader.ReadUInt16();
TyreTreadTemp[2] = binaryReader.ReadUInt16();
TyreTreadTemp[3] = binaryReader.ReadUInt16();

TyreLayerTemp[0] = binaryReader.ReadUInt16();
TyreLayerTemp[1] = binaryReader.ReadUInt16();
TyreLayerTemp[2] = binaryReader.ReadUInt16();
TyreLayerTemp[3] = binaryReader.ReadUInt16();

TyreCarcassTemp[0] = binaryReader.ReadUInt16();
TyreCarcassTemp[1] = binaryReader.ReadUInt16();
TyreCarcassTemp[2] = binaryReader.ReadUInt16();
TyreCarcassTemp[3] = binaryReader.ReadUInt16();

TyreRimTemp[0] = binaryReader.ReadUInt16();
TyreRimTemp[1] = binaryReader.ReadUInt16();
TyreRimTemp[2] = binaryReader.ReadUInt16();
TyreRimTemp[3] = binaryReader.ReadUInt16();

TyreInternalAirTemp[0] = binaryReader.ReadUInt16();
TyreInternalAirTemp[1] = binaryReader.ReadUInt16();
TyreInternalAirTemp[2] = binaryReader.ReadUInt16();
TyreInternalAirTemp[3] = binaryReader.ReadUInt16();

TyreTempLeft[0] = binaryReader.ReadUInt16();
TyreTempLeft[1] = binaryReader.ReadUInt16();
TyreTempLeft[2] = binaryReader.ReadUInt16();
TyreTempLeft[3] = binaryReader.ReadUInt16();

TyreTempCenter[0] = binaryReader.ReadUInt16();
TyreTempCenter[1] = binaryReader.ReadUInt16();
TyreTempCenter[2] = binaryReader.ReadUInt16();
TyreTempCenter[3] = binaryReader.ReadUInt16();

TyreTempRight[0] = binaryReader.ReadUInt16();
TyreTempRight[1] = binaryReader.ReadUInt16();
TyreTempRight[2] = binaryReader.ReadUInt16();
TyreTempRight[3] = binaryReader.ReadUInt16();

WheelLocalPositionY[0] = binaryReader.ReadSingle();
WheelLocalPositionY[1] = binaryReader.ReadSingle();
WheelLocalPositionY[2] = binaryReader.ReadSingle();
WheelLocalPositionY[3] = binaryReader.ReadSingle();

RideHeight[0] = binaryReader.ReadSingle();
RideHeight[1] = binaryReader.ReadSingle();
RideHeight[2] = binaryReader.ReadSingle();
RideHeight[3] = binaryReader.ReadSingle();

SuspensionTravel[0] = binaryReader.ReadSingle();
SuspensionTravel[1] = binaryReader.ReadSingle();
SuspensionTravel[2] = binaryReader.ReadSingle();
SuspensionTravel[3] = binaryReader.ReadSingle();

SuspensionVelocity[0] = binaryReader.ReadSingle();
SuspensionVelocity[1] = binaryReader.ReadSingle();
SuspensionVelocity[2] = binaryReader.ReadSingle();
SuspensionVelocity[3] = binaryReader.ReadSingle();

SuspensionRideHeight[0] = binaryReader.ReadUInt16();
SuspensionRideHeight[1] = binaryReader.ReadUInt16();
SuspensionRideHeight[2] = binaryReader.ReadUInt16();
SuspensionRideHeight[3] = binaryReader.ReadUInt16();

AirPressure[0] = binaryReader.ReadUInt16();
AirPressure[1] = binaryReader.ReadUInt16();
AirPressure[2] = binaryReader.ReadUInt16();
AirPressure[3] = binaryReader.ReadUInt16();

EngineSpeed = binaryReader.ReadSingle();
EngineTorque = binaryReader.ReadSingle();

Wings[0] = binaryReader.ReadByte();
Wings[1] = binaryReader.ReadByte();

Handbrake = binaryReader.ReadByte();

AeroDamage = binaryReader.ReadByte();
EngineDamage = binaryReader.ReadByte();

Joypad0 = binaryReader.ReadUInt32();
DPad = binaryReader.ReadByte();

TyreCompound[0] = binaryReader.ReadBytes(TYRE_NAME_LENGTH_MAX);
TyreCompound[1] = binaryReader.ReadBytes(TYRE_NAME_LENGTH_MAX);
TyreCompound[2] = binaryReader.ReadBytes(TYRE_NAME_LENGTH_MAX);
TyreCompound[3] = binaryReader.ReadBytes(TYRE_NAME_LENGTH_MAX);

TurboBoostPressure = binaryReader.ReadSingle();

FullPosition = binaryReader.ReadSingle();

BrakeBias = binaryReader.ReadByte();

TickCount = binaryReader.ReadUInt32();

}

public void ReadRaceData(Stream stream, BinaryReader binaryReader)
{
stream.Position = 12;

WorldFastestLapTime = binaryReader.ReadSingle();
PersonalFastestLapTime = binaryReader.ReadSingle();
PersonalFastestSector1Time = binaryReader.ReadSingle();
PersonalFastestSector2Time = binaryReader.ReadSingle();
PersonalFastestSector3Time = binaryReader.ReadSingle();
WorldFastestSector1Time = binaryReader.ReadSingle();
WorldFastestSector2Time = binaryReader.ReadSingle();
WorldFastestSector3Time = binaryReader.ReadSingle();
TrackLength = binaryReader.ReadSingle();
//TrackLocation = binaryReader.ReadChars(TRACKNAME_LENGTH_MAX);
//TrackVariation = binaryReader.ReadChars(TRACKNAME_LENGTH_MAX);
//TranslatedTrackLocation = binaryReader.ReadChars(TRACKNAME_LENGTH_MAX);
//TranslatedTrackVariation = binaryReader.ReadChars(TRACKNAME_LENGTH_MAX);
LapsTimeInEvent = binaryReader.ReadUInt16();
EnforcedPitStopLap = binaryReader.ReadSByte();
}


public void ReadTimings(Stream stream, BinaryReader binaryReader)
{
stream.Position = 12;
NumberParticipants = binaryReader.ReadSByte();
ParticipantsChangedTimestamp = binaryReader.ReadUInt32();
EventTimeRemaining = binaryReader.ReadSingle();
SplitTimeAhead = binaryReader.ReadSingle();
SplitTimeBehind = binaryReader.ReadSingle();
SplitTime = binaryReader.ReadSingle();

for (int i = 0; i < 32; i++)
{
ParticipantInfo[i, 0] = Convert.ToDouble(binaryReader.ReadInt16()); //WorldPosition
ParticipantInfo[i, 1] = Convert.ToDouble(binaryReader.ReadInt16()); //WorldPosition
ParticipantInfo[i, 2] = Convert.ToDouble(binaryReader.ReadInt16()); //WorldPosition
ParticipantInfo[i, 3] = Convert.ToDouble(binaryReader.ReadInt16()); //Orientation
ParticipantInfo[i, 4] = Convert.ToDouble(binaryReader.ReadInt16()); //Orientation
ParticipantInfo[i, 5] = Convert.ToDouble(binaryReader.ReadInt16()); //Orientation
ParticipantInfo[i, 6] = Convert.ToDouble(binaryReader.ReadUInt16()); //sCurrentLapDistance
ParticipantInfo[i, 7] = Convert.ToDouble(binaryReader.ReadByte()) - 128; //sRacePosition
byte Sector_ALL = binaryReader.ReadByte();
var Sector_Extracted = Sector_ALL & 0x0F;
ParticipantInfo[i, 8] = Convert.ToDouble(Sector_Extracted + 1); //sSector
ParticipantInfo[i, 9] = Convert.ToDouble(binaryReader.ReadByte()); //sHighestFlag
ParticipantInfo[i, 10] = Convert.ToDouble(binaryReader.ReadByte()); //sPitModeSchedule
ParticipantInfo[i, 11] = Convert.ToDouble(binaryReader.ReadUInt16());//sCarIndex
ParticipantInfo[i, 12] = Convert.ToDouble(binaryReader.ReadByte()); //sRaceState
ParticipantInfo[i, 13] = Convert.ToDouble(binaryReader.ReadByte()); //sCurrentLap
ParticipantInfo[i, 14] = Convert.ToDouble(binaryReader.ReadSingle()); //sCurrentTime
ParticipantInfo[i, 15] = Convert.ToDouble(binaryReader.ReadSingle()); //sCurrentSectorTime
}

}

public void ReadGameData(Stream stream, BinaryReader binaryReader)
{
stream.Position = 12;

BuildVersionNumber = binaryReader.ReadUInt16();
GameState = binaryReader.ReadByte();
AmbientTemperature = binaryReader.ReadSByte();
TrackTemperature = binaryReader.ReadSByte();
RainDensity = Convert.ToDouble(binaryReader.ReadByte());
SnowDensity = Convert.ToDouble(binaryReader.ReadByte());
WindSpeed = binaryReader.ReadSByte();
WindDirectionX = binaryReader.ReadSByte();
WindDirectionY = binaryReader.ReadSByte();
}

public void ReadParticipantsStatsInfo(Stream stream, BinaryReader binaryReader)
{
stream.Position = 12;

for (int i = 0; i < 32; i++)
{
ParticipantStatsInfo[i, 1] = Convert.ToDouble(binaryReader.ReadSingle()); //FastestLap
ParticipantStatsInfo[i, 2] = Convert.ToDouble(binaryReader.ReadSingle()); //LastLap
ParticipantStatsInfo[i, 3] = Convert.ToDouble(binaryReader.ReadSingle()); //LastSectorTime
ParticipantStatsInfo[i, 4] = Convert.ToDouble(binaryReader.ReadSingle()); //FastestSector1
ParticipantStatsInfo[i, 5] = Convert.ToDouble(binaryReader.ReadSingle()); //FastestSector2
ParticipantStatsInfo[i, 6] = Convert.ToDouble(binaryReader.ReadSingle()); //FastestSector3

}

}

public void close_UDP_Connection()
{
listener.Close();
}

public UdpClient listener
{
get
{
return _listener;
}
set
{
_listener = value;
}
}

public IPEndPoint groupEP
{
get
{
return _groupEP;
}
set
{
_groupEP = value;
}
}

public UInt32 PacketNumber
{
get
{
return _PacketNumber;
}
set
{
_PacketNumber = value;
}
}

public UInt32 CategoryPacketNumber
{
get
{
return _CategoryPacketNumber;
}
set
{
_CategoryPacketNumber = value;
}
}

public byte PartialPacketIndex
{
get
{
return _PartialPacketIndex;
}
set
{
_PartialPacketIndex = value;
}
}

public byte PartialPacketNumber
{
get
{
return _PartialPacketNumber;
}
set
{
_PartialPacketNumber = value;
}
}

public byte PacketType
{
get
{
return _PacketType;
}
set
{
_PacketType = value;
}
}

public byte PacketVersion
{
get
{
return _PacketVersion;
}
set
{
_PacketVersion = value;
}
}

public sbyte ViewedParticipantIndex
{
get
{
return _ViewedParticipantIndex;
}
set
{
_ViewedParticipantIndex = value;
}
}

public byte UnfilteredThrottle
{
get
{
return _UnfilteredThrottle;
}
set
{
_UnfilteredThrottle = value;
}
}

public byte UnfilteredBrake
{
get
{
return _UnfilteredBrake;
}
set
{
_UnfilteredBrake = value;
}
}

public sbyte UnfilteredSteering
{
get
{
return _UnfilteredSteering;
}
set
{
_UnfilteredSteering = value;
}
}

public byte UnfilteredClutch
{
get
{
return _UnfilteredClutch;
}
set
{
_UnfilteredClutch = value;
}
}

public byte CarFlags
{
get
{
return _CarFlags;
}
set
{
_CarFlags = value;
}
}

public Int16 OilTempCelsius
{
get
{
return _OilTempCelsius;
}
set
{
_OilTempCelsius = value;
}
}

public UInt16 OilPressureKPa
{
get
{
return _OilPressureKPa;
}
set
{
_OilPressureKPa = value;
}
}

public Int16 WaterTempCelsius
{
get
{
return _WaterTempCelsius;
}
set
{
_WaterTempCelsius = value;
}
}

public UInt16 WaterPressureKpa
{
get
{
return _WaterPressureKpa;
}
set
{
_WaterPressureKpa = value;
}
}

public UInt16 FuelPressureKpa
{
get
{
return _FuelPressureKpa;
}
set
{
_FuelPressureKpa = value;
}
}

public byte FuelCapacity
{
get
{
return _FuelCapacity;
}
set
{
_FuelCapacity = value;
}
}

public byte Brake
{
get
{
return _Brake;
}
set
{
_Brake = value;
}
}

public byte Throttle
{
get
{
return _Throttle;
}
set
{
_Throttle = value;
}
}

public byte Clutch
{
get
{
return _Clutch;
}
set
{
_Clutch = value;
}
}

public float FuelLevel
{
get
{
return _FuelLevel;
}
set
{
_FuelLevel = value;
}
}

public float Speed
{
get
{
return _Speed;
}
set
{
_Speed = value;
}
}

public UInt16 Rpm
{
get
{
return _Rpm;
}
set
{
_Rpm = value;
}
}

public UInt16 MaxRpm
{
get
{
return _MaxRpm;
}
set
{
_MaxRpm = value;
}
}

public sbyte Steering
{
get
{
return _Steering;
}
set
{
_Steering = value;
}
}

public byte GearNumGears
{
get
{
return _GearNumGears;
}
set
{
_GearNumGears = value;
}
}

public byte BoostAmount
{
get
{
return _BoostAmount;
}
set
{
_BoostAmount = value;
}
}

public byte CrashState
{
get
{
return _CrashState;
}
set
{
_CrashState = value;
}
}

public float OdometerKM
{
get
{
return _OdometerKM;
}
set
{
_OdometerKM = value;
}
}

public float[] Orientation
{
get
{
return _Orientation;
}
set
{
_Orientation = value;
}
}

public float[] LocalVelocity
{
get
{
return _LocalVelocity;
}
set
{
_LocalVelocity = value;
}
}

public float[] WorldVelocity
{
get
{
return _WorldVelocity;
}
set
{
_WorldVelocity = value;
}
}

public float[] AngularVelocity
{
get
{
return _AngularVelocity;
}
set
{
_AngularVelocity = value;
}
}

public float[] LocalAcceleration
{
get
{
return _LocalAcceleration;
}
set
{
_LocalAcceleration = value;
}
}

public float[] WorldAcceleration
{
get
{
return _WorldAcceleration;
}
set
{
_WorldAcceleration = value;
}
}

public float[] ExtentsCentre
{
get
{
return _ExtentsCentre;
}
set
{
_ExtentsCentre = value;
}
}

public byte[] TyreFlags
{
get
{
return _TyreFlags;
}
set
{
_TyreFlags = value;
}
}

public byte[] Terrain
{
get
{
return _Terrain;
}
set
{
_Terrain = value;
}
}

public float[] TyreY
{
get
{
return _TyreY;
}
set
{
_TyreY = value;
}
}

public float[] TyreRPS
{
get
{
return _TyreRPS;
}
set
{
_TyreRPS = value;
}
}

public byte[] TyreTemp
{
get
{
return _TyreTemp;
}
set
{
_TyreTemp = value;
}
}

public float[] TyreHeightAboveGround
{
get
{
return _TyreHeightAboveGround;
}
set
{
_TyreHeightAboveGround = value;
}
}

public byte[] TyreWear
{
get
{
return _TyreWear;
}
set
{
_TyreWear = value;
}
}

public byte[] BrakeDamage
{
get
{
return _BrakeDamage;
}
set
{
_BrakeDamage = value;
}
}

public byte[] SuspensionDamage
{
get
{
return _SuspensionDamage;
}
set
{
_SuspensionDamage = value;
}
}

public Int16[] BrakeTempCelsius
{
get
{
return _BrakeTempCelsius;
}
set
{
_BrakeTempCelsius = value;
}
}

public UInt16[] TyreTreadTemp
{
get
{
return _TyreTreadTemp;
}
set
{
_TyreTreadTemp = value;
}
}

public UInt16[] TyreLayerTemp
{
get
{
return _TyreLayerTemp;
}
set
{
_TyreLayerTemp = value;
}
}

public UInt16[] TyreCarcassTemp
{
get
{
return _TyreCarcassTemp;
}
set
{
_TyreCarcassTemp = value;
}
}

public UInt16[] TyreRimTemp
{
get
{
return _TyreRimTemp;
}
set
{
_TyreRimTemp = value;
}
}

public UInt16[] TyreInternalAirTemp
{
get
{
return _TyreInternalAirTemp;
}
set
{
_TyreInternalAirTemp = value;
}
}

public UInt16[] TyreTempLeft
{
get
{
return _TyreTempLeft;
}
set
{
_TyreTempLeft = value;
}
}

public UInt16[] TyreTempCenter
{
get
{
return _TyreTempCenter;
}
set
{
_TyreTempCenter = value;
}
}

public UInt16[] TyreTempRight
{
get
{
return _TyreTempRight;
}
set
{
_TyreTempRight = value;
}
}

public float[] WheelLocalPositionY
{
get
{
return _WheelLocalPositionY;
}
set
{
_WheelLocalPositionY = value;
}
}

public float[] RideHeight
{
get
{
return _RideHeight;
}
set
{
_RideHeight = value;
}
}

public float[] SuspensionTravel
{
get
{
return _SuspensionTravel;
}
set
{
_SuspensionTravel = value;
}
}

public float[] SuspensionVelocity
{
get
{
return _SuspensionVelocity;
}
set
{
_SuspensionVelocity = value;
}
}

public UInt16[] SuspensionRideHeight
{
get
{
return _SuspensionRideHeight;
}
set
{
_SuspensionRideHeight = value;
}
}

public UInt16[] AirPressure
{
get
{
return _AirPressure;
}
set
{
_AirPressure = value;
}
}

public float EngineSpeed
{
get
{
return _EngineSpeed;
}
set
{
_EngineSpeed = value;
}
}

public float EngineTorque
{
get
{
return _EngineTorque;
}
set
{
_EngineTorque = value;
}
}

public byte[] Wings
{
get
{
return _Wings;
}
set
{
_Wings = value;
}
}

public byte Handbrake
{
get
{
return _Handbrake;
}
set
{
_Handbrake = value;
}
}

public byte AeroDamage
{
get
{
return _AeroDamage;
}
set
{
_AeroDamage = value;
}
}

public byte EngineDamage
{
get
{
return _EngineDamage;
}
set
{
_EngineDamage = value;
}
}

public UInt32 Joypad0
{
get
{
return _Joypad0;
}
set
{
_Joypad0 = value;
}
}

public byte DPad
{
get
{
return _DPad;
}
set
{
_DPad = value;
}
}

public byte[][] TyreCompound
{
get
{
return _TyreCompound;
}
set
{
_TyreCompound = value;
}
}

public float TurboBoostPressure
{
get
{
return _TurboBoostPressure;
}
set
{
_TurboBoostPressure = value;
}
}

public float FullPosition
{
get
{
return _FullPosition;
}
set
{
_FullPosition = value;
}
}

public byte BrakeBias
{
get
{
return _BrakeBias;
}
set
{
_BrakeBias = value;
}
}

public UInt32 TickCount
{
get
{
return _TickCount;
}
set
{
_TickCount = value;
}
}

// RaceData
public float WorldFastestLapTime
{
get
{
return _WorldFastestLapTime;
}
set
{
_WorldFastestLapTime = value;
}
}

public float PersonalFastestLapTime
{
get
{
return _PersonalFastestLapTime;
}
set
{
_PersonalFastestLapTime = value;
}
}

public float PersonalFastestSector1Time
{
get
{
return _PersonalFastestSector1Time;
}
set
{
_PersonalFastestSector1Time = value;
}
}

public float PersonalFastestSector2Time
{
get
{
return _PersonalFastestSector2Time;
}
set
{
_PersonalFastestSector2Time = value;
}
}

public float PersonalFastestSector3Time
{
get
{
return _PersonalFastestSector3Time;
}
set
{
_PersonalFastestSector3Time = value;
}
}

public float WorldFastestSector1Time
{
get
{
return _WorldFastestSector1Time;
}
set
{
_WorldFastestSector1Time = value;
}
}

public float WorldFastestSector2Time
{
get
{
return _WorldFastestSector2Time;
}
set
{
_WorldFastestSector2Time = value;
}
}

public float WorldFastestSector3Time
{
get
{
return _WorldFastestSector3Time;
}
set
{
_WorldFastestSector3Time = value;
}
}

public float TrackLength
{
get
{
return _TrackLength;
}
set
{
_TrackLength = value;
}
}

// public char[] TrackLocation
// {
// get
// {
// return _TrackLocation;
// }
// set
// {
// _TrackLocation = value;
// }
// }

// public char[] TrackVariation
// {
// get
// {
// return _TrackVariation;
// }
// set
// {
// _TrackVariation = value;
// }
// }

// public char[] TranslatedTrackLocation
// {
// get
// {
// return _TranslatedTrackLocation;
// }
// set
// {
// _TranslatedTrackLocation = value;
// }
// }

// public char[] TranslatedTrackVariation
// {
// get
// {
// return _TranslatedTrackVariation;
// }
// set
// {
// _TranslatedTrackVariation = value;
// }
// }

public UInt16 LapsTimeInEvent
{
get
{
return _LapsTimeInEvent;
}
set
{
_LapsTimeInEvent = value;
}
}

public sbyte EnforcedPitStopLap
{
get
{
return _EnforcedPitStopLap;
}
set
{
_EnforcedPitStopLap = value;
}
}

// Timings
public sbyte NumberParticipants
{
get
{
return _NumberParticipants;
}
set
{
_NumberParticipants = value;
}
}

public UInt32 ParticipantsChangedTimestamp
{
get
{
return _ParticipantsChangedTimestamp;
}
set
{
_ParticipantsChangedTimestamp = value;
}
}

public float EventTimeRemaining
{
get
{
return _EventTimeRemaining;
}
set
{
_EventTimeRemaining = value;
}
}

public float SplitTimeAhead
{
get
{
return _SplitTimeAhead;
}
set
{
_SplitTimeAhead = value;
}
}

public float SplitTimeBehind
{
get
{
return _SplitTimeBehind;
}
set
{
_SplitTimeBehind = value;
}
}

public float SplitTime
{
get
{
return _SplitTime;
}
set
{
_SplitTime = value;
}
}

public double[,] ParticipantInfo
{
get
{
return _ParticipantInfo;
}
set
{
_ParticipantInfo = value;
}
}

// GameState
public UInt16 BuildVersionNumber
{
get
{
return _BuildVersionNumber;
}
set
{
_BuildVersionNumber = value;
}
}

public byte GameState
{
get
{
return _GameState;
}
set
{
_GameState = value;
}
}

public sbyte AmbientTemperature
{
get
{
return _AmbientTemperature;
}
set
{
_AmbientTemperature = value;
}
}

public sbyte TrackTemperature
{
get
{
return _TrackTemperature;
}
set
{
_TrackTemperature = value;
}
}

public double RainDensity
{
get
{
return _RainDensity;
}
set
{
_RainDensity = value;
}
}

public double SnowDensity
{
get
{
return _SnowDensity;
}
set
{
_SnowDensity = value;
}
}

public sbyte WindSpeed
{
get
{
return _WindSpeed;
}
set
{
_WindSpeed = value;
}
}

public sbyte WindDirectionX
{
get
{
return _WindDirectionX;
}
set
{
_WindDirectionX = value;
}
}

public sbyte WindDirectionY
{
get
{
return _WindDirectionY;
}
set
{
_WindDirectionY = value;
}
}

// Participanst stats info
public double[,] ParticipantStatsInfo
{
get
{
return _ParticipantStatsInfo;
}
set
{
_ParticipantStatsInfo = value;
}
}

}
}


Then this is my program.cs file:



using PcarsUDP;
using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using WiringPi;
using System.Threading;
using System.Data;
using System.Windows.Forms;


namespace UDP_Example
{
class Program
{
//Define LED pin
const int PitPin = 18; //Physical pin 18
const int WaterPin = 16;
const int OilPin = 12;
const int DamagePin = 10;
const int RGBBluePin = 40; //Physical pin 40 = GPIO 16
const int RGBGreenPin = 36; //Physical pin 36
const int RGBRedPin = 38; //Physical pin 38
const int FuelPin = 8;
const int Bar1Pin = 3; //Physical pin 3
const int Bar2Pin = 5; //Physical pin 5
const int Bar3Pin = 7; //Physical pin 7
const int Bar4Pin = 11; //Physical pin 11
const int Bar5Pin = 13; //Physical pin 13
const int Bar6Pin = 15; //Physical pin 15
const int Bar7Pin = 19; //Physical pin 19
const int Bar8Pin = 21; //Physical pin 21
const int Bar9Pin = 23; //Physical pin 23
const int Bar10Pin = 29; //Physical pin 29


static void Main(string[] args)
{


UdpClient listener = new UdpClient(5606); //Create a UDPClient object
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, 5606); //Start recieving data from any IP listening on port 5606 (port for PCARS2)


PCars2_UDP uDP = new PCars2_UDP(listener, groupEP); //Create an UDP object that will retrieve telemetry values from in game.

// Tell the user that we are attempting to start the GPIO
Console.WriteLine("Initializing GPIO Interface");

// The WiringPiSetup method is static and returns either true or false
// Any value less than 0 represents a failure
if (Init.WiringPiSetupPhys() == 0)
{
Console.WriteLine("GPIO Interface initializing OK");
}
else
{
Console.WriteLine("Error when initializing GPIO interface");
}
//ensures that it initializes the GPIO interface and reports ready to work. We will use Physical Pin Numbers

// Tell the Pi that we will send data out the GPIO
GPIO.pinMode(PitPin, (int)GPIO.GPIOpinmode.Output);
GPIO.pinMode(WaterPin, (int)GPIO.GPIOpinmode.Output);
GPIO.pinMode(OilPin, (int)GPIO.GPIOpinmode.Output);
GPIO.pinMode(DamagePin, (int)GPIO.GPIOpinmode.Output);
GPIO.pinMode(RGBGreenPin, (int)GPIO.GPIOpinmode.Output);
GPIO.pinMode(RGBBluePin, (int)GPIO.GPIOpinmode.Output);
GPIO.pinMode(RGBRedPin, (int)GPIO.GPIOpinmode.Output);
GPIO.pinMode(FuelPin, (int)GPIO.GPIOpinmode.Output);
GPIO.pinMode(Bar1Pin, (int)GPIO.GPIOpinmode.Output);
GPIO.pinMode(Bar2Pin, (int)GPIO.GPIOpinmode.Output);
GPIO.pinMode(Bar3Pin, (int)GPIO.GPIOpinmode.Output);
GPIO.pinMode(Bar4Pin, (int)GPIO.GPIOpinmode.Output);
GPIO.pinMode(Bar5Pin, (int)GPIO.GPIOpinmode.Output);
GPIO.pinMode(Bar6Pin, (int)GPIO.GPIOpinmode.Output);
GPIO.pinMode(Bar7Pin, (int)GPIO.GPIOpinmode.Output);
GPIO.pinMode(Bar8Pin, (int)GPIO.GPIOpinmode.Output);
GPIO.pinMode(Bar9Pin, (int)GPIO.GPIOpinmode.Output);
GPIO.pinMode(Bar10Pin, (int)GPIO.GPIOpinmode.Output);

//Turn all LEDs ON for 1 second

GPIO.digitalWrite(PitPin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(WaterPin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(OilPin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(DamagePin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(RGBGreenPin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(RGBBluePin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(RGBRedPin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(FuelPin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar1Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar2Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar3Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar4Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar5Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar6Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar7Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar8Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar9Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar10Pin, (int)GPIO.GPIOpinvalue.High);
Thread.Sleep(1000);

// Turn all LEDs off
GPIO.digitalWrite(PitPin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(WaterPin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(OilPin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(DamagePin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(RGBGreenPin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(RGBBluePin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(RGBRedPin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(FuelPin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar1Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar2Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar3Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar4Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar5Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar6Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar7Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar8Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar9Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar10Pin, (int)GPIO.GPIOpinvalue.Low);

// Tell the user that GPIO Initialization Completed successfully
//Console.WriteLine("GPIO Initialization Complete");


Console.WriteLine("wait for udp");

// Test writing a text file to path
//string path = @"MyTest.txt";
//if (!File.Exists(path))
// {
// Create a file to write to.
// using (StreamWriter sw = File.CreateText(path))
// {
// sw.WriteLine("Thankyou");
// sw.WriteLine("And");
// sw.WriteLine("Comeagain");
// }
//}

// Define variables

int FuelLED = 0;
int OldFuelLED = 0;
int SuspDamage = 0;
string[] TireNameArray = { "Front Left", "Front Right", "Rear Left", "Rear Right" };
float BBfloat = 0.0F; // Can't understand the Brake Bias value

// These values are counteres, for slowing down the printing to console. Each loop increases values by 1.
// When counter is high enough, the program prints UDP value to console and resets counter.
int BBshown = 0;
int speedcount = 800;
int tbpShown = 0;
int OdometerShown = 0;
int gngShown = 300;
int npShown = 1350;
int staShown = 0;
int piShown = 900;
int owShown = 900;
int ttShown = 0;
int trtShown = 500;
int ambShown = 300;
int rainShown = 1400;
int ChequeredFlag = 0;
int PitShown = 0;



while (true)
{
uDP.readPackets(); //Read Packets ever loop iteration

/* Participant info */

piShown++;
if (piShown >= 100)
{
// Current total time
double CurrentTotalTime = (uDP.ParticipantInfo[uDP.ViewedParticipantIndex, 14]) / 60;
var timeSpanTotalTime = TimeSpan.FromMinutes(CurrentTotalTime);
int hh1 = timeSpanTotalTime.Hours;
int mm1 = timeSpanTotalTime.Minutes;
int ss1 = timeSpanTotalTime.Seconds;
int ms1 = timeSpanTotalTime.Milliseconds;
Console.WriteLine("Current lap time: {0:00}:{1:00}:{2:00}:{3:000}", hh1, mm1, ss1, ms1); //Number of digits after colon sets number of digits displayed

//Current sector time
double CurrentSectorTime = (uDP.ParticipantInfo[uDP.ViewedParticipantIndex, 15]) / 60;
var timeSpanCurrentSector = TimeSpan.FromMinutes(CurrentSectorTime);
int hh2 = timeSpanCurrentSector.Hours;
int mm2 = timeSpanCurrentSector.Minutes;
int ss2 = timeSpanCurrentSector.Seconds;
int ms2 = timeSpanCurrentSector.Milliseconds;
Console.WriteLine("Current sector time: {0:00}:{1:00}:{2:00}:{3:000}", hh2, mm2, ss2, ms2);

// Current world record lap time
//float WorldFastestLapTime = (uDP.WorldFastestLapTime / 60);
//var timeSpanWRLapTime = TimeSpan.FromMinutes(WorldFastestLapTime);
//int hh4 = timeSpanWRLapTime.Hours;
//int mm4 = timeSpanWRLapTime.Minutes;
//int ss4 = timeSpanWRLapTime.Seconds;
//int ms4 = timeSpanWRLapTime.Milliseconds;
//Console.WriteLine("Current World Record: {0:00}:{1:00}:{2:00}:{3:000}", hh4, mm4, ss4, ms4);

// Race position
double RacePosition = (uDP.ParticipantInfo[uDP.ViewedParticipantIndex, 7]); //Get race position
int RaceParticipants = uDP.NumberParticipants; //Get number of participants
Console.WriteLine("Position: " + RacePosition + " out of " + RaceParticipants);

//Current sector
double CurrentSector = (uDP.ParticipantInfo[uDP.ViewedParticipantIndex, 8]);
Console.WriteLine("Current sector is: " + CurrentSector);

//Current time
double CurrentTime = (uDP.ParticipantInfo[uDP.ViewedParticipantIndex, 14]);
Console.WriteLine("Current time is: " + CurrentTime);



//Track Location
//Console.WriteLine("Track is " + uDP.TrackLocation);
//Console.WriteLine("Variation: " + uDP.TrackVariation);

// Current Lap
double CurrentLap = (uDP.ParticipantInfo[uDP.ViewedParticipantIndex, 13]);
Console.WriteLine("Current lap number is: " + CurrentLap);



// Fastest lap time
double FastestLapTime = (uDP.ParticipantStatsInfo[uDP.ViewedParticipantIndex,1]) / 60;
var timeSpanFLLapTime = TimeSpan.FromMinutes(FastestLapTime);
int hh6 = timeSpanFLLapTime.Hours;
int mm6 = timeSpanFLLapTime.Minutes;
int ss6 = timeSpanFLLapTime.Seconds;
int ms6 = timeSpanFLLapTime.Milliseconds;
Console.WriteLine("Fastest Lap Time: {0:00}:{1:00}:{2:00}:{3:000}", hh6, mm6, ss6, ms6);




// Last lap time
double LastLapTime = (uDP.ParticipantStatsInfo[uDP.ViewedParticipantIndex, 2]) / 60;
var timeSpanLLLapTime = TimeSpan.FromMinutes(LastLapTime);
int hh7 = timeSpanLLLapTime.Hours;
int mm7 = timeSpanLLLapTime.Minutes;
int ss7 = timeSpanLLLapTime.Seconds;
int ms7 = timeSpanLLLapTime.Milliseconds;
Console.WriteLine("Last Lap Time: {0:00}:{1:00}:{2:00}:{3:000}", hh7, mm7, ss7, ms7);

// Wind speed
Console.WriteLine("Wind speed is " + uDP.WindSpeed);

// Car index
double CarIndex = (uDP.ParticipantInfo[uDP.ViewedParticipantIndex, 11]);
Console.WriteLine("CarIndex is " + CarIndex);

// RaceState
double RaceState = (uDP.ParticipantInfo[uDP.ViewedParticipantIndex, 12]);
Console.WriteLine("RaceState is " + RaceState);

//Event time left
float EventTimeRemaining = uDP.EventTimeRemaining;
Console.WriteLine("Event time remaining is " + EventTimeRemaining);

// LapDistance
double LapDistance = (uDP.ParticipantInfo[uDP.ViewedParticipantIndex, 6]);
Console.WriteLine("Lap distance is " + LapDistance);

piShown = 0;

}

////************************
//// Pit mode schedule
////************************
double PitModeSchedule = (uDP.ParticipantInfo[uDP.ViewedParticipantIndex, 10]);
if (PitShown < 250)
{
if (PitModeSchedule == 0)
{
//Console.WriteLine("No pit requested");
GPIO.digitalWrite(PitPin, (int)GPIO.GPIOpinvalue.Low); //Turn Pit led off
}
else if (PitModeSchedule != 0)
{
//Console.WriteLine("Pit requested");
GPIO.digitalWrite(PitPin, (int)GPIO.GPIOpinvalue.High); //Turn Pit led on
}
PitShown = 0;
}
//*****************************
// Flag colours
//*****************************
double HighestFlag = (uDP.ParticipantInfo[uDP.ViewedParticipantIndex, 9]);
//Console.WriteLine(HighestFlag);


if (HighestFlag == 1)
{
//Console.WriteLine("Green flag, go go go!");
GPIO.digitalWrite(RGBBluePin, (int)GPIO.GPIOpinvalue.Low); //Turn on green LED
GPIO.digitalWrite(RGBGreenPin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(RGBRedPin, (int)GPIO.GPIOpinvalue.Low);
}
else if (HighestFlag == 2)
{
//Console.WriteLine("Blue flag, blue flag!");
GPIO.digitalWrite(RGBBluePin, (int)GPIO.GPIOpinvalue.High); //Turn on blue LED
GPIO.digitalWrite(RGBGreenPin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(RGBRedPin, (int)GPIO.GPIOpinvalue.Low);
}
else if (HighestFlag == 3)
{
//Console.WriteLine("White flag, slow car in area, watch out");
GPIO.digitalWrite(RGBBluePin, (int)GPIO.GPIOpinvalue.High); //Turn on ALL LED = white
GPIO.digitalWrite(RGBGreenPin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(RGBRedPin, (int)GPIO.GPIOpinvalue.High);
}
else if (HighestFlag == 4)
{
//Console.WriteLine("Final lap, c'mon push!");
GPIO.digitalWrite(RGBBluePin, (int)GPIO.GPIOpinvalue.High); //Turn on ALL LED = white
GPIO.digitalWrite(RGBGreenPin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(RGBRedPin, (int)GPIO.GPIOpinvalue.High);
}
else if (HighestFlag == 5)
{
//Console.WriteLine("Red flag! Huge pileup ahead.");
GPIO.digitalWrite(RGBBluePin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(RGBGreenPin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(RGBRedPin, (int)GPIO.GPIOpinvalue.High); //Turn on red LED
}
else if (HighestFlag == 6)
{
//Console.WriteLine("Yellow flag, slow down");
GPIO.digitalWrite(RGBBluePin, (int)GPIO.GPIOpinvalue.Low); //Turn on blue and green LED = yellow
GPIO.digitalWrite(RGBGreenPin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(RGBRedPin, (int)GPIO.GPIOpinvalue.High);
}
else if (HighestFlag == 7)
{
//Console.WriteLine("Double yellow, watch out");
GPIO.digitalWrite(RGBBluePin, (int)GPIO.GPIOpinvalue.Low); //Turn on blue and green LED = yellow
GPIO.digitalWrite(RGBGreenPin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(RGBRedPin, (int)GPIO.GPIOpinvalue.High);
}
else if (HighestFlag == 8)
{
//Console.WriteLine("Black and white flag, unsportsmanlike conduct");
GPIO.digitalWrite(RGBBluePin, (int)GPIO.GPIOpinvalue.High); //Turn on blue and green LED = yellow
GPIO.digitalWrite(RGBGreenPin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(RGBRedPin, (int)GPIO.GPIOpinvalue.Low);
}
else if (HighestFlag == 9)
{
//Console.WriteLine("Black and orange, mechanical failure");
}
else if (HighestFlag == 10)
{
//Console.WriteLine("Black flag, disqualified!");
}
else if (HighestFlag == 11)
{
Console.WriteLine("Chequered flag!");
if (ChequeredFlag == 0)
{
GPIO.digitalWrite(RGBBluePin, (int)GPIO.GPIOpinvalue.Low); //Turn off all LED
GPIO.digitalWrite(RGBGreenPin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(RGBRedPin, (int)GPIO.GPIOpinvalue.Low);
Thread.Sleep(250);
GPIO.digitalWrite(RGBBluePin, (int)GPIO.GPIOpinvalue.High); //Turn on ALL LED = white
GPIO.digitalWrite(RGBGreenPin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(RGBRedPin, (int)GPIO.GPIOpinvalue.High);
Thread.Sleep(250);
GPIO.digitalWrite(RGBBluePin, (int)GPIO.GPIOpinvalue.Low); //Turn off all LED
GPIO.digitalWrite(RGBGreenPin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(RGBRedPin, (int)GPIO.GPIOpinvalue.Low);
Thread.Sleep(250);
GPIO.digitalWrite(RGBBluePin, (int)GPIO.GPIOpinvalue.High); //Turn on ALL LED = white
GPIO.digitalWrite(RGBGreenPin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(RGBRedPin, (int)GPIO.GPIOpinvalue.High);
Thread.Sleep(250);
GPIO.digitalWrite(RGBBluePin, (int)GPIO.GPIOpinvalue.Low); //Turn off all LED
GPIO.digitalWrite(RGBGreenPin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(RGBRedPin, (int)GPIO.GPIOpinvalue.Low);
ChequeredFlag = 1;
}

}

// If not in race, turn off RGB led
//if ((uDP.ParticipantInfo[uDP.ViewedParticipantIndex, 12]) != 2)
//{
// GPIO.digitalWrite(RGBBluePin, (int)GPIO.GPIOpinvalue.Low);
// GPIO.digitalWrite(RGBGreenPin, (int)GPIO.GPIOpinvalue.Low);
// GPIO.digitalWrite(RGBRedPin, (int)GPIO.GPIOpinvalue.Low);
//}

/*********************************
* GearsNumGears
*********************************/
gngShown++;
if (gngShown > 1000)
{
string gearhex = uDP.GearNumGears.ToString("X2");
char CurrentGear = gearhex[1];
char AvailableGears = gearhex[0];
Console.WriteLine("Current gear is " + CurrentGear);
Console.WriteLine("Total available gear is " + AvailableGears);

gngShown = 0;
}

// Track Temperature
trtShown++;
if (trtShown > 1000)
{
Console.WriteLine("Track temp is " + uDP.TrackTemperature);
trtShown = 0;
}

// Ambient Temperature
ambShown++;
if (ambShown > 1000)
{
Console.WriteLine("Ambient temp is " + uDP.AmbientTemperature);
ambShown = 0;
}

// Rain Density
rainShown++;
if (rainShown > 1500)
{
Console.WriteLine("Rain density is " + uDP.RainDensity);
rainShown = 0;
}


// Tyre temps
ttShown++;
if (ttShown > 1200)
{
for (int i = 0; i < 4; i++)
{

int TTemp = (uDP.TyreTempCenter[i]);
Console.WriteLine(TireNameArray[i] + " is " + TTemp + " degrees Celsius");
ttShown = 0;
}
}



/*********************************
* Speed
*********************************/

var speedkmh = uDP.Speed * 3.6; // Convert speed from m/s to km/h
int speed = Convert.ToInt16(speedkmh);


speedcount++;
if (speedcount > 1000) // Only show speed at a certain interval
{
Console.WriteLine("Current speed is " + speed + " km/h");
speedcount = 0;
}


/************************************
* Display tire compound for each tire (this seems to crash the program sometimes, therefor disabled
*************************************/
//tireShown++;
//if (tireShown > 2000)
//{
// for (int i = 0; i < 4; i++)
// {
// tireName = System.Text.Encoding.UTF8.GetString(uDP.TyreCompound[i]);
// Console.WriteLine(TireNameArray[i] + " has " + tireName);
// tireShown = 0;
// }
//}

/*************************************
* Brake Bias (this is wrong, I can't decode it properly)
*************************************/
//string bbhex = uDP.BrakeBias.ToString("X2");
//Console.WriteLine(bbhex);
//string bbfront = ((255 - bbhex) / 2.55);
if (BBshown == 0)
{
Console.WriteLine("Front Brake Bias is " + BBfloat);
Console.WriteLine("Rear Brake Bias is " + (100 - BBfloat));
BBshown = 1;
}



/* NumberParticipants */
npShown++;
if (npShown > 1500)
{
Console.WriteLine("Number of participants is " + uDP.NumberParticipants);
npShown = 0;

}


// Split time ahead
staShown++;
if (staShown > 350)
{
float SplitTimeAhead = (uDP.SplitTimeAhead) / 60;
var timeSpanSTA = TimeSpan.FromMinutes(SplitTimeAhead);
int hh5 = timeSpanSTA.Hours;
int mm5 = timeSpanSTA.Minutes;
int ss5 = timeSpanSTA.Seconds;
int ms5 = timeSpanSTA.Milliseconds;
Console.WriteLine("Split time ahead is: {0:00}:{1:00}:{2:00}:{3:000}", hh5, mm5, ss5, ms5);
staShown = 0;
}
/*************************
* Turbo Boost Pressure (Can't decode value properly)
/************************/

if (tbpShown == 0)
{
float tbpf = uDP.TurboBoostPressure;
//Console.WriteLine("Gear is " + tbpf);
var tbpd = Convert.ToDecimal(tbpf);
Console.WriteLine("TurboBoostPressure is " + tbpd);
tbpShown = 1;
}


/* Odometer in km */
if (OdometerShown == 0)
{
Console.WriteLine("Odometer is showing " + uDP.OdometerKM + " km");
OdometerShown = 1;
}




//For Wheel Arrays 0 = Front Left, 1 = Front Right, 2 = Rear Left, 3 = Rear Right.
// Example:
// Console.WriteLine(uDP.TyreTempCenter[0]); //Tyre Temp Center, Front Left Tyre

/***************************************
// * Water and Oil Temp warnings
// ***************************************/

if (uDP.OilTempCelsius > 103)
{
// Console.WriteLine("Oil temp is a bit high"); //Change to light LED
GPIO.digitalWrite(OilPin, (int)GPIO.GPIOpinvalue.High); //Turn on LED
}
else { GPIO.digitalWrite(OilPin, (int)GPIO.GPIOpinvalue.Low); } //Turn off OilPin

if (uDP.WaterTempCelsius > 103)
{
// Console.WriteLine("Oil and/or water temp is too high"); //Change to light LED
GPIO.digitalWrite(WaterPin, (int)GPIO.GPIOpinvalue.High); //Turn on LED
}
else { GPIO.digitalWrite(WaterPin, (int)GPIO.GPIOpinvalue.Low); } //Turn off OilPin

// Oil and Water values
if (owShown > 200)
{
Console.WriteLine("Oil temp: " + uDP.OilTempCelsius + " degrees Celsius");
Console.WriteLine("Oil pressure: " + uDP.OilPressureKPa + " KPa");
Console.WriteLine("Water temp: " + uDP.WaterTempCelsius + " degrees Celsius");
Console.WriteLine("Water pressure: " + uDP.WaterPressureKpa + " KPa");
owShown = 0;
}


/***************************************
* Brake and suspension damage
***************************************/

byte bytevalue1 = uDP.SuspensionDamage[0];
byte bytevalue2 = uDP.SuspensionDamage[1];
byte bytevalue3 = uDP.SuspensionDamage[2];
byte bytevalue4 = uDP.SuspensionDamage[3];

//Check suspension damage
SuspDamage = 0;
for (int i = 0; i < 4; i++)
if (uDP.SuspensionDamage[i] > 20) // 0-255. 20 roughly equals 8 in-game
{
//Console.Write(TireNameArray[i] + "suspension damaged. Value = ");
//Console.WriteLine(uDP.SuspensionDamage[0]);
SuspDamage++;
}
if (SuspDamage > 0)
{
GPIO.digitalWrite(DamagePin, (int)GPIO.GPIOpinvalue.High); //Turn on LED
}
else
{
GPIO.digitalWrite(DamagePin, (int)GPIO.GPIOpinvalue.Low); //Turn off LED
}

/***************************************
* Fuel level meter
***************************************/
//Console.WriteLine(uDP.FuelLevel);
OldFuelLED = FuelLED;
if (uDP.FuelLevel > 0.95)
{
//Console.WriteLine("Fuel more than 95%"); // Light 10 led on fuel meter
FuelLED = 10;
GPIO.digitalWrite(Bar1Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar2Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar3Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar4Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar5Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar6Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar7Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar8Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar9Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar10Pin, (int)GPIO.GPIOpinvalue.High);
}
else if (uDP.FuelLevel > 0.85)
{
//Console.WriteLine("Fuel more than 85%"); // Light 9 led on fuel meter
FuelLED = 9;
GPIO.digitalWrite(Bar1Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar2Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar3Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar4Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar5Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar6Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar7Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar8Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar9Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar10Pin, (int)GPIO.GPIOpinvalue.Low);
}
else if (uDP.FuelLevel > 0.75)
{
//Console.WriteLine("Fuel more than 75%"); // Light 8 led on fuel meter
FuelLED = 8;
GPIO.digitalWrite(Bar1Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar2Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar3Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar4Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar5Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar6Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar7Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar8Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar9Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar10Pin, (int)GPIO.GPIOpinvalue.Low);
}
else if (uDP.FuelLevel > 0.65)
{
//Console.WriteLine("Fuel more than 65%"); // Light 7 led on fuel meter
FuelLED = 7;
GPIO.digitalWrite(Bar1Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar2Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar3Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar4Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar5Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar6Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar7Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar8Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar9Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar10Pin, (int)GPIO.GPIOpinvalue.Low);
}
else if (uDP.FuelLevel > 0.55)
{
//Console.WriteLine("Fuel more than 55%"); // Light 6 led on fuel meter
FuelLED = 6;
GPIO.digitalWrite(Bar1Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar2Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar3Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar4Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar5Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar6Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar7Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar8Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar9Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar10Pin, (int)GPIO.GPIOpinvalue.Low);
}
else if (uDP.FuelLevel > 0.45)
{
//Console.WriteLine("Fuel more than 45%"); // Light 5 led on fuel meter
FuelLED = 5;
GPIO.digitalWrite(Bar1Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar2Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar3Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar4Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar5Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar6Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar7Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar8Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar9Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar10Pin, (int)GPIO.GPIOpinvalue.Low);
}
else if (uDP.FuelLevel > 0.35)
{
//Console.WriteLine("Fuel more than 35%"); // Light 4 led on fuel meter
FuelLED = 4;
GPIO.digitalWrite(Bar1Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar2Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar3Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar4Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar5Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar6Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar7Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar8Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar9Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar10Pin, (int)GPIO.GPIOpinvalue.Low);
}
else if (uDP.FuelLevel > 0.25)
{
//Console.WriteLine("Fuel more than 25%"); // Light 3 led on fuel meter
FuelLED = 3;
GPIO.digitalWrite(Bar1Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar2Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar3Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar4Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar5Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar6Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar7Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar8Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar9Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar10Pin, (int)GPIO.GPIOpinvalue.Low);
}
else if (uDP.FuelLevel > 0.15)
{
//Console.WriteLine("Fuel more than 15%"); // Light 2 led on fuel meter
FuelLED = 2;
GPIO.digitalWrite(Bar1Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar2Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar3Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar4Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar5Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar6Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar7Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar8Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar9Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar10Pin, (int)GPIO.GPIOpinvalue.Low);
}
else if (uDP.FuelLevel > 0.05)
{
//Console.WriteLine("Fuel more than 5%"); // Light 1 led on fuel meter
FuelLED = 1;
GPIO.digitalWrite(Bar1Pin, (int)GPIO.GPIOpinvalue.High);
GPIO.digitalWrite(Bar2Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar3Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar4Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar5Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar6Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar7Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar8Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar9Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar10Pin, (int)GPIO.GPIOpinvalue.Low);
}
else if (uDP.FuelLevel < 0.05)
{
//Console.WriteLine("Fuel less than 5%"); // Light 0 led on fuel meter
FuelLED = 0;
GPIO.digitalWrite(Bar1Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar2Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar3Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar4Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar5Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar6Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar7Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar8Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar9Pin, (int)GPIO.GPIOpinvalue.Low);
GPIO.digitalWrite(Bar10Pin, (int)GPIO.GPIOpinvalue.Low);
}
//Check if Fuel level has changed
if (OldFuelLED != FuelLED)
{
Console.WriteLine("Fuel level now around " + FuelLED + "0 %");
}

}


}
}
}


Most is working, some are not working, but maybe is of help to someone else. I'm not a programmer, so look at the code at your own risk :)