RAM RAM I need more RAM!!

Sorry, been away.

I'm running 18 R/C servos on three different time functions. The functions are loaded via the serial port point by point into RAM. Then I use liner scaling to interpolate between the entered points in time. To coordinate this I have time objects that "go off" at prescribed times to tell the servos its time to recalculate positions and send out the next set of position info.

I'd got it all going for a single servo, tested all the bits. Worked wonderful. But, when expanding up to 18 times the size.. Well, it went bananas. I figured; "I doubt this'll fit in 2k, maybe I should look for more headroom?"

I come from the c++ application world so I have no RAM usage discipline at all.

-jim lee

If you'd like help...

Hahah!

Ok but I'm going to be out of town for a week. Next week I'll post it.

-jim lee

Maybe use external serial RAM IC.

Main program..

#include "antThorax.h"
#include "blinker.h"

#define buttonPin 2

blinker  aBlinker;
antThorax thorax;

char command[20];
byte index;


void setup() { 

  pinMode(buttonPin, INPUT);
  index = 0;
  Serial.begin(9600);            // Set up for commands as default.
  Serial.setTimeout(100);
  aBlinker.init();
  
  // Skip doing this through the serial port.. Lets hardcode it for now..
  thorax.addMapperPoint( 0, 0, 0);
  thorax.addMapperPoint( 0, 5, 0.66);
  thorax.addMapperPoint( 0, 20, 1);
  thorax.addMapperPoint( 0, 25, 1);
  thorax.addMapperPoint( 0, 45, 0.66);
  thorax.addMapperPoint( 0, 55, -0.66);
  thorax.addMapperPoint( 0, 75, -1);
  thorax.addMapperPoint( 0, 95, -0.66);
  thorax.addMapperPoint( 0, 100, 0);

  thorax.addMapperPoint( 1, 15, -1);
  thorax.addMapperPoint( 1, 20, 0.33);
  thorax.addMapperPoint( 1, 25, 0.5);
  thorax.addMapperPoint( 1, 75, 0.5);
  thorax.addMapperPoint( 1, 77.5, -.5);
  thorax.addMapperPoint( 1, 85, -1);

  thorax.addMapperPoint( 2, 15, -1);
  thorax.addMapperPoint( 2, 20, .25);
  thorax.addMapperPoint( 2, 25, 0.5);
  thorax.addMapperPoint( 2, 75, 0.5);
  thorax.addMapperPoint( 2, 77.5, -0.5);
  thorax.addMapperPoint( 2, 85, -1);
}


void commandCheck(void) {

  int  value1;
  float value2, value3;
  byte inByte;

  if (Serial.available() > 0) {
    inByte = Serial.read();
    if (isalpha(inByte)) {
      command[index] = inByte;
      index++;
    } 
    else if (index>0) {
      command[index] = '\0';
      index = 0;
      if (!strcmp("add",command)) {
        value1 = Serial.parseInt();
        value2 = Serial.parseFloat();
        value3 = Serial.parseFloat();
        thorax.addMapperPoint(value1,value2,value3);
        Serial.print("Added point : ");
        Serial.print(value2);
        Serial.print(", ");
        Serial.print(value3);
        Serial.print(" to mapper ");
        Serial.println(value1);
      } 
      else if (!strcmp("view",command)) {
        value1 = Serial.parseInt();
        thorax.mapperPointsToSerial(value1);
      }  
      else if (!strcmp("map",command)) {
        value1 = Serial.parseInt();
        value2 = Serial.parseFloat();
        value3 = thorax.mapPoint(value1,value2);
        Serial.print("Mapping : ");
        Serial.print(value2);
        Serial.print(" to : ");
        Serial.print(value3);
        Serial.print(" with mapper ");
        Serial.println(value1);

      }
      else if (!strcmp("dump",command)) {
        value1 = Serial.parseInt();
        Serial.print("Data for mapper : ");
        Serial.print(value1);
        Serial.println(" as (Time, value)");
        thorax.dumpMapperToSerial(value1);
      } 
      else if (!strcmp("clear",command)) {
        value1 = Serial.parseInt();
        thorax.clearMapper(value1);  
      } 
      else {
        Serial.println(command);
        Serial.println("add m x y adds point to mapper m.");
        Serial.println("view m lists mapper m");
        Serial.println("map m x returns a mapped point from mapper m.");
        Serial.println("clear m empties mapper m.");
        Serial.println("dump m returns the curve for mapper m.");
      }
    }
  }
}


void loop() {

  aBlinker.idle();
  if (!digitalRead(buttonPin)) {
    if (thorax.stepState != walkingFwd
      && thorax.stepState != startingToWalk) {    // If we are not walking..
      Serial.end();                          // kill the serial port.
      thorax.startSerial();                  // Let the thorax have the port..
    }
    thorax.walkFwd();
  } 
  else {
    if (thorax.stepState == walkingFwd) {
      thorax.park();
      thorax.stopSerial();            // Done with servos for now..
      Serial.begin(9600);            // Set up for commands.
      Serial.setTimeout(100);
    } 
    else
      commandCheck();
  }
}

thorax.h

#ifndef antThorax_h
#define antThorax_h

#include "multiMap.h"
#include "miniSSC.h"
#include "timeObj.h"

/*
                                   Front
   0           1
    \        /
     \  () /
       O  O
 2 --- O  O --- 3
       O  O
      /    \
     /       \ 
    4         5 
 
 Group A = 0,3,4
 Group B = 1,2,5
 
 */
#define swivel 0
#define lift   1
#define knee   2

#define swivel0 16
#define lift0   16
#define knee0   16

#define swivel1 5
#define lift1   6
#define knee1   7

#define swivel2 16
#define lift2   16
#define knee2   16

#define swivel3 0
#define lift3   1
#define knee3   2

#define swivel4 16
#define lift4   16
#define knee4   16

#define swivel5 16
#define lift5   16
#define knee5   16

#define swivleFwd  1
#define swivleBack -1
#define swivlePark 0

#define liftUp -1
#define liftDwn .5
#define liftPark -1

#define kneeExtend 1
#define kneeRetract -1
#define kneePark -1

#define NUM_LEGS   6                     // Duh! Its an ANT!
#define NUM_JOINTS 3

#define CYCLE_TIME 40000.0               // Time to complete one step
#define SLICES 50.0                      // How many times to update position per step.
#define STEP_SET_WAIT CYCLE_TIME/2
#define NUM_MAPPERS NUM_JOINTS

enum stepStateType { unknown, parked, startingToWalk, walkingFwd };

class antThorax;

class antLeg {

public:
  antLeg(antThorax* inThorax, byte inLegNum, byte inSwivel, byte inLift, byte inKnee);

  void walkFwd(void);
  void park(void);

private :
  antThorax*    thorax;       // Our thorax..
  byte          legNum;     
  byte          swivelPin;    // Output here..
  byte          liftPin;      // and here..
  byte          kneePin;      // and here..
  int           timeSlice;    // Where are we in our step?
  timeObj*      legTimer;     // How long between updates?
  
  float         swivelVal;    // Calculated values to send out to the servos.
  float         liftVal;
  float         kneeVal;
  stepStateType legState;

};


// **************************************************



class antThorax {

public :
  antThorax(void);

  void startSerial(void);
  void stopSerial(void);
  void park(void);
  void walkFwd(void);

  boolean checkMapperNum(int mapperNum);
  void addMapperPoint(int legMapperNum, double x, double y);
  double mapPoint(int legMapperNum, double x);
  double legsMapPoint(int legNum, int jointNum, double x);
  void clearMapper(int LegMapper);
  void mapperPointsToSerial(int LegMapper);
  void dumpMapperToSerial(int LegMapper);
  double mapTime(double timeSlice);                           // Acess the slice mapper..

  stepStateType stepState;
  miniSSC  theSSC;                                            // Used to make servos go..
  
private: 
  antLeg*  legList[NUM_LEGS];                                 // just the leg list
  mapper*  sliceMapper;                                       // Mapps from slice number to data scaled to %.
  multiMap legMapper[NUM_MAPPERS];                            // The joint position mappers.
  timeObj* waitTimer;    // We need to wait before doing something.
};


#endif

thorax.cpp

#include "antThorax.h"

// Start with leg code..

