From linear to exponential PWM output

Change:
fillLookupTable;
to
fillLookupTable();

Otherwise, looks pretty good!
How does speedstartValue get changed anyway, from a pot?

//speedstartValueold = speedstartValue; //will this work here?
//switchpointXValueold = switchpointXValue; //will this work here?
//switchpointYValueold = switchpointYValue; //will this work here?

Yes. Whenever you change the lookupTable is a perfect time and probably only time to save the previous values.

The fun really starts when you let the user change switchpointYValue, or even switchpointXValue (within reasonable limits)
Just change code to:

if (speedstartValue != speedstartValueold   ||  switchpointXValue != switchpointXValueold  ||  switchpointYValue != switchpointYValueold) 
{
    eePromOffTime = currentTime  + 3000;
    digitalWrite(ledEEWPin,HIGH);
    fillLookupTable();
}
if (eePromOffTime != 0  &&  currentTime  > eePromOffTime ) 
{
    EEPROM.write(2,speedstartValue);
    eePromOffTime = 0;
    digitalWrite(ledEEWPin,LOW);
}

It becomes like steering ratio on cars. Newbies like a low ratio; racers like large ones.
With your switchpoint values, you effectively dial up the engine size and accelerator response you want and can handle.

Techylah () :wink:
I was again so close.

Pots? No way.
If you look at all current slotrace controllers they all use turnknobs with pots one way or an other.
Some have scales, some have 10 points click switch positions.
It is always a guessing and try and error game for the user.

I use pushbuttons on the controller handle.
I have 6 pusshbuttons In total now.
By every click up or down you know the value you change and can confirm on the display.
Currently I use the pushbuttons for startspeed +/-, switchpointX +/- and switchpointY +/-
All other values are done currently by MENWIZ an easy to use software MENU structure.
In the end all MENU actions need to be done by smartphone over BlueTooth.

For the change of X and Y values they need to be find out what is a reasonable range to be used.
I already put that code in my sketch but as I did not wanted to make the code in the response to large so I omitted it and only added the speedstart..
Your code is less lines so I will use it anyway for the rest of the code. Learned something new "||" !

Let you know the results from Friday.

Thanks again for thinking with me.

Paco

Just had an additional thought. You could data log the controller position history of some experts, or anybody, over a race or a period of time.
Then afterwards you histogram the speeds and see which speed ranges they spend most of the time in.
You can then automatically give them finer control in those regions, at the cost of having coarser control in the inbetween regions.
Each fine region would automatically add 2 switchpoints, one at the beginning and one at the end of the speed region.
It would all be done with map statements, like your current one switchpoint implementation.

As a high school kid (in the 60's!) I always wanted to do slot car racing, like my friends did, but my parents thought it too frivolous and they didn't want to keep driving me someplace 5 miles away. Maybe I like your project so much because I am a frustrated slot car enthusiast at heart!
Cheers!

Techylah,

Let your 60's frustration be my guide 8)
But not so fast.
Datalogging is also on the list but that comes after the stuff is all running with the smartphone on BT.
Have to dive into Android apps programming first. Not an easy task but will get there one way or the other.
In the mean time I use the 4 x 20 display.
One of the quick Dutch drivers already requested datalogging.
I first have to do the basics and convince the rusties (maybe also from the 60's :slight_smile: ) that with the WYSIWYG and the digital pushbutton actions they are better off then just adjust by try and error.

Time table:
1] working hardware (no delays)
2] BT connection with Amarino (Android) plugin
3] Design custom Android app to read the controller and adjust settings and other data from the controller.
4] Datalogging of settings and use of controller.

In the meantime if your frustration is still at a high level let your ideas flow or should I send you the whole skecth to rework and clean it? :slight_smile:

Cheers,

Paco

Techylah,

Test evening is postponed to next week. :0
They have a clubmeeting race at the track not allowing me with enough time to test.

Paco

Techylah,

Mixed results at yesyerdays evening test.
Had the laptop with me so I could choose different set ups.

