2025-05-29, 09:30 PM
I've updated to the latest commit (08a6aa2b33) from the PR and I'm happy to report that everything still works, even the bottom-right button. This is my definition for that button:
I'm happy to no longer need the eval_on_ui_thread call!
I have no idea why it wouldn't work for you, though. If you're using similar code, and you've already replaced the faceplate, and Emily has the same issue... it seems that either I'm doing something "wrong" (that in fact makes it work) or you're both very unlucky. I'm probably doing something different, given those odds. All I'm doing is a build and flash based on that latest commit, though. I'll put my full input.lua here in case there's some other difference:
Code:
[2] = {
click = function()
-- Go to now-playing screen
require("playing"):push_if_not_shown()
end,
},
I'm happy to no longer need the eval_on_ui_thread call!
I have no idea why it wouldn't work for you, though. If you're using similar code, and you've already replaced the faceplate, and Emily has the same issue... it seems that either I'm doing something "wrong" (that in fact makes it work) or you're both very unlucky. I'm probably doing something different, given those odds. All I'm doing is a build and flash based on that latest commit, though. I'll put my full input.lua here in case there's some other difference:
Code:
local i2c = require('i2c')
local gpio = require('gpio')
local input_device = require('input_device')
local queue = require('queue')
local playback = require('playback')
local time = require('time')
local backstack = require("backstack")
--[[
Up to 16 buttons are supported with this driver.
If you assign actions to buttons you don't have, they will appear to always be pressed, so don't do that.
Tangarine button layout:
6 7 0
5 8 1
4 3 2
Actions that can be assigned per button:
[0] = {
click = function()
-- button was pressed and released once
end,
doubleclick = function()
-- button was pressed and released twice
end
press = function()
-- button was just pressed, not released yet
end,
longpress = function()
-- button was held
end,
repeatpress = function()
-- button is still being held after longpress
end,
},
You can perform arbitrary actions in these functions, or return data to be wrapped into an lvgl input event.
If returning data, it should be a table containing zero or more of the keys:
{
encoder_diff = (integer),
encoder_button_pressed = (boolean),
}
]]
local button_map = {
-- Top-right button
[0] = {
press = function()
if (not playback.track:get()) then
-- Restart the last played track
queue.position:set(queue.position:get())
end
playback.playing:set(not playback.playing:get())
end,
},
-- Right button
[1] = {
click = function()
queue:next()
end,
},
-- Bottom-right button
[2] = {
click = function()
-- Go to now-playing screen
require("playing"):push_if_not_shown()
end,
},
-- Bottom button
[3] = {
-- "doing literally anything to these buttons results in movement" feels more responsive to me
press = function()
return { encoder_diff = 1 }
end,
-- Hold down for fast scroll
repeatpress = function()
return { encoder_diff = 10 }
end,
doubleclick = function()
return { encoder_diff = 1 }
end,
},
-- Bottom-left button
[4] = {
longpress = function()
queue.clear()
end,
},
-- Left button
[5] = {
click = function()
if playback.position:get() > 3 then
playback.position:set(0)
else
queue.previous()
end
end,
},
-- Top-left button
[6] = {
longpress = function()
backstack.reset(require("main_menu"):new())
end,
click = function()
backstack.pop()
end
},
-- Top button
[7] = {
press = function()
return { encoder_diff = -1 }
end,
repeatpress = function()
return { encoder_diff = -10 }
end,
doubleclick = function()
return { encoder_diff = -1 }
end,
},
-- Center button
[8] = {
click = function()
return { encoder_button_pressed = true }
end,
},
}
local triggers = {}
local function buttons_setup()
for reg, value in pairs({
[6] = 0xFF, -- direction low: all pins inputs
[7] = 0xFF, -- direction high
[4] = 0xFF, -- invert low: all pins active low
[5] = 0xFF, -- invert high
}) do
i2c.execute(
i2c.start(),
i2c.write_addr(0x27, 'write'),
i2c.write_ack(reg),
i2c.write_ack(value),
i2c.stop()
)
end
for i = 0, 15 do
triggers[i] = input_device.make_trigger()
end
end
local button_states = {}
local locked = false
local function buttons_read()
if gpio.get_faceplate_interrupt_level() == 0 then
local input_low, input_high = i2c.execute(
i2c.start(),
i2c.write_addr(0x27, 'write'),
i2c.write_ack(0), -- input low
i2c.start(),
i2c.write_addr(0x27, 'read'),
i2c.read(),
i2c.read('nack'),
i2c.stop()
)
for i = 0, 7 do
button_states[i] = (input_low & (1 << i)) ~= 0
button_states[i + 8] = (input_high & (1 << i)) ~= 0
end
end
local merged_event = {
encoder_diff = 0,
encoder_button_pressed = false,
}
for i = 0, 15 do
local state = triggers[i]:update(button_states[i])
if not locked and button_map[i] and button_map[i][state] then
local ev = button_map[i][state]()
if type(ev) == 'table' then
if type(ev.encoder_diff) == 'number' then
merged_event.encoder_diff = merged_event.encoder_diff + ev.encoder_diff
end
if type(ev.encoder_button_pressed) == 'boolean' then
merged_event.encoder_button_pressed = (merged_event.encoder_button_pressed or ev.encoder_button_pressed)
end
end
end
end
return merged_event
end
buttons_setup()
input_device.register_read_func(buttons_read)
input_device.register_lock_func(function()
locked = true
end)
input_device.register_unlock_func(function()
locked = false
end)