arduino rotary table for dummies in stock
The stepper motor will have to be sized for your application. I used a small 3 rotary table and dont plan on using it for anything other than indexing so a high torque NEMA17 did the job. If youre working with a larger rotary table or want to be able to use it as a 4th axis in the mill you will want at least a NEMA23 size motor. You will have to reach out to the forum for help with selection.
Ok get out the anti-identity thief, pre-paid Visa card and order all the bits, wait for a month and a half for Canada Customs/Canada Post to figure out it isnt anything nasty and youre ready to begin.
Youll have to install the Arduino software (IDE) on your computer. Spark Fun has a good step by step tutorial for completing the install. https://learn.sparkfun.com/tutorials/installing-arduino-ide
Picked up this old Van Norman 12" rotary table fairly cheap ($50 IIRC) a few months ago. It was missing the dial/crank when I got it. Plan on adding a small clearpath servo and controller to allow simple automation, arc cuts, hole patterns, etc. I"ve seen similar project by both youtubers Oxtools and Stephen Gotteswinter. Arguably not very sophisticated and dubious merit but I wanted a simple project to play with servo control, possibly as a prelude to a DIY cnc plasma cutter. It"ll be a reversible modification so no big deal if its just a learning exercise. First step is to take it apart, clean it up, check it for play, and paint it. This weighs over a 100 lbs so it is a workout to get it in/out of the parts cleaner.
This website is using a security service to protect itself from online attacks. The action you just performed triggered the security solution. There are several actions that could trigger this block including submitting a certain word or phrase, a SQL command or malformed data.
In this tutorial we will learn how rotary encoder works and how to use it with Arduino. You can watch the following video or read the written tutorial below.
A rotary encoder is a type of position sensor which is used for determining the angular position of a rotating shaft. It generates an electrical signal, either analog or digital, according to the rotational movement.
There are many different types of rotary encoders which are classified by either Output Signal or Sensing Technology. The particular rotary encoder that we will use in this tutorial is an incremental rotary encoder and it’s the simplest position sensor to measure rotation.
Any of the two outputs can be used for determining the rotated position if we just count the pulses of the signal. However, if we want to determine the rotation direction as well, we need to consider both signals at the same time.
Let’s make a practical example of it using the Arduino. The particular module that I will use for this example comes on a breakout board and it has five pins. The first pin is the output A, the second pin is the output B, the third pin is the Button pin and of course the other two pins are the VCC and the GND pin.
Description of the code: So first we need to define the pins to which our encoder is connected and define some variables needed for the program. In the setup section we need to define the two pins as inputs, start the serial communication for printing the results on the serial monitor, as well as read the initial value of the output A and put the value into the variable aLastState.
That’s all we need for this example. If upload the code, start the Serial Monitor and start rotating the encoder we will start getting the values in the serial monitor. The particular module that I have makes 30 counts each full cycle.
After reviewing the code from the original project (Reference #3 below), and for the reasons mentioned in the above paragraph, I decided to make some major changes. The revised code can be downloaded below (most recent version is listed first). Older versions may contain known bugs and are kept for archival purposes only.
After writing the "conventional" Rotary Table Control program below (final version of the Arduino_Rotary_Table_Control_2019_Rev7 series), I decided to write a completely new program which would enable stepper motor control with acceleration and deceleration, as well as a number of additional features. For a link to this program, as well as additional related information, see this link:
UPDATE JUNE 15, 2022 :A new program update has been posted at the link below. This program has significant improvements over the "conventional" program listed further below. The new program version is therefore recommended.
Below is a "conventional" program based on the methods of the sketch in the original article in Home Model Engineering Machinist (see references below). This program is believed to work properly and offers the basic functions needed for an indexing controller.
[3-35-2019] Beeper Test programThis is a simple Arduino sketch which can be used to test whether a piezo beeper is working, and also to find the loudest (or otherwise most desirable) tone.
The programs below are included for archival purposes only.Note: Version 7 has the stepper motor settings for my setup hard coded in, but these can be easily changed (and changes permanently store) via the keypad. After loading the program, choose the Settings option to update the settings for your equipment. These settings will be maintained (even after power off) unless the program is re-loaded.
Note: Although I believe this code version handles moving in reverse correctly, it does NOT do any backlash compensation. This means that if you move forward X steps and then reverse X steps you will not return to the original position.
The code changes were made so that when stepper motor and rotary table parameters are entered into the program, calculations will be made in two ways:
The determination of the required number of steps to move is in all cases based on the theoretical steps, which are then converted to actual steps. This method provides the best approximation (typically with an error less than 0.01%), and enables the easy use of gear and table ratios which do not divide exactly into 360.
We are surrounded by rotary encoders without even realizing it, as they are used in so many everyday items, from printers and cameras to CNC machines and robots. The most common application of a rotary encoder is the volume knob on a car radio.
A rotary encoder is a type of position sensor that converts the angular position (rotation) of a knob into an output signal that can be used to determine which direction the knob is turned.
Rotary encoders are classified into two types: absolute and incremental. The absolute encoder reports the exact position of the knob in degrees, whereas the incremental encoder reports the number of increments the shaft has moved.
Potentiometers are used in situations where you need to know the exact position of the knob. Rotary encoders, on the other hand, are used in situations where you need to know the change in position rather than the exact position.
When they make contact with common ground, two signals are generated. These signals are 90° out of phase because one pin makes contact with common ground before the other. It is referred to as quadrature encoding.
When the knob is turned clockwise, the A pin connects to ground before the B pin. When the knob is turned anticlockwise, the B pin connects to ground before the A pin.
Let’s hook up the rotary encoder to the Arduino. The connections are quite simple. Begin by connecting the module’s +V pin to the Arduino’s 5V output and the GND pin to ground.
In the setup section, we first configure the rotary encoder connections as inputs, and then we enable the input pull-up on the SW pin. We also set up the serial monitor.
The next step involves reading and debouncing the push button switch. We first read the current button state, and when it changes to LOW, we wait 50 ms for the push button to debounce.
If the button remains LOW for more than 50ms, it indicates that it has really been pressed. As a result, we print “Button pressed!” to the serial monitor.
One way to detect these changes is to poll them continuously, as we did in our previous sketch. It is, however, not the best solution for the following reasons.
With interrupts, there is no need to continuously poll for a specific event. This frees up the Arduino to perform other tasks without missing an event.
Because most Arduino boards (including the Arduino UNO) only have two external interrupts, we can only monitor changes in the DT and CLK signals. Therefore, we will remove the SW pin connection.
Some boards (such as the Arduino Mega 2560) have more external interrupts than others. If you have one of these, you can keep the SW pin connection and modify the sketch below to include the push button code.
This sketch simply monitors digital pins 2 (corresponding to interrupt 0) and 3 (corresponding to interrupt 1) for signal changes. In other words, it detects when the voltage changes from HIGH to LOW or LOW to HIGH as a result of turning the knob.
When a change occurs, the Arduino immediately detects it, saves its execution state, executes the function updateEncoder() (also known as the Interrupt Service Routine or simply ISR), and then returns to whatever it was doing before.
The following two lines configure the interrupts. The attachInterrupt() function instructs the Arduino which pin to monitor, which ISR to execute when the interrupt is triggered, and what type of trigger to look for.
This project can be very useful in a variety of situations. For example, if you want to operate a robotic arm, it can help you position the arm and its grip accurately.
You can, of course, use the Arduino’s 5V output, but keep in mind that the servo may induce electrical noise on the 5V supply line, which could damage your Arduino. Therefore, it is advised that you use an external power supply.
Here is the code for using the rotary encoder to precisely control the servo motor. Each time the knob is rotated one detent (click), the position of the servo arm changes by one degree.
II. Earnings Table: Earnings Estimate, Earnings Actual, reported date, YOY Growth %, Surprise % of the following periods: TTM, Q(0), Q(-1), Q(-2), Q(-3), Y(-1), Y(-2), Y(-3)
III. Sales Table: 1. Sales Estimate, Sales Actual, reported date, YOY Growth %, Surprise % of the following periods: TTM, Q(0), Q(-1), Q(-2), Q(-3), Y(-1), Y(-2), Y(-3)
You need to create an ebook version for kindle readers of my book, then a table of content. You need to organise the book, 2 story cannot be on the same page.
But there is a problem. My search function from datatable searches only from first page not from second or others (i use pagination). I need to make it searchable from all the pages... Any help would be appricieted..
...virtual assistant or creative writer of some sort to chat with me about what it would be like to have a boxing match with her. She has to be detail oriented and not be afraid to include the little details about boxing. What I"m not looking for is a person that just replies with one or two words like "ok" and expects me to reply to that. Preferrably a strong Filipina freelancer with a mean uppercut that is confident that they can really give me a hard time in the boxing ring/ boxing chat. Someone very talkative and creative that can bring a lot of ideas to the table would be ideal. Even better if you know other languages
I am looking for a talented web developer who can help bring my mushroom business to the digital world. I am looking to create a website that will showcase our farm-to-table gourmet mushrooms and allow for easy online ordering for both delivery and subscription services. I already have the domain ready and now I need someone to make my website vision a reality. If this interests you, I"d love to discuss details, please reach out to me to discuss further details and see if we could work together to bring my mushroom business to the next level.
...virtual assistant or creative writer of some sort to chat with me about what it would be like to have a boxing match with her. She has to be detail oriented and not be afraid to include the little details about boxing. What I"m not looking for is a person that just replies with one or two words like "ok" and expects me to reply to that. Preferrably a strong Filipina freelancer with a mean uppercut that is confident that they can really give me a hard time in the boxing ring/ boxing chat. Someone very talkative and creative that can bring a lot of ideas to the table would be ideal. Even better if you know other languages
I have a Xamarin Mobile App using VS2022, C# built previously that needs some enhancements and ongoing development. The APP is used in IOS and Android. The current version is 1.0. I want this project to be released as 2.0 (See Table 1 below)
I would like to employ someone who has experience in Xamarin with Visual Studio to enhance the mobile App. This project is to perform some minor improvements. Once we complete this project, I will award the same developer ongoing changes and releases if the developer is responsive and is able to work fluently with me to complete tasks on the mobile application.
I have money exchange program web form app and want to add 1 more table to capture the customer details and implement Arabic language to it beside the English one. Please contact me for details
MVP Brewing Company started in late 2020. Our company consists of 3 Owners with no employees. I myself was a sales Manager of a brewery before I started selling cars full-time. Chris Otten, owns a construction company that builds houses and is now a full-time firefighter in Vaughn. Martin Vandenhoven owns a farm chore relief company. We each bring our own expertise to the table and things are running pretty smooth. We currently have an LCBO listing, grocery store listing and many beer store locations across Ontario. We currently sell out of few restaurants and bars as well. We have our license and approval to sell our beer in Alberta. We also acquired our distilling license to start creating vodka beverages. We have another beer in the works as well, a light lager....
Using the available public libraries create a bluetooth peripheral for the PDM microphone, pair the microphone and stream the microphone audio to the host.
Using the available public libraries create a bluetooth peripheral for a MS5837 pressure sensor pair and and publish the calibrated pressure and temperature to the host on an interval set by a pushed value from the host.
we have issue with data receiving in mysql from et300 gps tracker using gt06 protocol. Sometimes its receiving data sometimes not and sometimes slow response, while the login request is coming properly. Only issue with receiving data in location table.
I"m looking for a creative writer of some sort to discuss the sport of boxing with. She has to be detail oriented and not be afraid to include the little details about boxing. What I"m not looking for is a person that just replies with one or two words like "ok" and expects me to reply to that. Preferably someone very talkative and creative that can bring a lot of ideas and be consistent throughout this.
A big plus would be if the freelancer could pick a female character she"s familiar with from a videogame, anime, cartoon or any form of media. The idea would be to pretend you were boxing as that character in the writing at times. You would be mimicking the character at times. If you could bring a list of female characters to the table it wou...
Furniture and Product designers who can create original concepts and designs for solid wood case goods. We are a furniture factory which is manufacturing furniture for international markets. We make furniture out of solid wood.
AutoCAD with measures and details followed by 3D models. The final delivery maybe renders and Max File and all AutoCAD must be submitted in PDF format
This fact table will be used later for the implementation of the data warehouse. (You don"t have to implement the data warehouse, just generate the fact table using star or snowflake.)
The fact table must be designed so that the company can know how its sales are evolving over time and what type of demand is associated with each of the products.
The formatting becomes messed up and the tables do not look presentable at all. The columns can become too narrow and the table fills up too many pages (40+ pages!!). The goal is to create two well-formatted, readable and presentable tables in Word based on the two Excel tables I am providing you.
1). Logic to create a new work order based on a previously completed work order. For example, if a technician completed work at a customers home, we may want to create a follow up “audit” work order so his manager can inspect what was done.
I"m looking for a developer who can help with Arduino+VR project. I need to develop a haptic glove using Arduino and connect it to Unity VR. And the glove should be connected to Oculus via Bluetooth.
...currently calculates the price of printing according to the property and the price table. It will only offer printing technologies that are listed with the product. I would like to make it possible to choose more printing options - according to other properties. I am attaching a link to the product as an example
I have two tables in a chartink dashboard chartink supports downloading table data in CSV format. The requirement is to capture CSV data every minute and then Marge the data into one single CSV file after every one hour or specified duration. Dashboard link:
2) Stop repainting for alerts: At the moment the script is checking if buy condition "is true" during the 9:15am candle and then it sends alert as well as plots on the chart the buy signal and the same in a small table generated on the chart. However because of repainting it will show the signal for one second then dissapears; only when the candle closes then the final fixed signal stays. Need to fix it so that when the signal appears during the 9:15am candle it will stay and the signal in the table does not go away even before candle close.
Want to authenticate from different menthods mentioned in step 1.1. Then convert xml and json file request/response into class mentioned in step 1.2. In step 2. class response push into sql database table. 3. export the xml/json from sql db table into same format at step no. 1.2. But with the mapped data in same sequence.
Hi Gaurav, as per discussion, this project is to create the DISTINCT entries for Operator >> Region >> Sub-Region >> Cluster >> Sub-Cluster from the CLIENT SITE LIST
We need to create a School Fee template in our application for that we have UI ready and API is ready now. We want to display the row and column field names based on settings value and save the inputs we given in the page.
1. Based on the screenshot, we want the Fee amount to be saved in Transport Fee and Tuition Fees section table (the column and row field names should be coming from settings values)
This tool nearby booking trigger improves the efficiency of booking placements by sending out emails and SMS, to customers in your database that live within a 10km radius and who’s child has a birthday 7 days either side of every new booking to fill the slots for that day.
I recently read an article in Model Engineers Workshop Magazine (December 2016 issue 249) for adding a stepper motor drive to a rotary table. I don′t use my small Vertex rotary table very often but I thought this might be a useful project to learn a little about stepper motors and digital control of machinery. The article by Carl Wilson describes how to use an Arduino micro-controller to control the rotary division process. Much of the coding is contained in another article in Digital Machinist by Gary Liming. So no original thinking by me here, just a rehash of other engineers good work. All the links and useful information can be found in the Glossary at the end.
Prepping the table can be as simple as doing nothing, a complete strip down and re-build with thrust washers and the like or somewhere in between. There are a few articles on the web giving details (see glossary). Software setup and programming the Arduino is the quickest part and if you use Gary Liming′s software without alteration, the programming takes just a few seconds. Assembling the electronics is mainly about fitting the bits into the box and requires a bit of inginuity to fix things in place. The boards are fairly flimsy and things, like the display, tend not to be square or flat, I found.
The stepper motor mounting I made from three parts and assembled with Loctite and screws. I have no doubt there are other ways of making this or a suitable motor mount could be found ready made and adapted to fit the table. You will probably want to test things as you you go along rather than leave everything to the end. I discovered I had a faulty motor driver, easier to deal with whilst still uncased. I don′t think it makes makes any difference which order things are done.
This is covered elsewhere on the web in some detail so I have just made a few notes that may be of interest. Dismantling the table is quite straightforward, just look for allen headed grub screws at the bottom of deep holes. The notes refer to my 4" Vertex table.
Start off by removing the handle, the table locking clamps and the worm engagement lock. The handle is just one screw but watch out for the shaft key which is small and easily lost. Photo (2) shows the board I made to store the table with a cutout for the handle. The stepper motor will also need a similar storage solution. I used pliers and some cardboard to protect the finish to unscrew the table clamp handles. Remove the engagement lever and collar, two grub screws and it slides off, this is the part that the motor connector will attach to, it has three ready tapped holes for when used with division plates.
Remove the cam shaft securing and adjusting collar (4), four cap screws. Remove the grub screw that sets the worm engagement depth, found at the bottom of a deep hole (5). The worm shaft and cam bearing can now be removed as one unit, rotate the table and it will push the spindle out.
Turn the table upside down and remove the table bearing and adjustment plate (6), four cap screws. The table can now be removed, mine was pretty clean (7) not having been used much, there wasn′t even that much grease. Now that everything is apart it can all be cleaned re-greased and re-assembled. The worm drive shaft can be slid out of the cam adjuster by removing the collar, it is a ground shaft with an oilway and a very good fit in the cam adjuster.
Other than adjustment to remove backlash I didn′t make any changes to my rotary table, it was in fact pretty good before I started. If you have an older well used table it may take a bit more cleaning to remove old grease and any swarf that may have found it′s way inside.
The collar on the worm shaft acts as a thrust bearing and needs to be adjusted so that it is free to rotate but has no end play. You can just make out in photo (8) that there is a washer below it, this is a wave spring washer and provides a bit of tension to restrict the lateral motion. If you tighten the collar too much the shaft will lock up. Once set-up the collar is locked in place with two grub screws. The screws tighten directly onto the threaded portion of the worm shaft, this had caused some damage to the thread which needed cleaning up with a needle file. I made a couple of brass pads from some shim (9) (cut out with a leather punch) to prevent more damage, they are a bit thin but there isn′t room for much more.
Other parts worth note are the cam shaft retainer / bearing (4) and the table retainer / bearing (6). These both feature four cap screws which bolt the item in place and four grub screws which act as jack-screws to prevent clamping the rotating part. When reassembling it is worth adjusting these carefully to limit the table lifting whilst still turning freely and likewise to prevent the cam shaft moving in and out. I noticed with the table bearing / retainer that there was a noticeable stiff spot so it is worth rotating the table through a full 360° whilst adjusting. The cam shaft could be locked in place if you think there is no need to disengage the worm gear. Last bit is to set the worm engagement, this is controlled by a grub screw at the side (5) which engages with a slot in the cam shaft to prevent rotation. If you undo the grub screw and fully engage the worm it will be very difficult to turn, tighten the grub screw just enough so that the worm turns easily with a minimum of backlash.
Not much to this really but first you will need to go to the Arduino website and download the Integrated Development Environment (IDE) software. This is basically a fairly lightweight program that runs on your PC (Windows, Mac or Linux) and allows you to edit programs (sketches in Arduino speak) and upload to the Arduino board. You will also need to download Gary Liming′s software. Once the software is downloaded installation is straightforward. The Arduino IDE is self-installing from an exe file in Windows. Gary Liming′s programs come as a zip file which needs un-zipping to a folder. Once unzipped, double click on the "Stepindex23.ino" file and it will start the Arduino IDE and load the program.
All being well you should now have a screen like something like those above. Click on the image to read the text. Affix the LCD shield to the Uno making sure that all the pins are in the right places and none of the connectors is bent. Plug the Uno into the PC using a USB cable, often supplied with the board. The Uno will be powered by the USB connection. First thing to do is go to the "Tools" menu (10) and set the type of board. All being well the software should then report that it is talking to the Uno, bottom right of the IDE, something like "Arduino/Genuino UNO on COM4". You should also be able to click on the "Port" section of the menu to assign a COM port. If this isn′t working and the Port section is greyed out it may be driver related.
If you are using a genuine Uno board (possibly some clones) the USB driver is loaded with the IDE and once the board is selected in the menu everything works. Some copies of the the Uno use a different USB driver chip and the driver needs installing manually (see Glossary for link). Once the correct driver is installed the Port section of the Tools menu should be enabled and you can then select which COM port to use. (Note if you use a different USB port next time around you need to reselect the COM port). Click the upload button to copy the program onto the Uno, thats it. The program will auto-run and briefly display the start-up screen before waiting at the main menu for input. Chances are you won"t see anything as the LCD shield contrast probably needs setting, twiddle the multi-turn pot (variable resistor) above the display until the screen comes to life.
Arduino micro-controllers are mainly programmed using the C++ programming language or at least a subset of C++, so the programs are fairly understandable for basic editing. The first few lines of the program (11) are used to set parameters used later. These can be adjusted now, the program is well commented, or left until later when everything is assembled. You may wish to alter gear ratios or even remove some items. There is more help in the readme files that come in the program zip-file. If you are a C++ programmer the world is your oyster, the menu items can be moved around or even removed if you don′t need a particular function. You may wish to experiment with some of the delay timings to help de-bounce the keys but this is probably better done during final testing.
The parts are shown (12) above and are the Arduino Uno, the LCD shield, the cable gland, TB6560 stepper driver, switches, plug, socket and power supply. The circuit boards are all pretty flimsy and the mounting holes are very close to the edges. The LCD shield has a seperate smaller board for the LCD soldered on top and the two boards were not particularly parallel. There is also a multi-turn variable resistor on the board which cunningly sticks up higher than the LCD face. If you are adept with a soldering iron it can be re-positioned on the other side of the PCB. I solved the non-flush pot problem by using a 1.5mm clear polycarbonate sheet between the box lid and LCD with a small cutout for the variable resistor.
I fitted as much as I could to the box lid, only the mains in and stepper out are fitted to the box. The display needs a cutout in the lid as do the three switches and a number of 3mm holes for various mounting screws. Once I had worked out the position of all the bits I marked the inside of the box lid for the position of the LCD and switch cutouts. I set this up on the mill and used a 5mm slot drill to remove the cutouts, the ABS machines very easily. I fixed the lid to an off-cut of MDF with woodscrews through the mounting holes, I also used double sided tape to make sure nothing moved. A couple of T-nuts and studs fixed the MDF to the mill table (13). With hindsight the double sided tape was overkill, it took me longer to get it off the lid than it did to do the machining. The corners of the switch cutouts I filed square, I drilled the various mounting bolt holes by hand as I did for the other round holes opening them up as necessary with a taper reamer and file.
The diagram (16) above shows the inter-connections between the three main parts. Check carefully when soldering to the Uno and the LCD shield as the pins are close together, also check with data sheets that all the wires go to the right places. The diagram does not show the switch leads nor are the board connections in their exact positions (Diagram NOT To Scale). I found it easier to solder a short length of coloured wire to each location on the Uno / LCD shield whilst all the boards were out of the case. I made a note of which colour wire went where and then bolted the boards in place. The wires can then be grouped together, switch leads, control leads and power. I used some cable ties to try and keep things neat(ish) and then trimmed the wires to length ready to connect up. To extend the switches from the LCD shield I soldered a wire to the back of each miniature push button on the board. You need one wire for each switch plus one common wire. I just used a multimeter to find the right solder terminal on the switches.
I have included a drawing that shows how I made the mount, you may need to adjust dimensions to suit the components you have. I used 6082 T6 aluminium ¼" (6.35mm) plate to make the two ends of the connector and a length of 2" x ¼" (50.8MM x 6.35mm) thick wall tube for the middle bit. It is often still easier to get material in imperial sizes, come to that the stepper motor is imperial size as well.
I made the two flat plates and then fitted the motor, flexible coupling and rotary table together to measure the shortest length of tube that would work. The dimensions for the motor mounting plate were copied from the motor spec sheet.
The motor plate is from a length of 3" x ¼" bar, cut to length and then mount flat on parallels in the milling machine vice. Clean up one cut end then reverse and mill to length. Turn 90° clean up the edge reverse and mill to length. With the part still in the vice use an edge finder in X and Y directions to position first hole for drilling use co-ordinate drilling to position the remaining 3 holes (19) and a centre hole (much easier with a DRO, I still count turns). The centre hole will be the motor register. Drill and bore this out to 38.1mm (20) I found the easiest way to check the diameter was using a short length of 1.5" (38.1mm) bar. Strange all the dimensions for the stepper motor are given in mm but they are definitely made with imperial measurements, oh well provided everything fits together!
That was the first time I had used the boring head in the mill and I tried to use it to cut the recess for the tube. This didn′t work too well as you can′t really get a flat bottom to the recess. I remounted the plate in the four-jaw chuck on the lathe (21) with the motor register running true and completed the recess, 3mm deep and to suit the tube diameter, with a boring bar.
I used another bit of 3" x ¼" bar to turn the plate that bolts to the rotary table. I drilled a 10mm hole in the centre of the plate and used a length of studding to hold it (22). The studding has two nuts locked to it which fit against the back of the chuck jaws and a nut and washer clamp the plate against the front of the jaws, there is a centre in the outboard end of the studding for support. I used a trepanning tool to remove the corners and then turned the O.D. to to size.
When the R.T. mounting plate is the correct diameter add a step 3mm deep with 38.1mm diameter to create a short spigot to fit the tube bore. Remove from the mandrel (studding) and mount holding the just turned spigot (23), bore out the centre hole to 21mm to fit the R.T. collar. To finish this part it need the three mounting holes drilled to match the table. I clamped the table index ring to the plate, they should be the same diameter, then spotted through with a drill that just cleared the threads in the index ring. Unclamp and drill the holes 5mm, there is no other alignment so keep the holes small, don"t use an M5 clearance drill.
The three parts are "glued" together, I used Loctite 603 which is a high strength oil tolerant retainer. Check alignment before joining, it will depend on the orientation of the holes in the index collar on the R.T. probably easier to join the tube to the table end first and then bolt it in place. The motor mount can then be aligned so that it is square when in use. I had an interesting experience when I first tried assembly. applied the Loctite placed suitable weight on top and left overnight. The following day removed the weight picked it up and it came apart. Apparently Loctite "goes off" still mine was a few years old! If you want to add screws it is probably easier to do this after assembly, I used 3 M3 C/S screws in each end, a bit belt and braces as either screws or adhesive alone will probably do the job.
Once the two end plates are in place the slot to access the coupling can be cut (25). In stages starting with an 8mm slot drill, using a 20mm end mill is really pushing this tiny mill! I thought the best place for this was on the underside so that swarf will fall out rather than in. You may need to adjust the slot to suit the fixings in the coupling. I used an aluminium flexible coupling (26) which has both a split collar clamp and a grub screw.
Not much to this really, first bolt the connector to the rotary table. Slide in the flexible coupling and tighten onto the table drive, I aligned it so that the grub screw would tighten into the keyway. Fit the motor using four M5 capscews, nuts and shakeproof washers. Tighten the coupling onto the motor shaft and thats the mechanical bit done.
To test I went through each menu item in turn and made sure it did what it was supposed to. I discovered that clockwise and anti-clockwise were reversed but this can be adjusted in the software. I also discovered that I had wired one switch back to front and needed to reverse the leads fortunately just swapping a couple of push on connectors. Found that the motor vibrated rather heavily, haven′t got to the cause of that yet. I also set the table to zero on it′s scale and checked that the angle turned matched what the display said for a full 360° - it did.
With a bit of work on the software, to slow the motor down, I don′t see why the table could not be operated under power, to mill say a semi-circular slot. WIll also need a bit of work on the switch de-bounce software for this to ensure reliability, as it is it is easy to double press keys. Nice little project a good introduction to both the Arduino and to stepper motors neither of which I had used before.
New driver module (27) is enclosed and all the terminals are at one end so I had to do a bit of redesign to fit it in. No problem with the wiring but I had to bend up a bracket from a bit of aluminium sheet to hold it in place. The bracket (28) uses the old fixing holes and is bent to give a bit of room for ventilation of the power supply.
As I had to take everything apart I added a reset button (29) by soldering leads to the back of the shield button in the same way as for the other buttons. Caused me some aggravation as the first button I found in my "bits that will be useful one day box" remained steadfastly open-circuit when pressed, still it was probably 30 years old! Last but not least a short video (30) which shows the table spinning quietly in run mode and then vibrating in step and angle mode. It makes me think this might be software generated as that is the only difference between the modes.
Model Engineers Workshop Forum- thread discussing the original magazine article and various points arising including some useful information about variations in the Arduino hardware, particularly the LCD shield.
Step Indexer- software download for the original Digital Machinist article. The ZIP file includes three versions of the code and various text notes. The software is, I believe, in the public domain with a GNU licence.
Gary Liming′s Website- describes the making of the original step-indexer which could be used in place of a rotary table and outlines the software in a bit more detail.
Arduino Home Page- has all the information about the Arduino project. You can download the IDE (Integrated Development Environment) from here which you will need to program the micro-controller board.
CH340G driver- Some boards use the CH340G USB/serial chip as a cheaper alternative to the FTDI chip, this is the driver download link. The FTDI standard driver is installed when you setup the Arduino IDE.
LCD Keypad Shield- information and pinout diagram for the LCD shield. Be aware that different makes of board have slightly different components and working voltages for the keypad resistor chain, see the model engineer discussion thread if your keypad doesn′t seem to work correctly.
Model Engine Maker Forum- thread covering the preparation of a Vertex rotary table ready for automation. This was done by John "Bogstandard" Moore in readiness for the Division Master system but the mechanics are the same.
Stepper Motor Data- This is the specification sheet and wiring diagram for a similar motor to the one I used which is no longer available (2020). Any Nema 23 size motor around the 2Nm holding torque should be quite sufficient indeed a smaller motor may suffice if you have one to hand. My original motor was 8-wire but a 4-wire motor is just as good and will avoid some soldering.
The list above is for the major parts required for the project. The suppliers are those I used and the prices were correct in January 2017. (Please note the links to some of these items seem to change weekly, apologies if they don′t work) I make no particular recommendation as to the suppliers it is just where I found the bits needed, it is likely that better/cheaper/different parts are available from myriad locations on the web. In addition to the bits listed you will need - hook-up wire, solder, nuts, bolts, spacers, cable ties, crimp connectors and sleeving. Please note that the above table doesn"t display well on a small screen, try rotating to landscape to view!
Many of the links in the Glossary and particularly the Parts List table have gone missing over time so I have tried to update them with currently available parts and information. In fact none of the parts are particularly critical and a bit of web searching will find suitable replacements. The Model Engineer Forum link is still active and one of the later additions is the replacement of the switches with a cheaply available numeric keypad. I haven"t carried out this mod but it looks quite interesting.
I have also been told that the TB6560 has an inherent quirk, I quote "It may be of interest to you to know these modules apply power to the IC in the wrong order. The manufacturers specification clearly states the 5v logic voltage should be applied and allowed to settle before the higher stepper motor voltage is applied. On these modules the 5v is derived from the (say) 24v supply which compromises the “power-up” sequence and has resulted in “blown” chips." It may therefore be prudent to avoid this and use the TB6600 driver module.
This project is an upgrade to the electronic rotary table we built in a previous project. That project covers the electronics and software more in depth.
Along 3 years I have been trying several leg mechanism, at first I decided to do a simple desing with tibial motor where placed on femur joint.This design had several problems, like it wasn"t very robust and the most importat is that having the motor (with big mass) that far from the rotating axis, caused that in some movements it generate unwanted dynamics to the robot body, making controlability worse.New version have both motors of femur/tibial limb at coxa frame, this ends with a very simple setup and at the same time, the heaviest masses of the mechanism are centered to the rotating axis of coxa limb, so even though the leg do fast movements, inertias won"t be strong enough to affect the hole robot mass, achieving more agility.Inverse Kinematics of the mechanismAfter building it I notice that this mechanism was very special for another reason, at the domain the leg normally moves, it acts as a diferential mecanism, this means that torque is almost all the time shared between both motor of the longer limbs. That was an improvent since with the old mechanism tibial motor had to hold most of the weight and it was more forced than the one for femur.To visualize this, for the same movement, we can see how tibial motor must travel more arc of angel that the one on the new version.In order to solve this mechanism, just some trigonometry is needed. Combining both cosine and sine laws, we can obtain desired angle (the one between femur and tibia) with respect to the angle the motor must achieve.Observing these equations, with can notice that this angle (the one between femur and tibia) depends on both servos angles, which means both motors are contributing to the movement of the tibia.Calibration of servosAnother useful thing to do if we want to control servo precisely is to print a calibration tool for our set up. As shown in the image below, in order to know where real angles are located, angle protactor is placer just in the origin of the rotating joint, and choosing 2 know angles we can match PWM signal to the real angles we want to manipulate simply doing a lineal relation between angles and PWM pulse length.Then a simple program in the serial console can be wrtten to let the user move the motor to the desired angle. This way the calibration process is only about placing motor at certain position and everything is done and we won"t need to manually introduce random values that can be a very tedious task.With this I have achieved very good calibrations on motors, which cause the robot to be very simetrial making the hole system more predictable. Also the calibration procedure now is very easy to do, as all calculations are done automatically. Check Section 1 for the example code for calibration.More about this can be seen in the video below, where all the building process is shown as well as the new leg in action.SECTION 1:In the example code below, you can see how calibration protocol works, it is just a function called calibrationSecuence() which do all the work until calibration is finished. So you only need to call it one time to enter calibration loop, for example by sending a "c" character thought the serial console.Also some useful function are used, like moving motor directly with analogWrite functions which all the calculations involved, this is a good point since no interrupts are used.This code also have the feature to calibrate the potentiometer coming from each motor.#define MAX_PULSE 2500 #define MIN_PULSE 560 /*---------------SERVO PIN DEFINITION------------------------*/ int m1 = 6;//FR int m2 = 5; int m3 = 4; int m4 = 28;//FL int m5 = 29; int m6 = 36; int m7 = 3;//BR int m8 = 2; int m9 = 1; int m10 = 7;//BL int m11 = 24; int m12 = 25; int m13 = 0;//BODY /*----------------- CALIBRATION PARAMETERS OF EACH SERVO -----------------*/ double lowLim[13] = {50, 30, 30, 50, 30, 30, 50, 30, 30, 50, 30, 30, 70}; double highLim[13] = {130, 150, 150, 130, 150, 150, 130, 150, 150, 130, 150, 150, 110}; double a[13] = { -1.08333, -1.06667, -1.07778, //FR -1.03333, 0.97778, 1.01111, //FL 1.03333, 1.05556, 1.07778, //BR 1.07500, -1.07778, -1.00000, //BL 1.06250 }; double b[13] = {179.0, 192.0, 194.5, //FR 193.0, 5.5, -7.5, //FL 7.0, -17.0, -16.0, //BR -13.5, 191.5, 157.0, //BL -0.875 }; double ae[13] = {0.20292, 0.20317, 0.19904 , 0.21256, -0.22492, -0.21321, -0.21047, -0.20355, -0.20095, -0.20265, 0.19904, 0.20337, -0.20226 }; double be[13] = { -18.59717, -5.70512, -2.51697, -5.75856, 197.29411, 202.72169, 185.96931, 204.11902, 199.38663, 197.89534, -5.33768, -32.23424, 187.48058 }; /*--------Corresponding angles you want to meassure at in your system-----------*/ double x1[13] = {120, 135, 90, 60, 135 , 90, 120, 135, 90, 60, 135, 90, 110}; //this will be the first angle you will meassure double x2[13] = {60, 90, 135, 120, 90, 135, 60, 90, 135, 120, 90, 135, 70};//this will be the second angle you will meassure for calibration /*--------You can define a motor tag for each servo--------*/ String motorTag[13] = {"FR coxa", "FR femur", "FR tibia", "FL coxa", "FL femur", "FL tibia", "BR coxa", "BR femur", "BR tibia", "BL coxa", "BL femur", "BL tibia", "Body angle" }; double ang1[13] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; double ang2[13] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; float xi[500]; float yi[500]; float fineAngle; float fineL; float fineH; int motorPin; int motor = 0; float calibrationAngle; float res = 1.0; float ares = 0.5; float bres = 1.0; float cres = 4.0; float rawAngle; float orawAngle; char cm; char answer; bool interp = false; bool question = true; bool swing = false; int i; double eang; int freq = 100; // PWM frecuency can be choosen here. void connectServos() { analogWriteFrequency(m1, freq); //FR coxa digitalWrite(m1, LOW); pinMode(m1, OUTPUT); analogWriteFrequency(m2, freq); //femur digitalWrite(m2, LOW); pinMode(m2, OUTPUT); analogWriteFrequency(m3, freq); //tibia digitalWrite(m3, LOW); pinMode(m3, OUTPUT); analogWriteFrequency(m4, freq); //FL coxa digitalWrite(m4, LOW); pinMode(m4, OUTPUT); analogWriteFrequency(m5, freq); //femur digitalWrite(m5, LOW); pinMode(m5, OUTPUT); analogWriteFrequency(m6, freq); //tibia digitalWrite(m6, LOW); pinMode(m6, OUTPUT); analogWriteFrequency(m7, freq); //FR coxa digitalWrite(m7, LOW); pinMode(m7, OUTPUT); analogWriteFrequency(m8, freq); //femur digitalWrite(m8, LOW); pinMode(m8, OUTPUT); analogWriteFrequency(m9, freq); //tibia digitalWrite(m9, LOW); pinMode(m9, OUTPUT); analogWriteFrequency(m10, freq); //FR coxa digitalWrite(m10, LOW); pinMode(m10, OUTPUT); analogWriteFrequency(m11, freq); //femur digitalWrite(m11, LOW); pinMode(m11, OUTPUT); analogWriteFrequency(m12, freq); //tibia digitalWrite(m12, LOW); pinMode(m12, OUTPUT); analogWriteFrequency(m13, freq); //body digitalWrite(m13, LOW); pinMode(m13, OUTPUT); } void servoWrite(int pin , double angle) { float T = 1000000.0f / freq; float usec = float(MAX_PULSE - MIN_PULSE) * (angle / 180.0) + (float)MIN_PULSE; uint32_t duty = int(usec / T * 4096.0f); analogWrite(pin , duty); } double checkLimits(double angle , double lowLim , double highLim) { if ( angle >= highLim ) { angle = highLim; } if ( angle <= lowLim ) { angle = lowLim; } return angle; } int motorInfo(int i) { enc1 , enc2 , enc3 , enc4 , enc5 , enc6 , enc7 , enc8 , enc9 , enc10 , enc11 , enc12 , enc13 = readEncoders(); if (i == 0) { rawAngle = enc1; motorPin = m1; } else if (i == 1) { rawAngle = enc2; motorPin = m2; } else if (i == 2) { rawAngle = enc3; motorPin = m3; } else if (i == 3) { rawAngle = enc4; motorPin = m4; } else if (i == 4) { rawAngle = enc5; motorPin = m5; } else if (i == 5) { rawAngle = enc6; motorPin = m6; } else if (i == 6) { rawAngle = enc7; motorPin = m7; } else if (i == 7) { rawAngle = enc8; motorPin = m8; } else if (i == 8) { rawAngle = enc9; motorPin = m9; } else if (i == 9) { rawAngle = enc10; motorPin = m10; } else if (i == 10) { rawAngle = enc11; motorPin = m11; } else if (i == 11) { rawAngle = enc12; motorPin = m12; } else if (i == 12) { rawAngle = enc13; motorPin = m13; } return rawAngle , motorPin; } void moveServos(double angleBody , struct vector anglesServoFR , struct vector anglesServoFL , struct vector anglesServoBR , struct vector anglesServoBL) { //FR anglesServoFR.tetta = checkLimits(anglesServoFR.tetta , lowLim[0] , highLim[0]); fineAngle = a[0] * anglesServoFR.tetta + b[0]; servoWrite(m1 , fineAngle); anglesServoFR.alpha = checkLimits(anglesServoFR.alpha , lowLim[1] , highLim[1]); fineAngle = a[1] * anglesServoFR.alpha + b[1]; servoWrite(m2 , fineAngle); anglesServoFR.gamma = checkLimits(anglesServoFR.gamma , lowLim[2] , highLim[2]); fineAngle = a[2] * anglesServoFR.gamma + b[2]; servoWrite(m3 , fineAngle); //FL anglesServoFL.tetta = checkLimits(anglesServoFL.tetta , lowLim[3] , highLim[3]); fineAngle = a[3] * anglesServoFL.tetta + b[3]; servoWrite(m4 , fineAngle); anglesServoFL.alpha = checkLimits(anglesServoFL.alpha , lowLim[4] , highLim[4]); fineAngle = a[4] * anglesServoFL.alpha + b[4]; servoWrite(m5 , fineAngle); anglesServoFL.gamma = checkLimits(anglesServoFL.gamma , lowLim[5] , highLim[5]); fineAngle = a[5] * anglesServoFL.gamma + b[5]; servoWrite(m6 , fineAngle); //BR anglesServoBR.tetta = checkLimits(anglesServoBR.tetta , lowLim[6] , highLim[6]); fineAngle = a[6] * anglesServoBR.tetta + b[6]; servoWrite(m7 , fineAngle); anglesServoBR.alpha = checkLimits(anglesServoBR.alpha , lowLim[7] , highLim[7]); fineAngle = a[7] * anglesServoBR.alpha + b[7]; servoWrite(m8 , fineAngle); anglesServoBR.gamma = checkLimits(anglesServoBR.gamma , lowLim[8] , highLim[8]); fineAngle = a[8] * anglesServoBR.gamma + b[8]; servoWrite(m9 , fineAngle); //BL anglesServoBL.tetta = checkLimits(anglesServoBL.tetta , lowLim[9] , highLim[9]); fineAngle = a[9] * anglesServoBL.tetta + b[9]; servoWrite(m10 , fineAngle); anglesServoBL.alpha = checkLimits(anglesServoBL.alpha , lowLim[10] , highLim[10]); fineAngle = a[10] * anglesServoBL.alpha + b[10]; servoWrite(m11 , fineAngle); anglesServoBL.gamma = checkLimits(anglesServoBL.gamma , lowLim[11] , highLim[11]); fineAngle = a[11] * anglesServoBL.gamma + b[11]; servoWrite(m12 , fineAngle); //BODY angleBody = checkLimits(angleBody , lowLim[12] , highLim[12]); fineAngle = a[12] * angleBody + b[12]; servoWrite(m13 , fineAngle); } double readEncoderAngles() { enc1 , enc2 , enc3 , enc4 , enc5 , enc6 , enc7 , enc8 , enc9 , enc10 , enc11 , enc12 , enc13 = readEncoders(); eang1 = ae[0] * enc1 + be[0]; eang2 = ae[1] * enc2 + be[1]; eang3 = ae[2] * enc3 + be[2]; eang4 = ae[3] * enc4 + be[3]; eang5 = ae[4] * enc5 + be[4]; eang6 = ae[5] * enc6 + be[5]; eang7 = ae[6] * enc7 + be[6]; eang8 = ae[7] * enc8 + be[7]; eang9 = ae[8] * enc9 + be[8]; eang10 = ae[9] * enc10 + be[9]; eang11 = ae[10] * enc11 + be[10]; eang12 = ae[11] * enc12 + be[11]; eang13 = ae[12] * enc13 + be[12]; return eang1 , eang2 , eang3 , eang4 , eang5 , eang6 , eang7 , eang8 , eang9 , eang10 , eang11 , eang12 , eang13; } void calibrationSecuence( ) { //set servos at their middle position at firstt for (int i = 0; i <= 12; i++) { rawAngle , motorPin = motorInfo(i); servoWrite(motorPin , 90); } // sensorOffset0 = calibrateContacts(); Serial.println(" "); Serial.println("_________________________________SERVO CALIBRATION ROUTINE_________________________________"); Serial.println("___________________________________________________________________________________________"); Serial.println("(*) Don"t send several caracter at the same time."); delay(500); Serial.println(" "); Serial.println("Keyboard: "x"-> EXIT CALIBRATION. "c"-> ENTER CALIBRATION."); Serial.println(" "i"-> PRINT INFORMATION. "); Serial.println(" "); Serial.println(" "n"-> CHANGE MOTOR (+). "b" -> CHANGE MOTOR (-)."); Serial.println(" "m"-> START CALIBRATION."); Serial.println(" "q"-> STOP CALIBRATION."); Serial.println(" "); Serial.println(" "r"-> CHANGE RESOLUTION."); Serial.println(" "p"-> ADD ANGLE. "o"-> SUBTRACT ANGLE. "); Serial.println(" "s"-> SAVE ANGLE."); delay(500); Serial.println(" "); Serial.println("---------------------------------------------------------------------------------------------------"); Serial.print("SELECTED MOTOR: "); Serial.print(motorTag[motor]); Serial.print(". SELECTED RESOLUTION: "); Serial.println(res); while (CAL == true) { if (Serial.available() > 0) { cm = Serial.read(); if (cm == "x") { Serial.println("Closing CALIBRATION program..."); CAL = false; secuence = false; startDisplay(PAGE); angleBody = 90; anglesIKFR.tetta = 0.0; anglesIKFR.alpha = -45.0; anglesIKFR.gamma = 90.0; anglesIKFL.tetta = 0.0; anglesIKFL.alpha = -45.0; anglesIKFL.gamma = 90.0; anglesIKBR.tetta = 0.0; anglesIKBR.alpha = 45.0; anglesIKBR.gamma = -90.0; anglesIKBL.tetta = 0.0; anglesIKBL.alpha = 45.0; anglesIKBL.gamma = -90.0; } else if (cm == "i") { // + Serial.println(" "); Serial.println("---------------------------------------------------------------------------------------------------"); Serial.println("---------------------------------------------------------------------------------------------------"); Serial.println("(*) Don"t send several caracter at the same time."); delay(500); Serial.println(" "); Serial.println("Keyboard: "x"-> EXIT CALIBRATION. "c"-> ENTER CALIBRATION."); Serial.println(" "i"-> PRINT INFORMATION. "); Serial.println(" "); Serial.println(" "n"-> CHANGE MOTOR (+). "b" -> CHANGE MOTOR (-)."); Serial.println(" "m"-> START CALIBRATION."); Serial.println(" "q"-> STOP CALIBRATION."); Serial.println(" "); Serial.println(" "r"-> CHANGE RESOLUTION."); Serial.println(" "p"-> ADD ANGLE. "o"-> SUBTRACT ANGLE. "s"-> SAVE ANGLE."); Serial.println(" "); delay(500); Serial.println(" "); Serial.println("---------------------------------------------------------------------------------------------------"); Serial.println(" "); Serial.print("SELECTED MOTOR: "); Serial.print(motorTag[motor]); Serial.print(". SELECTED RESOLUTION: "); Serial.println(res); Serial.println("Actual parameters of the motor: "); Serial.print("High limit: "); Serial.print(highLim[motor]); Serial.print(" Low limit: "); Serial.print(lowLim[motor]); Serial.print(" Angle 1: "); Serial.print(ang1[motor]); Serial.print(" Angle 2: "); Serial.println(ang2[motor]); Serial.println("---------------------------------------------------------------------------------------------------"); } else if (cm == "m") { // + secuence = true; } else if (cm == "s") { // + } else if (cm == "n") { // + motor++; if (motor >= 13) { motor = 0; } Serial.print("SELECTED MOTOR: "); Serial.println(motorTag[motor]); } else if (cm == "b") { // + motor--; if (motor < 0) { motor = 13 - 1; } Serial.print("SELECTED MOTOR: "); Serial.println(motorTag[motor]); } else if (cm == "r") { // + if (res == ares) { res = bres; } else if (res == bres) { res = cres; } else if (res == cres) { res = ares; } Serial.print("SELECTED RESOLUTION: "); Serial.println(res); } } if (secuence == true) { Serial.print("Starting secuence for motor: "); Serial.println(motorTag[motor]); for (int i = 0; i <= 30; i++) { delay(20); Serial.print("."); } Serial.println("."); while (question == true) { unsigned long currentMicros = micros(); if (currentMicros - previousMicros >= 100000) { previousMicros = currentMicros; if (Serial.available() > 0) { answer = Serial.read(); if (answer == "y") { question = false; interp = true; secuence = true; } else if (answer == "n") { question = false; interp = false; secuence = true; } else { Serial.println("Please, select Yes(y) or No(n)."); } } } } answer = "t"; question = true; if (interp == false) { Serial.println("___"); Serial.println(" | Place motor at 1ts position and save angle"); Serial.println(" | This position can be the higher one"); rawAngle , motorPin = motorInfo(motor); calibrationAngle = 90; //start calibration at aproximate middle position of the servo. while (secuence == true) { /* find first calibration angle */ if (Serial.available() > 0) { cm = Serial.read(); if (cm == "p") { // + Serial.print(" | +"); Serial.print(res); Serial.print(" : "); calibrationAngle = calibrationAngle + res; servoWrite(motorPin , calibrationAngle); Serial.println(calibrationAngle); } else if (cm == "o") { // - Serial.print(" | -"); Serial.print(res); Serial.print(" : "); calibrationAngle = calibrationAngle - res; servoWrite(motorPin , calibrationAngle); Serial.println(calibrationAngle); } else if (cm == "r") { // + if (res == ares) { res = bres; } else if (res == bres) { res = cres; } else if (res == cres) { res = ares; } Serial.print("SELECTED RESOLUTION: "); Serial.println(res); } else if (cm == "q") { // quit secuence secuence = false; Serial.println(" | Calibration interrupted!!"); } else if (cm == "s") { // save angle ang1[motor] = calibrationAngle; secuence = false; Serial.print(" | Angle saved at "); Serial.println(calibrationAngle); } } } if (cm == "q") { Serial.println(" |"); } else { secuence = true; Serial.println("___"); Serial.println(" | Place motor at 2nd position and save angle"); Serial.println(" | This position can be the lower one"); } while (secuence == true) { /* find second calibration angle */ if (Serial.available() > 0) { cm = Serial.read(); if (cm == "p") { // + Serial.print(" | +"); Serial.print(res); Serial.print(" : "); calibrationAngle = calibrationAngle + res; servoWrite(motorPin , calibrationAngle); Serial.println(calibrationAngle); } else if (cm == "o") { // - Serial.print(" | -"); Serial.print(res); Serial.print(" : "); calibrationAngle = calibrationAngle - res; servoWrite(motorPin , calibrationAngle); Serial.println(calibrationAngle); } else if (cm == "r") { // + if (res == ares) { res = bres; } else if (res == bres) { res = cres; } else if (res == cres) { res = ares; } Serial.print("SELECTED RESOLUTION: "); Serial.println(res); } else if (cm == "q") { // quit secuence secuence = false; Serial.println(" | Calibration interrupted!!"); } else if (cm == "s") { // save angle ang2[motor] = calibrationAngle; secuence = false; Serial.print(" | Angle saved at "); Serial.println(calibrationAngle); } } } /*--------------------start calibration calculations------------------*/ if (cm == "q") { Serial.println("___|"); Serial.println("Calibration finished unespected."); Serial.println(" Select another motor."); Serial.print("SELECTED MOTOR: "); Serial.print(motorTag[motor]); Serial.print(". SELECTED RESOLUTION: "); Serial.println(res); } else { Serial.println("___"); Serial.println(" |___"); Serial.print( " | | Interpolating for motor: "); Serial.println(motorTag[motor]); secuence = true; //real angle is calculated interpolating both angles to a linear relation. a[motor] = (ang2[motor] - ang1[motor]) / (x2[motor] - x1[motor]); b[motor] = ang1[motor] - x1[motor] * (ang2[motor] - ang1[motor]) / (x2[motor] - x1[motor]); Serial.println(" | |"); } interp = true; } /*---------------------------make swing movement to interpolate motor encoder-----*/ if (interp == true and secuence == true) { delay(200); double x; int k = 0; int stp = 180; swing = true; i = 0; orawAngle , motorPin = motorInfo(motor); previousMicros = 0; while (swing == true) { // FIRST unsigned long currentMicros = micros(); if (currentMicros - previousMicros >= 10000) { // save the last time you blinked the LED previousMicros = currentMicros;