Merge branch 'feature/printerdriver' of github.com:Doodle3D/doodle3d-firmware into feature/printerdriver

This commit is contained in:
Wouter R 2013-10-22 20:36:26 +02:00
commit 5c7df6da36
4 changed files with 118 additions and 55 deletions

View File

@ -98,7 +98,14 @@ M.printer_baudrate = {
M.printer_temperature = { M.printer_temperature = {
default = 230, default = 230,
type = 'int', type = 'int',
description = '3D printer temperature', description = 'printing temperature',
min = 0
}
M.printer_bed_temperature = {
default = 70,
type = 'int',
description = 'printing bed temperature',
min = 0 min = 0
} }
@ -161,6 +168,12 @@ M.printer_heatup_temperature = {
description = '' description = ''
} }
M.printer_heatup_bed_temperature = {
default = 70,
type = 'int',
description = ''
}
M.printer_retraction_enabled = { M.printer_retraction_enabled = {
default = true, default = true,
type = 'bool', type = 'bool',
@ -195,13 +208,13 @@ M.printer_enableTraveling = {
} }
M.printer_startgcode = { M.printer_startgcode = {
default = ';Generated with Doodle3D\nM109 S{printingTemp} ;set target temperature \nG21 ;metric values\nG91 ;relative positioning\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E10 ;extrude 10mm of feed stock\nG92 E0 ;zero the extruded length again\nG92 E0 ;zero the extruded length again\nG1 F9000\nG90 ;absolute positioning\nM117 Printing Doodle... ;display message (20 characters to clear whole screen)', default = ';Generated with Doodle3D\nM109 S{printingTemp} ;set target temperature \n;M190 S{printingBedTemp} ;set target bed temperature\nG21 ;metric values\nG91 ;relative positioning\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E10 ;extrude 10mm of feed stock\nG92 E0 ;zero the extruded length again\nG92 E0 ;zero the extruded length again\nG1 F9000\nG90 ;absolute positioning\nM117 Printing Doodle... ;display message (20 characters to clear whole screen)',
type = 'string', type = 'string',
description = '' description = ''
} }
M.printer_endgcode = { M.printer_endgcode = {
default = 'M107 ;fan off\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;disable axes / steppers\nG90 ;absolute positioning\nM104 {preheatTemp}\nM117 Done ;display message (20 characters to clear whole screen)', default = 'M107 ;fan off\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;disable axes / steppers\nG90 ;absolute positioning\nM104 S{preheatTemp}\n;M140 S{preheatBedTemp}\nM117 Done ;display message (20 characters to clear whole screen)',
type = 'string', type = 'string',
description = '' description = ''
} }

View File

@ -106,6 +106,8 @@ end
function M.stop_POST(request, response) function M.stop_POST(request, response)
log:info("API:printer/stop")
if not accessManager.hasControl(request.remoteAddress) then if not accessManager.hasControl(request.remoteAddress) then
response:setFail("No control access") response:setFail("No control access")
return return
@ -114,8 +116,23 @@ function M.stop_POST(request, response)
local argId = request:get("id") local argId = request:get("id")
local printer,msg = printerUtils.createPrinterOrFail(argId, response) local printer,msg = printerUtils.createPrinterOrFail(argId, response)
if not printer then return end if not printer then return end
local endGcode = settings.get('printer.endgcode') -- replacing {printingTemp} and {preheatTemp} in endgcode
local printingTemperature = settings.get('printer.temperature')
local printingBedTemperature = settings.get('printer.bed.temperature')
local preheatTemperature = settings.get('printer.heatup.temperature')
local preheatBedTemperature = settings.get('printer.heatup.bed.temperature')
local endGcode = settings.get('printer.endgcode')
--log:info(" printingBedTemperature: "..utils.dump(printingBedTemperature))
--log:info(" preheatBedTemperature: "..utils.dump(preheatBedTemperature))
--log:info(" endGcode : "..utils.dump(endGcode))
endGcode = string.gsub(endGcode,"{printingTemp}",printingTemperature)
endGcode = string.gsub(endGcode,"{printingBedTemp}",printingBedTemperature)
endGcode = string.gsub(endGcode,"{preheatTemp}",preheatTemperature)
endGcode = string.gsub(endGcode,"{preheatBedTemp}",preheatBedTemperature)
--log:info(" >endGcode : "..utils.dump(endGcode))
local rv,msg = printer:stopPrint(endGcode) local rv,msg = printer:stopPrint(endGcode)
response:addData('id', argId) response:addData('id', argId)

View File

@ -14,27 +14,31 @@ local M = {
function M.status(request, response) function M.status(request, response)
updater.setLogger(log) updater.setLogger(log)
updater.setUseCache(false) updater.setUseCache(false)
local status,msg = updater.getStatus() local success,status,msg = updater.getStatus()
if not status then
--response:addData('current_version', status.currentVersion)
response:addData('current_version', updater.formatVersion(status.currentVersion))
response:addData('state_code', status.stateCode)
response:addData('state_text', status.stateText)
if not success then
response:setFail(msg) response:setFail(msg)
return return
end end
local canUpdate = updater.compareVersions(status.newestVersion, status.currentVersion) > 0 local canUpdate = updater.compareVersions(status.newestVersion, status.currentVersion) > 0
response:addData('current_version', updater.formatVersion(status.currentVersion))
response:addData('newest_version', updater.formatVersion(status.newestVersion))
--response:addData('current_version', status.currentVersion)
--response:addData('newest_version', status.newestVersion) --response:addData('newest_version', status.newestVersion)
response:addData('newest_version', updater.formatVersion(status.newestVersion))
response:addData('can_update', canUpdate) response:addData('can_update', canUpdate)
response:addData('state_code', status.stateCode)
response:addData('state_text', status.stateText)
if status.progress then response:addData('progress', status.progress) end if status.progress then response:addData('progress', status.progress) end
if status.imageSize then response:addData('image_size', status.imageSize) end if status.imageSize then response:addData('image_size', status.imageSize) end
response:setSuccess() response:setSuccess()
end end
-- requires: version(string) (major.minor.patch) -- accepts: version(string) (major.minor.patch)
-- accepts: clear_gcode(bool, defaults to true) (this is to lower the chance on out-of-memory crashes, but still allows overriding this behaviour) -- accepts: clear_gcode(bool, defaults to true) (this is to lower the chance on out-of-memory crashes, but still allows overriding this behaviour)
-- accepts: clear_images(bool, defaults to true) (same rationale as with clear_gcode) -- accepts: clear_images(bool, defaults to true) (same rationale as with clear_gcode)
-- note: call this with a long timeout - downloading may take a while (e.g. ~3.3MB with slow internet...) -- note: call this with a long timeout - downloading may take a while (e.g. ~3.3MB with slow internet...)
@ -45,17 +49,27 @@ function M.download_POST(request, response)
if argClearGcode == nil then argClearGcode = true end if argClearGcode == nil then argClearGcode = true end
if argClearImages == nil then argClearImages = true end if argClearImages == nil then argClearImages = true end
if not argVersion then
response:setError("missing version argument")
return
end
updater.setLogger(log) updater.setLogger(log)
updater.setState(updater.STATE.DOWNLOADING,"")
local vEnt, rv, msg local vEnt, rv, msg
if not argVersion then
local success,status,msg = updater.getStatus()
if not success then
updater.setState(updater.STATE.DOWNLOAD_FAILED, msg)
response:setFail(msg)
return
else
argVersion = updater.formatVersion(status.newestVersion)
end
end
if argClearImages then if argClearImages then
rv,msg = updater.clear() rv,msg = updater.clear()
if not rv then if not rv then
updater.setState(updater.STATE.DOWNLOAD_FAILED, msg)
response:setFail(msg) response:setFail(msg)
return return
end end
@ -66,6 +80,7 @@ function M.download_POST(request, response)
local rv,msg = printer:clearGcode() local rv,msg = printer:clearGcode()
if not rv then if not rv then
updater.setState(updater.STATE.DOWNLOAD_FAILED, msg)
response:setError(msg) response:setError(msg)
return return
end end
@ -73,15 +88,18 @@ function M.download_POST(request, response)
vEnt,msg = updater.findVersion(argVersion) vEnt,msg = updater.findVersion(argVersion)
if vEnt == nil then if vEnt == nil then
updater.setState(updater.STATE.DOWNLOAD_FAILED, "error searching version index (" .. msg .. ")")
response:setFail("error searching version index (" .. msg .. ")") response:setFail("error searching version index (" .. msg .. ")")
return return
elseif vEnt == false then elseif vEnt == false then
updater.setState(updater.STATE.DOWNLOAD_FAILED, "no such version")
response:setFail("no such version") response:setFail("no such version")
return return
end end
rv,msg = updater.downloadImageFile(vEnt) rv,msg = updater.downloadImageFile(vEnt)
if not rv then if not rv then
updater.setState(updater.STATE.DOWNLOAD_FAILED, msg)
response:setFail(msg) response:setFail(msg)
return return
end end
@ -93,25 +111,39 @@ end
function M.install_POST(request, response) function M.install_POST(request, response)
local argVersion = request:get("version") local argVersion = request:get("version")
updater.setLogger(log) updater.setLogger(log)
log:info("API:update/install")
updater.setState(updater.STATE.INSTALLING,"")
if not argVersion then if not argVersion then
response:setError("missing version argument") local success,status,msg = updater.getStatus()
return if not success then
updater.setState(updater.STATE.INSTALL_FAILED, msg)
response:setFail(msg)
return
else
argVersion = updater.formatVersion(status.newestVersion)
end
end end
vEnt,msg = updater.findVersion(argVersion) vEnt,msg = updater.findVersion(argVersion)
if vEnt == nil then if vEnt == nil then
updater.setState(updater.STATE.INSTALL_FAILED, "error searching version index (" .. msg .. ")")
response:setFail("error searching version index (" .. msg .. ")") response:setFail("error searching version index (" .. msg .. ")")
return return
elseif vEnt == false then elseif vEnt == false then
updater.setState(updater.STATE.INSTALL_FAILED, "no such version")
response:setFail("no such version") response:setFail("no such version")
return return
end end
local rv,msg = updater.flashImageVersion(vEnt) local rv,msg = updater.flashImageVersion(vEnt)
if not rv then response:setFail("installation failed (" .. msg .. ")") if not rv then
else response:setSuccess() updater.setState(updater.STATE.INSTALL_FAILED, "installation failed (" .. msg .. ")")
response:setFail("installation failed (" .. msg .. ")")
else
response:setSuccess()
end end
end end

View File

@ -113,23 +113,6 @@ local function getState()
return code,msg return code,msg
end end
-- NOTE: make sure the cache directory exists before calling this function or it will fail.
-- NOTE: this function _can_ fail but we don't expect this to happen so the return value is ignored for now
local function setState(code, msg)
local s = code .. '|' .. msg
D("set update state: " .. M.STATE_NAMES[code] .. " ('" .. s .. "')")
local file,msg = io.open(M.CACHE_PATH .. '/' .. M.STATE_FILE, 'w')
if not file then
E("error: could not open state file for writing (" .. msg .. ")")
return false
end
file:write(s)
file:close()
return true
end
-- trim whitespace from both ends of string (from http://snippets.luacode.org/?p=snippets/trim_whitespace_from_string_76) -- trim whitespace from both ends of string (from http://snippets.luacode.org/?p=snippets/trim_whitespace_from_string_76)
local function trim(s) local function trim(s)
if type(s) ~= 'string' then return s end if type(s) ~= 'string' then return s end
@ -267,25 +250,27 @@ function M.getStatus()
local unknownVersion = { major = 0, minor = 0, patch = 0 } local unknownVersion = { major = 0, minor = 0, patch = 0 }
local result = {} local result = {}
result.currentVersion = M.getCurrentVersion()
result.stateCode, result.stateText = getState()
result.stateCode = tonumber(result.stateCode)
local verTable,msg = M.getAvailableVersions() local verTable,msg = M.getAvailableVersions()
if not verTable then if not verTable then
D("could not obtain available versions (" .. msg .. ")") D("could not obtain available versions (" .. msg .. ")")
-- TODO: set an error state in result to signify we probably do not have internet access? -- TODO: set an error state in result to signify we probably do not have internet access?
return false, result, msg
end end
local newest = verTable and verTable[#verTable] local newest = verTable and verTable[#verTable]
result.currentVersion = M.getCurrentVersion()
result.newestVersion = newest and newest.version or unknownVersion result.newestVersion = newest and newest.version or unknownVersion
result.stateCode, result.stateText = getState()
result.stateCode = tonumber(result.stateCode)
if result.stateCode == M.STATE.DOWNLOADING then if result.stateCode == M.STATE.DOWNLOADING then
result.progress = fileSize(M.CACHE_PATH .. '/' .. newest.sysupgradeFilename) result.progress = fileSize(M.CACHE_PATH .. '/' .. newest.sysupgradeFilename)
if not result.progress then result.progress = 0 end -- in case the file does not exist yet (which yields nil) if not result.progress then result.progress = 0 end -- in case the file does not exist yet (which yields nil)
result.imageSize = newest.sysupgradeFileSize result.imageSize = newest.sysupgradeFileSize
end end
return result return true, result
end end
-- Turns a plain-text version into a table. -- Turns a plain-text version into a table.
@ -454,24 +439,24 @@ function M.downloadImageFile(versionEntry, devType, isFactory)
local rv = 0 local rv = 0
if doDownload then if doDownload then
setState(M.STATE.DOWNLOADING, "Downloading image (" .. filename .. ")") M.setState(M.STATE.DOWNLOADING, "Downloading image (" .. filename .. ")")
rv = downloadFile(baseUrl .. '/images/' .. filename, M.CACHE_PATH, filename) rv = downloadFile(baseUrl .. '/images/' .. filename, M.CACHE_PATH, filename)
end end
if rv == 0 then if rv == 0 then
if M.checkValidImage(versionEntry, devType, isFactory) then if M.checkValidImage(versionEntry, devType, isFactory) then
setState(M.STATE.IMAGE_READY, "Image downloaded, ready to install (image name: " .. filename .. ")") M.setState(M.STATE.IMAGE_READY, "Image downloaded, ready to install (image name: " .. filename .. ")")
return true return true
else else
removeFile(M.CACHE_PATH .. '/' .. filename) removeFile(M.CACHE_PATH .. '/' .. filename)
local ws = "Image download failed (invalid image)" local ws = "Image download failed (invalid image)"
setState(M.STATE_DOWNLOAD_FAILED, ws) M.setState(M.STATE.DOWNLOAD_FAILED, ws)
return nil,ws return nil,ws
end end
else else
local ws = wgetStatusToString(rv) local ws = wgetStatusToString(rv)
removeFile(M.CACHE_PATH .. '/' .. filename) removeFile(M.CACHE_PATH .. '/' .. filename)
setState(M.STATE.DOWNLOAD_FAILED, "Image download failed (wget error: " .. ws .. ")") M.setState(M.STATE.DOWNLOAD_FAILED, "Image download failed (wget error: " .. ws .. ")")
return nil,ws return nil,ws
end end
end end
@ -480,6 +465,7 @@ end
-- noRetain, devType and isFactory are optional -- noRetain, devType and isFactory are optional
-- returns true or nil + wget return value -- returns true or nil + wget return value
function M.flashImageVersion(versionEntry, noRetain, devType, isFactory) function M.flashImageVersion(versionEntry, noRetain, devType, isFactory)
log:info("flashImageVersion")
local imgName = M.constructImageFilename(versionEntry.version, devType, isFactory) local imgName = M.constructImageFilename(versionEntry.version, devType, isFactory)
local cmd = noRetain and 'sysupgrade -n ' or 'sysupgrade ' local cmd = noRetain and 'sysupgrade -n ' or 'sysupgrade '
cmd = cmd .. M.CACHE_PATH .. '/' .. imgName cmd = cmd .. M.CACHE_PATH .. '/' .. imgName
@ -491,15 +477,15 @@ function M.flashImageVersion(versionEntry, noRetain, devType, isFactory)
return nil,"no valid image for requested version present" return nil,"no valid image for requested version present"
end end
setState(M.STATE.INSTALLING, "Installing new image (" .. imgName .. ")") -- yes this is rather pointless M.setState(M.STATE.INSTALLING, "Installing new image (" .. imgName .. ")") -- yes this is rather pointless
local rv = runCommand(cmd) -- if everything goes to plan, this will not return local rv = runCommand(cmd) -- if everything goes to plan, this will not return
if rv == 0 then if rv == 0 then
setState(M.STATE.INSTALLED, "Image installed") M.setState(M.STATE.INSTALLED, "Image installed")
else else
-- NOTE: if cmdrv == 127, this means the command was not found -- NOTE: if cmdrv == 127, this means the command was not found
local cmdrv,sysrv = splitExitStatus(rv) local cmdrv,sysrv = splitExitStatus(rv)
setState(M.STATE.INSTALL_FAILED, "Image installation failed (sysupgrade returned " .. cmdrv .. ", execution status: " .. sysrv .. ")") M.setState(M.STATE.INSTALL_FAILED, "Image installation failed (sysupgrade returned " .. cmdrv .. ", execution status: " .. sysrv .. ")")
end end
return (rv == 0) and true or nil,rv return (rv == 0) and true or nil,rv
@ -511,12 +497,27 @@ function M.clear()
if not ccRv then return nil,ccMsg end if not ccRv then return nil,ccMsg end
D("Removing " .. M.CACHE_PATH .. "/doodle3d-wifibox-*.bin") D("Removing " .. M.CACHE_PATH .. "/doodle3d-wifibox-*.bin")
setState(M.STATE.NONE, "") M.setState(M.STATE.NONE, "")
local rv = os.execute('rm -f ' .. M.CACHE_PATH .. '/doodle3d-wifibox-*.bin') local rv = os.execute('rm -f ' .. M.CACHE_PATH .. '/doodle3d-wifibox-*.bin')
return (rv == 0) and true or nil,"could not remove image files" return (rv == 0) and true or nil,"could not remove image files"
end end
-- NOTE: make sure the cache directory exists before calling this function or it will fail.
-- NOTE: this function _can_ fail but we don't expect this to happen so the return value is ignored for now
function M.setState(code, msg)
local s = code .. '|' .. msg
D("set update state: " .. M.STATE_NAMES[code] .. " ('" .. s .. "')")
local file,msg = io.open(M.CACHE_PATH .. '/' .. M.STATE_FILE, 'w')
if not file then
E("error: could not open state file for writing (" .. msg .. ")")
return false
end
file:write(s)
file:close()
return true
end