Spark and Sabertooth 2x60 Board - Anyone out there?

Hi,

I’m using spark core with a 2x60 sabertooth driver board by Dimensional Engineering.

I successfully connected spark with a 2x25 and had it communicating effectively. I had been sending the spark.functions integers instead of strings for the servo speed commands, whoops. But it seemed to work on the 2x25 board but not on the 2x60 board.

Anyone else developing with a 2x60 sabertooth board and spark?

It shouldn't work on any board. If a function expects a String you'll have to give it a String.
Granted, implicit type conversion could do you a favour, but you should not rely on it without exactly knowing what it does and why and what not.

So try to fix this first and if you still see odd behaviour, you could post your code (or a gist if it).

1 Like

Hey,

So I fixed it and it’s still running full speed ahead on boot-up just as the board’s instructions thought it would do if it didn’t have anything recognizably connected.

I’ve tried turning board on and letting it connect to the cloud before turning the board on, same problem.

Here’s my code

Spark Core Code:


Servo left;  // create servo object to control a servo
Servo right;


#define PIN D7
// Load hash map up with values

int leftpin = A0;
int rightpin = A1;
int speedx = 90;
int pos = 90; // for motion

int eyeSignal = A4;
int headSignal = A5;
    
void setup()
{
    
  left.attach(leftpin);  // attaches the servo on the A0 pin to the servo object
  right.attach(rightpin);  // attaches the servo on the A0 pin to the servo object

  
  //speed adjusting forward
  Spark.function("forward", Forward); //setpos // setPosition // original

  //Backward default
  Spark.function("backward", Backward);
  
  Spark.function("turnright", TurnRight);

  Spark.function("turnleft", TurnLeft);

  //initialize off
  pinMode(PIN, OUTPUT);

  
}

void loop()
{
    digitalWrite(PIN, HIGH);
    myDelay(1000); // give time to receive messages!
    digitalWrite(PIN, LOW);
    
}



//Motion Functions
int Forward(String speed){

    //change value to int from string because string can only be passed to function
    speedx = speed.toInt();
    left.write(speedx);
    right.write(speedx);
    return 1;
    
}

int Backward(String speed){
    speedx = speed.toInt();
    
    
    left.write(convertBackSpeed(speedx)); // must be > 90
    right.write(convertBackSpeed(speedx));
    return 1;
    
}

int TurnLeft(String speed){
    
    speedx = speed.toInt();
    left.write(speedx);
    right.write(convertBackSpeed(speedx));
    return 1;
    
}

int TurnRight(String speed){
    
    speedx = speed.toInt();
    left.write(convertBackSpeed(speedx));
    right.write(speedx);
    return 1;
    
}


//Convert forward to backwards!
int convertBackSpeed(int value){
    //value = 90 70 80 60 45 35
    //convert  back to 0 to 5
    int clean[90] = {90}; 
    clean[80] = {110}; 
    clean[70] = {120}; 
    clean[65] = {130}; 
    clean[50] = {135}; 
    clean[40] = {140};

    return clean[value];
}

//Allow time to receive messages
void myDelay(unsigned long duration) {
    unsigned long start = millis();
    while (millis() - start <= duration){
        Spark.process();
    }
}

HTML / Javascript:

<!--

best version here

–>

Command App:

<!-- form here
    We need to map 90 to 30 : 0 mph to 5 mph or 12 pts = 1 mph 6 pts = .5 mph 3 pts = .25 mph 1.5 pts = .125 mph
    We then need to map 0 mph back to 90 - 30 for forward or 90 - 180 for backward function

  -->

<input type="range" name="degBox" id="degBoxId" min="0" max="5" step="1" value="0" list="myData">
<!-- This adds the tick marks to the range but does not in Safari 

  Changed 0 from 90in value
  -->

<datalist id="myData">

    <option value="5"> 
    <option value="4"> 
    <option value="3"> 
    <option value="2"> 
    <option value="1"> 
    <option value="0"> 


</datalist>
  <p><i>0-- 1-- 2---3 ---4 --5</i></p>
<br>

<button id="minusbutton">&lArr; -1 &deg;</button>
<button id="plusbutton">+1 &deg; &rArr;</button>

<br><br>
<P>MPH: <span id="curPos"></span><br>



<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js" type="text/javascript" charset="utf-8"></script>

  <!-- Javascript server side code -->
<script type="text/javascript">
  //set credentials
  // set functions to call
  var deviceID    = "53ff6e066667574845592267";
  var accessToken = "6d3faea8e2c0d23c4fb78b1ca846c619855010ae";
  var speed = 90; // default speed changed from 40
  var currentCommand = '';


  // USELESS
  var fwd = "forward"; 
  // DONT NEED THESE //
  var tr = "turnright";
  var tl = "turnleft";
  var getFunc = "getpos";
  var setFunc = "setpos";
  // DONT NEED THESE //

  //Mapping
  // 0 mph = 90 degrees pos
  // 5 mph = 30 degrees pos
  // 1 mph = 12 degrees pos
  // .083 mph = 1 degree pos

//Updating speed output

  $('#degBoxId').val(speed); // sets any ID with degBoxId to value speed
  // If i want to do multiple things use class. i.e. if i have multiple degree boxes and I want to set them. 
  // use class anywhere I want it to be set. 
  //Updates now for first update

