https://github.com/chilipeppr/cayenn-laseruv
Here is the latest working code for a ChiliPeppr Cayenn device. The code consists of a main entry point called main_laseruv.lua. That file then loads all the supporting Lua files to create a complete Cayenn device.
The code lets you setup a NodeMCU to talk back to ChiliPeppr via the Cayenn protocol. The code does a few things:
- Announces the existence of the device to your network so ChiliPeppr can see it
- Lets ChiliPeppr send commands to the device to control it
- Lets ChiliPeppr upload a set of commands with ID's from your pre-processed Gcode
- Sync your NodeMCU to your main CNC controller via the coolant on/off pin so as your Gcode executes, the NodeMCU will stay in sync and execute any relevant commands that were pre-uploaded at the exact time
- Send data back to ChiliPeppr during execution
The image below has 3 Cayenn devices in it, but this code is for the Laser 3A device.
main_laseruv.lua
(Please see Github project for latest version of this code, as code below is no longer current.)https://github.com/chilipeppr/cayenn-laseruv
-- Main entry point for Laser UV Cayenn device
-- set freq to highest possible
node.setcpufreq(node.CPU160MHZ)
cayenn = require('cayenn_v3')
laser = require('laser_3amp_driver_v1')
cnc = require('tinyg_read_v3')
queue = require("queue")
led = require("led")
opts = {}
opts.Name = "LaserUV"
opts.Desc = "Control the BDR209 UV laser"
opts.Icon = "https://raw.githubusercontent.com/chilipeppr/widget-cayenn/master/laser.png"
opts.Widget = "com-chilipeppr-widget-laser"
-- opts.WidgetUrl = "https://github.com/chilipeppr/widget-laser/auto-generated.html"
-- define commands supported
cmds = {
"ResetCtr", "GetCtr", "GetCmds", "GetQ", "WipeQ", "CmdQ", "Mem",
"LaserBoot", "LaserShutdown",
'PwmOn {Hz,Duty} (Max Hz:1000, Max Duty:1023)', "PwmOff",
'PulseFor {ms}',
'MaxDuty {Duty}'
}
-- this is called by the cnc library when the coolant pin changes
function onCncCounter(counter)
print("Got CNC pin change. counter:" .. counter)
local cmd = queue.getId(counter)
onCmd(cmd)
end
-- this is called by Cayenn when an incoming cmd comes in
-- from the network, i.e. from SPJS. These are TCP commands so
-- they are guaranteed to come in (vs UDP which could drop)
function onCmd(payload)
if (type(payload) == "table") then
-- print("is json")
-- print("Got incoming Cayenn cmd JSON: " .. cjson.encode(payload))
-- See what cmd
if payload.Cmd == "LaserBoot" then
laser.relayOn()
cayenn.sendBroadcast({["TransId"] = payload.TransId, ["Resp"] = payload.Cmd})
led.blink(4)
-- print("Turned on relay to laser driver. Takes 3 secs to boot.")
elseif payload.Cmd == "LaserShutdown" then
laser.relayOff()
cayenn.sendBroadcast({["TransId"] = payload.TransId, ["Resp"] = payload.Cmd})
led.blink(2)
-- print("Turned off relay to laser driver")
elseif payload.Cmd == "MaxDuty" then
-- force a max duty to control laser power
laser.pwmSetMaxDuty(payload.Duty)
cayenn.sendBroadcast({["TransId"] = payload.TransId, ["Resp"] = payload.Cmd, ["MaxDuty"] = payload.Duty})
led.blink(2)
elseif payload.Cmd == "PulseFor" then
-- should have been given milliseconds to pulse for
cayenn.sendBroadcast({["TransId"] = payload.TransId, ["Resp"] = payload.Cmd})
led.blink(1)
-- run pulse after sending resp so no cpu is being used
-- so we get precise timing
laser.pulseFor(payload.ms)
-- print("Turned off relay to laser driver")
elseif payload.Cmd == "PwmOn" then
-- should have been given milliseconds to pulse for
led.blink(1)
if payload.Hz == nil or payload.Duty == nil then
cayenn.sendBroadcast({["TransId"] = payload.TransId, ["Resp"] = payload.Cmd, ["Err"] = "Hz or Duty not specified"})
elseif 'number' ~= type(payload.Hz) or 'number' ~= type(payload.Duty) then
cayenn.sendBroadcast({["TransId"] = payload.TransId, ["Resp"] = payload.Cmd, ["Err"] = "Hz or Duty not a number"})
elseif payload.Hz > 1000 then
cayenn.sendBroadcast({["TransId"] = payload.TransId, ["Resp"] = payload.Cmd, ["Err"] = "Hz " .. payload.Hz .. " too high", ["Hz"] = payload.Hz})
elseif payload.Hz <= 0 then
cayenn.sendBroadcast({["TransId"] = payload.TransId, ["Resp"] = payload.Cmd, ["Err"] = "Hz " .. payload.Hz .. " too low", ["Hz"] = payload.Hz})
elseif payload.Duty > 1023 then
cayenn.sendBroadcast({["TransId"] = payload.TransId, ["Resp"] = payload.Cmd, ["Err"] = "Duty " .. payload.Duty .. " too high"})
else
local actualDuty = laser.pwmOn(payload.Hz, payload.Duty)
cayenn.sendBroadcast({["TransId"] = payload.TransId, ["Resp"] = "PwmOn", ["Hz"] = payload.Hz, ["Duty"] = actualDuty})
end
elseif payload.Cmd == "PwmOff" then
-- should have been given milliseconds to pulse for
cayenn.sendBroadcast({["TransId"] = payload.TransId, ["Resp"] = "PwmOff"})
led.blink(1)
laser.pwmOff()
elseif payload.Cmd == "ResetCtr" then
cnc.resetIdCounter()
cayenn.sendBroadcast({["TransId"] = payload.TransId, ["Resp"] = payload.Cmd, ["Ctr"] = cnc.getIdCounter()})
led.blink(1)
elseif payload.Cmd == "GetCtr" then
-- cnc.resetIdCounter()
cayenn.sendBroadcast({["TransId"] = payload.TransId, ["Resp"] = payload.Cmd, ["Ctr"] = cnc.getIdCounter()})
led.blink(1)
elseif payload.Cmd == "GetCmds" then
local resp = {}
resp.Resp = "GetCmds"
resp.Cmds = cmds
resp.TransId = payload.TransId
-- resp.CmdsMeta = cmdsMeta
cayenn.sendBroadcast(resp)
led.blink(2)
elseif payload.Cmd == "GetQ" then
-- this method will send slowly as not to overwhelm
queue.send(cayenn.sendBroadcast, payload.TransId)
-- print("Sending queue back to network")
-- cayenn.sendBroadcast({["Resp"] = payload.Cmd, ["Start"] = 0})
-- local resp = {}
-- local count = 0
-- resp.Resp = "GetQ"
-- -- loop and send multiple packets so we don't run out of mem
-- cmd = queue.getId(count)
-- while cmd ~= nil do
-- cayenn.sendBroadcast({["Resp"] = payload.Cmd, ["Cmd"] = cmd.Cmd, ["Id"] = cmd.Id})
-- print(cmd)
-- resp.Queue = cmd
-- cayenn.sendBroadcast(resp)
-- count = count + 1
-- cmd = queue.getId(count)
-- led.blink(1)
-- end
-- cayenn.sendBroadcast({["Resp"] = payload.Cmd, ["Q"] = queue.getTxt()})
led.blink(2)
elseif payload.Cmd == "WipeQ" then
-- queue = {}
-- print("Wiped queue: " .. cjson.encode(queue))
queue.wipe()
cayenn.sendBroadcast({["TransId"] = payload.TransId, ["Resp"] = payload.Cmd})
led.blink(1)
elseif payload.Cmd == "CmdQ" then
-- queuing cmd. we must have ID.
if payload.Id == nil then
-- print("Error queuing command. It must have an ID")
return
end
if payload.RunCmd == nil then
-- print("Error queuing command. It must have a RunCmd like RunCmd:{Cmd:AugerOn,Speed:10}.")
return
end
-- wipe the peerIp cuz don't need it
payload.peerIp = nil
-- print("Queing command")
--queue[payload.Id] = payload.RunCmd
payload.RunCmd.Id = payload.Id
queue.add(payload.RunCmd)
-- print("New queue: " .. cjson.encode(queue))
cayenn.sendBroadcast({["TransId"] = payload.TransId, ["Resp"] = payload.Cmd, ["Id"] = payload.Id, ["MemRemain"] = node.heap()})
led.blink(1)
elseif payload.Cmd == "Mem" then
cayenn.sendBroadcast({["TransId"] = payload.TransId, ["Resp"] = payload.Cmd, ["MemRemain"] = node.heap()})
led.blink(2)
elseif payload["Announce"] ~= nil then
-- do nothing.
if payload.Announce == "i-am-your-server" then
-- perhaps store this ip in future
-- so we know what our server is
end
else
cayenn.sendBroadcast({["TransId"] = payload.TransId, ["Resp"] = payload.Cmd, ["Err"] = "Unsupported cmd"})
-- print("Got cmd we do not understand. Huh?")
led.blink(1)
end
else
-- print("is string")
-- print("Got incoming Cayenn cmd. str: ", payload)
end
end
-- this callback is called when an incoming UDP broadcast
-- comes in to this device. typically this is just for
-- Cayenn Discover requests to figure out what devices are on
-- the network
function onIncomingBroadcast(cmd)
-- print("Got incoming UDP cmd: ", cmd)
if (type(cmd) == "table") then
if cmd["Cayenn"] ~= nil then
if cmd.Cayenn == "Discover" then
-- somebody is asking me to announce myself
cayenn.sendAnnounceBroadcast()
else
-- print("Do not understand incoming Cayenn cmd")
end
elseif cmd["Announce"] ~= nil then
if cmd.Announce == "i-am-your-server" then
-- we should store the server address so we can send
-- back TCP
-- print("Got a server announcement. Cool. Store it. TODO")
else
-- print("Got announce but not from a server. Huh?")
end
else
-- print("Do not understand incoming UDP cmd")
end
else
-- print("Got incoming UDP as string")
end
end
-- add listener to incoming cayenn commands
cayenn.addListenerOnIncomingCmd(onCmd)
cayenn.addListenerOnIncomingUdpCmd(onIncomingBroadcast)
cayenn.init(opts)
laser.init()
-- laser.pwmSetMaxDuty(100)
-- listen to coolant pin changes
cnc.addListenerOnIdChange(onCncCounter)
cnc.init()
led.blink(6)
print("Mem left:" .. node.heap())
cayenn_v3.lua
-- Cayenn Protocol for ChiliPeppr
-- This module does udp/tcp sending/receiving to talk with SPJS
-- or have the browser talk direct to this ESP8266 device.
-- This module has methods for connecting to wifi, then initting
-- UDP servers, TCP servers, and sending a broadcast announce out.
-- The broadcast announce let's any listening SPJS know we're alive.
-- SPJS then reflects that message to any listening browsers like
-- ChiliPeppr so they know a device is available on the network.
-- Then the browser can send commands back to this device.
local M = {}
-- M = {}
M.port = 8988
M.myip = nil
M.sock = nil
M.jsonTagTable = nil
--M.announce =
M.isInitted = false
-- When you are initting you can pass in tags to describe your device
-- You should use a format like this:
-- cayenn.init({
-- widget: "com-chilipeppr-widget-ina219",
-- icon: "http://chilipeppr.com/img/ina219.jpg",
-- widgetUrl: "https://github.com/chilipeppr/widget-ina219/auto-generated.html",
-- })
function M.init(jsonTagTable)
-- save the jsonTagTable
if jsonTagTable ~= nil then
M.jsonTagTable = jsonTagTable
end
if M.isInitted then
print("Already initted")
return
end
print("Init...")
-- figure out if i have an IP
M.myip = wifi.sta.getip()
if M.myip == nil then
print("Connecting to wifi.")
M.setupWifi()
else
print("My IP: " .. M.myip)
M.isInitted = true
-- create socket for outbound UDP sending
M.sock = net.createConnection(net.UDP, 0)
-- create server to listen to incoming udp
M.initUdpServer()
-- create server to listen to incoming tcp
M.initTcpServer()
-- send our announce
M.sendAnnounceBroadcast(M.jsonTagTable)
end
end
function M.createAnnounce(jsonTagTable)
local a = {}
a.Announce = "i-am-a-client"
-- a.MyDeviceId = "chip:" .. node.chipid() .. "-flash:" .. node.flashid() .. "-mac:" .. wifi.sta.getmac()
a.MyDeviceId = "chip:" .. node.chipid() .. "-mac:" .. wifi.sta.getmac()
-- if jsonTagTable.Name then
-- a.Name = jsonTagTable.Name
-- -- jsonTagTable.Name = nil -- erase from table so not in jsonTags
-- elseif M.jsonTagTable.Name then
-- a.Name = M.jsonTagTable.Name
-- else
-- a.Name = "Unnamed"
-- end
-- if jsonTagTable.Desc then
-- a.Desc = jsonTagTable.Desc
-- -- jsonTagTable.Desc = nil -- erase from table so not in jsonTags
-- else
-- a.Desc = "(no desc provided)"
-- end
-- if jsonTagTable.Icon then
-- a.Icon = jsonTagTable.Icon
-- -- jsonTagTable.Icon = nil -- erase from table so not in jsonTags
-- else
-- a.Icon = "(no icon provided)"
-- end
if jsonTagTable.Widget then
a.Widget = jsonTagTable.Widget
-- jsonTagTable.Widget = nil
elseif M.jsonTagTable.Widget then
a.Widget = M.jsonTagTable.Widget
else
a.Widget = "com-chilipeppr-widget-undefined"
end
-- if jsonTagTable.WidgetUrl then
-- a.WidgetUrl = jsonTagTable.WidgetUrl
-- -- jsonTagTable.WidgetUrl = nil
-- else
-- a.WidgetUrl = "(no url specified)"
-- end
-- see if there is a jsontagtable passed in as extra meta
local jsontag = ""
if jsonTagTable then
ok, jsontag = pcall(cjson.encode, jsonTagTable)
if ok then
-- print("Adding jsontagtable" .. jsontag)
else
print("failed to encode jsontag!")
end
end
a.JsonTag = jsontag
local ok, json = pcall(cjson.encode, a)
if ok then
--print("Encoded json for announce: " .. json)
else
print("failed to encode json!")
end
print("Announce msg: " .. json)
return json
end
-- send announce to broadcast addr so spjs
-- knows of our existence
function M.sendAnnounceBroadcast(jsonTagTable)
if M.isInitted == false then
print("You must init first.")
return
end
local bip = wifi.sta.getbroadcast()
--print("Broadcast addr:" .. bip)
-- if there was no jsonTagTable passed in, then used
-- stored one
if not jsonTagTable then
jsonTagTable = M.jsonTagTable
end
print("Sending announce to ip: " .. bip)
M.sock:connect(M.port, bip)
M.sock:send(M.createAnnounce(jsonTagTable))
M.sock:close()
end
function M.sendBroadcast(jsonTagTable)
if M.isInitted == false then
print("You must init first.")
return
end
-- we need to attach deviceid
local a = {}
a.MyDeviceId = "chip:" .. node.chipid() .. "-mac:" .. wifi.sta.getmac()
-- see if there is a jsontagtable passed in as extra meta
local jsontag = ""
if jsonTagTable then
ok, jsontag = pcall(cjson.encode, jsonTagTable)
if ok then
-- print("Adding jsontagtable" .. jsontag)
else
print("failed to encode jsontag!")
end
end
a.JsonTag = jsontag
local ok, json = pcall(cjson.encode, a)
if ok then
--print("Encoded json for announce: " .. json)
else
print("failed to encode json!")
end
local bip = wifi.sta.getbroadcast()
--print("Broadcast addr:" .. bip)
-- local msg = cjson.encode(jsonTagTable)
-- local msg = cjson.encode(a)
print("Sending UDP msg: " .. json .. " to ip: " .. bip)
M.sock:connect(M.port, bip)
M.sock:send(json)
M.sock:close()
end
function M.setupWifi()
-- setwifi
wifi.setmode(wifi.STATION)
-- longest range is b
wifi.setphymode(wifi.PHYMODE_N)
--Connect to access point automatically when in range
-- for some reason digits in password seem to get mangled
-- so splitting them seems to solve problem
wifi.sta.config("NETGEAR-main", "(your password")
wifi.sta.connect()
--register callback
wifi.sta.eventMonReg(wifi.STA_IDLE, function() print("STATION_IDLE") end)
--wifi.sta.eventMonReg(wifi.STA_CONNECTING, function() print("STATION_CONNECTING") end)
wifi.sta.eventMonReg(wifi.STA_WRONGPWD, function() print("STATION_WRONG_PASSWORD") end)
wifi.sta.eventMonReg(wifi.STA_APNOTFOUND, function() print("STATION_NO_AP_FOUND") end)
wifi.sta.eventMonReg(wifi.STA_FAIL, function() print("STATION_CONNECT_FAIL") end)
wifi.sta.eventMonReg(wifi.STA_GOTIP, M.gotip)
--register callback: use previous state
wifi.sta.eventMonReg(wifi.STA_CONNECTING, function(previous_State)
if(previous_State==wifi.STA_GOTIP) then
print ("Reconnecting")
-- print("Station lost connection with access point\n\tAttempting to reconnect...")
else
print("STATION_CONNECTING")
end
end)
--start WiFi event monitor with default interval
wifi.sta.eventMonStart(1000)
end
function M.gotip()
print("STATION_GOT_IP")
M.myip = wifi.sta.getip()
print("My IP: " .. M.myip)
-- stop monitoring now since we're connected
wifi.sta.eventMonStop()
print("Stopped monitoring") -- wifi events since connected.")
-- make sure we are initted
M.init()
end
function M.initUdpServer()
M.udpServer = net.createServer(net.UDP)
--M.udpServer:on("connection", M.onUdpConnection)
M.udpServer:on("receive", M.onUdpRecv)
M.udpServer:listen(8988)
print("UDP Server started on port 8988")
end
function M.onUdpConnection(sck)
print("UDP connection.")
--ip, port = sck:getpeer()
--print("UDP connection. from: " .. ip)
end
function M.onUdpRecv(sck, data)
print("UDP Recvd. data: " .. data)
if (M.listenerOnIncomingUdpCmd) then
-- see if json
if string.sub(data,1,1) == "{" then
-- catch json errors
local succ, results = pcall(function()
return cjson.decode(data)
end)
-- see if we could parse
if succ then
data = results --cjson.decode(data)
-- data.peerIp = peer
else
print("Error parsing JSON")
return
end
end
M.listenerOnIncomingUdpCmd(data)
end
end
-- this property and method let an external object attach a
-- listener to the incoming UDP cmd
M.listenerOnIncomingUdpCmd = nil
function M.addListenerOnIncomingUdpCmd(listenerCallback)
M.listenerOnIncomingUdpCmd = listenerCallback
-- print("Attached listener to incoming UDP cmd")
end
function M.removeListenerOnIncomingUdpCmd(listenerCallback)
M.listenerOnIncomingUdpCmd = nil
-- print("Removed listener on incoming UDP cmd")
end
function M.initTcpServer()
M.tcpServer = net.createServer(net.TCP)
M.tcpServer:listen(8988, M.onTcpListen)
print("TCP Server started on port 8988")
end
function M.onTcpListen(conn)
conn:on("receive", M.onTcpRecv)
end
function M.onTcpConnection(sck)
print("TCP connection.")
--ip, port = sck:getpeer()
--print("UDP connection. from: " .. ip)
end
function M.onTcpRecv(sck, data)
local peer = sck:getpeer()
print("TCP Recvd. data: " .. data .. ", Peer:" .. peer)
if (M.listenerOnIncomingCmd) then
-- see if json
if string.sub(data,1,1) == "{" then
-- catch json errors
local succ, results = pcall(function()
return cjson.decode(data)
end)
-- see if we could parse
if succ then
data = results --cjson.decode(data)
data.peerIp = peer
else
print("Error parsing JSON")
return
end
end
M.listenerOnIncomingCmd(data)
end
end
-- this property and method let an external object attach a
-- listener to the incoming TCP command
M.listenerOnIncomingCmd = nil
function M.addListenerOnIncomingCmd(listenerCallback)
M.listenerOnIncomingCmd = listenerCallback
-- print("Attached listener to incoming TCP cmd")
end
function M.removeListenerOnIncomingCmd(listenerCallback)
M.listenerOnIncomingCmd = nil
-- print("Removed listener on incoming TCP cmd")
end
return M
--M.init()
-- cayenn = M
-- cayenn.init()
-- GOOD INIT
-- opts = {}
-- opts.Name = "Dispenser DMP16"
-- opts.Desc = "Techcon DMP16 auger with stepper and linear slide"
-- opts.Icon = "http://gds-storage-prd.s3.amazonaws.com/fusion-360/161021/1972/70f0aa71/thumbnails/raasrendering-bbf3b3d5-01c5-48e4-bbdb-f81727a520ab-160-160.jpg"
-- opts.Widget = "com-chilipeppr-widget-dispenser"
-- opts.WidgetUrl = "https://github.com/chilipeppr/widget-dispenser/auto-generated.html"
-- cayenn.init(opts)
laser_3amp_driver_v1.lua
-- Laser module. Uses ACS714 for current reading.
-- Allows toggling/pulsing of laser
-- Has global relay for main power shutdown
node.setcpufreq(node.CPU160MHZ)
local m = {}
-- m = {}
m.pin = 2 -- laser TTL
m.pinRelay = 3 -- relay to main power supply
m.tmrReading = 0
m.tmrOutput = 4
m.tmrPulse = 3
m.isOn = false
m.isInitted = false
-- user can set this to ensure power is not above max
m.maxDuty = 1023
function m.init()
if m.isInitted then
return
end
-- TODO setup the i2c current sensor
-- setup TTL
gpio.mode(m.pin, gpio.OUTPUT)
gpio.write(m.pin, gpio.LOW)
-- setup relay
-- relay requires high or low (not float) to turn on
-- so set to float to turn off main power relay
gpio.mode(m.pinRelay, gpio.INPUT)
m.isOn = false
m.isInitted = true
end
function m.relayOn()
m.init()
-- relay requires high or low (not float) to turn on
gpio.mode(m.pinRelay, gpio.OUTPUT)
gpio.write(m.pinRelay, gpio.LOW)
-- gpio.write(m.pinRelay, gpio.HIGH)
print("Relay On")
end
function m.relayOff()
m.init()
-- relay requires float to turn off
gpio.mode(m.pinRelay, gpio.INPUT)
-- gpio.write(m.pinRelay, gpio.LOW)
print("Relay Off")
end
function m.on()
m.init()
gpio.write(m.pin, gpio.HIGH)
print("Laser On")
end
function m.off()
m.init()
gpio.write(m.pin, gpio.LOW)
print("Laser Off")
end
function m.read()
-- read 30 samples and average
local val = 0
for i=0,9,1
do
val = val + adc.read(0)
-- print("val: " .. val)
end
-- val = val / 1
local pct = (val * 100) / 1024
local millivolts = ((3300 * pct)) -- / 1000)
-- subtract 2500 mV cuz that means 0 amps (2560 mV actually)
millivolts = millivolts - 2570000
if millivolts < 0 then millivolts = 0 end
-- divide by 185 to figure out amps
local ma = (millivolts) / 185
-- print("ADC: " .. val .. " Pct: " .. pct .. " mv: " .. millivolts .. " mA: " .. ma)
return ma
end
m.samples = {} --{0,0,0,0,0,0,0,0,0,0}
m.lastSampleIndex = 0
function m.readAvgStart()
m.init()
-- let's do a reading each 10ms
-- create 10 samples, but always let the samples fall
-- off the queue
tmr.alarm(m.tmrReading, 20, tmr.ALARM_AUTO, function()
local ma = m.read()
m.samples[m.lastSampleIndex] = ma
m.lastSampleIndex = m.lastSampleIndex + 1
if m.lastSampleIndex > 9 then m.lastSampleIndex = 0 end
end)
tmr.alarm(m.tmrOutput, 500, tmr.ALARM_AUTO, function()
-- figure out avg
local s = 0
for i2=0,9,1 do
s = s + tonumber(m.samples[i2])
-- print("s: " .. s .. " i2: " .. i2 .. " m.samples[]: " .. m.samples[i2])
end
s = s / 10
print("mA: " .. s)
end)
end
function m.readAvgStop()
tmr.stop(m.tmrReading)
tmr.stop(m.tmrOutput)
-- print("Stopped average reading.")
end
function m.readStart()
m.init()
tmr.alarm(m.tmrReading, 500, tmr.ALARM_AUTO, m.onRead)
end
function m.onRead()
m.read()
end
function m.readStop()
tmr.stop(m.tmrReading)
end
-- pulse the laser for a delay of ms
function m.pulseFor(delay)
m.init()
local d = 100
if delay ~= nil then
d = delay
end
tmr.alarm(m.tmrPulse, d, tmr.ALARM_AUTO, m.pulseStop)
m.on()
end
function m.pulseStop()
tmr.stop(m.tmrPulse)
m.off()
end
-- frequency in hertz. max 1000hz or 1khz
-- duty cycle. 0 is 0% duty. 1023 is 100% duty. 512 is 50%.
function m.pwmOn(freqHz, duty)
if (duty > m.maxDuty) then duty = m.maxDuty end
print("Laser pwmOn hz:", freqHz, "duty:", duty)
pwm.setup(m.pin, freqHz, duty)
pwm.start(m.pin)
return duty
end
function m.pwmOff()
print("Laser pwmOff")
pwm.stop(m.pin)
end
function m.pwmSetMaxDuty(duty)
m.maxDuty = duty
end
-- m.init()
-- m.readStart()
-- m.readAvgStart()
return m
tinyg_read_v3.lua
-- Read the inputs from TinyG
-- We need to watch Coolant Pin
-- We also need to watch the A axis step/dir pins.
local m = {}
-- m = {}
m.pinCoolant = 5
m.pinADir = 6
m.pinAStep = 7
-- global ID counter. we increment this each time we see
-- a signal on the coolant pin
m.idCounter = -1
function m.init()
-- gpio.mode(m.pinCoolant, gpio.INPUT)
gpio.mode(m.pinCoolant, gpio.INT) --, gpio.PULLUP)
gpio.mode(m.pinADir, gpio.INT)
gpio.mode(m.pinAStep, gpio.INT)
gpio.trig(m.pinCoolant, "both", m.pinCoolantCallback)
-- gpio.trig(m.pinCoolant, "up", debounce(m.pinCoolantCallback))
-- gpio.trig(m.pinADir, "both", m.pinADirCallback)
-- gpio.trig(m.pinAStep, "both", m.pinAStepCallback)
-- print("Setup pin watchers for Coolant, ADir, AStep")
print("Coolant: " .. tostring(gpio.read(m.pinCoolant)) ..
", ADir: " .. tostring(gpio.read(m.pinADir)) ..
", AStep: " .. tostring(gpio.read(m.pinAStep))
)
end
function m.status()
print("Coolant: " .. tostring(gpio.read(m.pinCoolant)) ..
", ADir: " .. tostring(gpio.read(m.pinADir)) ..
", AStep: " .. tostring(gpio.read(m.pinAStep))
)
end
-- function debounce (func)
-- local last = 0
-- local delay = 50000 -- 50ms * 1000 as tmr.now() has μs resolution
-- return function (...)
-- local now = tmr.now()
-- local delta = now - last
-- if delta < 0 then delta = delta + 214748364 end; -- proposed because of delta rolling over, https://github.com/hackhitchin/esp8266-co-uk/issues/2
-- if delta < delay then return end;
-- last = now
-- return func(...)
-- end
-- end
-- function onChange ()
-- print('The pin value has changed to '..gpio.read(pin))
-- end
-- gpio.mode(pin, gpio.INT, gpio.PULLUP) -- see https://github.com/hackhitchin/esp8266-co-uk/pull/1
-- gpio.trig(pin, 'both', debounce(onChange))
m.lastTime = 0
m.lookingFor = gpio.HIGH
m.lookingCtr = 0
function m.pinCoolantCallback(level)
-- we get called here on rising and falling edge
if m.lookingFor == gpio.HIGH then
-- read 10 more times, and if we get good reads, trust it
local readCtr = 0
for i = 0, 5 do
if gpio.read(m.pinCoolant) == gpio.HIGH then
readCtr = readCtr + 1
end
end
if readCtr > 3 then
-- treat that as good avg
m.idCounter = m.idCounter + 1
-- print("Got coolant pin. idCounter: " .. m.idCounter)
m.onIdChange()
m.lookingFor = gpio.LOW
else
-- print("Failed avg")
end
else
-- looking for gpio.LOW
-- read 10 more times, and if we get good reads, trust it
local readCtr = 0
for i = 0, 5 do
if gpio.read(m.pinCoolant) == gpio.LOW then
readCtr = readCtr + 1
end
end
if readCtr > 3 then
-- treat that as good avg
-- print("Trusting low")
m.lookingFor = gpio.HIGH
else
-- print("Failed avg")
end
end
gpio.trig(m.pinCoolant, "both")
end
-- function m.pinCoolantCallbackOld(level)
-- -- this method is called when the coolant pin has an interrupt
-- if m.lookingFor == gpio.HIGH then
-- -- we are waiting for coolant to go high
-- if level == gpio.HIGH then
-- -- increment our ctr. if we get 10 in a row trust it.
-- m.lookingCtr = m.lookingCtr + 1
-- if m.lookingCtr > 5 then
-- -- we have hit our target. trust it.
-- m.idCounter = m.idCounter + 1
-- print("Got coolant pin. Level: " .. level .. " idCounter: " .. m.idCounter .. " tmr:" .. tmr.now())
-- m.onIdChange()
-- m.lookingFor = gpio.LOW
-- m.lookingCtr = 0
-- -- now look for falling edge
-- gpio.trig(m.pinCoolant, "down")
-- else
-- -- keep counting highs
-- gpio.trig(m.pinCoolant, "high")
-- end
-- else
-- -- we just saw a low, so reset our ctr to start again
-- -- so we get 10 clean reads
-- m.lookingCtr = 0
-- -- print("reset lookingCtr to 0")
-- gpio.trig(m.pinCoolant, "high")
-- end
-- else
-- -- we are waiting for coolant to go low
-- if level == gpio.LOW then
-- -- we got the low we are looking for
-- -- increment our ctr. if we get 10 in a row trust it.
-- m.lookingCtr = m.lookingCtr + 1
-- if m.lookingCtr > 5 then
-- -- print("Trusting we are low now")
-- -- now look for high
-- m.lookingFor = gpio.HIGH
-- m.lookingCtr = 0
-- -- now look for rising edge
-- gpio.trig(m.pinCoolant, "up")
-- else
-- -- keep counting lows
-- gpio.trig(m.pinCoolant, "low")
-- end
-- else
-- -- we just saw a high, so reset our ctr to start again
-- -- so we get 10 clean reads
-- m.lookingCtr = 0
-- -- print("reset lookingCtr to 0")
-- gpio.trig(m.pinCoolant, "low")
-- end
-- end
-- -- if we got a low, toss it
-- if level == gpio.LOW then
-- -- just trigger next callback
-- gpio.trig(m.pinCoolant, "up")
-- print("crapped bad lvl")
-- return
-- end
-- we have a high. now make sure we see high for 200 more reads.
-- if we do, we're safe
-- for i = 0, 200 do
-- if gpio.read(m.pinCoolant) == gpio.LOW then
-- -- if we got a low, consider this bad data
-- -- just trigger next callback
-- gpio.trig(m.pinCoolant, "up")
-- print("crapped on read " .. i .. " of 1000 reads")
-- return
-- end
-- end
-- -- if we got here, then we can be sure it's a good HIGH read
-- m.idCounter = m.idCounter + 1
-- m.onIdChange()
-- print("Got coolant pin. Level: " .. level .. " idCounter: " .. m.idCounter .. " tmr:" .. tmr.now())
-- gpio.trig(m.pinCoolant, "up")
-- end
-- end
function m.resetIdCounter()
m.idCounter = -1
m.onIdChange()
print("Reset idCounter: " .. m.idCounter)
end
function m.getIdCounter()
return m.idCounter
end
-- this property and method let an external object attach a
-- listener to the counter change
m.listenerOnIdChange = null
function m.addListenerOnIdChange(listenerCallback)
m.listenerOnIdChange = listenerCallback
-- print("Attached listener to Id Change")
end
function m.removeListenerOnIdChange(listenerCallback)
m.listenerOnIdChange = null
-- print("Removed listener on Id Change")
end
function m.onIdChange()
if m.listenerOnIdChange then
m.listenerOnIdChange(m.idCounter)
end
end
-- this property and method let an external object attach a
-- listener to the ADir pin
m.listenerOnADir = null
function m.addListenerOnADir(listenerCallback)
m.listenerOnADir = listenerCallback
-- print("Attached listener to ADir pin")
end
function m.removeListenerOnADir(listenerCallback)
m.listenerOnADir = null
-- print("Removed listener on ADir pin")
end
-- this property and method let an external object attach a
-- listener to the AStep pin
m.listenerOnAStep = null
function m.addListenerOnAStep(listenerCallback)
m.listenerOnAStep = listenerCallback
-- print("Attached listener to AStep pin")
end
function m.removeListenerOnAStep(listenerCallback)
m.listenerOnAStep = null
-- print("Removed listener on AStep pin")
end
function m.pinADirCallback(level)
gpio.trig(m.pinADir, "both")
-- this method is called when the ADir pin has an interrupt
-- we need to simply regurgitate it to appropriate listener
print("ADir: " .. level)
-- call listener
if m.listenerOnADir then
m.listenerOnADir()
end
-- print("Got coolant pin. idCounter: " .. m.idCounter)
end
function m.pinAStepCallback(level)
gpio.trig(m.pinAStep, "both")
-- this method is called when the AStep pin has an interrupt
-- we need to simply regurgitate it to appropriate listener
print("AStep: " .. level)
-- call listener
if m.listenerOnAStep then
m.listenerOnAStep()
end
end
return m
-- m.init()
queue.lua
-- Cayenn Queue by using a filelocal m = {}
-- m = {}
m.filename = "queue.txt"
m.tmrSend = 1
function m.wipe()
file.remove(m.filename)
file.open(m.filename, "w+")
-- file.writeline("")
file.close()
end
function m.add(payload)
-- open 'queue.txt' in 'a+' mode to append
file.open(m.filename, "a+")
local ok, jsontag = pcall(cjson.encode, payload)
if ok then
-- write to the end of the file
file.writeline(jsontag)
-- print("Wrote line to file:" .. jsontag)
else
-- print("failed to encode jsontag for queue file!")
end
file.close()
end
function m.getId(id)
-- if file.exists(m.filename) == false then
-- return nil
-- end
file.open(m.filename, "r")
-- read the # of lines by the id
local line
for ctr = 0, id do
line = file.readline()
-- print("Line:", id, ctr, line)
end
file.close()
-- print("Line:", line)
if line == nil then
return nil
end
-- parse json to table
local succ, results = pcall(function()
return cjson.decode(line)
end)
-- see if we could parse
if succ then
--data = results
return results
else
-- print("Error parsing JSON")
return nil
end
end
function m.getTxt()
file.open(m.filename, "r")
local txt = file.read()
file.close()
return txt
end
-- this method sends back data slowly
m.lastSendId = 0
m.callback = nil
m.transId = nil
function m.send(callback, transId)
-- reset ctr
m.lastSendId = 0
m.callback = callback
m.transId = transId
-- say we are starting
m.callback({["TransId"] = m.transId, ["Resp"] = "GetQ", ["Start"] = 0})
-- callback slowly and send q each time
tmr.alarm(m.tmrSend, 2, tmr.ALARM_SEMI, m.onSend)
end
function m.onSend()
-- get next line
local cmd = queue.getId(m.lastSendId)
if cmd ~= nil then
m.callback({["TransId"] = m.transId, ["Resp"] = "GetQ", ["Q"] = cmd})
m.lastSendId = m.lastSendId + 1
tmr.start(m.tmrSend)
else
-- we are done, cuz hit null
m.callback({["TransId"] = m.transId, ["Resp"] = "GetQ", ["Finish"] = m.lastSendId})
end
end
return m
led.lua
-- control led on esp8266-- use this module like
-- led = require("led")
-- led.on() -- turn on led
-- led.off() -- turn off led
-- led.blink(2) -- blink twice w/ 200ms delay
-- led.blink(6,100) -- blink 6 times w/ 100ms delay
-- led.blink(10,20) -- blink 10 times w/ 20ms delay
local led = {}
-- led = {}
led.pin = 4 -- this is GPIO2
led.timerId = 6 -- 0 thru 6 allowed. change to not conflict.
led.isOn = false
led.isInitted = false
function led.init()
-- setup led as output and turn on
gpio.mode(led.pin, gpio.OUTPUT)
gpio.write(led.pin, gpio.HIGH)
led.isOn = false
led.isInitted = true
end
function led.on()
if led.isInitted ~= true then
led.init()
end
gpio.write(4, gpio.LOW) --on
-- print("Led on")
end
function led.off()
if led.isInitted ~= true then
led.init()
end
gpio.write(4, gpio.HIGH) --off
-- print("Led off")
end
led.blinkTimes = 0
led.delay = 200
function led.blink(val, delay)
led.blinkTimes = val * 2
if delay ~= nil and delay > 0 then
led.delay = delay
end
tmr.alarm(led.timerId, led.delay, tmr.ALARM_AUTO, led.onTimer)
end
led.ctr = 0
function led.onTimer()
led.ctr = led.ctr + 1
if led.ctr > led.blinkTimes then
tmr.stop(led.timerId)
led.ctr = 0
led.off()
-- print("Done")
led.delay = 200 --reset delay to default
return
end
if gpio.read(4) == gpio.HIGH then
led.on()
else
led.off()
end
end
return led
init.lua
dofile("main_laseruv.lc")