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