Servos in uLisp


#1

Hello. I am a beginner in programming and microcontrollers.
I bought a couple of weeks ago an Arduino kit, which includes the Uno R3. I am doing the projects included in the manual without any problem. I program them in the Arduino language and also in uLisp (much clearer, cleaner and more elegant). But I have a problem activating a servo. I can do it in Arduino language using the Servo.h library, but not in uLisp. Is this not possible? Is there a way to use the library in uLisp?
Or some other solution accessible to a beginner?
I am not in a position to program something like this.
Any advice or help will be welcome.
This world is fascinating!!
Thank you very much


#2

Servos arenā€™t built-in to uLisp, but you could easily add them with an extension.


#3

Here is a little extension that manages an arbitrary number of servos:

#include <Servo.h>

 struct ulservo {
  int snum;
  int pin;
  struct ulservo* nextservo;
  Servo servo;
};

struct ulservo* servolist = NULL;
struct ulservo* curservo = NULL;

/*
  (servo-attach snum pin [usmin] [usmax])
  Attach servo snum to pin. Optionally define new pulse width min/max in microseconds.
*/
object *fn_ServoAttach (object *args, object *env) {
  (void) env;
 
  int snum = checkinteger(first(args));   // zero based index!;
  int pin = checkinteger(second(args));
  int usmin = 544;
  int usmax = 2400;

  args = cdr(args);
  args = cdr(args);
  if (args != NULL) {
    usmin = checkinteger(first(args));
    args = cdr(args);
    if (args != NULL) {
      usmax = checkinteger(first(args));
    }
  }

  if(servolist == NULL) {
    if((servolist = (struct ulservo*)malloc(sizeof(struct ulservo))) == NULL) {
       pfstring("Out of memory", (pfun_t)pserial);
       return nil;
    }

    servolist->snum = snum;
    servolist->pin = pin;
    servolist->nextservo = NULL;
    servolist->servo = Servo();
    servolist->servo.attach(pin, usmin, usmax);
  }
  else {
    curservo = servolist;
    while(curservo->nextservo != NULL) {
      if ((curservo->snum == snum) || (curservo->pin == pin)) {
        pfstring("Servo number or pin already in use!", (pfun_t)pserial);
        return nil;
      }
      curservo = curservo->nextservo;
    }

    if ((curservo->snum == snum) || (curservo->pin == pin)) {
      pfstring("Servo number or pin already in use!", (pfun_t)pserial);
      return nil;
    }

    if((curservo->nextservo = (struct ulservo*)malloc(sizeof(struct ulservo))) == NULL) {
        pfstring("Out of memory", (pfun_t)pserial);
        return nil;
    }

    curservo = curservo->nextservo;

    curservo->snum = snum;
    curservo->pin = pin;
    curservo->servo = Servo();
    curservo->servo.attach(pin, usmin, usmax);
    curservo->nextservo = NULL;
  }
  return tee;
}

/*
  (servo-write snum angle)
  Set angle of servo snum in degrees (0 to 180).
*/
object *fn_ServoWrite (object *args, object *env) {
  (void) env;
 
  int snum = checkinteger(first(args));
  int angle = checkinteger(second(args));
  curservo = servolist;

  if (curservo != NULL) {

    while(curservo->snum != snum) {
      curservo = curservo->nextservo;
      if (curservo == NULL) break;
    }

    if(curservo != NULL) {
      curservo->servo.write(angle);
      return number(angle);
    }
  }

  pfstring("Servo not found", (pfun_t)pserial);
  return nil;
}

/*
  (servo-write-microseconds snum usecs)
  Set angle of servo snum using a pulse width value in microseconds.
*/
object *fn_ServoWriteMicroseconds (object *args, object *env) {
  (void) env;
 
  int snum = checkinteger(first(args));
  int us = checkinteger(second(args));
  curservo = servolist;

  if (curservo != NULL) {

    while(curservo->snum != snum) {
      curservo = curservo->nextservo;
      if (curservo == NULL) break;
    }

    if(curservo != NULL) {
      curservo->servo.writeMicroseconds(us);
      return number(us);
    }
  }

  pfstring("Servo not found", (pfun_t)pserial);
  return nil;
}

/*
  (servo-read snum)
  Read current angle of servo snum in degrees.
*/
object *fn_ServoRead (object *args, object *env) {
  (void) env;

  int snum = checkinteger(first(args));
  curservo = servolist;

  if (curservo != NULL) {

    while(curservo->snum != snum) {
      curservo = curservo->nextservo;
      if (curservo == NULL) break;
    }

    if(curservo != NULL) {
      return number(curservo->servo.read());
    }
  }

  pfstring("Servo not found", (pfun_t)pserial);
  return nil;
}

