Skip to content

Carbot is in the weeds


I had changed the carbot control scheme to make it quicker and more precice. HAH! The S2 version ran head on into the wall at high speed and consistently made 180 degree turns instead of 90. I detuned the speed a bit but it stuck to its 180 degree turns and had no interest in following the wall.

I reverted to the previous(s1) control scheme but found the same 180 degree turns and wall disinterest. I’m documenting two runs with video here but i’m going on a no-robot walking holiday and the heck with it!

//olduino carbot simple version Jan 24, 2015
//15-04-26 using <> version of cpu1802spd4port7.h
//15-04-29 cruising speed increased, left turn now 5 ms
#include <olduino.h>
#include <nstdlib.h> //supports c d s x l cx
#include <cpu1802spd4port7.h>
#include "softpwm.h"
#include "carbot.h"
#include "pingBN.h"
unsigned int wdist,oldwdist, fprox; // distance to wall on right current & previous, proximity of barrier in front
#define maxfprox 160 //max front barrier proximity
#define maxwdist 30 //min right barrier dist
#define minwdist 25 //min right barrier dist
#define basespeed 192	//was 192
#define lowspeed 32	//was 32
unsigned int sharpy(){  //reads the Sharp GP2D12 IR rangefinder via the MCP3002 ADC, returns raw ADC value
	asm("	dec	sp\n" //make a work area
		"	sex 3\n"	//set x to pc
		"	out 6\n	db 0x68\n"	//send 68 to read channel 0
		"	sex sp\n	inp 6\n" //pick up top bits
		"	ani 3\n	phi 15\n"	//put bits 8&9 of result into return register

		"	sex 3\n"	//set x to pc
		"	out 6\n	db 0xAA\n"	//send pattern to read low bits
		"	sex sp\n	inp 6\n	plo 15\n" //pick up low bits to return register
		"	inc	sp\n"	//restore stack pointer
		"	cretn\n");	//actual return
	return 0;	//dummy return for compiler
}
void killdrive(){//stops both motors
	digitalWrite(pwmb,LOW);//kill power right
	digitalWrite(pwma,LOW);//kill power left
}
void hardleft(){//hard turn with left wheel running backwards
	printf("L>\n");
	digitalWrite(pwmb,HIGH);//full power right
	digitalWrite(pwma,HIGH);//full power left
	digitalWrite(bin1,HIGH); digitalWrite(bin2,LOW);
	digitalWrite(ain1,LOW); digitalWrite(ain2,HIGH); //reverse left wheel
	delay(1); //nominal delay only
}

void tooclose(){ //if we are closer than minwdist to the wall on our right side
	printf("TC");
    if (wdist>oldwdist) {//but going the right way
		analogWrite(pwma,basespeed);analogWrite(pwmb,basespeed); //proceed
		printf(" wf-1 ");
    }
    else{
		analogWrite(pwma,lowspeed);analogWrite(pwmb,basespeed); //bear left
		printf(" wvlft ");
    }
}
void toofar(){//if we are further than minwdist from the wall on our right side
	printf("TF");
    if (wdist<oldwdist) {//but going the right way
		analogWrite(pwma,basespeed);analogWrite(pwmb,basespeed); //proceed
		printf(" wf-2 ");
    }
    else{
		analogWrite(pwma,basespeed);analogWrite(pwmb,lowspeed); //bear right
		printf(" wvrt ");
    }
}

void inthezone(){ //if we are between minwdist and maxwdist
	printf("IZ");
	analogWrite(pwma,basespeed);analogWrite(pwmb,basespeed); //proceed
	printf(" wf-3 ");
}

void cruiseAlongWall(){
	if (0!=wdist) oldwdist=wdist; //track the wall distance after the first time
	wdist=pingQ()/2; //get the wall distance in cm
	printf("cw %d\n",wdist);
	if (wdist<minwdist){
		tooclose();
	}else if (wdist>maxwdist){
		toofar();
	}else{
		inthezone();
	}
	//the routines above will have set the motor power levels, now we activate them
	digitalWrite(bin1,HIGH); digitalWrite(bin2,LOW);
	digitalWrite(ain1,HIGH); digitalWrite(ain2,LOW);
	pwmcycleN(15);//forward a bit
}

void main(){
	unsigned int ttl=300; //time to live limit,
	printf("Simpler Panopticon Carbot(L=1,B=255,S=64,min/max=25/30) O-carbotS1.c\n");
	PIN4=0x80;out(4,0x80);
	while(ttl!=00){
		printf("@%d:",ttl);
		digitalWrite(7,LOW);fprox=sharpy(); digitalWrite(7,HIGH);
		printf("<%d ",fprox);
		if (fprox<maxfprox){ // more than 1 ft from barrier
			while((fprox<maxfprox)&&ttl!=0){
				printf("F>\n");
				cruiseAlongWall();//set a course to track the wall
				digitalWrite(7,LOW);fprox=sharpy(); digitalWrite(7,HIGH);
				ttl--;
				printf("[%d]",fprox);
			}
			killdrive();
		}else{
			while((fprox>=maxfprox) && ttl!=0){
				hardleft();
				digitalWrite(7,LOW);fprox=sharpy(); digitalWrite(7,HIGH);
				ttl--;
				printf("(%d)",fprox);
			}
			killdrive();
		}
	}
	PIN4=0;out(4,0); //kill it all
}
#include <olduino.c>
#include <nstdlib.c>
#include "softpwm.c"
#include "pingBN.c"
//olduino carbot simple version Jan 24, 2015
//
#include <olduino.h>
#include <nstdlib.h> //supports c d s x l cx
#include "cpu1802spd4port7.h"
#include "softpwm.h"
#include "carbot.h"
#include "pingBN.h"
unsigned int wdist,oldwdist, fprox; // distance to wall on right current & previous, proximity of barrier in front
#define maxfprox 160 //max front barrier proximity
#define minwdist 30 //min right barrier dist
#define basespeed 192
#define lowspeed 32
unsigned int sharpy(){  //reads the Sharp GP2D12 IR rangefinder via the MCP3002 ADC, returns raw ADC value
	asm("	dec	sp\n" //make a work area
		"	sex 3\n"	//set x to pc
		"	out 6\n	db 0x68\n"	//send 68 to read channel 0
		"	sex sp\n	inp 6\n" //pick up top bits
		"	ani 3\n	phi 15\n"	//put bits 8&9 of result into return register

		"	sex 3\n"	//set x to pc
		"	out 6\n	db 0xAA\n"	//send pattern to read low bits
		"	sex sp\n	inp 6\n	plo 15\n" //pick up low bits to return register
		"	inc	sp\n"	//restore stack pointer
		"	cretn\n");	//actual return
	return 0;	//dummy return for compiler
}
void killdrive(){//stops both motors
	digitalWrite(pwmb,LOW);//kill power right
	digitalWrite(pwma,LOW);//kill power left
}
void hardleft(){//60 ms turn with left wheel running backwards
	printf("L>\n");
	digitalWrite(pwmb,HIGH);//full power right
	digitalWrite(pwma,HIGH);//full power left
	digitalWrite(bin1,HIGH); digitalWrite(bin2,LOW);
	digitalWrite(ain1,LOW); digitalWrite(ain2,HIGH); //reverse left wheel
	delay(60);
}

void tooclose(){ //if we are closer than minwdist to the wall on our right side
    if (wdist>oldwdist) {//but going the right way
		analogWrite(pwma,basespeed);analogWrite(pwmb,basespeed); //proceed
		printf(" wf-1 ");
    }
    else{
		analogWrite(pwma,lowspeed);analogWrite(pwmb,basespeed); //bear left
		printf(" wvlft ");
    }
}
void toofar(){//if we are further than minwdist from the wall on our right side
    if (wdist<oldwdist) {//but going the right way
		analogWrite(pwma,basespeed);analogWrite(pwmb,basespeed); //proceed
		printf(" wf-2 ");
    }
    else{
		analogWrite(pwma,basespeed);analogWrite(pwmb,lowspeed); //bear right
		printf(" wvrt ");
    }
}
void cruiseAlongWall(){
	if (0!=wdist) oldwdist=wdist; //track the wall distance after the first time
	wdist=pingQ()/2; //get the wall distance in cm
	printf("cw %d\n",wdist);
	if (wdist<minwdist){
		tooclose();
	}else{
		toofar();
	}
	//the routines above will have set the motor power levels, now we activate them
	digitalWrite(bin1,HIGH); digitalWrite(bin2,LOW);
	digitalWrite(ain1,HIGH); digitalWrite(ain2,LOW);
	pwmcycleN(15);//forward a bit
}

void main(){
	unsigned int ttl=300; //time to live limit,
	printf("Simpler Panopticon Carbot O-carbotS1.c\n");
	PIN4=0x80;out(4,0x80);
	while(ttl!=00){
		printf("@ %d:",ttl);
		digitalWrite(7,LOW);fprox=sharpy(); digitalWrite(7,HIGH);
		printf("<%d ",fprox);
		if (fprox<maxfprox){ // more than 1 ft from barrier
			killdrive();//kill any turn that was going on
			printf("F>\n");
			cruiseAlongWall();//set a course to track the wall
		}else{
			hardleft();
		}
		ttl--; //reduce time to live
	}
	PIN4=0;out(4,0); //kill it all
}
#include <olduino.c>
#include <nstdlib.c>
#include "softpwm.c"
#include "pingBN.c"
15:02:49.211> @300:<60 F>
15:02:49.273> cw 12
15:02:49.460> TC wf-1 [56]F>
15:02:49.460> cw 12
15:02:49.702> TC wvlft [62]F>
15:02:49.764> cw 13
15:02:49.951> TC wf-1 [65]F>
15:02:50.011> cw 86
15:02:50.198> TF wvrt [65]F>
15:02:50.260> cw 28
15:02:50.510> IZ wf-3 [71]F>
15:02:50.510> cw 14
15:02:50.760> TC wvlft [76]F>
15:02:50.760> cw 15
15:02:51.009> TC wf-1 [85]F>
15:02:51.009> cw 15
15:02:51.259> TC wvlft [97]F>
15:02:51.259> cw 17
15:02:51.508> TC wf-1 [121]F>
15:02:51.508> cw 18
15:02:51.760> TC wf-1 [149]F>
15:02:51.760> cw 19
15:02:52.072> TC wf-1 [220]@289:<268 L>
15:02:52.072> (299)L>
15:02:52.135> (329)L>
15:02:52.197> (357)L>
15:02:52.197> (384)L>
15:02:52.260> (413)L>
15:02:52.322> (437)L>
15:02:52.322> (467)L>
15:02:52.384> (483)L>
15:02:52.447> (458)L>
15:02:52.509> (401)L>
15:02:52.509> (328)L>
15:02:52.634> (105)@277:<92 F>
15:02:52.634> cw 6
15:02:52.884> TC wvlft [28]F>
15:02:52.884> cw 27
15:02:53.133> IZ wf-3 [49]F>
15:02:53.133> cw 27
15:02:53.383> IZ wf-3 [51]F>
15:02:53.383> cw 27
15:02:53.632> IZ wf-3 [55]F>
15:02:53.632> cw 26
15:02:53.882> IZ wf-3 [62]F>
15:02:53.944> cw 25
15:02:54.132> IZ wf-3 [62]F>
15:02:54.132> cw 24
15:02:54.381> TC wvlft [65]F>
15:02:54.444> cw 24
15:02:54.631> TC wvlft [69]F>
15:02:54.693> cw 24
15:02:54.880> TC wvlft [78]F>
15:02:54.943> cw 26
15:02:55.130> IZ wf-3 [81]F>
15:02:55.192> cw 27
15:02:55.380> IZ wf-3 [89]F>
15:02:55.442> cw 30
15:02:55.629> IZ wf-3 [101]F>
15:02:55.692> cw 32
15:02:55.879> TF wvrt [127]F>
15:02:55.941> cw 32
15:02:56.191> TF wvrt [161]@263:<171 L>
15:02:56.253> (186)L>
15:02:56.316> (188)L>
15:02:56.378> (195)L>
15:02:56.378> (197)L>
15:02:56.440> (200)L>
15:02:56.440> (196)L>
15:02:56.503> (192)L>
15:02:56.565> (182)L>
15:02:56.565> (163)L>
15:02:56.693> (114)@253:<149 F>
15:02:56.693> cw 11
15:02:56.942> TC wvlft [151]F>
15:02:56.942> cw 20
15:02:57.192> TC wf-1 [153]F>
15:02:57.192> cw 20
15:02:57.504> TC wvlft [176]@250:<173 L>
15:02:57.566> (168)L>
15:02:57.566> (173)L>
15:02:57.629> (172)L>
15:02:57.691> (177)L>
15:02:57.691> (163)L>
15:02:57.816> (136)@244:<4 F>
15:02:57.816> cw 9
15:02:58.065> TC wvlft [55]F>
15:02:58.128> cw 88
15:02:58.315> TF wvrt [61]F>
15:02:58.315> cw 11
15:02:58.565> TC wvlft [61]F>
15:02:58.627> cw 85
15:02:58.814> TF wvrt [61]F>
15:02:58.877> cw 13
15:02:59.064> TC wvlft [61]F>
15:02:59.126> cw 86
15:02:59.313> TF wvrt [65]F>
15:02:59.376> cw 85
15:02:59.625> TF wf-2 [64]F>
15:02:59.625> cw 33
15:02:59.875> TF wf-2 [73]F>
15:02:59.875> cw 20
15:03:00.127> TC wvlft [82]F>
15:03:00.127> cw 132
15:03:00.377> TF wvrt [90]F>
15:03:00.377> cw 20
15:03:00.631> TC wvlft [93]F>
15:03:00.694> cw 22
15:03:00.881> TC wf-1 [100]F>
15:03:00.881> cw 32
15:03:01.130> TF wvrt [116]F>
15:03:01.130> cw 24
15:03:01.380> TC wvlft [145]F>
15:03:01.442> cw 25
15:03:01.691> IZ wf-3 [172]@229:<197 L>
15:03:01.754> (208)L>
15:03:01.754> (220)L>
15:03:01.816> (230)L>
15:03:01.879> (232)L>
15:03:01.879> (226)L>
15:03:01.941> (220)L>
15:03:02.003> (201)L>
15:03:02.003> (173)L>
15:03:02.128> (124)@220:<123 F>
15:03:02.128> cw 8
15:03:02.378> TC wvlft [104]F>
15:03:02.378> cw 25
15:03:02.627> IZ wf-3 [105]F>
15:03:02.627> cw 28
15:03:02.877> IZ wf-3 [118]F>
15:03:02.877> cw 24
15:03:03.127> TC wvlft [140]F>
15:03:03.127> cw 40
15:03:03.439> TF wvrt [172]@215:<185 L>
15:03:03.501> (196)L>
15:03:03.501> (203)L>
15:03:03.563> (204)L>
15:03:03.626> (207)L>
15:03:03.626> (209)L>
15:03:03.688> (188)L>
15:03:03.751> (161)L>
15:03:03.813> (145)@207:<81 F>
15:03:03.875> cw 11
15:03:04.063> TC wvlft [48]F>
15:03:04.125> cw 53
15:03:04.312> TF wvrt [37]F>
15:03:04.375> cw 14
15:03:04.562> TC wvlft [31]F>
15:03:04.624> cw 51
15:03:04.811> TF wvrt [28]F>
15:03:04.874> cw 18
15:03:05.061> TC wvlft [52]F>
15:03:05.123> cw 48
15:03:05.373> TF wvrt [48]F>
15:03:05.373> cw 22
15:03:05.610> TC wvlft [60]F>
15:03:05.610> cw 46
15:03:05.859> TF wvrt [52]F>
15:03:05.859> cw 44
15:03:06.109> TF wf-2 [69]F>
15:03:06.171> cw 43
15:03:06.358> TF wf-2 [77]F>
15:03:06.421> cw 41
15:03:06.608> TF wf-2 [86]F>
15:03:06.670> cw 39
15:03:06.858> TF wf-2 [98]F>
15:03:06.920> cw 37
15:03:07.107> TF wf-2 [117]F>
15:03:07.170> cw 37
15:03:07.482> TF wvrt [163]@193:<182 L>
15:03:07.482> (191)L>
15:03:07.544> (192)L>
15:03:07.544> (203)L>
15:03:07.606> (213)L>
15:03:07.671> (207)L>
15:03:07.671> (208)L>
15:03:07.736> (201)L>
15:03:07.799> (185)L>
15:03:07.861> (151)@184:<167 L>
15:03:07.923> (171)L>
15:03:07.986> (177)L>
15:03:07.986> (182)L>
15:03:08.048> (184)L>
15:03:08.048> (171)L>
15:03:08.173> (155)@178:<114 F>
15:03:08.235> cw 13
15:03:08.423> TC wvlft [59]F>
15:03:08.485> cw 23
15:03:08.672> TC wf-1 [36]F>
15:03:08.735> cw 22
15:03:08.922> TC wvlft [44]F>
15:03:08.984> cw 62
15:03:09.171> TF wvrt [28]F>
15:03:09.234> cw 79
15:03:09.483> TF wvrt [44]F>
15:03:09.483> cw 22
15:03:09.733> TC wvlft [60]F>
15:03:09.733> cw 56
15:03:09.985> TF wvrt [60]F>
15:03:09.985> cw 24
15:03:10.238> TC wvlft [65]F>
15:03:10.238> cw 53
15:03:10.488> TF wvrt [66]F>
15:03:10.488> cw 52
15:03:10.727> TF wf-2 [69]F>
15:03:10.789> cw 28
15:03:10.977> IZ wf-3 [74]F>
15:03:11.039> cw 30
15:03:11.226> IZ wf-3 [81]F>
15:03:11.289> cw 31
15:03:11.476> TF wvrt [92]F>
15:03:11.538> cw 32
15:03:11.725> TF wvrt [104]F>
15:03:11.788> cw 32
15:03:11.975> TF wvrt [116]F>
15:03:12.037> cw 31
15:03:12.225> TF wf-2 [130]F>
15:03:12.287> cw 30
15:03:12.537> IZ wf-3 [172]@161:<194 L>
15:03:12.599> (212)L>
15:03:12.661> (225)L>
15:03:12.661> (241)L>
15:03:12.724> (257)L>
15:03:12.786> (285)L>
15:03:12.786> (310)L>
15:03:12.849> (319)L>
15:03:12.849> (331)L>
15:03:12.911> (336)L>
15:03:12.973> (327)L>
15:03:12.973> (305)L>
15:03:13.036> (258)L>
15:03:13.098> (181)L>
15:03:13.223> (141)@147:<128 F>
15:03:13.285> cw 272
15:03:13.473> TF wvrt [105]F>
15:03:13.535> cw 20
15:03:13.722> TC wvlft [112]F>
15:03:13.722> cw 16
15:03:13.972> TC wvlft [119]F>
15:03:14.034> cw 14
15:03:14.221> TC wvlft [114]F>
15:03:14.284> cw 14
15:03:14.471> TC wvlft [77]F>
15:03:14.533> cw 13
15:03:14.733> TC wvlft [61]F>
15:03:14.733> cw 13
15:03:14.983> TC wvlft [73]F>
15:03:15.045> cw 13
15:03:15.232> TC wvlft [66]F>
15:03:15.232> cw 13
15:03:15.482> TC wvlft [61]F>
15:03:15.482> cw 14
15:03:15.732> TC wf-1 [61]F>
15:03:15.794> cw 14
15:03:15.981> TC wvlft [66]F>
15:03:15.981> cw 13
15:03:16.231> TC wvlft [85]F>
15:03:16.293> cw 13
15:03:16.480> TC wvlft [68]F>
15:03:16.543> cw 12
15:03:16.730> TC wvlft [4]F>
15:03:16.730> cw 40
15:03:16.980> TF wvrt [4]F>
15:03:17.042> cw 42
15:03:17.229> TF wvrt [4]F>
15:03:17.294> cw 44
15:03:17.481> TF wvrt [4]F>
15:03:17.544> cw 43
15:03:17.731> TF wf-2 [4]F>
15:03:17.793> cw 51
15:03:17.980> TF wvrt [4]F>
15:03:18.043> cw 51
15:03:18.230> TF wvrt [4]F>
15:03:18.292> cw 55
15:03:18.480> TF wvrt [4]F>
15:03:18.542> cw 53
15:03:18.729> TF wf-2 [4]F>
15:03:18.792> cw 67
15:03:19.041> TF wvrt [4]F>
15:03:19.041> cw 67
15:03:19.291> TF wvrt [4]F>
15:03:19.291> cw 66
15:03:19.540> TF wf-2 [5]F>
15:03:19.540> cw 66

Number Ninja – Fast and Flexible Binary to Ascii Conversion With Sharp Edges!

Double Dabble is way too gentle a tag for this bad boy. On my 4mhz processor this converts binary numbers to ascii strings at well over 1000 digits per second. It can handle numbers of arbitrary length up to buffer limits, and it punishes inattention with memory overruns – what more could you ask for!

Unlike the traditional itoa(int, char *) which takes an integer and a pointer to a buffer as parameters and returns a pointer to the ascii string, numberNinja takes three parameters: a POINTER to the first byte of the binary number which can be any length, a pointer to the TAIL of the buffer – not the 1st position, and the number of bytes in the binary number. The buffer, of course, has to be long enough for the result: e.g. 10 digits plus a terminating 0 for a long int.

The code is based on double dabble with some refinements: most notably the idea of storing the binary number in memory rather than a register and the use of an “add 120″ hack in the pre-correction loop. The general form of “double dabble” is to shift the bits from the top of the binary number into the bottom of the BCD number adjusting during each shift not to let the BCD digits get over 10. The previous versions used a pre-correction loop which are simplified dramatically with the 120 hack.

adding120

In the BCD digit sequence area, this code stores *one* BCD digit in
the low-order 4 bits of each byte. The high-order 4 bits are all
zeros. The rule for pre-correction is: if the BCD digit has the
value 4 or less, do *no* pre-correction at all! If the BCD digit has
the value 5 or more, add 3 for the pre-correction.

Note that if *no* pre-correction is done… the BCD digit has the
form “0xxx”; the high-order bit of the BCD digit will always be zero
if *no* precorrection is done. If a pre-correction *is* done, the
BCD digit will have the form “1xxx”. The BCD digit must have the
value 5 or greater to receive a pre-correction. 5 + 3 is 8, so the
smallest pre-corrected value for a BCD digit will be “1000” or 8.

RULE: BCD digits *not* needing correction will *always* have a “0”
high-order fourth bit; BCD digits that *are* corrected will *always*
have a “1” as the high-order fourth bit.

So if a pre-correction is done, the entire *byte* (containing the BCD
digit in the lower four bits)… will have the form “00001xxx”, where
“xxx” is some combination of three bits. The idea is to change the
pre-correction so the form of the byte will be “10000xxx”… or to
“move” that 1 bit at the high end of the BCD digit… to the high-
order bit in the byte. The “xxx” in the bottom will remain the
same. This is done *only* to BCD digits that are being pre-
corrected. The following chart shows how this works:

The above figure shows adding an additional 120 *after* the 3 pre-
correction has been done *and* the 5 has already been added back.
All these additions can be done at once by “adi 128″. Adding this
“block of ones” forces the 1 in the high-order bit of the BCD
digit… to become the high-order bit of the entire *byte*, so it
will shift into the carry (DF register) in the BCD digit shift loop.

Now the BCD digit shift loop can just do the left shift only and
*not* worry about the high-order 4 bits. BCD digits that were *not*
pre-corrected (4 or less in value) will have the form “00000xxx”
where “xxx” is 4 or less. The pre-corrected BCD digits will now have
the form “10000xxx”… and when this is shifted left, the high-order
1 goes into carry (DF register) and the high-order 4 bits will be
zero. So we are covered in both cases.

Number Ninja is, of course, coded in 1802 assembly but to be useful without being totally scary, it can be wrapped as for example:

char * numberninja(void * binary, char * buffertail, int sizeofbinarynumber);
char * itoa(int s, char *buffer){ //convert an integer to printable ascii in a buffer supplied by the caller
char * bptr;
if (s&lt;0){
s=-s;
bptr=numberninja(&amp;s,buffer+6,2)-1;
*bptr='-';
} else{
bptr=numberninja(&amp;s,buffer+6,2);
}
return bptr;
}
_numberninja:
;fierce and flexible - be careful with the inputs!
;binay-ascii conversion originally based the double-dabble algorithm
;handles variable length input and output
;thanks to Charles Richmond for the suggestion and code
;15-05-02 initial version
;POINTER to integer is passed in r12
;pointer to LAST BYTE of buffer is passed in r13
;number of bytes to convert is passed as the 3rd parameter
;a pointer to the 1st non-zero byte in the buffer is passed back in r15
;r8-11 are used as temps
;r13 is the working pointer to the BCD number
;r12 is the pointer to the input
;r15.0 is bit count in the current byte and the return value register
;r11.0 is the number of bytes to convert
;r10.0 is the count of digits as the bcd number is developed
;r9.0 is digit count for the current pass
ld2 r11,'O',sp,(2+4); pick up the number of bytes to convert

ldi 0	;source a 0
str r13	;terminate the buffer
dec r13	;back to units position
str r13	;initialize bcd number to 0
ldi 1	; initialize the bcd digit count at 1
plo r10 ;

$$nxtbyte:
ldi 8		;bits in byte
plo r15
$$nxtbit:
ldn r12		;pick up current byte
shl		;check top bit
bdf $$bitloop	;move on for a 1 bit
str r12		;put the byte back

dec r15		;decrement and test bits in byte
glo r15
bnz $$nxtbit

dec r11		;decrement and test bytes in number
glo 11
bz $$done	;the whole number was 0! no need for further processing

inc r12		;point to the next byte
br $$nxtbyte	;go back for it
$$bitloop:
glo r10	;digit count
plo r9
$$dcklp:
ldn r13 	;pick up a bcd digit
smi 5	;see if it's greater than 4
bnf $$dnoadd ;if not, bypass add
adi 128	;add the 5 black and 3 more and 120 per charles
str r13	;put it back
$$dnoadd:
inc r13
dec r9	;decrement digit count
glo r9
bnz $$dcklp ;and back for next digit

ldn r12 ;shift the current byte of the input number
shl
str r12

glo r10	;load the digit count again
plo r9
;r13 is now just past the units location and ready to walk back
$$dshlp:
dec r13	;walk back from 0's position
ldn r13	;get the digit back
shlc	;continue the shift
str r13	;put the digit back
dec r9	;decrement the digit count
glo r9
bnz $$dshlp ;back for more if needed
;we are now out of digit positions but if DF is 1 we need another digit
bnf $$nextbit	;no need to increase digits
inc r10	;increase BCD digit count
dec r13	;back up pointer to new digit position
ldi 1	;source a 1
str r13	;initialize the position

