diff --git a/machines/agb02/_configuration.nix b/machines/agb02/_configuration.nix index a6f9eca..630c830 100644 --- a/machines/agb02/_configuration.nix +++ b/machines/agb02/_configuration.nix @@ -3,6 +3,7 @@ { imports = [ "${modulesPath}/installer/sd-card/sd-image-aarch64.nix" + ./users.nix ]; sdImage.compressImage = false; services = { @@ -13,5 +14,10 @@ networking.hostName = "agb02"; networking.networkmanager.enable = true; + environment.systemPackages = [ + (pkgs.callPackage ./agb {}) + pkgs.libgpiod + ]; + system.stateVersion = "24.11"; } diff --git a/machines/agb02/agb/agb.cpp b/machines/agb02/agb/agb.cpp new file mode 100644 index 0000000..fa92675 --- /dev/null +++ b/machines/agb02/agb/agb.cpp @@ -0,0 +1,137 @@ +#include +#include +#include +#include + +using namespace std::literals::chrono_literals; + +constexpr std::chrono::microseconds debounce = 40ms; +constexpr std::chrono::microseconds poll_period = 50ms; + +constexpr std::pair joystick_movement(1.0, 1.0); + +const gpiod::line::offsets drive_down = { 21, 13, 6 }; + +const gpiod::line::offsets decoder = { 0, 0, 0, 0, 0, 0, 0, 0 }; // lsbf +const gpiod::line::offsets joystick = { 0, 0, 0, 0 }; // x+, y+, x-, y- +const gpiod::line::offset black_button = 0; +const gpiod::line::offset white_button = 0; + +const gpiod::line_settings input_settings = + gpiod::line_settings() + .set_direction(gpiod::line::direction::INPUT) + .set_bias(gpiod::line::bias::PULL_UP) + .set_active_low(true) + .set_debounce_period(debounce); + +// thanks to https://en.wikipedia.org/wiki/Gray_code#Converting_to_and_from_Gray_code +inline uint8_t gray_to_binary(uint8_t num) +{ + num ^= num >> 4; + num ^= num >> 2; + num ^= num >> 1; + return num; +} + +uint8_t read_decoder_realpos(gpiod::line_request& line_reader){ + static gpiod::line::values decoder_read(8); + line_reader.get_values(decoder, decoder_read); + uint8_t graycode = 0; + for(uint8_t i = 0; i < 8; ++i) graycode |= uint8_t(decoder_read[i]) << i; + return gray_to_binary(graycode); +}; + +inline void clamp_decoder(uint8_t& decoder, int move){ + decoder = uint8_t(std::clamp(decoder + move, 0, 255)); +} + +int main(const int argc, char const* const* const argv) { + if(argc < 2) + return 1; + + /// init gpio chip /// + + gpiod::chip chip(argv[1]); + gpiod::line_request line_reader = + chip.prepare_request() + .set_consumer("AGB") + .add_line_settings(drive_down, + gpiod::line_settings() + .set_direction(gpiod::line::direction::OUTPUT) + .set_drive(gpiod::line::drive::OPEN_DRAIN) + .set_output_value(gpiod::line::value::INACTIVE) + ) + .add_line_settings({ black_button, white_button }, input_settings) + .add_line_settings(joystick, input_settings) + .add_line_settings(decoder, input_settings) + .do_request(); + + // let the settings apply + std::this_thread::sleep_for(poll_period); + + /// init server communication /// + + cURLpp::initialize(); + + /// internal state /// + + gpiod::line::values joystick_read(4); + std::pair spot_pos(0.0, 0.0); //TODO: init from server + + uint8_t decoder_pos = 0; //TODO: init from server + uint8_t decoder_realpos = read_decoder_realpos(line_reader); + + bool white_pressed = false; + bool black_pressed = false; + + + + + for(;;){ + std::this_thread::sleep_for(poll_period); + + /// joystick /// + line_reader.get_values(joystick, joystick_read); + + if(bool(joystick_read[0])) spot_pos.first += joystick_movement.first; + if(bool(joystick_read[1])) spot_pos.second += joystick_movement.second; + if(bool(joystick_read[2])) spot_pos.first -= joystick_movement.first; + if(bool(joystick_read[3])) spot_pos.second -= joystick_movement.second; + + if (bool(joystick_read[0]) || bool(joystick_read[1]) + || bool(joystick_read[2]) || bool(joystick_read[3])) + ; //TODO: send to server + + /// Buttons /// + bool pressed = bool(line_reader.get_value(black_button)); + if(pressed ^ black_pressed) + ; //TODO: send to server + black_pressed = pressed; + + pressed = bool(line_reader.get_value(white_button)); + if(pressed ^ white_pressed) + ; //TODO: send to server + white_pressed = pressed; + + /// decoder /// + uint8_t new_realpos = read_decoder_realpos(line_reader); + uint8_t seen_travel = std::abs(int(new_realpos) - int(decoder_realpos)); + + //TODO: check CW and CCW + + // CW + if(seen_travel < 50 && new_realpos < decoder_realpos) + clamp_decoder(decoder_pos, seen_travel); + if(seen_travel >= 50 && new_realpos > decoder_realpos) + clamp_decoder(decoder_pos, 256 - seen_travel); + + // CCW + if(seen_travel < 50 && new_realpos > decoder_realpos) + clamp_decoder(decoder_pos, -seen_travel); + if(seen_travel >= 50 && new_realpos < decoder_realpos) + clamp_decoder(decoder_pos, seen_travel - 256); + + if(seen_travel) + ; //TODO: send to server + } +} diff --git a/machines/agb02/agb/default.nix b/machines/agb02/agb/default.nix new file mode 100644 index 0000000..5b83cb3 --- /dev/null +++ b/machines/agb02/agb/default.nix @@ -0,0 +1,17 @@ +{ stdenv, libgpiod, curlpp, curl }: +stdenv.mkDerivation rec { + pname = "agb"; + version = "oct-24"; + src = ./.; + + buildPhase = '' + g++ --std=c++23 agb.cpp -o agb \ + -L${libgpiod}/lib -lgpiodcxx -I${libgpiod}/include \ + -L${curl.out}/lib -lcurl -I${curl.dev}/include \ + -L${curlpp}/lib -lcurlpp -I${curlpp}/include + ''; + installPhase = '' + mkdir -p $out/bin + cp agb $out/bin + ''; +}