#!/usr/bin/env python """ piasketch - The Pi-A-Sketch program draws a few circles It is also a basic library for steppers controlling an X/Y device. The main functions are line(x,y) and circle(radius). """ import RPi.GPIO as gpio import time PINS_A = [4, 25, 24, 23] PINS_B = [22, 21, 18, 17] PINS = PINS_A + PINS_B SEQA = [(4, ), (4, 25), (25, ), (25, 24), (24, ), (24, 23), (23, ), (23, 4)] RSEQA = SEQA[::-1][1:] + SEQA[::-1][:1] SEQB = [(22, ), (22, 21), (21, ), (21, 18), (18, ), (18, 17), (17, ), (17, 22)] RSEQB = SEQB[::-1][1:] + SEQB[::-1][:1] CORRECTION = 22 def setallpins(): """ Before we can use them, we need to set the pins for both motors """ gpio.setmode(gpio.BCM) for pin in PINS: gpio.setup(pin, gpio.OUT) def stepper(sequence, pins, delay=0.002): """ One full step, based on an ordered sequence and corresponding pins """ for step in sequence: for pin in pins: gpio.output(pin, gpio.HIGH if pin in step else gpio.LOW) time.sleep(delay) def move(steps, axis="x"): """ Move a certain number of steps on a specific axis. sign for direction """ (seq, pins) = (SEQA, PINS_A) if axis == "x" else (SEQB, PINS_B) if steps < 0: steps = -steps seq = RSEQA if axis == "x" else RSEQB for _ in range(steps): stepper(seq, pins) def line(x1, y1): """ Bresenham's line algorithm from rosetta code (ADA/Python), adapted to relative positioning. It will be drawn from current position to x1,y1. Negative number goes in other direction. """ dx = abs(x1) dy = abs(y1) x, y = 0, 0 px, py = x, y sx = -1 if 0 > x1 else 1 sy = -1 if 0 > y1 else 1 if dx > dy: err = dx / 2.0 while x != x1: if x - px is not 0: move(x - px) if y - py is not 0: move(y - py, "y") px, py = x, y err -= dy if err < 0: y += sy err += dx x += sx else: err = dy / 2.0 while y != y1: if x - px is not 0: move(x - px) if y - py is not 0: move(y - py, "y") px, py = x, y err -= dx if err < 0: x += sx err += dy y += sy if x - px is not 0: move(x - px) if y - py is not 0: move(y - py, "y") def _circlepoints(radius): """ mid point circle drawing algorithm - basically Bresenham's. It was converted for use with relative. Cant use the bitmap approach of the original algorithm. Points are sequential clockwise. It works, but it is ugly. I only had a few minutes to write this. It needs to be cleaned up. """ points = [] segment = [] for seg in range(8): x0, y0 = 0, -radius f = 1 - radius ddf_x, ddf_y = 1, -2 * radius x, y = 0, radius segment = [] while x < y: if seg == 0: segment.append((x0 + x, y0 + y)) elif seg == 1: segment.append((x0 + y, y0 + x)) elif seg == 2: segment.append((x0 + y, y0 - x)) elif seg == 3: segment.append((x0 + x, y0 - y)) elif seg == 4: segment.append((x0 - x, y0 - y)) elif seg == 5: segment.append((x0 - y, y0 - x)) elif seg == 6: segment.append((x0 - y, y0 + x)) elif seg == 7: segment.append((x0 - x, y0 + y)) if f >= 0: y -= 1 ddf_y += 2 f += ddf_y x += 1 ddf_x += 2 f += ddf_x if seg % 2: points.extend(segment[::-1]) else: points.extend(segment) return points def circle(radius): """ Draw a circle of specified radius. It is not centered. It will start drawing a circle at its current position, going right and down. """ points = _circlepoints(radius) #print points dx, dy = 1, 1 px, py = 0, 0 counter = 0 total = len(points) quarter = total / 4 #print total, quarter for (x, y) in points: counter += 1 dx, dy = x - px, y - py px, py = x, y # print dx, dy if dx is not 0: move(dx) if dy is not 0: move(dy, "y") if counter == quarter: move(-CORRECTION) elif counter == quarter * 2: move(CORRECTION, "y") elif counter == quarter * 3: move(CORRECTION) elif counter == total: move(-CORRECTION, "y") def main(): """ As a standalone app, draw a few circles """ setallpins() #line(-50, -200) #line(5,0) #circle(350) for radius in range(100,400,25): circle(radius) gpio.cleanup() if __name__ == "__main__": main()