commit
					5e72f0a55d
				
				 16 changed files with 1099552 additions and 0 deletions
			
			
		| @ -0,0 +1,39 @@ | |||||||
|  | To make the pixel, 3D print all 7 parts. | ||||||
|  | 
 | ||||||
|  | The pixel insert (screw) should be in one colour. The case and guide should | ||||||
|  | be in a contrasting colour. The small parts can be in any colour, and are | ||||||
|  | only visible from the back. | ||||||
|  | 
 | ||||||
|  | Print one pixel before trying to scale up. Some of these objects are difficult | ||||||
|  | to print and will require a well set up 3D printer. | ||||||
|  | 
 | ||||||
|  | I printed the large parts on an Original Prusa i3 MK2S and the small parts | ||||||
|  | on a Creality CR-10. The printing time was of the order of two weeks plus | ||||||
|  | failures and spares. | ||||||
|  | 
 | ||||||
|  | There is some redesigning that would be helpful before scaling up. | ||||||
|  | 
 | ||||||
|  | The pixel also requires 2 nails and a Tower Pro SG90 servo motor. | ||||||
|  | 
 | ||||||
|  | Driving the system requires a Pololu Mini Maestro 24-channel USB Servo | ||||||
|  | Controller for each 24 servos. | ||||||
|  | 
 | ||||||
|  | https://www.pololu.com/product/1356 | ||||||
|  | 
 | ||||||
|  | Also required is a suitable 5 volt power supply. I used a Mean Well RS-150-5 | ||||||
|  | for each servo controller. The worst case current of the servo is around an | ||||||
|  | amp, and while the average current will never reach anywhere near that, I sized | ||||||
|  | the power supplies accordingly so that they should never shut down due to | ||||||
|  | transients. The PCB trace on the servo controller is unlikely to be able to | ||||||
|  | handle that much current for long. | ||||||
|  | 
 | ||||||
|  | Note that the pixel grid will need external mechanical support. I used parts | ||||||
|  | from a couple of monitor stands, but any sturdy vertical pole on either side | ||||||
|  | should suffice. Black cable ties will then hold it steady. | ||||||
|  | 
 | ||||||
|  | Assembly should be done carefully. Most parts are push-fit, but that does | ||||||
|  | rely on 3D printing tolerances. You may need to slightly adapt some of the | ||||||
|  | sizes in the source files to your printer, or be careful about how much | ||||||
|  | you file or cut away the rim of the first layer. | ||||||
|  | 
 | ||||||
|  | Prototyping with a single pixel is essential. | ||||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						| @ -0,0 +1,361 @@ | |||||||
|  | include <MCAD/screw.scad> | ||||||
|  | 
 | ||||||
|  | screw_length = 12; | ||||||
|  | shaft1_length = 5; | ||||||
|  | shaft2_length = 5; | ||||||
|  | screw_inner_radius = 1; | ||||||
|  | shaft1_radius = 3; | ||||||
|  | shaft2_radius = 2; | ||||||
|  | screw_radius = 20; | ||||||
|  | screw_pitch = 60; | ||||||
|  | fins = 6; | ||||||
|  | taper = 1; | ||||||
|  | groove_fraction = 0.85; | ||||||
|  | groove_width = 2; | ||||||
|  | groove_depth = 0; //0.5; | ||||||
|  | groove_taper_ratio = 0.3; | ||||||
|  | 
 | ||||||
|  | screw_travel_in = 10; | ||||||
|  | screw_travel_out = 10; | ||||||
|  | 
 | ||||||
|  | leadscrew_pitch = 60; | ||||||
|  | leadscrew_length = 30; | ||||||
|  | leadscrew_width = 2.5; | ||||||
|  | leadscrew_inner = 5; | ||||||
|  | leadscrew_outer = 7.5; | ||||||
|  | leadscrew_endcap = 2; | ||||||
|  | leadscrew_spacer = 5; // aka cone | ||||||
|  | leadscrew_starts = 4; | ||||||
|  | leadscrew_start = screw_length+leadscrew_spacer+shaft1_length; | ||||||
|  | 
 | ||||||
|  | leadscrew_width_shrink = 1; | ||||||
|  | leadscrew_inner_shrink = 0.5; | ||||||
|  | leadscrew_outer_shrink = 1; | ||||||
|  | 
 | ||||||
|  | clearance = 1; | ||||||
|  | shaft_clearance = 0.8; | ||||||
|  | body_thickness = 1; | ||||||
|  | 
 | ||||||
|  | // Overlap between the guide and the body | ||||||
|  | guide_depth = 2; | ||||||
|  | 
 | ||||||
|  | //body_length = 26; | ||||||
|  | //body_length = 30; // shaft_length+screw_length; | ||||||
|  | body_length = screw_length + screw_travel_in + shaft1_length + leadscrew_spacer + guide_depth; | ||||||
|  | body_inner_radius = screw_radius + clearance; | ||||||
|  | body_outer_radius = body_inner_radius + body_thickness; | ||||||
|  | 
 | ||||||
|  | pipe_inner_radius = 1; | ||||||
|  | pipe_hole_radius = 4; | ||||||
|  | hole_taper = 1; | ||||||
|  | 
 | ||||||
|  | body_screw_length = screw_pitch/fins; | ||||||
|  | 
 | ||||||
|  | slot_width = 1; | ||||||
|  | slot_start = shaft2_length - 3; | ||||||
|  | 
 | ||||||
|  | insert_height_inner = 4; | ||||||
|  | insert_height_outer = 6; | ||||||
|  | insert_width = 2; | ||||||
|  | insert_clearance = 0.1; | ||||||
|  | insert_block_thickness = 2 + insert_clearance; | ||||||
|  | insert_length = 20; // body_length - guide_depth; | ||||||
|  | 
 | ||||||
|  | guide_lid_depth = 2; | ||||||
|  | guide_radius = 5/2; | ||||||
|  | guide_clearance = 0; // 0.1; | ||||||
|  | guide_extra = 6; | ||||||
|  | guide_middle_radius = 8; | ||||||
|  | guide_notch_width = 4; | ||||||
|  | guide_notch_depth = 2; | ||||||
|  | 
 | ||||||
|  | guide_start_position = body_length - guide_depth; | ||||||
|  | guide_rotation = 360 * (guide_start_position - leadscrew_start) / leadscrew_pitch; | ||||||
|  | 
 | ||||||
|  | support_width = 8; | ||||||
|  | support_height = 4; | ||||||
|  | //support_length = 40; | ||||||
|  | support_radius = 14; | ||||||
|  | support_position = support_radius + support_height/2 + 1; | ||||||
|  | 
 | ||||||
|  | support_top_width = 4; | ||||||
|  | support_top_length = 5; | ||||||
|  | 
 | ||||||
|  | support_clearance = 0; //0.1; | ||||||
|  | 
 | ||||||
|  | driver_inner_radius = guide_radius; | ||||||
|  | driver_outer_radius = driver_inner_radius+guide_extra; | ||||||
|  | driver_slot_clearance = 0.5; | ||||||
|  | driver_slot_length = 25; | ||||||
|  | driver_end_cap = 2; // Later this will be an attachment for a servo horn | ||||||
|  | 
 | ||||||
