diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..9768a02
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,3 @@
+# ignore all files except *.md
+*
+!*.md
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..222861c
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,4 @@
+{
+ "tabWidth": 2,
+ "useTabs": false
+}
diff --git a/README.md b/README.md
index 38627e0..06b7380 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,7 @@ A set of Game Boy Advance (GBA) C++ libraries to interact with the Serial Port.
- [⏱️](#%EF%B8%8F-LinkUART) [LinkUART.hpp](lib/LinkUART.hpp): Easily connect to **any PC** using a USB to UART cable!
- [🟪](#-LinkCube) [LinkCube.hpp](lib/LinkCube.hpp): Exchange data with a _Wii_ or a _GameCube_ using the classic **Joybus** protocol!
- [📱](#-LinkMobile) [LinkMobile.hpp](lib/LinkMobile.hpp): Connect to **the internet** using the _Mobile Adapter GB_, brought back to life thanks to the [REON](https://github.com/REONTeam) project!
+- [📺](#-LinkIR) [LinkIR.hpp](lib/LinkIR.hpp): Turn down the volume of your **neighbor's TV** using the _Infrared Adapter_!
- [🖱️](#%EF%B8%8F-LinkPS2Mouse) [LinkPS2Mouse.hpp](lib/LinkPS2Mouse.hpp): Connect a **PS/2 mouse** to the GBA for extended controls!
- [⌨️](#%EF%B8%8F-LinkPS2Keyboard) [LinkPS2Keyboard.hpp](lib/LinkPS2Keyboard.hpp): Connect a **PS/2 keyboard** to the GBA for extended controls!
@@ -91,12 +92,12 @@ The library uses message queues to send/receive data and transmits when it's pos
`new LinkCable(...)` accepts these **optional** parameters:
-| Name | Type | Default | Description |
-| ------------- | -------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `baudRate` | **BaudRate** | `BaudRate::BAUD_RATE_1` | Sets a specific baud rate. |
-| `timeout` | **u32** | `3` | Maximum number of _frames_ without receiving data from other player before marking them as disconnected or resetting the connection. |
-| `interval` | **u16** | `50` | Number of _1024-cycle ticks_ (61.04μs) between transfers _(50 = 3.052ms)_. It's the interval of Timer #`sendTimerId`.
Lower values will transfer faster but also consume more CPU. You can use `Link::perFrame(...)` to convert from _transfers per frame_ to _interval values_. |
-| `sendTimerId` | **u8** _(0~3)_ | `3` | GBA Timer to use for sending. |
+| Name | Type | Default | Description |
+| ------------- | -------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `baudRate` | **BaudRate** | `BaudRate::BAUD_RATE_1` | Sets a specific baud rate. |
+| `timeout` | **u32** | `3` | Maximum number of _frames_ without receiving data from other player before marking them as disconnected or resetting the connection. |
+| `interval` | **u16** | `50` | Number of _1024-cycle ticks_ (61.04μs) between transfers _(50 = 3.052ms)_. It's the interval of Timer #`sendTimerId`.
Lower values will transfer faster but also consume more CPU. You can use `Link::perFrame(...)` to convert from _transfers per frame_ to _interval values_. |
+| `sendTimerId` | **u8** _(0~3)_ | `3` | GBA Timer to use for sending. |
You can update these values at any time without creating a new instance:
@@ -106,25 +107,25 @@ You can update these values at any time without creating a new instance:
## Methods
-| Name | Return type | Description |
-| --------------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `isActive()` | **bool** | Returns whether the library is active or not. |
-| `activate()` | - | Activates the library. |
-| `deactivate()` | - | Deactivates the library. |
-| `isConnected()` | **bool** | Returns `true` if there are at least 2 connected players. |
-| `playerCount()` | **u8** _(1~4)_ | Returns the number of connected players. |
-| `currentPlayerId()` | **u8** _(0~3)_ | Returns the current player ID. |
-| `sync()` | - | Collects available messages from interrupts for later processing with `read(...)`. Call this method whenever you need to fetch new data, and always process all the messages before calling it again. |
-| `waitFor(playerId)` | **bool** | Waits for data from player #`playerId`. Returns `true` on success, or `false` on disconnection. |
-| `waitFor(playerId, cancel)` | **bool** | Like `waitFor(playerId)` but accepts a `cancel()` function. The library will continuously invoke it, and abort the wait if it returns `true`. |
-| `canRead(playerId)` | **bool** | Returns `true` if there are pending messages from player #`playerId`.
Keep in mind that if this returns `false`, it will keep doing so until you _fetch new data_ with `sync()`. |
-| `read(playerId)` | **u16** | Dequeues and returns the next message from player #`playerId`. If there's no data from that player, a `0` will be returned. |
-| `peek(playerId)` | **u16** | Returns the next message from player #`playerId` without dequeuing it. If there's no data from that player, a `0` will be returned. |
-| `canSend()` | **bool** | Returns if a `send(...)` call would fail due to the queue being full. |
-| `send(data)` | **bool** | Sends `data` to all connected players. If `data` is invalid or the send queue is full, a `false` will be returned. |
-| `didQueueOverflow([clear])` | **bool** | Returns whether the internal queue lost messages at some point due to being full. This can happen if your queue size is too low, if you receive too much data without calling `sync(...)` enough times, or if you don't `read(...)` enough messages before the next `sync()` call.
After this call, the overflow flag is cleared if `clear` is `true` (default behavior). |
-| `resetTimeout()` | - | Resets other players' timeout count to `0`. Call this before reducing `config.timeout`. |
-| `resetTimer()` | - | Restarts the send timer without disconnecting. Call this if you changed `config.interval` |
+| Name | Return type | Description |
+| --------------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `isActive()` | **bool** | Returns whether the library is active or not. |
+| `activate()` | - | Activates the library. |
+| `deactivate()` | - | Deactivates the library. |
+| `isConnected()` | **bool** | Returns `true` if there are at least 2 connected players. |
+| `playerCount()` | **u8** _(1~4)_ | Returns the number of connected players. |
+| `currentPlayerId()` | **u8** _(0~3)_ | Returns the current player ID. |
+| `sync()` | - | Collects available messages from interrupts for later processing with `read(...)`. Call this method whenever you need to fetch new data, and always process all the messages before calling it again. |
+| `waitFor(playerId)` | **bool** | Waits for data from player #`playerId`. Returns `true` on success, or `false` on disconnection. |
+| `waitFor(playerId, cancel)` | **bool** | Like `waitFor(playerId)` but accepts a `cancel()` function. The library will continuously invoke it, and abort the wait if it returns `true`. |
+| `canRead(playerId)` | **bool** | Returns `true` if there are pending messages from player #`playerId`.
Keep in mind that if this returns `false`, it will keep doing so until you _fetch new data_ with `sync()`. |
+| `read(playerId)` | **u16** | Dequeues and returns the next message from player #`playerId`. If there's no data from that player, a `0` will be returned. |
+| `peek(playerId)` | **u16** | Returns the next message from player #`playerId` without dequeuing it. If there's no data from that player, a `0` will be returned. |
+| `canSend()` | **bool** | Returns whether a `send(...)` call would fail due to the queue being full or not. |
+| `send(data)` | **bool** | Sends `data` to all connected players. If `data` is invalid or the send queue is full, a `false` will be returned. |
+| `didQueueOverflow([clear])` | **bool** | Returns whether the internal queue lost messages at some point due to being full. This can happen if your queue size is too low, if you receive too much data without calling `sync(...)` enough times, or if you don't `read(...)` enough messages before the next `sync()` call.
After this call, the overflow flag is cleared if `clear` is `true` (default behavior). |
+| `resetTimeout()` | - | Resets other players' timeout count to `0`. Call this before reducing `config.timeout`. |
+| `resetTimer()` | - | Restarts the send timer without disconnecting. Call this if you changed `config.interval` |
⚠️ `0xFFFF` and `0x0` are reserved values, so don't send them!
@@ -151,9 +152,9 @@ This version is simpler and blocks the system thread until completion. It doesn'
### Methods
-| Name | Return type | Description |
-| --------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `sendRom(rom, romSize, cancel, [mode])` | **Result** | Sends the `rom` (must be 4-byte aligned). During the handshake process, the library will continuously invoke `cancel`, and abort the transfer if it returns `true`.
The `romSize` must be a number between `448` and `262144`, and a multiple of `16`.
The `mode` can be either `LinkCableMultiboot::TransferMode::MULTI_PLAY` for GBA cable (default value) or `LinkCableMultiboot::TransferMode::SPI` for GBC cable.
Once completed, the return value should be `LinkCableMultiboot::Result::SUCCESS`. |
+| Name | Return type | Description |
+| --------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `sendRom(rom, romSize, cancel, [mode])` | **Result** | Sends the `rom` (must be 4-byte aligned). During the handshake process, the library will continuously invoke `cancel`, and abort the transfer if it returns `true`.
The `romSize` must be a number between `448` and `262144`, and a multiple of `16`.
The `mode` can be either `LinkCableMultiboot::TransferMode::MULTI_PLAY` for GBA cable (default value) or `LinkCableMultiboot::TransferMode::SPI` for GBC cable.
Once completed, the return value should be `LinkCableMultiboot::Result::SUCCESS`. |
⚠️ stop DMA before sending the ROM! _(you might need to stop your audio player)_
@@ -169,32 +170,31 @@ This version is simpler and blocks the system thread until completion. It doesn'
This version (`LinkCableMultiboot::Async`) allows more advanced use cases like playing animations and/or audio during the transfers, displaying the number of connected players and send percentage, and marking the transfer as 'ready' to start. It requires adding the provided interrupt service routines. The class is polymorphic with `LinkWirelessMultiboot::Async`.
-
### Constructor
`new LinkCableMultiboot::Async(...)` accepts these **optional** parameters:
-| Name | Type | Default | Description |
-| ------------- | -------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `waitForReadySignal` | **bool** | `false` | Whether the code should wait for a `markReady()` call to start the actual transfer. |
-| `mode` | **TransferMode** | `TransferMode::MULTI_PLAY` | Either `LinkCableMultiboot::TransferMode::MULTI_PLAY` for GBA cable (default value) or `LinkCableMultiboot::TransferMode::SPI` for GBC cable. |
+| Name | Type | Default | Description |
+| -------------------- | ---------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
+| `waitForReadySignal` | **bool** | `false` | Whether the code should wait for a `markReady()` call to start the actual transfer. |
+| `mode` | **TransferMode** | `TransferMode::MULTI_PLAY` | Either `LinkCableMultiboot::TransferMode::MULTI_PLAY` for GBA cable (default value) or `LinkCableMultiboot::TransferMode::SPI` for GBC cable. |
You can update these values at any time without creating a new instance by mutating the `config` property. Keep in mind that the changes won't be applied after the next `sendRom(...)` call.
### Methods
-| Name | Return type | Description |
-| --------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `sendRom(rom, romSize)` | **bool** | Sends the `rom` (must be 4-byte aligned).
The `romSize` must be a number between `448` and `262144`, and a multiple of `16`.
Once completed, `getState()` should return `LinkCableMultiboot::Async::State::STOPPED` and `getResult()` should return `LinkCableMultiboot::Async::GeneralResult::SUCCESS`.
Returns `false` if there's a pending transfer or the data is invalid. |
-| `reset()` | **bool** | Deactivates the library, canceling the in-progress transfer, if any. |
-| `isSending()` | **bool** | Returns whether there's an active transfer or not. |
-| `getState()` | **State** | Returns the current state. |
-| `getResult([clear])` | **GeneralResult** | Returns the result of the last operation.
After this call, the result is cleared if `clear` is `true` (default behavior). |
-| `getDetailedResult([clear])` | **Result** | Returns the detailed result of the last operation.
After this call, the result is cleared if `clear` is `true` (default behavior). |
-| `playerCount()` | **u8** _(1~4)_ | Returns the number of connected players. |
-| `getPercentage()` | **u32** _(0~100)_ | Returns the completion percentage. |
-| `isReady()` | **bool** | Returns whether the ready mark is active or not.
This is only useful when using the `waitForReadySignal` parameter. |
-| `markReady()` | **bool** | Marks the transfer as ready.
This is only useful when using the `waitForReadySignal` parameter. |
+| Name | Return type | Description |
+| ---------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `sendRom(rom, romSize)` | **bool** | Sends the `rom` (must be 4-byte aligned).
The `romSize` must be a number between `448` and `262144`, and a multiple of `16`.
Once completed, `getState()` should return `LinkCableMultiboot::Async::State::STOPPED` and `getResult()` should return `LinkCableMultiboot::Async::GeneralResult::SUCCESS`.
Returns `false` if there's a pending transfer or the data is invalid. |
+| `reset()` | **bool** | Deactivates the library, canceling the in-progress transfer, if any. |
+| `isSending()` | **bool** | Returns whether there's an active transfer or not. |
+| `getState()` | **State** | Returns the current state. |
+| `getResult([clear])` | **GeneralResult** | Returns the result of the last operation.
After this call, the result is cleared if `clear` is `true` (default behavior). |
+| `getDetailedResult([clear])` | **Result** | Returns the detailed result of the last operation.
After this call, the result is cleared if `clear` is `true` (default behavior). |
+| `playerCount()` | **u8** _(1~4)_ | Returns the number of connected players. |
+| `getPercentage()` | **u32** _(0~100)_ | Returns the completion percentage. |
+| `isReady()` | **bool** | Returns whether the ready mark is active or not.
This is only useful when using the `waitForReadySignal` parameter. |
+| `markReady()` | **bool** | Marks the transfer as ready.
This is only useful when using the `waitForReadySignal` parameter. |
⚠️ never call `reset()` inside an interrupt handler!
@@ -214,19 +214,19 @@ You can update these values at any time without creating a new instance by mutat
## Methods
-| Name | Return type | Description |
-| ---------------------------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `isActive()` | **bool** | Returns whether the library is active or not. |
-| `activate(baudRate = BAUD_RATE_1)` | - | Activates the library in a specific `baudRate` (`LinkRawCable::BaudRate`). |
-| `deactivate()` | - | Deactivates the library. |
-| `transfer(data)` | **Response** | Exchanges `data` with the connected consoles. Returns the received data, including the assigned player ID. |
-| `transfer(data, cancel)` | **Response** | Like `transfer(data)` but accepts a `cancel()` function. The library will continuously invoke it, and abort the transfer if it returns `true`. |
-| `transferAsync(data)` | - | Schedules a `data` transfer and returns. After this, call `getAsyncState()` and `getAsyncData()`.
Note that until you retrieve the async data, normal `transfer(...)`s won't do anything! |
-| `getAsyncState()` | **AsyncState** | Returns the state of the last async transfer (one of `LinkRawCable::AsyncState::IDLE`, `LinkRawCable::AsyncState::WAITING`, or `LinkRawCable::AsyncState::READY`). |
-| `getAsyncData()` | **Response** | If the async state is `READY`, returns the remote data and switches the state back to `IDLE`. If not, returns an empty response. |
-| `getBaudRate()` | **BaudRate** | Returns the current `baudRate`. |
-| `isMaster()` | **bool** | Returns whether the console is connected as master or not. Returns garbage when the cable is not properly connected. |
-| `isReady()` | **bool** | Returns whether all connected consoles have entered the multiplayer mode. Returns garbage when the cable is not properly connected. |
+| Name | Return type | Description |
+| ---------------------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `isActive()` | **bool** | Returns whether the library is active or not. |
+| `activate(baudRate = BAUD_RATE_1)` | - | Activates the library in a specific `baudRate` (`LinkRawCable::BaudRate`). |
+| `deactivate()` | - | Deactivates the library. |
+| `transfer(data)` | **Response** | Exchanges `data` with the connected consoles. Returns the received data, including the assigned player ID. |
+| `transfer(data, cancel)` | **Response** | Like `transfer(data)` but accepts a `cancel()` function. The library will continuously invoke it, and abort the transfer if it returns `true`. |
+| `transferAsync(data)` | - | Schedules a `data` transfer and returns. After this, call `getAsyncState()` and `getAsyncData()`.
Note that until you retrieve the async data, normal `transfer(...)`s won't do anything! |
+| `getAsyncState()` | **AsyncState** | Returns the state of the last async transfer (one of `LinkRawCable::AsyncState::IDLE`, `LinkRawCable::AsyncState::WAITING`, or `LinkRawCable::AsyncState::READY`). |
+| `getAsyncData()` | **Response** | If the async state is `READY`, returns the remote data and switches the state back to `IDLE`. If not, returns an empty response. |
+| `getBaudRate()` | **BaudRate** | Returns the current `baudRate`. |
+| `isMaster()` | **bool** | Returns whether the console is connected as master or not. Returns garbage when the cable is not properly connected. |
+| `isReady()` | **bool** | Returns whether all connected consoles have entered the multiplayer mode. Returns garbage when the cable is not properly connected. |
⚠️ advanced usage only; if you're building a game, use `LinkCable`!
@@ -248,14 +248,14 @@ https://github.com/afska/gba-link-connection/assets/1631752/7eeafc49-2dfa-4902-a
`new LinkWireless(...)` accepts these **optional** parameters:
-| Name | Type | Default | Description |
-| ---------------- | -------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `forwarding` | **bool** | `true` | If `true`, the server forwards all messages to the clients. Otherwise, clients only see messages sent from the server (ignoring other peers). |
-| `retransmission` | **bool** | `true` | If `true`, the library handles retransmission for you, so there should be no packet loss. |
-| `maxPlayers` | **u8** _(2~5)_ | `5` | Maximum number of allowed players. |
-| `timeout` | **u32** | `10` | Maximum number of _frames_ without receiving data from other player before resetting the connection. |
+| Name | Type | Default | Description |
+| ---------------- | -------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `forwarding` | **bool** | `true` | If `true`, the server forwards all messages to the clients. Otherwise, clients only see messages sent from the server (ignoring other peers). |
+| `retransmission` | **bool** | `true` | If `true`, the library handles retransmission for you, so there should be no packet loss. |
+| `maxPlayers` | **u8** _(2~5)_ | `5` | Maximum number of allowed players. |
+| `timeout` | **u32** | `10` | Maximum number of _frames_ without receiving data from other player before resetting the connection. |
| `interval` | **u16** | `75` | Number of _1024-cycle ticks_ (61.04μs) between transfers _(75 = 4.578ms)_. It's the interval of Timer #`sendTimerId`. Lower values will transfer faster but also consume more CPU. You can use `Link::perFrame(...)` to convert from _transfers per frame_ to _interval values_. |
-| `sendTimerId` | **u8** _(0~3)_ | `3` | GBA Timer to use for sending. |
+| `sendTimerId` | **u8** _(0~3)_ | `3` | GBA Timer to use for sending. |
You can update these values at any time without creating a new instance:
@@ -273,33 +273,33 @@ You can update these values at any time without creating a new instance:
- `closeServer()`, to make it the room unavailable for new players.
- `getSignalLevel(...)`, to retrieve signal levels.
-| Name | Return type | Description |
-| ----------------------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `isActive()` | **bool** | Returns whether the library is active or not. |
-| `activate()` | **bool** | Activates the library. When an adapter is connected, it changes the state to `AUTHENTICATED`. It can also be used to disconnect or reset the adapter. |
-| `restoreExistingConnection()` | **bool** | Restores the state from an existing connection on the Wireless Adapter hardware.
This is useful, for example, after a fresh launch of a Multiboot game, to synchronize the library with the current state and avoid a reconnection.
Returns whether the restoration was successful. On success, the state should be either `SERVING` or `CONNECTED`.
This should be used as a replacement for `activate()`. |
-| `deactivate([turnOff])` | **bool** | Puts the adapter into a low consumption mode and then deactivates the library. It returns a boolean indicating whether the transition to low consumption mode was successful.
You can disable the transition and deactivate directly by setting `turnOff` to `true`. |
-| `serve([gameName], [userName], [gameId])` | **bool** | Starts broadcasting a server and changes the state to `SERVING`.
You can, optionally, provide a `gameName` (max `14` characters), a `userName` (max `8` characters), and a `gameId` _(0 ~ 0x7FFF)_ that games will be able to read. The strings must be null-terminated character arrays.
If the adapter is already serving, this method only updates the broadcast data. Updating broadcast data while serving can fail if the adapter is busy. In that case, this will return `false` and `getLastError()` will be `BUSY_TRY_AGAIN`. |
-| `closeServer()` | **bool** | Closes the server while keeping the session active, to prevent new users from joining the room. This action can fail if the adapter is busy. In that case, this will return `false` and `getLastError()` will be `BUSY_TRY_AGAIN`. |
-| `getSignalLevel(response)` | **bool** | Retrieves the signal level of each player (0-255), filling the `response` struct.
For hosts, the array will contain the signal level of each client in indexes 1-4. For clients, it will only include the index corresponding to the `currentPlayerId()`.
For clients, this action can fail if the adapter is busy. In that case, this will return `false` and `getLastError()` will be `BUSY_TRY_AGAIN`. For hosts, you already have this data, so it's free! |
-| `getServers(servers, serverCount, [onWait])` | **bool** | Fills the `servers` array with all the currently broadcasting servers. This action takes 1 second to complete, but you can optionally provide an `onWait()` function which will be invoked each time VBlank starts. |
-| `getServersAsyncStart()` | **bool** | Starts looking for broadcasting servers and changes the state to `SEARCHING`. After this, call `getServersAsyncEnd(...)` 1 second later. |
-| `getServersAsyncEnd(servers, serverCount)` | **bool** | Fills the `servers` array with all the currently broadcasting servers. Changes the state to `AUTHENTICATED` again. |
-| `connect(serverId)` | **bool** | Starts a connection with `serverId` and changes the state to `CONNECTING`. |
-| `keepConnecting()` | **bool** | When connecting, this needs to be called until the state is `CONNECTED`. It assigns a player ID.
Keep in mind that `isConnected()` and `playerCount()` won't be updated until the first message from the server arrives. |
-| `canSend()` | **bool** | Returns if a `send(...)` call would fail due to the queue being full. |
-| `send(data)` | **bool** | Enqueues `data` to be sent to other nodes. |
-| `receive(messages, receivedCount)` | **bool** | Fills the `messages` array with incoming messages. |
-| `getState()` | **State** | Returns the current state (one of `LinkWireless::State::NEEDS_RESET`, `LinkWireless::State::AUTHENTICATED`, `LinkWireless::State::SEARCHING`, `LinkWireless::State::SERVING`, `LinkWireless::State::CONNECTING`, or `LinkWireless::State::CONNECTED`). |
-| `isConnected()` | **bool** | Returns `true` if the player count is higher than `1`. |
-| `isSessionActive()` | **bool** | Returns `true` if the state is `SERVING` or `CONNECTED`. |
-| `isServerClosed()` | **bool** | Returns `true` if the server was closed with `closeServer()`. |
-| `playerCount()` | **u8** _(1~5)_ | Returns the number of connected players. |
-| `currentPlayerId()` | **u8** _(0~4)_ | Returns the current player ID. |
-| `didQueueOverflow([clear])` | **bool** | Returns whether the internal queue lost messages at some point due to being full. This can happen if your queue size is too low, if you receive too much data without calling `receive(...)` enough times, or if excessive `receive(...)` calls prevent the ISR from copying data.
After this call, the overflow flag is cleared if `clear` is `true` (default behavior). |
-| `resetTimeout()` | - | Resets other players' timeout count to `0`. Call this before reducing `config.timeout`. |
-| `resetTimer()` | - | Restarts the send timer without disconnecting. Call this if you changed `config.interval`. |
-| `getLastError([clear])` | **Error** | If one of the other methods returns `false`, you can inspect this to know the cause.
After this call, the last error is cleared if `clear` is `true` (default behavior). |
+| Name | Return type | Description |
+| -------------------------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `isActive()` | **bool** | Returns whether the library is active or not. |
+| `activate()` | **bool** | Activates the library. When an adapter is connected, it changes the state to `AUTHENTICATED`. It can also be used to disconnect or reset the adapter. |
+| `restoreExistingConnection()` | **bool** | Restores the state from an existing connection on the Wireless Adapter hardware.
This is useful, for example, after a fresh launch of a Multiboot game, to synchronize the library with the current state and avoid a reconnection.
Returns whether the restoration was successful. On success, the state should be either `SERVING` or `CONNECTED`.
This should be used as a replacement for `activate()`. |
+| `deactivate([turnOff])` | **bool** | Puts the adapter into a low consumption mode and then deactivates the library. It returns a boolean indicating whether the transition to low consumption mode was successful.
You can disable the transition and deactivate directly by setting `turnOff` to `true`. |
+| `serve([gameName], [userName], [gameId])` | **bool** | Starts broadcasting a server and changes the state to `SERVING`.
You can, optionally, provide a `gameName` (max `14` characters), a `userName` (max `8` characters), and a `gameId` _(0 ~ 0x7FFF)_ that games will be able to read. The strings must be null-terminated character arrays.
If the adapter is already serving, this method only updates the broadcast data. Updating broadcast data while serving can fail if the adapter is busy. In that case, this will return `false` and `getLastError()` will be `BUSY_TRY_AGAIN`. |
+| `closeServer()` | **bool** | Closes the server while keeping the session active, to prevent new users from joining the room. This action can fail if the adapter is busy. In that case, this will return `false` and `getLastError()` will be `BUSY_TRY_AGAIN`. |
+| `getSignalLevel(response)` | **bool** | Retrieves the signal level of each player (0-255), filling the `response` struct.
For hosts, the array will contain the signal level of each client in indexes 1-4. For clients, it will only include the index corresponding to the `currentPlayerId()`.
For clients, this action can fail if the adapter is busy. In that case, this will return `false` and `getLastError()` will be `BUSY_TRY_AGAIN`. For hosts, you already have this data, so it's free! |
+| `getServers(servers, serverCount, [onWait])` | **bool** | Fills the `servers` array with all the currently broadcasting servers. This action takes 1 second to complete, but you can optionally provide an `onWait()` function which will be invoked each time VBlank starts. |
+| `getServersAsyncStart()` | **bool** | Starts looking for broadcasting servers and changes the state to `SEARCHING`. After this, call `getServersAsyncEnd(...)` 1 second later. |
+| `getServersAsyncEnd(servers, serverCount)` | **bool** | Fills the `servers` array with all the currently broadcasting servers. Changes the state to `AUTHENTICATED` again. |
+| `connect(serverId)` | **bool** | Starts a connection with `serverId` and changes the state to `CONNECTING`. |
+| `keepConnecting()` | **bool** | When connecting, this needs to be called until the state is `CONNECTED`. It assigns a player ID.
Keep in mind that `isConnected()` and `playerCount()` won't be updated until the first message from the server arrives. |
+| `canSend()` | **bool** | Returns whether a `send(...)` call would fail due to the queue being full or not. |
+| `send(data)` | **bool** | Enqueues `data` to be sent to other nodes. |
+| `receive(messages, receivedCount)` | **bool** | Fills the `messages` array with incoming messages. |
+| `getState()` | **State** | Returns the current state (one of `LinkWireless::State::NEEDS_RESET`, `LinkWireless::State::AUTHENTICATED`, `LinkWireless::State::SEARCHING`, `LinkWireless::State::SERVING`, `LinkWireless::State::CONNECTING`, or `LinkWireless::State::CONNECTED`). |
+| `isConnected()` | **bool** | Returns `true` if the player count is higher than `1`. |
+| `isSessionActive()` | **bool** | Returns `true` if the state is `SERVING` or `CONNECTED`. |
+| `isServerClosed()` | **bool** | Returns `true` if the server was closed with `closeServer()`. |
+| `playerCount()` | **u8** _(1~5)_ | Returns the number of connected players. |
+| `currentPlayerId()` | **u8** _(0~4)_ | Returns the current player ID. |
+| `didQueueOverflow([clear])` | **bool** | Returns whether the internal queue lost messages at some point due to being full. This can happen if your queue size is too low, if you receive too much data without calling `receive(...)` enough times, or if excessive `receive(...)` calls prevent the ISR from copying data.
After this call, the overflow flag is cleared if `clear` is `true` (default behavior). |
+| `resetTimeout()` | - | Resets other players' timeout count to `0`. Call this before reducing `config.timeout`. |
+| `resetTimer()` | - | Restarts the send timer without disconnecting. Call this if you changed `config.interval`. |
+| `getLastError([clear])` | **Error** | If one of the other methods returns `false`, you can inspect this to know the cause.
After this call, the last error is cleared if `clear` is `true` (default behavior). |
## Compile-time constants
@@ -312,10 +312,10 @@ You can update these values at any time without creating a new instance:
- `LINK_WIRELESS_PUT_ISR_IN_IWRAM`: to put critical functions in IWRAM, which can significantly improve performance due to its faster access. This is disabled by default to conserve IWRAM space, which is limited, but it's enabled in demos to showcase its performance benefits.
- If you enable this, make sure that `lib/iwram_code/LinkWireless.cpp` gets compiled! For example, in a Makefile-based project, verify that the directory is in your `SRCDIRS` list.
- Depending on how much IWRAM you have available, you might want to tweak these knobs:
- * `LINK_WIRELESS_PUT_ISR_IN_IWRAM_SERIAL`: (default: `1`) Put the SERIAL ISR in IWRAM (recommended, since this handler runs ~20 times per frame)
- * `LINK_WIRELESS_PUT_ISR_IN_IWRAM_TIMER`: (default: `1`) Put the TIMER ISR in IWRAM (not that necessary)
- * `LINK_WIRELESS_PUT_ISR_IN_IWRAM_SERIAL_LEVEL`: (default: `"-Ofast"`) Optimization level for the SERIAL ISR
- * `LINK_WIRELESS_PUT_ISR_IN_IWRAM_TIMER_LEVEL`: (default: `"-Ofast"`) Optimization level for the TIMER ISR
+ - `LINK_WIRELESS_PUT_ISR_IN_IWRAM_SERIAL`: (default: `1`) Put the SERIAL ISR in IWRAM (recommended, since this handler runs ~20 times per frame)
+ - `LINK_WIRELESS_PUT_ISR_IN_IWRAM_TIMER`: (default: `1`) Put the TIMER ISR in IWRAM (not that necessary)
+ - `LINK_WIRELESS_PUT_ISR_IN_IWRAM_SERIAL_LEVEL`: (default: `"-Ofast"`) Optimization level for the SERIAL ISR
+ - `LINK_WIRELESS_PUT_ISR_IN_IWRAM_TIMER_LEVEL`: (default: `"-Ofast"`) Optimization level for the TIMER ISR
- `LINK_WIRELESS_ENABLE_NESTED_IRQ`: to allow `LINK_WIRELESS_ISR_*` functions to be interrupted. This can be useful, for example, if your audio engine requires calling a VBlank handler with precise timing.
# 💻 LinkWirelessMultiboot
@@ -334,10 +334,10 @@ This version is simpler and blocks the system thread until completion. It doesn'
### Methods
-| Name | Return type | Description |
-| -------------------------------------------------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `sendRom(rom, romSize, gameName, userName, gameId, players, listener, [keepConnectionAlive])` | **Result** | Sends the `rom`.
The `players` must be the number of consoles that will download the ROM. Once this number of players is reached, the code will start transmitting the ROM bytes.
During the process, the library will continuously invoke `listener` (passing a `LinkWirelessMultiboot::MultibootProgress` object as argument), and abort the transfer if it returns `true`.
The `romSize` must be a number between `448` and `262144`. It's recommended to use a ROM size that is a multiple of `16`, since this also ensures compatibility with Multiboot via Link Cable.
Once completed, the return value should be `LinkWirelessMultiboot::Result::SUCCESS`.
You can start the transfer before the player count is reached by running `*progress.ready = true;` in the`cancel` callback.
If `keepConnectionAlive` is `true`, the adapter won't be reset after a successful transfer, so users can continue the session using `LinkWireless::restoreExistingConnection()`. |
-| `reset()` | **bool** | Turns off the adapter and deactivates the library. It returns a boolean indicating whether the transition to low consumption mode was successful. |
+| Name | Return type | Description |
+| --------------------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `sendRom(rom, romSize, gameName, userName, gameId, players, listener, [keepConnectionAlive])` | **Result** | Sends the `rom`.
The `players` must be the number of consoles that will download the ROM. Once this number of players is reached, the code will start transmitting the ROM bytes.
During the process, the library will continuously invoke `listener` (passing a `LinkWirelessMultiboot::MultibootProgress` object as argument), and abort the transfer if it returns `true`.
The `romSize` must be a number between `448` and `262144`. It's recommended to use a ROM size that is a multiple of `16`, since this also ensures compatibility with Multiboot via Link Cable.
Once completed, the return value should be `LinkWirelessMultiboot::Result::SUCCESS`.
You can start the transfer before the player count is reached by running `*progress.ready = true;` in the`cancel` callback.
If `keepConnectionAlive` is `true`, the adapter won't be reset after a successful transfer, so users can continue the session using `LinkWireless::restoreExistingConnection()`. |
+| `reset()` | **bool** | Turns off the adapter and deactivates the library. It returns a boolean indicating whether the transition to low consumption mode was successful. |
### Compile-time constants
@@ -351,33 +351,33 @@ This version (`LinkWirelessMultiboot::Async`) allows more advanced use cases lik
`new LinkWirelessMultiboot::Async(...)` accepts these **optional** parameters:
-| Name | Type | Default | Description |
-| ------------- | -------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `gameName` | **const char\*** | `""` | Game name. Maximum `14` characters + null terminator. |
-| `userName` | **const char\*** | `""` | Maximum number of _frames_ without receiving data from other player before marking them as disconnected or resetting the connection. |
-| `gameId` | **u16** _(0 ~ 0x7FFF)_ | `0x7FFF` | The Game ID to be broadcasted. |
-| `players` | **u32** _(2~5)_ | `5` | The number of consoles that will download the ROM. Once this number of players is reached, the code will start transmitting the ROM bytes, unless `waitForReadySignal` is `true`. |
-| `waitForReadySignal` | **bool** | `false` | Whether the code should wait for a `markReady()` call to start the actual transfer. |
-| `keepConnectionAlive` | **bool** | `false` | If `true`, the adapter won't be reset after a successful transfer, so users can continue the session using `LinkWireless::restoreExistingConnection()`.
-| `interval` | **u16** | `50` | Number of _1024-cycle ticks_ (61.04μs) between transfers _(50 = 3.052ms)_. It's the interval of Timer #`timerId`.
Lower values will transfer faster but also consume more CPU. Some audio players require precise interrupt timing to avoid crashes! Use a minimum of 30. |
-| `timerId` | **u8** _(0~3)_ | `3` | GBA Timer to use for sending. |
+| Name | Type | Default | Description |
+| --------------------- | ---------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `gameName` | **const char\*** | `""` | Game name. Maximum `14` characters + null terminator. |
+| `userName` | **const char\*** | `""` | Maximum number of _frames_ without receiving data from other player before marking them as disconnected or resetting the connection. |
+| `gameId` | **u16** _(0 ~ 0x7FFF)_ | `0x7FFF` | The Game ID to be broadcasted. |
+| `players` | **u32** _(2~5)_ | `5` | The number of consoles that will download the ROM. Once this number of players is reached, the code will start transmitting the ROM bytes, unless `waitForReadySignal` is `true`. |
+| `waitForReadySignal` | **bool** | `false` | Whether the code should wait for a `markReady()` call to start the actual transfer. |
+| `keepConnectionAlive` | **bool** | `false` | If `true`, the adapter won't be reset after a successful transfer, so users can continue the session using `LinkWireless::restoreExistingConnection()`. |
+| `interval` | **u16** | `50` | Number of _1024-cycle ticks_ (61.04μs) between transfers _(50 = 3.052ms)_. It's the interval of Timer #`timerId`.
Lower values will transfer faster but also consume more CPU. Some audio players require precise interrupt timing to avoid crashes! Use a minimum of 30. |
+| `timerId` | **u8** _(0~3)_ | `3` | GBA Timer to use for sending. |
You can update these values at any time without creating a new instance by mutating the `config` property. Keep in mind that the changes won't be applied after the next `sendRom(...)` call.
### Methods
-| Name | Return type | Description |
-| --------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `sendRom(rom, romSize)` | **bool** | Sends the `rom`.
The `romSize` must be a number between `448` and `262144`. It's recommended to use a ROM size that is a multiple of `16`, since this also ensures compatibility with Multiboot via Link Cable.
Once completed, `isSending()` should return `false` and `getResult()` should return `LinkWirelessMultiboot::Async::GeneralResult::SUCCESS`.
Returns `false` if there's a pending transfer or the data is invalid. |
-| `reset()` | **bool** | Turns off the adapter and deactivates the library, canceling the in-progress transfer, if any. It returns a boolean indicating whether the transition to low consumption mode was successful. |
-| `isSending()` | **bool** | Returns whether there's an active transfer or not. |
-| `getState()` | **State** | Returns the current state. |
-| `getResult([clear])` | **GeneralResult** | Returns the result of the last operation.
After this call, the result is cleared if `clear` is `true` (default behavior). |
-| `getDetailedResult([clear])` | **Result** | Returns the detailed result of the last operation.
After this call, the result is cleared if `clear` is `true` (default behavior). |
-| `playerCount()` | **u8** _(1~5)_ | Returns the number of connected players. |
-| `getPercentage()` | **u32** _(0~100)_ | Returns the completion percentage. |
-| `isReady()` | **bool** | Returns whether the ready mark is active or not. |
-| `markReady()` | **bool** | Marks the transfer as ready. |
+| Name | Return type | Description |
+| ---------------------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `sendRom(rom, romSize)` | **bool** | Sends the `rom`.
The `romSize` must be a number between `448` and `262144`. It's recommended to use a ROM size that is a multiple of `16`, since this also ensures compatibility with Multiboot via Link Cable.
Once completed, `isSending()` should return `false` and `getResult()` should return `LinkWirelessMultiboot::Async::GeneralResult::SUCCESS`.
Returns `false` if there's a pending transfer or the data is invalid. |
+| `reset()` | **bool** | Turns off the adapter and deactivates the library, canceling the in-progress transfer, if any. It returns a boolean indicating whether the transition to low consumption mode was successful. |
+| `isSending()` | **bool** | Returns whether there's an active transfer or not. |
+| `getState()` | **State** | Returns the current state. |
+| `getResult([clear])` | **GeneralResult** | Returns the result of the last operation.
After this call, the result is cleared if `clear` is `true` (default behavior). |
+| `getDetailedResult([clear])` | **Result** | Returns the detailed result of the last operation.
After this call, the result is cleared if `clear` is `true` (default behavior). |
+| `playerCount()` | **u8** _(1~5)_ | Returns the number of connected players. |
+| `getPercentage()` | **u32** _(0~100)_ | Returns the completion percentage. |
+| `isReady()` | **bool** | Returns whether the ready mark is active or not. |
+| `markReady()` | **bool** | Marks the transfer as ready. |
⚠️ never call `reset()` inside an interrupt handler!
@@ -442,14 +442,14 @@ Additionally, there's a `LinkWirelessOpenSDK::MultiTransfer` class for file tran
## Methods
-| Name | Return type | Description |
-| ------------------------------------------------------------------------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `getChildrenData(response)` | **ChildrenData** | Parses the `response` and returns a struct containing all the received packets from the connected clients. |
-| `getParentData(response)` | **ParentData** | Parses the `response` and returns a struct containing all the received packets from the host. |
+| Name | Return type | Description |
+| ------------------------------------------------------------------------------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `getChildrenData(response)` | **ChildrenData** | Parses the `response` and returns a struct containing all the received packets from the connected clients. |
+| `getParentData(response)` | **ParentData** | Parses the `response` and returns a struct containing all the received packets from the host. |
| `createServerBuffer(fullPayload, fullPayloadSize, sequence, [targetSlots], [offset])` | **SendBuffer** | Creates a buffer for the host to send a `fullPayload` with a valid header.
If `fullPayloadSize` is higher than `84` (the maximum payload size), the buffer will only contain the **first** `84` bytes (unless an `offset` > 0 is used).
A `sequence` number must be created by using `LinkWirelessOpenSDK::SequenceNumber::fromPacketId(...)`.
Optionally, a `targetSlots` bit array can be used to exclude some clients from the transmissions (the default is `0b1111`). |
-| `createServerACKBuffer(clientHeader, clientNumber)` | **SendBuffer** | Creates a buffer for the host to acknowledge a header received from a certain `clientNumber`. |
-| `createClientBuffer(fullPayload, fullPayloadSize, sequence, [offset])` | **SendBuffer** | Creates a buffer for the client to send a `fullPayload` with a valid header.
If `fullPayloadSize` is higher than `14` (the maximum payload size), the buffer will only contain the **first** `14` bytes (unless an `offset` > 0 is used).
A `sequence` number must be created by using `LinkWirelessOpenSDK::SequenceNumber::fromPacketId(...)`. |
-| `createClientACKBuffer(serverHeader)` | **SendBuffer** | Creates a buffer for the client to acknowledge a header received from the host. |
+| `createServerACKBuffer(clientHeader, clientNumber)` | **SendBuffer** | Creates a buffer for the host to acknowledge a header received from a certain `clientNumber`. |
+| `createClientBuffer(fullPayload, fullPayloadSize, sequence, [offset])` | **SendBuffer** | Creates a buffer for the client to send a `fullPayload` with a valid header.
If `fullPayloadSize` is higher than `14` (the maximum payload size), the buffer will only contain the **first** `14` bytes (unless an `offset` > 0 is used).
A `sequence` number must be created by using `LinkWirelessOpenSDK::SequenceNumber::fromPacketId(...)`. |
+| `createClientACKBuffer(serverHeader)` | **SendBuffer** | Creates a buffer for the client to acknowledge a header received from the host. |
⚠️ advanced usage only; you only need this if you want to interact with N software!
@@ -463,26 +463,26 @@ https://github.com/afska/gba-link-connection/assets/1631752/d1f49a48-6b17-4954-9
`new LinkUniversal(...)` accepts these **optional** parameters:
-| Name | Type | Default | Description |
-| ----------------- | ---------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Name | Type | Default | Description |
+| ----------------- | ------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `protocol` | **Protocol** | `AUTODETECT` | Specifies what protocol should be used (one of `LinkUniversal::Protocol::AUTODETECT`, `LinkUniversal::Protocol::CABLE`, `LinkUniversal::Protocol::WIRELESS_AUTO`, `LinkUniversal::Protocol::WIRELESS_SERVER`, `LinkUniversal::Protocol::WIRELESS_CLIENT`, or `LinkUniversal::Protocol::WIRELESS_RESTORE_EXISTING`). |
-| `gameName` | **const char\*** | `""` | The game name that will be broadcasted in wireless sessions (max `14` characters). The string must be a null-terminated character array. The library uses this to only connect to servers from the same game. |
-| `cableOptions` | **CableOptions** | _same as LinkCable_ | All the [👾 LinkCable](#-LinkCable) constructor parameters in one _struct_. |
-| `wirelessOptions` | **WirelessOptions** | _same as LinkWireless_ | All the [📻 LinkWireless](#-LinkWireless) constructor parameters in one _struct_. |
+| `gameName` | **const char\*** | `""` | The game name that will be broadcasted in wireless sessions (max `14` characters). The string must be a null-terminated character array. The library uses this to only connect to servers from the same game. |
+| `cableOptions` | **CableOptions** | _same as LinkCable_ | All the [👾 LinkCable](#-LinkCable) constructor parameters in one _struct_. |
+| `wirelessOptions` | **WirelessOptions** | _same as LinkWireless_ | All the [📻 LinkWireless](#-LinkWireless) constructor parameters in one _struct_. |
## Methods
The interface is the same as [👾 LinkCable](#-LinkCable). Additionally, it supports these methods:
-| Name | Return type | Description |
-| ----------------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `getState()` | **State** | Returns the current state (one of `LinkUniversal::State::INITIALIZING`, `LinkUniversal::State::WAITING`, or `LinkUniversal::State::CONNECTED`). |
-| `getMode()` | **Mode** | Returns the active mode (one of `LinkUniversal::Mode::LINK_CABLE`, or `LinkUniversal::Mode::LINK_WIRELESS`). |
-| `getProtocol()` | **Protocol** | Returns the active protocol (one of `LinkUniversal::Protocol::AUTODETECT`, `LinkUniversal::Protocol::CABLE`, `LinkUniversal::Protocol::WIRELESS_AUTO`, `LinkUniversal::Protocol::WIRELESS_SERVER`, `LinkUniversal::Protocol::WIRELESS_CLIENT`, or `LinkUniversal::Protocol::WIRELESS_RESTORE_EXISTING`). |
-| `getWirelessState()` | **LinkWireless::State** | Returns the wireless state (same as [📻 LinkWireless](#-LinkWireless)'s `getState()`). |
-| `setProtocol(protocol)` | - | Sets the active `protocol`. |
-| `getLinkCable()` | **LinkCable\*** | Returns the internal `LinkCable` instance (for advanced usage). |
-| `getLinkWireless()` | **LinkWireless\*** | Returns the internal `LinkWireless` instance (for advanced usage). |
+| Name | Return type | Description |
+| ----------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `getState()` | **State** | Returns the current state (one of `LinkUniversal::State::INITIALIZING`, `LinkUniversal::State::WAITING`, or `LinkUniversal::State::CONNECTED`). |
+| `getMode()` | **Mode** | Returns the active mode (one of `LinkUniversal::Mode::LINK_CABLE`, or `LinkUniversal::Mode::LINK_WIRELESS`). |
+| `getProtocol()` | **Protocol** | Returns the active protocol (one of `LinkUniversal::Protocol::AUTODETECT`, `LinkUniversal::Protocol::CABLE`, `LinkUniversal::Protocol::WIRELESS_AUTO`, `LinkUniversal::Protocol::WIRELESS_SERVER`, `LinkUniversal::Protocol::WIRELESS_CLIENT`, or `LinkUniversal::Protocol::WIRELESS_RESTORE_EXISTING`). |
+| `getWirelessState()` | **LinkWireless::State** | Returns the wireless state (same as [📻 LinkWireless](#-LinkWireless)'s `getState()`). |
+| `setProtocol(protocol)` | - | Sets the active `protocol`. |
+| `getLinkCable()` | **LinkCable\*** | Returns the internal `LinkCable` instance (for advanced usage). |
+| `getLinkWireless()` | **LinkWireless\*** | Returns the internal `LinkWireless` instance (for advanced usage). |
## Compile-time constants
@@ -499,15 +499,15 @@ _(aka General Purpose Mode)_
## Methods
-| Name | Return type | Description |
-| ---------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------- |
-| `reset()` | - | Resets communication mode to General Purpose (same as `Link::reset()`). **Required to initialize the library!** |
-| `setMode(pin, direction)` | - | Configures a `pin` to use a `direction` (input or output). |
+| Name | Return type | Description |
+| ---------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------- |
+| `reset()` | - | Resets communication mode to General Purpose (same as `Link::reset()`). **Required to initialize the library!** |
+| `setMode(pin, direction)` | - | Configures a `pin` to use a `direction` (input or output). |
| `getMode(pin)` | **Direction** | Returns the direction set at `pin`. |
-| `readPin(pin)` | **bool** | Returns whether a `pin` is _HIGH_ or not (when set as an input). |
-| `writePin(pin, isHigh)` | - | Sets a `pin` to be high or not (when set as an output). |
-| `setSIInterrupts(isEnabled)` | - | If it `isEnabled`, an IRQ will be generated when `SI` changes from _HIGH_ to _LOW_. |
-| `getSIInterrupts()` | **bool** | Returns whether SI-falling interrupts are enabled or not. |
+| `readPin(pin)` | **bool** | Returns whether a `pin` is _HIGH_ or not (when set as an input). |
+| `writePin(pin, isHigh)` | - | Sets a `pin` to be high or not (when set as an output). |
+| `setSIInterrupts(isEnabled)` | - | If it `isEnabled`, an IRQ will be generated when `SI` changes from _HIGH_ to _LOW_. |
+| `getSIInterrupts()` | **bool** | Returns whether SI-falling interrupts are enabled or not. |
⚠️ always set the `SI` terminal to an input!
@@ -523,20 +523,20 @@ _(aka Normal Mode)_
## Methods
-| Name | Return type | Description |
-| ------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `isActive()` | **bool** | Returns whether the library is active or not. |
-| `activate(mode, [dataSize])` | - | Activates the library in a specific `mode` (one of `LinkSPI::Mode::SLAVE`, `LinkSPI::Mode::MASTER_256KBPS`, or `LinkSPI::Mode::MASTER_2MBPS`). By default, the `dataSize` is 32-bit, but can be changed to `LinkSPI::DataSize::SIZE_8BIT`. |
-| `deactivate()` | - | Deactivates the library. |
-| `transfer(data)` | **u32** | Exchanges `data` with the other end. Returns the received data. |
-| `transfer(data, cancel)` | **u32** | Like `transfer(data)` but accepts a `cancel()` function. The library will continuously invoke it, and abort the transfer if it returns `true`. |
-| `transferAsync(data, [cancel])` | - | Schedules a `data` transfer and returns. After this, call `getAsyncState()` and `getAsyncData()`.
Note that until you retrieve the async data, normal `transfer(...)`s won't do anything! |
+| Name | Return type | Description |
+| ------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `isActive()` | **bool** | Returns whether the library is active or not. |
+| `activate(mode, [dataSize])` | - | Activates the library in a specific `mode` (one of `LinkSPI::Mode::SLAVE`, `LinkSPI::Mode::MASTER_256KBPS`, or `LinkSPI::Mode::MASTER_2MBPS`). By default, the `dataSize` is 32-bit, but can be changed to `LinkSPI::DataSize::SIZE_8BIT`. |
+| `deactivate()` | - | Deactivates the library. |
+| `transfer(data)` | **u32** | Exchanges `data` with the other end. Returns the received data. |
+| `transfer(data, cancel)` | **u32** | Like `transfer(data)` but accepts a `cancel()` function. The library will continuously invoke it, and abort the transfer if it returns `true`. |
+| `transferAsync(data, [cancel])` | - | Schedules a `data` transfer and returns. After this, call `getAsyncState()` and `getAsyncData()`.
Note that until you retrieve the async data, normal `transfer(...)`s won't do anything! |
| `getAsyncState()` | **AsyncState** | Returns the state of the last async transfer (one of `LinkSPI::AsyncState::IDLE`, `LinkSPI::AsyncState::WAITING`, or `LinkSPI::AsyncState::READY`). |
-| `getAsyncData()` | **u32** | If the async state is `READY`, returns the remote data and switches the state back to `IDLE`. If not, returns an empty response. |
+| `getAsyncData()` | **u32** | If the async state is `READY`, returns the remote data and switches the state back to `IDLE`. If not, returns an empty response. |
| `getMode()` | **Mode** | Returns the current `mode`. |
| `getDataSize()` | **DataSize** | Returns the current `dataSize`. |
-| `setWaitModeActive(isActive)` | - | Enables or disables `waitMode` (\*). |
-| `isWaitModeActive()` | **bool** | Returns whether `waitMode` (\*) is active or not. |
+| `setWaitModeActive(isActive)` | - | Enables or disables `waitMode` (\*). |
+| `isWaitModeActive()` | **bool** | Returns whether `waitMode` (\*) is active or not. |
> (\*) `waitMode`: The GBA adds an extra feature over SPI. When working as master, it can check whether the other terminal is ready to receive (ready: `MISO=LOW`), and wait if it's not (not ready: `MISO=HIGH`). That makes the connection more reliable, but it's not always supported on other hardware units (e.g. the Wireless Adapter), so it must be disabled in those cases.
>
@@ -579,23 +579,23 @@ _(aka UART Mode)_
## Methods
-| Name | Return type | Description |
-| ---------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `isActive()` | **bool** | Returns whether the library is active or not. |
-| `activate(baudRate, dataSize, parity, useCTS)` | - | Activates the library using a specific UART mode. _Defaults: 9600bps, 8-bit data, no parity bit, no CTS_. |
-| `deactivate()` | - | Deactivates the library. |
-| `sendLine(string)` | - | Takes a null-terminated `string`, and sends it followed by a `'\n'` character. The null character is not sent. |
-| `sendLine(data, cancel)` | - | Like `sendLine(string)` but accepts a `cancel()` function. The library will continuously invoke it, and abort the transfer if it returns `true`. |
+| Name | Return type | Description |
+| ---------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `isActive()` | **bool** | Returns whether the library is active or not. |
+| `activate(baudRate, dataSize, parity, useCTS)` | - | Activates the library using a specific UART mode. _Defaults: 9600bps, 8-bit data, no parity bit, no CTS_. |
+| `deactivate()` | - | Deactivates the library. |
+| `sendLine(string)` | - | Takes a null-terminated `string`, and sends it followed by a `'\n'` character. The null character is not sent. |
+| `sendLine(data, cancel)` | - | Like `sendLine(string)` but accepts a `cancel()` function. The library will continuously invoke it, and abort the transfer if it returns `true`. |
| `readLine(string, [limit])` | **bool** | Reads characters into `string` until finding a `'\n'` character or a character `limit` is reached. A null terminator is added at the end.
Returns `false` if the limit has been reached without finding a newline character. |
-| `readLine(string, cancel, [limit])` | **bool** | Like `readLine(string, [limit])` but accepts a `cancel()` function. The library will continuously invoke it, and abort the transfer if it returns `true`. |
-| `send(buffer, size, offset)` | - | Sends `size` bytes from `buffer`, starting at byte `offset`. |
-| `read(buffer, size, offset)` | **u32** | Tries to read `size` bytes into `(u8*)(buffer + offset)`. Returns the number of read bytes. |
-| `canRead()` | **bool** | Returns whether there are bytes to read or not. |
-| `canSend()` | **bool** | Returns whether there is room to send new messages or not. |
-| `availableForRead()` | **u32** | Returns the number of bytes available for read. |
-| `availableForSend()` | **u32** | Returns the number of bytes available for send (buffer size - queued bytes). |
-| `read()` | **u8** | Reads a byte. Returns 0 if nothing is found. |
-| `send(data)` | - | Sends a `data` byte. |
+| `readLine(string, cancel, [limit])` | **bool** | Like `readLine(string, [limit])` but accepts a `cancel()` function. The library will continuously invoke it, and abort the transfer if it returns `true`. |
+| `send(buffer, size, offset)` | - | Sends `size` bytes from `buffer`, starting at byte `offset`. |
+| `read(buffer, size, offset)` | **u32** | Tries to read `size` bytes into `(u8*)(buffer + offset)`. Returns the number of read bytes. |
+| `canRead()` | **bool** | Returns whether there are bytes to read or not. |
+| `canSend()` | **bool** | Returns whether there is room to send new messages or not. |
+| `availableForRead()` | **u32** | Returns the number of bytes available for read. |
+| `availableForSend()` | **u32** | Returns the number of bytes available for send (buffer size - queued bytes). |
+| `read()` | **u8** | Reads a byte. Returns 0 if nothing is found. |
+| `send(data)` | - | Sends a `data` byte. |
## Compile-time constants
@@ -621,25 +621,27 @@ _(aka JOYBUS Mode)_
## Methods
-| Name | Return type | Description |
-| ------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `isActive()` | **bool** | Returns whether the library is active or not. |
-| `activate()` | - | Activates the library. |
-| `deactivate()` | - | Deactivates the library. |
-| `wait()` | **bool** | Waits for data. Returns `true` on success, or `false` on JOYBUS reset. |
-| `wait(cancel)` | **bool** | Like `wait()` but accepts a `cancel()` function. The library will invoke it after every SERIAL interrupt, and abort the wait if it returns `true`. |
-| `canRead()` | **bool** | Returns `true` if there are pending received values to read. |
-| `read()` | **u32** | Dequeues and returns the next received value. If there's no received data, a `0` will be returned. |
-| `peek()` | **u32** | Returns the next received value without dequeuing it. If there's no received data, a `0` will be returned. |
-| `send(data)` | - | Sends 32-bit `data`. If the other end asks for data at the same time you call this method, a `0x00000000` will be sent. |
-| `pendingCount()` | **u32** | Returns the number of pending outgoing transfers. |
-| `didQueueOverflow([clear])` | **bool** | Returns whether the internal queue lost messages at some point due to being full. This can happen if your queue size is too low, if you receive too much data without calling `read(...)` enough times, or if excessive `read(...)` calls prevent the ISR from copying data.
After this call, the overflow flag is cleared if `clear` is `true` (default behavior). |
-| `didReset([clear])` | **bool** | Returns whether a JOYBUS reset was requested or not.
After this call, the reset flag is cleared if `clear` is `true` (default behavior). |
+| Name | Return type | Description |
+| --------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `isActive()` | **bool** | Returns whether the library is active or not. |
+| `activate()` | - | Activates the library. |
+| `deactivate()` | - | Deactivates the library. |
+| `wait()` | **bool** | Waits for data. Returns `true` on success, or `false` on JOYBUS reset. |
+| `wait(cancel)` | **bool** | Like `wait()` but accepts a `cancel()` function. The library will invoke it after every SERIAL interrupt, and abort the wait if it returns `true`. |
+| `canRead()` | **bool** | Returns `true` if there are pending received values to read. |
+| `read()` | **u32** | Dequeues and returns the next received value. If there's no received data, a `0` will be returned. |
+| `peek()` | **u32** | Returns the next received value without dequeuing it. If there's no received data, a `0` will be returned. |
+| `send(data)` | - | Sends 32-bit `data`. If the other end asks for data at the same time you call this method, a `0x00000000` will be sent. |
+| `pendingCount()` | **u32** | Returns the number of pending outgoing transfers. |
+| `didQueueOverflow([clear])` | **bool** | Returns whether the internal queue lost messages at some point due to being full. This can happen if your queue size is too low, if you receive too much data without calling `read(...)` enough times, or if excessive `read(...)` calls prevent the ISR from copying data.
After this call, the overflow flag is cleared if `clear` is `true` (default behavior). |
+| `didReset([clear])` | **bool** | Returns whether a JOYBUS reset was requested or not.
After this call, the reset flag is cleared if `clear` is `true` (default behavior). |
## Compile-time constants
- `LINK_CUBE_QUEUE_SIZE`: to set a custom buffer size (how many incoming and outgoing values the queues can store at max). The default value is `10`, which seems fine for most games.
+
- This affects how much memory is allocated. With the default value, it's around `120` bytes. There's a double-buffered pending queue (to avoid data races), and 1 outgoing queue.
+
- You can approximate the memory usage with:
- `LINK_CUBE_QUEUE_SIZE * sizeof(u32) * 3` <=> `LINK_CUBE_QUEUE_SIZE * 12`
@@ -685,30 +687,73 @@ You can update these values at any time without creating a new instance:
| `activate()` | - | Activates the library. After some time, if an adapter is connected, the state will be changed to `SESSION_ACTIVE`. If not, the state will be `NEEDS_RESET`, and you can retrieve the error with `getError()`. |
| `deactivate()` | - | Deactivates the library, resetting the serial mode to GPIO. Calling `shutdown()` first is recommended, but the adapter will put itself in sleep mode after 3 seconds anyway. |
| `shutdown()` | **bool** | Gracefully shuts down the adapter, closing all connections. After some time, the state will be changed to `SHUTDOWN`, and only then it's safe to call `deactivate()`. |
-| `call(phoneNumber)` | **bool** | Initiates a P2P connection with a `phoneNumber`. After some time, the state will be `CALL_ESTABLISHED` (or `ACTIVE_SESSION` if the connection fails or ends).
In REON/libmobile the phone number can be a number assigned by the relay server, or a 12-digit IPv4 address (for example, `"127000000001"` would be `127.0.0.1`). |
-| `callISP(password, loginId)` | **bool** | Calls the ISP number registered in the adapter configuration, or a default number if the adapter hasn't been configured. Then, performs a login operation using the provided `password` and `loginId`. After some time, the state will be `PPP_ACTIVE`.
If `loginId` is empty and the adapter has been configured, it will use the one stored in the configuration.
Both parameters are null-terminated strings (max `32` characters). |
-| `dnsQuery(domainName, result)` | **bool** | Looks up the IPv4 address for a `domainName` (a null-terminated string, max `253` characters). It also accepts an ASCII IPv4 address, converting it into a 4-byte address instead of querying the DNS server.
The `result` is a pointer to a `LinkMobile::DNSQuery` struct that will be filled with the result.
When the request is completed, the `completed` field will be `true`.
If an IP address was found, the `success` field will be `true` and the `ipv4` field can be read as a 4-byte address. |
-| `openConnection(ip, port, type, result)` | **bool** | Opens a TCP/UDP (`type`) connection at the given `ip` (4-byte address) on the given `port`.
The `result` is a pointer to a `LinkMobile::OpenConn` struct that will be filled with the result.
When the request is completed, the `completed` field will be `true`.
If the connection was successful, the `success` field will be `true` and the `connectionId` field can be used when calling the `transfer(...)` method.
Only `2` connections can be opened at the same time. |
-| `closeConnection(connectionId, type, result)` | **bool** | Closes an active TCP/UDP (`type`) connection.
The `result` is a pointer to a `LinkMobile::CloseConn` struct that will be filled with the result.
When the request is completed, the `completed` field will be `true`.
If the connection was closed correctly, the `success` field will be `true`. |
-| `transfer(dataToSend, result, [connectionId])` | **bool** | Requests a data transfer (up to `254` bytes) and responds the received data. The transfer can be done with the other node in a P2P connection, or with any open TCP/UDP connection if a PPP session is active. In the case of a TCP/UDP connection, the `connectionId` must be provided.
The `result` is a pointer to a `LinkMobile::DataTransfer` struct that will be filled with the received data. It can also point to `dataToSend` to reuse the struct.
When the request is completed, the `completed` field will be `true`.
If the transfer was successful, the `success` field will be `true`. If not, you can assume that the connection was closed. |
-| `waitFor(asyncRequest)` | **bool** | Waits for `asyncRequest` to be completed. Returns `true` if the request was completed && successful, and the adapter session is still alive. Otherwise, it returns `false`.
The `asyncRequest` is a pointer to a `LinkMobile::DNSQuery`, `LinkMobile::OpenConn`, `LinkMobile::CloseConn`, or `LinkMobile::DataTransfer`. |
+| `call(phoneNumber)` | **bool** | Initiates a P2P connection with a `phoneNumber`. After some time, the state will be `CALL_ESTABLISHED` (or `ACTIVE_SESSION` if the connection fails or ends).
In REON/libmobile the phone number can be a number assigned by the relay server, or a 12-digit IPv4 address (for example, `"127000000001"` would be `127.0.0.1`). |
+| `callISP(password, loginId)` | **bool** | Calls the ISP number registered in the adapter configuration, or a default number if the adapter hasn't been configured. Then, performs a login operation using the provided `password` and `loginId`. After some time, the state will be `PPP_ACTIVE`.
If `loginId` is empty and the adapter has been configured, it will use the one stored in the configuration.
Both parameters are null-terminated strings (max `32` characters). |
+| `dnsQuery(domainName, result)` | **bool** | Looks up the IPv4 address for a `domainName` (a null-terminated string, max `253` characters). It also accepts an ASCII IPv4 address, converting it into a 4-byte address instead of querying the DNS server.
The `result` is a pointer to a `LinkMobile::DNSQuery` struct that will be filled with the result.
When the request is completed, the `completed` field will be `true`.
If an IP address was found, the `success` field will be `true` and the `ipv4` field can be read as a 4-byte address. |
+| `openConnection(ip, port, type, result)` | **bool** | Opens a TCP/UDP (`type`) connection at the given `ip` (4-byte address) on the given `port`.
The `result` is a pointer to a `LinkMobile::OpenConn` struct that will be filled with the result.
When the request is completed, the `completed` field will be `true`.
If the connection was successful, the `success` field will be `true` and the `connectionId` field can be used when calling the `transfer(...)` method.
Only `2` connections can be opened at the same time. |
+| `closeConnection(connectionId, type, result)` | **bool** | Closes an active TCP/UDP (`type`) connection.
The `result` is a pointer to a `LinkMobile::CloseConn` struct that will be filled with the result.
When the request is completed, the `completed` field will be `true`.
If the connection was closed correctly, the `success` field will be `true`. |
+| `transfer(dataToSend, result, [connectionId])` | **bool** | Requests a data transfer (up to `254` bytes) and responds the received data. The transfer can be done with the other node in a P2P connection, or with any open TCP/UDP connection if a PPP session is active. In the case of a TCP/UDP connection, the `connectionId` must be provided.
The `result` is a pointer to a `LinkMobile::DataTransfer` struct that will be filled with the received data. It can also point to `dataToSend` to reuse the struct.
When the request is completed, the `completed` field will be `true`.
If the transfer was successful, the `success` field will be `true`. If not, you can assume that the connection was closed. |
+| `waitFor(asyncRequest)` | **bool** | Waits for `asyncRequest` to be completed. Returns `true` if the request was completed && successful, and the adapter session is still alive. Otherwise, it returns `false`.
The `asyncRequest` is a pointer to a `LinkMobile::DNSQuery`, `LinkMobile::OpenConn`, `LinkMobile::CloseConn`, or `LinkMobile::DataTransfer`. |
| `hangUp()` | **bool** | Hangs up the current P2P or PPP call. Closes all connections. |
-| `readConfiguration(configurationData)` | **bool** | Retrieves the adapter configuration, and puts it in the `configurationData` struct.
If the adapter has an active session, the data is already loaded, so it's instantaneous. |
-| `getState()` | **State** | Returns the current state (one of `LinkMobile::State::NEEDS_RESET`, `LinkMobile::State::PINGING`, `LinkMobile::State::WAITING_TO_START`, `LinkMobile::State::STARTING_SESSION`, `LinkMobile::State::ACTIVATING_SIO32`, `LinkMobile::State::WAITING_32BIT_SWITCH`, `LinkMobile::State::READING_CONFIGURATION`, `LinkMobile::State::SESSION_ACTIVE`, `LinkMobile::State::CALL_REQUESTED`, `LinkMobile::State::CALLING`, `LinkMobile::State::CALL_ESTABLISHED`, `LinkMobile::State::ISP_CALL_REQUESTED`, `LinkMobile::State::ISP_CALLING`, `LinkMobile::State::PPP_LOGIN`, `LinkMobile::State::PPP_ACTIVE`, `LinkMobile::State::SHUTDOWN_REQUESTED`, `LinkMobile::State::ENDING_SESSION`, `LinkMobile::State::WAITING_8BIT_SWITCH`, or `LinkMobile::State::SHUTDOWN`). |
-| `getRole()` | **Role** | Returns the current role in the P2P connection (one of `LinkMobile::Role::NO_P2P_CONNECTION`, `LinkMobile::Role::CALLER`, or `LinkMobile::Role::RECEIVER`). |
+| `readConfiguration(configurationData)` | **bool** | Retrieves the adapter configuration, and puts it in the `configurationData` struct.
If the adapter has an active session, the data is already loaded, so it's instantaneous. |
+| `getState()` | **State** | Returns the current state (one of `LinkMobile::State::NEEDS_RESET`, `LinkMobile::State::PINGING`, `LinkMobile::State::WAITING_TO_START`, `LinkMobile::State::STARTING_SESSION`, `LinkMobile::State::ACTIVATING_SIO32`, `LinkMobile::State::WAITING_32BIT_SWITCH`, `LinkMobile::State::READING_CONFIGURATION`, `LinkMobile::State::SESSION_ACTIVE`, `LinkMobile::State::CALL_REQUESTED`, `LinkMobile::State::CALLING`, `LinkMobile::State::CALL_ESTABLISHED`, `LinkMobile::State::ISP_CALL_REQUESTED`, `LinkMobile::State::ISP_CALLING`, `LinkMobile::State::PPP_LOGIN`, `LinkMobile::State::PPP_ACTIVE`, `LinkMobile::State::SHUTDOWN_REQUESTED`, `LinkMobile::State::ENDING_SESSION`, `LinkMobile::State::WAITING_8BIT_SWITCH`, or `LinkMobile::State::SHUTDOWN`). |
+| `getRole()` | **Role** | Returns the current role in the P2P connection (one of `LinkMobile::Role::NO_P2P_CONNECTION`, `LinkMobile::Role::CALLER`, or `LinkMobile::Role::RECEIVER`). |
| `isConfigurationValid()` | **int** | Returns whether the adapter has been configured or not. Returns `1` = yes, `0` = no, `-1` = unknown (no session active). |
| `isConnectedP2P()` | **bool** | Returns `true` if a P2P call is established (the state is `CALL_ESTABLISHED`). |
| `isConnectedPPP()` | **bool** | Returns `true` if a PPP session is active (the state is `PPP_ACTIVE`). |
| `isSessionActive()` | **bool** | Returns `true` if the session is active. |
| `canShutdown()` | **bool** | Returns `true` if there's an active session and there's no previous shutdown requests. |
| `getDataSize()` | **LinkSPI::DataSize** | Returns the current operation mode (`LinkSPI::DataSize`). |
-| `getError()` | **Error** | Returns details about the last error that caused the connection to be aborted. |
+| `getError()` | **Error** | Returns details about the last error that caused the connection to be aborted. |
## Compile-time constants
- `LINK_MOBILE_QUEUE_SIZE`: to set a custom request queue size (how many commands can be queued at the same time). The default value is `10`, which seems fine for most games.
- This affects how much memory is allocated. With the default value, it's around `3` KB.
+# 📺 LinkIR
+
+_(aka Infrared Adapter or AGB-006)_
+
+[⬆️](#gba-link-connection) The Infrared adapter was only used in one commercial game: _Cyber Drive Zoids: Kiju no Senshi Hyuu_, but we can now give it a better use with homebrew!
+
+This library lets you control the IR LED directly via bitbanging, send/receive modulated 38kHz signals, and send/receive pulses in the standard NEC protocol.
+
+// TODO: VIDEO
+
+
+## Constructor
+
+`new LinkIR(...)` accepts these **optional** parameters:
+
+| Name | Type | Default | Description |
+| ------------------ | -------------- | ------- | ----------------------------------------- |
+| `primaryTimerId` | **u8** _(0~3)_ | `2` | GBA Timer to use for counting time (1/2). |
+| `secondaryTimerId` | **u8** _(0~3)_ | `3` | GBA Timer to use for counting time (2/2). |
+
+You can update these values at any time without creating a new instance:
+
+- Call `deactivate()`.
+- Mutate the `config` property.
+- Call `activate()`.
+
+## Methods
+
+| Name | Return type | Description |
+| -------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `isActive()` | **bool** | Returns whether the library is active or not. |
+| `activate()` | - | Activates the library. Returns whether the adapter is connected or not. |
+| `deactivate()` | - | Deactivates the library. |
+| `sendNEC(address, command)` | - | Sends a NEC signal, with an 8-bit `address` and an 8-bit `command`. |
+| `receiveNEC(address, command, [startTimeout])` | **bool** | Receives a signal and returns whether it's a NEC signal or not. If it is, the `address` and `command` will be filled. Returns `true` on success.
If a `startTimeout` is provided, the reception will be canceled after that number of microseconds if no signal is detected. |
+| `parseNEC(pulses, address, command)` | **bool** | Tries to interpret an already received array of `pulses` as a NEC signal. On success, returns `true` and fills the `address` and `command` parameters. |
+| `send(pulses)` | - | Sends a generic IR signal, modulating at standard 38kHz.
The `pulses` are u16 numbers describing the signal. Even indices are _marks_ (IR on), odd indices are _spaces_ (IR off), and `0` ends the signal. |
+| `receive(pulses, maxEntries, [startTimeout], [signalTimeout])` | **bool** | Receives a generic IR signal modulated at standard 38kHz, up to a certain number of pulses (`maxEntries`). Returns whether something was received or not.
The `pulses` are u16 numbers describing the signal. Even indices are _marks_ (IR on), odd indices are _spaces_ (IR off), and `0` ends the signal.
If a `startTimeout` is provided, the reception will be canceled after that number of microseconds if no signal is detected.
If a `signalTimeout` is provided, the reception will be terminated after a _space_ longer than that number of microseconds (default: `15000`). |
+| `setLight(on)` | - | Turns the output IR LED ON/OFF through the `SO` pin (HIGH = ON). Add some pauses after every 10µs! |
+| `isEmittingLight()` | **bool** | Returns whether the output IR LED is ON or OFF. |
+| `isDetectingLight()` | **bool** | Returns whether a remote light signal is detected through the `SI` pin (LOW = DETECTED) or not. |
+
+⚠️ wait at least 1 microsecond before `send(...)` and `receive(...)` calls!
# 🖱️ LinkPS2Mouse
@@ -722,11 +767,11 @@ You can update these values at any time without creating a new instance:
## Methods
-| Name | Return type | Description |
-| ----------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `isActive()` | **bool** | Returns whether the library is active or not. |
-| `activate()` | - | Activates the library. |
-| `deactivate()` | - | Deactivates the library. |
+| Name | Return type | Description |
+| ----------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `isActive()` | **bool** | Returns whether the library is active or not. |
+| `activate()` | - | Activates the library. |
+| `deactivate()` | - | Deactivates the library. |
| `report(data[3])` | - | Fills the `data` int array with a report.
The first int contains _clicks_ that you can check against the bitmasks `LINK_PS2_MOUSE_LEFT_CLICK`, `LINK_PS2_MOUSE_MIDDLE_CLICK`, and `LINK_PS2_MOUSE_RIGHT_CLICK`.
The second int is the _X movement_, and the third int is the _Y movement_. |
⚠️ calling `activate()` or `report(...)` could freeze the system if nothing is connected: detecting timeouts using interrupts is the user's responsibility!
diff --git a/docs/infrared_adapter.md b/docs/infrared_adapter.md
index c478f15..c2f0dc3 100644
--- a/docs/infrared_adapter.md
+++ b/docs/infrared_adapter.md
@@ -13,10 +13,10 @@
The AGB-006 is an accessory released in conjunction with Cyber Drive Zoids: Kiju no Senshi Hyuu on July 18, 2003. It serves as an infrared adapter, coming as a bundle with each game. Using IR signals, players can turn their GBAs into remote controls to pilot three toy model Zoids. Although the GBA removed the GBC's IR port, the AGB-006 officially restored that functionality as an add-on. Unfortunately Cyber Drive Zoids was the only game to take adavantage of the AGB-006.
-* The AGB-006 is a small attachment that fits into the GBA serial port providing IR functionalities
-* Has 2 IR diodes, one for receiving and one for transmitting
-* Very similar in size and shape to the connecting end of a DOL-011 (Gamecube-to-GBA cable)
-* Compatible with CDZ-01 Diablotiger, CDZ-02 Cyclops, and CDZ-EX Diablotiger B
+- The AGB-006 is a small attachment that fits into the GBA serial port providing IR functionalities
+- Has 2 IR diodes, one for receiving and one for transmitting
+- Very similar in size and shape to the connecting end of a DOL-011 (Gamecube-to-GBA cable)
+- Compatible with CDZ-01 Diablotiger, CDZ-02 Cyclops, and CDZ-EX Diablotiger B
## \[AGB-006\] : Device Detection
@@ -140,20 +140,20 @@ For Misc. Actions
0x0 If Motion Type == 1 -> Fire
0x1 If Motion Type == 1 -> Fire
-0x2 If Motion Type == 0 -> Jump
- If Motion Type == 3 -> Sync ID1, Boost Level 0
+0x2 If Motion Type == 0 -> Jump
+ If Motion Type == 3 -> Sync ID1, Boost Level 0
If Motion Type == 5 -> Intimidate
0x3 If Motion Type == 0 -> Jump
If Motion Type == 3 -> Sync ID2, Boost Level 0
- If Motion Type == 5 -> Intimidate
+ If Motion Type == 5 -> Intimidate
-0x4 If Motion Type == 3 -> Sync ID1, Boost Level 1
- If Motion Type == 5 -> Swing Hips
+0x4 If Motion Type == 3 -> Sync ID1, Boost Level 1
+ If Motion Type == 5 -> Swing Hips
-0x5 If Motion Type == 3 -> Sync ID2, Boost Level 1
+0x5 If Motion Type == 3 -> Sync ID2, Boost Level 1
If Motion Type == 5 -> Swing Hips
0xA War Cry
diff --git a/lib/LinkCable.hpp b/lib/LinkCable.hpp
index dd62bb1..9e1adad 100644
--- a/lib/LinkCable.hpp
+++ b/lib/LinkCable.hpp
@@ -265,7 +265,7 @@ class LinkCable {
}
/**
- * @brief Returns if a `send(...)` call would fail due to the queue being
+ * @brief Returns whether a `send(...)` call would fail due to the queue being
* full.
*/
bool canSend() { return !_state.outgoingMessages.isFull(); }
diff --git a/lib/LinkIR.hpp b/lib/LinkIR.hpp
index 697de33..804ff75 100644
--- a/lib/LinkIR.hpp
+++ b/lib/LinkIR.hpp
@@ -34,6 +34,9 @@
// - 9) Receive manually:
// bool ledOn = linkIR->isDetectingLight();
// --------------------------------------------------------------------------
+// considerations:
+// - wait at least 1 microsecond before `send(...)` and `receive(...)` calls!
+// --------------------------------------------------------------------------
#ifndef LINK_DEVELOPMENT
#pragma GCC system_header
@@ -100,7 +103,8 @@ class LinkIR {
[[nodiscard]] bool isActive() { return isEnabled; }
/**
- * @brief Activates the library. Returns if the adapter is connected or not.
+ * @brief Activates the library. Returns whether the adapter is connected or
+ * not.
*/
bool activate() {
LINK_READ_TAG(LINK_IR_VERSION);
@@ -133,6 +137,14 @@ class LinkIR {
return detected;
}
+ /**
+ * @brief Deactivates the library.
+ */
+ void deactivate() {
+ isEnabled = false;
+ linkGPIO.reset();
+ }
+
/**
* Sends a NEC signal.
* @param address An 8-bit address, to specify the device.
@@ -178,6 +190,8 @@ class LinkIR {
/**
* Tries to interpret an already received array of `pulses` as a NEC signal.
+ * On success, returns `true` and fills the `address` and `command`
+ * parameters.
* @param pulses The pulses to interpret. Returns `true` on success.
* @param address The read 8-bit address, specifying the device.
* @param command The read 8-bit command, specifying the action.
@@ -231,7 +245,8 @@ class LinkIR {
void send(u16 pulses[]); // defined in `LinkIR.cpp`
/**
- * Receives a generic IR signal modulated at standard 38kHz.
+ * Receives a generic IR signal modulated at standard 38kHz. Returns whether
+ * something was received or not.
* @param pulses An array to be filled with u16 numbers describing the signal.
* Even indices are *marks* (IR on), odd indices are *spaces* (IR off), and
* `0` ends the signal.
@@ -251,7 +266,7 @@ class LinkIR {
/**
* Turns the output IR LED ON/OFF through the `SO` pin (HIGH = ON).
* @param on Whether the light should be ON.
- * \warning Add some pauses after every 10µs.
+ * \warning Add some pauses after every 10µs!
*/
void setLight(bool on) { linkGPIO.writePin(Pin::SO, on); }
@@ -266,14 +281,6 @@ class LinkIR {
*/
bool isDetectingLight() { return !linkGPIO.readPin(Pin::SI); }
- /**
- * @brief Deactivates the library.
- */
- void deactivate() {
- isEnabled = false;
- linkGPIO.reset();
- }
-
/**
* @brief This method is called by the SERIAL interrupt handler.
* \warning This is internal API!
diff --git a/lib/LinkUniversal.hpp b/lib/LinkUniversal.hpp
index c8ca83f..7b65ca5 100644
--- a/lib/LinkUniversal.hpp
+++ b/lib/LinkUniversal.hpp
@@ -354,8 +354,8 @@ class LinkUniversal {
}
/**
- * @brief Returns if a `send(...)` call would fail due to the queue being
- * full.
+ * @brief Returns whether a `send(...)` call would fail due to the queue being
+ * full or not.
*/
bool canSend() {
return mode == Mode::LINK_CABLE ? linkCable.canSend()
diff --git a/lib/LinkWireless.hpp b/lib/LinkWireless.hpp
index 14ebbc5..28c0bcb 100644
--- a/lib/LinkWireless.hpp
+++ b/lib/LinkWireless.hpp
@@ -613,8 +613,8 @@ class LinkWireless {
}
/**
- * @brief Returns if a `send(...)` call would fail due to the queue being
- * full.
+ * @brief Returns whether a `send(...)` call would fail due to the queue being
+ * full or not.
*/
bool canSend() { return !sessionState.newOutgoingMessages.isFull(); }