nodemcu-firmware/lua_modules/joystick/joystick.lua

256 lines
6.3 KiB
Lua

-- ChiliPeppr Joystick controller
-- Used to control analog joysticks that are implemented just using
-- variable resistors where you feed in 3.3v and get out a variable voltage
-- based on position of the joystick. Joysticks like the Playstation portable
-- or Xbox joysticks work this way and are readily available on Amazon, Ebay,
-- or Aliexpress.
--
-- Working example of using this library:
-- https://www.youtube.com/watch?v=ITgh5epyPRk
--
-- Visit http://chilipeppr.com/esp32
-- By John Lauer
--
-- There is no license needed to use/modify this software. It is given freely
-- to the open source community. Modify at will.
--
-- To use:
-- joystick = require("joystick")
-- joystick.on("xy", function(x, y)
-- print("Got xy change. x:", x, "y:", y)
-- end)
-- joystick.on("x", function(x)
-- print("Got x change. x:", x)
-- end)
-- joystick.on("y", function(y)
-- print("Got y change. y:", y)
-- end)
-- joystick.on("xcenter", function()
-- print("Got x center")
-- end)
-- joystick.on("ycenter", function()
-- print("Got y center")
-- end)
-- joystick.on("button", function(val)
-- print("Got button:", val)
-- end)
-- joystick.init()
-- For test/debug
-- if m.tmr then m.tmr:stop(); m.tmr:unregister() end
local m = {}
-- m = {}
m.pinAdcX = 6 -- ADC 6: GPIO34
-- m.pinAdcY = 3 -- ADC 3: GPIO39
-- m.pinAdcY = 7 -- ADC 7: GPIO35
m.pinAdcY = 5 -- ADC 5: GPIO33
-- m.pinSwitch = 18
m.pinSwitch = 32
-- For TTGO
-- m.centerX = 1850
-- m.centerXMin = 1800
-- m.centerXMax = 1915
-- m.centerY = 1850
-- m.centerYMin = 1800
-- m.centerYMax = 1915
-- For Wemos battery powered
m.centerX = 1800
m.centerXMin = 1700
m.centerXMax = 1870
m.centerY = 1800
m.centerYMin = 1700
m.centerYMax = 1870
m.isInitted = false
m.tmr = tmr.create()
---
-- @name init
-- @description Call this first to setup your ADC ports and button port. You
-- need to connect X to ADC 6: GPIO34, Y to ADC5: GPIO33, and the button to GPIO32.
-- If you need to use alternate ports you should change the code in joystick.lua.
-- Also, 11db attenuation is turned on by default because it is the best
-- performance for analog joysticks. If you are finding a broader range of voltage
-- is coming back from your joystick, adjust the attenuation in the init() method.
-- @returns nil
function m.init()
if m.isInitted then
return
end
print("Initting joystick pins. X: " .. m.pinAdcX .. ", Y: " .. m.pinAdcY .. ", Switch: " .. m.pinSwitch)
-- Initialise the pins
gpio.config( { gpio=m.pinSwitch, dir=gpio.IN, pull=gpio.PULL_UP } )
m.startSwitchInterrupt()
-- adc.setwidth(adc.ADC1, 9) -- read 9 bit on ADC
adc.setwidth(adc.ADC1, 12) -- read 12 bit on ADC
adc.setup(adc.ADC1, m.pinAdcX, adc.ATTEN_11db )
adc.setup(adc.ADC1, m.pinAdcY, adc.ATTEN_11db )
m.tmr:alarm(250, tmr.ALARM_AUTO, function()
m.readXY()
-- m.readY()
-- m.readX()
end)
m.isInitted = true
end
m.onX = nil
m.onXCenter = nil
m.onY = nil
m.onYCenter = nil
m.onXY = nil
m.onButton = nil
---
-- @name on
-- @description Attach to the callback events available from this joystick library
-- @param event_name A string of the event you want a callback on. "x", "y", "xy", "xcenter", "ycenter", "button"
-- @param func The callback to receive after the event
-- @returns nil
function m.on(event_name, func)
if event_name == "x" then
m.onX = func
elseif event_name == "xcenter" then
m.onXCenter = func
elseif event_name == "y" then
m.onY = func
elseif event_name == "ycenter" then
m.onYCenter = func
elseif event_name == "xy" then
m.onXY = func
elseif event_name == "button" then
m.onButton = func
end
end
m.lastX = 0
function m.readX()
local val = adc.read(adc.ADC1, m.pinAdcX)
if m.lastX == val then return end
m.lastX = val
print("X: " .. val)
if m.onX then m.onX(val) end
end
m.lastY = 0
function m.readY()
local val = adc.read(adc.ADC1, m.pinAdcY)
if m.lastY == val then return end
m.lastY = val
print("Y: " .. val)
if m.onY then m.onY(val) end
end
m.lastXWasCenter = true
m.lastYWasCenter = true
function m.readXY()
local valX = adc.read(adc.ADC1, m.pinAdcX)
local valY = adc.read(adc.ADC1, m.pinAdcY)
-- if x or y is new value then send xy update
local isXYUpdated = false
-- do some level setting for center point
if valX >= m.centerXMin and valX <= m.centerXMax then
-- consider it a center point
-- valX = m.centerX
-- see if last X was center, and if so ignore
if m.lastXWasCenter then
-- do nothing
else
-- last x was not center, so fire off event
m.lastXWasCenter = true
if m.onXCenter then
node.task.post(node.task.LOW_PRIORITY, function()
m.onXCenter(m.centerX)
end)
end
end
else
-- x is not on center, so fire event
m.lastXWasCenter = false
isXYUpdated = true
if m.onX then
node.task.post(node.task.LOW_PRIORITY, function()
m.onX(valX)
end)
end
end
if valY >= m.centerYMin and valY <= m.centerYMax then
-- consider it a center point
-- valY = m.centerY
-- see if last Y was center, and if so ignore
if m.lastYWasCenter then
-- do nothing
else
-- last x was not center, so fire off event
m.lastYWasCenter = true
if m.onYCenter then
node.task.post(node.task.LOW_PRIORITY, function()
m.onYCenter(m.centerY)
end)
end
end
else
-- y is not on center, so fire event
m.lastYWasCenter = false
isXYUpdated = true
if m.onY then
node.task.post(node.task.LOW_PRIORITY, function()
m.onY(valY)
end)
end
end
if isXYUpdated then
if m.onXY then
node.task.post(node.task.LOW_PRIORITY, function()
m.onXY(valX, valY)
end)
end
end
-- print("X: " .. valX .. ", Y: " .. valY)
end
function m.readSwitch()
local val = gpio.read(m.pinSwitch)
print("Switch: ", val)
if m.onButton then m.onButton(val) end
end
function m.startSwitchInterrupt()
gpio.trig(m.pinSwitch, gpio.INTR_UP_DOWN, m.onSwitchInterrupt)
print("Set interrupt for switch.")
end
m.lastSwitchVal = 0
function m.onSwitchInterrupt(pin, level)
if level == m.lastSwitchVal then return end
m.lastSwitchVal = level
print("Got switch interrupt. pin: ", pin, ", level: ", level)
end
-- m.init()
return m