antLeg::antLeg(antThorax* inThorax, byte inLegNum, byte inSwivel, byte inLift, byte inKnee) {

  thorax = inThorax;
  legNum = inLegNum;
  swivelPin = inSwivel;
  liftPin = inLift;
  kneePin = inKnee;
  legTimer = new timeObj(CYCLE_TIME/SLICES);
  legState = unknown;
}


void antLeg::walkFwd() {

  float mappedTime;

  switch (legState) {
  case walkingFwd :
    if (legTimer->ding()) {
      legTimer->stepTime();                  // reset the timer..

      // send step data.
      thorax->theSSC.setServo(swivelPin,swivelVal);    // Send out the data.
      thorax->theSSC.setServo(liftPin,liftVal);
      thorax->theSSC.setServo(kneePin,kneeVal);

      timeSlice++;                          // calculate next step.
      if (timeSlice>SLICES)                 // Overrun number of slices?
        timeSlice = 0;                      // reset the silly thing.
      mappedTime = thorax->mapTime(timeSlice);
      swivelVal = thorax->legsMapPoint(legNum,swivel,mappedTime);
      liftVal = thorax->legsMapPoint(legNum,lift,mappedTime);
      kneeVal = thorax->legsMapPoint(legNum,knee,mappedTime);
    }
    break;
  default :
    timeSlice = 0;
    mappedTime = thorax->mapTime(timeSlice);
    swivelVal = thorax->legsMapPoint(legNum,swivel,mappedTime);
    liftVal = thorax->legsMapPoint(legNum,lift,mappedTime);
    kneeVal = thorax->legsMapPoint(legNum,knee,mappedTime);
    legState = walkingFwd;
    break;
  } 
}


void antLeg::park(void) {

  if (!(legState == parked)) {
    thorax->theSSC.setServo(swivelPin,swivlePark);
    delay(100);
    thorax->theSSC.setServo(liftPin,liftPark);
    delay(100);
    thorax->theSSC.setServo(kneePin,kneePark);
    legState = parked;
    delay(100);
  }
}



// ***********************************************
// antThorax code below


antThorax::antThorax(void) {

  legList[0] = new antLeg(this,0,swivel0,lift0,knee0);
  legList[1] = new antLeg(this,1,swivel1,lift1,knee1);
  legList[2] = new antLeg(this,2,swivel2,lift2,knee2); 
  legList[3] = new antLeg(this,3,swivel3,lift3,knee3);
  legList[4] = new antLeg(this,4,swivel4,lift4,knee4);
  legList[5] = new antLeg(this,5,swivel5,lift5,knee5);
  sliceMapper = new mapper(0,SLICES,0,100);
  waitTimer = new timeObj(STEP_SET_WAIT);
  stepState = unknown;
}


void antThorax::startSerial(void) {

  theSSC.initSSC(true);           // Grab the serial port for ourselves.
}


void antThorax::stopSerial(void) {

  Serial.end();
  stepState = unknown;
}


void antThorax::park() {

  int i;

  delay(200);
  for (i=0;i<NUM_LEGS;i++) {
    if (legList[i]!=NULL)
      legList[i]->park();
  }
  stepState = parked;
}



void antThorax::walkFwd() {

  byte i;

  switch (stepState) {
  case walkingFwd :
    for (i=0;i<=NUM_LEGS;i++) {
      legList[i]->walkFwd();
    }
    break;
  case startingToWalk :
    legList[0]->walkFwd();
    legList[3]->walkFwd();
    legList[4]->walkFwd();
    if (waitTimer->ding()) {
      legList[1]->walkFwd();
      legList[2]->walkFwd();
      legList[5]->walkFwd();
      stepState = walkingFwd;
    }
    break;
  default :
    waitTimer->start();
    legList[0]->walkFwd();
    legList[3]->walkFwd();
    legList[4]->walkFwd();
    stepState = startingToWalk;
    break;
  }
}


boolean antThorax::checkMapperNum(int mapperNum) {

  return mapperNum>=0 && mapperNum <= NUM_MAPPERS;
}


void antThorax::addMapperPoint(int legMapperNum, double x, double y) {

  if (checkMapperNum(legMapperNum)) {
    legMapper[legMapperNum].addPoint(x,y);
  }
}


double antThorax::mapPoint(int legMapperNum, double x) {

  if (checkMapperNum(legMapperNum)) {
    return legMapper[legMapperNum].Map(x);
  } 
  else
    return 0;
}

