I have just found about PICO-8 recently and I must say this is a neat little platform to play around :D
So after scrolling the manual a few times, I realize that PICO-8 got the 128 bytes of GPIO, which begs to be used for all sort of external communication. However, I haven't found someone who has made a wrapper library for this, all of the examples I found are simply examples of how someone use the GPIO for their specific communication requirements.
I tinkered for a while in my free time and thought that I might as well just create my own. The requirements that I had in mind is simply:
- able use the GPIO without race condition between the Host and PICO-8
- able to send a message of arbitrary length, even more than 128 bytes
- is configurable, doesn't take all of the GPIO space
- enable user to create their own protocol on top for their own usage
A little bit of details:
So after (not so) much consideration, I choose to implement a packet based protocol design. The header is 8 bytes long (actually can shrink to 4 bytes, but eh) with these fields:
0 - packet type
1 - sequence number
2 - context id
3 - packet length
4 - packet crc
5, 6, 7 - empty, reserved
8 - [up to]127 - body
by default the packet length is set to be 96 bytes
In order to send a message, the flow would be like this:
- sender: send message start packet (includes the total message length in body)
- client: ack
- sender: send message part packet
- client: ack
- sender: send message end packet
- client: ack, call the user registered callback for this message type
The IPC class have several states:
- noop: nothing to do
- wait_msg_start_ack: sent start message, wait for ack
To make it race condition free, we simply define which packets sent by which system by a convention:
- if packet_type % 2 == 0 then pico sent it
- if packet_type % 2 == 1 then the external host sent it
currently, it has several built in packet_types:
- pico_noop = 0
- ext_noop = 1
- pico_ping = 2
- ext_ping = 3
- pico_pong = 4
- ext_pong = 5
- pico_ack = 6
- ext_ack = 7
- pico_reject = 8
- ext_reject = 9
- pico_start_msg = 10
- ext_start_msg = 11
- pico_end_msg = 12
- ext_end_msg = 13
If anyone is interested, you can check the code here
I have added a few simple examples on how you can use it. Simply copy paste the picoipc.p8 and the example script to your cart directory, create the input/output file, and run
pico8 -i input -o output -p picoweb
(the flag is necessary to indicate that you're using the library)
Please note that this code is still POC quality, the naming is not consistent yet, and a lot of features are missing such as:
- sequence number checks and wrap around
- randomized sequence number start
- packet buffer update (for progress bar purposes)
- context switching
- nice GUI for the adapter
- reduce token usage
Later on, as the repository name suggest, I will build a protocol on top of this IPC to support:
- HTTP calls
- Websocket connections
Let me know what you think, I'll continue working on this on the next weekend. Happy tinkering!
Good to see more experimentation in this area!
I am a little confused by your post; pico8 has access to real GPIO pins when running on raspberry pis, or uses that memory area as a shared byte array when embedded in HTML. You seem to say that your lib uses GPIO on all platforms, and the examples seem to use the -i/-o serial streams. Can you clarify?
@merwok ok so actually the current implementation has 2 steps, depending on the platform it is running on:
- read from serial file input and dump into GPIO (if -p picoweb is defined, which means it's a standalone client)
- do it's thing with the data from GPIO
- dump back from GPIO to serial file output, with the same condition above
so the implementation on standalone client will use serial file io, while on web it will use direct GPIO
That behavior is a simple shortcut for my testing purposes and ofc I totally forgot about raspberry PI lol, I was simply thinking desktop and web xD. Later on I will simply dump from GPIO / serial file into a byte table and use that instead of poking each GPIO directly. I believe this will solve the issue if it's run on raspberry pi / CHIP.
(hmm but probably it will be cool as well to see a real physical adapter lol)
interesting starting point but the token cost is really really high.
oop is interesting but not really the best option on pico - a C api would be much more efficient .
message size could also be encoded using the full 32bits, using 0x.0001 as increment.
I also don’t understand why crc code is part of the pico knowing that it
needs a ‘host’ in practical situations (read html page).
not sure building http or any other complex protocol really make sense either.
@freds72 haha I will consider this as a novelty rather than practicality. The whole IPC thing is actually designed for apps or games with not-so-real time requirements. As for the complex protocols, it's really "just because". Like it would be cool to see let's say, a web server running in PICO-8 xD
Ah yeah the OOP... I was carried away during implementation xD. Picked up Lua just for this and thought I want to experiment with metatables.
These spambots don't care what humans think, they're just happy to post links (benign or otherwise) on irrelevant websites just for the SEO juice.
on topic: Using the GPIO pins to allow Pico-8 access to networked multiplayer (via WebSocket, WebRTC, or two physical wires connecting two Pis together) has been my ideal use case for it since the day it was added. :3 The serialization API should help a lot.
[Please log in to post a comment]