|  | motor_x = 12; | ||||||
|  | motor_y = 22.5; | ||||||
|  | motor_mount_depth = 5; | ||||||
|  | motor_x_thickness = 2; | ||||||
|  | motor_y_thickness = 2; | ||||||
|  | motor_screw_diameter = 1.5; | ||||||
|  | motor_screw_housing = 3; | ||||||
|  | motor_screw_position = 14; | ||||||
|  | motor_shaft_position = 6 - motor_y/2; | ||||||
|  | 
 | ||||||
|  | motor_to_horn_end = 13; | ||||||
|  | motor_mount_to_horn_end = motor_to_horn_end - motor_mount_depth; | ||||||
|  | 
 | ||||||
|  | support_spare = 1; | ||||||
|  | shaft2_overlap = 2; | ||||||
|  | nails_length = 10; | ||||||
|  | horn_shaft_length = 5; | ||||||
|  | horn_shaft_overlap = 2; | ||||||
|  | horn_thickness = 2.5; | ||||||
|  | 
 | ||||||
|  | support_length = leadscrew_length + shaft2_length - shaft2_overlap + nails_length + horn_shaft_length - horn_shaft_overlap + horn_thickness + motor_mount_to_horn_end + support_spare; | ||||||
|  | 
 | ||||||
|  | delta = 0.1; | ||||||
|  | 
 | ||||||
|  | debug_travel = 0; | ||||||
|  | 
 | ||||||
|  | render_screw = 1; | ||||||
|  | render_pipe_screw = 0; | ||||||
|  | render_guide = 0; | ||||||
|  | render_motor_mount = 0; | ||||||
|  | 
 | ||||||
|  | // These are obsolete | ||||||
|  | render_pipe = 0; | ||||||
|  | render_pipe_alternate = 0; | ||||||
|  | render_driver = 0; | ||||||
|  | 
 | ||||||
|  | translate_guide = render_pipe_screw?(guide_start_position):0; | ||||||
|  | translate_motor_mount = render_guide?(translate_guide+support_length+guide_depth+guide_lid_depth):0; | ||||||
|  | 
 | ||||||