// This is where the logic like turning, opposite sides etc. happens. 
// The leg gives us the joint & time, we have the mapper and other 
// information to bundle up into where we want that joint to be set.
// No this bit isn't complete. Only legs 1 & 3 have been built.
double antThorax::legsMapPoint(int legNum, int jointNum, double x) {

  switch(legNum) {
  case 0 :
    switch(jointNum) {
    case swivel :
      return legMapper[swivel].Map(x);
      break;
    case lift :
      return legMapper[lift].Map(x);
      break;
    case knee :
      return legMapper[knee].Map(x);
      break;
    }
    break;
  case 1 :
    switch(jointNum) {
    case swivel :
      return legMapper[swivel].Map(x);
      break;
    case lift :
      return legMapper[lift].Map(x);
      break;
    case knee :
      return legMapper[knee].Map(x);
      break;
    }
    break; 
  case 2 :
    switch(jointNum) {
    case swivel :
      return legMapper[swivel].Map(x);
      break;
    case lift :
      return legMapper[lift].Map(x);
      break;
    case knee :
      return legMapper[knee].Map(x);
      break;
    }
    break; 
  case 3 :
    switch(jointNum) {
    case swivel :
      return legMapper[swivel].Map(x);
      break;
    case lift :
      return legMapper[lift].Map(x);
      break;
    case knee :
      return legMapper[knee].Map(x);
      break;
    }
    break; 
  case 4 :
    switch(jointNum) {
    case swivel :
      return legMapper[swivel].Map(x);
      break;
    case lift :
      return legMapper[lift].Map(x);
      break;
    case knee :
      return legMapper[knee].Map(x);
      break;
    }
    break; 
  case 5 :
    switch(jointNum) {
    case swivel :
      return legMapper[swivel].Map(x);
      break;
    case lift :
      return legMapper[lift].Map(x);
      break;
    case knee :
      return legMapper[knee].Map(x);
      break;
    }
    break; 
  }
}


void antThorax::clearMapper(int legMapperNum) {

  if (checkMapperNum(legMapperNum))
    legMapper[legMapperNum].clearMap();
}



void antThorax::mapperPointsToSerial(int legMapperNum) {

  if (checkMapperNum(legMapperNum))
    legMapper[legMapperNum].outputList();
}



void antThorax::dumpMapperToSerial(int LegMapper) {

  int timeSlice;
  float value;

  timeSlice = 0;
  while (timeSlice<=SLICES) {
    value = legMapper[LegMapper].Map(timeSlice);
    Serial.print(timeSlice);
    Serial.print(", ");
    Serial.println(value);
    timeSlice++;
  }
}


double antThorax::mapTime(double timeSlice) {

  return sliceMapper->Map(timeSlice);
}

multiMap.h

#ifndef multiMap_h
#define multiMap_h


class mapItem {

public :
  mapItem(double inX, double inY);
  ~mapItem(void);

  void setValues(void);
  bool linkIn(mapItem* itemPtr);
  double Map(double inVal);
  double doMap(double inVal);
  void outputItem(void);

  double x;
  double y;
  double slope;
  double intercept;
  mapItem* lessItem;
  mapItem* greaterItem;
};


class multiMap {

public:
  multiMap(void);
  ~multiMap(void);

  void addPoint(double x, double y);
  void clearMap(void);
  double Map(double inVal);
  void outputList(void);

private :

  mapItem* itemList;
};


#endif

multiMap.cpp

#include "multiMap.h"
#include "mapper.h"

mapper mapItemMppr(1,2,1,2);


mapItem::mapItem(double inX, double inY) {

  x = inX;
  y = inY;
  lessItem = NULL;
  greaterItem = NULL;
}

mapItem::~mapItem(void) {     // Time to die. If your in a list, remove yourself.

  if (lessItem!=NULL) // Have a "less" item.
    lessItem->greaterItem = greaterItem;
  if (greaterItem!=NULL) // Have a greater item.
    greaterItem->lessItem = lessItem;
  lessItem = NULL;
  greaterItem = NULL;
  // Our work is done here..
}


void mapItem::setValues() {

  if (lessItem!=NULL) {
    mapItemMppr.setValues(lessItem->x,x,lessItem->y,y);
    slope = mapItemMppr.getSlope();
    intercept = mapItemMppr.getIntercept();
  }
}