$$nextbit:
dec r15		;see if bits left in byte
glo r15
bnz $$bitloop

dec r11		;see if bytes left in number
glo r11
bz $$done
inc r12		;point to next byte of number
ldi 8		;prep for 8 more bits
plo r15
br $$bitloop

$$done: ;this is also used for for an early exit.  R13 and R10 must be correct
cpy2 r15,r13	;save the starting location of the digits to return
glo r10		;digit count again
plo r9
$$upnxt:
ldn r13		;get digit
ori 0x30	;make ascii
str r13		;put it back
inc r13		;next digit
dec r9		;counter
glo r9
bnz $$upnxt	;upgrade all spots
cretn		;return with pointer to 1st digit in r15

I did a little test of printing ip addresses which is what got me started on this. Using the number ninja, printf takes about 15ms to print an ip address as opposed to about 50 using itoa(). It will be a little bit of work to revise the compiler version of printf but that really needs an overhaul anyway: I did it a while ago and my understanding of c strings and pointers has improved a lot in the meantime.

Carbot Video Tour and Wireless Programming

Today I set up the Olduino Carbot for wireless programming over the XBee link. Because I had already hooked up the XBee to power, ground, and transmit to get the telemetry out, it was just a matter of one more connection to receive data from avrdude on the windows computer. I still have to press reset on the olduino to activate the bootloader but it beats the heck out of shutting everything down to re-program the 1802.
15-04-29 Carbot Wireless
The first video is a walk-around of Carbot showing its various sensors and drivers.

The second video is a demonstration of programming the Olduino wirelessly over the XBee serial link.

The last video is the current state of Carbot on the floor. Slow-ish but no wild turns.

For my records, here’s the code at close of day today.

//olduino carbot simple version Jan 24, 2015
//15-04-26 using <> version of cpu1802spd4port7.h
//15-04-29 cruising speed increased, left turn now 1 ms increments
#include <olduino.h>
#include <nstdlib.h> //supports c d s x l cx
#include <cpu1802spd4port7.h>
#include "softpwm.h"
#include "carbot.h"
#include "pingBN.h"
unsigned int wdist,oldwdist, fprox; // distance to wall on right current & previous, proximity of barrier in front
#define maxfprox 160 //max front barrier proximity
#define maxwdist 30 //min right barrier dist
#define minwdist 25 //min right barrier dist
#define basespeed 255	//was 192
#define lowspeed 64	//was 32
unsigned int sharpy(){  //reads the Sharp GP2D12 IR rangefinder via the MCP3002 ADC, returns raw ADC value
	asm("	dec	sp\n" //make a work area
		"	sex 3\n"	//set x to pc
		"	out 6\n	db 0x68\n"	//send 68 to read channel 0
		"	sex sp\n	inp 6\n" //pick up top bits
		"	ani 3\n	phi 15\n"	//put bits 8&9 of result into return register

		"	sex 3\n"	//set x to pc
		"	out 6\n	db 0xAA\n"	//send pattern to read low bits
		"	sex sp\n	inp 6\n	plo 15\n" //pick up low bits to return register
		"	inc	sp\n"	//restore stack pointer
		"	cretn\n");	//actual return
	return 0;	//dummy return for compiler
}
void killdrive(){//stops both motors
	digitalWrite(pwmb,LOW);//kill power right
	digitalWrite(pwma,LOW);//kill power left
}
void hardleft(){//5 ms turn with left wheel running backwards
	printf("L>\n");
	digitalWrite(pwmb,HIGH);//full power right
	digitalWrite(pwma,HIGH);//full power left
	digitalWrite(bin1,HIGH); digitalWrite(bin2,LOW);
	digitalWrite(ain1,LOW); digitalWrite(ain2,HIGH); //reverse left wheel
	delay(1); //nominal delay only
}

void tooclose(){ //if we are closer than minwdist to the wall on our right side
    if (wdist>oldwdist) {//but going the right way
		analogWrite(pwma,basespeed);analogWrite(pwmb,basespeed); //proceed
		printf(" wf-1 ");
    }
    else{
		analogWrite(pwma,lowspeed);analogWrite(pwmb,basespeed); //bear left
		printf(" wvlft ");
    }
}
void toofar(){//if we are further than minwdist from the wall on our right side
    if (wdist<oldwdist) {//but going the right way
		analogWrite(pwma,basespeed);analogWrite(pwmb,basespeed); //proceed
		printf(" wf-2 ");
    }
    else{
		analogWrite(pwma,basespeed);analogWrite(pwmb,lowspeed); //bear right
		printf(" wvrt ");
    }
}

void inthezone(){ //if we are between minwdist and maxwdist
	analogWrite(pwma,basespeed);analogWrite(pwmb,basespeed); //proceed
	printf(" wf-3 ");
}

void cruiseAlongWall(){
	if (0!=wdist) oldwdist=wdist; //track the wall distance after the first time
	wdist=pingQ()/2; //get the wall distance in cm
	printf("cw %d\n",wdist);
	if (wdist<minwdist){
		tooclose();
	}else if (wdist>maxwdist){
		toofar();
	}else{
		inthezone();
	}
	//the routines above will have set the motor power levels, now we activate them
	digitalWrite(bin1,HIGH); digitalWrite(bin2,LOW);
	digitalWrite(ain1,HIGH); digitalWrite(ain2,LOW);
	pwmcycleN(15);//forward a bit
}

