Public Access
Debug Console: the listener is checked and retried, and debug off <seconds> comes back by itself
The framework's server begin() fails without a word: the console's task now asks whether it listens, says so, and tries again. `debug off <seconds>` closes the console and reopens it after the pause, which is the only way to test its closing and reopening from afar. Checked on the device: 25 closings and reopenings, each back a second after the pause. Free heap dips about 270 bytes for each connection the device closes and is all back two minutes later (TCP keeps a closed connection that long): not a leak. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
@@ -204,7 +204,7 @@ A note holds up to 16 KB while it's edited. A bigger text file opens read-only i
|
|||||||
| `coredump erase` | Forgets the core dump |
|
| `coredump erase` | Forgets the core dump |
|
||||||
| `loop spin on` / `loop spin off` | Make the main loop spin without resting, to compare load and radio noise |
|
| `loop spin on` / `loop spin off` | Make the main loop spin without resting, to compare load and radio noise |
|
||||||
| `crash abort` / `crash wdt` | Crash on purpose, or hang the main loop until the watchdog fires |
|
| `crash abort` / `crash wdt` | Crash on purpose, or hang the main loop until the watchdog fires |
|
||||||
| `debug status` / `debug off` | The Debug Console: whether it's on, has a token and a client; switch it off |
|
| `debug status` / `debug off [seconds]` | The Debug Console: whether it's on, has a token and a client; switch it off. With a number of seconds, it comes back by itself after that long |
|
||||||
| `debug on` / `debug token <value>` / `debug token new` | USB serial only: switch it on (making a token if there's none), give it a token of 16 to 64 characters, or make a new one. The token is never printed |
|
| `debug on` / `debug token <value>` / `debug token new` | USB serial only: switch it on (making a token if there's none), give it a token of 16 to 64 characters, or make a new one. The token is never printed |
|
||||||
| `help` | Lists the commands |
|
| `help` | Lists the commands |
|
||||||
|
|
||||||
|
|||||||
@@ -147,6 +147,8 @@ The issue asked for a token that could be set, so that Debug Builds could be pub
|
|||||||
- **Two settings,** `DebugConsole` and `DebugToken`, with the others: validated, and invalid stored values count as missing.
|
- **Two settings,** `DebugConsole` and `DebugToken`, with the others: validated, and invalid stored values count as missing.
|
||||||
- **The console's task and ring come and go with the setting.** `Console::openRing` allocates the ring; switching off closes the socket, frees it and ends the task. `tick` follows the settings once a second, so a switch off and on again takes a second or two.
|
- **The console's task and ring come and go with the setting.** `Console::openRing` allocates the ring; switching off closes the socket, frees it and ends the task. `tick` follows the settings once a second, so a switch off and on again takes a second or two.
|
||||||
- **Measured against the two builds it replaces:** 1,881,799 bytes of flash and 54,700 of static RAM. The release build was 1,851,387 and 54,612 (so 30 KB more flash and 88 bytes more RAM); the Debug Build was 1,874,887 and 58,780 (4 KB of RAM back: the ring is no longer a static array).
|
- **Measured against the two builds it replaces:** 1,881,799 bytes of flash and 54,700 of static RAM. The release build was 1,851,387 and 54,612 (so 30 KB more flash and 88 bytes more RAM); the Debug Build was 1,874,887 and 58,780 (4 KB of RAM back: the ring is no longer a static array).
|
||||||
|
- **The listener is asked whether it listens.** The framework's `begin()` returns nothing and fails without a word; the task now checks, says so on the console, and tries again every two seconds.
|
||||||
|
- **`debug off <seconds>`** closes the console and brings it back by itself: the only way to try its closing and reopening from afar, since switching it on is for the device and the cable only.
|
||||||
- **`scripts/rdbg.py`** answers the challenge, and fetches a release's ELF from Gitea when a crash names a version that isn't in `.pio/elves/`.
|
- **`scripts/rdbg.py`** answers the challenge, and fetches a release's ELF from Gitea when a crash names a version that isn't in `.pio/elves/`.
|
||||||
|
|
||||||
### Checks
|
### Checks
|
||||||
@@ -164,6 +166,9 @@ The issue asked for a token that could be set, so that Debug Builds could be pub
|
|||||||
| Three `crash abort` in a row | **Safe Mode**, with the console reachable: `info` works, `ls /` says `not available in Safe Mode`. `rdbg.py crash` decodes the backtrace to `runCommand` at the `abort()` line. `reboot` leaves Safe Mode |
|
| Three `crash abort` in a row | **Safe Mode**, with the console reachable: `info` works, `ls /` says `not available in Safe Mode`. `rdbg.py crash` decodes the backtrace to `runCommand` at the `abort()` line. `reboot` leaves Safe Mode |
|
||||||
| An update over Wi-Fi with the console on | The setting and the token survive: the console is back by itself after the restart |
|
| An update over Wi-Fi with the console on | The setting and the token survive: the console is back by itself after the restart |
|
||||||
| `debug off` over the console | The client is dropped and port 2323 refuses connections; the update port still answers |
|
| `debug off` over the console | The client is dropped and port 2323 refuses connections; the update port still answers |
|
||||||
|
| `debug off 2`, 10 times in a row, then 15 | It came back each time, a second after the pause. Free heap dips by about 270 bytes for each connection the device closes and **is all back two minutes later** (107.2 KB before, 103.2 right after 15, 107.0 at two minutes): TCP holds a closed connection that long. Not a leak, though it looked like one for an hour |
|
||||||
| Memory, console on with a client | 108 KB free (the Debug Build it replaces: 104 KB). In Safe Mode: 178 KB free |
|
| Memory, console on with a client | 108 KB free (the Debug Build it replaces: 104 KB). In Safe Mode: 178 KB free |
|
||||||
|
|
||||||
|
**One false alarm:** after the tests the console "wouldn't come back on". It was off: the setting had been left off by `debug off`, and the page's "Switch it on?" dialog opens on **Cancel**, so Enter twice leaves it off. Nothing was lost.
|
||||||
|
|
||||||
**Not checked:** `scripts/flash.sh --debug` over USB (no device on USB here); "New token" and "Type a token" from the page (the code paths are the ones `debug token` uses, which need the cable); the Toast itself on screen (the console is closed while it shows; its Notification is in the log); free memory with the console off, which only the serial port could say (the static figures above are the evidence); fetching a release's ELF, which needs a crash on a released version.
|
**Not checked:** `scripts/flash.sh --debug` over USB (no device on USB here); "New token" and "Type a token" from the page (the code paths are the ones `debug token` uses, which need the cable); the Toast itself on screen (the console is closed while it shows; its Notification is in the log); free memory with the console off, which only the serial port could say (the static figures above are the evidence); fetching a release's ELF, which needs a crash on a released version.
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ irc start | irc stop | irc dump | irc say <buffer> <text>
|
|||||||
install <path.ota> Update from SD
|
install <path.ota> Update from SD
|
||||||
update check | list | status | install <tag> the project's releases on Gitea
|
update check | list | status | install <tag> the project's releases on Gitea
|
||||||
sd card | sd list | cat <path> | log <text> | burst | sound on|off | short | normal
|
sd card | sd list | cat <path> | log <text> | burst | sound on|off | short | normal
|
||||||
debug status | debug off the Debug Console over Wi-Fi (Settings > Debug Console)
|
debug status | debug off [seconds] the Debug Console over Wi-Fi (Settings > Debug Console); with seconds, it comes back
|
||||||
debug on | debug token <16 to 64 characters> | debug token new (USB serial only) switch it on, set its token
|
debug on | debug token <16 to 64 characters> | debug token new (USB serial only) switch it on, set its token
|
||||||
crash abort|wdt crash on purpose (to test crash reports and Safe Mode)
|
crash abort|wdt crash on purpose (to test crash reports and Safe Mode)
|
||||||
wifi ip ... try <seconds> | wifi ip keep a trial IP setting: back to the previous one unless kept
|
wifi ip ... try <seconds> | wifi ip keep a trial IP setting: back to the previous one unless kept
|
||||||
@@ -105,7 +105,7 @@ In **Safe Mode** (see [Crashes and Safe Mode](/dev/debug/crashes/)) only a few r
|
|||||||
| `coredump erase` | Forgets the core dump |
|
| `coredump erase` | Forgets the core dump |
|
||||||
| `loop spin on` / `loop spin off` | Make the main loop spin without resting, to compare load and radio noise |
|
| `loop spin on` / `loop spin off` | Make the main loop spin without resting, to compare load and radio noise |
|
||||||
| `crash abort` / `crash wdt` | Crash on purpose, or hang the main loop until the watchdog fires |
|
| `crash abort` / `crash wdt` | Crash on purpose, or hang the main loop until the watchdog fires |
|
||||||
| `debug status` / `debug off` | The Debug Console: whether it's on, has a token and a client; switch it off |
|
| `debug status` / `debug off [seconds]` | The Debug Console: whether it's on, has a token and a client; switch it off. With a number of seconds, it comes back by itself after that long |
|
||||||
| `debug on` / `debug token <value>` / `debug token new` | USB serial only: switch it on (making a token if there's none), give it a token of 16 to 64 characters, or make a new one. The token is never printed |
|
| `debug on` / `debug token <value>` / `debug token new` | USB serial only: switch it on (making a token if there's none), give it a token of 16 to 64 characters, or make a new one. The token is never printed |
|
||||||
| `help` | Lists the commands |
|
| `help` | Lists the commands |
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Before version 0.12 this was a separate *Debug Build* with a token compiled in f
|
|||||||
|
|
||||||
| Row | Does |
|
| Row | Does |
|
||||||
|---|---|
|
|---|---|
|
||||||
| **Debug Console** | The switch. Switching it **on** asks first, and makes a token if there is none |
|
| **Debug Console** | The switch. Switching it **on** asks first (the question opens on *Cancel*: move to *Switch on*), and makes a token if there is none |
|
||||||
| **Connect to** | The address and port: `10.39.39.12:2323` |
|
| **Connect to** | The address and port: `10.39.39.12:2323` |
|
||||||
| **New token** | Makes another one. The old one stops working, and whoever is connected is cut off |
|
| **New token** | Makes another one. The old one stops working, and whoever is connected is cut off |
|
||||||
| **Type a token** | One of your own, of 16 to 64 characters |
|
| **Type a token** | One of your own, of 16 to 64 characters |
|
||||||
@@ -63,6 +63,7 @@ debug token <value> # give it this token: 16 to 64 characters
|
|||||||
debug token new # make a new one
|
debug token new # make a new one
|
||||||
debug status # on or off, token set or not, a client or not (the token itself is never shown)
|
debug status # on or off, token set or not, a client or not (the token itself is never shown)
|
||||||
debug off # switch it off
|
debug off # switch it off
|
||||||
|
debug off 30 # ...for 30 seconds: it comes back by itself
|
||||||
```
|
```
|
||||||
|
|
||||||
`debug on` and `debug token` work **over USB serial only**: the console cannot be used to open itself wider. `debug status` and `debug off` work from anywhere, and a screenshot taken over the console while this page is open shows the token, to someone who already had it. Your token file is made by the first build (`scripts/_docker.sh`), 32 hex digits, and is never committed.
|
`debug on` and `debug token` work **over USB serial only**: the console cannot be used to open itself wider. `debug status` and `debug off` work from anywhere, and a screenshot taken over the console while this page is open shows the token, to someone who already had it. Your token file is made by the first build (`scripts/_docker.sh`), 32 hex digits, and is never committed.
|
||||||
|
|||||||
@@ -155,6 +155,8 @@ The issue asked for a token that could be set, so that Debug Builds could be pub
|
|||||||
- **Two settings,** `DebugConsole` and `DebugToken`, with the others: validated, and invalid stored values count as missing.
|
- **Two settings,** `DebugConsole` and `DebugToken`, with the others: validated, and invalid stored values count as missing.
|
||||||
- **The console's task and ring come and go with the setting.** `Console::openRing` allocates the ring; switching off closes the socket, frees it and ends the task. `tick` follows the settings once a second, so a switch off and on again takes a second or two.
|
- **The console's task and ring come and go with the setting.** `Console::openRing` allocates the ring; switching off closes the socket, frees it and ends the task. `tick` follows the settings once a second, so a switch off and on again takes a second or two.
|
||||||
- **Measured against the two builds it replaces:** 1,881,799 bytes of flash and 54,700 of static RAM. The release build was 1,851,387 and 54,612 (so 30 KB more flash and 88 bytes more RAM); the Debug Build was 1,874,887 and 58,780 (4 KB of RAM back: the ring is no longer a static array).
|
- **Measured against the two builds it replaces:** 1,881,799 bytes of flash and 54,700 of static RAM. The release build was 1,851,387 and 54,612 (so 30 KB more flash and 88 bytes more RAM); the Debug Build was 1,874,887 and 58,780 (4 KB of RAM back: the ring is no longer a static array).
|
||||||
|
- **The listener is asked whether it listens.** The framework's `begin()` returns nothing and fails without a word; the task now checks, says so on the console, and tries again every two seconds.
|
||||||
|
- **`debug off <seconds>`** closes the console and brings it back by itself: the only way to try its closing and reopening from afar, since switching it on is for the device and the cable only.
|
||||||
- **`scripts/rdbg.py`** answers the challenge, and fetches a release's ELF from Gitea when a crash names a version that isn't in `.pio/elves/`.
|
- **`scripts/rdbg.py`** answers the challenge, and fetches a release's ELF from Gitea when a crash names a version that isn't in `.pio/elves/`.
|
||||||
|
|
||||||
### Checks
|
### Checks
|
||||||
@@ -172,6 +174,9 @@ The issue asked for a token that could be set, so that Debug Builds could be pub
|
|||||||
| Three `crash abort` in a row | **Safe Mode**, with the console reachable: `info` works, `ls /` says `not available in Safe Mode`. `rdbg.py crash` decodes the backtrace to `runCommand` at the `abort()` line. `reboot` leaves Safe Mode |
|
| Three `crash abort` in a row | **Safe Mode**, with the console reachable: `info` works, `ls /` says `not available in Safe Mode`. `rdbg.py crash` decodes the backtrace to `runCommand` at the `abort()` line. `reboot` leaves Safe Mode |
|
||||||
| An update over Wi-Fi with the console on | The setting and the token survive: the console is back by itself after the restart |
|
| An update over Wi-Fi with the console on | The setting and the token survive: the console is back by itself after the restart |
|
||||||
| `debug off` over the console | The client is dropped and port 2323 refuses connections; the update port still answers |
|
| `debug off` over the console | The client is dropped and port 2323 refuses connections; the update port still answers |
|
||||||
|
| `debug off 2`, 10 times in a row, then 15 | It came back each time, a second after the pause. Free heap dips by about 270 bytes for each connection the device closes and **is all back two minutes later** (107.2 KB before, 103.2 right after 15, 107.0 at two minutes): TCP holds a closed connection that long. Not a leak, though it looked like one for an hour |
|
||||||
| Memory, console on with a client | 108 KB free (the Debug Build it replaces: 104 KB). In Safe Mode: 178 KB free |
|
| Memory, console on with a client | 108 KB free (the Debug Build it replaces: 104 KB). In Safe Mode: 178 KB free |
|
||||||
|
|
||||||
|
**One false alarm:** after the tests the console "wouldn't come back on". It was off: the setting had been left off by `debug off`, and the page's "Switch it on?" dialog opens on **Cancel**, so Enter twice leaves it off. Nothing was lost.
|
||||||
|
|
||||||
**Not checked:** `scripts/flash.sh --debug` over USB (no device on USB here); "New token" and "Type a token" from the page (the code paths are the ones `debug token` uses, which need the cable); the Toast itself on screen (the console is closed while it shows; its Notification is in the log); free memory with the console off, which only the serial port could say (the static figures above are the evidence); fetching a release's ELF, which needs a crash on a released version.
|
**Not checked:** `scripts/flash.sh --debug` over USB (no device on USB here); "New token" and "Type a token" from the page (the code paths are the ones `debug token` uses, which need the cable); the Toast itself on screen (the console is closed while it shows; its Notification is in the log); free memory with the console off, which only the serial port could say (the static figures above are the evidence); fetching a release's ELF, which needs a crash on a released version.
|
||||||
|
|||||||
+20
-2
@@ -564,7 +564,7 @@ static const char* const kHelp =
|
|||||||
"install <path.ota> Update from SD\n"
|
"install <path.ota> Update from SD\n"
|
||||||
"update check | list | status | install <tag> the project's releases on Gitea\n"
|
"update check | list | status | install <tag> the project's releases on Gitea\n"
|
||||||
"sd card | sd list | cat <path> | log <text> | burst | sound on|off | short | normal\n"
|
"sd card | sd list | cat <path> | log <text> | burst | sound on|off | short | normal\n"
|
||||||
"debug status | debug off the Debug Console over Wi-Fi (Settings > Debug Console)\n"
|
"debug status | debug off [seconds] the Debug Console over Wi-Fi (Settings > Debug Console); with seconds, it comes back\n"
|
||||||
"debug on | debug token <16 to 64 characters> | debug token new (USB serial only) switch it on, set its token\n"
|
"debug on | debug token <16 to 64 characters> | debug token new (USB serial only) switch it on, set its token\n"
|
||||||
"crash abort|wdt crash on purpose (to test crash reports and Safe Mode)\n"
|
"crash abort|wdt crash on purpose (to test crash reports and Safe Mode)\n"
|
||||||
"wifi ip ... try <seconds> | wifi ip keep a trial IP setting: back to the previous one unless kept\n"
|
"wifi ip ... try <seconds> | wifi ip keep a trial IP setting: back to the previous one unless kept\n"
|
||||||
@@ -585,6 +585,16 @@ static bool safeModeCommand(const String& line) {
|
|||||||
line == "wifi status" || line.startsWith("wifi add ") || line.startsWith("debug ");
|
line == "wifi status" || line.startsWith("wifi add ") || line.startsWith("debug ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `debug off <seconds>`: the console closes and comes back by itself. It can't open itself wider
|
||||||
|
// that way (it was on, with the same token), and it's how its closing and reopening is tested from afar.
|
||||||
|
static bool debugResumes = false;
|
||||||
|
static uint32_t debugResumeMs = 0;
|
||||||
|
static void debugResumeStep() {
|
||||||
|
if (!debugResumes || static_cast<int32_t>(millis() - debugResumeMs) < 0) return;
|
||||||
|
debugResumes = false;
|
||||||
|
settings.setBool(Setting::DebugConsole, true);
|
||||||
|
}
|
||||||
|
|
||||||
// `debug ...`: the Debug Console's switch and token (ADR 0010). Switching it on and setting its token
|
// `debug ...`: the Debug Console's switch and token (ADR 0010). Switching it on and setting its token
|
||||||
// are for USB serial only (Q192): whoever holds the cable holds the device anyway, and the console
|
// are for USB serial only (Q192): whoever holds the cable holds the device anyway, and the console
|
||||||
// can't be used to open itself wider. The token is never printed.
|
// can't be used to open itself wider. The token is never printed.
|
||||||
@@ -595,6 +605,13 @@ static void debugCommand(const String& args, bool fromSerial) {
|
|||||||
} else if (args == "off") {
|
} else if (args == "off") {
|
||||||
settings.setBool(Setting::DebugConsole, false);
|
settings.setBool(Setting::DebugConsole, false);
|
||||||
console.println("debug: off");
|
console.println("debug: off");
|
||||||
|
} else if (args.startsWith("off ")) { // debug off <seconds>: a pause, then on again as it was
|
||||||
|
uint32_t seconds = constrain(args.substring(4).toInt(), 1, 600);
|
||||||
|
if (!settings.getBool(Setting::DebugConsole)) return (void)console.println("debug: it is off");
|
||||||
|
settings.setBool(Setting::DebugConsole, false);
|
||||||
|
debugResumeMs = millis() + seconds * 1000;
|
||||||
|
debugResumes = true;
|
||||||
|
console.printf("debug: off for %lu s\n", (unsigned long)seconds);
|
||||||
} else if (!fromSerial) {
|
} else if (!fromSerial) {
|
||||||
console.println("debug: over USB serial only (or Settings > Debug Console)");
|
console.println("debug: over USB serial only (or Settings > Debug Console)");
|
||||||
} else if (args == "on") {
|
} else if (args == "on") {
|
||||||
@@ -607,7 +624,7 @@ static void debugCommand(const String& args, bool fromSerial) {
|
|||||||
std::string token = debug::tidyToken(args.substring(6).c_str());
|
std::string token = debug::tidyToken(args.substring(6).c_str());
|
||||||
bool ok = debug::validToken(token) && settings.setString(Setting::DebugToken, token);
|
bool ok = debug::validToken(token) && settings.setString(Setting::DebugToken, token);
|
||||||
console.println(ok ? "debug: token set" : "debug: a token is 16 to 64 characters");
|
console.println(ok ? "debug: token set" : "debug: a token is 16 to 64 characters");
|
||||||
} else console.println("debug: status | off | on | token <16 to 64 characters> | token new");
|
} else console.println("debug: status | off [seconds] | on | token <16 to 64 characters> | token new");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void runCommand(String line, bool fromSerial = false) {
|
static void runCommand(String line, bool fromSerial = false) {
|
||||||
@@ -1004,6 +1021,7 @@ static void remoteCommands() {
|
|||||||
console.printf("> %s\n", remote.c_str()); // so the transcript reads the same on both ends
|
console.printf("> %s\n", remote.c_str()); // so the transcript reads the same on both ends
|
||||||
runCommand(remote.c_str());
|
runCommand(remote.c_str());
|
||||||
}
|
}
|
||||||
|
debugResumeStep();
|
||||||
for (std::string alert; debugConsole->takeAlert(alert);)
|
for (std::string alert; debugConsole->takeAlert(alert);)
|
||||||
bus.publish(Event::withText(EventType::Notification, alert.c_str(), static_cast<int32_t>(NotificationLevel::Warning)));
|
bus.publish(Event::withText(EventType::Notification, alert.c_str(), static_cast<int32_t>(NotificationLevel::Warning)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,12 +130,23 @@ void DebugConsole::taskEntry(void* self) { static_cast<DebugConsole*>(self)->lis
|
|||||||
void DebugConsole::listen() {
|
void DebugConsole::listen() {
|
||||||
{
|
{
|
||||||
NetworkServer server(kPort);
|
NetworkServer server(kPort);
|
||||||
bool listening = false;
|
bool listening = false, complained = false;
|
||||||
while (wanted_) {
|
while (wanted_) {
|
||||||
bool up = wifi_.state() == WifiController::State::Connected;
|
bool up = wifi_.state() == WifiController::State::Connected;
|
||||||
if (up && !listening) {
|
if (up && !listening) {
|
||||||
|
// begin() gives up without a word (no socket, the port taken): ask whether it
|
||||||
|
// listens, and try again rather than believe it.
|
||||||
server.begin();
|
server.begin();
|
||||||
listening = true;
|
listening = static_cast<bool>(server);
|
||||||
|
if (!listening) {
|
||||||
|
if (!complained) console.printf("debug: can't listen on port %u (errno %d), trying again\n", kPort, errno);
|
||||||
|
complained = true;
|
||||||
|
server.end(); // closes the socket a failed begin() leaves open
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||||
|
} else if (complained) {
|
||||||
|
console.printf("debug: listening on port %u\n", kPort);
|
||||||
|
complained = false;
|
||||||
|
}
|
||||||
} else if (!up && listening) {
|
} else if (!up && listening) {
|
||||||
server.end();
|
server.end();
|
||||||
listening = false;
|
listening = false;
|
||||||
@@ -152,7 +163,9 @@ void DebugConsole::listen() {
|
|||||||
}
|
}
|
||||||
if (listening) server.end();
|
if (listening) server.end();
|
||||||
}
|
}
|
||||||
// Switched off: nothing is left behind (Q189). The commands a client queued die with it.
|
// Switched off: nothing is left behind (Q189). The commands a client queued die with it. (Free
|
||||||
|
// heap dips by about 270 bytes for each connection the device closed, for two minutes: TCP keeps
|
||||||
|
// them that long. Measured over 15 switches: all of it comes back.)
|
||||||
xSemaphoreTake(lock_, portMAX_DELAY);
|
xSemaphoreTake(lock_, portMAX_DELAY);
|
||||||
commands_.clear();
|
commands_.clear();
|
||||||
xSemaphoreGive(lock_);
|
xSemaphoreGive(lock_);
|
||||||
|
|||||||
Reference in New Issue
Block a user