bool mapItem::linkIn(mapItem* itemPtr) {

  if (itemPtr==NULL)                         // Sanity, is there a list?
    return false;
  if (itemPtr->x == x)                       // Duplicates are NOT allowed!
    return false;
  if (itemPtr->x < x) {                      // Ok, we belong on the greater side..
    if (itemPtr->greaterItem!=NULL) {        // There is a greater one..
      return linkIn(itemPtr->greaterItem);   // Do the recursive jump.
    }
    else {                                   // There is no one on greater side..
      lessItem = itemPtr;                    // Link in to end of list here.
      lessItem->greaterItem = this;
      setValues();
      return true;
    }
  } 
  else {                                     // We belong on the less side..
    if (itemPtr->lessItem!=NULL) {           // There is a lesser one..
      if (itemPtr->lessItem->x<x) {          // This is our spot!
        lessItem = itemPtr->lessItem;
        greaterItem = itemPtr;
        lessItem->greaterItem = this;
        greaterItem->lessItem = this;
        setValues();
        greaterItem->setValues();
        return true;
      } 
      else                                  // We are NOT greater than the less item..
      return linkIn(itemPtr->lessItem);     // Do the recursive jump down.
    } 
    else {                                  // There is no lesser one. And we are smaller..
      greaterItem = itemPtr;
      itemPtr->lessItem = this;
      setValues();
      greaterItem->setValues();
      return true;
    }
  }
}


double mapItem::Map(double inVal) {  // We need to find the correct mapper and map this value.

  if (inVal == x)                      // Hey its us!
    return (y);                        // Pass back our y value.
  if (inVal>x) {                       // inVal is greater than us.
    if (greaterItem!=NULL)             // Fine, if there's a greaterItem -
      return greaterItem->Map(inVal);  // Pass it up the line.
    else                               // Oh! We are the top of the line..
    return(y);                         // Again, pass back our y value.
  } 
  else {                               // inVal is less than us..
    if (lessItem==NULL)                // We are the smallest and inVal's smaller?
      return(y);                       // Again, pass back our y value.
    else {                             // inVal's less than us, and there is someone down there..
      if (lessItem->x>inVal)            // inVal is less than the smaller guy -
        return lessItem->Map(inVal);      // Pass it down the line.
      else {                            // inVal is less than us but not less than the smaller guy?
        return doMap(inVal);             // This one we can map!       
      }
    }
  }
}   


double mapItem::doMap(double inVal) {  // We have been choosen to map this value.

  if (inVal < lessItem->x)
    inVal = lessItem->x;
  else if (inVal > x)
    inVal = x;
  return(slope*inVal+intercept);
}


void mapItem::outputItem() {

  Serial.print("x = ");
  Serial.print(x);
  Serial.print(", y = ");
  Serial.println(y);
}

// ***********************

multiMap::multiMap() {

  itemList  = NULL;
}


multiMap::~multiMap(void) {

  clearMap();
}


void multiMap::addPoint(double x, double y) {

  mapItem* newItem;

  newItem = new mapItem(x,y);          // Whip up a fresh item.
  if (itemList == NULL) {             // Look its the first one!
    itemList = newItem;  
  } 
  else {
    if (newItem->linkIn(itemList)) {  // We were successful at linking this in?
      while(itemList->lessItem!=NULL)  // Make sure we point at the smaller end.
        itemList = itemList->lessItem;
    } 
    else {
      delete(newItem);                // Not succesfull at linkin in the new item? Recycle it.
    }
  }
}


void multiMap::clearMap(void) {

  Serial.println("I'm being cleared!!");
  if (itemList!=NULL) {
    while(itemList->greaterItem!=NULL)
      delete(itemList->greaterItem);
    while(itemList->lessItem!=NULL)
      delete(itemList->lessItem);
    delete(itemList);
    itemList = NULL;
  } 
}


double multiMap::Map(double inVal) {

  if (itemList!=NULL)              // If we have mappers..
    return itemList->Map(inVal);   // Map that item!
  else                             // What the heck? We have no mappers at all?
  return 0;                      // I guess zero is the best we can do here.
}

