1 Objectives
-
Research Project
-
Make a Conductivity based AFM (Atomic Force Microscope)
-
Construct the rig that scans the object
-
Create Code for it, and Wire it.
-
Generate Image of the area scaned
-
prove components work together and are useful.
2 Component List
-
Arduino Mega.
-
2 10kΩ Resistor
-
1 47uF Capicitor, One 1000uF one
(1000uF is all I had, but it worked enough, under 47uF is to small)
-
10 47mm Piezo Disc (Marketed for Hybrid Acoustic Guitar Pickups)
Used to make 3 actuators
One is actually used as a conductive platform and isn’t active.
-
wires and breadboard, solder and phototype board to expand and anchor wires.
-
a needle to be used as probe
-
Hot glue and hot glue gun
-
10 30mm M3 Screws, with 10 M3 nuts, and 20 M3 Washers
-
A spring to put platform on so it can move freely, I used 7X12 5mm compressing spring.
-
3D printer for 3D printing parts
-
5V from USB.
-
Computer with Arduino IDE, and to run the image program on Processing
-
A condutive item to scan, I choose the thread of a small screw.
3 Choosing Project/Research
I have did this project before with less experience, with a similar setup but made of cardboard and without knowing the difference between PWM and real Analog Voltage, assuming it was already analog voltage, I got patterns of dots, but could not understand it, I built it because I became obsessed with microtechnology and saw a video of IBM making cartoons with molocules with a version, that can both see and make things,
There version is way to complex for me to build,
I can get started by making a very simple one, its very common to use piezoelectric actuators instead of normal motors, ones made for other applications like guitar pickups, or speakers, can be modified for this application,
Theres one here using it:
The one above uses a custom laser setup made from a CD-ROM, but since I am instrusted in electronics and looking at metals, I can simplify it even more by making a conducivity based one, and using a needle and conductive platform to get the conducvity creating a microscopic map/image of conductivity.
This can be useful for looking at the quality of metal like microscopic cracks, or to see the conductivity of semiconductors, it may be possible to connect one pin at a time and get several images, of what each pin connects to, unfortently most chips have epoxy and other coatings thats hard to remove without destroying the component. Because doing this correctly would be to time consuming I will be just imaging a thread of a screw, which can be used to see thats there a edge, and if quality is high enough to see if there is any cracks.
4 Procedure
4.1 Part 1: Designing and building the rig:
My final phototype is based on this:
I used a combination of hand made drawings and Freecad to design the Rig,
To be honest I was running out of time so instead of redesigning for the decision to connect all of the actuators to the platform, which I originally planned on being on the probe, I just drilled holes to fit the actuators the way on the picture, onto the base in the freecad design., it formed a partial box, so instead of making a holder for the probe, I just took a spare plastic part and glued it on time, and glued the needle to it, after placing the object to be scanned, unfortentently because its glued its fixed for the expirement with the screw.
Each actuator is made out of 3 piezo disk
They are then wired in parallel.
At the end the machine looks like this:
4.2 Part 2: Writing Code for it:
I wrote the code mostly surrounding 2 for loops for X and Y, to create the 2D image.
void ProbeCord(char movx, char movy) {
// had to make the code downscale by a quarter for it to fit on arduino.
int qmovx = movx*4;
int qmovy = movy*4;
moveprobe(qmovx, qmovy, 255);
char qnn = constrain(map(analogRead(probe), lower, upper, 0, 255), 0, 255);
moveprobe(qmovx+1, qmovy, 255);
char qpn = constrain(map(analogRead(probe), lower, upper, 0, 255), 0, 255);
moveprobe(qmovx, qmovy+1, 255);
char qnp = constrain(map(analogRead(probe), lower, upper, 0, 255), 0, 255);
moveprobe(qmovx+1, qmovy+1, 255);
char qpp = constrain(map(analogRead(probe), lower, upper, 0, 255), 0, 255);
ImageBuffer0[movx][movy] = (qnn+qpn+qnp+qpp)/4;
digitalWrite(Zenable, LOW);
digitalWrite(XYenable, LOW);
}
void TakeImage() {
for(int yscan=0; yscan < 64; yscan++) {
for(int xscan=0; xscan < 64; xscan++) {
ProbeCord(xscan, yscan);
}
analogWrite(piezoX, 0);
delay(RCDelay); // Give time for voltage to go down.
}
}
I created the code above to scan the object, In TakeImage() it uses a for a for loop for y to go back to front and then within that for loop theres another one for x to go left to right that runs quicker, they fill the buffer made from a 2D char matrix 64x64, I would use int but it would be to big, and the resoultions already low. The Processing code is like this but backwards.
In order to include more data without using more memory I split each point into quadrants and scan 4 segments in a square, and sum them up, to get data from a total of 256x256 points, unfortently because this process downscales it back down to 64x64 it still loses detail, but I think its better then not including the data.
4.3 Part 2: Wiring it:
I explain this in detail in the diagrams section.
But when wiring the piezo motors I needed to solder each array together in parrelel and hot glue the photoboard to the 3dprinted base since the wires where delicate. 2 of them snaped off, one I repaired, and another I used as the platform instead. Just for the metal surface so its important to anchor or tie down the wires for something like this, this is true for the probe too, which I hot glued the wire down for.
4.4 Part 3: Place object and Calibrate Machine
In order to make the first image I needed a object to scan, I choose a small screw with a fine thread because its conductive and thought it would look interesting when imaged,
This was done by glueing one side down, while making sure the other side hit the metal surface, I then glued the needle on the piece of plastic above so it hit the screw.
unfortently it was mostly white because it was too close, moving it did not make better images, everything seemed to be working. the values did seem to vary but stayed aroud 130, there did seem to be a line, when looking at the numbers. so I added code to define lower and higher, and set it from 100 to 175.
4.5 Part 4: Getting Image:
After running my processing program with the AFM connected,
I then got this image! :
I think its the thread or edge of the screw because of the lines, there was some glue on it I could not get off maybe thats the dots.
5 Diagrams
5.1 Hardware: (Schematic Diagram)
This is the schematic diagram of the project, the probe is connected directly to A0, 5V is connected to the metal platform.
1 of the piezo actuators made up of 3 piezo disk in parallel are connected directly between D11 and ground I used this instead of a driver conneted to pin 7 because I dont need PWM since its always 255, and switched rapidly on and off when needed via Zenable, I left the code there because I may change it later if I need depth detection, for now its just a 2D image,
The actuators starting with X and Y are grounded via a NPN transistor, controlled by pin 10, XYenable, this allows the RC circuit to charge to a certain voltage before the piezo disk moves, using the RC circuit alone would not work good because the Piezo disk have a certain frequency response, so while the rc circuit slowly brings the voltage up and down, the transistors rapidly switch on and off to put them on and back off, I designed the RC circuits though expirementation and guesing, and a suggestion I found on stackoverflow. I needed far bigger caps then what was mentioned though,
5.2 Hardware: (Picture of Electrical Circuit)
In the diagram above you can see a reflection of the schematic above I color coded the wires on the bottom, the black ones are ground, the Green one is X, The Red one is Y, and the Blue one, connected directly to the arduino is Z.
The yellow aligator clip is A0 for the probe, and the white aligator clip, is 5V for the metal platform. I used the red VCC rail as the XYenable instead if power connected to the Arduno with a orange wire.
The one on the bottom is Z with the blue wire, The one on the Left is X with the Green Wire, The one in the back is Y with the Red wire. the platform is a inactive piezo disk upsidedown connected to Z via a spring. and connected to the others with a plastic stick hot glued together. Theres a screw to image sitting on it.
Following sub-systems of a computer-controlled system
are involved in this lab exercise:
Data Input Sub-system(s):
A needle as a probe connected directly to pin A0,
with the platform connected to 5V.
Data Processing and Data Storage Sub-systems:
-
Microcontroller: Arduino Mega with ATMega2560-16AU (16Mhz, TQFP Package)
Data Output Sub-systems(s):
3 Piezo based linear actuators, drived by 2 transistor activated RC Circuits.
Data Communication Sub-systems:
-
Low Level: Serial Communcation
-
High Level: USB Communication
Diagrams and pictures (cont.):
-
Software: Block Diagram
-
Explains the loop of the system in a block diagram.
6 Source Code
<AFM-ArduinoCode.ino> - Arduino C Code, for AFM Machine.
char ImageBuffer0[64][64];
#define piezoX 3 // For X axis
#define piezoY 5 // For Y axis
#define piezoZ 7 // Not used if 255 only
// May be expanded for depth later
#define XYenable 10
#define Zenable 11 // Connect Piezo Z directly here
// it stays at 255 or 5V so this allows me make
// the circuit simpler
#define probe A0
// Rewrite this during your calibration process. leave * 4, I prefer to use
// 0 to 255 but did not went to change code for 255, so left it like this.
int upper = 175 * 4; // Sets higher expected value, default is 255.
int lower = 100 * 4; // Sets lower expected value, default is 0.
// sets delay in ms to allow RC filter to charge.
int RCDelay = 10;
void setup() {
// init serial
Serial.begin(115200);
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
pinMode(piezoX, OUTPUT);
pinMode(piezoY, OUTPUT);
pinMode(piezoZ, OUTPUT);
pinMode(XYenable, OUTPUT);
pinMode(Zenable, OUTPUT);
pinMode(probe, INPUT);
}
int GetValue(int getx, int gety){
return ImageBuffer0[getx][gety];
}
void SendViaSerial() {
Serial.print("."); // Signals new image.
for(int ysend=0; ysend < 64; ysend++) {
Serial.print(GetValue(0, ysend));
for(int xsend=1; xsend < 63; xsend++) {
Serial.print(GetValue(xsend, ysend));
Serial.print(","); // next value
}
// next row
Serial.println(GetValue(64, ysend));
}
Serial.print(";"); // to tell the system its done.
}
void moveprobe(char movx, char movy, char movz) {
digitalWrite(Zenable, LOW);
digitalWrite(XYenable, LOW); // Wait until cords are set.
analogWrite(piezoX, movx); // go to cords
analogWrite(piezoY, movy);
analogWrite(piezoZ, movz);
delay(1); // allow rc filter to get to right voltage.
digitalWrite(XYenable, HIGH);
delay(1); // very short delay, so probe does not scratch.
digitalWrite(Zenable, HIGH);
}
void ProbeCord(char movx, char movy) {
// had to make the code downscale by a quarter for it to fit on arduino.
int qmovx = movx*4;
int qmovy = movy*4;
moveprobe(qmovx, qmovy, 255);
char qnn = constrain(map(analogRead(probe), lower, upper, 0, 255), 0, 255);
moveprobe(qmovx+1, qmovy, 255);
char qpn = constrain(map(analogRead(probe), lower, upper, 0, 255), 0, 255);
moveprobe(qmovx, qmovy+1, 255);
char qnp = constrain(map(analogRead(probe), lower, upper, 0, 255), 0, 255);
moveprobe(qmovx+1, qmovy+1, 255);
char qpp = constrain(map(analogRead(probe), lower, upper, 0, 255), 0, 255);
ImageBuffer0[movx][movy] = (qnn+qpn+qnp+qpp)/4;
digitalWrite(Zenable, LOW);
digitalWrite(XYenable, LOW);
}
void TakeImage() {
for(int yscan=0; yscan < 64; yscan++) {
for(int xscan=0; xscan < 64; xscan++) {
ProbeCord(xscan, yscan);
}
analogWrite(piezoX, 0);
delay(RCDelay); // Give time for voltage to go down.
}
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // show that program starts.
TakeImage(); // Take the Atomic Force Image
// send over serial
SendViaSerial();
digitalWrite(LED_BUILTIN, LOW); // To show speed of program
delay(1500); // wait a while before next image.
}
<AFMProcessing.pde> - Processing Code for Image.
import processing.serial.*;
Serial myPort; // Create object from Serial class
int val; // Data received from the serial port
String SerialMsg;
void setup()
{
// 64 by 64 resolution scaled up by 10.
size(640, 640);
// Assumes 1st Serial Port
String portName = Serial.list()[0];
myPort = new Serial(this, portName, 115200);
myPort.bufferUntil(’;’);
}
void draw()
{
String[] rows = new String[65];
String[] columns = new String[65];
if (SerialMsg != null) {
rows = split(SerialMsg, ’\n’);
for(int i=0; i<rows.length; i++) {
columns = split(rows[i], ’,’);
for(int j=0; j < columns.length; j++) {
fill(int(columns[j]));
rect(j*10, i*10, 10, 10);
}
}
SerialMsg = null;
}
}
void serialEvent(Serial myPort) {
SerialMsg = myPort.readStringUntil(’;’);
}
7 Measurements
8 Troubleshooting
I needed to change from a 256x256 int matrix to char matrix then had to settle for a 64x64 char matrix because of memory problems, I compromised by taking a bigger images and downscaling to include all the data. I also had trubble in the processing program, that it displayed nothing, it was solved when I removed the code setting the background that seemed to also blank it.
9 Discussion
9.1 Data Input Sub-Systems:
-
Hardware:
-
Sensor(s) used for data input:
Just a probe connected to A0 and a platform connected to 5V.
-
Interface Circuit(s):
None
-
void ProbeCord(char movx, char movy) {
// had to make the code downscale by a quarter for it to fit on arduino.
int qmovx = movx*4;
int qmovy = movy*4;
moveprobe(qmovx, qmovy, 255);
char qnn = constrain(map(analogRead(probe), lower, upper, 0, 255), 0, 255);
moveprobe(qmovx+1, qmovy, 255);
char qpn = constrain(map(analogRead(probe), lower, upper, 0, 255), 0, 255);
moveprobe(qmovx, qmovy+1, 255);
char qnp = constrain(map(analogRead(probe), lower, upper, 0, 255), 0, 255);
moveprobe(qmovx+1, qmovy+1, 255);
char qpp = constrain(map(analogRead(probe), lower, upper, 0, 255), 0, 255);
ImageBuffer0[movx][movy] = (qnn+qpn+qnp+qpp)/4;
digitalWrite(Zenable, LOW);
digitalWrite(XYenable, LOW);
}
The above code averages the values from A0 in a 4x4 area and stores it, in the 64x64 char matrix.
9.2 Data Processing/Data Storage Subsystems:
9.3 Data Output Sub-systems:
-
Hardware:
-
Output device(s) used: 3 piezo actuators made up of 3 stacked piezo disk for X, Y & Z.
-
Interface Circuit(s): 2 RC circuits made with 47uF, 1000uF Capitors and 2 10kΩ Resistors, for XY connected to 3 & 5, with ground osculated via NPN transistor to allow RC circuits to charge at right voltage connected to pin 10, Z is connected directly between pin 11 and ground.
-
Software:
void moveprobe(char movx, char movy, char movz) {
digitalWrite(Zenable, LOW);
digitalWrite(XYenable, LOW); // Wait until cords are set.
analogWrite(piezoX, movx); // go to cords
analogWrite(piezoY, movy);
analogWrite(piezoZ, movz);
delay(1); // allow rc filter to get to right voltage.
digitalWrite(XYenable, HIGH);
delay(1); // very short delay, so probe does not scratch.
digitalWrite(Zenable, HIGH);
}
It sets the position by giving the RC circuits a PWM signal which is defined 0 to 255, then turning the ground on to allow it to power the piezo actuators.
Zenable lifts the platform, I decided to bypass PiezoZ to make the circuit simple since it stays at 255.
9.4 Data Communications Subsystems:
9.5 Hardware:
-
Output Communcation Interface circuit(s) used:
Just a USB-B cable using Arduinos built in USB controller, It use’s a FTDI chip to convert USB signals to TTL UART Serial.
-
Software:
arduino
void SendViaSerial() {
Serial.print("."); // Signals new image.
for(int ysend=0; ysend < 64; ysend++) {
Serial.print(GetValue(0, ysend));
for(int xsend=1; xsend < 63; xsend++) {
Serial.print(GetValue(xsend, ysend));
Serial.print(","); // next value
}
// next row
Serial.println(GetValue(64, ysend));
}
Serial.print(";"); // to tell the system its done.
}
This code sends the data to processing so it can be inteprated as a image, it works by running a for loop similar to scanning the object, begining with a ’.’ to start, signeling a new row with a new line, and ending it with ’;’. GetValue just concerts the output to int or the arduino would send it as a character instead of a 8 bit number defined in ASCII. this is because char is really short for characters but can also be used for 8bit numbers because in memory its the same thing.
10 Conclusion
This was a very interesting and educational project, while it may have not have been the smartest move to pick something that requried so many custom parts due to the time it took, it is still great because of the usefulness and the way it can be built on. I plan on continueing research on AFM and other nanotechnology. Specifically projects that can be done at home without fancy expensive machines or chemicals, because I believe its the next step for technology.
I am glad I got a interesting Image at the end of this expirement.
Some improvements I would make besides a better microcontroller and software would be a sound proof box or less noisy design, because it was to loud when running almost giving me ringing after the last time meaning that I cant really use it again without hearing protection or other improvements also I may add a normal microscope to see how it actually moves, I wanted to do this but ran out of time, after this I may see how it works on semiconductors.