Skip to content

Commit 5a465e1

Browse files
committed
2 parents e2d86bc + 25f4038 commit 5a465e1

File tree

94 files changed

+1652
-236
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+1652
-236
lines changed

.github/scripts/get_affected.py

Lines changed: 119 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,123 @@ def detect_universal_ctags() -> bool:
222222
except Exception:
223223
return False
224224

225+
def normalize_function_signature(signature: str) -> str:
226+
"""
227+
Normalize a function signature by removing parameter names, keeping only types.
228+
229+
This handles cases where header declarations and implementations have different parameter names.
230+
Uses a simple heuristic: the last word in each parameter is typically the parameter name.
231+
232+
For example:
233+
- "ltoa(long val, char *s, int radix)" -> "ltoa(long,char *,int)"
234+
- "ltoa(long value, char *result, int base)" -> "ltoa(long,char *,int)"
235+
236+
Args:
237+
signature: The function signature string, e.g., "(long val, char *s, int radix)"
238+
239+
Returns:
240+
Normalized signature with parameter names removed, e.g., "(long,char *,int)"
241+
"""
242+
if not signature:
243+
return signature
244+
245+
# Normalize signatures: treat (void) and () as equivalent (both mean no parameters)
246+
if signature == "(void)":
247+
return "()"
248+
249+
if not (signature.startswith("(") and signature.endswith(")")):
250+
return signature
251+
252+
# Handle const qualifier at the end (e.g., "(int i) const")
253+
const_qualifier = ""
254+
if signature.endswith(" const"):
255+
signature = signature[:-6] # Remove " const"
256+
const_qualifier = " const"
257+
258+
# Extract parameter list without parentheses
259+
param_list = signature[1:-1]
260+
if not param_list.strip():
261+
return "()" + const_qualifier
262+
263+
# Split by comma and process each parameter
264+
params = []
265+
for param in param_list.split(","):
266+
param = param.strip()
267+
if not param:
268+
continue
269+
270+
# Handle default parameters (e.g., "int x = 5")
271+
if "=" in param:
272+
param = param.split("=")[0].strip()
273+
274+
# Try simple approach first: remove the last word
275+
# This works for most cases: "int x" -> "int", "MyStruct s" -> "MyStruct"
276+
import re
277+
278+
# Handle arrays first: "int arr[10]" -> "int [10]", "char *argv[]" -> "char *[]"
279+
array_match = re.match(r'^(.+?)\s+(\w+)((?:\[\d*\])+)$', param)
280+
if array_match:
281+
type_part = array_match.group(1).strip()
282+
array_brackets = array_match.group(3)
283+
params.append(type_part + array_brackets)
284+
else:
285+
# Handle function pointers: "int (*func)(int, char)" -> "int (*)(int, char)"
286+
func_ptr_match = re.match(r'^(.+?)\s*\(\s*\*\s*(\w+)\s*\)\s*\((.+?)\)\s*$', param)
287+
if func_ptr_match:
288+
return_type = func_ptr_match.group(1).strip()
289+
inner_params = func_ptr_match.group(3).strip()
290+
# Recursively normalize the inner parameters
291+
if inner_params:
292+
inner_normalized = normalize_function_signature(f"({inner_params})")
293+
inner_normalized = inner_normalized[1:-1] # Remove outer parentheses
294+
else:
295+
inner_normalized = ""
296+
params.append(f"{return_type} (*)({inner_normalized})")
297+
else:
298+
# Try simple approach: remove the last word
299+
simple_match = re.match(r'^(.+)\s+(\w+)$', param)
300+
if simple_match:
301+
# Simple case worked - just remove the last word
302+
type_part = simple_match.group(1).strip()
303+
params.append(type_part)
304+
else:
305+
# Fallback to complex regex for edge cases with pointers
306+
# First, try to match cases with pointers/references (including multiple *)
307+
# Pattern: (everything before) (one or more * or &) (space) (parameter name)
308+
m = re.match(r'^(.+?)(\s*[*&]+\s+)(\w+)$', param)
309+
if m:
310+
# Keep everything before the pointers, plus the pointers (without the space before param name)
311+
type_part = m.group(1) + m.group(2).rstrip()
312+
params.append(type_part.strip())
313+
else:
314+
# Try to match cases without space between type and pointer: "char*ptr", "char**ptr"
315+
m = re.match(r'^(.+?)([*&]+)(\w+)$', param)
316+
if m:
317+
# Keep everything before the pointers, plus the pointers
318+
type_part = m.group(1) + m.group(2)
319+
params.append(type_part.strip())
320+
else:
321+
# Single word - assume it's a type
322+
params.append(param.strip())
323+
324+
# Normalize spacing around pointers to ensure consistent output
325+
# This ensures "char *" and "char*" both become "char *"
326+
if params:
327+
last_param = params[-1]
328+
# Normalize spacing around * and & symbols
329+
# Replace multiple spaces with single space, ensure space before * and &
330+
normalized = re.sub(r'\s+', ' ', last_param) # Collapse multiple spaces
331+
normalized = re.sub(r'\s*([*&]+)', r' \1', normalized) # Ensure space before * and &
332+
normalized = re.sub(r'([*&]+)\s*', r'\1 ', normalized) # Ensure space after * and &
333+
normalized = re.sub(r'\s+', ' ', normalized).strip() # Clean up extra spaces
334+
params[-1] = normalized
335+
336+
result = "(" + ",".join(params) + ")"
337+
if const_qualifier:
338+
result += const_qualifier
339+
340+
return result
341+
225342
def build_qname_from_tag(tag: dict) -> str:
226343
"""
227344
Compose a qualified name for a function/method using scope + name + signature.
@@ -231,9 +348,8 @@ def build_qname_from_tag(tag: dict) -> str:
231348
name = tag.get("name") or ""
232349
signature = tag.get("signature") or ""
233350

234-
# Normalize signatures: treat (void) and () as equivalent (both mean no parameters)
235-
if signature == "(void)":
236-
signature = "()"
351+
# Normalize the signature to remove parameter names
352+
signature = normalize_function_signature(signature)
237353

238354
qparts = []
239355
if scope:

.github/workflows/pre-commit-status.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,24 @@ jobs:
131131
gh pr edit ${{ steps.pr-info.outputs.pr_number }} --repo ${{ github.repository }} --remove-label 'Status: Pre-commit fixes required ⚠️'
132132
env:
133133
GH_TOKEN: ${{ github.token }}
134+
135+
- name: Comment on PR about pre-commit failures
136+
if: |
137+
steps.pr-info.outputs.artifacts_found == 'true' &&
138+
steps.pr-info.outputs.pre_commit_outcome == 'failure' &&
139+
steps.pr-info.outputs.pending_commit == '0'
140+
uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1
141+
with:
142+
pr-number: ${{ steps.pr-info.outputs.pr_number }}
143+
message: |
144+
## ⚠️ Pre-commit Hooks Failed
145+
146+
Some pre-commit hooks failed and require manual fixes. Please see the detailed error report below.
147+
148+
**What to do:**
149+
1. 📋 [**View the detailed error report**](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}) to see which hooks failed
150+
2. 🔧 Fix the issues locally in your code
151+
3. 💾 Commit and push your changes
152+
4. 🔄 The hooks will run again automatically
153+
154+
**Need help?** Ask in the comments below.

boards.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36597,9 +36597,15 @@ XIAO_ESP32S3.menu.PartitionScheme.max_app_8MB.build.partitions=max_app_8MB
3659736597
XIAO_ESP32S3.menu.PartitionScheme.max_app_8MB.upload.maximum_size=8257536
3659836598
XIAO_ESP32S3.menu.PartitionScheme.tinyuf2=TinyUF2 8MB (2MB APP/3.7MB FFAT)
3659936599
XIAO_ESP32S3.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2
36600-
XIAO_ESP32S3.menu.PartitionScheme.tinyuf2.build.custom_partitions=partitions-8MB-tinyuf2
36600+
XIAO_ESP32S3.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-8MB
3660136601
XIAO_ESP32S3.menu.PartitionScheme.tinyuf2.upload.maximum_size=2097152
3660236602
XIAO_ESP32S3.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x410000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin"
36603+
XIAO_ESP32S3.menu.PartitionScheme.tinyuf2_noota=TinyUF2 8MB No OTA (4MB APP/3.7MB FFAT)
36604+
XIAO_ESP32S3.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2
36605+
XIAO_ESP32S3.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-8MB-noota
36606+
XIAO_ESP32S3.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=4194304
36607+
XIAO_ESP32S3.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x410000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin"
36608+
3660336609

3660436610
XIAO_ESP32S3.menu.CPUFreq.240=240MHz (WiFi)
3660536611
XIAO_ESP32S3.menu.CPUFreq.240.build.f_cpu=240000000L

cores/esp32/Arduino.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,12 +222,36 @@ size_t getArduinoLoopTaskStackSize(void);
222222
return sz; \
223223
}
224224

225+
#define ESP32_USB_MIDI_DEFAULT_NAME "TinyUSB MIDI"
226+
/**
227+
* @brief Set the current device name
228+
* 1. Name set via constructor (if any)
229+
* 2. Name set via SET_USB_MIDI_DEVICE_NAME() macro (if defined)
230+
* 3. Default name "TinyUSB MIDI"
231+
* If device name is set as "", it will be ignored
232+
*/
233+
#define SET_USB_MIDI_DEVICE_NAME(name) \
234+
const char *getUSBMIDIDefaultDeviceName() { \
235+
if (!name || strlen(name) == 0) { \
236+
return ESP32_USB_MIDI_DEFAULT_NAME; \
237+
} \
238+
return name; \
239+
}
240+
225241
bool shouldPrintChipDebugReport(void);
226242
#define ENABLE_CHIP_DEBUG_REPORT \
227243
bool shouldPrintChipDebugReport(void) { \
228244
return true; \
229245
}
230246

247+
// macro SET_TIME_BEFORE_STARTING_SKETCH_MS(time_ms) can set a time in milliseconds
248+
// before the sketch would start its execution. It gives the user time to open the Serial Monitor
249+
uint64_t getArduinoSetupWaitTime_ms(void);
250+
#define SET_TIME_BEFORE_STARTING_SKETCH_MS(time_ms) \
251+
uint64_t getArduinoSetupWaitTime_ms() { \
252+
return (time_ms); \
253+
}
254+
231255
// allows user to bypass esp_spiram_test()
232256
bool esp_psram_extram_test(void);
233257
#define BYPASS_SPIRAM_TEST(bypass) \

cores/esp32/esp32-hal-rmt.c

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ bool rmtDeinit(int pin) {
285285
return false;
286286
}
287287

288-
static bool _rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, bool blocking, bool loop, uint32_t timeout_ms) {
288+
static bool _rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, bool blocking, uint32_t loop, uint32_t timeout_ms) {
289289
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
290290
if (bus == NULL) {
291291
return false;
@@ -303,11 +303,21 @@ static bool _rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, bool bl
303303
}
304304
}
305305

306+
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
306307
log_v("GPIO: %d - Request: %d RMT Symbols - %s - Timeout: %d", pin, num_rmt_symbols, blocking ? "Blocking" : "Non-Blocking", timeout_ms);
307-
log_v(
308-
"GPIO: %d - Currently in Loop Mode: [%s] | Asked to Loop: %s, LoopCancel: %s", pin, bus->rmt_ch_is_looping ? "YES" : "NO", loop ? "YES" : "NO",
309-
loopCancel ? "YES" : "NO"
310-
);
308+
// loop parameter semantics:
309+
// loop == 0: no looping (single transmission)
310+
// loop == 1: infinite looping
311+
// loop > 1: transmit the data 'loop' times
312+
{
313+
char buf[17]; // placeholder for up to maximum uint32_t value (4294967295) = 10 digits + " times" (6 chars) + null terminator (17 bytes)
314+
snprintf(buf, sizeof(buf), "%lu times", loop);
315+
log_v(
316+
"GPIO: %d - Currently in Loop Mode: [%s] | Loop Request: [%s], LoopCancel: [%s]", pin, bus->rmt_ch_is_looping ? "YES" : "NO",
317+
loop == 0 ? "NO" : (loop == 1 ? "FOREVER" : buf), loopCancel ? "YES" : "NO"
318+
);
319+
}
320+
#endif
311321

312322
if ((xEventGroupGetBits(bus->rmt_events) & RMT_FLAG_TX_DONE) == 0) {
313323
log_v("GPIO %d - RMT Write still pending to be completed.", pin);
@@ -336,8 +346,8 @@ static bool _rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, bool bl
336346
bus->rmt_ch_is_looping = false;
337347
} else { // new writing | looping request
338348
// looping | Writing over a previous looping state is valid
339-
if (loop) {
340-
transmit_cfg.loop_count = -1; // enable infinite loop mode
349+
if (loop > 0) {
350+
transmit_cfg.loop_count = (loop == 1) ? -1 : loop;
341351
// keeps RMT_FLAG_TX_DONE set - it never changes
342352
} else {
343353
// looping mode never sets this flag (IDF 5.1) in the callback
@@ -348,8 +358,10 @@ static bool _rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, bool bl
348358
retCode = false;
349359
log_w("GPIO %d - RMT Transmission failed.", pin);
350360
} else { // transmit OK
351-
if (loop) {
352-
bus->rmt_ch_is_looping = true; // for ever... until a channel canceling or new writing
361+
if (loop > 0) {
362+
// rmt_ch_is_looping is used as a flag to indicate that RMT is in looping execution in order to
363+
// be canceled whenever a new _rmtWrite() is executed while it is looping
364+
bus->rmt_ch_is_looping = true;
353365
} else {
354366
if (blocking) {
355367
// wait for transmission confirmation | timeout
@@ -402,15 +414,36 @@ static bool _rmtRead(int pin, rmt_data_t *data, size_t *num_rmt_symbols, bool wa
402414
}
403415

404416
bool rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t timeout_ms) {
405-
return _rmtWrite(pin, data, num_rmt_symbols, true /*blocks*/, false /*looping*/, timeout_ms);
417+
return _rmtWrite(pin, data, num_rmt_symbols, true /*blocks*/, 0 /*looping*/, timeout_ms);
406418
}
407419

408420
bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols) {
409-
return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, false /*looping*/, 0 /*N/A*/);
421+
return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, 0 /*looping*/, 0 /*N/A*/);
410422
}
411423

412424
bool rmtWriteLooping(int pin, rmt_data_t *data, size_t num_rmt_symbols) {
413-
return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, true /*looping*/, 0 /*N/A*/);
425+
return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, 1 /*looping*/, 0 /*N/A*/);
426+
}
427+
428+
// Same as rmtWriteLooping(...) but it transmits the data a fixed number of times ("loop_count").
429+
// loop_count == 0 is invalid (no transmission); loop_count == 1 transmits once (no looping); loop_count > 1 transmits the data repeatedly (looping).
430+
bool rmtWriteRepeated(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t loop_count) {
431+
if (loop_count == 0) {
432+
log_e("RMT TX GPIO %d : Invalid loop_count (%u). Must be at least 1.", pin, loop_count);
433+
return false;
434+
}
435+
if (loop_count == 1) {
436+
// send the RMT symbols once using non-blocking write (single non-looping transmission)
437+
return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, 0 /*looping*/, 0 /*N/A*/);
438+
} else {
439+
// write the RMT symbols for loop_count times
440+
#if SOC_RMT_SUPPORT_TX_LOOP_COUNT
441+
return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, loop_count /*looping*/, 0 /*N/A*/);
442+
#else
443+
log_e("RMT TX GPIO %d : Loop Count is not supported. Writing failed.", pin);
444+
return false;
445+
#endif
446+
}
414447
}
415448

416449
bool rmtTransmitCompleted(int pin) {

cores/esp32/esp32-hal-rmt.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,9 @@ bool rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t timeou
124124
bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols);
125125

126126
/**
127-
Writing data up to the reserved memsize, looping continuously
127+
Writing data up to the reserved memsize, looping continuously (rmtWriteLooping()) or fixed
128+
number of <loop_count> times (rmtWriteRepeated())
129+
128130
<rmt_symbol> is a 32 bits structure as defined by rmt_data_t type.
129131
It is possible to use the macro RMT_SYMBOLS_OF(data), if data is an array of rmt_data_t
130132
@@ -133,9 +135,11 @@ bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols);
133135
Non-Blocking mode - returns right after execution
134136
Returns <true> on execution success, <false> otherwise
135137
136-
<bool rmtTransmitCompleted(int pin)> will return always <true> while it is looping.
138+
<bool rmtTransmitCompleted(int pin)> will return always <true> while it is looping mode.
139+
looping mode is active for rmtWriteLooping() and for rmtWriteRepeated() when loop_count > 1.
137140
*/
138141
bool rmtWriteLooping(int pin, rmt_data_t *data, size_t num_rmt_symbols);
142+
bool rmtWriteRepeated(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t loop_count);
139143

140144
/**
141145
Checks if transmission is completed and the rmtChannel ready for transmitting new data.

0 commit comments

Comments
 (0)