/*
  (servo-detach snum)
  Detach servo snum, thus freeing the assigned pin for other tasks.
*/
object *fn_ServoDetach (object *args, object *env) {
  (void) env;

  int snum = checkinteger(first(args));
  curservo = servolist;
  struct ulservo* lastservo = servolist;

  if (curservo != NULL) {

    while(curservo->snum != snum) {
      lastservo = curservo;
      curservo = curservo->nextservo;
      if (curservo == NULL) break;
    }

    if(curservo != NULL) {
      curservo->servo.detach();
      if (curservo == servolist) {    // delete first element of list
        servolist = curservo->nextservo;
      }
      else {
        lastservo->nextservo = curservo->nextservo;
      }
      free(curservo);
      return tee;
    }
  }

  pfstring("Servo not found", (pfun_t)pserial);
  return nil;
}

// Symbol names
const char stringServoAttach[] PROGMEM = "servo-attach";
const char stringServoWrite[] PROGMEM = "servo-write";
const char stringServoWriteMicroseconds[] PROGMEM = "servo-write-microseconds";
const char stringServoRead[] PROGMEM = "servo-read";
const char stringServoDetach[] PROGMEM = "servo-detach";

// Documentation strings
const char docServoAttach[] PROGMEM = "(servo-attach snum pin [usmin] [usmax])\n"
"Attach servo snum to pin. Optionally define new pulse width min/max in microseconds.";
const char docServoWrite[] PROGMEM = "(servo-write snum angle)\n"
"Set angle of servo snum in degrees (0 to 180).";
const char docServoWriteMicroseconds[] PROGMEM = "(servo-write snum usecs)\n"
"Set angle of servo snum using a pulse width value in microseconds.";
const char docServoRead[] PROGMEM = "(servo-read snum)\n"
"Read current angle of servo snum in degrees.";
const char docServoDetach[] PROGMEM = "(servo-detach snum)\n"
"Detach servo snum, thus freeing the assigned pin for other tasks.";

// Symbol lookup table
const tbl_entry_t lookup_table2[] PROGMEM = {

  { stringServoAttach, fn_ServoAttach, 0224, docServoAttach },
  { stringServoWrite, fn_ServoWrite, 0222, docServoWrite },
  { stringServoWriteMicroseconds, fn_ServoWriteMicroseconds, 0222, docServoWriteMicroseconds },
  { stringServoRead, fn_ServoRead, 0211, docServoRead },
  { stringServoDetach, fn_ServoDetach, 0211, docServoDetach },

};

// Table cross-reference functions - do not edit below this line

tbl_entry_t *tables[] = {lookup_table, lookup_table2};
const unsigned int tablesizes[] = { arraysize(lookup_table), arraysize(lookup_table2) };

const tbl_entry_t *table (int n) {
  return tables[n];
}

unsigned int tablesize (int n) {
  return tablesizes[n];
}

Save the code above as ā€œServoExtension.inoā€ (for example) in the same directory as your ulisp.ino file. When you compile uLisp within the Arduino ide, the extension should automatically be included. Before that please uncomment

#define extensions

in the main .ino file. These are the new uLisp functions the extension provides:

(servo-attach snum pin [usmin] [usmax]) 
"Attach servo snum to pin. Optionally define new pulse width min/max in microseconds."
(servo-write snum angle)
"Set angle of servo snum in degrees (0 to 180)."
(servo-write snum usecs)
"Set angle of servo snum using a pulse width value in microseconds."
(servo-read snum)
"Read current angle of servo snum in degrees."
(servo-detach snum)
"Detach servo snum, thus freeing the assigned pin for other tasks."

This should work; Iā€™ve extracted it from my ulisp-lispbox extension. If you run into problems, let me know!


#4

I hope I donā€™t have space problems with the board.
Thank you very much for your help


#5

Hello again.
Today I tried the solution you provided me.
I get the following error:
C:\Users\PC\Documents\Arduino\uLisp4-7\ServoExtension.ino:224:1: error: too many initializers for ā€˜tbl_entry_t {aka const}ā€™
};
^
C:\Users\PC\Documents\Arduino\uLisp4-7\ServoExtension.ino:224:1: error: too many initializers for ā€˜tbl_entry_t {aka const}ā€™
C:\Users\PC\Documents\Arduino\uLisp4-7\ServoExtension.ino:224:1: error: too many initializers for ā€˜tbl_entry_t {aka const}ā€™
C:\Users\PC\Documents\Arduino\uLisp4-7\ServoExtension.ino:224:1: error: too many initializers for ā€˜tbl_entry_t {aka const}ā€™
C:\Users\PC\Documents\Arduino\uLisp4-7\ServoExtension.ino:224:1: error: too many initializers for ā€˜tbl_entry_t {aka const}ā€™

exit status 1

