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
Servos in uLisp
Servos arenāt built-in to uLisp, but you could easily add them with an extension.
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!
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.
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!
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.
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.
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!!!