I've always done it by keeping track of state.
In fact, if you create a wrapper around the joystick it becomes very straightforward and useful. Create a JoystickManager class and give it an update method a state table and a previousState table.
In update() copy state to previousState, then go through your joystick's buttons and set state values for each of them. You can also set a bunch of values for new button presses (if it's pressed in state and not pressed in previousState then it's a newPress!).
Call update once per tick and you now have a convenient wrapper around your joystick and you can easily check for single button presses without having to muck around with timers and all that.
You can also take it a step further and wrap your JoystickManager class inside an input manager class and have a single place to check for user input.
If you don't want to do all that, just copy my code:
https://github.com/GloryFish/RedditGame ... anager.lua
https://github.com/GloryFish/RedditGame ... /input.lua
The best part about all of that is you can define some simple actions like "jump", "fire", "back" and just check for those in your code, the input manager can handle mapping those actions to either keyboard or gamepad button presses.
Here's an example:
https://github.com/GloryFish/RedditGame ... e.lua#L208
On line 208 I'm checking to see if the player pushed the jump button (on the keyboard or joystick) for the first time (a newpress), and, if so, I trigger a jump. Then on line 217, I check to see if the button is being held down and alter the gravity based on that. Using the input manager allows me to make that distinction and keeps the gameplay code simpler.