Compilation error: too many initializers for ā€˜tbl_entry_t {aka const}ā€™

I think I followed your instructions.
Maybe I forgot something.
What could be the problem?
Thanks again.


#6

Iā€™m sorry it didnā€™t work out immediately - thatā€™s always frustrating. Currently, Iā€™m not sure what might cause that error. Forgive me for asking that, but did you include the lines below the following commentary line exactly?

// Table cross-reference functions - do not edit below this line

The block that follows below this line must be kept unaltered (see Davids own page about extending uLisp).

Another thought - Iā€™ve had strange stuff like that from time to time: Maybe the compiler complains because of an empty line here. Please try and change the following block

// Symbol lookup table
const tbl_entry_t lookup_table2[] PROGMEM = {

  { stringServoAttach, fn_ServoAttach, 0224, docServoAttach },
  { stringServoWrite, fn_ServoWrite, 0222, docServoWrite },
  { stringServoWriteMicroseconds, fn_ServoWriteMicroseconds, 0222, docServoWriteMicroseconds },
  { stringServoRead, fn_ServoRead, 0211, docServoRead },
  { stringServoDetach, fn_ServoDetach, 0211, docServoDetach },

};

to this:

// Symbol lookup table
const tbl_entry_t lookup_table2[] PROGMEM = {
  { stringServoAttach, fn_ServoAttach, 0224, docServoAttach },
  { stringServoWrite, fn_ServoWrite, 0222, docServoWrite },
  { stringServoWriteMicroseconds, fn_ServoWriteMicroseconds, 0222, docServoWriteMicroseconds },
  { stringServoRead, fn_ServoRead, 0211, docServoRead },
  { stringServoDetach, fn_ServoDetach, 0211, docServoDetach },
};

Sounds a bit absurd, but maybe the compiler doesnā€™t like the empty line between the last comma and the closing bracket. Just an idea, not sure that helps.
Let me know if you had any success!


#7

Yes Idid.

I changed the block, but without success. The error repeats exactly the same


#8

Iā€™ve probably overlooked something here: Youā€™re using the AVR-Nano version of uLisp, right? If so, itā€™s probably not possible to use extensions with it at all, Iā€™m afraid.
If thatā€™s the case, you might want to consider buying a more powerful board like the Arduino R4 Minima or the Adafruit Metro M4 - both share the shield compatibility with the original UNO. The 328P CPU has very limited capabilities, above all barely enough RAM.


#9

I think so too.
In any case, thank you very much for your help and interest.
It has helped me think and learn a little more.


#10

Hello! I am modify same uLisp firmware for addition to registry a several extensions!

HowTo:

1. In uLisp firmware at head to registry two extension for sample:

//#define extensions
#define extension_1 registry_extension_servos()
#define extension_2 registry_extension_gfx()

2. Extended tables to ten:

tbl_entry_t *tables[] = {lookup_table, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
unsigned int tablesizes[] = { arraysize(lookup_table), 0, 0, 0, 0, 0, 0, 0, 0, 0 };

3. Addition a registry_extension() function:

void registry_extenstion(tbl_entry_t *tbl, unsigned int tbl_size) {
int i=0;
for (;i<10;i++) if (tables[i]==NULL) break;
if (i<10) {
tables[i] = tbl;
tablesizes[i] = tbl_size;
}
}

4. Modify lookupbuiltin() to ten and break of limits:

builtin_t lookupbuiltin (char* c) {
unsigned int end = 0, start;
for (int n=0; n<10; n++) {
start = end;
int entries = tablesize(n);
if (entries==0) return ENDFUNCTIONS;
end = end + entries;
for (int i=0; i<entries; i++) {
if (strcasecmp_P(c, (char*)pgm_read_ptr(&(table(n)[i].string))) == 0)
return (builtin_t)(start + i);
}
}
return ENDFUNCTIONS;
}

5. Additions to setup():
//extensions:
#ifdef extension_1
extension_1;
#endif
#ifdef extension_2
extension_2;
#endif
#ifdef extension_3
extension_3;
#endif
#ifdef extension_4
extension_4;
#endif
#ifdef extension_5
extension_5;
#endif
#ifdef extension_6
extension_6;
#endif
#ifdef extension_7
extension_7;
#endif
#ifdef extension_8
extension_8;
#endif
#ifdef extension_9
extension_9;
#endif

6. In extension at the end of file of gfx tab:

void registry_extension_gfx() {
registry_extenstion(lookup_table_gfx, arraysize(lookup_table_gfx));
}

7. In extension at the end of file of servos tab:

void registry_extension_servos() {
registry_extenstion(lookup_table_servos, arraysize(lookup_table_servos));
}

P.S. I tested it in [AVR-Nano Release 4.6]. Itā€™s a only one firmware, that work in Simul IDE for Arduino Mega!!!