-- 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