// ROBOT DRIVE COMMANDS ONLY
var is_keydown = false;
$(‘body’).on(‘keydown’, function (event) { // whole page onclick
if(is_keydown) return // dont do anything while a key is pressed down.

    is_keydown = true;
    
    console.log(event.keyCode);
    actual_speed = speed; // 0
    switch(event.keyCode){
      case 40 :
          currentCommand = 'backward';
          actual_speed = convertToDegrees(speed);
        break;
      case 38:
          currentCommand = 'forward';
          actual_speed = convertToDegrees(speed);
        break;
      case 37:
          currentCommand = 'turnleft';    
          actual_speed = convertToDegrees(speed);
        break;
      case  39:
          currentCommand = 'turnright';
          actual_speed = convertToDegrees(speed);
        break
      case 75:
          currentCommand = 'thr';
          actual_speed = '33';
        break;
      case 76:
          currentCommand = 'turnheadleft'
          actual_speed ='34';
        break;
      case 77:
          currentCommand = 'lookdown'
          actual_speed ='33';
        break;
      case 74:
          currentCommand = 'lookup'
          actual_speed ='240';
        break;
      default:
        currentCommand = null;
    }
       
    if(currentCommand){
      var requestURL = "https://api.spark.io/v1/devices/" + deviceID + "/" + currentCommand + "/";
                // execution post of lrequest string
      $.post(requestURL, { params: actual_speed, access_token: accessToken });          
    }

  });

   
  $('body').on('keyup', function () {
    is_keydown = false;
    if(!currentCommand){return}
    if(currentCommand === 'turnheadleft' || currentCommand === 'thr' || currentCommand === 'lookup' || currentCommand === 'lookdown'){
      var requestURL = "https://api.spark.io/v1/devices/" + deviceID + "/" + currentCommand + "/";
      $.post( requestURL, { params: '0', access_token: accessToken });
    }else{
      var requestURL = "https://api.spark.io/v1/devices/" + deviceID + "/" + currentCommand + "/";
      $.post( requestURL, { params: '90', access_token: accessToken });
    }
    
  });

// END OF ROBOT DRIVE COMMANDS ONLY

  //whenever the slider changes, it will update the current position by re-assigning speed value
  $('#degBoxId').on('change', function (event) {
    speed = event.target.value;
    $('#curPos').text(speed);//assigns ID #curPos the newly made speed
  });


  // decrementing down with fineAdjust function
  $('#minusbutton').on('click', function (event) {
    fineAdjust(-1);
  });


  // Incrementing 
  $('#plusbutton').on('click', function (event) {
    fineAdjust(1);
  });

  // Map mph to degrees of which the control is manipulated through
  function convertToDegrees(value){
    var map = ['90', '80', '70', '65', '50', '40'];
    return map[value];
  }


  // Used in input form. Gets curPos string element value, changes it to an integer
  function fineAdjust(value) {
    var setValue = speed + value; //adds last value plus this one
    document.getElementById("degBoxId").value = setValue; // set degBoxId element value to setValue DISPLAY

    // conversion needs to happen here befoe being given to speed then to function on backend
    speed = setValue;
    //sp(setValue); //sets current position
  }

  function oppositeNum(value){
  
  }




</script>

Not a solution, but a debugging tip.
If you change your return statements in your Spark.functions() to return speedx; you could see if your Core actually reads what you expect it to receive.

I don’t quite get your use of clean[90]. Maybe you could explain this too.
For what I see you create an array of 90 integers, initializing clean[0] to 90 and five other values, but leave all the others uninitialized.
Not having looked into your Java, could it be that you do send any value into convertBackSpeed that is not one of 0, 40, 50, 65, 70, 80 - especially 90 would be dangerous, since it’s beyond the limit of your array!
And once you leave the function, you “destroy” your array, just to recreate it afresh next time round.

1 Like

My javascript defines speeds 0 - 5 mph. Each 1 mph maps to forward or backward speeds. I’m using the sabertooth 2x60 controller which takes values 40 (fastest forward) to 90 (stop) and 140 (fastest backward).

In the front end I map 0 - 5 mph integers to 90 (aka 0), 80 (aka 1 mph) and so on.

When I pass 90 - 40 to the backend, if those integers enter into the backward function, it needs to map 90 - 40 to 90 to 140.

convertBackSpeed is supposed to take the values that it receives for forward speed from the javascript and and flip them to backward speeds in the appropriate degree of speeds.

I’m trying to assign place 90 to integer 90 since that’s the value being passed. I didn’t really care about all those empty arrays (shame on me) as long as the correct key values mapped to the opposite backward values.

Hope this helps! God praise your soul for help! :smiley:

Supposed that the forward and backward mappings are linear I’d change your function this way

//Convert forward to backwards!
inline int convertBackSpeed(int value)
{
    //values 40..90 should mirror around 90 to 90..140
    return (180 - value);
}

But given the one-liner I’d suggest you do the math directly in your direction functions.
Furthermore I’d even only use one Spark.function() that takes two speeds for each servo. This way you got all flexibility on your Java side without the need to reflash the Core if you’d like to implement curves or so.


BTW: The way you did attempt to do it would not have worked. Especially if you declare clean[90] your maximum index will be 89 so you haven’t got a place with the index 90. And given the values you initialized, you didn’t quite get the “correct key values” either :wink:

wow so much easier. Let me throw this on my robot.

Just to sprinkle this over the top.
Instead of wrapping your speed settings around 90 (degrees) you could “normalize” this quite easily by just subtracting 90 and flipping the sign.
This way you’d send more intuitive values in the range -50…0…+50 from your front end.

Said this, you’d do something like

  ...
  left.write(90 - speedL);
  right.write(90 - speedR);
  ...
1 Like

It worked great!

So my frontend should map to -50 to 0 to +50 and hand that over so that it flip the sign for left and right. backward being negative number so it’d be positive>90 number

Not flip right/left but forward/backward.
Normally higher values would be considered to be more forward than lower ones, but in this case values greater than 90 mean backwards and bellow means forward.
But with my code snippet above the flipping is already done, so you can forget my confusing comment about flips :wink: