diff --git a/.claude/skills/info/SKILL.md b/.claude/skills/info/SKILL.md new file mode 100644 index 0000000..d3d829a --- /dev/null +++ b/.claude/skills/info/SKILL.md @@ -0,0 +1,7 @@ +--- +name: info +description: Query device info from connected Picos. Use when the user says "info", "check devices", or wants to verify devices are responding. +user-invocable: true +--- + +Run `go run ./cmd/picomap/ info` from the project root. This queries all connected devices and prints board ID, MAC, IP, and firmware name. diff --git a/.claude/skills/test/SKILL.md b/.claude/skills/test/SKILL.md new file mode 100644 index 0000000..1cf9b29 --- /dev/null +++ b/.claude/skills/test/SKILL.md @@ -0,0 +1,10 @@ +--- +name: test +description: Run device tests on connected Picos. Use when the user says "test", "run tests", or wants to verify firmware behavior. +user-invocable: true +--- + +Run `go run ./cmd/picomap/ test all` from the project root. This runs all registered tests on the test device. + +To list available tests: `go run ./cmd/picomap/ test list` +To run a specific test: `go run ./cmd/picomap/ test run ` diff --git a/firmware/include/dispatch.h b/firmware/include/dispatch.h index 2ff8f4f..dd5fc67 100644 --- a/firmware/include/dispatch.h +++ b/firmware/include/dispatch.h @@ -6,17 +6,17 @@ #include #include "wire.h" #include "timer_queue.h" +#include "net.h" struct responder { uint32_t message_id; - std::function)> send; + send_fn send; template void respond(const T& msg) const { - uint8_t buf[1024]; - span_writer out(buf, sizeof(buf)); - auto r = encode_response_into(out, message_id, msg); - if (r) send({buf, *r}); + send([&](span_writer& out) { + return encode_response_into(out, message_id, msg); + }); } }; diff --git a/firmware/include/net.h b/firmware/include/net.h index 2716941..a4bbe5d 100644 --- a/firmware/include/net.h +++ b/firmware/include/net.h @@ -13,8 +13,11 @@ struct net_state { ipv4::ip4_addr ip; }; +using encode_fn = std::function(span_writer&)>; +using send_fn = std::function; + using net_handler = std::function payload, - std::function)> send)>; + send_fn send)>; using net_frame_callback = std::function frame)>; diff --git a/firmware/lib/dispatch.cpp b/firmware/lib/dispatch.cpp index 92386d1..26798bd 100644 --- a/firmware/lib/dispatch.cpp +++ b/firmware/lib/dispatch.cpp @@ -45,7 +45,7 @@ bool dispatch_cancel_timer(timer_handle h) { static static_vector usb_rx_buf; static std::array tx_buf; - auto dispatch_msg = [&](const DecodedMessage& msg, std::function)> send) { + auto dispatch_msg = [&](const DecodedMessage& msg, send_fn send) { auto it = handler_map.find(msg.type_id); if (it == handler_map.end()) { dlogf("dispatch: unknown type_id %d", msg.type_id); @@ -56,7 +56,7 @@ bool dispatch_cancel_timer(timer_handle h) { }; net_set_handler([&](std::span payload, - std::function)> send) { + send_fn send) { auto msg = try_decode(payload.data(), payload.size()); if (!msg) return; dispatch_msg(*msg, std::move(send)); @@ -82,7 +82,12 @@ bool dispatch_cancel_timer(timer_handle h) { continue; } - dispatch_msg(*msg, [&](std::span data) { + dispatch_msg(*msg, [&](encode_fn encode) { + uint8_t buf[4096]; + span_writer out(buf, sizeof(buf)); + auto r = encode(out); + if (!r) return; + std::span data{buf, *r}; if (data.size() <= usb.tx.free()) { if (!usb.send(data)) dlogf("usb send failed: %zu bytes, %u free", data.size(), usb.tx.free()); diff --git a/firmware/lib/net.cpp b/firmware/lib/net.cpp index 8c645c4..fb38e98 100644 --- a/firmware/lib/net.cpp +++ b/firmware/lib/net.cpp @@ -52,11 +52,14 @@ static void handle_udp(std::span frame, span_writer& tx) { uint16_t dst_port = uhdr->src_port; msg_handler(pb.remaining().subspan(0, payload_len), - [dst_mac, dst_ip, dst_port](std::span resp_data) { + [dst_mac, dst_ip, dst_port](encode_fn encode) { prepend_buffer<4096> buf; - buf.append_copy(resp_data); + span_writer out(buf.payload_ptr(), 2048); + auto r = encode(out); + if (!r) return; + buf.append(*r); udp::prepend(buf, dst_mac, state.mac, state.ip, dst_ip, - PICOMAP_PORT, dst_port, resp_data.size()); + PICOMAP_PORT, dst_port, *r); net_send_raw(buf.span()); }); }