This is an /etc/init.d/ script you can use on your Raspberry Pi, or other Linux machine, to auto start cloud9 at boot time.
#!/bin/bash
### BEGIN INIT INFO
# Provides: cloud9
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Simple script to start cloud9 at boot
# Description: A simple script which will start / stop cloud9 at boot / shutdown.
### END INIT INFO
# If you want a command to always run, put it here
# Carry out specific functions when asked to by the system
case "$1" in
start)
# echo "Starting noip"
# run application you want to start
cd /home/pi/c9sdk
sudo -u pi ./server.js -s standalone-homedir -l 0.0.0.0 -a : &
echo "Launching cloud9 with workspace root set to /home/pi"
;;
stop)
echo "Stopping cloud9"
# kill application you want to stop
pkill -f "node ./server.js"
;;
*)
echo "Usage: /etc/init.d/cloud9 {start|stop}"
exit 1
;;
esac
exit 0
Wednesday, September 6, 2017
Tuesday, January 3, 2017
Latest Lua Code for Laser Cayenn Device
Please see new Github repo for latest code, as the code below is now outdated, but the description of how this project works is still valid.
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:
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 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)
local 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
-- 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
dofile("main_laseruv.lc")
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")
Subscribe to:
Posts (Atom)