#include #include #include #include #include #include #include using namespace std::literals::chrono_literals; constexpr std::chrono::microseconds debounce = 40ms; constexpr std::chrono::microseconds poll_period = 5ms; constexpr std::chrono::microseconds server_ratelimit = 50ms; constexpr std::chrono::microseconds retry_timeout = 500ms; constexpr std::pair joystick_movement(1.0, 1.0); const gpiod::line::offsets drive_down = { 21, 13, 6 }; const gpiod::line::offsets decoder = { 2, 3, 4, 14, 15, 18, 23, 24 }; // lsbf const gpiod::line::offsets joystick = { 0, 0, 0, 0 }; // x+, y+, x-, y- const gpiod::line::offset black_button = 16; const gpiod::line::offset white_button = 20; 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(false) .set_debounce_period(debounce); constexpr std::array decoder_table = #include "decoder_table.inl" 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 decoder_table[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) { std::cerr << "usage: agb gpiodevice" << std::endl; 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, gpiod::line_settings(input_settings) .set_active_low(true)) .add_line_settings(decoder, gpiod::line_settings(input_settings) .set_debounce_period(0ms)) .do_request(); // let the settings apply std::this_thread::sleep_for(poll_period); /// init server communication /// int socket_file_desc = socket(AF_INET, SOCK_STREAM, 0); { sockaddr_in socket_addr = { .sin_family = AF_INET, .sin_port = htons(1235), .sin_addr = { .s_addr = inet_addr("10.10.10.1") } }; int con_ret = connect(socket_file_desc, reinterpret_cast(&socket_addr), sizeof(socket_addr)); if(con_ret < 0) { std::cerr << "Failed to open tcp socket." << std::endl; return 1; } } /// internal state and buffers /// 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; bool has_changed = true; std::chrono::time_point last_send = std::chrono::system_clock::now(); std::string postData; 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])) has_changed = true; /// Buttons /// bool pressed = bool(line_reader.get_value(black_button)); if(pressed ^ black_pressed) has_changed = true; black_pressed = pressed; pressed = bool(line_reader.get_value(white_button)); if(pressed ^ white_pressed) has_changed = true; 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)); // 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 - 128); // 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, 128 - seen_travel); decoder_realpos = new_realpos; if(seen_travel) has_changed = true; /// server notification std::chrono::time_point now = std::chrono::system_clock::now(); if(has_changed && (now - last_send > server_ratelimit)){ postData.clear(); std::format_to(std::back_inserter(postData), "{{" "\"pan\": {}," "\"tilt\": {}," "\"focus\": {}," "\"whiteButton\": {}," "\"blackButton\": {}" "}}\n", spot_pos.first, spot_pos.second, int(decoder_pos), white_pressed, black_pressed ); int wrote = write(socket_file_desc, postData.data(), postData.size()); if(wrote < postData.size()){ std::cerr << "Failed to send data, retrying in " << retry_timeout << "." << std::endl; last_send = now + retry_timeout - server_ratelimit; } else { has_changed = false; last_send = now; } } } }