diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index bc002a3c..53e57c45 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -173,38 +173,51 @@ jobs: run: | EXIT_VALUE=0 - MAX_WIDTH=800 - MAX_HEIGHT=2000 + MIN_WIDTH=800 + MAX_WIDTH=2160 + # Height is enforced via 4:3 aspect ratio (height = width * 3 / 4) + ASPECT_TOLERANCE_PX=2 for FILE in $FILES; do if ! [[ $FILE =~ \.(svg|jpe?g|png)$ ]]; then continue # non-image file fi - # use imagemagick for the width - WIDTH=`magick identify -ping -format "%w" ${FILE}[0]` + # use imagemagick for width/height + DIMS=$(magick identify -ping -format "%w %h" ${FILE}[0]) + WIDTH=$(echo "$DIMS" | cut -d' ' -f1) + HEIGHT=$(echo "$DIMS" | cut -d' ' -f2) + BAD_WIDTH=false - if [[ "$WIDTH" -gt "$MAX_WIDTH" ]]; then + if [[ "$WIDTH" -lt "$MIN_WIDTH" || "$WIDTH" -gt "$MAX_WIDTH" ]]; then EXIT_VALUE=1 BAD_WIDTH=true fi - # use imagemagick for the height - HEIGHT=`magick identify -ping -format "%h" ${FILE}[0]` - BAD_HEIGHT=false - if [[ "$HEIGHT" -gt "$MAX_HEIGHT" ]]; then + # 4:3 aspect ratio check (height ~= width * 3 / 4) + EXPECTED_HEIGHT=$(( (WIDTH * 3 + 2) / 4 )) + EXPECTED_WIDTH=$(( (HEIGHT * 4 + 1) / 3 )) + BAD_ASPECT=false + if [[ "$HEIGHT" -lt $((EXPECTED_HEIGHT - ASPECT_TOLERANCE_PX)) || "$HEIGHT" -gt $((EXPECTED_HEIGHT + ASPECT_TOLERANCE_PX)) ]]; then EXIT_VALUE=1 - BAD_HEIGHT=true + BAD_ASPECT=true fi - if [[ $BAD_WIDTH = true || $BAD_HEIGHT = true ]]; then + if [[ $BAD_WIDTH = true || $BAD_ASPECT = true ]]; then echo "❌ $FILE (${WIDTH}x${HEIGHT})" if [[ $BAD_WIDTH = true ]]; then - echo " ↔️ width must be 800 pixels or less" + echo " ↔️ width must be between ${MIN_WIDTH}px and ${MAX_WIDTH}px" fi - if [[ $BAD_HEIGHT = true ]]; then - echo " ↕️ height must be 2000 pixels or less" + if [[ $BAD_ASPECT = true ]]; then + if [[ "$HEIGHT" -gt $((EXPECTED_HEIGHT + ASPECT_TOLERANCE_PX)) ]]; then + echo " ↕️ image is too tall for 4:3" + else + echo " ↔️ image is too wide for 4:3" + fi + + echo " - keep ${WIDTH}px width → height ~${EXPECTED_HEIGHT}px" + echo " - keep ${HEIGHT}px height → width ~${EXPECTED_WIDTH}px" fi else @@ -213,7 +226,7 @@ jobs: done if [[ $EXIT_VALUE = 1 ]]; then - echo "Fix these ☝️ issues by resizing each ❌ image to fit within ${MAX_WIDTH}x${MAX_HEIGHT}." + echo "Fix these ☝️ issues by resizing each ❌ image to width ${MIN_WIDTH}-${MAX_WIDTH}px and 4:3 aspect ratio." fi exit $EXIT_VALUE diff --git a/boards/dfrobot-firebeetle2-esp32c5/definition.json b/boards/dfrobot-firebeetle2-esp32c5/definition.json new file mode 100644 index 00000000..5da7dfc9 --- /dev/null +++ b/boards/dfrobot-firebeetle2-esp32c5/definition.json @@ -0,0 +1,228 @@ +{ + "boardName":"dfrobot-firebeetle2-esp32c5", + "published": true, + "mcuName":"esp32c5", + "mcuRefVoltage":3.3, + "displayName":"DFRobot FireBeetle 2 ESP32-C5", + "vendor":"DFRobot", + "productURL":"https://www.dfrobot.com/product-2965.html", + "documentationURL":"https://wiki.dfrobot.com/SKU_DFR1222_Firebeetle_2_ESP32_C5_Development_Board", + "installMethod":"web", + "installBoardName": "dfrobot_firebeetle2_esp32c5", + "esptool": { + "fileSystemSize": 131072, + "blockSize": 4096, + "baudRate": 115200, + "offset": "0x3D0000", + "chip": "esp32c5", + "flashMode": "qio", + "flashFreq": "80m", + "flashSize": "4MB", + "structure": { + "0x0": "wippersnapper.dfrobot_firebeetle2_esp32c5.littlefs.VERSION.combined.bin" + } + }, + "components":{ + "digitalPins":[ + { + "name":"D0", + "displayName":"0 (3V3_C Enable)", + "dataType":"bool", + "hasPWM":true, + "hasServo":true + }, + { + "name":"D2", + "displayName":"A1 (GPIO2)", + "dataType":"bool", + "hasPWM":true, + "hasServo":true + }, + { + "name":"D3", + "displayName":"A2 (GPIO3) - GDI#10 SD_CS", + "dataType":"bool", + "hasPWM":true, + "hasServo":true, + "isHardwired":true + }, + { + "name":"D4", + "displayName":"A3 (GPIO4)", + "dataType":"bool", + "hasPWM":true, + "hasServo":true + }, + { + "name":"D5", + "displayName":"A4 (GPIO5)", + "dataType":"bool", + "hasPWM":true, + "hasServo":true + }, + { + "name":"D6", + "displayName":"D12/A5 (GPIO6) - GDI#12 TCS", + "dataType":"bool", + "hasPWM":true, + "hasServo":true, + "isHardwired":true + }, + { + "name":"D7", + "displayName":"D11 (GPIO7) - GDI#15 INT", + "dataType":"bool", + "hasPWM":true, + "hasServo":true, + "isHardwired":true + }, + { + "name":"D8", + "displayName":"D2 (GPIO8) - GDI#7 LCD_DC", + "dataType":"bool", + "hasPWM":true, + "hasServo":true, + "isHardwired":true + }, + { + "name":"D9", + "displayName":"SDA (GPIO9) - GDI#14 SDA", + "dataType":"bool", + "hasPWM":true, + "hasServo":true, + "isHardwired":true + }, + { + "name":"D10", + "displayName":"SCL (GPIO10) - GDI#13 SCL", + "dataType":"bool", + "hasPWM":true, + "hasServo":true, + "isHardwired":true + }, + { + "name":"D11", + "displayName":"TX (GPIO11)", + "dataType":"bool", + "hasPWM":true, + "hasServo":true, + "uartTx":true + }, + { + "name":"D12", + "displayName":"RX (GPIO12)", + "dataType":"bool", + "hasPWM":true, + "hasServo":true, + "uartRx":true + }, + { + "name":"D15", + "displayName":"D13 (GPIO15) - LED/GDI#2 LCD_BL", + "dataType":"bool", + "hasPWM":true, + "hasServo":true, + "isHardwired":true + }, + { + "name":"D23", + "displayName":"SCK (GPIO23) - GDI#4 SCLK", + "dataType":"bool", + "hasPWM":true, + "hasServo":true, + "isHardwired":true + }, + { + "name":"D24", + "displayName":"MOSI (GPIO24) - GDI#5 MOSI", + "dataType":"bool", + "hasPWM":true, + "hasServo":true, + "isHardwired":true + }, + { + "name":"D25", + "displayName":"MISO (GPIO25) - GDI#6 MISO", + "dataType":"bool", + "hasPWM":true, + "hasServo":true, + "isHardwired":true + }, + { + "name":"D26", + "displayName":"D3 (GPIO26) - GDI#8 LCD_RST", + "dataType":"bool", + "hasPWM":true, + "hasServo":true, + "isHardwired":true + }, + { + "name":"D27", + "displayName":"D6 (GPIO27) - GDI#9 LCD_CS", + "dataType":"bool", + "hasPWM":true, + "hasServo":true, + "isHardwired":true + }, + { + "name":"D28", + "displayName":"D9/Boot (GPIO28)", + "dataType":"bool", + "direction":"INPUT" + } + ], + "analogPins":[ + { + "name":"A1", + "displayName":"GPIO1 (Battery Voltage)", + "dataType":"int16", + "hasPWM":true, + "hasServo":true + }, + { + "name":"A2", + "displayName":"A1 (GPIO2)", + "dataType":"int16", + "hasPWM":true, + "hasServo":true + }, + { + "name":"A3", + "displayName":"A2 (GPIO3) - GDI#10 SD_CS", + "dataType":"int16", + "hasPWM":true, + "hasServo":true, + "isHardwired":true + }, + { + "name":"A4", + "displayName":"A3 (GPIO4)", + "dataType":"int16", + "hasPWM":true, + "hasServo":true + }, + { + "name":"A5", + "displayName":"A4 (GPIO5)", + "dataType":"int16", + "hasPWM":true, + "hasServo":true + }, + { + "name":"A6", + "displayName":"D12/A5 (GPIO6) - GDI#12 TCS", + "dataType":"int16", + "hasPWM":true, + "hasServo":true, + "isHardwired":true + } + ], + "i2cPorts":[ + { + "i2cPortId":0, + "SDA":9, + "SCL":10 + } + ] + } + } diff --git a/boards/dfrobot-firebeetle2-esp32c5/image.png b/boards/dfrobot-firebeetle2-esp32c5/image.png new file mode 100644 index 00000000..bdc08186 Binary files /dev/null and b/boards/dfrobot-firebeetle2-esp32c5/image.png differ diff --git a/boards/dfrobot-firebeetle2-esp32c5/magic.json b/boards/dfrobot-firebeetle2-esp32c5/magic.json new file mode 100644 index 00000000..b37e45b9 --- /dev/null +++ b/boards/dfrobot-firebeetle2-esp32c5/magic.json @@ -0,0 +1,30 @@ +{ + "exportVersion": "1.0.0", + "exportedBy": "Adafruit", + "exportedAt": "2026-01-29T00:00:00.000Z", + "exportedFromDevice": { + "board": "dfrobot-firebeetle2-esp32c5", + "firmwareVersion": "1.0.0-beta.103" + }, + "components": [ + { + "name": "Button (Boot)", + "pinName": "D28", + "type": "push_button", + "mode": "DIGITAL", + "direction": "INPUT", + "period": 0, + "isPin": true, + "pull": "UP" + }, + { + "name": "LED (D15)", + "pinName": "D15", + "type": "led", + "mode": "DIGITAL", + "direction": "OUTPUT", + "period": 0, + "isPin": true + } + ] +}