Duet3D Logo Duet3D
    • Tags
    • Documentation
    • Order
    • Register
    • Login

    Five bar Parallel SCARA print area problem

    Scheduled Pinned Locked Moved
    My Duet controlled machine
    4
    73
    4.2k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • rutkuundefined
      rutku
      last edited by rutku

      Hello there

      @Joergs5 and @bondus thank you very much.

      I've been working on Five Bar SCARA since last week. I couldn't determine the print area. I get the following error after the G28 X command. "ERROR: G0 / G1: TARGET Position Not Reachable from Current Position" .Firmware recompiling the variables by "Debugprintf". I'm putting my mistake and setting files in the below. If you are helpful I will appreciate it.

      fiveBarScara_topView.jpg


      VIDEO : Five Bar Parallel Scara Home


      config.g file

      ; General preferences
      G90 ; Send absolute coordinates...
      M83 ; ...but relative extruder moves
      M552 S1 ; Turn network on
      
      M586 P0 S1 ; Enable HTTP
      M586 P1 S0 ; Enable FTP
      M586 P2 S0 ; Enable Telnet
      
      ; Drives
      M569 P0 S1 ; Drive 0 (X) goes forwards
      M569 P1 S1 ; Drive 1 (Y) goes forwards
      ;M569 P2 S1 ; Drive 2 (Z) goes forwards
      ;M569 P3 S1 ; Drive 3 (E0) goes forwards
      M584 X0 Y1 ; set drive mapping
      
      M350 X128 Y128 I1 ;S3 ; Configure microstepping with interpolation
      M669 K9 L2 X-95:95 Y0:0 P130:130 D186:186:0:0 B30:270 A15:165:0:360:0:360 C270:0:0:270
      M92 X1444.444444 Y1444.444444 Z400 E95.2 ; Set steps per mm
      
      M203 X10000 Y10000 ; maximum speeds mm/minute
      
      M566 X15 Y15 ; Set maximum instantaneous speed changes (mm/min)
      M201 X40 Y40 ; Set accelerations (mm/s^2)
      M906 X1200 Y1200 ; Set motor currents (mA) and motor idle factor in per cent
      
      M84 S0 ; Set idle timeout
      
      ; Axis Limits
      M208 X1000:-1000 Y1000:-1000 ; set axis minima and maxima
      
      ; Endstops
      M574 X1 S1 P"xstop"   ; X min active high endstop switch
      M574 Y2 S1 P"ystop"   ; Y min active high endstop switch
      ;M574 Z0 S1 P"zstop"   ; Z min active high endstop switch
      

      home5barscara.g file

      G91              ; relative positioning
      
      G1 H1 X100 Y300  F900 ; move quickly to endstop and stop there (first pass)
      G1 H1 X300 F900
      G1 H1 Y300 F900
      G1 H2 X-2 Y-2 F900      ; go back a few degrees
      G1 H1 X100 F90  ; move slowly to endstop once more (second pass)
      G1 H1 Y100 F90  ; move slowly to endstop once more (second pass)
      
      G92 Z0
      G90              ; absolute positioning
      G0 X0 Y0 F6000 ; move to a reasonable position
      

      Debug outputs:

      m115
      FIRMWARE_NAME: RepRapFirmware for STM32F4 based Boards FIRMWARE_VERSION: 3.2.2_2 ELECTRONICS: STM32F4 FIRMWARE_DATE: 2021-05-02
      ok
      g28 x
      
      getForwad->getIntersec(distalL:186.00,distalR:186.00,xL:-142.06,yL:121.18,xR:95.00,yR:-130.00)
      getIntersec firstRadius: 186.00 secondRadius: 186.00 firstX: -142.06 firstY: 121.18 secondX: 95.00 secondY: -130.00
      getForwad->getIntersec(distalL:186.00,distalR:186.00,xL:-142.06,yL:121.18,xR:95.00,yR:-130.00)
      getIntersec firstRadius: 186.00 secondRadius: 186.00 firstX: -142.06 firstY: 121.18 secondX: 95.00 secondY: -130.00
      getForwad->getIntersec(distalL:186.00,distalR:186.00,xL:17.58,yL:65.00,xR:95.00,yR:-130.00)
      getIntersec firstRadius: 186.00 secondRadius: 186.00 firstX: 17.58 firstY: 65.00 secondX: 95.00 secondY: -130.00
      getForwad->getIntersec(distalL:186.00,distalR:186.00,xL:17.58,yL:65.00,xR:95.00,yR:-130.00)
      getIntersec firstRadius: 186.00 secondRadius: 186.00 firstX: 17.58 firstY: 65.00 secondX: 95.00 secondY: -130.00
      getForwad->getIntersec(distalL:186.00,distalR:186.00,xL:17.58,yL:65.00,xR:95.00,yR:-130.00)
      getIntersec firstRadius: 186.00 secondRadius: 186.00 firstX: 17.58 firstY: 65.00 secondX: 95.00 secondY: -130.00
      getForwad->getIntersec(distalL:186.00,distalR:186.00,xL:19.78,yL:61.03,xR:90.46,yR:-129.92)
      getIntersec firstRadius: 186.00 secondRadius: 186.00 firstX: 19.78 firstY: 61.03 secondX: 90.46 secondY: -129.92
      getForwad->getIntersec(distalL:186.00,distalR:186.00,xL:19.78,yL:61.03,xR:90.46,yR:-129.92)
      getIntersec firstRadius: 186.00 secondRadius: 186.00 firstX: 19.78 firstY: 61.03 secondX: 90.46 secondY: -129.92
      getForwad->getIntersec(distalL:186.00,distalR:186.00,xL:17.58,yL:65.00,xR:90.46,yR:-129.92)
      getIntersec firstRadius: 186.00 secondRadius: 186.00 firstX: 17.58 firstY: 65.00 secondX: 90.46 secondY: -129.92
      getForwad->getIntersec(distalL:186.00,distalR:186.00,xL:17.58,yL:65.00,xR:90.46,yR:-129.92)
      getIntersec firstRadius: 186.00 secondRadius: 186.00 firstX: 17.58 firstY: 65.00 secondX: 90.46 secondY: -129.92
      getForwad->getIntersec(distalL:186.00,distalR:186.00,xL:17.58,yL:65.00,xR:95.00,yR:-130.00)
      getIntersec firstRadius: 186.00 secondRadius: 186.00 firstX: 17.58 firstY: 65.00 secondX: 95.00 secondY: -130.00
      getInverse not cantilevered proximalL:130.00 distalL:186.00 xOrigL:-95.00 yOrigL:0.00 x_0:199.05 y_0:24.18
      getTheta
      getIntersec firstRadius: 130.00 secondRadius: 186.00 firstX: -95.00 firstY: 0.00 secondX: 199.05 secondY: 24.18
      getTheta use: 1 x2: 26.69 y2: -45.74 thetaB: 339.40 x1: 17.58 y1: 65.00 thetaA: 30.00
      getInverse not cantilevered proximalR:130.00 distalR:186.00 xOrigR:95.00 yOrigR:0.00 x_0:199.05 y_0:24.18
      getTheta
      getIntersec firstRadius: 130.00 secondRadius: 186.00 firstX: 95.00 firstY: 0.00 secondX: 199.05 secondY: 24.18
      getTheta use: 2 x2: 95.00 y2: -130.00 thetaB: 270.00 x1: 37.68 y1: 116.68 thetaA: 116.16
      getInverse cachedInvalid 0 => cachedX0:199.05 cachedY0:24.18 cachedX1:199.05 cachedY1:24.18
      cachedXL:17.58 cachedYL:65.00 cachedXR:95.00 cachedYR:-130.00 cachedThetaR:270.00 cachedXR:95.00
      cachedYR:-130.00 cachedThetaL:30.00 cachedXL:17.58 cachedYL:65.00
      getInverse => cachedInvalid:0 x_0:199.05 y_0:24.18 xL:17.58 yL:65.00 thetaL:30.00 xR:95.00 yR:-130.00 thetaR:270.00 x1:199.05 y1:24.18
      getInverse not cantilevered proximalL:130.00 distalL:186.00 xOrigL:-95.00 yOrigL:0.00 x_0:199.05 y_0:24.18
      getTheta
      getIntersec firstRadius: 130.00 secondRadius: 186.00 firstX: -95.00 firstY: 0.00 secondX: 199.05 secondY: 24.18
      getTheta use: 1 x2: 26.69 y2: -45.74 thetaB: 339.40 x1: 17.58 y1: 65.00 thetaA: 30.00
      getInverse not cantilevered proximalR:130.00 distalR:186.00 xOrigR:95.00 yOrigR:0.00 x_0:199.05 y_0:24.18
      getTheta
      getIntersec firstRadius: 130.00 secondRadius: 186.00 firstX: 95.00 firstY: 0.00 secondX: 199.05 secondY: 24.18
      getTheta use: 2 x2: 95.00 y2: -130.00 thetaB: 270.00 x1: 37.68 y1: 116.68 thetaA: 116.16
      getInverse cachedInvalid 0 => cachedX0:199.05 cachedY0:24.18 cachedX1:199.05 cachedY1:24.18
      cachedXL:17.58 cachedYL:65.00 cachedXR:95.00 cachedYR:-130.00 cachedThetaR:270.00 cachedXR:95.00
      cachedYR:-130.00 cachedThetaL:30.00 cachedXL:17.58 cachedYL:65.00
      getInverse => cachedInvalid:0 x_0:199.05 y_0:24.18 xL:17.58 yL:65.00 thetaL:30.00 xR:95.00 yR:-130.00 thetaR:270.00 x1:199.05 y1:24.18
      getInverse not cantilevered proximalL:130.00 distalL:186.00 xOrigL:-95.00 yOrigL:0.00 x_0:100.00 y_0:200.00
      getTheta
      getIntersec firstRadius: 130.00 secondRadius: 186.00 firstX: -95.00 firstY: 0.00 secondX: 100.00 secondY: 200.00
      getTheta use: 1 x2: 32.21 y2: 26.79 thetaB: 11.89 x1: -71.43 y1: 127.85 thetaA: 79.56
      getInverse not cantilevered proximalR:130.00 distalR:186.00 xOrigR:95.00 yOrigR:0.00 x_0:100.00 y_0:200.00
      getTheta
      getIntersec firstRadius: 130.00 secondRadius: 186.00 firstX: 95.00 firstY: 0.00 secondX: 100.00 secondY: 200.00
      getTheta use: 2 x2: 213.77 y2: 52.85 thetaB: 23.99 x1: -20.98 y1: 58.72 thetaA: 153.15
      getInverse cachedInvalid 0 => cachedX0:100.00 cachedY0:200.00 cachedX1:100.00 cachedY1:200.00
      cachedXL:-71.43 cachedYL:127.85 cachedXR:213.77 cachedYR:52.85 cachedThetaR:23.99 cachedXR:213.77
      cachedYR:52.85 cachedThetaL:79.56 cachedXL:-71.43 cachedYL:127.85
      getInverse => cachedInvalid:0 x_0:100.00 y_0:200.00 xL:-71.43 yL:127.85 thetaL:79.56 xR:213.77 yR:52.85 thetaR:23.99 x1:100.00 y1:200.00Error: G0/G1: target position not reachable from current position
      ok
      
      dc42undefined JoergS5undefined 2 Replies Last reply Reply Quote 0
      • dc42undefined
        dc42 administrators @rutku
        last edited by

        After the second pass homing moves, try going back a few degrees using e.g. G1 H2 X-5 Y-5.

        Duet WiFi hardware designer and firmware engineer
        Please do not ask me for Duet support via PM or email, use the forum
        http://www.escher3d.com, https://miscsolutions.wordpress.com

        rutkuundefined 1 Reply Last reply Reply Quote 0
        • rutkuundefined
          rutku @dc42
          last edited by rutku

          @dc42

          Thank you very much for the answer.

          I changed the part in the home5barscara.g file as you said.

          G1 H2 X-2 Y-2 F900 -> G1 H2 X-5 Y-5 F900

          It still gives the same error.

          "Error: G0/G1: target position not reachable from current position"

          I can't add the same error output. Akismet detects it as spam.

          dc42undefined 1 Reply Last reply Reply Quote 0
          • dc42undefined
            dc42 administrators @rutku
            last edited by

            @rutku my suggestion was not to change the backup move you already had, but to add another backup move after the second pair of homing commands. I made this suggestion because on serial SCARA printers, this is necessary before you can do a G1 move after homing.

            Does a G0 move succeed?

            Duet WiFi hardware designer and firmware engineer
            Please do not ask me for Duet support via PM or email, use the forum
            http://www.escher3d.com, https://miscsolutions.wordpress.com

            rutkuundefined 1 Reply Last reply Reply Quote 0
            • JoergS5undefined
              JoergS5 @rutku
              last edited by JoergS5

              @rutku said in Five bar Parallel SCARA print area problem:

              M208 X1000:-1000 Y1000:-1000 ; set axis minima and maxima

              hello, nice to see another example of a parallel scara. Welcome!

              M208 looks strange
              M208 X1000:-1000 Y1000:-1000 ; set axis minima and maxima
              I would set minima to -1000 and maxima to 1000.
              So
              M208 X-1000:1000 Y-1000:1000

              The
              G0 X0 Y0 F6000 ; move to a reasonable position
              in the homing file should give an error already.

              Another possibility is that an angle restriction limit is reached, in parameter A.
              And please check parameter C also, if I rember well, the values should be ordered min-max, so the low values first:
              instead of C270:0:0:270
              take
              C0:270:0:270

              rutkuundefined 1 Reply Last reply Reply Quote 0
              • rutkuundefined
                rutku @dc42
                last edited by

                @dc42

                Yes, now I get it. Thanks ... I added as below. I got the same error again.

                home5barscara.g

                G91              ; relative positioning
                
                G1 H1 X100 Y300  F900 ; move quickly to endstop and stop there (first pass)
                G1 H1 X300 F900
                G1 H1 Y300 F900
                G1 H2 X-2 Y-2 F900      ; go back a few degrees
                G1 H1 X100 F90  ; move slowly to endstop once more (second pass)
                G1 H1 Y100 F90  ; move slowly to endstop once more (second pass)
                G1 H2 X-5 Y-5 F900      ; go back a few degrees
                
                G92 Z0
                G90              ; absolute positioning
                G0 X0 Y0 F6000 ; move to a reasonable position
                

                G0 X0 Y0 F6000 command was not successful. The error output I received;

                G0 X0 Y0 F6000
                
                getInverse not cantilevered proximalL:130.00 distalL:186.00 xOrigL:-95.00 yOrigL:0.00 x_0:0.00 y_0:0.00
                getTheta
                getIntersec firstRadius: 130.00 secondRadius: 186.00 firstX: -95.00 firstY: 0.00 secondX: 0.00 secondY: 0.00
                getTheta use: 1 x2: -140.64 y2: -121.73 thetaB: 249.45 x1: -140.64 y1: 121.73 thetaA: 110.55
                getInverse not cantilevered proximalR:130.00 distalR:186.00 xOrigR:95.00 yOrigR:0.00 x_0:0.00 y_0:0.00
                getTheta
                getIntersec firstRadius: 130.00 secondRadius: 186.00 firstX: 95.00 firstY: 0.00 secondX: 0.00 secondY: 0.00
                getTheta use: 2 x2: 140.64 y2: 121.73 thetaB: 69.45 x1: 140.64 y1: -121.73 thetaA: 290.55
                getInverse cachedInvalid 0 => cachedX0:0.00 cachedY0:0.00 cachedX1:0.00 cachedY1:0.00
                cachedXL:-140.64 cachedYL:121.73 cachedXR:140.64 cachedYR:121.73 cachedThetaR:69.45 cachedXR:140.64
                cachedYR:121.73 cachedThetaL:110.55 cachedXL:-140.64 cachedYL:121.73
                getInverse => cachedInvalid:0 x_0:0.00 y_0:0.00 xL:-140.64 yL:121.73 thetaL:110.55 xR:140.64 yR:121.73 thetaR:69.45 x1:0.00 y1:0.00Error: G0/G1: target position not reachable from current position
                ok
                
                
                1 Reply Last reply Reply Quote 0
                • rutkuundefined
                  rutku @JoergS5
                  last edited by

                  @joergs5

                  hello, thank you very much. I did what you said. It made very limited movements. moved in negative directions. I could not understand how to solve it. I am sending it as a video.

                  Video

                  JoergS5undefined 2 Replies Last reply Reply Quote 0
                  • JoergS5undefined
                    JoergS5 @rutku
                    last edited by JoergS5

                    This post is deleted!
                    1 Reply Last reply Reply Quote 0
                    • JoergS5undefined
                      JoergS5 @rutku
                      last edited by JoergS5

                      @rutku ok I think I found something:

                      according to https://duet3d.dozuki.com/Guide/Five+Bar+Parallel+SCARA/24#s90 the B parameter the homing position are the degrees in respect to the X axis, so the first one has angle 30 degree, but the second one -90 degree.

                      You can try the current endstops and set the second endstop position to -90 instead of 270. B30:-90 and force work mode 1 with L1.
                      Reason for workmode 1 please see https://duet3d.dozuki.com/Guide/Five+Bar+Parallel+SCARA/24#s92

                      If this doesn't work because of the negative second endstop angle, maybe you can place your second endstop so you can use working mode 1 with eg 30 degree for the second endstop also.

                      I am not sure whether I have tested negative endstop angles. But please try, it may work. This is faster than to change the endstop location.

                      The print area for workmode 1 and your homing position is near the edge of the printable area, which may mean in this area movement is critical (maybe even near a singularity). After homing it is better to rotate both arms forward to say 90 degree each (but remove the chair/laptop before doing this πŸ˜‰ ) . This is a safe area for testing movements.

                      You have to change parameter C to include -90 for the second homing position also:
                      C0:270:-90:270

                      rutkuundefined 1 Reply Last reply Reply Quote 0
                      • rutkuundefined
                        rutku @JoergS5
                        last edited by

                        @joergs5

                        Hi, I did what you said. I get the same mistake. Perhaps the problem is in coordinates. I have moved the computer not improved :). It is not moved to 0 points of X and Y coordinates. I think it needs to make offset.

                        workMode2.PNG

                        I have changed the first endstop flour. Again was the same mistake.
                        workMode1.PNG
                        config.g

                        ...
                        M669 K9 L1 X-95:95 Y0:0 P130:130 D186:186:0:0 B-20:-90 A15:165:0:360:0:360 C-20:270:-90:270 
                        ...
                        ; Endstops
                        M574 X2 S1 P"xstop"   ; X min active high endstop switch
                        M574 Y2 S1 P"ystop"   ; Y min active high endstop switch
                        

                        workMode1_scara.jpg

                        dc42undefined JoergS5undefined 2 Replies Last reply Reply Quote 0
                        • dc42undefined
                          dc42 administrators @rutku
                          last edited by

                          @rutku it looks to mew that X0 Y0 is not reachable on your machine because of its geometry. Does it work if you command it to reachable coordinates?

                          The serial SCARA kinematics allow you to shift the X0 Y0 position using X and Y parameters in the M669 command. Perhaps @JoergS5 can tell you whether the five-bar kinematics provides a similar feature.

                          Duet WiFi hardware designer and firmware engineer
                          Please do not ask me for Duet support via PM or email, use the forum
                          http://www.escher3d.com, https://miscsolutions.wordpress.com

                          JoergS5undefined rutkuundefined 2 Replies Last reply Reply Quote 0
                          • JoergS5undefined
                            JoergS5 @dc42
                            last edited by

                            @dc42 said in Five bar Parallel SCARA print area problem:

                            Perhaps @JoergS5 can tell you whether the five-bar kinematics provides a similar feature.

                            yes, https://duet3d.dozuki.com/Guide/Five+Bar+Parallel+SCARA/24#s90 documents to set X and X for the actuator positions: absolute coordinates and how far they are away from each other.

                            But even if you change the coordinates, the position itself is still unreachable or at the edge of the reachable area. Preferable would be to place the second endstop to a similar 30 degree position like the first one and use L1 mode, work mode 1. Then after homing, the printer is in a comfortable print area.

                            1 Reply Last reply Reply Quote 0
                            • JoergS5undefined
                              JoergS5 @rutku
                              last edited by JoergS5

                              @rutku please try

                              • the homing positions should place the proximal arms in a position like in the images for workmode 1 or 2
                              • the actuators have far distance, in your first picture you're already near of the singularity: the distal arms are nearly horizontal, which means you need a lot of force to rotate and when they are horizontal, the arms can move up or down, which means if youre unlucky, they move down. Better make the distal arms a bit longer (or use workmode 1)

                              In your last image, you changed one endstop, but I meant the other one, so both proximal arms are "above" the X axis.

                              I mean like this for workmode L1:

                              parallelScaraSmall.jpg

                              JoergS5undefined 1 Reply Last reply Reply Quote 0
                              • JoergS5undefined
                                JoergS5 @JoergS5
                                last edited by JoergS5

                                To explain the work modes: every proximal arm has two possible solutions. The workmode tells which one to take. Example:

                                parallelScaraTwoSolutionsSmall.jpg
                                The firmware cannot decide which one to use from the actuator information and arm length information alone. Workmode 1 says to use the first one always.

                                Your last image shows something similar to workmode 3: proximal arms are direction down. Distal arms are looking up. This mode is not supported yet. I wanted to avoid workmodes, but as explained there must be decided somehow which of the two possibilities is the correct one.

                                JoergS5undefined 1 Reply Last reply Reply Quote 0
                                • JoergS5undefined
                                  JoergS5 @JoergS5
                                  last edited by JoergS5

                                  By singularities and avoid too big actuator distances I mean:

                                  parallelScaraSingularity.jpg

                                  The first one is when the distal arms are horizontally. Then actuator rotation forces the arms to go up or down, which is very bad if you want only workmode 1, distal arms being upward. Another disadvantage if you're near this situation is that the actuators need more force than normal to rotate and the resulting position is less accurate.

                                  The second case is if the proximal arms are rotated so they result in one destination point. Then the distal arms' angle is totally unspecified.

                                  1 Reply Last reply Reply Quote 0
                                  • rutkuundefined
                                    rutku @dc42
                                    last edited by rutku

                                    @dc42 said in Five bar Parallel SCARA print area problem:

                                    @rutku it looks to mew that X0 Y0 is not reachable on your machine because of its geometry. Does it work if you command it to reachable coordinates?

                                    Yes, it makes limited movements. X 200 -> 185~ Y 29 -> 78

                                    @JoergS5 Thanks, you explained very well like a teacher :).

                                    workMode1_30degree.jpg

                                    I did what you said. I still get the same error. My idea is that enstopes should be used and removed once. Because the arms want to move behind. I ordered an optical sensor. I will try it. Because arms want to be free.

                                    config.g

                                    M669 K9 L1 X-95:95 Y0:0 P130:130 D186:186:0:0 B30:30 A15:165:0:360:0:360 C30:270:30:270 
                                    
                                    ; Endstops
                                    M574 X1 S1 P"xstop"   ; X min active high endstop switch
                                    M574 Y1 S1 P"ystop"   ; Y min active high endstop switch
                                    

                                    error code:

                                    M120 G91 G1 X10 F6000 G90 M121
                                    Error: G0/G1: target position not reachable from current position
                                    
                                    JoergS5undefined 1 Reply Last reply Reply Quote 0
                                    • JoergS5undefined
                                      JoergS5 @rutku
                                      last edited by JoergS5

                                      @rutku G1 X10 means you want to go to a position a bit right (right in the sense of X-Axis direction) of the homing position. This would mean you have to rotate the actuators to an angle smaller 30 degree. This is not allowed (C parameter 30 limit, and your current endstop type).

                                      After homing, please try G1 X-10 first several times to get away from the homing position. Maybe you have to move Y a bit at the same time at the beginning, like G1 X-10 Y10

                                      ...limited movements. X 200 -> 185~ Y 29 -> 78
                                      makes sense: the print area is in the diminishing X, and bigger actuator angles mean the hotend moves to a higher Y.

                                      Depending on your hardware,
                                      M350 X128 Y128 I1
                                      may be a too high microstepping value. You can check whether you get lost steps by making some movements, then calling M122 and look at the hiccups value. A high number would mean you loose steps. Often 16 microsteps are used, M350 X16 Y16 I1

                                      rutkuundefined 1 Reply Last reply Reply Quote 0
                                      • rutkuundefined
                                        rutku @JoergS5
                                        last edited by

                                        @joergs5 Hi, I did what you said. However, I'm getting the same mistake again. In the output of debug ThetaL: 249.5 ThetaR: 69.45.

                                        config.g

                                        M350 X16 Y16 I1 ;S3 ; Configure microstepping with interpolation
                                        M669 K9 L1 X-95:95 Y0:0 P130:130 D186:186:0:0 B30:30 A15:165:0:360:0:360 C-90:270:-90:270 
                                        M92 X115.555556 Y115.555556 ; Set steps per mm
                                        

                                        Debug Output

                                        getInverse not cantilevered proximalL:130.00 distalL:186.00 xOrigL:-95.00 yOrigL:0.00 x_0:0.00 y_0:0.00
                                        getTheta
                                        getIntersec firstRadius: 130.00 secondRadius: 186.00 firstX: -95.00 firstY: 0.00 secondX: 0.00 secondY: 0.00
                                        getTheta use: 2 x2: -140.64 y2: -121.73 thetaB: 249.45 x1: -140.64 y1: 121.73 thetaA: 110.55
                                        getInverse not cantilevered proximalR:130.00 distalR:186.00 xOrigR:95.00 yOrigR:0.00 x_0:0.00 y_0:0.00
                                        getTheta
                                        getIntersec firstRadius: 130.00 secondRadius: 186.00 firstX: 95.00 firstY: 0.00 secondX: 0.00 secondY: 0.00
                                        getTheta use: 2 x2: 140.64 y2: 121.73 thetaB: 69.45 x1: 140.64 y1: -121.73 thetaA: 290.55
                                        getInverse cachedInvalid 0 => cachedX0:0.00 cachedY0:0.00 cachedX1:0.00 cachedY1:0.00
                                        cachedXL:-140.64 cachedYL:-121.73 cachedXR:140.64 cachedYR:121.73 cachedThetaR:69.45 cachedXR:140.64
                                        cachedYR:121.73 cachedThetaL:249.45 cachedXL:-140.64 cachedYL:-121.73
                                        getInverse => cachedInvalid:0 x_0:0.00 y_0:0.00 xL:-140.64 yL:-121.73 thetaL:249.45 xR:140.64 yR:121.73 thetaR:69.45 x1:0.00 y1:0.00
                                        constraintsOK->cachedInvalid:0 actuatorAngleLMin:-90.00<0 && thetaL:249.45>actuatorAngleLMax:270.00
                                        constraintsOK->cachedInvalid:0 thetaL:249.45 < actuatorAngleLMin:-90.00 || thetaL:249.45 > actuatorAngleLMax:270.00 return false
                                        constraintsOK->cachedInvalid:0 actuatorAngleRMin:-90.00<0 && thetaR:69.45>actuatorAngleRMax:270.00
                                        constraintsOK->cachedInvalid:0 thetaR:69.45 < actuatorAngleRMin:-90.00 || thetaR:69.45 > actuatorAngleRMax:270.00 return false
                                        constraintsOK->cachedInvalid:0 headAngle:180.00 < headAngleMin:15.00 || headAngle:180.00 > headAngleMax:165.00 isnan(headAngle):0 return false
                                        
                                        JoergS5undefined 1 Reply Last reply Reply Quote 0
                                        • JoergS5undefined
                                          JoergS5 @rutku
                                          last edited by JoergS5

                                          @rutku said in Five bar Parallel SCARA print area problem:

                                          => Please comment out the G0 Y0 Y0 line at the end of the homing file, so the values when the endstops are triggered can be controlled.

                                          Then start Duet, and run the homing file. Both endstops must be triggered. Please report M114.

                                          ThetaL and ThetaR should be 30 each at the homing position.

                                          I suspect that the G0 X0 Y0, which is at the end of the homing file, is not reachable. My documentation in https://duet3d.dozuki.com/Guide/Five+Bar+Parallel+SCARA/24#s92 is only a draft of the printable area.

                                          ThetaL: 249.5 (= 180 + 69.5) ThetaR: 69.45.
                                          mean the both arms are nearly in opposite positions, and singularity example 1 of my explanation above is met:

                                          parallelScara00.jpg

                                          rutkuundefined 1 Reply Last reply Reply Quote 0
                                          • rutkuundefined
                                            rutku @JoergS5
                                            last edited by

                                            @joergs5 How could it be? I have printed almost all values ​​in the FiveBarScaraKinematics.cpp file. I am adding all my settings and outputs again. I am adding the video of the process steps.

                                            config.g

                                            ; General preferences
                                            G90 ; Send absolute coordinates...
                                            M83 ; ...but relative extruder moves
                                            M552 S1 ; Turn network on
                                            
                                            M586 P0 S1 ; Enable HTTP
                                            M586 P1 S0 ; Enable FTP
                                            M586 P2 S0 ; Enable Telnet
                                            
                                            ; Drives
                                            M569 P0 S1 ; Drive 0 (X) goes forwards
                                            M569 P1 S1 ; Drive 1 (Y) goes forwards
                                            ;M569 P2 S1 ; Drive 2 (Z) goes forwards
                                            ;M569 P3 S1 ; Drive 3 (E0) goes forwards
                                            M584 X0 Y1 ; set drive mapping
                                            
                                            M350 X16 Y16 I1 ;S3 ; Configure microstepping with interpolation
                                            M669 K9 L1 X-95:95 Y0:0 P130:130 D210:210:0:0 B30:30 A15:165:0:360:0:360 C-90:270:-90:270 
                                            M92 X115.555556 Y115.555556 ; Set steps per mm
                                            
                                            M203 X10000 Y10000 ; maximum speeds mm/minute
                                            
                                            M566 X15 Y15 ; Set maximum instantaneous speed changes (mm/min)
                                            M201 X40 Y40 ; Set accelerations (mm/s^2)
                                            M906 X1200 Y1200 ; Set motor currents (mA) and motor idle factor in per cent
                                            
                                            M84 S0 ; Set idle timeout
                                            
                                            ; Axis Limits
                                             M208 X-1000:1000 Y-1000:1000 ; set axis minima and maxima
                                            
                                            ; Endstops
                                            M574 X1 S1 P"xstop"   ; X min active high endstop switch
                                            M574 Y1 S1 P"ystop"   ; Y min active high endstop switch
                                            ;M574 Z0 S1 P"zstop"   ; Z min active high endstop switch
                                            

                                            home5barscara.g

                                            G91              ; relative positioning
                                            
                                            G1 H1 X100 Y300  F900 ; move quickly to endstop and stop there (first pass)
                                            G1 H1 X300 F900
                                            G1 H1 Y300 F900
                                            G1 H2 X-2 Y-2 F900      ; go back a few degrees
                                            G1 H1 X100 F90  ; move slowly to endstop once more (second pass)
                                            G1 H1 Y100 F90  ; move slowly to endstop once more (second pass)
                                            G1 H2 X-5 Y-5 F900      ; go back a few degrees
                                            
                                            G92 Z0
                                            G90              ; absolute positioning
                                            G0 X0 Y0 ; move to a reasonable position
                                            

                                            outputs:

                                            m115
                                            FIRMWARE_NAME: RepRapFirmware for STM32F4 based Boards FIRMWARE_VERSION: 3.2.2_2 ELECTRONICS: STM32F4 FIRMWARE_DATE: 2021-05-02
                                            ok
                                            WiFi module is connected to access point Utku, IP address 192.168.1.3
                                            
                                            MotorStepsToCartesian => thetaL:224.27=MotorPosX:25916.00/StepsPermmX:115.56 
                                            MotorStepsToCartesian => thetaR:224.27=MotorPosY:3467.00/StepsPermmY:115.56 
                                            getForwad->getIntersec(distalL:210.00,distalR:210.00,xL:-188.08,yL:-90.75,xR:207.58,yR:65.01)
                                            getIntersec firstRadius: 210.00 secondRadius: 210.00 firstX: -188.08 firstY: -90.75 secondX: 207.58 secondY: 65.01
                                            MotorStepsToCartesian => MachinePosX:nan MachinePosY:nan XYZ_AXES:3 numVisibleAxes:3
                                            MotorStepsToCartesian => thetaL:224.27=MotorPosX:25916.00/StepsPermmX:115.56 
                                            MotorStepsToCartesian => thetaR:224.27=MotorPosY:3467.00/StepsPermmY:115.56 
                                            getForwad->getIntersec(distalL:210.00,distalR:210.00,xL:-188.08,yL:-90.75,xR:207.58,yR:65.01)
                                            getIntersec firstRadius: 210.00 secondRadius: 210.00 firstX: -188.08 firstY: -90.75 secondX: 207.58 secondY: 65.01
                                            MotorStepsToCartesian => MachinePosX:nan MachinePosY:nan XYZ_AXES:3 numVisibleAxes:3
                                            MotorStepsToCartesian => thetaL:30.00=MotorPosX:3467.00/StepsPermmX:115.56 
                                            MotorStepsToCartesian => thetaR:30.00=MotorPosY:3467.00/StepsPermmY:115.56 
                                            getForwad->getIntersec(distalL:210.00,distalR:210.00,xL:17.58,yL:65.01,xR:207.58,yR:65.01)
                                            getIntersec firstRadius: 210.00 secondRadius: 210.00 firstX: 17.58 firstY: 65.01 secondX: 207.58 secondY: 65.01
                                            MotorStepsToCartesian => MachinePosX:112.58 MachinePosY:252.29 XYZ_AXES:3 numVisibleAxes:3
                                            MotorStepsToCartesian => thetaL:30.00=MotorPosX:3467.00/StepsPermmX:115.56 
                                            MotorStepsToCartesian => thetaR:30.00=MotorPosY:3467.00/StepsPermmY:115.56 
                                            getForwad->getIntersec(distalL:210.00,distalR:210.00,xL:17.58,yL:65.01,xR:207.58,yR:65.01)
                                            getIntersec firstRadius: 210.00 secondRadius: 210.00 firstX: 17.58 firstY: 65.01 secondX: 207.58 secondY: 65.01
                                            MotorStepsToCartesian => MachinePosX:112.58 MachinePosY:252.29 XYZ_AXES:3 numVisibleAxes:3
                                            MotorStepsToCartesian => thetaL:30.00=MotorPosX:3467.00/StepsPermmX:115.56 
                                            MotorStepsToCartesian => thetaR:30.00=MotorPosY:3467.00/StepsPermmY:115.56 
                                            getForwad->getIntersec(distalL:210.00,distalR:210.00,xL:17.58,yL:65.01,xR:207.58,yR:65.01)
                                            getIntersec firstRadius: 210.00 secondRadius: 210.00 firstX: 17.58 firstY: 65.01 secondX: 207.58 secondY: 65.01
                                            MotorStepsToCartesian => MachinePosX:112.58 MachinePosY:252.29 XYZ_AXES:3 numVisibleAxes:3
                                            MotorStepsToCartesian => thetaL:28.00=MotorPosX:3236.00/StepsPermmX:115.56 
                                            MotorStepsToCartesian => thetaR:28.00=MotorPosY:3236.00/StepsPermmY:115.56 
                                            getForwad->getIntersec(distalL:210.00,distalR:210.00,xL:19.78,yL:61.04,xR:209.78,yR:61.04)
                                            getIntersec firstRadius: 210.00 secondRadius: 210.00 firstX: 19.78 firstY: 61.04 secondX: 209.78 secondY: 61.04
                                            MotorStepsToCartesian => MachinePosX:114.78 MachinePosY:248.32 XYZ_AXES:3 numVisibleAxes:3
                                            MotorStepsToCartesian => thetaL:28.00=MotorPosX:3236.00/StepsPermmX:115.56 
                                            MotorStepsToCartesian => thetaR:28.00=MotorPosY:3236.00/StepsPermmY:115.56 
                                            getForwad->getIntersec(distalL:210.00,distalR:210.00,xL:19.78,yL:61.04,xR:209.78,yR:61.04)
                                            getIntersec firstRadius: 210.00 secondRadius: 210.00 firstX: 19.78 firstY: 61.04 secondX: 209.78 secondY: 61.04
                                            MotorStepsToCartesian => MachinePosX:114.78 MachinePosY:248.32 XYZ_AXES:3 numVisibleAxes:3
                                            MotorStepsToCartesian => thetaL:30.00=MotorPosX:3467.00/StepsPermmX:115.56 
                                            MotorStepsToCartesian => thetaR:30.00=MotorPosY:3236.00/StepsPermmY:115.56 
                                            getForwad->getIntersec(distalL:210.00,distalR:210.00,xL:17.58,yL:65.01,xR:209.78,yR:61.04)
                                            getIntersec firstRadius: 210.00 secondRadius: 210.00 firstX: 17.58 firstY: 65.01 secondX: 209.78 secondY: 61.04
                                            MotorStepsToCartesian => MachinePosX:117.53 MachinePosY:249.69 XYZ_AXES:3 numVisibleAxes:3
                                            MotorStepsToCartesian => thetaL:30.00=MotorPosX:3467.00/StepsPermmX:115.56 
                                            MotorStepsToCartesian => thetaR:30.00=MotorPosY:3236.00/StepsPermmY:115.56 
                                            getForwad->getIntersec(distalL:210.00,distalR:210.00,xL:17.58,yL:65.01,xR:209.78,yR:61.04)
                                            getIntersec firstRadius: 210.00 secondRadius: 210.00 firstX: 17.58 firstY: 65.01 secondX: 209.78 secondY: 61.04
                                            MotorStepsToCartesian => MachinePosX:117.53 MachinePosY:249.69 XYZ_AXES:3 numVisibleAxes:3
                                            MotorStepsToCartesian => thetaL:30.00=MotorPosX:3467.00/StepsPermmX:115.56 
                                            MotorStepsToCartesian => thetaR:30.00=MotorPosY:3467.00/StepsPermmY:115.56 
                                            getForwad->getIntersec(distalL:210.00,distalR:210.00,xL:17.58,yL:65.01,xR:207.58,yR:65.01)
                                            getIntersec firstRadius: 210.00 secondRadius: 210.00 firstX: 17.58 firstY: 65.01 secondX: 207.58 secondY: 65.01
                                            MotorStepsToCartesian => MachinePosX:112.58 MachinePosY:252.29 XYZ_AXES:3 numVisibleAxes:3
                                            MotorStepsToCartesian => thetaL:30.00=MotorPosX:3467.00/StepsPermmX:115.56 
                                            MotorStepsToCartesian => thetaR:30.00=MotorPosY:3467.00/StepsPermmY:115.56 
                                            getForwad->getIntersec(distalL:210.00,distalR:210.00,xL:17.58,yL:65.01,xR:207.58,yR:65.01)
                                            getIntersec firstRadius: 210.00 secondRadius: 210.00 firstX: 17.58 firstY: 65.01 secondX: 207.58 secondY: 65.01
                                            MotorStepsToCartesian => MachinePosX:112.58 MachinePosY:252.29 XYZ_AXES:3 numVisibleAxes:3
                                            MotorStepsToCartesian => thetaL:25.00=MotorPosX:2889.00/StepsPermmX:115.56 
                                            MotorStepsToCartesian => thetaR:25.00=MotorPosY:2889.00/StepsPermmY:115.56 
                                            getForwad->getIntersec(distalL:210.00,distalR:210.00,xL:22.82,yL:54.94,xR:212.82,yR:54.94)
                                            getIntersec firstRadius: 210.00 secondRadius: 210.00 firstX: 22.82 firstY: 54.94 secondX: 212.82 secondY: 54.94
                                            MotorStepsToCartesian => MachinePosX:117.82 MachinePosY:242.23 XYZ_AXES:3 numVisibleAxes:3
                                            constraintsOK->cachedInvalid:0 cachedX0:0.00 cachedY0:0.00
                                            getInverse not cantilevered proximalL:130.00 distalL:210.00 xOrigL:-95.00 yOrigL:0.00 x_0:117.82 y_0:242.23
                                            getTheta
                                            getIntersec firstRadius: 130.00 secondRadius: 210.00 firstX: -95.00 firstY: 0.00 secondX: 117.82 secondY: 242.23
                                            getTheta use: 2 x2: 22.82 y2: 54.94 thetaB: 25.00 x1: -55.68 y1: 123.91 thetaA: 72.39
                                            getInverse not cantilevered proximalR:130.00 distalR:210.00 xOrigR:95.00 yOrigR:0.00 x_0:117.82 y_0:242.23
                                            getTheta
                                            getIntersec firstRadius: 130.00 secondRadius: 210.00 firstX: 95.00 firstY: 0.00 secondX: 117.82 secondY: 242.23
                                            getTheta use: 2 x2: 212.82 y2: 54.94 thetaB: 25.00 x1: -10.49 y1: 75.98 thetaA: 144.24
                                            getInverse cachedInvalid 0 => cachedX0:117.82 cachedY0:242.23 cachedX1:117.82 cachedY1:242.23
                                            cachedXL:22.82 cachedYL:54.94 cachedXR:212.82 cachedYR:54.94 cachedThetaR:25.00 cachedXR:212.82
                                            cachedYR:54.94 cachedThetaL:25.00 cachedXL:22.82 cachedYL:54.94
                                            getInverse => cachedInvalid:0 x_0:117.82 y_0:242.23 xL:22.82 yL:54.94 thetaL:25.00 xR:212.82 yR:54.94 thetaR:25.00 x1:117.82 y1:242.23
                                            constraintsOK->cachedInvalid:0 actuatorAngleLMin:-90.00<0 && thetaL:25.00>actuatorAngleLMax:270.00
                                            constraintsOK->cachedInvalid:0 thetaL:25.00 < actuatorAngleLMin:-90.00 || thetaL:25.00 > actuatorAngleLMax:270.00 return false
                                            constraintsOK->cachedInvalid:0 actuatorAngleRMin:-90.00<0 && thetaR:25.00>actuatorAngleRMax:270.00
                                            constraintsOK->cachedInvalid:0 thetaR:25.00 < actuatorAngleRMin:-90.00 || thetaR:25.00 > actuatorAngleRMax:270.00 return false
                                            constraintsOK->cachedInvalid:0 headAngle:53.79 < headAngleMin:15.00 || headAngle:53.79 > headAngleMax:165.00 isnan(headAngle):0 return false
                                            constraintsOK->cachedInvalid:0 angleProxDistL:218.10 < proxDistLAngleMin:0.00 || angleProxDistL:218.10 > proxDistLAngleMax:360.00 isnan(angleProxDistL):0 return false
                                            constraintsOK->cachedInvalid:0 angleProxDistR:271.90 < proxDistRAngleMin:0.00 || angleProxDistR:271.90 > proxDistRAngleMax:360.00 isnan(angleProxDistR):0 return false
                                            constraintsOK->cachedInvalid:0 cachedX0:117.82 cachedY0:242.23
                                            CartesianToMotorSteps => motorPosX = cachedThetaL:25.00 * stespPermm[X_AXIS]115.56
                                             motorPosY = cachedThetaR:25.00 * stepsPermm[Y_AXIS]115.56
                                            CartesianToMotorSteps => machinePos[0]:117.82 machinePos[1]:242.23 motorPosX:0.00 motorPosY:115.56
                                            CartesianToMotorSteps => XYZ_AXES:3 numVisibleAxes:3
                                            constraintsOK->cachedInvalid:0 cachedX0:117.82 cachedY0:242.23
                                            CartesianToMotorSteps => motorPosX = cachedThetaL:25.00 * stespPermm[X_AXIS]115.56
                                             motorPosY = cachedThetaR:25.00 * stepsPermm[Y_AXIS]115.56
                                            CartesianToMotorSteps => machinePos[0]:117.82 machinePos[1]:242.23 motorPosX:0.00 motorPosY:115.56
                                            CartesianToMotorSteps => XYZ_AXES:3 numVisibleAxes:3
                                            constraintsOK->cachedInvalid:0 cachedX0:117.82 cachedY0:242.23
                                            getInverse not cantilevered proximalL:130.00 distalL:210.00 xOrigL:-95.00 yOrigL:0.00 x_0:0.00 y_0:0.00
                                            getTheta
                                            getIntersec firstRadius: 130.00 secondRadius: 210.00 firstX: -95.00 firstY: 0.00 secondX: 0.00 secondY: 0.00
                                            getTheta use: 2 x2: -190.66 y2: -88.03 thetaB: 222.62 x1: -190.66 y1: 88.03 thetaA: 137.38
                                            getInverse not cantilevered proximalR:130.00 distalR:210.00 xOrigR:95.00 yOrigR:0.00 x_0:0.00 y_0:0.00
                                            getTheta
                                            getIntersec firstRadius: 130.00 secondRadius: 210.00 firstX: 95.00 firstY: 0.00 secondX: 0.00 secondY: 0.00
                                            getTheta use: 2 x2: 190.66 y2: 88.03 thetaB: 42.62 x1: 190.66 y1: -88.03 thetaA: 317.38
                                            getInverse cachedInvalid 0 => cachedX0:0.00 cachedY0:0.00 cachedX1:0.00 cachedY1:0.00
                                            cachedXL:-190.66 cachedYL:-88.03 cachedXR:190.66 cachedYR:88.03 cachedThetaR:42.62 cachedXR:190.66
                                            cachedYR:88.03 cachedThetaL:222.62 cachedXL:-190.66 cachedYL:-88.03
                                            getInverse => cachedInvalid:0 x_0:0.00 y_0:0.00 xL:-190.66 yL:-88.03 thetaL:222.62 xR:190.66 yR:88.03 thetaR:42.62 x1:0.00 y1:0.00
                                            constraintsOK->cachedInvalid:0 actuatorAngleLMin:-90.00<0 && thetaL:222.62>actuatorAngleLMax:270.00
                                            constraintsOK->cachedInvalid:0 thetaL:222.62 < actuatorAngleLMin:-90.00 || thetaL:222.62 > actuatorAngleLMax:270.00 return false
                                            constraintsOK->cachedInvalid:0 actuatorAngleRMin:-90.00<0 && thetaR:42.62>actuatorAngleRMax:270.00
                                            constraintsOK->cachedInvalid:0 thetaR:42.62 < actuatorAngleRMin:-90.00 || thetaR:42.62 > actuatorAngleRMax:270.00 return false
                                            constraintsOK->cachedInvalid:0 headAngle:180.00 < headAngleMin:15.00 || headAngle:180.00 > headAngleMax:165.00 isnan(headAngle):0 return false
                                            constraintsOK->cachedInvalid:1 cachedX0:0.00 cachedY0:0.00
                                            getInverse not cantilevered proximalL:130.00 distalL:210.00 xOrigL:-95.00 yOrigL:0.00 x_0:0.00 y_0:0.00
                                            getTheta
                                            getIntersec firstRadius: 130.00 secondRadius: 210.00 firstX: -95.00 firstY: 0.00 secondX: 0.00 secondY: 0.00
                                            getTheta use: 2 x2: -190.66 y2: -88.03 thetaB: 222.62 x1: -190.66 y1: 88.03 thetaA: 137.38
                                            getInverse not cantilevered proximalR:130.00 distalR:210.00 xOrigR:95.00 yOrigR:0.00 x_0:0.00 y_0:0.00
                                            getTheta
                                            getIntersec firstRadius: 130.00 secondRadius: 210.00 firstX: 95.00 firstY: 0.00 secondX: 0.00 secondY: 0.00
                                            getTheta use: 2 x2: 190.66 y2: 88.03 thetaB: 42.62 x1: 190.66 y1: -88.03 thetaA: 317.38
                                            getInverse cachedInvalid 0 => cachedX0:0.00 cachedY0:0.00 cachedX1:0.00 cachedY1:0.00
                                            cachedXL:-190.66 cachedYL:-88.03 cachedXR:190.66 cachedYR:88.03 cachedThetaR:42.62 cachedXR:190.66
                                            cachedYR:88.03 cachedThetaL:222.62 cachedXL:-190.66 cachedYL:-88.03
                                            getInverse => cachedInvalid:0 x_0:0.00 y_0:0.00 xL:-190.66 yL:-88.03 thetaL:222.62 xR:190.66 yR:88.03 thetaR:42.62 x1:0.00 y1:0.00
                                            constraintsOK->cachedInvalid:0 actuatorAngleLMin:-90.00<0 && thetaL:222.62>actuatorAngleLMax:270.00
                                            constraintsOK->cachedInvalid:0 thetaL:222.62 < actuatorAngleLMin:-90.00 || thetaL:222.62 > actuatorAngleLMax:270.00 return false
                                            constraintsOK->cachedInvalid:0 actuatorAngleRMin:-90.00<0 && thetaR:42.62>actuatorAngleRMax:270.00
                                            constraintsOK->cachedInvalid:0 thetaR:42.62 < actuatorAngleRMin:-90.00 || thetaR:42.62 > actuatorAngleRMax:270.00 return false
                                            constraintsOK->cachedInvalid:0 headAngle:180.00 < headAngleMin:15.00 || headAngle:180.00 > headAngleMax:165.00 isnan(headAngle):0 return false
                                            

                                            M114 outputs:

                                            m114
                                            X:117.819 Y:242.226 Z:0.000 E:0.000 E0:0.0 Count 2889 2889 0 Machine 117.819 242.226 0.000 Bed comp 0.000
                                            ok
                                            
                                            

                                            VIDEO OF PROCESS STEPS


                                            JoergS5undefined 2 Replies Last reply Reply Quote 0
                                            • First post
                                              Last post
                                            Unless otherwise noted, all forum content is licensed under CC-BY-SA