Weekly Summary
Machine Week! As Asano-san and myself are the only current students at FabLab Kamakura, we created an simple, entertaining, and - we hope funny - machine. The Fruity Secret Messenger.
The machine is the implementation of a simple, silly idea we had on the first weekend session. Let's create (another!) drawing machine, but with a twist! Can we use lemon (lime, orange, grapefruit, etc) juice as a drawing liquid? And can we use a heat-bed to make the invisible drawing visible?
Group Assignment
The Group Assignment is to: design a machine that includes mechanism+actuation+automation, build the mechanical parts and operate it manually, document the group project and your individual contribution, and programming environments as possible, actuate and automate your machine, document the group project and your individual contribution.
Here is the link to the Group Assignment page.
And our 1 min presentation video!
Time Constraints & Time Planning
The time for this assignment was two weeks, on the first week's lab session we brainstormed ideas and arrived - as we are only 2 students in our group - at a doable, and possibly funny project.
The Idea
The idea is a variation on the tested and well-done project of a drawing machine. XY actuation, a visible out. But our idea has a twist. Instead of using ink, we will use citric acid (lemon juice!) as an invisible ink to write a secret message.
Our initial idea - and hope - was to use the heat bed of a 3D printer to make the invisible ink visible.
Prototyping
Fruit Parade
"I got a Lemon Pen"
Testing the Lemon Pen
Heating the Heatbed
The heatbed is heating up, currently at 112ยบC, but it will not reach 135ยบC.
Using Gas Range to create the heat necessary for making the invisible ink visible. Not recommended. Especially if your FabLab is made from wood.
Manually actuating the Machine
Machine Sketches
Machine Vision
That's how it should look at the end.
Individual Contribution: Modelling the Juice Extruder
Sketches
I set myself the goal to model the extruder in OpenSCAD. Why OpenSCAD? The extruder is fairly symmetric.
Usually I don't include longer code, but I think OpenSCAD is worth an exception. You can also download the code from the files section below.
echo(version=version());
// number of fragments, set number of faces, global
$fn=200;
// colors
alpha = 0.75;
green = [0, 0.5, 0, alpha];
yellow = [1, 0.75, 0, alpha];
red = [1, 0, 0, alpha];
blue = [0, 0, 1, alpha];
// * hides object
// % makes in semi-transpartent?
// rotate_extrude() rotates a 2D shape around the Z axis.
// Note that the 2D shape must be either completely on the
// positive or negative side of the X axis.
*color([1,1,0, 0.1])
rotate_extrude()
translate([10, 0])
square(50 );
// Using a shape that touches the X axis is allowed and produces
// 3D objects that don't have a hole in the center.
height = 80;
radius = 7.5;
wall = 3;
cubeWidth = 10;
cube2Width = 8;
function cyl(h, r, w) =
[[r-w,0],[r,0],[r,h],[r-w,h]];
color(yellow) {
translate([0, 0, height/2 - 10]) {
difference() { // Tube x Crown
// Tube
difference() {
rotate_extrude()
translate([0, -height/2, 0])
polygon( points=cyl(height, radius, wall));
*union() {
cylinder(height+10, radius-wall, radius-wall, true);
rotate(-90) {
translate([-cubeWidth/2, 10, 0]) {
cube([cubeWidth, 2, 20]);
}
translate([-cube2Width/2, 0, 2]) {
cube([cube2Width, 12, 15]);
}
}
}
}
// Crown
crownThickness = 2;
steps = 4;
crownHeight = 7.5;
color(green) {
union() { // Union for Crown Elements
for (angle = [0 : 180/steps : 180-180/steps]) {
rotate([0, 0, angle+45/2]) {
translate([-radius*2, -crownThickness/2, 6]) {
cube([100, crownThickness, crownHeight]);
}
}
}
}
union() { // Union for Crown Elements
for (angle = [0 : 180/steps : 180-180/steps]) {
rotate([0, 0, angle+45/2]) {
translate([-radius*2, -crownThickness/2, 25]) {
cube([100, crownThickness, 100]);
}
}
}
}
}
} // end difference Tube x Crown
}
}
// connection plate
connectorWidth = 40;
r = 5;
cw = connectorWidth - r*2;
cw2 = cw/2;
connectorHeight = 5/2;
ch = connectorHeight;
// Plate
color(green) {
difference(){
color("blue")
translate([-cw2, -cw2, 0]) {
minkowski() {
cube([cw, cw, ch]);
cylinder(ch, r, r);
}
}
// Small Holes
/* ----------- */
holeDiameter = 3; // M3 inter diameter
holeOffsetFromCenter = 12;
/* ----------- */
holeRadius = holeDiameter/2;
hr = holeRadius;
holeOffset = cw2 - holeOffsetFromCenter;
ho = holeOffset;
color("red") {
translate([-cw2+ho, -cw2+ho, -1]) {
cylinder(ch*3, hr, hr);
}
translate([+cw2-ho, -cw2+ho, -1]) {
cylinder(ch*3, hr, hr);
}
translate([+cw2-ho, +cw2-ho, -1]) {
cylinder(ch*3, hr, hr);
}
translate([-cw2+ho, +cw2-ho, -1]) {
cylinder(ch*3, hr, hr);
}
}
// Central Hole
color("red") {
translate([0, 0, -1]) {
cylinder(ch*3, radius, radius);
}
}
} // end diff
} // end color
// Collector
color(blue) {
r = 30;
z = 43.5 + r;
translate([0, 0, z-40+r]) {
difference() {
sphere(r); // Outer Sphere
sphere(r - 3); // Inner Sphere
translate([-r, -r, -r+r/3]) { // Diff Sphere with Cube
cube([r*2, r*2, r*2]);
}
translate([0, 0, -r]) { // Central Hole
cylinder(10, radius, radius);
}
} // end diff
}
}
// Scaffolding
translate([0, 0, 0]) {
color(red) {
thickness = 2;
steps = 2;
height = 43.5;
difference() {
union() {
for (angle = [0 : 180/steps : 180-180/steps]) {
rotate([0, 0, angle]) {
translate([-radius*2, -thickness/2, 0]) {
cube([radius*4, thickness, height]);
}
}
}
}
translate([0, 0, -r]) { // Central Hole
cylinder(100, radius, radius);
}
}
}
}
echo ("height", height);
Print Attempt 1
Learning: Don't print horizontal.
Inkscape G-code Export
Exporting G-code from Inkscape
We are using an extension called Gcodetools to export G-Code from Inkscape.
Cleaning and Modifying G-code in Python
We repurposed the extruder servo to twist our fruits:
add_e_values_to_gcode.py
#!/usr/bin/env python3
inputFile = open("kamakura_0001.ngc", "r")
outputFile = open("kamakura_e.ngc", "w")
# add E values
a = list(range(10))
b = list(range(9))
b.reverse()
l = a + b
lineNr = 0
for line in inputFile:
values = line.split()
i = 0
for v in values:
firstLetter = v[0].upper()
# remove Z values at 3
if (i == 3 and firstLetter == 'Z'):
values[i] = ''
# remove F values at 6
if (i == 6 and firstLetter == 'F'):
values[i] = ''
# increment
i = i + 1
# add E to end
if len(values) > 0:
if (values[0][:3] == 'G02'):
e = l[lineNr % len(l)]
values.append('E' + str(e))
lineNr = lineNr + 1
# filter empty list entries
newValues = list(filter(None, values))
# join list to string
newValues = ' '.join(newValues) + "\n"
# print to console
#print(newValues)
#write to output file
outputFile.writelines(newValues)
Instructions
- Save the
add_e_values_to_gcode.py
Python Script, and make it executablechmod 777 add_e_values_to_gcode.py
- Match your Input file with the name in the Script
- Name your Output file
- Execute the Execute with
./add_e_values_to_gcode.py
Time Management
Well execute time management. We left on Sunday at 17:58, having completed all our tasks!
And it was great fun!
Producing the Video
Producing the Video was a nice exercise with sliders and bokeh. The repetitive motion of the writing machine did lend itself to a series of takes with different fruits. I spend quite a lot of time getting the right shots - and cutting the video to 1min. Here is it again: