From linear to exponential PWM output

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

Hi Paco,
Yes. Use Serial.print(lookupTable [ i ] , HEX) or Serial.print(lookupTable [ i ] , DEC);
The whole 256 set of 8-bit characters includes control characters and other unprintable ones.
In general you don't put binary data into a string since any 0 will terminate it at that point.
You can put a space or return after each 2-digit hex or decimal number to help you read each one
on the VB pc side and, with a loop, put that number into an array of chars again.
Best,
Mitch

Actually, I think the problem is how you are interpreting the incoming bytes on the PC side.
They are not supposed to be printable or viewable; they are binary.

The better way to send the lookuptable is as an array of bytes.
Serial.write(buf, len) or specifically,
Serial.write(lookupTable, 256);

This also will appear to be unprintable box characters; That is proper.
When your VB code is expecting a lookuptable it should read, not a string, but an array of bytes 256 long.
It should read them right into the array of 256 chars.
This link might help.
http://snipplr.com/view/2012/
Keep us posted!

Mitch,

Got it working at VB6 side. (with some help)
Final problem was
0 to255 = 256 values where 0 to 254 = 255 values.
And if you send only 255 and you tell it to expect 256 you have a problem.... :grin:

I am now able this code to be shown in VB6 in the graph.

if (i < switchpointXValue)
                lookupTable[i] = map(i, deadbandXvalue, switchpointXValue, speedstartValue, switchpointYValue);

But this curve code is not yet visible.
Have to check if Arduino sends this also in serial monitor first, as I forgot whether it does or not.

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

Still busy to get the shield working properly and also redesigned the buttons for the MENUWIZ(ard)
So some large rewrite of the program too.

Keep you U all posted.

Paco

That code works only for those X values greater or = to switchPointX value, so you can't make that the whole lookuptable.
You have to leave it as just the upper part, as in response #93.

VB6 should alway display the whole lookuptable as a connected set of points, i.e. a line graph, or if that's too may points,
then only every N of them.

Hi Mitch and others,

OK after a long radio silence I report back again.
Worked on the code to get the menu navigation by analog port and 10 switches.
Finally I got it to work.
Happy with it I checked the working of the controller then found out some other code was not working.
Had to sort that out too. To much time taken sofar but learned a lot again.
Have to check this week if the look up table and the connection (by bluetooth) is working with the PC.
Then the guys have to do some more testing t the track.

Keep you informed about the progress.

Paco

Great! I'm still tuned in.
Mitch

Hum,

Still debugging code (nemu navigation still working fine :slight_smile: )
While moving the speed trigger I suddenly see the deaband led coming ON and OFF even if we are not in the deadband range.
Never seen this behavior before.
It looks like the constrain function is not working.
MIN = 48 MAX = 212 are calibrated for the full throw of the trigger.
As soon as sensorRawValue passes the 212 the sensorValue jumps to "0" I can see on the display.

Hum again.

Do I overlook something basic?

void loop()
{
sensorRawValue = analogRead(sensorspeedPin); // read the raw value from the linear hall sensor / potentio meter:
sensorValue = map(sensorRawValue, sensorMin, sensorMax, 0, 255);// map the calibration to the sensor reading from the hall sensor / potentiometer
}

Hi!
I think you did.
Add the constrain line in between, to make it:

sensorRawValue = analogRead(sensorspeedPin); // read the raw value from the linear hall sensor / potentiometer
sensorRawValue = constrain(sensorRawValue, sensorMin, sensorMax);
sensorValue = map(sensorRawValue, sensorMin, sensorMax, 0, 255);// map the calibration to the sensor reading from the hall sensor / potentiometer

The problem is that analogRead is returning values outside the range MIN to MAX
Mitch

Mitch,

Your on the spot. Thanks
Moving the constrain upwards did the trick.
Due to the fact the speed trigger potentiometer is giving eratic values I am converting to neodyne magnet and linear hall sensor.
Hope to have it ready at the end of the week.
From now on I use an original slot race controller body (with the magnet) instead of the custom made white controller handle.
To be continued.

paco

One step forward 2 steps back. :slight_smile:

The magnet and hallsensor are working like a charm now.

Stuck in the lookuptable again.
I cant figure out why.
Nothing in this part of the code has changed (as far as I can see).
**SpeedfinalValue never passes the switchpointYValue.**With endcurvepower set to 1.0 the speedfinale value should follow sensormapped value as in previous codes.
As usual it must be so simple I over look it.

  //  +++++++++++++++  regular speed - brake mode  +++++++++++++++

  // in regular mode control the outputs with output cross block function to prevent speed and brake to be ON together 
  sensorRawValue = analogRead(sensorspeedPin); // read the raw value from the linear hall sensor / potentiometer
  sensorRawValue = constrain(sensorRawValue, sensorMin, sensorMax);
  sensorValue = map(sensorRawValue, sensorMin, sensorMax, 0, 255);// map the calibration to the sensor reading from the hall sensor / potentiometer
  sensormappedValue = map(sensorValue, 0, 255, speedstartValue, 255); // full linear control mapped with startspeed
  
  //  ++++  curve section  +++++++
  speedfinalValue = lookupTable[sensormappedValue];
  //  ++++  curve section  +++++++
  
  if (sensorValue > deadbandXvalue)
  {
    analogWrite(pwmoutspeedPin, speedfinalValue); 
    analogWrite(pwmoutbrakePin, 0); 
    digitalWrite(led8Pin, LOW);
    digitalWrite(led7Pin, HIGH);
  } 
  else
  { 
    //speedValue = 0;
    analogWrite(pwmoutspeedPin, 0); 
    analogWrite(pwmoutbrakePin, brakeNValue);
    digitalWrite(led8Pin,HIGH);
    digitalWrite(led7Pin, LOW);
  }