void multiMap::outputList(void) {

  mapItem* trace;

  if (itemList!=NULL) {           // If we have mappers..
    trace = itemList;
    while(trace!=NULL) {
      trace->outputItem();
      trace = trace->greaterItem;
    } 
  } 
  else  {                           // What the heck? We have no mappers at all?
    Serial.println("Empty");
  }
}

mapper.h

#ifndef mapper_h
#define mapper_h

#include <Arduino.h>

class mapper {
public:
  mapper(double x1,double x2,double y1,double y2);

  double Map(double inNum);

  // This stuff is for using the mapper as a liner calculator.
  void setValues(double x1,double x2,double y1,double y2);
  double getSlope(void);
  double getMinX(void);
  double getMaxX(void);
  double getIntercept(void);

private:
  double minX;
  double maxX;
  double slope;
  double intercept;
};

#endif

mapper.cpp

#include "mapper.h"

mapper::mapper(double x1,double x2,double y1,double y2) {

  setValues(x1,x2,y1,y2);
}


void mapper::setValues(double x1,double x2,double y1,double y2) {
  maxX = max(x1,x2);
  minX = min(x1,x2);
  slope = (y1-y2)/(x1-x2);
  intercept = y1 - slope*x1;
}


double mapper::getSlope(void) { 
  return slope; 
}


double mapper::getMinX(void) { 
  return minX; 
}


double mapper::getMaxX(void) { 
  return maxX; 
}


double mapper::getIntercept(void) { 
  return intercept; 
}


double mapper::Map(double inNum) {

  if (inNum < minX)
    inNum = minX;
  else if (inNum > maxX)
    inNum = maxX;
  return(slope*inNum+intercept);
}

miniSSC.h

#ifndef miniSSC_h
#define miniSSC_h

#include <Arduino.h>
#include "mapper.h"

#define miniSSCMinVal -1
#define miniSSCMaxVal 1

class miniSSC  {

public:
  miniSSC(void);

  void initSSC(bool fast=false);
  void setServo(byte servoNum,float inVal);  // Value ranged from -1.0 ... 1.0
private :
  
  byte buff[3];
  byte lastVal[255];
};

#endif

miniSSC.cpp

#include "miniSSC.h"

mapper SSCMapper(miniSSCMinVal,miniSSCMaxVal,0,254);

miniSSC::miniSSC() {

  byte i;
  buff[0] = 255;
  for(i=0;i<=254;i++)
    lastVal[i] = 255;
}


void miniSSC::initSSC(bool fast) {

  if (fast)
    Serial.begin(9600);
  else
    Serial.begin(2400);
}


void miniSSC::setServo(byte servoNum,float inVal) {

  buff[1] = servoNum;                                    // Slam the servo num into the output buffer
  buff[2] = byte(SSCMapper.Map(inVal));                  // Scale the inVal to a byte and pop it in there..
  if (servoNum<255 && buff[2] != lastVal[servoNum]) {    // Now before spending time to write it out, sanity check all this.
    Serial.flush();                                      // Maybe we need to wait 'till its done?
    Serial.write(buff,3);                                // Everything's ok, write the buffer out.
    lastVal[servoNum] = buff[2];                         // update our last position for this servo.
  }
}

timeObj.h

#ifndef timeObj_h
#define timeObj_h


class timeObj {

public:
  timeObj(float inMs);

  void setTime(float inMs,bool startNow=true);    // Change the time duration for next start..
  void start(void);                               // Start the timer "now".
  void stepTime(void);                            // Restart the timer from last end time.
  bool ding(void);                                // Timer has expired.

private:
  unsigned long waitTime;
  unsigned long startTime;
  unsigned long endTime;
  bool crossing;
};

#endif

timeObj.cpp

#include "timeObj.h"
#include <arduino.h>

timeObj::timeObj(float inMs) {

  setTime(inMs);
  startTime = 0;
  endTime = 0;
  crossing = false;
}


void timeObj::setTime(float inMs,bool startNow) {

  waitTime = 1000 * inMs;
  if (startNow) start();
}


void timeObj::start(void) {

  startTime = micros();
  endTime = startTime + waitTime;
  crossing = endTime < startTime;
}


void timeObj::stepTime(void) {

  startTime = startTime + waitTime;
  endTime = startTime + waitTime;
  crossing = endTime < startTime;
}