void main(){
	unsigned int ttl=300; //time to live limit,
	printf("Simpler Panopticon Carbot(L=1,B=255,S=64,min/max=25/30) O-carbotS1.c\n");
	PIN4=0x80;out(4,0x80);
	while(ttl!=00){
		printf("@%d:",ttl);
		digitalWrite(7,LOW);fprox=sharpy(); digitalWrite(7,HIGH);
		printf("<%d ",fprox);
		if (fprox<maxfprox){ // more than 1 ft from barrier
			killdrive();//kill any turn that was going on
			printf("F>\n");
			cruiseAlongWall();//set a course to track the wall
		}else{
			hardleft();
		}
		ttl--; //reduce time to live
	}
	PIN4=0;out(4,0); //kill it all
}
#include <olduino.c>
#include <nstdlib.c>
#include "softpwm.c"
#include "pingBN.c"
//softpwm.c routines for olduino software pulse width management.
//Oct 12 - pwmstep included in pwmcycleN
void analogWrite(unsigned int bit, unsigned int val){
	//printf("analogWrite(%d,%d)\n",bit,val);
	if (bit<2){
		dct[bit]=val>>5; //step down range to 0..7
		//printf("dct[%d]=%cx\n",bit,dct[bit]);
	}
}
void pwmcycleN(unsigned int howmany){ //run howmany*8 steps of the software pwm
	unsigned int step; //toggle table index
	unsigned char * togglep=toggle; //maybe togglep will be in a register
	while(howmany-->0){
		togglep[dct[0]]|=1; //set the end of the duty cycle for bit 0
		togglep[dct[1]]|=2; //set the end of the duty cycle for bit 1
		togglep[0]^=~PIN4; //set up with current state of port4 low bits
		togglep[0]&=0x03; //only the pwm bits are set
		for(step=0;step<8;step++){
			PIN4^=togglep[step];	//toggles the bit off if the duty cycle is over
			out(4,PIN4);
			togglep[step]=0;
		}
	}
}

Wednesday Double Dabbling

I had a little epiphany based on a friend’s work on the double dabble code.

He had improved the baseline code a lot with a bunch of tricks but I had lost lock on how the code worked. One major thing he had done was to make the number of passes of the inner loop variable, increasing as the number of bcd digits developed. Looking at my own code, it occurred to me that there was a fairly easy way for me to do the same thing. A conceptually minor change to my code gave me the variable length inner loop and obviated the need to initialize the buffer. It’s not quite as quick as his code but I understand it and i may be able to apply some of his other innovations. I’m quite pleased. For the record, it takes about 900 instructions to convert 0x3039 to “12345” vs 2200 for my current itoa and 750 for the best previous effort. Also, as a result of understanding the code better i can probably dispense with the C wrapper code.

_dubdab16v:
;experimental binay-ascii conversion using the double-dabble algorithm for 16 bits
;thanks to Charles Richmond for the suggestion and code
;15-04-29 upgraded to variable digit processing
;interger is passed in r12
;buffer pointer is passed in r13
;a pointer to the 1st non-zero byte in the buffer is passed back in r15
;r8-11 are used as temps
;r8 is the working pointer
;r15.0 is bit count(16) and the return value register
;r10.0 is the count of digits as the bcd number is developed
;r9.0 is digit count for the current pass
alu2i r8,r13,6,adi,adci	;point to the terminator position
ldi 0	;source a 0
str r8	;terminate the buffer
dec r8	;back to units position
str r8	;initialize bcd number to 0
ldi 1	;vdc initialize the bcd digit count at 1
plo r10 ;vdc
;at this point the bcd return buffer has been initialized and the digit count is 1
ldi 16	;bit count
plo r15
;now i'm going to spin off any leading 0's in the binary number
$$cktop:
ghi r12		;get the top bit of the number
shl		;check for a 1
bdf $$bitloop	;move on if we have one
shl2 r12	;shift the input number
dec r15		;reduce the number of times to shift
glo r15
bnz $$cktop	;
inc r15		;our whole number was 0 but force at least one pass
$$bitloop:
glo r10	;digit count vdc
plo r9
$$dcklp:
ldn r8 	;pick up a digit
smi 5	;see if it's greater than 4
bnf $$dnoadd ;if not, bypass add
adi 0x08	;add the 5 black and 3 more
str r8	;put it back
$$dnoadd:
inc r8
dec r9	;decrement digit count
glo r9
bnz $$dcklp ;and back for next digit

shl2 r12 ;shift the input number

glo r10	;load the digit count again vdc
plo r9
;r8 is now just past the units location and ready to walk back
$$dshlp:
dec r8	;walk back from 0's position
ldn r8	;get the digit back
shlc	;continue the shift
phi r15 ;save it for the carry test
ani 0x0f ;clear the 10 bit
str r8	;put the digit back
ghi r15	;now test for carry
smi 0x10 ; this will make df 1 if the 10 bit is set
dec r9	;decrement the digit count
glo r9
bnz $$dshlp ;back for more if needed
;we are now out of digit positions but if DF is 1 we need another digit vdc
bnf $$nextbit	;no need to increase digits vdc
inc r10	;increase BCD digit count vdc
dec r8	;back up pointer to new digit position vdc
ldi 1	;source a 1 vdc
str r8	;initialize the position vdc

$$nextbit:
dec r15
glo r15
bnz $$bitloop