1] With startspeed and linear line. They said there is no delay felt any more. The car follows the trigger nicely.
2] With startspeed and X and Y switchpoint lines. They said there is no delay felt anymore. The car follows the trigger nicely.
As there is no visualisation of the curve/lines they where having trouble to set up the rigth sweet spot.
3] Some one knowledgeable with programming changed the program and inserted a look up table for a exponential curve.
Since he has done that the driver who tested the controller noticed again a delay .

He changed in the void filllookupTable()

  for (int i=0; i<256; i++)
        //++ linear powerline from 0 to 255 ++
        //lookupTable[i] = i; // linear powerline
        //++ linear power line from startspeed to 255 ++
        //lookupTable[i] = map(i, 0, 255, speedstartValue, 255); // linear powerline with startspeed included
        //++ knicked powerline at X and Y value ++
        
        if (i < switchpointXValue)
             lookupTable[i] = map(i, 0, switchpointXValue, speedstartValue, switchpointYValue);
        else
             //lookupTable[i] = map(i, switchpointXValue, 255, switchpointYValue, 255 );       // this could get replaced by a curve after all is working
        lookupTable[i] = int (sqrt (map(i, switchpointXValue, 255, switchpointYValue, 255 )) * 15.96934);              //++++++++++

lookupTable[0] = 0;
lookupTable[1] = 48;
lookupTable[2] = 59;
lookupTable[3] = 67;
lookupTable[4] = 73;
lookupTable[5] = 78;
lookupTable[6] = 82;
lookupTable[7] = 86;
lookupTable[8] = 90;
lookupTable[9] = 93;
lookupTable[10] = 96;
lookupTable[11] = 99;
lookupTable[12] = 101;
lookupTable[13] = 104;
lookupTable[14] = 106;
lookupTable[15] = 108;
lookupTable[16] = 111;
lookupTable[17] = 113;
lookupTable[18] = 115;
lookupTable[19] = 117;
lookupTable[20] = 118;
lookupTable[21] = 120;
lookupTable[22] = 122;
lookupTable[23] = 123;
lookupTable[24] = 125;
lookupTable[25] = 127;
lookupTable[26] = 128;
lookupTable[27] = 130;
lookupTable[28] = 131;
lookupTable[29] = 132;
lookupTable[30] = 134;
lookupTable[31] = 135;
lookupTable[32] = 136;
lookupTable[33] = 138;
lookupTable[34] = 139;
lookupTable[35] = 140;
lookupTable[36] = 141;
lookupTable[37] = 142;
lookupTable[38] = 144;
lookupTable[39] = 145;
lookupTable[40] = 146;
lookupTable[41] = 147;
lookupTable[42] = 148;
lookupTable[43] = 149;
lookupTable[44] = 150;
lookupTable[45] = 151;
lookupTable[46] = 152;
lookupTable[47] = 153;
lookupTable[48] = 154;
lookupTable[49] = 155;
lookupTable[50] = 156;
lookupTable[51] = 157;
lookupTable[52] = 158;
lookupTable[53] = 159;
lookupTable[54] = 160;
lookupTable[55] = 160;
lookupTable[56] = 161;
lookupTable[57] = 162;
lookupTable[58] = 163;
lookupTable[59] = 164;
lookupTable[60] = 165;
lookupTable[61] = 166;
lookupTable[62] = 166;
lookupTable[63] = 167;
lookupTable[64] = 168;
lookupTable[65] = 169;
lookupTable[66] = 169;
lookupTable[67] = 170;
lookupTable[68] = 171;
lookupTable[69] = 172;
lookupTable[70] = 173;
lookupTable[71] = 173;
lookupTable[72] = 174;
lookupTable[73] = 175;
lookupTable[74] = 175;
lookupTable[75] = 176;
lookupTable[76] = 177;
lookupTable[77] = 178;
lookupTable[78] = 178;
lookupTable[79] = 179;
lookupTable[80] = 180;
lookupTable[81] = 180;
lookupTable[82] = 181;
lookupTable[83] = 182;
lookupTable[84] = 182;
lookupTable[85] = 183;
lookupTable[86] = 184;
lookupTable[87] = 184;
lookupTable[88] = 185;
lookupTable[89] = 185;
lookupTable[90] = 186;
lookupTable[91] = 187;
lookupTable[92] = 187;
lookupTable[93] = 188;
lookupTable[94] = 189;
lookupTable[95] = 189;
lookupTable[96] = 190;
lookupTable[97] = 190;
lookupTable[98] = 191;
lookupTable[99] = 191;
lookupTable[100] = 192;
lookupTable[101] = 193;
lookupTable[102] = 193;
lookupTable[103] = 194;
lookupTable[104] = 194;
lookupTable[105] = 195;
lookupTable[106] = 195;
lookupTable[107] = 196;
lookupTable[108] = 197;
lookupTable[109] = 197;
lookupTable[110] = 198;
lookupTable[111] = 198;
lookupTable[112] = 199;
lookupTable[113] = 199;
lookupTable[114] = 200;
lookupTable[115] = 200;
lookupTable[116] = 201;
lookupTable[117] = 201;
lookupTable[118] = 202;
lookupTable[119] = 202;
lookupTable[120] = 203;
lookupTable[121] = 203;
lookupTable[122] = 204;
lookupTable[123] = 204;
lookupTable[124] = 205;
lookupTable[125] = 205;
lookupTable[126] = 206;
lookupTable[127] = 206;
lookupTable[128] = 207;
lookupTable[129] = 207;
lookupTable[130] = 208;
lookupTable[131] = 208;
lookupTable[132] = 209;
lookupTable[133] = 209;
lookupTable[134] = 210;
lookupTable[135] = 210;
lookupTable[136] = 211;
lookupTable[137] = 211;
lookupTable[138] = 212;
lookupTable[139] = 212;
lookupTable[140] = 213;
lookupTable[141] = 213;
lookupTable[142] = 213;
lookupTable[143] = 214;
lookupTable[144] = 214;
lookupTable[145] = 215;
lookupTable[146] = 215;
lookupTable[147] = 216;
lookupTable[148] = 216;
lookupTable[149] = 217;
lookupTable[150] = 217;
lookupTable[151] = 217;
lookupTable[152] = 218;
lookupTable[153] = 218;
lookupTable[154] = 219;
lookupTable[155] = 219;
lookupTable[156] = 220;
lookupTable[157] = 220;
lookupTable[158] = 220;
lookupTable[159] = 221;
lookupTable[160] = 221;
lookupTable[161] = 222;
lookupTable[162] = 222;
lookupTable[163] = 222;
lookupTable[164] = 223;
lookupTable[165] = 223;
lookupTable[166] = 224;
lookupTable[167] = 224;
lookupTable[168] = 224;
lookupTable[169] = 225;
lookupTable[170] = 225;
lookupTable[171] = 226;
lookupTable[172] = 226;
lookupTable[173] = 226;
lookupTable[174] = 227;
lookupTable[175] = 227;
lookupTable[176] = 228;
lookupTable[177] = 228;
lookupTable[178] = 228;
lookupTable[179] = 229;
lookupTable[180] = 229;
lookupTable[181] = 230;
lookupTable[182] = 230;
lookupTable[183] = 230;
lookupTable[184] = 231;
lookupTable[185] = 231;
lookupTable[186] = 231;
lookupTable[187] = 232;
lookupTable[188] = 232;
lookupTable[189] = 233;
lookupTable[190] = 233;
lookupTable[191] = 233;
lookupTable[192] = 234;
lookupTable[193] = 234;
lookupTable[194] = 234;
lookupTable[195] = 235;
lookupTable[196] = 235;
lookupTable[197] = 236;
lookupTable[198] = 236;
lookupTable[199] = 236;
lookupTable[200] = 237;
lookupTable[201] = 237;
lookupTable[202] = 237;
lookupTable[203] = 238;
lookupTable[204] = 238;
lookupTable[205] = 238;
lookupTable[206] = 239;
lookupTable[207] = 239;
lookupTable[208] = 239;
lookupTable[209] = 240;
lookupTable[210] = 240;
lookupTable[211] = 240;
lookupTable[212] = 241;
lookupTable[213] = 241;
lookupTable[214] = 241;
lookupTable[215] = 242;
lookupTable[216] = 242;
lookupTable[217] = 242;
lookupTable[218] = 243;
lookupTable[219] = 243;
lookupTable[220] = 243;
lookupTable[221] = 244;
lookupTable[222] = 244;
lookupTable[223] = 244;
lookupTable[224] = 245;
lookupTable[225] = 245;
lookupTable[226] = 245;
lookupTable[227] = 246;
lookupTable[228] = 246;
lookupTable[229] = 246;
lookupTable[230] = 247;
lookupTable[231] = 247;
lookupTable[232] = 247;
lookupTable[233] = 248;
lookupTable[234] = 248;
lookupTable[235] = 248;
lookupTable[236] = 249;
lookupTable[237] = 249;
lookupTable[238] = 249;
lookupTable[239] = 250;
lookupTable[240] = 250;
lookupTable[241] = 250;
lookupTable[242] = 251;
lookupTable[243] = 251;
lookupTable[244] = 251;
lookupTable[245] = 251;
lookupTable[246] = 252;
lookupTable[247] = 252;
lookupTable[248] = 252;
lookupTable[249] = 253;
lookupTable[250] = 253;
lookupTable[251] = 253;
lookupTable[252] = 254;
lookupTable[253] = 254;
lookupTable[254] = 254;
lookupTable[255] = 255;

