diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644
index 0000000..2bc766f
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Button_class.pde b/Button_class.pde
index 65052c5..3a0a14a 100644
--- a/Button_class.pde
+++ b/Button_class.pde
@@ -67,13 +67,14 @@ class Button {
if (showInfo) {
pushMatrix();
if (dp == 0) {
- translate(x, y-1.2*sy); // put description above
+ translate(x, y-1.15*sy); // put description above
} else if (dp == 1) {
- translate(x, y+1.2*sy); // put description bellow
+ translate(x, y+1.15*sy); // put description bellow
} else if (dp == 2) {
translate(x-textWidth(d)-font_size, y); // put description to the left side
} else if (dp == 3) {
- translate(x+sx+10, y); // put description to the right side
+ translate(x+sx, y); // put description to the right side
+ } else {
}
noFill();
rect(0, 0, textWidth(d)+font_size, sy);
diff --git a/FFBgraph_class.pde b/FFBgraph_class.pde
index c3f2c5f..0dea4e9 100644
--- a/FFBgraph_class.pde
+++ b/FFBgraph_class.pde
@@ -1,19 +1,17 @@
class FFBgraph {
- float x;
- float y;
- int gh;
+ float x, y;
int ps;
int[] pointY = new int [gbuffer];
- float gwidthX, sclX, sclY;
+ float gwidthX, gh, sclX, sclY;
- FFBgraph(float posx, float posy, int gheight, int pointsize) {
- x = posx;
- y = posy;
- gh = gheight;
+ FFBgraph(float posx, float posy, float gheight, int pointsize) {
+ x = posx - 1;
+ y = posy - 1;
+ gh = gheight - 1;
ps = pointsize;
- gwidthX = gbuffer/gskip;
+ gwidthX = gbuffer / gskip;
sclX = gwidthX / gbuffer;
- sclY = float(gh) / (2*maxTorque);
+ sclY = gh / (2*maxTorque);
}
void update(String val1) {
@@ -38,16 +36,12 @@ class FFBgraph {
pushMatrix();
translate(-9, gh);
int l = 32;
- float n = (gh+1)/l;
+ float n = gh/l;
float m = n/5;
for (int i = 0; i >= -l; i--) {
for (int j = 0; j > -5; j--) {
- if (i > -l) {
- line(0, i*n, 9, i*n); // small ticks
- line(4, j*m + i*n, 9, j*m + i*n); // major ticks
- } else {
- line(0, (i*n)+1, 9, (i*n)+1);
- }
+ line(0, n*i, 9, n*i); // major ticks
+ if (i > -l) line(4, m*j + n*i, 9, m*j + n*i); // small ticks (do not draw them after last major tick)
}
}
popMatrix();
@@ -62,7 +56,7 @@ class FFBgraph {
//stroke(32, 255, 255);
strokeWeight(ps);
stroke(map(abs(pointY[i]), 0, maxTorque, 145, 0), 255, 255);
- line(1+i*sclX, gh/2-sclY*pointY[i], 1+(i+1)*sclX, gh/2-sclY*pointY[i+1]);
+ line(sclX*i+1, gh/2-sclY*pointY[i], sclX*(i+1)+1, gh/2-sclY*pointY[i+1]);
}
popMatrix();
}
diff --git a/Profile_class.pde b/Profile_class.pde
index 42c3070..e3d49ca 100644
--- a/Profile_class.pde
+++ b/Profile_class.pde
@@ -43,20 +43,49 @@ class Profile {
this.fromContents();
}
void storeToFile(String fn) {
- String tempName = "";
- if (name == "default") {
- showMessageDialog(frame, "Can not be modifyed.\nSelect another profile.");
+ String tempStr = "";
+ int tempOpt = -1;
+ if (this.name.equals("default")) {
+ showMessageDialog(frame, "Can not be modified.\nSelect another profile.");
} else {
- tempName = showInputDialog("Profile name", name);
- if (tempName != null) {
- name = tempName;
+ tempStr = showInputDialog("Save profile name as?", this.name);
+ if (tempStr != null) {
+ this.name = tempStr;
cp5.get(ScrollableList.class, "profile").setItems(ProfileNameList());
- upload();
- saveStrings("/data/"+fn+".txt", contents);
- println(name, "saved in "+fn+".txt");
+ if (nameExists(name)) { // if this profile name arleady exists
+ showConfirmDialog(frame, "Name already exists.\nOverwrite?");
+ }
+ if (tempOpt == -1 || tempOpt == 0) {
+ upload();
+ saveStrings("/data/"+fn+".txt", contents);
+ println(this.name, "saved as "+fn+".txt");
+ }
}
}
}
+ String getPrfParm(int iProfile, int iParm) { // retrieve certain parameter from a given profile
+ String profileContents[] = new String[num_profiles];
+ profileContents = loadStrings("profile"+str(iProfile)+".txt");
+ if (profileContents == null) {
+ return null;
+ } else {
+ return profileContents[iParm];
+ }
+ }
+ boolean nameExists(String cName) { // returns true if profile with this name already exists
+ String pName;
+ boolean c = false;
+ for (int i=1; i= 230) { // if fw-v230 - h-shifter advanced configuration
+ buttons[15].active = true;
+ buttons[16].active = true;
+ buttons[17].active = true;
+ } else {
+ buttons[15].active = false;
+ buttons[16].active = false;
+ buttons[17].active = false;
+ }
refreshXYshifterCal(); // get shifter calibration config from arduino
- shifters[0].updateCal(rb);
- if (bitRead(shifters[0].sConfig, 1) == 1) {
+ shifters[0].updateCal(rb); // decode and update shifter cal and config byte values
+ showSetupText(rb);
+ if (bitRead(shifters[0].sConfig, 0) == 1) { // if reverse gear button is inverted
+ buttonpressed[17] = true;
+ } else {
+ buttonpressed[17] = false;
+ }
+ if (bitRead(shifters[0].sConfig, 1) == 1) { // if reverse gear in 8th
buttonpressed[12] = true;
} else {
buttonpressed[12] = false;
}
+ if (bitRead(shifters[0].sConfig, 2) == 1) { // if shifter X-axis is inverted
+ buttonpressed[15] = true;
+ } else {
+ buttonpressed[15] = false;
+ }
+ if (bitRead(shifters[0].sConfig, 3) == 1) { // if shifter Y-axis is inverted
+ buttonpressed[16] = true;
+ } else {
+ buttonpressed[16] = false;
+ }
} else {
buttons[11].active = false;
buttons[12].active = false;
+ buttons[15].active = false;
+ buttons[16].active = false;
+ buttons[17].active = false;
}
- if (bitRead(fwOpt, 0) == 0 && fwVerNum >= 200) { // if bit0 and =>fw-v200 - pedal autocalibration is disabled, then we have manual pedal calibration
+ if (bitRead(fwOpt2, 0) == 1) { // if bit0=1 - extra buttons suported by firmware (option "e")
+ showSetupText("4x4 button matrix detected");
+ println("Button matrix detected");
+ }
+ if (bitRead(fwOpt2, 1) == 1) { // if bit1=1 - analog axis for FFB suported by firmware (option "x")
+ showSetupText("Analog axis for FFB detected");
+ println("Analog axis for FFB detected");
+ }
+ if (fwVerNum >= 200) { // if =>fw-v200 - we have additional firmware options
if (LCenabled) {
slajderi[1].yLimits[0].active = false; // if load cell, inactivate manual cal for brake axis
slajderi[1].yLimits[1].active = false;
@@ -466,21 +563,37 @@ void setup() {
slajderi[4].yLimits[0].active = false; // if extra buttons, inactivate manual cal for handbrake axis
slajderi[4].yLimits[1].active = false;
}
- refreshPedalCalibration();
- updateLastPedalCalibration(rb);
- } else {
- buttons[13].active = false; // disable manual cal button if pedal auto calib firmware
- }
- if (bitRead(fwOpt, 1) == 1) { // if bit1=1, encoder z-index is support by firmware
- buttons[14].active = true; // activate z-reset button
- } else {
- buttons[14].active = false; // inactivate z-reset button
+ if (bitRead(fwOpt, 7) == 1 && bitRead(fwOpt, 5) == 1) { // if options "f" and "m" clutch and hbrake axis are unavailable
+ slajderi[3].yLimits[0].active = false;
+ slajderi[3].yLimits[1].active = false;
+ slajderi[4].yLimits[0].active = false;
+ slajderi[4].yLimits[1].active = false;
+ }
+ if (bitRead(fwOpt2, 1) == 1) { // if bit1 of firmware options byte2 is HIGH, we have available FFB axis selector
+ AFFBenabled = true;
+ }
+ if (bitRead(fwOpt, 0) == 0) { // if bit0=0 - pedal autocalibration is disabled, then we have manual pedal calibration
+ println("Manual pcal detected");
+ refreshPedalCalibration();
+ updateLastPedalCalibration(rb);
+ showSetupText("Manual calibration for pedals detected");
+ showSetupText(rb);
+ } else {
+ showSetupText("Automatic calibration for pedals detected");
+ println("Automatic pcal detected");
+ buttons[13].active = false; // disable manual cal button if pedal auto calib firmware
+ }
+ if (bitRead(fwOpt, 1) == 1) { // if bit1=1, encoder z-index is supported by firmware
+ buttons[14].active = true; // activate z-reset button
+ } else {
+ buttons[14].active = false; // inactivate z-reset button
+ }
}
wb = "V";
executeWR();
//FFB graph
- ffbgraphs[0] = new FFBgraph(width-1, height-1-gbuffer/gskip, width-1, 1);
+ ffbgraphs[0] = new FFBgraph(width, height-gbuffer/gskip, width, 1);
// create number box object
cp5 = new ControlP5(this);
@@ -583,16 +696,31 @@ void setup() {
cp5.get(ScrollableList.class, "dacmode").close();
cp5.get(ScrollableList.class, "dacmode").setValue(int(modedac));
}
- loadProfiles(); //check if exist and load profiles from txt
+ if (AFFBenabled) { // if firmware supports analog FFB axis
+ List fb = Arrays.asList("x-enc", "y-brk", "z-acc", "rx-clt", "ry-hbr");
+ if (bitRead(fwOpt, 5) == 1 && bitRead(fwOpt, 7) == 1) fb = Arrays.asList("x-enc", "y-brk", "z-acc"); // if "f" and "m" options, we don't have clutch and hbrake axis available
+ cp5.addScrollableList("FFBaxis")
+ .setPosition(Xoffset+int(width/3.5) - 15 - 0.6*60, height-posY+5)
+ .setSize(50, 100)
+ .setBarHeight(20)
+ .setItemHeight(20)
+ .addItems(fb)
+ //.setType(ScrollableList.DROPDOWN) // currently supported DROPDOWN and LIST
+ ;
+ cp5.get(ScrollableList.class, "FFBaxis").close();
+ cp5.get(ScrollableList.class, "FFBaxis").setValue(int(FFBAxisIndex));
+ }
+ loadProfiles(); // check if exists and load profiles from txt
+ showSetupText("Configuration done");
+ setupTextTimer = 0;
}
void draw() {
background(51);
- drawWheelControll();
+ drawGUI();
}
-void drawWheelControll() {
- //SquareAroundButtons();
+void drawGUI() {
draw_labels();
/*for (int j = 0; j < btnToggle.length; j++) {
handleToggleButtonEvents(btnToggle[j], j);
@@ -611,19 +739,18 @@ void drawWheelControll() {
hatsw[k].show();
hatsw[k].showArrow();
}
- //my simple animated wheel gfx
- wheels[0].update(slajderi[0].axisVal*wParmFFB[0]/2); //update the angle in units of degrees
- wheels[0].showDeg(); //show the angle in units of degrees in a nice number format
+ // my simple animated wheel gfx
+ wheels[0].update(slajderi[FFBAxisIndex].axisVal*wParmFFB[0]/2); // update the angle in units of degrees
+ wheels[0].showDeg(); // show the angle in units of degrees in a nice number format
+ //wheels[0].show();
- //animated wheel from png sprite
+ // animated wheel from png sprite
if (!buttonpressed[7]) {
S4P.updateSprites(1);
- sprite[0].setRot(slajderi[0].axisVal*wParmFFB[0]/2/180*PI); //set the angle of the sprite in units of radians
- //sprite[0].setRot(PI*sin(2*PI*frameCount*0.001));
+ sprite[0].setRot(slajderi[FFBAxisIndex].axisVal*wParmFFB[0]/2/180*PI); // set the angle of the sprite in units of radians
S4P.drawSprites();
}
- //wheels[0].show();
//wheels[1].update();
//axisValue = correct_axis(Axis[0]);
//wheels[1].show();
@@ -650,7 +777,7 @@ void drawWheelControll() {
} else {
if (fbmnstp) { // read remaining serial read buffer content
String temprb = "";
- for (int i=0; i= 4 ) {
- label=label.substring(0, 4);
- }
+ labelStr = str(ceil(slider_value[j]*100)); // fix, show 100% instead of 1.0%
+ /*if (slider_value[j]*100 >= 100 ) {
+ labelStr=labelStr.substring(0, 3);
+ } else {
+ if (labelStr.length() >= 4 ) {
+ labelStr=labelStr.substring(0, 4);
+ }
+ }*/
}
//textMode(TOP);
text(sliderlabel[j], sldXoff+width/2-slider_width/3, slider_height/2*(1+j)); // slider label
- text(label, sldXoff+width/2+slider_width+20, slider_height/2*(1+j)); // slider value
+ text(labelStr, sldXoff+width/2+slider_width+20, slider_height/2*(1+j)); // slider value
}
+ if (AFFBenabled) text("FFB axis", Xoffset+int(width/3.5) - 14 - 0.6*60, height-posY+2); // FFB axis selector label
//text("Couple with pysical wheel turn", 70+1*(slider_width+20), slider_height/2+50);
//text("Decouple coefs", 70+3*(slider_width+20), slider_height/2+50);
pushMatrix();
translate(width/3.5, height-159);
- text("Arduino FFB Wheel", 0, 0);
- text("Control panel " + cpVer, 0, 20);
- text("Miloš Ranković 2018-2023 ©", 0, 40);
+ text("Arduino FFB Wheel, HEX " + FullfwVerStr.substring(3, FullfwVerStr.length()), 0, 0);
+ text("Control Panel " + cpVer, 0, 20);
+ text("Miloš Ranković 2018-2024 ©", 0, 40);
text("ranenbg@gmail.com, paypal@ranenbg.com", 0, 60);
popMatrix();
}
@@ -803,6 +945,21 @@ String readString() {
return rb;
}
+/*void serialEvent(Serial myPort) {
+ String temp;
+ try {
+ temp = myPort.readStringUntil(char(10)); // read till terminator char - LF (line feed) and store it in rb
+ if (temp != null) { // if there is something in rb
+ temp = temp.substring(0, (rb.indexOf(char(13)))); // remove last 2 chars - Arduino sends both CR+LF, char(13)+char(10)
+ }
+ rb = temp;
+ println("Serial event: " + temp);
+ }
+ catch(RuntimeException e) {
+ e.printStackTrace();
+ }
+ }*/
+
void executeWR() {
rbt_ms = 0;
writeString(wb);
@@ -823,14 +980,21 @@ void executeWR() {
void refreshWheelParm() {
myPort.clear();
writeString("U");
- println("Wheel parameters:");
- //delay(60); // no longer needed, improved readParmUntillEmpty()
- UpdateWparms(readParmUntillEmpty());
- if (bitRead(effstate, 4) == 1) { // if FFB monitor is ON
+ if (UpdateWparms(readParmUntillEmpty())) {
+ showSetupText("Firmware: OK, reading config");
+ showSetupText(rb);
+ println("Arduino FFB Wheel detected");
+ } else {
+ showSetupText("Firmware: ERROR");
+ println("Incompatible firmware detected");
+ }
+ if (bitRead(effstate, 4) == 1) { // if FFB mon is running
+ showSetupText("De-activating FFB monitor");
+ println("De-activating FFB monitor");
effstate = bitWrite(effstate, 4, false); // turn it OFF
sendEffstate(); // send command to Arduino
+ readParmUntillEmpty(); // read remaining FFB mon data
}
- //rb = " "; // do not clear rb
for (int i=0; i < wParmFFB.length; i++) {
wParmFFBprev[i] = wParmFFB[i];
print(wParmFFB[i]);
@@ -839,6 +1003,8 @@ void refreshWheelParm() {
readEffstate();
readPWMstate();
print(int(effstate));
+ //print(" ");
+ //print("read: " + int(FFBAxisIndex));
print(" ");
print(maxTorque);
print(" ");
@@ -855,8 +1021,14 @@ void refreshWheelParm() {
}
-void UpdateWparms(String input) {
+boolean UpdateWparms(String input) { // decode wheel parameters into FFB, CPR and PWM settings and returns false if format is incorrect
+ boolean correct;
float[] temp = float(split(input, ' '));
+ if (temp.length > 0) {
+ correct = true;
+ } else {
+ correct = false;
+ }
for (int i=0; i < temp.length; i++) {
if (i == 0) {
wParmFFB[0] = temp[0];
@@ -879,16 +1051,17 @@ void UpdateWparms(String input) {
}
}
wParmFFB[10] = wParmFFB[10] / float(maxTorque) * 100.0; // recalculate to percentage min pwm value
+ return correct;
}
-String readParmUntillEmpty() { // reads untill empty and returns a string longer than 5 chars (non-FFB monitor data)
+String readParmUntillEmpty() { // reads serial buffer untill empty and returns a string longer than 5 chars (non-FFB monitor data)
String buffer = "";
String temp = "";
rbt_ms = 0;
- for (int i=0; i<9999; i++) {
+ for (int i=0; i<999; i++) {
temp = readString();
if (temp != "empty") {
- if (temp.length() > 5) {
+ if (temp.length() > 6) { // 5 digits + sign
buffer = temp;
break;
}
@@ -900,20 +1073,12 @@ String readParmUntillEmpty() { // reads untill empty and returns a string longer
return buffer;
}
-void readFwVersion() { // reads firmware version from String and checks if load cell is enabled, updates all options
+void readFwVersion() { // reads firmware version from String, checks and updates all firmware options
myPort.clear();
+ println("Reading firmware version");
wb = "V";
- //delay(20);
- executeWR();
- String temp = rb;
- if (temp.length() <= 5) {
- // read all remaining data from FFB mon
- for (int i=0; i<20; i++) {
- temp = readString();
- delay(1);
- }
- }
executeWR();
+ FullfwVerStr = rb;
fwVerStr = rb.substring(4); // first 4 chars are allways "fw-v", followed by 3 numbers
String fwTemp = fwVerStr;
String fwOpts = "0";
@@ -951,7 +1116,7 @@ void readFwVersion() { // reads firmware version from String and checks if load
// decode 1st byte of firmware options
byte decodeFwOpts (String fopt) {
byte temp = 0;
- if (fopt != "0") { // has firmware any options
+ if (fopt != "0") { // if firmware has any options
for (int j=0; j0; i--) {
+ setupTextBuffer[i] = setupTextBuffer[i-1];
+ }
+}
+
+void clearSetupText() {
+ for (int i=0; i= maxWidth) maxWidth = textWidth(setupTextBuffer[i]);
+ }
+ if (setupTextTimer < setupTextTimeout_ms) {
+ for (int i=1; i