cpy2 r15,r8	;save the starting location of the digits to return
glo r10		;digit count again vdc
plo r9
$$upnxt:
ldn r8		;get digit
ori 0x30	;make ascii
str r8		;put it back
inc r8		;next digit
dec r9		;counter
glo r9
bnz $$upnxt	;upgrade all spots

cretn		;return with pointer to 1st digit in r15

Carbot Telemetry Results

I ran the Carbot with the XBee reporting the sensor results this morning.

In the video you can see it make one good turn and then one wild turn. The front sensor initiates a turn if the proximity reading gets above 160 and keeps turning until it drops below that. The good and bad turn data are shown below. The @nnn is a time-to-live counter, allowing carbot to run 500 cycles, the are the proximity readings and the resulting direction either Forward or Left.

9:52:58.712> @276<148 F>
9:52:58.774> @275<168 L>
9:52:58.961> @274:<@273:<197 L>
9:52:59.086> @272:<201 L>
9:52:59.149> @271:<176 L>
9:52:59.273> @270:126 F>
9:53:01.145> @253:<159 F>
9:53:01.270> @252:<184 L>
9:53:01.333> @251:<201 L>
9:53:01.457> @250:<228 L>
9:53:01.520> @249:<260 L>
9:53:01.645> @248:<258 L>
9:53:01.707> @247:<217 L>
9:53:01.832> <115 F>
9:53:24.520> @39:<174 L>
9:53:24.644> @38:<186 L>
9:53:24.707> @37:<201 L>
9:53:24.769> @36:<383 L>
9:53:24.894> @35:<193 L>
9:53:24.956> @34:<102 F>

I note that the good turn got five Left turn cycles while the bad turn got six. Other good turns in the data show four cycles. I could also tell that the car had been veering left before the bad turn and veering right before the good one(veering left or right is what the car does to keep its distance from the wall). In the last turn shown in the video – which is a bit too sharp, there are five cycles of left turn and it was veering left before the turn.

I measured the distance from the wall when the front facing infrared sensor was reading 160 and it was right at the 18 inches I was aiming for. Somewhat to my surprise though, when the car was at an angle to the wall it read 160 at a slightly greater distance – I had actually figured it the proximity reading would drop off when the sensor was at an angle but even at quite a sharp oblique(22 degrees) it would read 160 at a distance of 20 inches. The ultrasonic by comparison drops off quickly at anything less than 45 degrees.

You would think that four-six cycles would be granular enough for control but one easy change would be to reduce the length of the turn cycles.

UPDATE: I tried a 30ms turn cycle which generally seemed more successful although my telemetry wasn’t working and the motor batteries ran out. I’ll think about what i can do to improve forward speed and try again tomorrow once the batteries are recharged.

Adding Wireless Telemetry to Carbot With XBee’s

15-04-26 xbeeWhen I last tried the Carbot it could struggle around the arena but didn’t track the wall very well. I needed to see what its sensors saw to figure out if the issue was sensors or logic so i wanted some telemetry.

The original carbot used XBees as a serial bridge so that’s what I’m trying. These are set up at 19,200 baud with one hooked up to the olduino and one to the PC through a serial cable. On the Olduino it’s actually hooked to the output of the AVR’s uart; The olduino just does “out(x,7)” which loads the spi subsystem and drives the avr to clock it in and send it out on its uart.

The uart had been running at 57,600 which is too fast for the XBees so i changed the code in run1802() to switch the serial speed down to 19,200 when it puts the 1802 into “run” mode. Then I had to do something to slow down the olduino so it wouldn’t overrun the avr’s buffer. The simplest thing was just an extra level of call/return which adds enough instructions to get us in the range. When I tried it it worked pretty much right away firing in the existing telemetry from the carbot. The XBees are not the new hotness but they’re very simple to hook up and reliable to use.
I’ll charge up the batteries and try it on the floor.

  Serial.flush();
  delay(2);
  Serial.end();
  Serial.begin(19200);
  cout<<"\nrun1802.5("<<timeoutinterval<<") at 19,200 BPS\n";
#define putc(x) outd(7,x)
void outd(unsigned int port,unsigned char what){
	out(port,what);
}

Update: Looking at the data sheet, i may be able to run these at 57600 baud which would obviate the code changes and i could probably the program the olduino wirelessly which i used to do with the original carbot.

Because someday you’ll need that fantastic collection of stamps

15-04-20 stuff
I have a minor problem with the w5500-based servers. Sometimes you get a session established but when you check how many bytes of data have been sent the answer is zero. I used to just drop those sessions with the w5200 but now it seems to hang some browsers sometimes. Specifically, safari coming from outside the lan almost always does this and it doesn’t like it if you just ignore or close the session. I get around this by re-reading the data length up to 10X with a 20ms delay between reads. I am not a fan of this because sometimes, with other browsers(I’m looking at you chrome) they cause a session and there never IS any data so the server is tied up for 200ms waiting in vain. Also, it’s icky.

I posted a query on the wiznet forums and they asked for a wireshark capture. So here we have, on the left, a windows PC running wireshark, my laptop serving as power supply and serial console for the olduino, and, behind the olduino, an ancient powered ethernet hub that puts the desktop pc on the same ethernet segment as the olduino so it can see the packets. The cable modem is plugged into the hub as are the desktop and olduino. The phone, which sent the transaction, is taking the picture. I needed the hub because most routers act as switches as well. Packets for one port are only visible on that port.

I have no idea at all what the packet trace data shows but there are plenty of retries and such, I’m hoping for wisdom from afar.
15-04-20 packets

Follow

Get every new post delivered to your Inbox.