DuetLapse3



  • This is a modified version of the original DuetLapse created by Danal Estes.
    The bulk of the functionality is his and we all should remember him for his generous work

    It's been sufficiently modified that I believe a new version is warranted. Doing so will make it easier to track and respond to changes. Note that this version is only for RRF3.

    The modifications include:

    • [1] Removal of dependency on DuetWebAPI.py (by Danal Estes). DuetLapse3.py is a standalone Python3 script.
    • [2] Added support for 2 cameras
    • [3] Reorganized Directory Structure to allow logical separation of files (by printer)
    • [4] Added configurable base directory
    • [5] Added logfile support
    • [6] Added verbose option
    • [7] Added control over multiple instances
    • [8] Added ability to gracefully terminate when executing in background
    • [9] Added ability to extend the video duration of the last image
    • [10] Generalized capture with Camera type "other" and arbitrary capture commands
    • [11] Generalized video creation with optional commands

    Detailed Instructions / Documentation are here:

    https://github.com/stuartofmt/DuetLapse3

    Note that there are some minor changes to the naming and use of options from the original version. Note especially the ffmpeg version requirements.



  • @stuartofmt Excellent way to carry on Danal's work!



  • @stuartofmt

    wget https://github.com/stuartofmt/DuetLapse3/blob/main/DuetLapse3.py
    

    should be changed to

    wget https://github.com/stuartofmt/DuetLapse3/raw/main/DuetLapse3.py
    

    😉



  • This post is deleted!


  • @stuartofmt I am running the LPC SBC Port of RRF. But i can not connect to my Pi.
    here is my Object Model
    alt text

    192.168.140.126
    192.168.140.126 The printer at 192.168.140.126 did not respond
    192.168.140.126 Check the ip address or logical printer name is correct
    192.168.140.126 Duet software version must be at RRF3 or above and suppotr rr_model
    

    Best



  • @stuartofmt
    The duet+SBC doesn't support the object model and uses machine/status instead.
    The DuetWebAPI handled both methods of obtaining information.



  • @PCR

    Instructions on wget for download fixed. Thanks!



  • @jay_s_uk

    Let me take a look at the SBC situation (which I do not own). I was under the impression that /machine/status was replaced (to be?) by rr_model as the preferred method.

    It may take me a few of days to figure out.



  • @stuartofmt its the other way round.
    rr_model is replaced by machine/status in SBC situations



  • @jay_s_uk

    OK - thx. It's not difficult but I will need some help testing.



  • @stuartofmt
    i can provide a current 3.2 output from machine/status if needed.
    i'm sure @PCR would do some testing for you



  • @jay_s_uk

    That would be very helpful (much better than closing my eyes and imagining) - thanks!



  • {"boards":[{"bootloaderFileName":null,"canAddress":0,"directDisplay":null,"firmwareDate":"2021-01-05","firmwareFileName":"Duet3Firmware_Mini5plus.uf2","firmwareName":"RepRapFirmware for Duet 3 Mini 5\u002B","firmwareVersion":"3.2","iapFileNameSBC":"Duet3_SBCiap32_Mini5plus.bin","iapFileNameSD":"Duet3_SDiap32_Mini5plus.bin","maxHeaters":32,"maxMotors":7,"mcuTemp":{"current":-273.15,"min":-273.15,"max":-273.15},"name":"Duet 3 Mini 5\u002B","shortName":"Mini5plus","state":"unknown","supports12864":true,"supportsDirectDisplay":true,"uniqueId":"P2P4H-PA67A-G65J0-40TFU-L2D0Z-ZTS4L","v12":{"current":0,"min":0,"max":0},"vIn":{"current":12.3,"min":0,"max":12.3}}],"directories":{"filaments":"0:/filaments/","firmware":"0:/sys/","gCodes":"0:/gcodes/","macros":"0:/macros/","menu":"0:/menu/","scans":"0:/scans/","system":"0:/sys/","web":"0:/www/"},"fans":[{"actualValue":0,"blip":0.1,"frequency":0,"max":1,"min":0.1,"name":"","requestedValue":0,"rpm":-1,"thermostatic":{"heaters":[],"highTemperature":null,"lowTemperature":null}},{"actualValue":0,"blip":0.1,"frequency":0,"max":1,"min":0.1,"name":"","requestedValue":1,"rpm":-1,"thermostatic":{"heaters":[1],"highTemperature":45,"lowTemperature":45}}],"heat":{"bedHeaters":[0,-1],"chamberHeaters":[-1,-1],"coldExtrudeTemperature":160,"coldRetractTemperature":90,"heaters":[{"active":0,"current":15.4,"max":120,"min":-273.1,"model":{"deadTime":2.2,"enabled":true,"gain":114,"heatingRate":0.219,"inverted":false,"maxPwm":1,"pid":{"overridden":false,"p":1.5,"i":0.1,"d":2.2,"used":true},"standardVoltage":12.2,"timeConstant":520.5,"timeConstantFansOn":520.5},"monitors":[{"action":0,"condition":"tooHigh","limit":120},{"action":null,"condition":"disabled","limit":null},{"action":null,"condition":"disabled","limit":null}],"name":null,"sensor":0,"standby":0,"state":"off"},{"active":0,"current":15.9,"max":285,"min":-273.1,"model":{"deadTime":7,"enabled":true,"gain":407.9,"heatingRate":2.343,"inverted":false,"maxPwm":1,"pid":{"overridden":false,"p":0,"i":0,"d":0.2,"used":true},"standardVoltage":12.2,"timeConstant":174.1,"timeConstantFansOn":152.7},"monitors":[{"action":0,"condition":"tooHigh","limit":285},{"action":null,"condition":"disabled","limit":null},{"action":null,"condition":"disabled","limit":null}],"name":null,"sensor":1,"standby":0,"state":"active"}]},"httpEndpoints":[],"inputs":[{"axesRelative":false,"compatibility":"RepRapFirmware","distanceUnit":"mm","drivesRelative":false,"feedRate":50,"inMacro":false,"name":"HTTP","stackDepth":0,"state":"idle","lineNumber":0,"volumetric":false},{"axesRelative":false,"compatibility":"Marlin","distanceUnit":"mm","drivesRelative":false,"feedRate":50,"inMacro":false,"name":"Telnet","stackDepth":0,"state":"idle","lineNumber":0,"volumetric":false},{"axesRelative":false,"compatibility":"RepRapFirmware","distanceUnit":"mm","drivesRelative":false,"feedRate":50,"inMacro":false,"name":"File","stackDepth":0,"state":"idle","lineNumber":0,"volumetric":false},{"axesRelative":false,"compatibility":"Marlin","distanceUnit":"mm","drivesRelative":false,"feedRate":50,"inMacro":false,"name":"USB","stackDepth":0,"state":"idle","lineNumber":0,"volumetric":false},{"axesRelative":false,"compatibility":"RepRapFirmware","distanceUnit":"mm","drivesRelative":false,"feedRate":50,"inMacro":false,"name":"Aux","stackDepth":0,"state":"idle","lineNumber":0,"volumetric":false},{"axesRelative":false,"compatibility":"RepRapFirmware","distanceUnit":"mm","drivesRelative":false,"feedRate":50,"inMacro":true,"name":"Trigger","stackDepth":1,"state":"idle","lineNumber":0,"volumetric":false},{"axesRelative":false,"compatibility":"RepRapFirmware","distanceUnit":"mm","drivesRelative":false,"feedRate":50,"inMacro":false,"name":"Queue","stackDepth":0,"state":"idle","lineNumber":0,"volumetric":false},{"axesRelative":false,"compatibility":"RepRapFirmware","distanceUnit":"mm","drivesRelative":false,"feedRate":50,"inMacro":false,"name":"LCD","stackDepth":0,"state":"idle","lineNumber":0,"volumetric":false},{"axesRelative":false,"compatibility":"RepRapFirmware","distanceUnit":"mm","drivesRelative":false,"feedRate":50,"inMacro":false,"name":"SBC","stackDepth":0,"state":"idle","lineNumber":0,"volumetric":false},{"axesRelative":false,"compatibility":"RepRapFirmware","distanceUnit":"mm","drivesRelative":false,"feedRate":50,"inMacro":false,"name":"Daemon","stackDepth":0,"state":"idle","lineNumber":0,"volumetric":false},{"axesRelative":false,"compatibility":"RepRapFirmware","distanceUnit":"mm","drivesRelative":false,"feedRate":50,"inMacro":false,"name":"Aux2","stackDepth":0,"state":"idle","lineNumber":0,"volumetric":false},{"axesRelative":false,"compatibility":"RepRapFirmware","distanceUnit":"mm","drivesRelative":false,"feedRate":50,"inMacro":false,"name":"Autopause","stackDepth":0,"state":"idle","lineNumber":0,"volumetric":false}],"job":{"build":null,"duration":null,"file":{"filament":[],"fileName":null,"firstLayerHeight":0,"generatedBy":null,"height":0,"lastModified":null,"layerHeight":0,"numLayers":0,"printTime":null,"simulatedTime":null,"size":0,"thumbnails":[]},"filePosition":0,"firstLayerDuration":null,"lastDuration":0,"lastFileName":null,"lastFileAborted":false,"lastFileCancelled":false,"lastFileSimulated":false,"layer":null,"layerTime":null,"layers":[],"timesLeft":{"filament":null,"file":null,"layer":null},"warmUpDuration":null},"limits":{"axes":8,"axesPlusExtruders":8,"bedHeaters":2,"boards":5,"chamberHeaters":2,"drivers":14,"driversPerAxis":4,"extruders":5,"extrudersPerTool":5,"fans":16,"gpInPorts":16,"gpOutPorts":32,"heaters":32,"heatersPerTool":2,"monitorsPerHeater":3,"restorePoints":6,"sensors":56,"spindles":2,"tools":50,"trackedObjects":32,"triggers":16,"volumes":2,"workplaces":9,"zProbeProgramBytes":8,"zProbes":4},"messages":[],"move":{"axes":[{"acceleration":1000,"babystep":0,"current":1200,"drivers":["0.4"],"homed":false,"jerk":1000,"letter":"X","machinePosition":0,"max":270,"maxProbed":false,"microstepping":{"interpolated":true,"value":16},"min":0,"minProbed":false,"speed":6000,"stepsPerMm":100,"userPosition":0,"visible":true,"workplaceOffsets":[0,0,0,0,0,0,0,0,0]},{"acceleration":1000,"babystep":0,"current":1200,"drivers":["0.3"],"homed":false,"jerk":1000,"letter":"Y","machinePosition":0,"max":260,"maxProbed":false,"microstepping":{"interpolated":true,"value":16},"min":0,"minProbed":false,"speed":6000,"stepsPerMm":100,"userPosition":0,"visible":true,"workplaceOffsets":[0,0,0,0,0,0,0,0,0]},{"acceleration":200,"babystep":0,"current":1200,"drivers":["0.1","0.2"],"homed":false,"jerk":200,"letter":"Z","machinePosition":0,"max":350,"maxProbed":false,"microstepping":{"interpolated":true,"value":16},"min":0,"minProbed":false,"speed":300,"stepsPerMm":400,"userPosition":0,"visible":true,"workplaceOffsets":[0,0,0,0,0,0,0,0,0]}],"calibration":{"final":{"deviation":0,"mean":0},"initial":{"deviation":0,"mean":0},"numFactors":0},"compensation":{"fadeHeight":null,"file":null,"meshDeviation":null,"probeGrid":{"xMin":15,"xMax":230,"xSpacing":20,"yMin":15,"yMax":230,"ySpacing":20,"radius":-1},"skew":{"compensateXY":true,"tanXY":0,"tanXZ":0,"tanYZ":0},"type":"none"},"currentMove":{"acceleration":0,"deceleration":0,"laserPwm":null,"requestedSpeed":0,"topSpeed":0},"daa":{"enabled":false,"minimumAcceleration":10,"period":0},"extruders":[{"acceleration":250,"current":1000,"driver":"0.0","filament":"","factor":1,"jerk":120,"microstepping":{"interpolated":true,"value":16},"nonlinear":{"a":0,"b":0,"upperLimit":0.2},"position":0,"pressureAdvance":0,"rawPosition":0,"speed":1200,"stepsPerMm":837}],"idle":{"timeout":30,"factor":0.3},"kinematics":{"forwardMatrix":[[1,0,0],[0,1,0],[0,0,1]],"inverseMatrix":[[1,0,0],[0,1,0],[0,0,1]],"tiltCorrection":{"correctionFactor":1,"lastCorrections":[0,0],"maxCorrection":10,"screwPitch":0.5,"screwX":[-130,400],"screwY":[150,150]},"name":"cartesian"},"printingAcceleration":10000,"speedFactor":1,"travelAcceleration":10000,"virtualEPos":0,"workplaceNumber":0,"workspaceNumber":1},"network":{"corsSite":null,"hostname":"duet3","interfaces":[{"activeProtocols":[],"actualIP":"192.168.1.31","configuredIP":"192.168.1.31","firmwareVersion":null,"gateway":"192.168.1.1","mac":"B8:27:EB:C9:9C:1A","numReconnects":null,"signal":null,"speed":null,"subnet":"255.255.255.0","type":"wifi"}],"name":"duet3"},"plugins":[{"dwcFiles":["GCodeViewer.10bd9549.js","GCodeViewer.88004336.css"],"sbcFiles":[],"rrfFiles":[],"pid":-1,"name":"GCodeViewer","author":"Juan Rosario","version":"0.11.1","license":"LGPL-3.0-or-later","sourceRepository":null,"dwcVersion":"3.2.0-beta4","dwcDependencies":[],"dwcWebpackChunk":"GCodeViewer","sbcRequired":false,"sbcDsfVersion":null,"sbcData":{},"sbcExecutable":null,"sbcExecutableArguments":null,"sbcOutputRedirected":true,"sbcPermissions":[],"sbcPackageDependencies":[],"sbcPluginDependencies":[],"rrfVersion":null}],"scanner":{"progress":0,"status":"D"},"sensors":{"analog":[{"lastReading":15.4,"name":"","type":"thermistor"},{"lastReading":15.9,"name":"","type":"thermistor"}],"endstops":[{"triggered":false,"type":"inputPin","probeNumber":null},{"triggered":false,"type":"inputPin","probeNumber":null},null],"filamentMonitors":[],"gpIn":[],"probes":[{"calibrationTemperature":25,"deployedByUser":false,"disablesHeaters":false,"diveHeight":2,"lastStopHeight":0,"maxProbeCount":5,"offsets":[-27.3,3.5],"recoveryTime":0,"speed":2,"temperatureCoefficient":0,"temperatureCoefficients":[0,0],"threshold":1000,"tolerance":0.02,"travelSpeed":333.3,"triggerHeight":3.553,"type":1,"value":[671]}]},"spindles":[{"active":0,"current":0,"frequency":0,"min":60,"max":10000,"tool":-1},{"active":0,"current":0,"frequency":0,"min":60,"max":10000,"tool":-1}],"state":{"atxPower":null,"beep":null,"currentTool":0,"displayMessage":"","dsfVersion":"3.2.0","gpOut":[{"pwm":0}],"laserPwm":null,"logFile":null,"logLevel":"off","messageBox":null,"machineMode":"FFF","msUpTime":503,"nextTool":0,"powerFailScript":"","previousTool":-1,"restorePoints":[{"coords":[0,0,0],"extruderPos":0,"feedRate":50,"ioBits":0,"laserPwm":null,"spindleSpeeds":[0,0],"toolNumber":-1},{"coords":[0,0,0],"extruderPos":0,"feedRate":50,"ioBits":0,"laserPwm":null,"spindleSpeeds":[0,0],"toolNumber":-1},{"coords":[0,0,0],"extruderPos":0,"feedRate":50,"ioBits":0,"laserPwm":null,"spindleSpeeds":[0,0],"toolNumber":-1},{"coords":[0,0,0],"extruderPos":0,"feedRate":50,"ioBits":0,"laserPwm":null,"spindleSpeeds":[0,0],"toolNumber":-1},{"coords":[0,0,0],"extruderPos":0,"feedRate":50,"ioBits":0,"laserPwm":null,"spindleSpeeds":[0,0],"toolNumber":-1},{"coords":[0,0,0],"extruderPos":0,"feedRate":50,"ioBits":0,"laserPwm":null,"spindleSpeeds":[0,0],"toolNumber":-1}],"status":"idle","time":"2021-01-14T17:44:25","upTime":363424},"tools":[{"active":[0],"axes":[[0],[1]],"extruders":[0],"fans":[0],"filamentExtruder":0,"heaters":[1],"mix":[1],"name":"","number":0,"offsets":[0,0,0],"offsetsProbed":0,"retraction":{"extraRestart":0,"length":5,"speed":40,"unretractSpeed":40,"zHop":0.5},"standby":[0],"state":"active"}],"userSessions":[],"userVariables":[],"volumes":[{"capacity":29077254144,"freeSpace":20587024384,"mounted":true,"name":null,"openFiles":null,"path":"/","speed":null},{"capacity":2143281152,"freeSpace":2028986368,"mounted":true,"name":null,"openFiles":null,"path":"/boot","speed":null}]}
    


  • Could someone please try this? It's a test script for the SBC calls. It's read only with the exception of a M117 'Test of SBC' which should show up in DWC.

    Once its placed in a directory permissions will need to be changed.

    chmod +x ./SBCTEST.py
    

    it can then be run with the following command

    python3 ./SBCTEST.py -duet <ipaddress of your SBC printer>
    

    with a bit of luck it will report success, in any case - if the resultant SBCTEST.log file can be posted back, that would be a great help.

    I've tested it on my non SBC duet - there may be some syntax errors in the unexercised code - but I hope not 🙂
    If there are then a screen shot would be helpful.

    [SBCTEST.py]



  • Can do it tomorrow! Thank you



  • @stuartofmt said in DuetLapse3:

    python3 ./SBCTEST.py -duet <ipaddress of your SBC printer>

    localhost Determine API Version
    localhost Testing: rr_model at address localhost
    localhost http://localhost/rr_model?key=boards
    localhost
    localhost Testing: /machine/system at address localhost
    localhost http://localhost/machine/status
    localhost
    localhost API model is: machine   Version is: 3
    localhost Status is :idle
    localhost Layer is :0
    Traceback (most recent call last):
      File "./SBCTEST.py", line 147, in <module>
        logger.info('Send Gcode result: '+sendDuetGcode(apiModel, 'M117 Test of SBC'))
      File "./SBCTEST.py", line 122, in sendDuetGcode
        r = requests.get.post(URL, data=command)
    AttributeError: 'function' object has no attribute 'post'
    
    

    Nothing is displayed on DWC



  • @jay_s_uk

    Aghhhh - A definite typo ... Try this please.
    The rest looks good.

    [SBCTEST.py]



  • @stuartofmt

    192.168.140.126 Determine API Version
    192.168.140.126 Testing: rr_model at address 192.168.140.126
    192.168.140.126 http://192.168.140.126/rr_model?key=boards
    192.168.140.126
    192.168.140.126 Testing: /machine/system at address 192.168.140.126
    192.168.140.126 http://192.168.140.126/machine/status
    192.168.140.126
    192.168.140.126 API model is: machine   Version is: 3
    192.168.140.126 Status is :processing
    192.168.140.126 Layer is :0
    --- Logging error ---
    Traceback (most recent call last):
      File "/usr/lib/python3.7/logging/__init__.py", line 1034, in emit
        msg = self.format(record)
      File "/usr/lib/python3.7/logging/__init__.py", line 880, in format
        return fmt.format(record)
      File "/usr/lib/python3.7/logging/__init__.py", line 619, in format
        record.message = record.getMessage()
      File "/usr/lib/python3.7/logging/__init__.py", line 380, in getMessage
        msg = msg % self.args
    TypeError: not all arguments converted during string formatting
    Call stack:
      File "./sbctest.py", line 147, in <module>
        logger.info('Send Gcode result: '+sendDuetGcode(apiModel, 'M117 Test of SBC'))
      File "./sbctest.py", line 127, in sendDuetGcode
        logger.info("gCode command return code = ",r.status_code)
    Message: 'gCode command return code = '
    Arguments: (405,)
    --- Logging error ---
    Traceback (most recent call last):
      File "/usr/lib/python3.7/logging/__init__.py", line 1034, in emit
        msg = self.format(record)
      File "/usr/lib/python3.7/logging/__init__.py", line 880, in format
        return fmt.format(record)
      File "/usr/lib/python3.7/logging/__init__.py", line 619, in format
        record.message = record.getMessage()
      File "/usr/lib/python3.7/logging/__init__.py", line 380, in getMessage
        msg = msg % self.args
    TypeError: not all arguments converted during string formatting
    Call stack:
      File "./sbctest.py", line 147, in <module>
        logger.info('Send Gcode result: '+sendDuetGcode(apiModel, 'M117 Test of SBC'))
      File "./sbctest.py", line 127, in sendDuetGcode
        logger.info("gCode command return code = ",r.status_code)
    Message: 'gCode command return code = '
    Arguments: (405,)
    192.168.140.126 Method Not Allowed
    Traceback (most recent call last):
      File "./sbctest.py", line 147, in <module>
        logger.info('Send Gcode result: '+sendDuetGcode(apiModel, 'M117 Test of SBC'))
    TypeError: can only concatenate str (not "int") to str
    
    


  • @PCR @jay_s_uk

    Thanks - there was a silly formatting error (now fixed) but underlying that it looks like there was an http 405 error returned.

    I've added a two more version of the send gcode function to try - hopefully one of them works. Unfortunately the documentation and other examples (which purport to work) are somewhat vague on this aspect ....

    ALSO - if you are running RRF3.2 the default behavior for CORS seems to have changed. With another program I am using (DueUI) - I had to add the following to config.sys (up front in the network section) - after the upgrade. BUT DuetLapse was unaffected. It MAY be different on SBC.

    I'd suggest first trying "as is" but if this script still does not work with one of the methods (depending on what the errors are) - the CORS thing may need to be looked into.

    M586 C"*"                                     ;enable CORS
    

    New Test Script is here

    [SBCTEST.py]



  • Awesome Stuart, thanks for carrying on Danal's work

    Looks well documented too, I'll give it a shot over the weekend.

    Very neat of you to handle issues outside of your own setup too

    Do you think in it's current state this could realistically be implemented as a plugin in DWC for a possible GUI?



  • @fractalengineer

    Thanks. As a plugin ? hmmm . I'll have to see what that means / entails. Let me put that as a priority 2 for right now.

    Priority 1 is getting SSB working and tidying up some of the logic / behavior surrounding pauses. I did not test that aspect much and its somewhat ambiguous - so stay tuned ...



  • @stuartofmt

    192.168.1.31 Determine API Version
    192.168.1.31 Testing: rr_model at address 192.168.1.31
    192.168.1.31 http://192.168.1.31/rr_model?key=boards
    192.168.1.31
    192.168.1.31 Testing: /machine/system at address 192.168.1.31
    192.168.1.31 http://192.168.1.31/machine/status
    192.168.1.31
    192.168.1.31 API model is: SBC   Version is: 3
    192.168.1.31 Status is :idle
    192.168.1.31 Layer is :0
    192.168.1.31 ***Testing V0***
    405
    Method Not Allowed
    192.168.1.31 gCode command return code = 405
    192.168.1.31 Method Not Allowed
    192.168.1.31 Send Gcode result: 405
    192.168.1.31 ***Testing V1***
    200
    OK
    192.168.1.31 Send Gcode result: 0
    192.168.1.31 ***Testing V2***
    404
    Not Found
    192.168.1.31 gCode command return code = 404
    192.168.1.31 Not Found
    192.168.1.31 Send Gcode result: 404
    
    


  • @jay_s_uk

    OK! Looks like V1 worked!! So you should have got a message on DWC saying "Test of SBC"

    Did you see that ?



  • @stuartofmt
    No, nothing displayed on DWC



  • @jay_s_uk

    Ok - I'm going to have to get some help. The code I have seen elsewhere (including Danal's) does not work (or I've done something stupid). The one that reports success - does not seem to send the gcode .... go figure .....

    So - this should work for SBC -- but not for using the pause capability (that's the only thing that sends gcodes). Appreciate it if you could test before I update github

    Hmm - wont let me upload the file to here. I'll update on github - if you would not mind downloading from there.


Log in to reply