|  | rotate([0, 0, -360*debug_travel/leadscrew_pitch]) | ||||||
|  | translate([0, 0, debug_travel]) | ||||||
|  | if (render_screw) { | ||||||
|  |     // Visible screw | ||||||
|  |     augers(screw_pitch, screw_length, screw_radius, screw_inner_radius, taper, groove_fraction, groove_width, groove_depth, 0, groove_taper_ratio, fins); | ||||||
|  | 
 | ||||||
|  |     // shaft 1 | ||||||
|  |     translate([0, 0, screw_length]) | ||||||
|  |         translate([0, 0, -delta]) | ||||||
|  |             cylinder(h=shaft1_length+2*delta, r=shaft1_radius, $fn=36); | ||||||
|  |      | ||||||
|  |     // shaft 2 | ||||||
|  |     translate([0, 0, screw_length+shaft1_length+leadscrew_spacer+leadscrew_length]) | ||||||
|  |     difference() { | ||||||
|  |         translate([0, 0, -delta]) | ||||||
|  |             cylinder(h=shaft2_length+delta, r=shaft2_radius, $fn=36); | ||||||
|  |         translate([-shaft2_radius*1.5, -slot_width/2, slot_start]) | ||||||
|  |             cube([shaft2_radius*3,slot_width, shaft2_length-slot_start+delta]); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // cone | ||||||
|  |     translate([0, 0, screw_length+shaft1_length]) | ||||||
|  |         cylinder(h=leadscrew_spacer, r1=shaft1_radius, r2=sqrt(leadscrew_outer*leadscrew_outer + leadscrew_width*leadscrew_width), $fn=36); | ||||||
|  |      | ||||||
|  |     // leadscrew | ||||||
|  |     translate([0, 0, leadscrew_start]) | ||||||
|  |     difference() { | ||||||
|  |         leadscrew(leadscrew_pitch, leadscrew_length, leadscrew_outer, leadscrew_width, leadscrew_inner, leadscrew_starts); | ||||||
|  |         translate([0, 0, -leadscrew_pitch]) | ||||||
|  |         intersection() { | ||||||
|  |             leadscrew(leadscrew_pitch, leadscrew_length+2*leadscrew_pitch, leadscrew_outer-leadscrew_outer_shrink, leadscrew_width-leadscrew_width_shrink, leadscrew_inner-leadscrew_inner_shrink, leadscrew_starts); | ||||||
|  |             translate([0, 0, 0]) | ||||||
|  |                 cylinder(h=leadscrew_length-leadscrew_endcap, r=leadscrew_outer*2); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* width is doubled within this module */ | ||||||
|  | module leadscrew(pitch, length, outer, width, inner, n=4) | ||||||
|  | { | ||||||
|  |     augers(pitch, length, outer, width, 1, n=n); | ||||||
|  |     cylinder(h=length, r=inner); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module augers(pitch, length, outside_radius, inner_radius, taper_ratio = 0.25, ,groove_fraction = 0, groove_width = 0, groove1_depth = 0, groove2_depth = 0, groove_taper_ratio = 1, n = 1) | ||||||
|  | { | ||||||
|  |     for (i = [0:n-1]) { | ||||||
|  |         rotate([0, 0, i*360/n]) auger_plus(pitch, length, outside_radius, inner_radius, taper_ratio, groove_fraction, groove_width, groove1_depth, groove2_depth, groove_taper_ratio); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module auger_plus(pitch, length, outside_radius, inner_radius, taper_ratio = 0.25, groove_fraction = 0, groove_width = 0, groove1_depth = 0, groove2_depth = 0, groove_taper_ratio = 1) { | ||||||
|  |     groove_radius = groove_fraction*(outside_radius-inner_radius) + inner_radius; | ||||||
|  |     groove_inner_radius = max(0, groove_radius - groove_width/2); | ||||||
|  |     groove_outer_radius = min(outside_radius, groove_radius + groove_width/2); | ||||||
|  |     groove_taper = (groove_outer_radius - groove_inner_radius) * (1- groove_taper_ratio) / 2; | ||||||
|  |     groove_mid_inner_radius = groove_inner_radius + groove_taper; | ||||||
|  |     groove_mid_outer_radius = groove_outer_radius - groove_taper; | ||||||
|  |     union(){ | ||||||
|  |         helix(pitch, length) | ||||||
|  |         polygon(points=[ | ||||||
|  |             [0,inner_radius], | ||||||
|  |             [groove_inner_radius, inner_radius * (1 + groove_inner_radius * (taper_ratio - 1) / outside_radius)], | ||||||
|  |             [groove_mid_inner_radius, inner_radius * (1 + groove_mid_inner_radius * (taper_ratio - 1) / outside_radius) + groove1_depth], | ||||||
|  |             [groove_mid_outer_radius, inner_radius * (1 + groove_mid_outer_radius * (taper_ratio - 1) / outside_radius) + groove1_depth], | ||||||
|  |             [groove_outer_radius, inner_radius * (1 + groove_outer_radius * (taper_ratio - 1) / outside_radius)], | ||||||
|  |             [outside_radius,(inner_radius * taper_ratio)], | ||||||
|  |             [outside_radius,(inner_radius * -1 * taper_ratio)], | ||||||
|  |             [groove_outer_radius, -inner_radius * (1 + groove_outer_radius * (taper_ratio - 1) / outside_radius)], | ||||||
|  |             [groove_mid_outer_radius, -inner_radius * (1 + groove_mid_outer_radius * (taper_ratio - 1) / outside_radius) - groove2_depth], | ||||||
|  |             [groove_mid_inner_radius, -inner_radius * (1 + groove_mid_inner_radius * (taper_ratio - 1) / outside_radius) - groove2_depth], | ||||||
|  |             [groove_inner_radius, -inner_radius * (1 + groove_inner_radius * (taper_ratio - 1) / outside_radius)], | ||||||
|  |             [0,(-1 * inner_radius)]], | ||||||
|  |                 paths=[[0,1,2,3,4,5,6,7,8,9,10,11]]); | ||||||
|  |         cylinder(h=length, r=inner_radius); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | if (render_pipe) { | ||||||
|  | translate([render_screw?50:0, 0, 0]) | ||||||
|  |     difference() { | ||||||
|  |         cylinder(h=body_length, r=body_outer_radius); | ||||||
|  |         translate([0, 0, body_screw_length]) cylinder(h=body_length - body_screw_length + delta, r=body_inner_radius); | ||||||
|  |         translate([0, 0, -delta]) augers(screw_pitch, body_screw_length+2*delta, screw_radius+clearance, screw_inner_radius+shaft_clearance, hole_taper, groove_fraction, groove_width, 0, -groove_depth, groove_taper_ratio, fins); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | if (render_pipe_alternate) { | ||||||
|  | translate([render_screw?50:0, 0, 0]) | ||||||
|  |     scale([1.1,1.1,1]) | ||||||
|  |     difference() { | ||||||
|  |         cylinder(h=body_length, r=body_outer_radius); | ||||||
|  |         translate([0, 0, body_screw_length]) cylinder(h=body_length - body_screw_length + delta, r=body_inner_radius); | ||||||
|  |         translate([0, 0, -delta]) augers(screw_pitch, body_screw_length+2*delta, screw_radius, screw_inner_radius, hole_taper, groove_fraction, groove_width, 0, -groove_depth, groove_taper_ratio, fins); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module matrix_insert(c = 0) { | ||||||
|  |     translate([0, 0, -c]) | ||||||
|  |     linear_extrude(height=insert_length+2*c) { | ||||||
|  |         polygon(points=[ | ||||||
|  |             [body_outer_radius, insert_height_inner/2+c], | ||||||
|  |             [body_outer_radius+insert_width+2*c, insert_height_outer/2+c], | ||||||
|  |             [body_outer_radius+insert_width+2*c, -insert_height_outer/2-c], | ||||||
|  |             [body_outer_radius, -insert_height_inner/2-c] | ||||||
|  |         ]); | ||||||
|  |     } | ||||||
|  |     translate([body_outer_radius, 0, insert_length/2]) | ||||||
|  |     cube([2*body_thickness, insert_height_inner+2*c, insert_length], center=true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module matrix_block() { | ||||||
|  |     difference() { | ||||||
|  |         translate([-body_inner_radius-insert_width-body_thickness-2*insert_clearance, -insert_height_outer/2-insert_block_thickness]) | ||||||
|  |         cube([body_thickness+insert_width+2*insert_clearance, insert_height_outer+2*insert_block_thickness, insert_length]); | ||||||
|  |         translate([-2*body_outer_radius-insert_width-2*insert_clearance, 0, 0]) | ||||||
|  |             matrix_insert(insert_clearance); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | if (render_pipe_screw) { | ||||||
|  |     difference() { | ||||||
|  |         rotate([0, 0, -180/fins]) | ||||||
|  |             augers(screw_pitch, body_screw_length, screw_radius+clearance, pipe_inner_radius, hole_taper, groove_fraction, groove_width, 0, -groove_depth, groove_taper_ratio, fins); | ||||||
|  |         translate([0, 0, -delta]) | ||||||
|  |             cylinder(h=body_length + 2*delta, r=pipe_hole_radius); | ||||||
|  |         translate([0, 0, -delta]) augers(screw_pitch, body_screw_length+2*delta, screw_radius+clearance, screw_inner_radius+shaft_clearance, hole_taper, groove_fraction, groove_width, 0, -groove_depth, groove_taper_ratio, fins); | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |     difference() { | ||||||
|  |         cylinder(h=body_length, r=body_outer_radius); | ||||||
|  |         translate([0, 0, -delta]) | ||||||
|  |             cylinder(h=body_length + 2*delta, r=body_inner_radius); | ||||||
|  |         translate([0, 0, body_length-guide_notch_depth/2]) | ||||||
|  |             cube([body_outer_radius*3, guide_notch_width+guide_clearance, guide_notch_depth+delta], center=true); | ||||||
|  |         translate([0, 0, body_length-guide_notch_depth/2]) | ||||||
|  |             cube([guide_notch_width+guide_clearance, body_outer_radius*3, guide_notch_depth+delta], center=true); | ||||||
|  |     } | ||||||
|  |     matrix_insert(); | ||||||
|  |     rotate([0, 0, 90]) matrix_insert(); | ||||||
|  |     matrix_block(); | ||||||
|  |     rotate([0, 0, 90]) matrix_block(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | if (render_guide) { | ||||||
|  | translate([0, 0, translate_guide]) { | ||||||
|  |     difference() { | ||||||
|  |         union() { | ||||||
|  |             // Bottom plate | ||||||
|  |             cylinder(h=guide_depth+delta, r=body_inner_radius-guide_clearance); | ||||||
|  |              | ||||||
|  |             // Lugs | ||||||
|  |             intersection() { | ||||||
|  |                 cylinder(h=guide_depth+delta, r=body_outer_radius); | ||||||
|  |                 union() { | ||||||
|  |                     translate([0, 0, guide_notch_depth/2]) | ||||||
|  |                         cube([body_outer_radius*3, guide_notch_width, guide_notch_depth+delta], center=true); | ||||||
|  |                     translate([0, 0, guide_notch_depth/2]) | ||||||
|  |                         cube([guide_notch_width, body_outer_radius*3, guide_notch_depth+delta], center=true); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Top plate | ||||||
|  |             translate([0, 0, guide_depth]) | ||||||
|  |                 cylinder(h=guide_lid_depth, r=body_outer_radius); | ||||||
|  |              | ||||||
|  |             // Screw casing | ||||||
|  |             translate([0, 0, guide_depth + guide_lid_depth - delta]) | ||||||
|  |                 cylinder(h=guide_extra + delta, r=guide_middle_radius); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         // Hole | ||||||
|  |         //translate([0, 0, -delta]) | ||||||
|  |         //    cylinder(h=guide_depth+guide_lid_depth+guide_extra+2*delta, r=guide_radius, $fn=36); | ||||||
|  |         scale([1.1, 1.1, 1]) | ||||||
|  |         translate([0, 0, -leadscrew_pitch]) | ||||||
|  |         rotate([0, 0, -guide_rotation]) | ||||||
|  |         leadscrew(leadscrew_pitch, leadscrew_length+leadscrew_pitch*2, leadscrew_outer, leadscrew_width, leadscrew_inner, leadscrew_starts); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // Support | ||||||
|  |     translate([0, -support_position, guide_depth + guide_lid_depth]) { | ||||||
|  |         translate([0, 0, support_length/2+delta/2]) | ||||||
|  |             cube([support_width, support_height, support_length+delta], center=true); | ||||||
|  |         translate([0, 0, support_length+support_top_length/2-delta/2]) | ||||||
|  |             cube([support_top_width, support_height, support_top_length+delta], center=true); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | if (render_driver) { | ||||||
|  |     difference() { | ||||||
|  |         cylinder(h = driver_slot_length + driver_end_cap, r=driver_outer_radius, $fn=36); | ||||||
|  |         translate([0, 0, driver_end_cap]) | ||||||
|  |             cylinder(h = driver_slot_length + delta, r=driver_inner_radius, $fn=36); | ||||||
|  |     } | ||||||
|  |     cubex = (driver_inner_radius+driver_outer_radius); | ||||||
|  |     cubey = slot_width - driver_slot_clearance; | ||||||
|  |     translate([-cubex/2, -cubey/2, driver_end_cap-delta]) | ||||||
|  |         cube([cubex, cubey, driver_slot_length+delta]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | translate([0, 0, translate_motor_mount]) | ||||||
|  | if (render_motor_mount) { | ||||||
|  |     translate([0, -motor_shaft_position, motor_mount_depth/2]) { | ||||||
|  |         difference() { | ||||||
|  |             union() { | ||||||
|  |                 cube([motor_x + 2*motor_x_thickness, motor_y + 2*motor_y_thickness, motor_mount_depth], center=true); | ||||||
|  |                 translate([0, motor_screw_position, 0]) | ||||||
|  |                     cylinder(h=motor_mount_depth, d=motor_screw_housing, $fn=36, center=true); | ||||||
|  |                 translate([0, -motor_screw_position, 0]) | ||||||
|  |                     cylinder(h=motor_mount_depth, d=motor_screw_housing, $fn=36, center=true); | ||||||
|  |                 translate([0, (motor_screw_position + motor_y/2 + delta)/2, 0]) | ||||||
|  |                 cube([motor_screw_housing, motor_screw_position - motor_y/2 - delta, motor_mount_depth], center=true); | ||||||
|  |                 translate([0, -(motor_screw_position + motor_y/2 + delta)/2, 0]) | ||||||
|  |                 cube([motor_screw_housing, motor_screw_position - motor_y/2 - delta, motor_mount_depth], center=true); | ||||||
|  |                 support_extra = (support_width - support_top_width)/2; | ||||||
|  |                 cube_len = support_extra+support_position+support_height/2-motor_shaft_position-motor_y/2-motor_y_thickness+delta; | ||||||
|  |                 translate([0, -cube_len/2 - motor_y/2 - motor_y_thickness + delta/2, 0]) | ||||||
|  |                 cube([support_width, cube_len, motor_mount_depth], center=true); | ||||||
|  |             } | ||||||
|  |             cube([motor_x, motor_y, motor_mount_depth+2*delta], center=true); | ||||||
|  |             translate([0, motor_screw_position, 0]) | ||||||
|  |                 cylinder(h=motor_mount_depth+2*delta, d=motor_screw_diameter, $fn=36, center=true); | ||||||
|  |             translate([0, -motor_screw_position, 0]) | ||||||
|  |                 cylinder(h=motor_mount_depth+2*delta, d=motor_screw_diameter, $fn=36, center=true); | ||||||
|  |             translate([0, motor_shaft_position-support_position, 0]) | ||||||
|  |                 cube([support_top_width+2*support_clearance, support_height+2*support_clearance, motor_mount_depth+2*delta], center=true); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,37 @@ | |||||||
|  | == Pixel-screw.scad == | ||||||
|  | 
 | ||||||
|  | This can generate four parts. Set each of the following to 1 in turn: | ||||||
|  | 
 | ||||||
|  | render_screw = 1; | ||||||
|  | render_pipe_screw = 0; | ||||||
|  | render_guide = 0; | ||||||
|  | render_motor_mount = 0; | ||||||
|  | 
 | ||||||
|  | Note that they can be combined to see how the model fits together. | ||||||
|  | 
 | ||||||
|  | The screw should be printed in a one colour (I chose yellow) and the pipe screw | ||||||
|  | and the guide should be printed in a contrasting colour (I chose black). | ||||||
|  | 
 | ||||||
|  | The colour of the motor mount and the other components isn't visible from the | ||||||
|  | front, and so only matters to the aesthetics from the back. (I chose blue) | ||||||
|  | 
 | ||||||
|  | == nail-test.scad == | ||||||
|  | 
 | ||||||
|  | Set the following to 0 and to 1 to generate the two halves of this part. | ||||||
|  | 
 | ||||||
|  | nail = 0; | ||||||
|  | 
 | ||||||
|  | The difference is that one has a narrower diameter in the holes, so that a nail | ||||||
|  | can be banged into each hole. I used 40x1.6mm steel panel pins. | ||||||
|  | 
 | ||||||
|  | == horn-holder.scad == | ||||||
|  | 
 | ||||||
|  | This fits the straight arm from a Tower Pro SG90 servo. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | == Notes == | ||||||
|  | 
 | ||||||
|  | The mechanism for attaching the nail system to the rest of the parts is | ||||||
|  | considerably weaker than the rest of the system, and is prone to breakage. | ||||||
|  | This really needs redesigning. A square hole/insert would probably make | ||||||
|  | more sense here. | ||||||
| @ -0,0 +1,58 @@ | |||||||
|  | thickness = 2; | ||||||
|  | centre_d = 7; | ||||||
|  | middle_d = 5.5; | ||||||
|  | outer_d = 4; | ||||||
|  | length = 32; | ||||||
|  | $fn = 36; | ||||||
|  | 
 | ||||||
|  | rim = 2; | ||||||
|  | rim_thickness = 2; | ||||||
|  | clearance = 0.1; | ||||||
|  | 
 | ||||||
|  | shaft_radius = 2; | ||||||
|  | shaft_length = 5; | ||||||
|  | groove_width = 1; | ||||||
|  | groove_length = 3; | ||||||
|  | 
 | ||||||
|  | outer_centre = length/2 - outer_d/2; | ||||||
|  | 
 | ||||||
|  | screw_d = 2.4; | ||||||
|  | screw_depth = 1; | ||||||
|  | 
 | ||||||
|  | delta = 0.1; | ||||||
|  | 
 | ||||||
|  | module horn(thickness, length, centre_d, middle_d, outer_d) { | ||||||
|  |     cylinder(h=thickness, d=centre_d); | ||||||
|  | 
 | ||||||
|  |     translate([-outer_centre, 0, 0]) | ||||||
|  |         cylinder(h=thickness, d=outer_d); | ||||||
|  | 
 | ||||||
|  |     translate([outer_centre, 0, 0]) | ||||||
|  |         cylinder(h=thickness, d=outer_d); | ||||||
|  | 
 | ||||||
|  |     linear_extrude(height = thickness) | ||||||
|  |         polygon(points=[ | ||||||
|  |             [0, -middle_d/2], | ||||||
|  |             [-outer_centre, -outer_d/2], | ||||||
|  |             [-outer_centre, outer_d/2], | ||||||
|  |             [0, middle_d/2], | ||||||
|  |             [outer_centre, outer_d/2], | ||||||
|  |             [outer_centre, -outer_d/2] | ||||||
|  |         ]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | difference() { | ||||||
|  |     horn(thickness+rim, length+rim, centre_d+rim, middle_d+rim, outer_d+rim); | ||||||
|  |     translate([0, 0, -delta]) | ||||||
|  |         horn(thickness+delta, length + clearance, centre_d + clearance, middle_d + clearance, outer_d + clearance); | ||||||
|  |     translate([0, 0, thickness-delta]) | ||||||
|  |         cylinder(h=screw_depth+delta, d=screw_d); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | difference() { | ||||||
|  |     translate([0, 0, thickness + rim - delta]) | ||||||
|  |         cylinder(h=delta + shaft_length, r = shaft_radius); | ||||||
|  |     translate([-shaft_radius*3/2, -groove_width/2, thickness + rim + shaft_length - groove_length]) | ||||||
|  |         cube([shaft_radius*3, groove_width, groove_length+delta]); | ||||||
|  | } | ||||||
| @ -0,0 +1,54 @@ | |||||||
|  | 
 | ||||||
|  | delta = 0.1; | ||||||
|  | xo = 10; | ||||||
|  | height = 10; | ||||||
|  | bar_thickness = 2; | ||||||
|  | 
 | ||||||
|  | nail = 0; | ||||||
|  | 
 | ||||||
|  | d_inner_nail = 1.8; | ||||||
|  | d_outer_nail = 6; | ||||||
|  | 
 | ||||||
|  | d_inner_hole = 2.2; | ||||||
|  | d_outer_hole = 6; | ||||||
|  | 
 | ||||||
|  | d_inner = nail ? d_inner_nail : d_inner_hole; | ||||||
|  | d_outer = nail ? d_outer_nail : d_outer_hole; | ||||||
|  | 
 | ||||||
|  | d_centre = 7; | ||||||
|  | d_joint = 4.55; | ||||||
|  | w_joint = 0.7; | ||||||
|  | 
 | ||||||
|  | difference() { | ||||||
|  |     union() { | ||||||
|  |         translate([-xo, -d_outer/2, 0]) | ||||||
|  |             cube([xo*2, d_outer, bar_thickness]); | ||||||
|  |         cylinder(h=bar_thickness, d=d_centre, $fn=36); | ||||||
|  |         translate([-xo, 0, 0]) | ||||||
|  |             nail_outer(); | ||||||
|  |         translate([xo, 0, 0]) | ||||||
|  |             nail_outer(); | ||||||
|  |     } | ||||||
|  |     translate([-xo, 0, 0]) | ||||||
|  |         nail_hole(d_inner); | ||||||
|  |     translate([xo, 0, 0]) | ||||||
|  |         nail_hole(d_inner); | ||||||
|  |     nail_hole(d_joint); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | joint_bar(); | ||||||
|  | 
 | ||||||
|  | module nail_outer() { | ||||||
|  |     cylinder(h=height, d=d_outer, $fn=36); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module nail_hole(diameter) { | ||||||
|  |     translate([0, 0, -delta]) | ||||||
|  |         cylinder(h=height+2*delta, d=diameter, $fn=36); | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module joint_bar() { | ||||||
|  |     translate([-d_joint, -w_joint/2, 0]) | ||||||
|  |         cube([d_joint*2, w_joint, bar_thickness]); | ||||||
|  | } | ||||||
| @ -0,0 +1,45 @@ | |||||||
|  | Software for the pixel grid. | ||||||
|  | 
 | ||||||
|  | == pixel.py == | ||||||
|  | 
 | ||||||
|  | This software will turn an 8x8 image or animated image (as read by the PIL | ||||||
|  | library) into servo commands for a pixel grid. | ||||||
|  | 
 | ||||||
|  | Usage:  | ||||||
|  |     pixel.py [-h] [-t] [-v] [-c] [-d delay] <files> | ||||||
|  | 
 | ||||||
|  |     -h     Display usage string | ||||||
|  |     -t     Test mode | ||||||
|  |                This sets all pixels to 0, then to 255, then back to 0. | ||||||
|  |     -v     View mode | ||||||
|  |                Outputs to the screen only. Does not try to open any devices. | ||||||
|  |     -c     Calibration mode | ||||||
|  |                Sets all outputs to 1500us. Useful for assembling the motors. | ||||||
|  |     -d n   Sets inter-image delay to n seconds | ||||||
|  | 
 | ||||||
|  | This command will display all files in sequence, with a specified delay in | ||||||
|  | between each image. Animated formats will be played once through, frame by | ||||||
|  | frame, with a fixed inter-frame delay. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | maestro.py is from: | ||||||
|  | https://github.com/FRC4564/Maestro | ||||||
|  | 
 | ||||||
|  | With the addition of the ability to specify the baud rate when opening the | ||||||
|  | serial port. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | == maketext.py == | ||||||
|  | 
 | ||||||
|  | maketext.py is a helper program that will generate a scrolltext as an animated | ||||||
|  | image file. | ||||||
|  | 
 | ||||||
|  | Usage: | ||||||
|  |     maketext.py text file.gif | ||||||
|  | 
 | ||||||
|  | Note that text is a single argument and should be quoted appropriately. | ||||||
|  | 
 | ||||||
|  | It requires ImageMagick to be installed. It will also look for a font in | ||||||
|  | Beeb/Beeb.ttf. That font is available from: | ||||||
|  | 
 | ||||||
|  | https://fontstruct.com/fontstructions/show/63444/beeb | ||||||
| @ -0,0 +1,169 @@ | |||||||
|  | import serial | ||||||
|  | from sys import version_info | ||||||
|  | 
 | ||||||
|  | PY2 = version_info[0] == 2   #Running Python 2.x? | ||||||
|  | 
 | ||||||
|  | # | ||||||
|  | #--------------------------- | ||||||
|  | # Maestro Servo Controller | ||||||
|  | #--------------------------- | ||||||
|  | # | ||||||
|  | # Support for the Pololu Maestro line of servo controllers | ||||||
|  | # | ||||||
|  | # Steven Jacobs -- Aug 2013 | ||||||
|  | # https://github.com/FRC4564/Maestro/ | ||||||
|  | # | ||||||
|  | # These functions provide access to many of the Maestro's capabilities using the | ||||||
|  | # Pololu serial protocol | ||||||
|  | # | ||||||
|  | class Controller: | ||||||
|  |     # When connected via USB, the Maestro creates two virtual serial ports | ||||||
|  |     # /dev/ttyACM0 for commands and /dev/ttyACM1 for communications. | ||||||
|  |     # Be sure the Maestro is configured for "USB Dual Port" serial mode. | ||||||
|  |     # "USB Chained Mode" may work as well, but hasn't been tested. | ||||||
|  |     # | ||||||
|  |     # Pololu protocol allows for multiple Maestros to be connected to a single | ||||||
|  |     # serial port. Each connected device is then indexed by number. | ||||||
|  |     # This device number defaults to 0x0C (or 12 in decimal), which this module | ||||||
|  |     # assumes.  If two or more controllers are connected to different serial | ||||||
|  |     # ports, or you are using a Windows OS, you can provide the tty port.  For | ||||||
|  |     # example, '/dev/ttyACM2' or for Windows, something like 'COM3'. | ||||||
|  |     def __init__(self,ttyStr='/dev/ttyACM0',device=0x0c,baud=115200): | ||||||
|  |         # Open the command port | ||||||
|  |         self.usb = serial.Serial(ttyStr,baudrate=baud) | ||||||
|  |         # Command lead-in and device number are sent for each Pololu serial command. | ||||||
|  |         self.PololuCmd = chr(0xaa) + chr(device) | ||||||
|  |         # Track target position for each servo. The function isMoving() will | ||||||
|  |         # use the Target vs Current servo position to determine if movement is | ||||||
|  |         # occuring.  Upto 24 servos on a Maestro, (0-23). Targets start at 0. | ||||||
|  |         self.Targets = [0] * 24 | ||||||
|  |         # Servo minimum and maximum targets can be restricted to protect components. | ||||||
|  |         self.Mins = [0] * 24 | ||||||
|  |         self.Maxs = [0] * 24 | ||||||
|  |          | ||||||
|  |     # Cleanup by closing USB serial port | ||||||
|  |     def close(self): | ||||||
|  |         self.usb.close() | ||||||
|  | 
 | ||||||
|  |     # Send a Pololu command out the serial port | ||||||
|  |     def sendCmd(self, cmd): | ||||||
|  |         cmdStr = self.PololuCmd + cmd | ||||||
|  |         if PY2: | ||||||
|  |             self.usb.write(cmdStr) | ||||||
|  |         else: | ||||||
|  |             self.usb.write(bytes(cmdStr,'latin-1')) | ||||||
|  | 
 | ||||||
|  |     # Set channels min and max value range.  Use this as a safety to protect | ||||||
|  |     # from accidentally moving outside known safe parameters. A setting of 0 | ||||||
|  |     # allows unrestricted movement. | ||||||
|  |     # | ||||||
|  |     # ***Note that the Maestro itself is configured to limit the range of servo travel | ||||||
|  |     # which has precedence over these values.  Use the Maestro Control Center to configure | ||||||
|  |     # ranges that are saved to the controller.  Use setRange for software controllable ranges. | ||||||
|  |     def setRange(self, chan, min, max): | ||||||
|  |         self.Mins[chan] = min | ||||||
|  |         self.Maxs[chan] = max | ||||||
|  | 
 | ||||||
|  |     # Return Minimum channel range value | ||||||
|  |     def getMin(self, chan): | ||||||
|  |         return self.Mins[chan] | ||||||
|  | 
 | ||||||
|  |     # Return Maximum channel range value | ||||||
|  |     def getMax(self, chan): | ||||||
|  |         return self.Maxs[chan] | ||||||
|  |          | ||||||
|  |     # Set channel to a specified target value.  Servo will begin moving based | ||||||
|  |     # on Speed and Acceleration parameters previously set. | ||||||
|  |     # Target values will be constrained within Min and Max range, if set. | ||||||
|  |     # For servos, target represents the pulse width in of quarter-microseconds | ||||||
|  |     # Servo center is at 1500 microseconds, or 6000 quarter-microseconds | ||||||
|  |     # Typcially valid servo range is 3000 to 9000 quarter-microseconds | ||||||
|  |     # If channel is configured for digital output, values < 6000 = Low ouput | ||||||
|  |     def setTarget(self, chan, target): | ||||||
|  |         # if Min is defined and Target is below, force to Min | ||||||
|  |         if self.Mins[chan] > 0 and target < self.Mins[chan]: | ||||||
|  |             target = self.Mins[chan] | ||||||
|  |         # if Max is defined and Target is above, force to Max | ||||||
|  |         if self.Maxs[chan] > 0 and target > self.Maxs[chan]: | ||||||
|  |             target = self.Maxs[chan] | ||||||
|  |         #     | ||||||
|  |         lsb = target & 0x7f #7 bits for least significant byte | ||||||
|  |         msb = (target >> 7) & 0x7f #shift 7 and take next 7 bits for msb | ||||||
|  |         cmd = chr(0x04) + chr(chan) + chr(lsb) + chr(msb) | ||||||
|  |         self.sendCmd(cmd) | ||||||
|  |         # Record Target value | ||||||
|  |         self.Targets[chan] = target | ||||||
|  |          | ||||||
|  |     # Set speed of channel | ||||||
|  |     # Speed is measured as 0.25microseconds/10milliseconds | ||||||
|  |     # For the standard 1ms pulse width change to move a servo between extremes, a speed | ||||||
|  |     # of 1 will take 1 minute, and a speed of 60 would take 1 second. | ||||||
|  |     # Speed of 0 is unrestricted. | ||||||
|  |     def setSpeed(self, chan, speed): | ||||||
|  |         lsb = speed & 0x7f #7 bits for least significant byte | ||||||
|  |         msb = (speed >> 7) & 0x7f #shift 7 and take next 7 bits for msb | ||||||
|  |         cmd = chr(0x07) + chr(chan) + chr(lsb) + chr(msb) | ||||||
|  |         self.sendCmd(cmd) | ||||||
|  | 
 | ||||||
|  |     # Set acceleration of channel | ||||||
|  |     # This provide soft starts and finishes when servo moves to target position. | ||||||
|  |     # Valid values are from 0 to 255. 0=unrestricted, 1 is slowest start. | ||||||
|  |     # A value of 1 will take the servo about 3s to move between 1ms to 2ms range. | ||||||
|  |     def setAccel(self, chan, accel): | ||||||
|  |         lsb = accel & 0x7f #7 bits for least significant byte | ||||||
|  |         msb = (accel >> 7) & 0x7f #shift 7 and take next 7 bits for msb | ||||||
|  |         cmd = chr(0x09) + chr(chan) + chr(lsb) + chr(msb) | ||||||
|  |         self.sendCmd(cmd) | ||||||
|  |      | ||||||
|  |     # Get the current position of the device on the specified channel | ||||||
|  |     # The result is returned in a measure of quarter-microseconds, which mirrors | ||||||
|  |     # the Target parameter of setTarget. | ||||||
|  |     # This is not reading the true servo position, but the last target position sent | ||||||
|  |     # to the servo. If the Speed is set to below the top speed of the servo, then | ||||||
|  |     # the position result will align well with the acutal servo position, assuming | ||||||
|  |     # it is not stalled or slowed. | ||||||
|  |     def getPosition(self, chan): | ||||||
|  |         cmd = chr(0x10) + chr(chan) | ||||||
|  |         self.sendCmd(cmd) | ||||||
|  |         lsb = ord(self.usb.read()) | ||||||
|  |         msb = ord(self.usb.read()) | ||||||
|  |         return (msb << 8) + lsb | ||||||
|  | 
 | ||||||
|  |     # Test to see if a servo has reached the set target position.  This only provides | ||||||
|  |     # useful results if the Speed parameter is set slower than the maximum speed of | ||||||
|  |     # the servo.  Servo range must be defined first using setRange. See setRange comment. | ||||||
|  |     # | ||||||
|  |     # ***Note if target position goes outside of Maestro's allowable range for the | ||||||
|  |     # channel, then the target can never be reached, so it will appear to always be | ||||||
|  |     # moving to the target.   | ||||||
|  |     def isMoving(self, chan): | ||||||
|  |         if self.Targets[chan] > 0: | ||||||
|  |             if self.getPosition(chan) != self.Targets[chan]: | ||||||
|  |                 return True | ||||||
|  |         return False | ||||||
|  |      | ||||||
|  |     # Have all servo outputs reached their targets? This is useful only if Speed and/or | ||||||
|  |     # Acceleration have been set on one or more of the channels. Returns True or False. | ||||||
|  |     # Not available with Micro Maestro. | ||||||
|  |     def getMovingState(self): | ||||||
|  |         cmd = chr(0x13) | ||||||
|  |         self.sendCmd(cmd) | ||||||
|  |         if self.usb.read() == chr(0): | ||||||
|  |             return False | ||||||
|  |         else: | ||||||
|  |             return True | ||||||
|  | 
 | ||||||
|  |     # Run a Maestro Script subroutine in the currently active script. Scripts can | ||||||
|  |     # have multiple subroutines, which get numbered sequentially from 0 on up. Code your | ||||||
|  |     # Maestro subroutine to either infinitely loop, or just end (return is not valid). | ||||||
|  |     def runScriptSub(self, subNumber): | ||||||
|  |         cmd = chr(0x27) + chr(subNumber) | ||||||
|  |         # can pass a param with command 0x28 | ||||||
|  |         # cmd = chr(0x28) + chr(subNumber) + chr(lsb) + chr(msb) | ||||||
|  |         self.sendCmd(cmd) | ||||||
|  | 
 | ||||||
|  |     # Stop the current Maestro Script | ||||||
|  |     def stopScript(self): | ||||||
|  |         cmd = chr(0x24) | ||||||
|  |         self.sendCmd(cmd) | ||||||
|  | 
 | ||||||
| @ -0,0 +1,32 @@ | |||||||
|  | #!/usr/bin/python3 | ||||||
|  | 
 | ||||||
|  | from PIL import Image, ImageFont, ImageDraw | ||||||
|  | import os | ||||||
|  | import subprocess | ||||||
|  | import shutil | ||||||
|  | import sys | ||||||
|  | 
 | ||||||
|  | directory = "output" | ||||||
|  | text = sys.argv[1] | ||||||
|  | outfile = sys.argv[2] | ||||||
|  | 
 | ||||||
|  | fnt = ImageFont.truetype('Beeb/Beeb.ttf', 8) | ||||||
|  | 
 | ||||||
|  | (pixels, height) = fnt.getsize(text) | ||||||
|  | 
 | ||||||
|  | # Lead-in plus lead-out | ||||||
|  | frames = pixels + 8 + 8 | ||||||
|  | 
 | ||||||
|  | image = Image.new('L', (frames, 8), 0) | ||||||
|  | draw = ImageDraw.Draw(image) | ||||||
|  | 
 | ||||||
|  | draw.text((8, 0), text, 255, fnt) | ||||||
|  | 
 | ||||||
|  | os.mkdir(directory) | ||||||
|  | 
 | ||||||
|  | for frame in range(0,frames): | ||||||
|  |     img = image.crop((frame, 0, frame+8, 8)) | ||||||
|  |     img.save("%s/%04d.png" % (directory, frame)) | ||||||
|  | 
 | ||||||
|  | subprocess.call(["convert", "%s/*.png" % directory, outfile]) | ||||||
|  | shutil.rmtree(directory) | ||||||
| @ -0,0 +1,261 @@ | |||||||
|  | #!/usr/bin/python3 | ||||||
|  | 
 | ||||||
|  | import time | ||||||
|  | import maestro | ||||||
|  | from PIL import Image | ||||||
|  | from PIL import ImageSequence | ||||||
|  | import sys, getopt | ||||||
|  | 
 | ||||||
|  | default_min = 7232 | ||||||
|  | default_max = 4032 | ||||||
|  | 
 | ||||||
|  | def create_display(s1, s2, s3): | ||||||
|  |     pixels = [ | ||||||
|  |         [ | ||||||
|  |             [s3,  8, 7232, 4032], | ||||||
|  |             [s3,  9], | ||||||
|  |             [s3, 10], | ||||||
|  |             [s3, 11], | ||||||
|  |             [s3, 12], | ||||||
|  |             [s3, 13], | ||||||
|  |             [s3, 14], | ||||||
|  |             [s3, 15], | ||||||
|  |         ], [ | ||||||
|  |             [s3,  0], | ||||||
|  |             [s3,  1], | ||||||
|  |             [s3,  2], | ||||||
|  |             [s3,  3], | ||||||
|  |             [s3,  4], | ||||||
|  |             [s3,  5], | ||||||
|  |             [s3,  6], | ||||||
|  |             [s3,  7], | ||||||
|  |         ], [ | ||||||
|  |             [s2, 16], | ||||||
|  |             [s2, 17], | ||||||
|  |             [s2, 18], | ||||||
|  |             [s2, 19], | ||||||
|  |             [s2, 20], | ||||||
|  |             [s2, 21], | ||||||
|  |             [s2, 22], | ||||||
|  |             [s2, 23], | ||||||
|  |         ], [ | ||||||
|  |             [s2,  8], | ||||||
|  |             [s2,  9], | ||||||
|  |             [s2, 10], | ||||||
|  |             [s2, 11], | ||||||
|  |             [s2, 12], | ||||||
|  |             [s2, 13], | ||||||
|  |             [s2, 14], | ||||||
|  |             [s2, 15], | ||||||
|  |         ], [ | ||||||
|  |             [s2,  0], | ||||||
|  |             [s2,  1], | ||||||
|  |             [s2,  2], | ||||||
|  |             [s2,  3], | ||||||
|  |             [s2,  4], | ||||||
|  |             [s2,  5], | ||||||
|  |             [s2,  6], | ||||||
|  |             [s2,  7], | ||||||
|  |         ], [ | ||||||
|  |             [s1, 16], | ||||||
|  |             [s1, 17], | ||||||
|  |             [s1, 18], | ||||||
|  |             [s1, 19], | ||||||
|  |             [s1, 20], | ||||||
|  |             [s1, 21], | ||||||
|  |             [s1, 22], | ||||||
|  |             [s1, 23], | ||||||
|  |         ], [ | ||||||
|  |             [s1,  8], | ||||||
|  |             [s1,  9], | ||||||
|  |             [s1, 10], | ||||||
|  |             [s1, 11], | ||||||
|  |             [s1, 12], | ||||||
|  |             [s1, 13], | ||||||
|  |             [s1, 14], | ||||||
|  |             [s1, 15], | ||||||
|  |         ], [ | ||||||
|  |             [s1,  0], | ||||||
|  |             [s1,  1], | ||||||
|  |             [s1,  2], | ||||||
|  |             [s1,  3], | ||||||
|  |             [s1,  4], | ||||||
|  |             [s1,  5], | ||||||
|  |             [s1,  6], | ||||||
|  |             [s1,  7], | ||||||
|  |         ] | ||||||
|  |     ] | ||||||
|  |     return display(pixels) | ||||||
|  | 
 | ||||||
|  | class pixel: | ||||||
|  |     def __init__(self, servo, id, min=default_min, max=default_max): | ||||||
|  |         self.servo = servo | ||||||
|  |         self.id = id | ||||||
|  |         self.value = 0 | ||||||
|  |         self.dirty = True | ||||||
|  |         self.min = min | ||||||
|  |         self.max = max | ||||||
|  |         self.output() | ||||||
|  | 
 | ||||||
|  |     def set_min(self, value): | ||||||
|  |         self.min = value | ||||||
|  | 
 | ||||||
|  |     def set_max(self, value): | ||||||
|  |         self.max = value | ||||||
|  | 
 | ||||||
|  |     def set(self, value): | ||||||
|  |         if value < 0: | ||||||
|  |             value = 0 | ||||||
|  |         if value > 255: | ||||||
|  |             value = 255 | ||||||
|  |         if self.value != value: | ||||||
|  |             self.dirty = True | ||||||
|  |         self.value = value | ||||||
|  | 
 | ||||||
|  |     def get(self): | ||||||
|  |         return self.value | ||||||
|  | 
 | ||||||
|  |     def output(self): | ||||||
|  |         if self.dirty: | ||||||
|  |             value = ((self.max-self.min) * self.value / 255) + self.min | ||||||
|  |             #print('('+repr(self.id)+') '+repr(value)) | ||||||
|  |             if self.servo: | ||||||
|  |                 self.servo.setTarget(self.id, int(round(value))) | ||||||
|  |             self.dirty = False | ||||||
|  | 
 | ||||||
|  |     def cal(self): | ||||||
|  |         self.servo.setTarget(self.id, 1500*4) | ||||||
|  | 
 | ||||||
|  | class display: | ||||||
|  |     def __init__(self, params): | ||||||
|  |         self.p = [] | ||||||
|  |         for param_row in params: | ||||||
|  |             row = [] | ||||||
|  |             for args in param_row: | ||||||
|  |                 row.append(pixel(*args)) | ||||||
|  |             self.p.append(row) | ||||||
|  | 
 | ||||||
|  |     def init(self, x, y, pixel): | ||||||
|  |         self.p[y][x] = pixel | ||||||
|  | 
 | ||||||
|  |     def set(self, x, y, value): | ||||||
|  |         self.p[y][x].set(value) | ||||||
|  | 
 | ||||||
|  |     def draw(self, im): | ||||||
|  |         for y, row in enumerate(self.p): | ||||||
|  |             for x, pixel in enumerate(row): | ||||||
|  |                 pixel.set(im.getpixel((x, y))) | ||||||
|  | 
 | ||||||
|  |     def output(self): | ||||||
|  |         for row in self.p: | ||||||
|  |             for pixel in row: | ||||||
|  |                 if pixel != None: | ||||||
|  |                     pixel.output() | ||||||
|  | 
 | ||||||
|  |     def cal(self): | ||||||
|  |         for row in self.p: | ||||||
|  |             for pixel in row: | ||||||
|  |                 if pixel != None: | ||||||
|  |                     pixel.cal() | ||||||
|  | 
 | ||||||
|  |     def print(self): | ||||||
|  |         for row in self.p: | ||||||
|  |             s = "" | ||||||
|  |             for pixel in row: | ||||||
|  |                 if pixel != None: | ||||||
|  |                     if pixel.get() > 127: | ||||||
|  |                         s += "X" | ||||||
|  |                     else: | ||||||
|  |                         s += " " | ||||||
|  |             print(s) | ||||||
|  |         print("") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def slideshow(d, images, delay): | ||||||
|  |     for image in images: | ||||||
|  |         show(d, image) | ||||||
|  |         time.sleep(delay) | ||||||
|  | 
 | ||||||
|  | def show(d, image): | ||||||
|  |     global viewmode | ||||||
|  |     print("Displaying " + repr(image)) | ||||||
|  |     im = Image.open(image) | ||||||
|  |     print(repr(im.info)) | ||||||
|  |     for frame in ImageSequence.Iterator(im): | ||||||
|  |         im8 = frame.convert("L") | ||||||
|  |         print(repr(frame.info)) | ||||||
|  |         if viewmode: | ||||||
|  |             im8.show() | ||||||
|  |         else: | ||||||
|  |             d.draw(im8) | ||||||
|  |             d.output() | ||||||
|  |             d.print() | ||||||
|  |         time.sleep(0.2) | ||||||
|  | 
 | ||||||
|  | def test(d, value): | ||||||
|  |     print("Outputting "+repr(value)) | ||||||
|  |     for y in range(0,8): | ||||||
|  |         for x in range(0,8): | ||||||
|  |             d.set(x, y, value) | ||||||
|  |     d.output() | ||||||
|  | 
 | ||||||
|  | baudrate = 115200 | ||||||
|  | 
 | ||||||
|  | usage = "'pixel.py [-h] [-t] [-v] [-c] [-d delay] <files>'" | ||||||
|  | 
 | ||||||
|  | def main(argv): | ||||||
|  |     global viewmode | ||||||
|  | 
 | ||||||
|  |     delay = 10 | ||||||
|  |     testmode = False | ||||||
|  |     viewmode = False | ||||||
|  |     calmode = False | ||||||
|  |     try: | ||||||
|  |         opts, args = getopt.getopt(argv, "htvcd:", ["delay="]) | ||||||
|  |     except getopt.GetoptError: | ||||||
|  |         print(usage) | ||||||
|  |         sys.exit(2) | ||||||
|  |     for opt, arg in opts: | ||||||
|  |         if opt in ('-h', "--help"): | ||||||
|  |             print(usage) | ||||||
|  |             sys.exit() | ||||||
|  |         elif opt in ("-d", "--delay"): | ||||||
|  |             delay = arg | ||||||
|  |         elif opt in ("-t", "--test"): | ||||||
|  |             testmode = True | ||||||
|  |         elif opt in ("-v", "--view"): | ||||||
|  |             viewmode = True | ||||||
|  |         elif opt in ("-c", "--cal"): | ||||||
|  |             calmode = True | ||||||
|  |     d = None | ||||||
|  |     if not viewmode: | ||||||
|  |         try: | ||||||
|  |             s1 = maestro.Controller(device=0x0c, baud=baudrate) | ||||||
|  |             s2 = maestro.Controller(device=0x0d, baud=baudrate) | ||||||
|  |             s3 = maestro.Controller(device=0x0e, baud=baudrate) | ||||||
|  |         except: | ||||||
|  |             s1 = None | ||||||
|  |             s2 = None | ||||||
|  |             s3 = None | ||||||
|  |         d = create_display(s1, s2, s3) | ||||||
|  |     if calmode: | ||||||
|  |         d.cal() | ||||||
|  |     elif testmode: | ||||||
|  |         test(d, 0) | ||||||
|  |         time.sleep(1) | ||||||
|  |         test(d, 255) | ||||||
|  |         time.sleep(1) | ||||||
|  |         test(d, 0) | ||||||
|  |     else: | ||||||
|  |         slideshow(d, args, delay) | ||||||
|  | 
 | ||||||
|  |     if not viewmode: | ||||||
|  |         s3.close | ||||||
|  |         s2.close | ||||||
|  |         s1.close | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     main(sys.argv[1:]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
					Loading…
					
					
				
		Reference in new issue