diff --git a/data/Wheel_control_step0.png b/data/Wheel_control_step0.png index 7a2901c..771971c 100644 Binary files a/data/Wheel_control_step0.png and b/data/Wheel_control_step0.png differ diff --git a/data/Wheel_control_step1.png b/data/Wheel_control_step1.png index 85dc1e3..e4183b1 100644 Binary files a/data/Wheel_control_step1.png and b/data/Wheel_control_step1.png differ diff --git a/data/Wheel_control_step2.png b/data/Wheel_control_step2.png index 3fe9276..b7a6c48 100644 Binary files a/data/Wheel_control_step2.png and b/data/Wheel_control_step2.png differ diff --git a/data/Wheel_control_step2a.png b/data/Wheel_control_step2a.png index 12ef5b4..6d6feb8 100644 Binary files a/data/Wheel_control_step2a.png and b/data/Wheel_control_step2a.png differ diff --git a/data/Wheel_control_step3.png b/data/Wheel_control_step3.png index f0c8846..8c278a4 100644 Binary files a/data/Wheel_control_step3.png and b/data/Wheel_control_step3.png differ diff --git a/data/Wheel_control_v2_3.png b/data/Wheel_control_v2_3.png deleted file mode 100644 index 91343c6..0000000 Binary files a/data/Wheel_control_v2_3.png and /dev/null differ diff --git a/data/Wheel_control_v2_4.png b/data/Wheel_control_v2_4.png new file mode 100644 index 0000000..3a388c9 Binary files /dev/null and b/data/Wheel_control_v2_4.png differ diff --git a/data/manual.txt b/data/manual.txt index 16aef3b..8bcd942 100644 --- a/data/manual.txt +++ b/data/manual.txt @@ -1,7 +1,7 @@ Milos Rankovic ranenbg@gmail.com -12.08.2022. -wheel control v2.3 - compatible with Arduino HEX versions v200 and newer (backward compatible with v170, v180 and v190) +29.08.2022. +wheel control v2.4 - compatible with Arduino HEX versions v200 and newer (backward compatible with v170, v180 and v190) About: Wheel control GUI is made in windows programming environment called Processing v3.5.4, which is based on Java. It opperartes by directly reading HID values from Arduino such as axis and buttons. It uses RS232 or virtual serial port to send many settings that you may like to adjust (check firmware_info.txt for more details). The settings can be stored in Arduino EEPROM, such that they will be saved and automatically loaded at every powerup. Arduino firmware and GUI are developed and tested in Win10. @@ -21,7 +21,7 @@ How to use wheel control: [1] set desired Rotation degrees [2] manually align your wheel to center position, press center and then save button [3] select PWM type (phase correct is recommended, but you can use fast top for twice higher frequency at the same resolution) -[4] select PWM mode (pwm+-, pwm+dir or pwm0.50.100-use fast top with this mode) +[4] select PWM mode (pwm+-, pwm+dir, pwm0.50.100-use or rcm) [5] select PWM frequency (check firmware_info.txt for more details) [6] press pwm button and close wheel control, restart or replug Arduino to apply new PWM settings, start wheel control @@ -30,7 +30,7 @@ Use overall gain to set the master volume or gain for all FFB effects. You may u The button next to overall gain is for enabling/disabling real time FFB monitor graph. It is extremely usefull for troubleshooting or fine tunning your FFB settings in the game and finding the point of clipping. It is directly showing an FFB signal over COM port, that a game is sending in 1sec time window. It is recommended not to keep it always on, its purpose is only for FFB signal inspection and making sure there is no clipping. Once you are happy wiht your FFB, disable the FFB monitor, not to cause any delays due to COM data transfer. New feature is profile selector. Select one of the empty slots first. Make changes to the FFB settings as you wish and click store button. Type some name and click ok. This will save a profile into a txt file. Wheel control will look for any existing profiles at each start up, so you can load it. Note that PWM settings are not stored and loaded from a profile config. -Additional startup troubleshooting has been added from v2.0, such that startup problems will be easier to diagnose. Window is no longer white, and it shows some advanced setup info. +Additional startup troubleshooting has been added from v2.0, such that startup problems will be easier to diagnose. Window is no longer white, and it shows some advanced setup info. Since v2.4 I have improved the text info messages in each window from setup process, to contain some more details and to be more user friendly. Axis color setup: The axis colors are stored in a axisColor.txt file in a HEX format. This file will be created at startup with default values if it does not already exist. If it exists then the axis colors will be loaded from it. First two letters are alpha channel, so you can leave this at FF and fill in the remaining 6 numbers. In following link you can get those 6 HEX numbers from RGB values. @@ -42,6 +42,9 @@ This version of wheel control allows you to setup an analog shifter. The shifter Manual pedal axis calibration In this version you can manual set pedal axis calibration limits and save them into Arduino EEPROM. The calibration values are automatically loaded at each powerup of Arduino. Each time you start wheel control it will ask Arduino for latest calibration values and update the sliders accordingly. In order to set calibration limits first press "manual cal" button to unhide the calibration sliders. Move sliders to ther lowest (0) and maximum (4095) positions if they are not already there. Now press each pedal to its full range and set its corresponding maximum slider to a value slightly below the pedal axis value. Once done, now move back each pedal into ther lowest position and set the minimim slider to a value slightly above the pedal axis value. Pedal axis values should show a full range 0-4095 if done correctly. Once happy with your pedal travel and calibration limits you can press "save" button. +RCM pwm mode settings +Some RC servos and other brushless motor drivers require a special pwm mode for their operation, called RCM or PPM. This version of wheel control supports the lattest version of firmware fw-v21X where I have added this new pwm mode. Note that not all frequencies are available due to the nature of this mode. A zero force is represented as a square wave with a pulse width of 1.5ms, while full left force has 1.0ms pulse width and full right force has 2.0ms pulse width. This imposes a limit for the max allowed frequency of 500Hz which corresponds to 2ms period. Any higher frequency than 500Hz would have a lower period, therefore it does not allow to achieve the full range of right (positive) forces. For that reason I have labeled such frequencies as NA - not available. I have implemented some safety features in this version of wheel control, such that you can't select or send incorrect pwm settings to firmware. + Hopefully everything else will be self explanatory, enjoy :) rane. diff --git a/data/processing_3_5_4_status_log.txt b/data/processing_3_5_4_status_log.txt index afdbbdc..c1a2b55 100644 --- a/data/processing_3_5_4_status_log.txt +++ b/data/processing_3_5_4_status_log.txt @@ -1,31 +1,37 @@ +======================================================= + Arduino Leonardo FFB user interface + wheel control v2.4 created by Milos Rankovic ======================================================= GameControlPlus V1.2.2 created by Christian Riekoff and Peter Lager ======================================================= -Instance: org.gamecontrolplus.ControlIO@54dd0999 +Instance: org.gamecontrolplus.ControlIO@5a93fb1f ########################################################################################## Game Control Plus - available devices -------------------------------------- 0 Mouse [Mouse] on [Unknown] 1 Keyboard [Keyboard] on [Unknown] - 2 Lenovo USB Keyboard [Unknown] on [Unknown] - 3 Lenovo USB Keyboard [Unknown] on [Unknown] - 4 Arduino Leonardo [Stick] on [Unknown] + 2 G203 LIGHTSYNC Gaming Mouse [Unknown] on [Unknown] + 3 G203 LIGHTSYNC Gaming Mouse [Unknown] on [Unknown] + 4 G203 LIGHTSYNC Gaming Mouse [Unknown] on [Unknown] + 5 G203 LIGHTSYNC Gaming Mouse [Unknown] on [Unknown] + 6 Lenovo USB Keyboard [Unknown] on [Unknown] + 7 Lenovo USB Keyboard [Unknown] on [Unknown] + 8 Arduino Leonardo [Stick] on [Unknown] ########################################################################################## Device: Arduino Leonardo -COM: searching... -[0] "COM1" -[1] "COM4" -[2] "COM5" -Arduino Leonardo at COM5 -config: saved +COM: loaded from txt axis colors: loaded from txt ======================================================= G4P V4.3.8 created by Peter Lager ======================================================= Wheel parameters: -1080.0 1.0 0.5 0.5 1.0 1.0 1.0 0.5 0.7 1.0 0.0 128.0 1 500 2400 9; 11ms -WB:V, RB:fw-v170h; 1ms -WB:V, RB:fw-v170h; 6ms -ControlP5 2.2.6 infos, comments, questions at http://www.sojamo.de/libraries/controlP5 \ No newline at end of file +1080.0 1.0 0.5 0.5 1.0 1.0 1.0 0.5 0.7 1.0 0.0 128.0 1 500 2400 9; 2ms +WB:V, RB:fw-v210zf; 2ms +WB:V, RB:fw-v210zf; 14ms +WB:HG, RB:255 511 767 255 511 0; 21ms +WB:YR, RB:0 4095 0 4095 0 4095 0 4095; 11ms +WB:V, RB:fw-v210zf; 9ms +ControlP5 2.2.6 infos, comments, questions at http://www.sojamo.de/libraries/controlP5 +profile1.txt found \ No newline at end of file diff --git a/wheel_control.pde b/wheel_control.pde index 5e6049b..9170581 100644 --- a/wheel_control.pde +++ b/wheel_control.pde @@ -33,7 +33,7 @@ import controlP5.*; import java.util.*; import static javax.swing.JOptionPane.*; -String cpVer="v2.3"; // control panel version +String cpVer="v2.4"; // control panel version Serial myPort; // Create object from Serial class String rb; // Data received from the serial port @@ -95,6 +95,10 @@ boolean checkFwVer = true; // when enabled update fwVersion will take place boolean enabledac, modedac; // keeps track of DAC output settings boolean profileActuated = false; // keeps track if we pressed the profile selection boolean CPRlimit = false; // true if we input more than max allowed CPR +boolean pwm0_50_100enabled = false; // true if firmware supports pwm0.50.100 mode +boolean pwm0_50_100selected = false; // keeps track if pwm0.50.100 mode is selected +boolean RCMenabled = false; // true if firmware supports RCM pwm mode +boolean RCMselected = false; // keeps track if RCM pwm mode is selected int rbt_ms = 0; // read buffer response time in milliseconds String fwVerStr; // Arduino firmware version including the options int fwVerNum; // Arduino firmware version digits only @@ -108,6 +112,11 @@ String[] pdlCommand = new String[9]; // commands for pedal calibration float[] pdlMinParm = new float [4]; // curent pedal minimum cal values float[] pdlMaxParm = new float [4]; // curent pedal maximum cal values float[] pdlParmDef = new float [8]; // default pedal cal values +// pwm frequency selection possibilities - depends on firmware version and RCM mode +List a = Arrays.asList("40.0 kHz", "20.0 kHz", "16.0kHz", "8.0 kHz", "4.0 kHz", "3.2 kHz", "1.6 kHz", "976 Hz", "800 Hz", "488 Hz"); // for fw-v200 or lower +List a1 = Arrays.asList("40.0 kHz", "20.0 kHz", "16.0kHz", "8.0 kHz", "4.0 kHz", "3.2 kHz", "1.6 kHz", "976 Hz", "800 Hz", "488 Hz", "533 Hz", "400 Hz", "244 Hz"); // wider pwm freq selection (fw-v210+), no RCM selected +List a2_rcm = Arrays.asList("na", "na", "na", "na", "500 Hz", "400 Hz", "200 Hz", "122 Hz", "100 Hz", "61 Hz", "67 Hz", "50 Hz", "30 Hz"); // alternate pwm freq selection if RCM selected +int allowedRCMfreqID = 4; // first allowed pwm freq ID for RCM mode from the above list (anything after and including 500Hz is allowed) GImageToggleButton[] btnToggle = new GImageToggleButton[2]; @@ -170,20 +179,20 @@ void setup() { //noSmooth(); smooth(2); background(51); + println("=======================================================\n Arduino Leonardo FFB user interface\t\n wheel control "+cpVer +" created by Milos Rankovic"); showSetupText("Configuring wheel control"); File f = new File(dataPath("COM_cfg.txt")); //https://docs.oracle.com/javase/tutorial/uiswing/components/dialog.html - if (!f.exists()) showMessageDialog(frame, "COM_cfg.txt is not present, a one time\nautomatic device setup will now start.\n", "Arduino FFB Wheel " + cpVer, INFORMATION_MESSAGE); + if (!f.exists()) showMessageDialog(frame, "COM_cfg.txt was not found in your PC, but do not worry.\nYou either run the app for the 1st time, or you have\ndeleted the configuration file for a fresh start.\n\t\nPress OK to continue with the automatic setup process.", "Arduino FFB Wheel " + cpVer +" - Hello World :)", INFORMATION_MESSAGE); if (!f.exists()) showMessageDialog(frame, "Setup will now try to find control IO instances.\n", "Setup - step 1/3", INFORMATION_MESSAGE); // Initialise the ControlIO control = ControlIO.getInstance(this); println("Instance:", control); // Find a device that matches the configuration file - if (!f.exists()) showMessageDialog(frame, "Setup will now try to list available game devices.\n", "Setup - step 2/3", INFORMATION_MESSAGE); + if (!f.exists()) showMessageDialog(frame, "Step 1 of setup has passed succesfully.\nSetup will now try to look for available game devices in your PC.\n", "Setup - step 2/3", INFORMATION_MESSAGE); String inputdevices = ""; inputdevices = control.deviceListToText(""); - //inputdevices = control.devicesToText(""); - if (!f.exists()) showMessageDialog(frame, inputdevices+"\nIf this step does not pass you may try to run wheel_control.pde source code from Processsing 3.5.4.\n", "Setup - Device list", INFORMATION_MESSAGE); + if (!f.exists()) showMessageDialog(frame, "\nThe following devices are found in your PC:\n\t\n"+inputdevices+"\nThe setup will now try to configure each device, but bare in mind that some devices may cause the app to crash.\nIf that happens, you may try to manually create COM_cfg.txt file (see manual.txt in data folder for instructions),\nor you may try to run wheel_control.pde source code from Processsing IDE version 3.5.4.\n", "Setup - list of available devices", INFORMATION_MESSAGE); println(inputdevices); gpad = control.getMatchedDevice("Arduino Leonardo wheel v5"); if (gpad == null) { @@ -463,9 +472,10 @@ void setup() { makeEditable(num1); cp5 = new ControlP5(this); - List a = Arrays.asList("40.0 kHz", "20.0 kHz", "16.0kHz", "8.0 kHz", "4.0 kHz", "3.2 kHz", "1.6 kHz", "976 Hz", "800 Hz", "488 Hz"); List b = Arrays.asList("fast top", "phase corr"); - List c = Arrays.asList("pwm +-", "pwm+dir", "pwm0-50-100"); + List c = Arrays.asList("pwm +-", "pwm+dir"); + List c1 = Arrays.asList("pwm +-", "pwm+dir", "pwm0-50-100"); + List c2_rcm = Arrays.asList("pwm +-", "pwm+dir", "pwm0-50-100", "rcm"); List d = Arrays.asList("dac +-", "dac+dir"); List e = Arrays.asList("default"); /* add a ScrollableList, by default it behaves like a DropdownList */ @@ -504,6 +514,10 @@ void setup() { .addItems(a) //.setType(ScrollableList.LIST) // currently supported DROPDOWN and LIST ; + if (RCMenabled) { // fw-v210 has bigger pwm frequency selection + cp5.get(ScrollableList.class, "frequency").removeItems(a); + cp5.get(ScrollableList.class, "frequency").addItems(a1); + } cp5.addScrollableList("pwmtype") .setPosition(Xoffset+int(width/3.5) - 15 + 402, height-posY+30+108) .setSize(66, 100) @@ -520,6 +534,14 @@ void setup() { .addItems(c) //.setType(ScrollableList.LIST) // currently supported DROPDOWN and LIST ; + if (pwm0_50_100enabled) { // fw-v200 has pwm0.50.100 mode added + cp5.get(ScrollableList.class, "pwmmode").removeItems(c); + cp5.get(ScrollableList.class, "pwmmode").addItems(c1); + } else if (RCMenabled) { // fw-v210 has RCM mode added + cp5.get(ScrollableList.class, "pwmmode").removeItems(c); + cp5.get(ScrollableList.class, "pwmmode").addItems(c2_rcm); + } + //println(pwm0_50_100enabled+" "+RCMenabled); cp5.get(ScrollableList.class, "frequency").close(); cp5.get(ScrollableList.class, "pwmtype").close(); cp5.get(ScrollableList.class, "pwmmode").close(); @@ -895,6 +917,8 @@ void readFwVersion() { // reads firmware version from String and checks if load XYshifterEnabled = true; } if (bitRead(fwOpt, 0) == 0) buttons[2].active = false; // if bit0 is LOW (no pedal autocalibration available) + if (fwVerNum >= 200 && fwVerNum < 210) pwm0_50_100enabled = true; + if (fwVerNum >= 210) RCMenabled = true; } byte decodeFwOpts (String fopt) { @@ -1278,13 +1302,23 @@ void updateEffstate () { // code switches into effstate value void readPWMstate () { // decode settings from pwmstate value and update lists to those value typepwm = boolean(bitRead (pwmstate, 0)); // bit0 of pwmstate is pwm type + // put pwmstate bit1 to modepwm bit0 modepwm = bitWrite(byte(modepwm), 0, boolean(bitRead (pwmstate, 1))); // bit1 and bit6 of pwmstate contain pwm mode + + // pwmstate bits meaning + // bit1 bit6 pwm_mode + // 0 0 pwm+- + // 0 1 pwm0.50.100 + // 1 0 pwm+dir + // 1 1 rcm + for (int i=2; i<=5; i++) { // read frequency index, bits 2-5 of pwmstate freqpwm = bitWrite(byte(freqpwm), i-2, boolean(bitRead(pwmstate, i))); } if (DACenabled) { modedac = boolean(bitRead (pwmstate, 6)); // bit6 of pwmstate is DAC mode } else { + // put pwmstate bit6 to modepwm bit1 modepwm = bitWrite(byte(modepwm), 1, boolean(bitRead (pwmstate, 6))); // bit1 and bit6 of pwmstate contain pwm mode } enabledac = boolean(bitRead (pwmstate, 7)); // bit7 of pwmstate is DAC out enable @@ -1292,11 +1326,27 @@ void readPWMstate () { // decode settings from pwmstate value and update lists t } void sendPWMstate () { // send pwmstate value to arduino - wb = "W " + str(int(pwmstate)); // set command for pwm settings - executeWR(); // send values to arduino and read buffer from wheel (arduino will save it in EEPPROM right away) + boolean settingAllowed = false; + // check if selected pwm freq in RCM mode is allowed + if (!RCMenabled) { // for older fw version all pwm modes are available + settingAllowed = true; + } else { // for RCM fw-v210 or above + if (RCMselected) { // if we selected RCM mode + if (freqpwm >= allowedRCMfreqID) settingAllowed = true; // if freq is lower or equal than 500Hz, freq ID higher or equal 4 + } else { // if we selected any other pwm mode + settingAllowed = true; + } + } + if (settingAllowed) { + wb = "W " + str(int(pwmstate)); // set command for pwm settings + executeWR(); // send values to arduino and read buffer from wheel (arduino will save it in EEPPROM right away) + //println(str(typepwm)+ " "+str(modepwm)+ " "+str(freqpwm)); + } else { + showMessageDialog(frame, "You are trying to send pwm settings which are not allowed,\nplease set correct pwm settings first and try again.", "Caution", WARNING_MESSAGE); + } } -void updatePWMstate () { // code pwm byte from pwm settings values +void updatePWMstate () { // code pwmstate byte from pwm settings values pwmstate = bitWrite(pwmstate, 0, typepwm); pwmstate = bitWrite(pwmstate, 1, boolean(bitRead(byte(modepwm), 0))); // only look at bit0 of modepwm for (int i=0; i <=5; i++) { // set frequency index, bits 2-5 of pwmstate @@ -1312,6 +1362,7 @@ void updatePWMstate () { // code pwm byte from pwm settings values print(bitRead(pwmstate, 7-i)); } println("");*/ + //println("bit0="+str(bitRead(byte(modepwm), 0)) +", bit1= "+ str(bitRead(byte(modepwm), 1))); } // function that will be called when controller 'numbers' changes @@ -1351,7 +1402,7 @@ void makeEditable(Numberbox n) { void frequency(int n) { /* request the selected item based on index n */ //println(n, cp5.get(ScrollableList.class, "frequency").getItem(n)); - /* here an item is stored as a Map with the following key-value pairs: + /* here an item is stored as a Map with the following key-value pairs: * name, the given name of the item * text, the given text of the item by default the same as name * value, the given value of the item, can be changed by using .getItem(n).put("value", "abc"); a value here is of type Object therefore can be anything @@ -1363,6 +1414,9 @@ void frequency(int n) { cp5.get(ScrollableList.class, "frequency").getItem(n).put("color", c); freqpwm = n; updatePWMstate (); + if (n < allowedRCMfreqID) { // everything above 500Hz, or lower than freq ID 4 + if (RCMselected) showMessageDialog(frame, "This frequency is not available for RCM mode,\nplease select one of the other available ones.", "Caution", WARNING_MESSAGE); + } } void pwmtype(int n) { @@ -1379,6 +1433,20 @@ void pwmmode(int n) { cp5.get(ScrollableList.class, "pwmmode").getItem(n).put("color", c); modepwm = n; updatePWMstate (); + if (n==3) { // if we selected RCM pwm mode, only available if firmware supports RCM pwm mode + if (!RCMselected) { // if RCM mode is not selected + cp5.get(ScrollableList.class, "frequency").removeItems(a1); // remove extended pwm freq selection + cp5.get(ScrollableList.class, "frequency").addItems(a2_rcm); // add pwm freq selection for RCM pwm mode + RCMselected = true; + } + } else { // if we selected anything else except for RCM mode + if (RCMselected) { // if previous selection was RCM mode + cp5.get(ScrollableList.class, "frequency").removeItems(a2_rcm); // remove freq selection for RCM pwm mode + cp5.get(ScrollableList.class, "frequency").addItems(a1); // add the extented pwm freq selection + } + RCMselected = false; + } + cp5.get(ScrollableList.class, "frequency").setValue(freqpwm); // update the frequency list to the last freq selection } void dacmode(int n) { @@ -1421,9 +1489,10 @@ int COMselector() { COMlist += "(" +char(j+'a') + ") " + Serial.list()[j]; if (++j < i) COMlist += ", "; } - COMx = showInputDialog(frame, gpad + " at? (letter only)\n" + COMlist, "Setup - step 3/3", QUESTION_MESSAGE); + COMx = showInputDialog(frame, "Step 2 of setup passed succesfuly, we're almost finished.\n\t" + gpad + " at? (type letter only)\n" + COMlist, "Setup - step 3/3", QUESTION_MESSAGE); if (COMx == null) exit(); if (COMx.isEmpty()) exit(); + i = int(COMx.toLowerCase().charAt(0) - 'a') + 1; } String portName = Serial.list()[i-1]; @@ -1433,7 +1502,7 @@ int COMselector() { result = i-1; //exit(); } else { - showMessageDialog(frame, "No COM port deviced detected.\n", "Warning", WARNING_MESSAGE); + showMessageDialog(frame, "No serial port deviced detected.\n", "Warning", WARNING_MESSAGE); result = 0; //exit(); }