Gavan Fantom
7 years ago
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