I do not know if the code has introduced this delay and need to be redesigned.
Coming Wednessday I have an other evening planned for testing.
Also the deathband needs to be taken in account otherwise the first part of the curve starts to early as the trigger movement is not exactly from 0 to 255.

In meantime I have to see if I can adapt the current control panel VB program to add a graph that shows the way the curve is currently used.
After a lot of talking to these drivers they NEED a linear to exponential curve where the top is having the umbrella shape.
Other curves are not working they say.
So for the time being we have to listen to them and first make them happy!

Tip and tricks are welcome.

Paco

curve.jpg

Hi Paco,
Ok. Glad there is no delay. It is all about the feel. Regardless of the data put into the table, there should be no delay;
Perhaps the loop code was modified as well.

Regarding this person's exponential curve code, its beginning needs to exactly match the linear portion's end. No big jump at the switchpoint!
Also, it needs parens around the "int".
I'd change it to (adding your deadband)

int   deadbandXvalue = 25;  // or whatever
//...

if (i < deadbandXvalue)
     lookupTable[i] = 0;
else
      if (i < switchpointXValue)
             lookupTable[i] = map(i, deadbandXvalue, switchpointXValue, speedstartValue, switchpointYValue);
        else
             lookupTable[i] = switchpointYValue  + (int) (sqrt (map(i, switchpointXValue, 255, 0, 255 - switchpointYValue)) * 15.96934);

