// // pinplotter3.nxc -- by Alfonso Martone // www.alfonsomartone.itb.it // // compiling: requires at least nbc 1.0.1.b34 from http://bricxcc.sf.net // nbc pinplotter3.nxc -o pinplotter3.rxe // #define __PROG__ "pinplotter3" #define __DATE__ "02-aug-2008" // --- configuration ---------------------------------------------------------- #undef ITALIANO // if defined, screen messages are in Italian #define LINESKIP 0 // skip these much columns before plotting #define PBMIMAGE "bitmap.pbm" // PBM (portable bitmap) image file #define XSIZE 116 // X size required for image file #define YSIZE 91 // Y size required for image file #define XOFFSET 1175 // border space to skip on leftmost side #define YOFFSET 975 // border space to skip (nearest margin) #define POFFSET 150 // unneeded space at base of pen range #define XBASE 6400 // where to move the slider when print ends // printable area is about 5100x4000 #define XUNITS 44 // pixel-to-pixel X-spacing ("columns") #define YUNITS 44 // pixel-to-pixel Y-spacing ("rows") #define PUNITS 300 // pen-up (park) to pen-down (hole) space #define XPOWER 60 // power needed to move along X axis #define YPOWER 60 // power needed to move along Y axis #define PPOWER 48 // 65 ? // power needed to move down the needle #define BPOWER 80 // 90 ? // power needed to move up the needle #define XPAUSE 40 // time to wait for X axis stabilization #define YPAUSE 5 // time to wait for Y axis stabilization #define PPAUSE 1 // time to wait after making a hole #define XAXIS OUT_A // motor: tool slider (main tracks) #define YAXIS OUT_B // motor: pen tool slider #define PAXIS OUT_C // motor: needle movement #define XSTOP S1 // touch sensor for parking on X axis #define YSTOP S2 // touch sensor for parking on Y axis #define PSTOP S3 // touch sensor for needle parking #define JUICE 6900 // stop all if less than 6900 mV on battery #define FULL_THROTTLE 100 // maximum motor power #define minipause() Wait(1) // small pause in loops (to save some juice) // --- de gustibus: graphics and other things #define exit(i) Stop(true) #define clock() CurrentTick() #define pause(msec) Wait(msec) #define millivolt() BatteryLevel() #define keepalive() ResetSleepTimer() #define cls() ClearScreen() #define puts(x,y,str) TextOut(x,y,str) #define putn(x,y,n) NumOut(x,y,n) #define pute(x,y,n) TextOut(x,y," "); NumOut(x,y,n) // --- battery check void batterycheck() // return if battery has some power left { int n; if(millivolt()>=JUICE) return; for(n=0; n<3; n++) // power is getting low: keep sounding { PlayFile("! Attention.rso"); pause(500); } for(n=0; n<32000; n++) // keep "flashing" alert until shutoff { if(n&1) { puts(0, LCD_LINE6, " "); } else { #ifdef ITALIANO puts(0, LCD_LINE6, "BATTERIA SCARICA"); #else puts(0, LCD_LINE6, "* battery low! *"); #endif } PlayFile("Woops.rso"); pause(800); } } // --- on-screen safe plotting void plot(int x, int y) { if(x>=100) return; if(y>=64) return; PointOut(x,y); } // --- bitmap processing bool bitmap[]; void fileerror(string msg) { int n; string s1, s2; if(StrLen(msg)<=16) { s1=msg; ArrayInit(s2, 0, 1); // empty string } else { s1=SubStr(msg, 0, 16); s2=SubStr(msg, 16, StrLen(msg)-16); } cls(); #ifdef ITALIANO puts(0, LCD_LINE1, "file immagine:"); puts(0, LCD_LINE2, PBMIMAGE); puts(0, LCD_LINE4, "errore del file:"); puts(0, LCD_LINE5, s1); puts(0, LCD_LINE6, s2); puts(0, LCD_LINE7, "correggilo e"); puts(0, LCD_LINE8, "riavvia tutto!"); #else puts(0, LCD_LINE1, "image file:"); puts(0, LCD_LINE2, PBMIMAGE); puts(0, LCD_LINE4, "file error:"); puts(0, LCD_LINE5, s1); puts(0, LCD_LINE6, s2); puts(0, LCD_LINE7, "correct it and"); puts(0, LCD_LINE8, "restart program!"); #endif for(n=0; n<32000; n++) { PlayFile("! Attention.rso"); pause(700); } exit(1); } int loadbitmap() { int filesize, fp, n, pix, ptr, len, v, ppc=0; string str, tmp, sx, sy, sp; byte buf[]; puts(20, LCD_LINE7, "bitmap:"); ArrayInit(bitmap, 0, XSIZE*YSIZE); ArrayInit(buf, 0, 1); #ifdef ITALIANO if(OpenFileRead(PBMIMAGE, filesize, fp)!=NO_ERR) fileerror("errore di open"); if(ReadLnString(fp, str)!=NO_ERR) fileerror("header invalido"); if(str!="P4") fileerror("non e' un PBM P4"); if(!((ReadLnString(fp, str)==NO_ERR) && (ReadLnString(fp, str)==NO_ERR))) fileerror("header mancante"); #else if(OpenFileRead(PBMIMAGE, filesize, fp)!=NO_ERR) fileerror("cant open file"); if(ReadLnString(fp, str)!=NO_ERR) fileerror("cant read header"); // first two bytes of a Portable Bitmap (PBM) file in binary mode // in 2-colors mode (b/w: 1 bit per pixel) are "P4" and carriage-return if(str!="P4") fileerror("not a P4 b/w PBM"); // skip "#" comment line and get 3rd line (where X and Y resolution is stored) if(!((ReadLnString(fp, str)==NO_ERR) && (ReadLnString(fp, str)==NO_ERR))) fileerror("cant read header"); #endif sp=" "; // has the PBM the defined x/y resolution? sx=NumToStr(XSIZE); sy=NumToStr(YSIZE); tmp=StrCat(sx, sp, sy); if(str!=tmp) { #ifdef ITALIANO str=StrCat(tmp, ": grandezza non valida"); #else str=StrCat(tmp, " is not a valid size"); #endif fileerror(str); } pix=0; ptr=0; len=1; for(n=0; n>7)&1; // get pixel value (0 or 1) ppc=ppc+v; // update "printable" pixels counter bitmap[ptr++]=v; // update pixel array buf[0] <<= 1; pix++; if(pix>=XSIZE) { pix=0; break; } if(!(pix&7)) { n--; break; } } } return ppc; // return the number of "black" pixels (that is: "holes") } // --- stepper motor functions --- void addstep(int mot, int pwr, int offs, int stepsiz, int stepnum) { int target=offs+stepsiz*stepnum; int curpos=abs(MotorRotationCount(mot)); if(curpos==target) return; // do not even turn on if we are "in place" OnRev(mot, pwr); for(;;) // wait loop { curpos=abs(MotorRotationCount(mot)); if(curpos>=target) break; minipause(); } OffEx(mot, RESET_NONE); // brake (this is stepping, not just moving) } void xmove(int stepnum) { addstep(XAXIS, XPOWER, XOFFSET, XUNITS, stepnum); pause(XPAUSE); // small pause for stabilization } void ymove(int stepnum) { addstep(YAXIS, YPOWER, YOFFSET, YUNITS, stepnum); pause(YPAUSE); // small pause for stabilization } // --- parking functions --- void park(int mot, int pwr, int sens) { OnFwd(mot, pwr); // move towards park zone... for(;;) { int i=Sensor(sens); if(i>0) break; // ...until sensor say it's OK... minipause(); } OffEx(mot, RESET_NONE); // ...and brake } void xpark() { park(XAXIS, FULL_THROTTLE, XSTOP); } void ypark() { park(YAXIS, FULL_THROTTLE, YSTOP); } void ppark() { park(PAXIS, PPOWER, PSTOP); } void yback() // "lazy" parking while drawing { OnFwd(YAXIS, FULL_THROTTLE); // move towards park zone... for(;;) { int i=abs(MotorRotationCount(YAXIS)); if(i<(YOFFSET/2)) break; // ...until we are in a safe area minipause(); } CoastEx(YAXIS, RESET_NONE); // no need to brake when in safe area } // --- other moving functions void epilogue() // slider: park outside printable area { OnRev(XAXIS, FULL_THROTTLE); for(;;) { int c=abs(MotorRotationCount(XAXIS)); if(c>=XBASE) break; minipause(); } OffEx(XAXIS, RESET_NONE); PlayFile("! Attention.rso"); } void hole() // make a hole { OnRev(PAXIS, PPOWER); // down the needle for(;;) { int i=abs(MotorRotationCount(PAXIS)); if(i>=PUNITS) break; minipause(); } OnFwd(PAXIS, BPOWER); // recall needle for(;;) { int i=abs(MotorRotationCount(PAXIS)); if(i<=POFFSET) break; if(Sensor(PSTOP)>0) { OffEx(PAXIS, RESET_NONE); // immediate stop if too much speed break; } minipause(); } CoastEx(PAXIS, RESET_NONE); // needle is in safe zone: no need to brake } // --- setup before plotting --- int setup() { int x, y, i, n, ppc; CoastEx(OUT_ABC, RESET_NONE); SetSensorTouch(S1); SetSensorTouch(S2); SetSensorTouch(S3); cls(); puts(12, LCD_LINE2, "alfonsoftware"); puts(13, LCD_LINE3, "internat"); puts(59, LCD_LINE3, "ional"); n=50-(3*StrLen(__PROG__)); puts(n, LCD_LINE5, __PROG__); putn(20, LCD_LINE6, millivolt()); putn(50, LCD_LINE6, FreeMemory()); for(n=0; n<5; n++) { RectOut(n,n, 99-n-n, 63-n-n); n++; } ppc=loadbitmap(); cls(); for(x=0; x=LINESKIP) // loop only if inside printable area { for(y=0; y0) yback(); // need to align if any pixels printed putn(66, LCD_LINE1, v); // now print out lots of statistics... pute(66, LCD_LINE3, c); putn(66, LCD_LINE4, h); pute(66, LCD_LINE5, millivolt()); if(LINESKIP==0) { puts(66, LCD_LINE7, " "); if(h>=ppc) { puts(66, LCD_LINE7, "100%"); } else { q = h*1000/ppc; // 0 to 999 e = q/10; // 0 to 99 s1=NumToStr(q%10); // decimal s2=NumToStr(e); s3=StrCat(s2, ".", s1, "%"); puts(66, LCD_LINE7, s3); } } e=(clock()-t)/100; // time in deciseconds c=e/10; // time in seconds s1=NumToStr(e%10); s2=NumToStr(c); s3=StrCat(s2, ".", s1); puts(66, LCD_LINE6, " "); puts(66, LCD_LINE6, s3); q=(clock()-FirstTick())/1000; // 68231 --> 68sec --> 1:08 e=q/60; // 1 q=q%60; // 8 s1=NumToStr(e); // "1" s2=StrCat(" ", s1); // " 1" s3=SubStr(s2, StrLen(s2)-3, 3); // " 1" adjusted to 3 chars s1=NumToStr(q); // "8" if(q<10) s2=StrCat("0", s1); // add a "0" if needed else s2=s1; // "08" s1=StrCat(s3, ":", s2); // " 1:08" puts(60, LCD_LINE8, s1); batterycheck(); // stop if low on juice keepalive(); // reset "sleep" timers } epilogue(); // move the moving tool out of printing area pause(30000); // statistics remain on screen for 30secs exit(0); } // --- the end ---