bool timeObj::ding(void) {
  
  unsigned long now;

  now = micros();
  if (crossing)
    return (now < startTime) && (now >= endTime);
  else
    return now >= endTime;
}

blinker.h

#ifndef blinker_h
#define blinker_h

// A simple blink class used to turn on and off blinking LEDs.
// Spawn a blinker for each pin you would like to blink an LED on.
// Its not ment to be the end all. Its just something to blink LEDs.
// It will probably crash and burn when the microsecond counter overruns.
// 
// Written by : Jim Lee @ www.LeftCoast.biz


// Some defaults in case the user just doesn't care..
#define defPin 13
#define defOnMs 50
#define defPeriodMs 400

class blinker {
	
public:
	blinker(int inPin=defPin,float inOnMs=defOnMs, float inPeriodMs=defPeriodMs);
	
	void init(bool running=true);	        // Call this in the setup function perhaps?
	void setBlink(bool onOff);		// Start or stop the blinking..
	void idle(void);			// Call this in the loop function.
	
	void setTimes(float inOnMs, float inPeriodMs);	// Want to change the blink?
	
private:
	unsigned long onTime;
	unsigned long periodTime;
	unsigned long startTime;
	bool running;
	bool pinHigh;
	int pin;
};

#endif

blinker.cpp

#include "blinker.h"
#include <arduino.h>

blinker::blinker(int inPin,float inOnMs, float inPeriodMs) {

   running = false;
   pinHigh = false;
   pin = inPin;
   setTimes(inOnMs,inPeriodMs);
}


void blinker::init(bool running) {

   pinMode(pin, OUTPUT);
   setBlink(running); 	
}


void blinker::setBlink(bool onOff) {

   if(onOff != running) {		// ignore if no change
      if (onOff) {			// Start blinking..
         startTime = micros();		// Starting NOW!
         digitalWrite(pin,HIGH);	// light on!
         pinHigh = true;		// set state.
         running = true;
      } 
      else {			         // Stop blinking..
         digitalWrite(pin,LOW);		// light off.
         running = false;		// set state.
         pinHigh = false;
      }
   }
}


void blinker::idle(void) {

   unsigned long now;

   if (running) {
      now = micros();				// Look at the time!
      if (pinHigh) {				// If the light is on..
         if (now >= startTime+onTime) {		// And its been on long enough!
            digitalWrite(pin,LOW);		// light off.
            pinHigh = false;			// set state
         }
      } 
      else {					// Else the light is off..
         if (now >= startTime+periodTime) {	// And its been off long enough!
            digitalWrite(pin,HIGH);		// light on!
            pinHigh = true;			// set state
            startTime = startTime + periodTime;	// Set next iteration.
         }
      }
   }
}


void blinker::setTimes(float inOnMs, float inPeriodMs) {

   onTime = round(1000*inOnMs);
   periodTime = round(1000*inPeriodMs);
}

I think that's all of it..

-jim lee

Holy cow! I can see why you are running out of ram.
But, there is room for optimizations.
First of all, some of the instructional strings in main.cpp can be stored in PROGMEM. That should gain you atleast 100 bytes.
I am also sure there are some optimizations that can be done in the "thorax" code but it makes my brain hurt to look at. :roll_eyes:

Lots of candidates for PROGMEM strings there

Hey Jim,
Really you should look into reframicating the software if only that others may use it on UNO's buuuut,

Check out the Teensy++!
Teensy USB Development Board

128k flash, 8k SRAM, 4k EEPROM.
46 I/O pins; 8 analog inputs, 9 PWM's.
USB based Atmel chip, programmable as HID/MIDI and they have libraries.
Really small. Arduino compatible, able to use the Arduino IDE.
Way more than an UNO, not sure how it rates compared to a MEGA2560.
With pins (sticks right into a breadboard), $27. Without, $24.

Or you can buy SPI-bus SRAM, 8k x 8-bit through-hole DIPs are pretty cheap @ $.70. See the 3K640-I/P:
RAM's (Random Access Memory ICs)

Not as fast as on-board SRAM but way cheaper than a new board.
The 23K256-I/P 32k x 8-bit SPI-bus SRAM are surface mount, for $1.35 they are 4x as much.

Of course either way there's a shipping charge so save up and buy enough to justify that.

