1. For downloading SimTools plugins you need a Download Package. Get it with virtual coins that you receive for forum activity or Buy Download Package - We have a zero Spam tolerance so read our forum rules first.

    Buy Download Package Now!
  2. Do not try to cheat our system and do not post an unnecessary amount of useless posts only to earn credits here. We have a zero spam tolerance policy and this will cause a ban of your user account. Otherwise we wish you a pleasant stay here! Read the forum rules
  3. We have a few rules which you need to read and accept before posting anything here! Following these rules will keep the forum clean and your stay pleasant here. Do not following these rules will lead to permanent exclusion from this website: Read the forum rules.

Showroom FlyPT - 6DOF Brushless DIY Actuators

Discussion in 'DIY Motion Simulator Projects' started by pmvcda, Aug 29, 2017.

  1. pmvcda

    pmvcda Active Member

    Joined:
    Nov 3, 2010
    Messages:
    126
    Location:
    Portugal
    Balance:
    691Coins
    Ratings:
    +156 / 1 / -0
    My Motion Simulator:
    6DOF
    Hi,

    First time posting, but sneaking for a long long time (sorry for some English errors I might post).
    So some of the guys here are guilty! Yes, I started to build a 6DOF motion simulator with DIY actuators.
    SeatTimes, SilentChill and some others, you are an inspiration.
    I think my wife hates you guys... ;)

    So, for some years I've been looking at motion sims, but since my desire was always cars and air planes, I always thought that less than 6DOF would kill the aircraft experience (probably I'm wrong).
    But now with so many 6DOF builds, specially with actuators, I though it's the time to start my build.

    Well it already started some months ago, but free time is always a problem.
    I will make some posts to put at the start of this thread the current project development.
    I will make the following topics:
    -Videos and photos (latest images and videos of the construction)
    -Parts (list of all parts, and where I got them)
    -Plans (drawings, schematics and 3D printed parts files)
    -Software (software used, setups and code developed for this project)

    So let's start! Hope my project can help others. And let's hope I can make it.


    (FlyPT is the user I usually use in other forums, but I had it banned here, probably because I was not active for a long time?)
    • Like Like x 1
    • Creative Creative x 1
  2. pmvcda

    pmvcda Active Member

    Joined:
    Nov 3, 2010
    Messages:
    126
    Location:
    Portugal
    Balance:
    691Coins
    Ratings:
    +156 / 1 / -0
    My Motion Simulator:
    6DOF
    Videos and photos
    latest images and videos of the construction

    Current state:
    WP_20180924_18_58_24_Rich.jpg

    Latest video
    Testing the Interface between SimTools and ESP32 boards:


    Fifth video
    With load:


    Fourth video
    Hexapod moving.


    Third test video
    Arduino replaced by ESP32 and no more potentiometers.
    Positioning made with internal hall sensors of BLDC motor.


    Older videos in my Youtube Channel...
    • Like Like x 4
    • Winner Winner x 1
    Last edited: Nov 12, 2018
  3. pmvcda

    pmvcda Active Member

    Joined:
    Nov 3, 2010
    Messages:
    126
    Location:
    Portugal
    Balance:
    691Coins
    Ratings:
    +156 / 1 / -0
    My Motion Simulator:
    6DOF
    Parts
    List of all parts, and where I got them
    No prices, because they are always changing, I don't want to know how much I spend in total :p

    Motors (6x):
    Nanotec.jpg
    Nanotec DB59C024035-A (brushless).
    1 Bought from Portuguese reseller
    5 Bought at Farnell (http://pt.farnell.com/nanotec/db59c024035-a/bldc-motor-24vdc-4500rpm/dp/2507576 - less than half the price from above )
    Specs: http://en.nanotec.com/fileadmin/files/Datenblaetter/BLDC/DB59/DB59C024035-A.pdf

    Motor drivers (6x):
    BLDC-8015A.jpg
    ACT Motor BLDC-8015A
    Bought on ebay (http://www.ebay.co.uk/itm/252468509819?_trksid=p2060353.m1438.l2649&ssPageName=STRK:MEBIDX:IT).
    Specs: http://www.wantmotor.com/product/8015a.html (From Wantai, but equal to ACT Motor)
    ATTENTION: ACT / Wantai / Longs motors seems the same, but try to buy all from the same source. I got from Wantai and ACT, and controllers have different speeds!

    Power supply (1x):
    Mean Well.jpg
    Mean Well RSP-3000-24
    Bought on ebay (http://www.ebay.co.uk/itm/231473165159?_trksid=p2060353.m2749.l2649&var=530732464572&ssPageName=STRK:MEBIDX:IT).
    Specs: http://media.it-tronics.de/Datasheets/Power_Supplies/MeanWell/RSP-3000.pdf

    Microswitches (12x):
    Microswitch.jpg
    Bought on ebay (http://www.ebay.co.uk/itm/282088135235?_trksid=p2060353.m2749.l2649&var=581040167277&ssPageName=STRK:MEBIDX:IT)
    Specs: 16mm lever

    Emergency stop buttons (2x):
    p.jpg
    Bought on ebay (https://www.ebay.fr/itm/Push-Button...069214?hash=item1cb45cdd9e:g:Lg4AAOSw4A5YpGpe)

    Aluminium tube (6x):
    Aluminium tube.jpg
    Bought from Motedis (http://www.motedis.com/shop/Tube-sy...ular-tube-aluminium-anodized-28x2mm::823.html)
    Specs: 28mm exterior, 2mm wall with 500mm length

    Aluminium profile (12x):
    Aluminium profile.JPG
    Bought from Motedis (http://www.motedis.com/shop/Slot-pr...-slot-5/Profile-20x60-I-Type-slot-5::499.html)
    Specs: 20x60 I-Type slot 5 with 730mm lenght

    Aluminium for upper structure with 40x40mm (5m)

    Aluminium for bottom structure with 80x40mm (5m)

    Seat (1x):
    p.jpg
    Caterham Seven/7/R500 fiberglass seat
    Bought on ebay (https://www.ebay.co.uk/p/Caterham-S...breglass-Bucket-Seat-in-Black-SE81/1042847988)

    Ballscrew (6x):
    Ball screw.jpg
    Bought from Motedis (http://www.motedis.com/shop/Dynamic...es/Ball-screw/Ballscrew-SFU1605-DM::1444.html)
    Specs: SFU1605-DM with 542mm (5mm step for each turn with 16mm diameter)

    Nut for ballscrew (6x):
    Ball screw nut.jpg
    Bought from Motedis (http://www.motedis.com/shop/Dynamic...ball-screw/Screw-nut-DIN-439-M10x1::2444.html)
    Specs: DIN 439 M10x1

    3D Filament PET-G 1,75mm black (4kg):
    PETG.jpg
    Bought from Motedis (http://www.motedis.com/shop/table_cats.php?table_id=45)

    Flange Bearing (6x)
    Bearing.jpg
    Bought from Motedis (http://www.motedis.com/shop/Dynamic...lange-Bearing-10mm-die-cast-KFL000::5212.html)
    Specs: 10mm die-cast aluminium KFL000

    Plain bearing (6x):
    Igus.jpg
    Bought from Motedis (http://www.motedis.com/shop/Dynamic...g/Plain-bearing-Igus-GFM-283239-20::1166.html)
    Specs: Igus GFM-283239-20 Type 283239-20

    Flexible clutch (6x):
    Clutch.jpg
    Bought from ebay(http://www.ebay.co.uk/itm/Wellenkup...287275?hash=item3f76fdd8eb:g:MIAAAOSwOgdYynFj)
    Specs: Diameter 25mm Lenght 30mm Shaft diameter 8,00mm/8,00mm

    Universal joint (12x):
    UJoint.jpg
    Bought from ebay (http://www.ebay.co.uk/itm/12mm-Dia-...654174&hash=item2cadada585:g:odAAAOSwuhhXWW89)
    Specs: 12mm Dia Shaft Coupling Motor Connector

    Screws:
    Socket cap Allen screw M4 x 12mm - To fix limit switches (6 x 4x = 24x)
    Socket cap Allen screw M5 x 20mm - Most used screw (6 x 18x = 108x)
    Flat slotted screw M5 x 50mm - To hold bottom of actuator (6 x 4x = 24x)
    Hex screw M6 x 25mm - To join motor mount and plain bearing (6 x 2x = 24x)
    Hex screw M12 x 25mm - To join upper structure to actuators (6x)
    Flat Allen screw M5x30 (6 x 4x = 24x)
    Flat slotted screw M5x50 (6 x 4x = 24x)
    Cheese head slotted screw M5x60 (6 x 9x = 54x)

    Nuts:
    Retaining nuts for M5 (6 x 8x = 48x)
    Retaining nuts for M6 (6 x 2x = 12x)

    Washers:
    For M5 (6 x 12x = 72x)
    For M6 (6 x 4x = 24x)
    For M12 with 32mm diameter (6x)
    For M12 with 24mm diameter (6 x 2x = 12x)

    D Sub Connectors (6x):
    2294339-40.jpg 2294341-40.jpg 633860-40.jpg
    Bought at Farnell:
    Male: http://pt.farnell.com/norcomp/681s13w3103l001/d-sub-combo-plug-40amp-13way/dp/2294355?st=d sub
    Female: http://pt.farnell.com/norcomp/680s13w3103l401/d-sub-combo-plug-40amp-13way/dp/2294339?st=d sub
    Housing: http://pt.farnell.com/mh-connectors/mhd45zk-25-k/backshell-d-nickel-45dg-25way/dp/633860
    Specs: 3 power pins up to 40A and 10 signal pins up to 5A.

    Wires:
    0.35 mm2 cross section for signal (various colours)
    1.00 mm2 cross section for motors power (yellow, black and red)
    s-l1600 (3).jpg
    Bought on ebay (https://www.ebay.fr/itm/Automotive-Wire-0-35-1-5-mm-Thin-Wall-Cable-AWG16-22-Singlecore-5m-10m-lead/282093003064?ssPageName=STRK:MEBIDX:IT&_trksid=p2057872.m2749.l2649)

    Diode to protect PSU (6x):
    s-l500.jpg
    Bought on ebay (https://www.ebay.fr/itm/P1000M-10A1...-10/182909329114?_trksid=p2485497.m4902.l9144)
    Specs: P1000M 10A10 1000V 10A

    Relay 24V 200A (1x):
    s-l1600.jpg
    Bought on ebay (https://www.ebay.fr/itm/Car-Truck-Boat-24V-200A-Heavy-Duty-Split-Charge-Starter-Relay-with-Teminal/163238356171?ssPageName=STRK:MEBIDX:IT&_trksid=p2057872.m2749.l2649)


    PARTS SPECIFIC FOR INTERNAL HALL SENSOR POSITIONING

    ESP32 Dev Kit (2x):
    s-l1600.jpg
    Bought on ebay (https://www.ebay.fr/itm/Espressif-ESP32-WLAN-Dev-Kit-Board-Development-Bluetooth-Wifi-v1-WROOM32-NodeMCU/253059783728?ssPageName=STRK:MEBIDX:IT&_trksid=p2057872.m2749.l2649)

    Level shifters (10x):
    p.jpg
    Bought on ebay (https://www.ebay.fr/itm/5-Stuck-4-K...668603?hash=item3aeafec63b:g:xR0AAOSwiYlaD2Gc)

    Male/Female connectors with 10 pins to mount on PCB (6x):
    s-l1600 (1).jpg
    Bought on ebay (https://www.ebay.fr/itm/KIT-5x-BUCHSE-STECKER-10-polig-CRIMPKONTAKTE-2-54mm-PCB-Gerade-A2011/253322647034?ssPageName=STRK:MEBIDX:IT&_trksid=p2057872.m2749.l2649)

    Male/Female pins to mount ESP32 and level shifters on the PCB (10x M/F with 6 pins and 4x M/F with 15 pins):
    s-l1600 (2).jpg
    Bought on ebay (https://www.ebay.fr/itm/Pin-or-Female-Header-Edge-Pins-Strip-0-1-2-54mm-for-Breadboard-PCB-for-Dupont/263333424929?ssPageName=STRK:MEBIDX:IT&_trksid=p2057872.m2749.l2649)

    PCB board 5x7 (2x):
    s-l500.jpg
    Bought on ebay (https://www.ebay.fr/itm/1055-Double-Side-Copper-prototype-pcb-Universal-Board-for-Arduino-au-choix/292031954800?ssPageName=STRK:MEBIDX:IT&_trksid=p2057872.m2749.l2649)




    PARTS SPECIFIC FOR POT POSITIONING VERSION

    Potentiometers (6x):
    4434310.jpg
    Vishay 10K 10 turn
    Bought at Farnell (http://pt.farnell.com/vishay/536-1-1-103/wirewound-potentiometer-10kohm/dp/1386483?st=vishay pot)
    Specs: http://www.farnell.com/datasheets/2...MI_oTz8b3L2wIVhPdRCh08AQzXEAAYAiAAEgLrj_D_BwE

    Idler wheel for 6mm wide belt (6x):
    Idler wheel.jpg
    Bought from Motedis (http://www.motedis.com/shop/Dynamic...-6mm/Idler-wheel-for-6mm-wide-belt::4523.html)
    Specs: 6mm wide with 18mm outside diameter

    Aluminium pulley (6x):
    6mm GT2 pulley with 24 teeth.jpg
    Bought on ebay (http://www.ebay.co.uk/itm/152084840946?_trksid=p2060353.m2749.l2649&var=451287689730&ssPageName=STRK:MEBIDX:IT)
    Specs: 24 teeth pulley for 6mm GT2 belt

    6mm GT2 belt (10m):
    6mm GT2 belt.jpg
    Bought from Motedis (http://www.motedis.com/shop/Dynamic...mm/GT2-Open-Timing-Belt-6mm-1000mm::4251.html)
    Specs: 6mm GT2 belt
    • Informative Informative x 3
    Last edited: Nov 13, 2018
  4. pmvcda

    pmvcda Active Member

    Joined:
    Nov 3, 2010
    Messages:
    126
    Location:
    Portugal
    Balance:
    691Coins
    Ratings:
    +156 / 1 / -0
    My Motion Simulator:
    6DOF
    Cost £££ $$$ €€€
    Cost for building this motion sim

    I thought of not making this, but here's a current look at the costs for the current version using hall sensors from the motors:

    Sem nome.jpg

    Those costs are only for:
    -The hexapod, no chair or wheel support.
    -No delivery costs (depends on where you are)

    I spent more with errors I made and building a pot version of the actuators.
    Parts are from Farnell, Motedis and ebay. You can see the list in a post above.

    Could have bought a pack of motor and controller for 98,99€:
    s-l1600.jpg
    BLDC 8015A + 57BLF03 (https://www.ebay.fr/itm/ACT-MOTOR-G...552331?hash=item3afed62d4b:g:ZU8AAOSw0hlZMXao)
    Motor is slower, but cost would come down to 2636,58€.





    Plans

    drawings, schematics and 3D printed parts files

    Sketchup 3D model with MSPhysics integration (Version 4):
    https://3dwarehouse.sketchup.com/mo...eec50f0a92/6DOF-Motion-Sim-by-FlyPT-Version-4
    update.jpg

    Dimensions of the hexapod (joints)
    Dimensions.png
    Full resolution at: https://www.flickr.com/photos/128465277@N06/31328973698/sizes/o/

    Files with the 3D printed parts for actuators using pot positioning:
    [​IMG]
    File: https://drive.google.com/open?id=1VDiJsNzJmQrOuzEaqN5gCoLyDfJcxLRb

    Files with the 3D printed parts for actuators using internal hall sensor positioning:
    SOON!

    Files with the 3D printed parts for the bottom and upper structure of the Stewart platform:
    SOON!

    Warning!
    Those files are made for my printer. They work for me and they have the right tolerances to fit the aluminium profiles and parts I use.
    They might not work for you. So use them at your own risk.
    They are printed in solid PETG. I think they are strong enough, although due to personal problems I still have to finish my rig to test them with the final load.
    So I don't know if they hold or how long they will hold.
    Also, this is continuously in development and I still might change the design.
    Last edited: Oct 9, 2018
  5. pmvcda

    pmvcda Active Member

    Joined:
    Nov 3, 2010
    Messages:
    126
    Location:
    Portugal
    Balance:
    691Coins
    Ratings:
    +156 / 1 / -0
    My Motion Simulator:
    6DOF
    Software
    Software used, setups and code developed for this project

    INTERNALL HALL SENSOR POSITIONING VERSION WITH ESP32 DEVKIT

    Used software:
    SimTools 2 (got the DIY license)
    LFS (demo version)
    Sketchup to draw all the printed parts (free)
    Cura 2.6 to print 3D parts (free)
    MSPhysics plugin for Sketchup, to test angles and dimensions of rig (free)
    Arduino and needed libraries to program the ESP32 (free)
    Google Chrome to browse this site...

    Developed software:
    ESP32 code that:
    -Calibrates actuators
    -Receives data (speed and position) from FlyPT 6DOF Interface
    -Commands the motor controllers
    -Interprets the readings from the internal hall sensors to determine the actuator position
    PC software (FlyPT 6DOF Interface) that:
    -Calculates lengths of hexapod arms for a specific pose.
    -Calculates speed of actuators so they reach target at the same time
    -Uses the folowing logic:
    Interface.png
    Full resolution at https://www.flickr.com/photos/128465277@N06/44640995994/sizes/o/
    Current interface:
    image4493.png
    Code will be available in the future...

    Update history:

    Version 4:
    -Change Simtools analyse function to handle two boards for 6 actuators
    -Board 1 handles actuator 1, 3 and 5
    -Board 2 handles actuator 2, 4 and 6
    -Actuators are alternated, so any error should happen on one board, the problem is softened at the rig
    -Removed some loops for faster code

    Version 3:
    -Code optimisation and cleaning
    -Added simultaneous calibration for all 3 actuators

    Version 2:
    -Added support for 3 actuators
    -Optimisation

    Version 1:
    -There's always a first one. Hope it's not the last...
    -Working with only one actuator

    Latest version:
    Code:
    // ============================
    // DIY 6DOF Motion Sim by FlyPT
    // ============================
    // SimTools2 ESP32 code to control actuators
    // Actuators use brushless motors controlled by BLDC8015A drivers
    // Position is obtained from internall hall sensors
    // This code is made to handle 3 actuators in one board and also made to support 6 in two boards.
    // Just upload this skectch in two boards and change the SimTools2 interface output for each board
    // ===================================
    // Update history - Current: Version 4
    // ===================================
    // Version 4
    //  Change Simtools analyse function to handle two boards for 6 actuators
    //  Board 1 handles actuator 1, 3 and 5
    //  Board 2 handles actuator 2, 4 and 6
    //  Actuators are alternated, so any error should happen on one board, the problem is softened at the rig
    //  Removed some loops for faster code
    // Version 3
    //  Code optimisation and cleaning
    //  Added simultaneous calibration for all 3 actuators
    // Version 2:
    //  Added support for 3 actuators
    //  Optimisation
    // Version 1:
    //  There's always a first one. Hope it's not the last...
    //  Working with only one actuator
    // ==================================
    // SimTools2 configuration should be:
    // ==================================
    // Interface settings:
    // Interface Type: Serial
    // ComPort: The one used by your ESP32
    // BitsPerSec: 115200 (You can change the define to use another speed)
    // Data Bits: 8
    // Parity: None
    // Stop Bits: 1
    // Output - Bit Range: 10
    // Output - Type: Binary
    // Startup - Output: -
    // HW Start: -
    // Interface output for board 1: [1<Axis1a>][3<Axis3a>][5<Axis5a>]
    // Interface output for board 2: [2<Axis2a>][4<Axis4a>][6<Axis6a>]
    // Output Rate: 10ms
    // Shutdown - Output: -
    // HW Stop: -
    // =======
    // Defines
    // =======
    #define SERIAL_SPEED 115200 // Define to set up the serial speed comunication
    #define CALIBRATION_SPEED 50 // Speed used to search for minimum and maximum values
    #define UP HIGH // Define to set up direction as HIGH
    #define DOWN LOW // Define to set down direction as LOW
    #define PWM_RESOLUTION 8
    #define PWM_FREQUENCY 5000
    #define LIMITS_PIN 0           // Pin to capture position from pot wich defines the limits
    #define HALL_1_PIN 1           // Pin used for hall 1 interrupt
    #define HALL_2_PIN 2           // Pin used for hall 2 interrupt
    #define HALL_3_PIN 3           // Pin used for hall 3 interrupt
    #define LAST_ANGLE 4           // Last angle that was detected
    #define LAST_SUM 5             // Last sum to know last direction it took
    #define DIRECTION_PIN 6        // Pin to se direction
    #define SPEED_PIN 7            // Pin to set speed with PWM
    #define PWM_CHANNEL 8          // Channel used to generate PWM
    #define MINIMUM_POSITION 9     // Minimum position
    #define MAXIMUM_POSITION 10    // Maximum position
    #define CURRENT_POSITION 11    // Current position
    #define PRETENDED_POSITION 12  // Pretended position
    #define P_PID 13               // P from PID
    #define I_PID 14               // I from PID
    #define D_PID 15               // D from PID
    #define INTEGRATED_ERROR 16    // Sum of differences between pretended and current position
    #define LAST_ERROR 17          // Last difference between pretended and current position
    #define SPEED 18               // Speed of motor rotation
    #define FOUND 19               // For help. Used for example in actuator calibration
    #define NUMBERACTUATORS 3      // Number of actuators (for one board)
    #define P 256
    #define I 0
    #define D 100
    // ======================
    // Actuators information:
    // ======================
    // 0  - LIMITS_PIN          - Pin to capture position from pot
    // 1  - HALL_1_PIN          - Pin used for hall 1 interrupt
    // 2  - HALL_2_PIN          - Pin used for hall 2 interrupt
    // 3  - HALL_3_PIN          - Pin used for hall 3 interrupt
    // 4  - LAST_ANGLE          - Last angle that was detected
    // 5  - LAST_SUM            - Last sum to know last direction it took
    // 6  - DIRECTION_PIN       - Pin to se direction
    // 7  - SPEED_PIN           - Pin to set speed with PWM
    // 8  - PWM_CHANNEL         - Channel used to generate PWM
    // 9  - MINIMUM_POSITION    - Minimum position
    // 10 - MAXIMUM_POSITION    - Maximum position
    // 11 - CURRENT_POSITION    - Current position
    // 12 - PRETENDED_POSITION  - Pretended position
    // 13 - P_PID               - P from PID
    // 14 - I_PID               - I from PID
    // 15 - D_PID               - D from PID
    // 16 - INTEGRATED_ERROR    - Sum of differences between pretended and current position
    // 17 - LAST_ERROR          - Last difference between pretended and current position
    // 18 - SPEED               - Speed of motor rotation
    // 19 - FOUND               - Used as help for calibraion
    static int actuator[3][20] ={   {  4, 27, 26, 25,  5,  0, 32, 33,  0, -1, -1, 0, 0, P, I, D, 0, 0, 0, 0 },
                                    { 36, 35, 34, 39,  5,  0, 18,  5,  1, -1, -1, 0, 0, P, I, D, 0, 0, 0, 0 },           
                                    { 15, 23, 22, 21,  5,  0, 19, 17,  2, -1, -1, 0, 0, P, I, D, 0, 0, 0, 0 } };                         
    static int bufferCurrent = 0; // To hold current read fom SimTools command
    static int bufferCount = -1; // To hold position in bufferCommand array
    static int bufferCommand[5] = {0}; // To hold command info from SimTools
    // Maps to get increment off position from comparing previous hall sensor state with current state
    // Vertical, the new value, horizontal, the old one
    // Stores for 0 and 7, althought they should not ocurr, digital read might get them. They are ignored with zero change.
    // State sequence going up
    // 5=101
    // 1=001
    // 3=011
    // 2=010
    // 6=110
    // 4=100
    // First index for new state, second for old state ([new][old])
    static int slowSpeedUp[8][8] =      { { 0,  0,  0,  0,  0,  0,  0, 0},
                                          { 0,  0, -2, -1,  2,  1,  3, 0},
                                          { 0,  2,  0,  1, -2,  3, -1, 0},
                                          { 0,  1, -1,  0,  3,  2, -2, 0},
                                          { 0, -2,  2,  3,  0, -1,  1, 0},
                                          { 0, -1,  3, -2,  1,  0,  2, 0},
                                          { 0,  3,  1,  2, -1, -2,  0, 0},
                                          { 0,  0,  0,  0,  0,  0,  0, 0} };
                           
    static int slowSpeedDown[8][8] =    { { 0,  0,  0,  0,  0,  0,  0, 0},
                                          { 0,  0, -2, -1,  2,  1, -3, 0},
                                          { 0,  2,  0,  1, -2, -3, -1, 0},
                                          { 0,  1, -1,  0, -3,  2, -2, 0},
                                          { 0, -2,  2, -3,  0, -1,  1, 0},
                                          { 0, -1, -3, -2,  1,  0,  2, 0},
                                          { 0, -3,  1,  2, -1, -2,  0, 0},
                                          { 0,  0,  0,  0,  0,  0,  0, 0} };
                                                           
    // ==============
    // Initialization
    // ==============
    void setup()
    {
      // Setup communication
      Serial.begin(115200, SERIAL_8N1);
     
      for(int n=0;n<NUMBERACTUATORS;n++)
      {
        // Initialize pins
        pinMode(actuator[n][LIMITS_PIN], INPUT);
        pinMode(actuator[n][HALL_1_PIN], INPUT);
        pinMode(actuator[n][HALL_2_PIN], INPUT);
        pinMode(actuator[n][HALL_3_PIN], INPUT);
        pinMode(actuator[n][DIRECTION_PIN], OUTPUT);
        pinMode(actuator[n][SPEED_PIN], OUTPUT);
     
        // Setup PWM
        ledcSetup(actuator[n][PWM_CHANNEL], PWM_FREQUENCY, PWM_RESOLUTION);
        ledcAttachPin(actuator[n][SPEED_PIN], actuator[n][PWM_CHANNEL]);
        ledcWrite(actuator[n][PWM_CHANNEL], 0);
      }
     
      // Initialize pin to light up onboard led
      pinMode(2, OUTPUT);
      // Countdown of 5 seconds before calibration, making internal led flash at half a second
      for(byte n=5; n>0; n--)
      {
        Serial.print("Calibrating in ");
        Serial.println(n);
        delay(500);
        digitalWrite(2, HIGH); 
        delay(500);
        digitalWrite(2, LOW);
      }
      digitalWrite(2, LOW);
     
      // Calibrate actuators, searching for minimums and maximums
      calibrate();
      // Put actuators in the middle after calibration
      for(int n=0;n<NUMBERACTUATORS;n++) actuator[n][PRETENDED_POSITION]=actuator[n][MAXIMUM_POSITION]/2;
      // Create thread that gets position from hall sensors to run in the core 0
      // It will run in paralell with the main loop that uses core 1 by default
      xTaskCreatePinnedToCore(
        TaskUpdatePositions,  // Task function
        "UpdatePositions",    // Name         
        10000,                // Stack size
        NULL,                 // Task input parameter
        1,                    // Priority of the task
        NULL,                 // Task handle
        0);                   // Core
    }
    // ======================
    // Update actuator action
    // ======================
    // Here we calculate the PID depending of desired vs current position and send respective signal to motor controller
    // Definitions to avoid repeated definitions in functions
    static int limitValue=1023;
    static int error=0;
    static int p=0;
    static int i=0;
    static int d=0;
    void updateActuator(byte n)
    {
      // Calculate pid
      // =============
      error=actuator[n][PRETENDED_POSITION]-actuator[n][CURRENT_POSITION];
      //  actuator[n][INTEGRATED_ERROR]+=error;  // <<<<<<< commented for speed, not in use (i from pid)
      p=actuator[n][P_PID] * error;
      //  i=actuator[n][I_PID] * constrain(actuator[n][INTEGRATED_ERROR], -100, 100); // <<<<<<< commented for speed, not in use (i from pid)
      d=actuator[n][D_PID] * (error - actuator[n][LAST_ERROR]);
      actuator[n][LAST_ERROR]=error;
      //  actuator[n][SPEED]=constrain((p+i+d)/100, -255, 255);  // <<<<<<< commented for speed, not in use (i from pid)
      actuator[n][SPEED]=constrain((p+d)>>7, -255, 255); // 2 4 8 16 32 64 128 Divide by 128
      // Apply changes to motor controller
      // =================================
      if(actuator[n][SPEED]>0)
      {
        ledcWrite(actuator[n][PWM_CHANNEL], actuator[n][SPEED]);
        digitalWrite (actuator[n][DIRECTION_PIN], UP);
      }
      else
      {
        ledcWrite(actuator[n][PWM_CHANNEL], -actuator[n][SPEED]);
        digitalWrite (actuator[n][DIRECTION_PIN], DOWN);
      }
      // Check limits
      // ============
      // Adjust position if minimum or maximum switch is activated in case of a drift in position calculation
      // WITH A FIXED RESISTANCE IT WILL BE CHANGE TO A RANGE WITH ONLY 1 READ and 2 IF'S
      limitValue=(analogRead(actuator[n][LIMITS_PIN])+analogRead(actuator[n][LIMITS_PIN])+analogRead(actuator[n][LIMITS_PIN])+analogRead(actuator[n][LIMITS_PIN]))>>2;
      // If they are actuated, recalibrate position
      if(limitValue>=4095) actuator[n][CURRENT_POSITION]=actuator[n][MAXIMUM_POSITION];    // We are at maximum position
      else if (limitValue<=0) actuator[n][CURRENT_POSITION]=actuator[n][MINIMUM_POSITION]; // We are at minimum position
    }
    // ====================================
    // Update current position of actuators
    // ====================================
    // Definitions to avoid repeated definitions in functions
    static int angle=0;
    void updatePositions(byte n)
    {
        angle=digitalRead(actuator[n][HALL_1_PIN])*4+digitalRead(actuator[n][HALL_2_PIN])*2+digitalRead(actuator[n][HALL_3_PIN]);
        if (actuator[n][LAST_SUM]>0) actuator[n][LAST_SUM]=slowSpeedUp[angle][actuator[n][LAST_ANGLE]];
        else actuator[n][LAST_SUM]=slowSpeedDown[angle][actuator[n][LAST_ANGLE]];
        actuator[n][LAST_ANGLE]=angle;
        actuator[n][CURRENT_POSITION]+=actuator[n][LAST_SUM];
    }
    // =============================
    // Get information from SimTools
    // =============================
    void getSimToolsCommands()
    {
      // Look for start and finish of a command on the serial
      // When we have a command send it for analyze
      if (Serial.available()) // Using if instead of while to avoid the code to stop here for to long
      {
        bufferCurrent = Serial.read();
        if (bufferCount == -1)
        {
          if (bufferCurrent != '[') bufferCount = -1;
          else bufferCount = 0;
        }
        else
        {
          bufferCommand[bufferCount] = bufferCurrent;
          bufferCount++;
          if (bufferCount > 3)
          {
            if (bufferCommand[3] == ']') analyzeSimToolsCommand();
            bufferCount = -1;
          }
        }
      }
    }
    // ================================
    // Analyze command sent by SimTools
    // ================================
    void analyzeSimToolsCommand()
    {
      // Analyze command, see if it's a command and wich actuator is affected
      // Since we need to use 2 boards for 6 actuators, there are two cases
      // So board 1 is used for actuator 1, 3 and 5 and board 2 for 2, 4 and 6
      // For board 1, actuators 1, 3 and 5
      // actuator 1 = index 0
      // actuator 3 = index 1
      // actuator 5 = index 2
      // For board 2, actuators 2, 4 and 6
      // actuator 2 = index 0
      // actuator 4 = index 1
      // actuator 6 = index 2
      switch (bufferCommand[0])
      {
        case '1':
        case '2':
          actuator[0][PRETENDED_POSITION] = map((bufferCommand[1] * 256) + bufferCommand[2], 0, 1023, actuator[0][MINIMUM_POSITION], actuator[0][MAXIMUM_POSITION]);
          break;
        case '3':
        case '4':
          actuator[1][PRETENDED_POSITION] = map((bufferCommand[1] * 256) + bufferCommand[2], 0, 1023, actuator[1][MINIMUM_POSITION], actuator[1][MAXIMUM_POSITION]);
          break;
        case '5':
        case '6':
          actuator[2][PRETENDED_POSITION] = map((bufferCommand[1] * 256) + bufferCommand[2], 0, 1023, actuator[2][MINIMUM_POSITION], actuator[2][MAXIMUM_POSITION]);
          break;
        default:
          return;
          break;
      }
    }
    // =========
    // Calibrate
    // =========
    void calibrate()
    {
      // Move all actuators away from the minimum until we have no minimum switch return
      // ===============================================================================
      for(int n=0;n<NUMBERACTUATORS;n++)
      {
        digitalWrite (actuator[n][DIRECTION_PIN],UP);
        ledcWrite(actuator[n][PWM_CHANNEL], CALIBRATION_SPEED);
        actuator[n][FOUND]=false;
      }
      int found=0;
      while (found<NUMBERACTUATORS)
      {
        for(int n=0;n<NUMBERACTUATORS;n++)
        {
          // Read limit switch position (we are looking for more than zero)
          // We read more than one time to be sure (4 times in this case)
          if(!actuator[n][FOUND] && analogRead(actuator[n][LIMITS_PIN])>0 && analogRead(actuator[n][LIMITS_PIN])>0 && analogRead(actuator[n][LIMITS_PIN])>0 && analogRead(actuator[n][LIMITS_PIN])>0)
          {
            ledcWrite(actuator[n][PWM_CHANNEL], 0);
            actuator[n][FOUND]=true;
            found++;
          }
        }
      }
     
      // Move all actuators to minimum
      // =============================
      for(int n=0;n<NUMBERACTUATORS;n++)
      {
        digitalWrite (actuator[n][DIRECTION_PIN],DOWN);
        ledcWrite(actuator[n][PWM_CHANNEL], CALIBRATION_SPEED);
        actuator[n][FOUND]=false;
      }
      found=0;
      while (found<NUMBERACTUATORS)
      {
        for(int n=0;n<NUMBERACTUATORS;n++)
        {
          // Read limit switch position (we are looking for zero)
          // We read more than one time to be sure (4 times in this case)
          if(!actuator[n][FOUND] && analogRead(actuator[n][LIMITS_PIN])+analogRead(actuator[n][LIMITS_PIN])+analogRead(actuator[n][LIMITS_PIN])+analogRead(actuator[n][LIMITS_PIN])<=0)
          {
            ledcWrite(actuator[n][PWM_CHANNEL], 0);
            actuator[n][MINIMUM_POSITION]=0;
            actuator[n][CURRENT_POSITION]=0;
            actuator[n][FOUND]=true;
            found++;
            Serial.print("Actuator ");
            Serial.print(n);
            Serial.print(" minimum found (");
            Serial.print(actuator[n][MINIMUM_POSITION]);
            Serial.println(")");
          }
        }
      }
      // Search for maximums
      // ===================
      for(int n=0;n<NUMBERACTUATORS;n++)
      {
        digitalWrite (actuator[n][DIRECTION_PIN],UP);
        ledcWrite(actuator[n][PWM_CHANNEL], CALIBRATION_SPEED);
        actuator[n][FOUND]=false;
      }
      found=0;
      while (found<NUMBERACTUATORS)
      {
        for(int n=0;n<NUMBERACTUATORS;n++)
        {
          // Update position of actuator
          updatePositions(n);
          // Read limit switch position (we are looking for big value 4095)
          if(!actuator[n][FOUND] && (analogRead(actuator[n][LIMITS_PIN])+analogRead(actuator[n][LIMITS_PIN])+analogRead(actuator[n][LIMITS_PIN])+analogRead(actuator[n][LIMITS_PIN]))/4>=4095)
          {
            ledcWrite(actuator[n][PWM_CHANNEL], 0);
            updatePositions(n);
            actuator[n][MAXIMUM_POSITION]=actuator[n][CURRENT_POSITION];
            actuator[n][FOUND]=true;
            found++;
            Serial.print("Actuator ");
            Serial.print(n);
            Serial.print(" maximum found (");
            Serial.print(actuator[n][MAXIMUM_POSITION]);
            Serial.println(")");
          }
        }
      }
    }
    // =========
    // Main loop
    // =========
    // Here we get the simtools commands and make the actuator move
    void loop()
    {
      // While we are here, nothing else runs on the background...
      while(true)
      {
        getSimToolsCommands();
        updateActuator(0);
        updateActuator(1);
        updateActuator(2);
      }
    }
    // ============================
    // Update positions thread loop
    // ============================
    // This thread updates the actuators positions reading the hall sensors
    void TaskUpdatePositions(void * parameter)
    {
      while (true)
      {
        updatePositions(0);
        updatePositions(1);
        updatePositions(2);
      }
    }
    
    

    SimTools2 configuration should be:
    Interface settings:
    Interface Type: Serial
    ComPort: The one used by the ESP32 (Since each ESP32 controls 3 actuators, you need two, so you have to configure another one but use the corresponding ComPort)
    BitsPerSec: 115200 (You can change the define to use another speed)
    Data Bits: 8
    Parity: None
    Stop Bits: 1
    Output - Bit Range: 10
    Output - Type: Binary
    Startup - Output: -
    HW Start: -
    Interface output for board 1: [1<Axis1a>][3<Axis3a>][5<Axis5a>]
    Interface output for board 2: [2<Axis2a>][4<Axis4a>][6<Axis6a>]
    Output Rate: 10ms
    Shutdown - Output: -
    HW Stop: -
    • Like Like x 2
    Last edited: Nov 13, 2018
  6. pmvcda

    pmvcda Active Member

    Joined:
    Nov 3, 2010
    Messages:
    126
    Location:
    Portugal
    Balance:
    691Coins
    Ratings:
    +156 / 1 / -0
    My Motion Simulator:
    6DOF
    Software (continuation)
    Software used, setups and code developed for this project

    POT POSITIONING VERSION WITH ARDUINO UNO

    I moved to the internal hall positioning solution, so this is no longer updated, but here for reference

    Used software:
    SimTools 2 (got the DIY license)
    LFS (demo version)
    Sketchup to draw all the printed parts (free)
    Cura 2.6 to print 3D parts (free)
    MSPhysics plugin for Sketchup, to test angles and dimensions of rig (free)
    Arduino to program the Arduino Uno (free)
    Google Chrome to browse this site...

    Developed software:
    Arduino Uno code that:
    -Calibrates actuators
    -Receives data from SimTools2
    -Implements PID to position actuators

    Update history:

    Version 8:
    -Corrected error in the PID calculation (affected D where error and last error where already equal, returning always zero for D)
    -Remove unnecessary calculus int he PID calculation
    -Commented I calculation, since it's not used and always returns zero

    Version 7:
    -Removed dead zone, no noticeable benefit. One less if.
    -Removed counts when searching for maximum minimum, no noticeable benefit since the analogue read repeats solves this problem.

    Version 6:
    -Add constrain to position value to avoid oscillation when actuator is close to limits and activates the switches (sudden change for min to 0 or max to 1024)
    -Uses two functions for speed reasons, this avoids more if's. One gets position cropped and the other one not cropped.

    Version 5:
    -Remove unused functions
    -Remove optional hack for speed, now it's always implemented

    Version 4:
    -More code optimisation, removing some loops
    -For that, functions where created that receive index of actuator
    -Remove direction from array, it's already stored on the signal of the speed

    Version 3:
    Code optimisation
    Renamed functions for better understanding

    Version 2:
    Add minimum and maximum calibration

    Latest version:
    Code:
    // ============================
    // DIY 6DOF Motion Sim by FlyPT
    // ============================
    // SimTools2 Arduino code to control 6DOF actuators
    // Actuators use brushless motors controlled by BLDC8015A drivers
    
    
    // ===================================
    // Update history - Current: Version 8
    // ===================================
    // Version 8:
    //  Corrected error in the PID calculation (affected D where error and last error where already equal, returning always zero for D)
    //  Remove unecessary calculus inthe PID calculation
    //  Commented I calculation, since it's not used and always returns zero
    // Version 7:
    //  Removed dead zone, no noticeable benefit. One less if.
    //  Removed counts when searching for maximum minimum, no noticeable benefit since the analogue read repeats solves this problem.
    // Version 6:
    //  Add constrain to position value to avoid oscillation when actuator is close to limits and activates the switches (sudden change for min to 0 or max to 1024)
    //  Uses two functions for speed reasons, this avoids more if's. One gets position cropped and the other one not cropped.
    // Version 5:
    //  Remove unused functions
    //  Remove optional hack for speed, now it's always implemented
    // Version 4:
    //  More code optimisation, removing some loops
    //  For that, functions where created that receive index of actuator
    //  Remove direction from array, it's already stored on the signal of the speed
    // Version 3:
    //  Code optimisation
    //  Renamed functions for better understanding
    // Version 2:
    //  Add minimum and maximum calibration
    
    
    // ==================================
    // SimTools2 configuration should be:
    // ==================================
    // Interface settings:
    // Interface Type: Serial
    // ComPort: The one used by your Arduino
    // BitsPerSec: 57600 (You can change the define to use another speed)
    // Data Bits: 8
    // Parity: None
    // Stop Bits: 1
    // Output - Bit Range: 10
    // Output - Type: Binary
    // Startup - Output: -
    // HW Start: -
    // Interface output: [1<Axis1a>][2<Axis2a>][3<Axis3a>][4<Axis4a>][5<Axis5a>][6<Axis6a>]
    // Output Rate: 10ms
    // Shutdown - Output: -
    // HW Stop: -
    
    
    // =======
    // Defines
    // =======
    #define NUMBER_ACTUATORS 6  // Define to set the number of actuators to use
    #define SERIAL_SPEED 57600 // Define to set up the serial speed comunication
    #define CALIBRATION_SPEED 60 // Speed used to search for minimum and maximum values
    #define ENDS_GAP 0 // Value added or subtracted to maximum and minimum positions when calibrating
    #define UP HIGH // Define to set up direction as HIGH
    #define DOWN LOW // Define to se down direction as LOW
    #define POSITION_PIN 0 // Holds index where this information is stored in the actuator array
    #define DIRECTION_PIN 1 // Holds index where this information is stored in the actuator array
    #define SPEED_PIN 2 // Holds index where this information is stored in the actuator array
    #define MINIMUM_POSITION 3 // Holds index where this information is stored in the actuator array
    #define MAXIMUM_POSITION 4 // Holds index where this information is stored in the actuator array
    #define CURRENT_POSITION 5 // Holds index where this information is stored in the actuator array
    #define PRETENDED_POSITION 6 // Holds index where this information is stored in the actuator array
    #define P_PID 7 // Holds index where this information is stored in the actuator array
    #define I_PID 8 // Holds index where this information is stored in the actuator array
    #define D_PID 9 // Holds index where this information is stored in the actuator array
    #define INTEGRATED_ERROR 10 // Holds index where this information is stored in the actuator array
    #define LAST_ERROR 11 // Holds index where this information is stored in the actuator array
    #define SPEED 12 // Holds index where this information is stored in the actuator array
    #define ANALOG_READ_REPEATS 16 // Number of times we get analog read to calculate a medium value (to reduce noise, maximum 32)
    // Defines for setting and clearing register bits (used for speed hack)
    #ifndef cbi
    #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
    #endif
    #ifndef sbi
    #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
    #endif
    #define LOWBYTE(v)   ((unsigned char) (v))        //Read
    #define HIGHBYTE(v)  ((unsigned char) (((unsigned int) (v)) >> 8))
    #define BYTELOW(v)   (*(((unsigned char *) (&v) + 1)))      //Write
    #define BYTEHIGH(v)  (*((unsigned char *) (&v)))
    
    
    // ======================
    // Actuators information:
    // ======================
    // 0  - POSITION_PIN       - Pin to capture position from pot
    // 1  - DIRECTION_PIN      - Pin to se direction
    // 2  - SPEED_PIN          - Pin to set speed with PWM
    // 3  - MINIMUM_POSITION   - Minimum position
    // 4  - MAXIMUM_POSITION   - Maximum position
    // 5  - CURRENT_POSITION   - Current position
    // 6  - PRETENDED_POSITION - Pretended position
    // 7  - P_PID              - P from PID
    // 8  - I_PID              - I from PID
    // 9  - D_PID              - D from PID
    // 10 - INTEGRATED_ERROR   - Sum of differences between pretended and current position
    // 11 - LAST_ERROR         - Last difference between pretended and current position
    // 12 - SPEED              - Speed of motor rotation
    int actuator[6][13] = {
      {A0,  2,  3, 1023, 0, 0, 511, 100, 0, 100, 0, 0, 0},   //actuator 1
      {A1,  4,  5, 1023, 0, 0, 511, 100, 0, 100, 0, 0, 0},   //actuator 2
      {A2,  7,  6, 1023, 0, 0, 511, 100, 0, 100, 0, 0, 0},   //actuator 3
      {A3,  8,  9, 1023, 0, 0, 511, 100, 0, 100, 0, 0, 0},   //actuator 4
      {A4, 12, 10, 1023, 0, 0, 511, 100, 0, 100, 0, 0, 0},   //actuator 5
      {A5, 13, 11, 1023, 0, 0, 511, 100, 0, 100, 0, 0, 0}    //actuator 6
    };
    int buffer = 0; // To hold current read fom SimTools command
    int bufferCount = -1; // To hold position in bufferCommand array
    int bufferCommand[5] = {0}; // To hold command info from SimTools
    
    
    // ==============
    // Initialization
    // ==============
    void setup()
    {
      // Initialize serial comunication with SimTools2
      Serial.begin(SERIAL_SPEED);
      // Initialize pins
      for (byte n = 0; n < NUMBER_ACTUATORS; n++)
      {
        pinMode(actuator[n][POSITION_PIN], INPUT); // Pin to get position of actuator (0 to 1023)
        pinMode(actuator[n][DIRECTION_PIN], OUTPUT); // Pin to specify direction of rotation (0 or 1)
        pinMode(actuator[n][SPEED_PIN], OUTPUT); // Pin to specify speed (0 to 255)
      }
      // Speed up the arduino analogue read function setting analogue prescale to 16
      sbi(ADCSRA, ADPS2) ;
      cbi(ADCSRA, ADPS1) ;
      cbi(ADCSRA, ADPS0) ;
      // Calibrate actuators, searching for minimums and maximums
      calibrate();
    }
    
    
    // =======================================================================
    // Get actuator position (used in calibration to find minimum and maximum)
    // =======================================================================
    void getCurrentPosition(byte n)
    {
      // Make one reading
      actuator[n][CURRENT_POSITION] = analogRead(actuator[n][POSITION_PIN]);
      // Get determined number of readings and add them all
      for (byte m = 1; m < ANALOG_READ_REPEATS; m++) actuator[n][CURRENT_POSITION] += analogRead(actuator[n][POSITION_PIN]);
      // Divide to get medium value of the readings
      actuator[n][CURRENT_POSITION] /= ANALOG_READ_REPEATS;
    }
    
    
    // =======================================================================================
    // Get's actuators position constrained to maximum and minimum values found on calibration
    // =======================================================================================
    void getConstrainCurrentPosition(byte n)
    {
      // Make one reading
      actuator[n][CURRENT_POSITION] = analogRead(actuator[n][POSITION_PIN]);
      // Get determined number of readings and add them all
      for (byte m = 1; m < ANALOG_READ_REPEATS; m++) actuator[n][CURRENT_POSITION] += analogRead(actuator[n][POSITION_PIN]);
      // Divide to get medium value of the readings
      actuator[n][CURRENT_POSITION] /= ANALOG_READ_REPEATS;
      // Constrain position value to minimum/maximum range (this avoids oscilation when limit switches are activated)
      constrain(actuator[n][CURRENT_POSITION], actuator[n][MINIMUM_POSITION], actuator[n][MAXIMUM_POSITION]);
    }
    
    
    // =============================
    // Get information from SimTools
    // =============================
    void getSimToolsCommands()
    {
      // Look for start and finish of a command on the serial
      // When we have a command send it form analyze
      while (Serial.available())
      {
        if (bufferCount == -1)
        {
          buffer = Serial.read();
          if (buffer != '[') bufferCount = -1;
          else bufferCount = 0;
        }
        else
        {
          buffer = Serial.read();
          bufferCommand[bufferCount] = buffer;
          bufferCount++;
          if (bufferCount > 3)
          {
            if (bufferCommand[3] == ']') analyzeSimToolsCommand();
            bufferCount = -1;
          }
        }
      }
    }
    
    
    // ================================
    // Analyze command sent by SimTools
    // ================================
    void analyzeSimToolsCommand()
    {
      // Analyze command, see if it's a command and wich actuator is affected
      switch (bufferCommand[0])
      {
        case '1':
          actuator[0][PRETENDED_POSITION] = map((bufferCommand[1] * 256) + bufferCommand[2], 0, 1023, actuator[0][MINIMUM_POSITION], actuator[0][MAXIMUM_POSITION]);
          break;
        case '2':
          actuator[1][PRETENDED_POSITION] = map((bufferCommand[1] * 256) + bufferCommand[2], 0, 1023, actuator[1][MINIMUM_POSITION], actuator[1][MAXIMUM_POSITION]);
          break;
        case '3':
          actuator[2][PRETENDED_POSITION] = map((bufferCommand[1] * 256) + bufferCommand[2], 0, 1023, actuator[2][MINIMUM_POSITION], actuator[2][MAXIMUM_POSITION]);
          break;
        case '4':
          actuator[3][PRETENDED_POSITION] = map((bufferCommand[1] * 256) + bufferCommand[2], 0, 1023, actuator[3][MINIMUM_POSITION], actuator[3][MAXIMUM_POSITION]);
          break;
        case '5':
          actuator[4][PRETENDED_POSITION] = map((bufferCommand[1] * 256) + bufferCommand[2], 0, 1023, actuator[4][MINIMUM_POSITION], actuator[4][MAXIMUM_POSITION]);
          break;
        case '6':
          actuator[5][PRETENDED_POSITION] = map((bufferCommand[1] * 256) + bufferCommand[2], 0, 1023, actuator[5][MINIMUM_POSITION], actuator[5][MAXIMUM_POSITION]);
          break;
        default:
          return;
          break;
      }
    }
    
    
    // ==========================================================
    // Calculate PID for each actuator and update actuators array
    // ==========================================================
    void calculatePID(byte n)
    {
      float error = (float)actuator[n][PRETENDED_POSITION] - (float)actuator[n][CURRENT_POSITION];
      actuator[n][INTEGRATED_ERROR] += error;
      float p = ((float)actuator[n][P_PID] * error);
      //float i = ((float)actuator[n][I_PID] * constrain(actuator[n][INTEGRATED_ERROR], -100, 100)); // Uncomment to use I
      float d = ((float)actuator[n][D_PID] * (error - actuator[n][LAST_ERROR]));
      actuator[n][LAST_ERROR] = error;
      //actuator[n][SPEED] = constrain((p + i + d) / 200.0, -255, 255); // Uncomment to use I
      actuator[n][SPEED] = constrain((p + d) / 200.0, -255, 255); // Comment to use I
    }
    
    
    // ======================================================================
    // Picks values from actuators arrays and sends data to motor controllers
    // ======================================================================
    void updateController(byte n)
    {
      if (actuator[n][SPEED] > 0)
      {
        analogWrite(actuator[n][SPEED_PIN], actuator[n][SPEED]);
        digitalWrite (actuator[n][DIRECTION_PIN], UP);
      }
      else
      {
        analogWrite(actuator[n][SPEED_PIN], -actuator[n][SPEED]);
        digitalWrite (actuator[n][DIRECTION_PIN], DOWN);
      }
    }
    
    
    // =========
    // Main loop
    // =========
    void loop()
    {
      // While to make main loop, avoiding some of the Arduino framework delays
      while (1 == 1)
      {
        getSimToolsCommands();
        for (byte n = 0; n < NUMBER_ACTUATORS; n++)
        {
          getConstrainCurrentPosition(n);
          calculatePID(n);
          updateController(n);
        }
      }
    }
    
    
    // ==================================================
    // Calibration, search for minimum and maximum values
    // ==================================================
    void calibrate()
    {
      getMinimums(); // We might be at the minimum already, better put all at minimum and then search maximum followed by minimum
      getMaximums();
      getMinimums();
    }
    
    
    // ====================================
    // Search for minimums in all actuators
    // ====================================
    void getMinimums()
    {
      byte found = 0;
      bool foundMinimum[6] = {false, false, false, false, false, false};
      for (byte n = 0; n < NUMBER_ACTUATORS; n++)
      {
        digitalWrite (actuator[n][DIRECTION_PIN], DOWN);
        analogWrite(actuator[n][SPEED_PIN], CALIBRATION_SPEED);
      }
      while (found < NUMBER_ACTUATORS)
      {
        for (byte n = 0; n < NUMBER_ACTUATORS; n++)
        {
          getCurrentPosition(n);
          if (!foundMinimum[n])
          {
            if (actuator[n][CURRENT_POSITION] > 0)
            {
              if (actuator[n][CURRENT_POSITION] < actuator[n][MINIMUM_POSITION]) actuator[n][MINIMUM_POSITION] = actuator[n][CURRENT_POSITION];
            }
            else
            {
              analogWrite(actuator[n][SPEED_PIN], 0);
              actuator[n][MINIMUM_POSITION] += ENDS_GAP;
              foundMinimum[n] = true;
              found++;
            }
          }
        }
      }
    }
    
    
    // ====================================
    // Search for maximums in all actuators
    // ====================================
    void getMaximums()
    {
      byte found = 0;
      bool foundMaximum[6] = {false, false, false, false, false, false};
      for (byte n = 0; n < NUMBER_ACTUATORS; n++)
      {
        digitalWrite (actuator[n][DIRECTION_PIN], UP);
        analogWrite(actuator[n][SPEED_PIN], CALIBRATION_SPEED);
      }
      while (found < NUMBER_ACTUATORS)
      {
        for (byte n = 0; n < NUMBER_ACTUATORS; n++)
        {
          getCurrentPosition(n);
          if (!foundMaximum[n])
          {
            if (actuator[n][CURRENT_POSITION] < 1023)
            {
              if (actuator[n][CURRENT_POSITION] > actuator[n][MAXIMUM_POSITION]) actuator[n][MAXIMUM_POSITION] = actuator[n][CURRENT_POSITION];
            }
            else
            {
              analogWrite(actuator[n][SPEED_PIN], 0);
              actuator[n][MAXIMUM_POSITION] -= ENDS_GAP;
              foundMaximum[n] = true;
              found++;
            }
          }
        }
      }
    }
    

    SimTools2 configuration should be:
    Interface settings:
    Interface Type: Serial
    ComPort: The one used by your Arduino
    BitsPerSec: 57600 (You can change the define to use another speed)
    Data Bits: 8
    Parity: None
    Stop Bits: 1
    Output - Bit Range: 10
    Output - Type: Binary
    Startup - Output: -
    HW Start: -
    Interface output: [1<Axis1a>][2<Axis2a>][3<Axis3a>][4<Axis4a>][5<Axis5a>][6<Axis6a>]
    Output Rate: 10ms
    Shutdown - Output: -
    HW Stop: -
    Last edited: Sep 26, 2018
  7. pmvcda

    pmvcda Active Member

    Joined:
    Nov 3, 2010
    Messages:
    126
    Location:
    Portugal
    Balance:
    691Coins
    Ratings:
    +156 / 1 / -0
    My Motion Simulator:
    6DOF
    Electronics
    Schematics used for this project


    INTERNALL HALL SENSOR POSITIONING VERSION WITH ESP32 DEVKIT
    Still using a similar solution to the pot positioning to detect minimum and maximum switches.
    The pot was replace by two resistances (10k):

    No Pot electronics v2.png
    (For full resolution: https://c2.staticflickr.com/2/1966/43113342910_bbfc75294e_o.png)

    So if minimum is pressed we get 0 in the ESP32.
    If maximum is pressed we get 4095 on the ESP32.
    None pressed we get around 2047.
    Now positioning is calculated from reading the hall sensors state.
    Lower resolution than the pot, but I think it's enough.

    For 6 actuators we need two ESP32 boards. They are connected the same way.
    Here's the current schematic in use:

    No Pot electronics 3 actuators.png
    (For full resolution: https://www.flickr.com/photos/128465277@N06/30098511787/sizes/o/)

    And the board I made with the ESP32 DevKit, plugs and level shifters:
    WP_20180727_18_25_46_Rich.jpg

    Mounted in the controllers "box":
    WP_20181112_17_02_57_Rich.jpg
    WP_20180925_17_35_19_Rich.jpg


    POT POSITIONING VERSION WITH ARDUINO UNO

    I moved to the internal hall positioning solution, so this is no longer updated, but here for reference.

    Pot and controller connection for one actuator:
    [​IMG]
    (For full resolution: https://c1.staticflickr.com/5/4389/36059736774_07d10f6b53_o.png)

    The idea is to control all the actuators with one Arduino Uno.
    I get position feedback with pots and two limit switches.
    When the maximum switch is pressed, the pot value is immediately 1023 on the Arduino.
    The same for the minimum, we get 0 when the switch is enabled
    While no limit switch is activated, we get the value from the pot.
    The motor controller needs speed and direction information.
    Speed is fed with PWM and direction is on/off.
    So with this config I can fit everything in one Arduino Uno:
    [​IMG]
    (For full resolution: https://c1.staticflickr.com/5/4362/36059748064_4c644dc11d_o.jpg)
    Last edited: Nov 13, 2018
  8. pmvcda

    pmvcda Active Member

    Joined:
    Nov 3, 2010
    Messages:
    126
    Location:
    Portugal
    Balance:
    691Coins
    Ratings:
    +156 / 1 / -0
    My Motion Simulator:
    6DOF
    I already have two actuators made.
    Built the first one as a prototype, looking for design problems.
    Built the second one, just to find new problems o_O
    So this is the current design (will update the above posts with link to Sketchup files):

    Some Sketchup drawings (those are still WIP):
    [​IMG]

    [​IMG]

    The parts to be printed in solid PETG:
    [​IMG]


    1: Join the screw with the slider and aluminium tube:
    [​IMG]

    [​IMG]

    [​IMG]

    [​IMG]

    Please ignore that bad print, it was already replaced.
    Aluminium tube is hold tight by two screws, one in each side of the PETG "slider".
    The two visible holes will get two screws to mount a GT2 belt


    2: Mount motor support

    Open threads in the aluminium profile:
    [​IMG]

    Slide the motor mount in place and put the screws:
    [​IMG]

    Mount the other side:
    [​IMG]



    3: Mount ball screw, slider and aluminium tube in the motor mount:
    [​IMG]
    • Like Like x 5
    Last edited: Sep 26, 2018
  9. pmvcda

    pmvcda Active Member

    Joined:
    Nov 3, 2010
    Messages:
    126
    Location:
    Portugal
    Balance:
    691Coins
    Ratings:
    +156 / 1 / -0
    My Motion Simulator:
    6DOF
    3: Mount ball screw, slider and aluminium tube in the motor mount:
    [​IMG]

    3: Top end actuator plate.
    Here we have the wheel for the GT2 belt and an Ingus bearing:

    [​IMG]

    [​IMG]

    [​IMG]

    [​IMG]

    [​IMG]

    We need to open some M5 threads on the aluminium:
    [​IMG]
    Only made 4 or 5 by hand, next ones with WD40 and electric drill...

    Testing the mounting:
    [​IMG]
    • Like Like x 3
    Last edited: Oct 2, 2018
  10. pmvcda

    pmvcda Active Member

    Joined:
    Nov 3, 2010
    Messages:
    126
    Location:
    Portugal
    Balance:
    691Coins
    Ratings:
    +156 / 1 / -0
    My Motion Simulator:
    6DOF
    4: Mounting wires and limit switches:

    Solder wires, pass them between aluminium profile and motor mount.
    [​IMG]
    (note: microswitch should be placed before top end or we can't mount the housings for the switches)

    [​IMG]
    Motor already mounted

    Insert micro switches in 3D printed housings and tight them to the aluminium profile:
    [​IMG]

    [​IMG]
    Here we can see the slider touching the switch

    Updated list of parts, software and added a video on the posts at the start of this thread.
    • Like Like x 3
  11. pmvcda

    pmvcda Active Member

    Joined:
    Nov 3, 2010
    Messages:
    126
    Location:
    Portugal
    Balance:
    691Coins
    Ratings:
    +156 / 1 / -0
    My Motion Simulator:
    6DOF
    5: Make cables that connect to the control box.
    In my idea, I want to build a box with all the power supply and controllers.
    When we have kids, just the actuator is already pretty dangerous, so electrics should be really safe and hidden, at least for cosmetic reasons.

    So, since I could not get the needed cables, I made my owns.
    Joined 6 packs of wires:
    [​IMG]

    With 11 wires, 3 for limit switches and pot 3 to power the motor and the rest for the hall sensor:
    [​IMG]

    Joined them at one end so I could put them inside a sleeve:
    [​IMG]

    [​IMG]

    And the result:
    [​IMG]
    • Like Like x 3
  12. pmvcda

    pmvcda Active Member

    Joined:
    Nov 3, 2010
    Messages:
    126
    Location:
    Portugal
    Balance:
    691Coins
    Ratings:
    +156 / 1 / -0
    My Motion Simulator:
    6DOF
    6: Bottom plate
    This holds the pot and the UJoint. Must be strong!

    Printed two equal parts.
    They make a sandwich to hold the pot.
    All wires go inside and the go out in one cable, the one built in the above post:
    [​IMG]

    There's also a part to mount this on the aluminium profile and hold the UJoint:
    [​IMG]

    [​IMG]

    Mounting everything:
    [​IMG]

    [​IMG]

    [​IMG]
    This one with the belt already in place.
    • Like Like x 12
  13. pmvcda

    pmvcda Active Member

    Joined:
    Nov 3, 2010
    Messages:
    126
    Location:
    Portugal
    Balance:
    691Coins
    Ratings:
    +156 / 1 / -0
    My Motion Simulator:
    6DOF
    Some updates to the Arduino code.
    Updated the post about software in the start of the thread with the new code.

    Version 5:

    -Remove unused functions
    -Remove optional hack for speed, now it's always implemented

    Version 4:
    -More code optimisation, removing some loops
    -For that, functions where created that receive index of actuator
    -Remove direction from array, it's already stored on the signal of the speed

    Version 3:
    Code optimisation
    Renamed functions for better understanding

    Version 2:
    Add minimum and maximum calibration
    • Like Like x 1
  14. matthew loomis

    matthew loomis Member

    Joined:
    May 18, 2017
    Messages:
    90
    Location:
    rochester,ny
    Balance:
    440Coins
    Ratings:
    +80 / 0 / -0
    Pretty impressive. You made your own code for the uno. Pretty cool. I was trying to avoid that by using rc components. Much better solution. How do you handle the pid tuning? I've seen some arduino code for self tuning pid but don't have the skills to paste that into an existing sketch.
    Glad someone got brushless working.
    • Like Like x 1
  15. pmvcda

    pmvcda Active Member

    Joined:
    Nov 3, 2010
    Messages:
    126
    Location:
    Portugal
    Balance:
    691Coins
    Ratings:
    +156 / 1 / -0
    My Motion Simulator:
    6DOF
    PID tuning is manual, we can change the values in the array for each motor.
    Really curious about that code with self tuning. Can you give me a link?
    I'm still having vibrations in stand still, around 10º rotation off the motor to each side.
    I'm uploading a video right now with 2 actuators and this code. Still some time to go...

    EDIT:
    Found the Arduino library for PID control and tuning, will have a look.
    But it must be fast for 6 motors.
    I still have to tune mine manually.
    Last edited: Aug 31, 2017
  16. matthew loomis

    matthew loomis Member

    Joined:
    May 18, 2017
    Messages:
    90
    Location:
    rochester,ny
    Balance:
    440Coins
    Ratings:
    +80 / 0 / -0

    GitHub - jackw01/arduino-pid-autotuner: Automated PID tuning using Ziegler-Nichols/relay method on Arduino and compatible boards
    https://github.com/jackw01/arduino-pid-autotuner
    • Like Like x 1
  17. pmvcda

    pmvcda Active Member

    Joined:
    Nov 3, 2010
    Messages:
    126
    Location:
    Portugal
    Balance:
    691Coins
    Ratings:
    +156 / 1 / -0
    My Motion Simulator:
    6DOF
    Uploaded a new video.
    We can see two actuators working with LFS (like a 2DOF).
    At the start, the Arduino code is searching for the minimum and maximum positions, performing a basic calibration.

    • Like Like x 6
  18. SeatTime

    SeatTime Well-Known Member SimTools 2.0 Beta Tester

    Joined:
    Dec 27, 2013
    Messages:
    2,034
    Location:
    Newcastle Australia
    Balance:
    25,397Coins
    Ratings:
    +2,343 / 20 / -0
    My Motion Simulator:
    DC motor, Motion platform, 6DOF
    Nice :thumbs, What sort of load can they lift?
  19. baykah

    baykah Active Member

    Joined:
    Sep 1, 2016
    Messages:
    119
    Location:
    Toulouse - France
    Balance:
    616Coins
    Ratings:
    +123 / 0 / -0
    My Motion Simulator:
    6DOF
    Nice !

    Carefull with the potentiometers, I had bad experience with cheap pots ... and the way you mounted them all the force of the belt tension goes to the pot shaft ...

    It would be better to have the pot axe free from stress ..
    • Agree Agree x 2
  20. alexdixey

    alexdixey New Member

    Joined:
    May 17, 2017
    Messages:
    28
    Occupation:
    Designer
    Location:
    Gardiner, NY USA
    Balance:
    213Coins
    Ratings:
    +33 / 0 / -0
    My Motion Simulator:
    4DOF
    Beautiful craftsmanship.. I like the 3 d printed parts on aluminum profiles together.
    Are the plastic parts solid density?
    Do you use special filament?
    What is the actuator max speed ?

    Thanks