Also, I'd make the curve adjustable, by changing the last line to:

lookupTable[i] = (int) (0.5 + (switchpointYValue  + pow( map(i, switchpointXValue, 255, 0, 255 - switchpointYValue ) / 255.0, curvePower) * 255.0));

CurvePower of 2.0 is the same as sqrt. Raising curvePower up to 3,4, 5 or more increased the extremeness of the curve.
Bring curvePower down from 2.0 to 1.0 reduces the curviness.
As a test, CurvePower of 1.0 should be the same as a linear continuation past the switchPoint to (255, 255).

Also, you might like the following old trick to produce a quick and dirty graph view.
For each line "lookupTable[0] = 0;" also print before the CR a series of asterisks, their number (for 51 asterisks being 100%, or 255) being
numAsterisks = lookupTable [ i ] / 5;
The linear and curved portions should be readily apparent (when you tilt your head 90 deg clockwise. :slight_smile: )
Cheers!

Techylah,

Also, you might like the following old trick to produce a quick and dirty graph view.
For each line "lookupTable[0] = 0;" also print before the CR a series of asterisks, their number (for 51 asterisks being 100%, or 255) being
numAsterisks = lookupTable / 5;
The linear and curved portions should be readily apparent (when you tilt your head 90 deg clockwise. :slight_smile: )
Cheers!

I start with the graph function first and possible without turning the head CW :slight_smile:
So I can see what is happening first with Y and X switchpoint and later with expo curves.
I added the VB6 standard MSchart object to the VB6 project of the control panel.

Looked for some snippet code and cooked something like this.

Private Sub Timer1_Timer()
'Declare 2D array to store values for the chart
'Variant ----so that can store both text as well as numbers
Dim X(1 To 2, 1 To 2) As Variant
 
X(1, 2) = "Speed"
X(2, 2) = 0
X(2, 2) = TextStartSpeedValue.Text

X(2, 1) = "TRIGGER"
X(2, 2) = TextSwitchpointXValue.Text

MSChart1.ChartData = X
MSChart1.chartType = 3
End Sub

What I cant work out is creating the line from startspeed point at the Y-axis to the X/Y point to the 255,255 point in the rigth hand top corner.
So startspeed,deathbandvalue to switchpointYValue, switchpointXValue to 255,255
So there should be three datapoints plotted in the graph.
I know it is quite simple but working for 3 hours with try and error this is all it brought me.

Variable names differ from Arduino slightly but mean the same thing.

Paco

Hi Paco,
It looks like the dimension declaration is wrong for the graph.
The Variant business just complicates things.
There is very little about line graphs in VB.
I found this slightly lower level code:

You have to scale the 0-255 coordinates to be graph values (use map) before drawing them.
You have only 3 pairs of XY data:
0, 0
switchPointX, switchPointY
255, 255

After that you could add drawing the hor speedstart line and vertical deadband lines (dotted).
You could also add some of the lookuptable points between the switchpoint and (255, 255) to see the curve.
Sorry I haven't done Visual Basic.

Techylah,

Graph only in basic form is working with your suggestion of the VB6 picturebox drawing lines.
I am now looking in the 2DNTgraph.ocx to see if I can get this to work too as it more fancy and has more features.
But for the time being the simpel graph exactly shows what we are doing with X and Y points although it is not using the curvePower yet ;-).

I just tested the curvePower line.

//lookupTable[i] = map(i, switchpointXValue, 255, switchpointYValue, 255 );       // this could get replaced by a curve after all is working
             lookupTable[i] = (int) (0.5 + (switchpointYValue  + pow( map(i, switchpointXValue, 255, 0, 255 - switchpointYValue ) / 255.0, curvePower/10) * 255.0));

As soon as CP value is 1 I can reach 255 as maximum
When I raise CP to 2,4 value the maximum is getting lower. The higher CP the lower the maximum.
Hum.

Paco

Soemtimes thing go quicker then expected.
With the help from VB forum guys I got the NTgraph.ocx working too.

Paco