And my non-disclaimer is that I have no connection to either of those companies except being a satisfied customer. PJRC (the Teensy site) shipping got to me a lot quicker but then across the US isn't as far from me than Hong Kong. But the HK shipment 'only' took between 2-3 weeks and they DO have so many different things.

There are many other good sites but those jump out at me when it comes to 'Mo RAM!'. Hey... Moe RAM, that'd be a good forum name, wouldn't it? People could say "Hey Moe!".

'1284's have 16K SRAM. '2560 only has 8K.
$5 processor vs $13 too.

miniSSC.h:
  byte lastVal[255];

ouch? I certainly hope that this only gets instantiated once. Even then, I think it's safe to say that the Arduino is NOT EVER going to support 255 simultaneous servos, so you can immediately save a couple hundred bytes here by changing this to a more realistic value... (nit: it should be a #define somewhere too, rather than a hardcoded constant.)

mapper.h:
private:
  double minX;
  double maxX;
  double slope;
  double intercept;

Since you are adding a significant number of these to each joint (?), you might want to check out just how much space the list of mappers is taking vs a simpler data structure like an array with a static maximum size.

The usual argument against C++ at my old stomping grounds was not so much that it was "inefficient", but that it could be silently and "innocently" inefficient. A small piece of normal-looking source code could inadvertently lead to runtime and/or storage problems that were otherwise unexpected. In an embedded system without much (or any) memory protection, this is particularly serious. An Arduino won't tell you that there is no space for the next "new" that your program does, it will just start failing. Sometimes in mysterious ways...

Sorry, was off the grid for a week there.

In the miniSSC file, The 255 byte array is only created once. Its just some code to run a string of SSC-ASD2 Discontinued boards. They listen on the serial port. The idea was to unload the processor from dealing with keeping the Servos alive. Currently 18 servos.

As for the mappers, I didn't create them with the 6 legs, just the single thorax. The legs go to the thorax for mapping services.

I worry about the mutiMapper code because I kinda' cheated and made them recursive.

Serial.writeLn("bla bla bla in quotes") <- I used form this a lot in the main file. Am I reading this correct that this is not a good idea memory-wise?

Actually, I've been looking at the teensy++. But really, I have no idea what RAM I need. Am I close and with a few tweaks I'll be ok? Or am I way out in left field where the extra 8k won't make any difference at all? Some sort of took to look at this would sure be nice.

One last thing, what is the difference between EEPROM and Flash memory? They seem like the same thing, but I guess they are not?

-jim lee

Serial.writeLn("bla bla bla in quotes") <- I used form this a lot in the main file. Am I reading this correct that this is not a good idea memory-wise?

Correct. Since program memory is not "normally" addressable, all C variables/objects/etc will end up being allocated in RAM, even if their value is constant. In arduino 1.0, steams (xxx.write()) understand "strings allocated in flash", which can be done by adding F("string"), ("Serial.writeln(F("bla bla bla in quotes")):wink:

Jim, when you're making structures, etc, that store and use data.. don't you count the bytes required for the data?

And if there's 18 servo boards then use [18] or a realistic maximum instead of [255] for that array.

All floating point on Arduino is 32-bit no matter float or double. If you can switch the sections that use FP to use 16 or 32-bit integer then you will gain speed and possibly save on ram. A 16-bit integer is good for +/- 4 digits or unsigned, 5 digits. Where the decimal place is makes it flexible; if I want fractions of a kilogram then I weigh -integer- grams, not -fp- .001 kg's. 16 bits for 4 or 5 places is pretty good. Back in the ages BC (Before Calculators), 3 or 4 places was deemed good for most classwork. And I see you use it on robot insect leg joints. How precise do you need?

Tables should be made in PROGMEM to hold pre-calculated values, trig, sqrt, that FP functions supplied except for integers -- table them up in PROGMEM, lookup there will be faster than using FP but slower than ram. But you have no choice unless you go to a bigger MCU.

Man, that 1284 has gotta be looking real good about now.....

"Man, that 1284 has gotta be looking real good about now....."

For sure!
I need to figure out my FTP password again so I can upload some more pictures. For now,
check #282, #287 here,
http://arduino.cc/forum/index.php/topic,80483.285.html
and PL on #313