// Program to test clock functioning. The clock uses 28BYJ-48 stepper motors // to turn an equilateral triangle "gear" and bring a new digit on a // 3D printed "chain" up by turning 120 degrees. // Try to guess if I'm compiling this on the Raspberry Pi if I haven't // already been told. #ifndef COMPILING_ON_PI #ifdef __arm__ #define COMPILING_ON_PI 1 #else #define COMPILING_ON_PI 0 #endif #endif #if COMPILING_ON_PI==1 #include #else // Dummy up the defs I use just so I can test compile the code on x86 to // check for syntax errors struct gpiod_chip { int dummy; }; struct gpiod_line { int dummy; }; extern int gpiod_line_set_value(struct gpiod_line*,int); extern struct gpiod_chip* gpiod_chip_open_by_name(const char *); extern struct gpiod_line* gpiod_chip_get_line(struct gpiod_chip*,int); extern int gpiod_line_request_output(struct gpiod_line*,const char *,int); extern int gpiod_line_release(struct gpiod_line*); extern int gpiod_chip_close(struct gpiod_chip*); #endif #include #include #include #include #include // The gear train in the 28BYJ-48 has gear ratios of 32/9, 22/11, 26/9, 31/10 // and the motor shaft turns 11.25 degrees on one step (I'm not using // half stepping). So the angle a single step turns is: // I don't think this is right. I drift really quickly away from the expected // angle using these numbers. //#define ONE_STEP_ANGLE (11.25/(((double)(32*22*26*31))/((double)(9*11*9*10)))) // I also drift using this number //#define ONE_STEP_ANGLE (11.25/63.65) // Lets try the claimed 64:1. Dang! This works much better than the others // Possibly even perfectly #define ONE_STEP_ANGLE (11.25/64.0) // I found a web page that claims different manufacturers use slightly different // gear ratios, and I guess that's true because the motors I have act as if // they are exactly 64:1 // The gpio pins I connect to the driver board #define BLUE_GPIO 22 #define PINK_GPIO 23 #define YELLOW_GPIO 24 #define ORANGE_GPIO 25 #define GPIO_DELAY 1800 struct gpiod_chip *chip; struct gpiod_line *lineBlue; struct gpiod_line *linePink; struct gpiod_line *lineYellow; struct gpiod_line *lineOrange; // Colors of the four wires in the stepper socket const char * names[4] = {"blue", "pink", "yellow", "orange"}; int step_state = 0; int step_dir = 1; int prompt_visible = 0; // Wait for a commnd on stdin for up to "msec" microseconds. If msec // is 0, wait forever. Return 1 if input is available, 0 if // timed out. // int cmd_available(long msec) { struct timeval tmout; struct timeval * tmoutp; int fd, rval; fd_set infds; if (msec == 0) { tmoutp = (struct timeval *)0; } else { tmout.tv_usec = msec % 1000000; tmout.tv_sec = msec / 1000000; tmoutp = &tmout; } fd = fileno(stdin); FD_ZERO(&infds); FD_SET(fd, &infds); if (! prompt_visible) { printf("cmd> "); fflush(stdout); prompt_visible=1; } rval = select(fd+1, &infds, NULL, NULL, tmoutp); return (rval == 1); } void read_command(char * buf, int buflen) { prompt_visible=0; size_t saw = read(fileno(stdin),buf,buflen-1); if (saw <= 0) { buf[0] = 'q'; buf[1] = '\0'; } else { if (buf[saw-1] == '\n') { --saw; } buf[saw] = '\0'; } } void reset_step_state() { step_state = 0; step_dir = 1; gpiod_line_set_value(lineBlue, 0); gpiod_line_set_value(linePink, 0); gpiod_line_set_value(lineYellow, 0); gpiod_line_set_value(lineOrange, 0); } // Turn the stepper motor as close as possible to the given angle. Remember // any fraction it was unable to turn so it can be added in on the next call. // If angle is positive, turn clockwise, if negative, turn counterclockwise. // // NOTE: Using some rational arithmetic package would allow perfection, // but I'll see how it goes with just double floats doing the work. // void turn_through_angle(double deg) { static double last_fraction = 0.0; int next_dir; double dsteps, last_int; long int step_count, i; if (deg < 0) { deg = -deg; next_dir = -1; } else if (deg > 0) { next_dir = 1; } else { return; } dsteps = deg/ONE_STEP_ANGLE; if (next_dir == step_dir) { dsteps += last_fraction; } else { dsteps -= last_fraction; } last_fraction = modf(dsteps, &last_int); step_count = (long int)last_int; fflush(stdout); step_dir = next_dir; for (i = 0; i < step_count; ++i) { step_state += step_dir; if (step_state < 0) { step_state = 3; } else if (step_state > 3) { step_state = 0; } switch(step_state) { case 0: gpiod_line_set_value(lineBlue,1); gpiod_line_set_value(linePink,1); gpiod_line_set_value(lineYellow,0); gpiod_line_set_value(lineOrange,0); break; case 1: gpiod_line_set_value(lineBlue,0); gpiod_line_set_value(linePink,1); gpiod_line_set_value(lineYellow,1); gpiod_line_set_value(lineOrange,0); break; case 2: gpiod_line_set_value(lineBlue,0); gpiod_line_set_value(linePink,0); gpiod_line_set_value(lineYellow,1); gpiod_line_set_value(lineOrange,1); break; case 3: gpiod_line_set_value(lineBlue,1); gpiod_line_set_value(linePink,0); gpiod_line_set_value(lineYellow,0); gpiod_line_set_value(lineOrange,1); break; } usleep(GPIO_DELAY); } } // Turn each digit into place, pause 1/4 second, turn again (till // a new command shows up). // void run_clock() { for ( ; ; ) { turn_through_angle(120.0); if (cmd_available(250000)) { break; } } } int main(int argc, char **argv) { const char *chipname = "gpiochip0"; char cmd[80]; double deg; // Open GPIO chip chip = gpiod_chip_open_by_name(chipname); // Open GPIO lines lineBlue = gpiod_chip_get_line(chip, BLUE_GPIO); linePink = gpiod_chip_get_line(chip, PINK_GPIO); lineYellow = gpiod_chip_get_line(chip, YELLOW_GPIO); lineOrange = gpiod_chip_get_line(chip, ORANGE_GPIO); // Open lines for output gpiod_line_request_output(lineBlue, "stepcheck", 0); gpiod_line_request_output(linePink, "stepcheck", 0); gpiod_line_request_output(lineYellow, "stepcheck", 0); gpiod_line_request_output(lineOrange, "stepcheck", 0); for ( ; ; ) { cmd_available(0); read_command(cmd,sizeof(cmd)); if (cmd[0]=='r' || cmd[0] == 'R') { reset_step_state(); run_clock(); } else if (cmd[0]=='q' || cmd[0]=='Q') { printf("Bye!\n"); break; } else if (sscanf(cmd,"%Lf",°,NULL) == 1) { printf("Turning %Lf degrees.\n",deg); fflush(stdout); turn_through_angle(deg); } else { printf("q - quit\n\ r - run clock till new command enered.\n\ degrees - turn stepper this many degrees (if negative turn in reverse)\n\ otherwise print this help.\n"); } } gpiod_line_release(lineBlue); gpiod_line_release(linePink); gpiod_line_release(lineYellow); gpiod_line_release(lineOrange); gpiod_chip_close(chip); return 0; }