Excellent, Paco. Nice to see the better graph.
I changed this post because the math was wrong. (perhaps that's considered bad form)

As you move the switchPoint around, this curve will automatically adjust, always starting where the linear part ends.

The curved part will approach 100% very slowly with the fastest change happening near the switchpoint.
endCurvePower adjusts this (from 1.0 for none to 4.0 or more, for very steeply curved)

The slope, or trigger sensitivity, changes right at the switchpoint.
Your graphing app is now really helpful to visualize this.
In order to really see and verify this, you should add to your graph all the points of the lookuptable that have X greater than switchpointX.

float endCurvePower = 2.5;
for (i=0; i<255: i++)
{
   if (i < deadbandXvalue)
        lookupTable[i] = 0;
   else
         if (i < switchpointXValue)
                lookupTable[i] = map(i, deadbandXvalue, switchpointXValue, speedstartValue, switchpointYValue);
           else
                lookupTable[i] = (int)(0.5 + switchpointYValue + (255-switchpointYValue) * ( 1.0 - pow( (255-x)/(255-switchpointXValue), endCurvePower) ));
}

Paco,
I completely changed the math so my last post is correct for the curve part.
I graphed it as a check. Sorry for the inconvenience.

If you implement and graph it I'd like to see your plot.
Best,
Mitch (Techylah)

graph.php.png

graph3.png

graph2.png

The near 0 slope at the end is wasteful of trigger range.
You might want to add one more linear section, say at the last 20% of the curve range,
and use map to go from that point to full.

Mitch,

With full confidend I went with the equipment to the race track yesterday evening.
All prepaired well and tested at home.

Hooked up the controller to do some DRY testing on the power adapter.
All worked fine.
Then started the laptop and connected the USB cable for the communication.
When I looked back at the display of the controller all digits were fully lighted.
I unhooked the USB cable to the controller and switched back on but it seems the MEGA 2560 has died :0

So the start was not what I and the other guy expected. But S happens and we then started to concentrate on progrmaming.
At the end of the evening we can retrieve the lookuptable from the MEGA and show it at the Visual Basic CONTROL PANEL side!

As the controller was out of order we could not test your curve lines.
Will discuss your new power curve witht he guy (Gabe) whether or not it is useable.
He was already happy with the fact we can now show what the controller is doing in real time and not guess how the curve exactly is.
He still dreams of drawing his own curve on his smart phone to be used. :slight_smile:
In meantime I checked on programming Android app but at this moment out of my league
Have ordered a new MEGA but that will take some time to arrive 2 weeks from China.
Have to check the display this evening with an UNO and if that has gone south too I have to order that one too.

Will add your code in the sketch and add and rework to get the variables adjusted by the menu buttons.

Keep you posted.

Thanks sofar, Paco

Yep, your control panel is kinda cool.
I'd change the labels "Switch Point X" and "Switch Point Y"
to "Switch Point Trigger" and "Switch Point Speed" to be consistent with and more intuitive.
Good Luck!
Mitch

I think I should have perhaps explained the curve part definition. I appreciate it when others comment their equations and code.

lookupTable[i] = (int)(0.5 + switchpointYValue + (255-switchpointYValue) * ( 1.0 - pow( (255-x)/(255-switchpointXValue), endCurvePower) ));

The curves of y=x^2, y=x^3, y=x^4 ..... (including the non integer numbers inbetween 2, 3, 4, etc) represent curves that start from (0, 0) on the left, and curve upward up to (1.0, 1.0) as they go to the right.

The above code implements that same set of curves, but starting at the top right, and curving increasingly downward as x goes to the left, until it reaches the switchPoint.
The expression (255-x)/255-switchpointXValue) represents how far along (in a fraction between 0 and 1) a point is to the left from the upper right corner on its way left to the switchPoint.

Raising that to a power, curves it upward.
(1.0 minus that) curves it downward.

To get it to meet vertically at the switch point, that result, 0 to 1, is scaled by the amount it has to cover, which is the distance from the top down to the switchPoint.
That factor is (255-switchpointYValue).

Finally, that scaled amount gets added to the switchpointY value, as it represents an amount higher than that Y value.

I don't know if this made it any clearer, but I thought I'd explain my math the way I like others to explain their's!
Cheers!

Good morning Mitch,

Well explained.
A lot of people are following the thread (1800+ reads) but for what reason? :slight_smile:
The speedcontroller itself or the curve equations passing by?

Still waiting for the new parts to get starting again. :frowning:

Paco

Mitch,

New Mega is in and running.
To make sure the lookuptable at the MEGA is the same we send it as string to the Serial.print
Unfortunenatly if the lookuptable is put in the serial.print the serial monitor shows only square blocks.
When I replace lookuptable for X it nicely fills the string with numbers from 0 to 255.
Am I running into limitations or did I (we) misused a string for this?
As long as the arduino side is not sending the right info I cant get it at the VB pc side not receive and decode propperly.
Any thoughts?

void fillLookupTable()
{

/* write lookupTable to stream so it can be used at the PC side to show the exact lookuptable curve used in the graph */
  Serial.print("Y,");
  for (int i=0; i<256; i++)
  {
    Serial.print(lookupTable[i]);
    //Serial.print(i); //test to see if string is filled with numbers from 0 to 255
    if (i<255)
    {
      Serial.print("|"); //delimiter
    }
  }       
  Serial.println();
}

Paco