endcurvepower is currently set to 10 so diveded by 10 will give 1 and so a linear powerline.

void fillLookupTable()
{
  speedstartValueold = speedstartValue; 
  switchpointXValueold = switchpointXValue; 
  switchpointYValueold = switchpointYValue;

  Serial.print("B,"); 
  Serial.println(speedstartValue);
  Serial.print("C,");
  Serial.println(switchpointXValue);
  Serial.print("D,");
  Serial.println(switchpointYValue);

  switchpointXValue = constrain (switchpointXValue,deadbandXvalue, 255);
  switchpointYValue = constrain(switchpointYValue,speedstartValue, 255);
  
  for (int i=0; i<256; 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/10) ));
      }
      
      
   for (int i=0; i<256; i++) 
   {
   Serial.print(lookupTable[i]);
   if (i<255) 
   {
   Serial.print("|");
   }
   }       
   Serial.println();

Paco

Found the trouble maker not the solution yet.
Reverted back to old code and replaced it line by line.
Line with the arrow with the POW function is causing the stop at switchpointYValue.
If I replace the line with the line above speedfinalValue nicely runs till 255.

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 + (255-switchpointYValue) * ( 1.0 - pow( (255-x)/(255-switchpointXValue), (endCurvePower/10)) ));

Paco

Hi Paco,
Sorry, I think I made a typo. That "255-x" should be"255-i" in the problem line.
The line should be:lookupTable[i] = (int)(0.5 + switchpointYValue + (255-switchpointYValue) * ( 1.0 - pow( (255-i)/(255-switchpointXValue), (endCurvePower/10)) ));

There is no "x" at this time. The lookuptable is being calculated for all 256 values, via the loop index "i".
That should work well.
Good Luck!
Mitch

Hi Mitch,

Your correct the X was wrong and it should be i but the result is was not as expected.
If i correct the X for i then after the switch point the line goes straigth roof high. See screenshot.
In the case I use the other line without the curve it is nice and straigth.

However in both cases when I use the lookuptable we never start at the startspeed value but always higher as soon as the trigger leaves the deadband zone.
If I do not use the lookptable we nicely start at the startspeed point.

Any thoughts about this all?

As you can see the graphing at the PC is working now as intended.
As soon as on the controller we change either startspeed or X or Y point up or down this is shown in the graph so we know what we are doing.

Cheers, Paco

Now I like to make this output not linear but exponential

Calculate the mapping offline and load the value in a table into your code. Much faster than any calculation you can find any mcu.

backbone:
If i correct the X for i then after the switch point the line goes straigth roof high. See screenshot.

If you're trying to store a curve in the array, why don't you just calculate the values you want and put them in a static initialiser?

If you're trying to approximate the curve with three straight lines then I don't see the benefit of the lookup table - you might just as well do a range check, scale and offset. If execution time is critical then you could implement that logic during setup and cache the result, but I'd still calculate it doing three linear transforms rather than trying to write it as a single expression. But the circumstances where it's sensible to pre-calculate a linear transform are pretty unusual imo.

Dhenry

Do not fully understand what you mean with Offline calculation of the mapping.

PeterH

This whole slotrace controller project is not standard so we try the things step by step.
Currently all controllers that have an exponential curve just run from the startspeed point to full speed.

For those just stepping in and try to help.
With the selectable XY switchpoint we try to be different and this has not been tried or tested before.
Now for this step in the project we like only to curve the last straight line by changing a curve value.
1 is a straight line and 3 a full curve.
We have to test if good slot race drivers can benefit from this last line to be linear or to be curved.
Also even if the lines are linear we not yet tested with drivers if this would be enough and the curving is not needed at all.
But I like to have it already and then test drive all possible options at one day to have comprehensive comparising with same drives and track conditions.

All settings to XY and curve value need to be changeable while the controller is plugged in and in use.

This is where the project currently stands and we are not ready yet.
I am eager to learn but my level of programming and mathematics in not at the same level you guys are. :frowning:

Your given options are acabadabra to me so I cant say if this or that would be better or give an other result that is desired.

Cheers, Paco

Mitch,

Can you chime in?.
SpeedfinaleValue never starts at speedstartValue when the lookuptable is used.
VB nicely shows the lookuptable is filled with correct values.
If startspeed is 100 and Y value is 175 as soon as the trigger leaves deadband zone it jumps to 183 instead of 101.
Looks like it does not use the first part of the line from startspeed to XY point.

void fillLookupTable()
{
  speedstartValueold = speedstartValue; 
  switchpointXValueold = switchpointXValue; 
  switchpointYValueold = switchpointYValue;
 
  Serial.print("B,"); 
  Serial.println(speedstartValue);
  Serial.print("C,");
  Serial.println(switchpointXValue);
  Serial.print("D,");
  Serial.println(switchpointYValue);

  switchpointXValue = constrain (switchpointXValue,deadbandXvalue, 255);
  switchpointYValue = constrain(switchpointYValue,speedstartValue, 255);
  
  for (int i=0; i<256; i++)
    if (i < deadbandXvalue)
    {
      lookupTable[i] = 0;
    }
    else
    //++ knicked powerline at X and Y value ++ SETTING
    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
    streamUp();
}

It looks as it does not use the lookupTable[i] = map(i, 0, switchpointXValue, speedstartValue, switchpointYValue);

and always starts from the XY Value point set.

Paco

Can you post the output of the function?
What are the values used in the function??

Rob the output of the function I read too into the VB application for displaying where the immidiate screen nicely show the lookuptable values to be correct and start at 104 and go upto 255.
See the screenshot.

Paco