|
2 | 2 | #include <sdkconfig.h> |
3 | 3 |
|
4 | 4 | #include "i2c.hpp" |
| 5 | +#include "logger.hpp" |
5 | 6 | #include "pi4ioe5v.hpp" |
6 | 7 | #include "task.hpp" |
7 | 8 |
|
8 | 9 | using namespace std::chrono_literals; |
9 | 10 |
|
10 | 11 | extern "C" void app_main(void) { |
11 | | - fmt::print("Starting PI4IOE5V example...\n"); |
| 12 | + |
| 13 | + espp::Logger logger({.tag = "PI4IOE5V example", .level = espp::Logger::Verbosity::INFO}); |
| 14 | + |
| 15 | + logger.info("Starting PI4IOE5V example..."); |
12 | 16 |
|
13 | 17 | //! [pi4ioe5v_example] |
| 18 | + // make the I2C that we'll use to communicate |
14 | 19 | espp::I2c i2c({ |
15 | 20 | .port = I2C_NUM_1, |
16 | 21 | .sda_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SDA_GPIO, |
17 | 22 | .scl_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SCL_GPIO, |
| 23 | + .sda_pullup_en = GPIO_PULLUP_ENABLE, |
| 24 | + .scl_pullup_en = GPIO_PULLUP_ENABLE, |
18 | 25 | }); |
19 | 26 |
|
20 | | - // Configure PI4IOE5V: single 8-bit port as outputs |
| 27 | + // Configure PI4IOE5V: 8-bit port with mixed inputs/outputs |
21 | 28 | espp::Pi4ioe5v exp({ |
22 | 29 | .device_address = espp::Pi4ioe5v::DEFAULT_ADDRESS, |
23 | | - .direction_mask = 0x00, // all outputs |
24 | | - .output_state = 0xFF, // initial state |
25 | | - .probe = std::bind(&espp::I2c::probe_device, &i2c, std::placeholders::_1), |
26 | | - .write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2, |
27 | | - std::placeholders::_3), |
28 | | - .read_register = |
29 | | - std::bind(&espp::I2c::read_at_register, &i2c, std::placeholders::_1, |
30 | | - std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), |
31 | | - .write_then_read = |
32 | | - std::bind(&espp::I2c::write_read, &i2c, std::placeholders::_1, std::placeholders::_2, |
33 | | - std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), |
34 | | - .log_level = espp::Logger::Verbosity::WARN, |
| 30 | + .direction_mask = 0xF0, // upper 4 bits as outputs, lower 4 bits as inputs |
| 31 | + .interrupt_mask = 0xFF, // all interrupts disabled for now |
| 32 | + .initial_output = 0xA0, // initial pattern for outputs |
| 33 | + .write = std::bind_front(&espp::I2c::write, &i2c), |
| 34 | + .write_then_read = std::bind_front(&espp::I2c::write_read, &i2c), |
| 35 | + .auto_init = false, |
| 36 | + .log_level = espp::Logger::Verbosity::INFO, |
35 | 37 | }); |
36 | 38 |
|
37 | 39 | std::error_code ec; |
38 | | - exp.initialize(ec); |
39 | | - if (ec) { |
40 | | - fmt::print("PI4IOE5V init failed: {}\n", ec.message()); |
| 40 | + // Initialize separately from the constructor since we set auto_init to false |
| 41 | + if (!exp.initialize(ec)) { |
| 42 | + logger.error("PI4IOE5V initialization failed: {}", ec.message()); |
41 | 43 | return; |
42 | 44 | } |
43 | 45 |
|
| 46 | + // Configure pull resistors for input pins (lower 4 bits) |
| 47 | + exp.set_pull_resistor_for_pin(0xF0, espp::Pi4ioe5v::PullResistor::PULL_UP, ec); |
| 48 | + if (ec) { |
| 49 | + logger.error("Failed to configure pull resistors: {}", ec.message()); |
| 50 | + } |
| 51 | + |
| 52 | + // Create task to periodically toggle outputs and read inputs |
44 | 53 | auto task_fn = [&](std::mutex &m, std::condition_variable &cv) { |
45 | | - static uint8_t value = 0xAA; |
46 | | - value ^= 0xFF; // toggle pattern |
47 | | - exp.write_outputs(value, ec); |
48 | | - if (!ec) { |
49 | | - uint8_t inputs = exp.read_inputs(ec); |
50 | | - if (!ec) { |
51 | | - fmt::print("OUT=0x{:02X} IN=0x{:02X}\n", value, inputs); |
52 | | - } |
| 54 | + static auto start = std::chrono::high_resolution_clock::now(); |
| 55 | + static uint8_t output_pattern = 0x50; |
| 56 | + |
| 57 | + auto now = std::chrono::high_resolution_clock::now(); |
| 58 | + auto seconds = std::chrono::duration<float>(now - start).count(); |
| 59 | + |
| 60 | + // Toggle output pattern on upper 4 bits |
| 61 | + output_pattern ^= 0xF0; |
| 62 | + exp.output(output_pattern, ec); |
| 63 | + if (ec) { |
| 64 | + logger.error("Failed to set outputs: {}", ec.message()); |
| 65 | + return true; // stop the task |
| 66 | + } |
| 67 | + |
| 68 | + // Read all pins |
| 69 | + auto pins = exp.get_pins(ec); |
| 70 | + if (ec) { |
| 71 | + logger.error("Failed to read pins: {}", ec.message()); |
| 72 | + return true; // stop the task |
53 | 73 | } |
| 74 | + |
| 75 | + // Read current output state |
| 76 | + auto outputs = exp.get_output(ec); |
| 77 | + if (ec) { |
| 78 | + logger.error("Failed to read outputs: {}", ec.message()); |
| 79 | + return true; // stop the task |
| 80 | + } |
| 81 | + |
| 82 | + fmt::print("{:.3f}, inputs: {:#04x}, outputs: {:#04x}\n", seconds, pins, outputs); |
| 83 | + |
| 84 | + // NOTE: sleeping in this way allows the sleep to exit early when the |
| 85 | + // task is being stopped / destroyed |
54 | 86 | { |
55 | 87 | std::unique_lock<std::mutex> lk(m); |
56 | 88 | cv.wait_for(lk, 500ms); |
57 | 89 | } |
| 90 | + // don't want to stop the task |
58 | 91 | return false; |
59 | 92 | }; |
60 | 93 |
|
61 | | - espp::Task task({.callback = task_fn, |
62 | | - .task_config = {.name = "PI4IOE5V Task", .stack_size_bytes = 4 * 1024}, |
63 | | - .log_level = espp::Logger::Verbosity::WARN}); |
| 94 | + auto task = espp::Task({.callback = task_fn, |
| 95 | + .task_config = {.name = "PI4IOE5V Task", .stack_size_bytes = 4 * 1024}, |
| 96 | + .log_level = espp::Logger::Verbosity::WARN}); |
| 97 | + fmt::print("% time(s), inputs (lower 4 bits), outputs (upper 4 bits)\n"); |
64 | 98 | task.start(); |
65 | 99 | //! [pi4ioe5v_example] |
66 | 100 |
|
|
0 commit comments