diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/AbstractMakerBoard.java b/diylc/diylc-library/src/main/java/org/diylc/components/AbstractMakerBoard.java index c2d48946..83e27a4e 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/AbstractMakerBoard.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/AbstractMakerBoard.java @@ -810,8 +810,10 @@ public abstract class AbstractMakerBoard extends AbstractTransparentComponent traceH ? (height - traceH) / 2.0 : height * 0.15; + double antTopY = y + marginY; + double antMidY = y + height - marginY; antPath.moveTo(antPadX, antMidY); antPath.lineTo(antPadX, antTopY); antPath.lineTo(antPadX + antPadW * 0.25, antTopY); diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/micro/ESP32DevKit.java b/diylc/diylc-library/src/main/java/org/diylc/components/micro/ESP32DevKit.java index 8c0d36e4..bf82db52 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/micro/ESP32DevKit.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/micro/ESP32DevKit.java @@ -52,7 +52,7 @@ import org.diylc.core.measures.SizeUnit; import org.diylc.utils.Constants; @ComponentDescriptor(name = "ESP32 DevKit", category = "Controllers", - author = "Branislav Stojkovic", description = "ESP32 DevKit Wi-Fi and Bluetooth Development Board (30-Pin / 38-Pin)", + author = "Branislav Stojkovic", description = "ESP32 DevKit Wi-Fi and Bluetooth Development Board (30-Pin / 38-Pin / 44-Pin S3)", instanceNamePrefix = "MCU", zOrder = IDIYComponent.COMPONENT, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, enableCache = true) @@ -62,7 +62,8 @@ public class ESP32DevKit extends AbstractMakerBoard { public enum DevKitVersion { DevKit_V1_30Pin("30-Pin (DevKit V1 / DOIT)"), - DevKitC_V4_38Pin("38-Pin (DevKitC V4)"); + DevKitC_V4_38Pin("38-Pin (DevKitC V4)"), + ESP32_S3_DevKitC_44Pin("44-Pin (9-S3 DevKitC-1)"); private final String label; @@ -103,6 +104,18 @@ public class ESP32DevKit extends AbstractMakerBoard { // Common Spacing public static Size ROW_SPACING = new Size(1.0d, SizeUnit.in); + // ESP32-S3 DevKitC-1 (44-Pin) Dimensions + // BOARD_LENGTH_S3 is the main PCB body only; antenna extends above separately + public static Size BOARD_WIDTH_S3 = new Size(25.40d, SizeUnit.mm); + public static Size BOARD_LENGTH_S3 = new Size(62.74d, SizeUnit.mm); + public static Size ROW_SPACING_S3 = new Size(22.86d, SizeUnit.mm); + public static Size TOP_MARGIN_S3 = new Size(1.40d, SizeUnit.mm); + public static Size BOTTOM_MARGIN_S3 = new Size(8.0d, SizeUnit.mm); + public static Size ANTENNA_WIDTH_S3 = new Size(18.0d, SizeUnit.mm); + public static Size ANTENNA_LENGTH_S3 = new Size(6.0d, SizeUnit.mm); + public static Size SHIELD_WIDTH_S3 = new Size(16.0d, SizeUnit.mm); + public static Size SHIELD_LENGTH_S3 = new Size(16.0d, SizeUnit.mm); + public static final String[] PIN_NAMES_30 = new String[] { // Left row (pins 0..14, top to bottom) "EN", "VP (GPIO36)", "VN (GPIO39)", "D34", "D35", "D32", "D33", "D25", "D26", "D27", "D14", "D12", "GND1", "D13", "3V3", @@ -121,6 +134,20 @@ public class ESP32DevKit extends AbstractMakerBoard { "GPIO15", "D1 (GPIO8)", "D0 (GPIO7)", "CLK (GPIO6)" }; + public static final String[] PIN_NAMES_S3_44 = new String[] { + // Left row (pins 0..21, top to bottom) + "3V3_1", "3V3_2", "RST", "GPIO4", "GPIO5", "GPIO6", "GPIO7", + "GPIO15", "GPIO16", "GPIO17", "GPIO18", "GPIO8", "GPIO3", + "GPIO46", "GPIO9", "GPIO10", "GPIO11", "GPIO12", "GPIO13", + "GPIO14", "5V0", "GND1", + // Right row (pins 22..43, top to bottom) + "GND2", "GPIO43 (U0TXD)", "GPIO44 (U0RXD)", "GPIO1", "GPIO2", + "GPIO42", "GPIO41", "GPIO40", "GPIO39", "GPIO38", + "GPIO37", "GPIO36", "GPIO35", "GPIO0 (BOOT)", "GPIO45", + "GPIO48", "GPIO47", "GPIO21", "GPIO20 (USB D+)", "GPIO19 (USB D-)", + "GND3", "GND4" + }; + private DevKitVersion version = DevKitVersion.DevKit_V1_30Pin; protected boolean headers = false; @@ -157,10 +184,14 @@ public class ESP32DevKit extends AbstractMakerBoard { if (index >= 0 && index < PIN_NAMES_30.length) { return PIN_NAMES_30[index]; } - } else { + } else if (version == DevKitVersion.DevKitC_V4_38Pin) { if (index >= 0 && index < PIN_NAMES_38.length) { return PIN_NAMES_38[index]; } + } else if (version == DevKitVersion.ESP32_S3_DevKitC_44Pin) { + if (index >= 0 && index < PIN_NAMES_S3_44.length) { + return PIN_NAMES_S3_44[index]; + } } return "Pin " + (index + 1); } @@ -169,9 +200,9 @@ public class ESP32DevKit extends AbstractMakerBoard { protected void updateControlPoints() { Point2D firstPoint = controlPoints[0]; double spacing = PIN_SPACING.convertToPixels(); - double rowSpacing = ROW_SPACING.convertToPixels(); // 200px (1.00") if (version == DevKitVersion.DevKit_V1_30Pin) { + double rowSpacing = ROW_SPACING.convertToPixels(); double[][] relativeOffsets = new double[30][2]; for (int i = 0; i < 15; i++) { relativeOffsets[i][0] = 0; @@ -182,7 +213,8 @@ public class ESP32DevKit extends AbstractMakerBoard { relativeOffsets[15 + i][1] = (14 - i) * spacing; } rotatePoints(firstPoint, relativeOffsets); - } else { + } else if (version == DevKitVersion.DevKitC_V4_38Pin) { + double rowSpacing = ROW_SPACING.convertToPixels(); double[][] relativeOffsets = new double[PIN_NAMES_38.length][2]; for (int i = 0; i < 19; i++) { relativeOffsets[i][0] = 0; @@ -193,6 +225,18 @@ public class ESP32DevKit extends AbstractMakerBoard { relativeOffsets[19 + i][1] = i * spacing; } rotatePoints(firstPoint, relativeOffsets); + } else if (version == DevKitVersion.ESP32_S3_DevKitC_44Pin) { + double rowSpacingS3 = ROW_SPACING_S3.convertToPixels(); + double[][] relativeOffsets = new double[PIN_NAMES_S3_44.length][2]; + for (int i = 0; i < 22; i++) { + relativeOffsets[i][0] = 0; + relativeOffsets[i][1] = i * spacing; + } + for (int i = 0; i < 22; i++) { + relativeOffsets[22 + i][0] = rowSpacingS3; + relativeOffsets[22 + i][1] = i * spacing; + } + rotatePoints(firstPoint, relativeOffsets); } } @@ -201,16 +245,33 @@ public class ESP32DevKit extends AbstractMakerBoard { Point2D p0 = controlPoints[0]; double x = p0.getX(); double y = p0.getY(); - double rowSpacing = ROW_SPACING.convertToPixels(); if (version == DevKitVersion.DevKit_V1_30Pin) { + double rowSpacing = ROW_SPACING.convertToPixels(); double boardW = BOARD_WIDTH_30.convertToPixels(); double boardH = BOARD_LENGTH_30.convertToPixels(); double topMargin = TOP_MARGIN_30.convertToPixels(); double boardX = (x + rowSpacing / 2.0) - boardW / 2.0; double boardY = y - topMargin; return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 8, 8); + } else if (version == DevKitVersion.ESP32_S3_DevKitC_44Pin) { + double rowSpacingS3 = ROW_SPACING_S3.convertToPixels(); + double boardW = BOARD_WIDTH_S3.convertToPixels(); + double boardH = BOARD_LENGTH_S3.convertToPixels(); + double antennaW = ANTENNA_WIDTH_S3.convertToPixels(); + double antennaH = ANTENNA_LENGTH_S3.convertToPixels(); + double topMargin = TOP_MARGIN_S3.convertToPixels(); + + double mainX = (x + rowSpacingS3 / 2.0) - boardW / 2.0; + double mainY = y - topMargin; + double antennaX = (x + rowSpacingS3 / 2.0) - antennaW / 2.0; + double antennaY = mainY - antennaH; + + Area bodyArea = new Area(new Rectangle2D.Double(mainX, mainY, boardW, boardH)); + bodyArea.add(new Area(new Rectangle2D.Double(antennaX, antennaY, antennaW, antennaH + 1))); + return bodyArea; } else { + double rowSpacing = ROW_SPACING.convertToPixels(); double mainW = BOARD_WIDTH_38.convertToPixels(); double mainH = MAIN_BODY_LENGTH_38.convertToPixels(); double antennaW = ANTENNA_WIDTH.convertToPixels(); @@ -243,10 +304,17 @@ public class ESP32DevKit extends AbstractMakerBoard { g2d.rotate(orientation.toRadians(), x, y); } - double rowSpacing = ROW_SPACING.convertToPixels(); Shape boardShape = getBodyShape(); Composite oldComposite = applyAlpha(g2d, componentState); + // Determine the center X for the current version + double centerX; + if (version == DevKitVersion.ESP32_S3_DevKitC_44Pin) { + centerX = x + ROW_SPACING_S3.convertToPixels() / 2.0; + } else { + centerX = x + ROW_SPACING.convertToPixels() / 2.0; + } + drawingObserver.startTracking(); if (outlineMode) { g2d.setColor(Constants.TRANSPARENT_COLOR); @@ -255,7 +323,27 @@ public class ESP32DevKit extends AbstractMakerBoard { if (version == DevKitVersion.DevKit_V1_30Pin) { g2d.setColor(bodyColor); g2d.fill(boardShape); + } else if (version == DevKitVersion.ESP32_S3_DevKitC_44Pin) { + double boardW = BOARD_WIDTH_S3.convertToPixels(); + double boardH = BOARD_LENGTH_S3.convertToPixels(); + double antennaW = ANTENNA_WIDTH_S3.convertToPixels(); + double antennaH = ANTENNA_LENGTH_S3.convertToPixels(); + double topMargin = TOP_MARGIN_S3.convertToPixels(); + + double mainX = centerX - boardW / 2.0; + double mainY = y - topMargin; + double antennaX = centerX - antennaW / 2.0; + double antennaY = mainY - antennaH; + + // Main board body (sharp rectangular edges) + g2d.setColor(bodyColor); + g2d.fill(new Rectangle2D.Double(mainX, mainY, boardW, boardH)); + + // Antenna tab in #1e1e1e + g2d.setColor(ANTENNA_BG_COLOR); + g2d.fill(new Rectangle2D.Double(antennaX, antennaY, antennaW, antennaH)); } else { + double rowSpacing = ROW_SPACING.convertToPixels(); double mainW = BOARD_WIDTH_38.convertToPixels(); double mainH = MAIN_BODY_LENGTH_38.convertToPixels(); double antennaW = ANTENNA_WIDTH.convertToPixels(); @@ -281,32 +369,32 @@ public class ESP32DevKit extends AbstractMakerBoard { g2d.draw(boardShape); if (!outlineMode) { - double antennaW = ANTENNA_WIDTH.convertToPixels(); - double antennaH = (version == DevKitVersion.DevKit_V1_30Pin) ? ANTENNA_LENGTH.convertToPixels() : ANTENNA_LENGTH_38.convertToPixels(); - double antennaX = (x + rowSpacing / 2.0) - antennaW / 2.0; - double antennaY; - double shieldY; - double shieldH = SHIELD_LENGTH.convertToPixels(); - double shieldW = SHIELD_WIDTH.convertToPixels(); - double shieldX = (x + rowSpacing / 2.0) - shieldW / 2.0; - double usbY; - double btnLeftX; - double btnRightX; - double btnY; + double shift1mm = new Size(1.0d, SizeUnit.mm).convertToPixels(); double btnW = 20; double btnH = 20; - double shift1mm = new Size(1.0d, SizeUnit.mm).convertToPixels(); if (version == DevKitVersion.DevKit_V1_30Pin) { + double rowSpacing = ROW_SPACING.convertToPixels(); double boardW = BOARD_WIDTH_30.convertToPixels(); double boardH = BOARD_LENGTH_30.convertToPixels(); double topMargin = TOP_MARGIN_30.convertToPixels(); - double boardX = (x + rowSpacing / 2.0) - boardW / 2.0; + double boardX = centerX - boardW / 2.0; double boardY = y - topMargin; - antennaY = boardY; - shieldY = boardY + antennaH; - usbY = boardY + boardH - 22; + double antennaW = ANTENNA_WIDTH.convertToPixels(); + double antennaH = ANTENNA_LENGTH.convertToPixels(); + double antennaX = centerX - antennaW / 2.0; + double antennaY = boardY; + + // Antenna (dark rectangle underneath + gold serpentine trace) + drawPcbAntenna(g2d, antennaX, antennaY, antennaW, antennaH); + + // ESP32-WROOM-32 metal shield module below antenna + double shieldW = SHIELD_WIDTH.convertToPixels(); + double shieldH = SHIELD_LENGTH.convertToPixels(); + double shieldX = centerX - shieldW / 2.0; + double shieldY = boardY + antennaH; + drawMetalConnector(g2d, shieldX, shieldY, shieldW, shieldH, "ESP32-WROOM-32"); // 4 Corner Mounting Holes (diameter 2.8mm, 0.8mm away from edges in both directions) double holeDiameter = HOLE_DIAMETER_30.convertToPixels(); @@ -322,58 +410,109 @@ public class ESP32DevKit extends AbstractMakerBoard { drawMountingHole(g2d, leftHoleX, bottomHoleY, holeDiameter); drawMountingHole(g2d, rightHoleX, bottomHoleY, holeDiameter); - btnY = boardY + boardH - 25 - shift1mm; - btnLeftX = leftHoleX + 14 + shift1mm; - btnRightX = rightHoleX - 14 - shift1mm - btnW; + // Micro-USB Jack at bottom + double usbW = 58; + double usbH = 34; + double usbX = centerX - usbW / 2.0; + double usbY = boardY + boardH - 22; + drawMetalConnector(g2d, usbX, usbY, usbW, usbH, "USB"); + + // EN & BOOT tactile buttons at bottom (flanking the Micro-USB port) + double btnY = boardY + boardH - 25 - shift1mm; + double btnLeftX = leftHoleX + 14 + shift1mm; + double btnRightX = rightHoleX - 14 - shift1mm - btnW; + drawButtons(g2d, btnLeftX, btnRightX, btnY, btnW, btnH, "EN", "BOOT"); + + // Silkscreen + g2d.setColor(SILK_COLOR); + g2d.setFont(SILK_FONT); + StringUtils.drawCenteredText(g2d, "ESP32 DevKit V1", centerX, shieldY + shieldH + 45, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + + } else if (version == DevKitVersion.ESP32_S3_DevKitC_44Pin) { + double boardW = BOARD_WIDTH_S3.convertToPixels(); + double boardH = BOARD_LENGTH_S3.convertToPixels(); + double topMargin = TOP_MARGIN_S3.convertToPixels(); + double antennaW = ANTENNA_WIDTH_S3.convertToPixels(); + double antennaH = ANTENNA_LENGTH_S3.convertToPixels(); + + double mainX = centerX - boardW / 2.0; + double mainY = y - topMargin; + double antennaX = centerX - antennaW / 2.0; + double antennaY = mainY - antennaH; + + // Antenna at top (dark rectangle + gold serpentine trace) + drawPcbAntenna(g2d, antennaX, antennaY, antennaW, antennaH); + + // ESP32-S3-WROOM-1 metal shield module below antenna + double shieldW = SHIELD_WIDTH_S3.convertToPixels(); + double shieldH = SHIELD_LENGTH_S3.convertToPixels(); + double shieldX = centerX - shieldW / 2.0; + double shieldY = mainY + 4; + drawMetalConnector(g2d, shieldX, shieldY, shieldW, shieldH, "ESP32-S3-WROOM-1"); + + // BOOT & RESET tactile buttons (per DXF: BOOT on left near pin 13/14, RESET on right near pin 20/19) + double btnY = y + 18.5 * PIN_SPACING.convertToPixels() - btnH / 2.0; + double btnLeftX = mainX + 49; + double btnRightX = mainX + boardW - 49 - btnW; + drawButtons(g2d, btnLeftX, btnRightX, btnY, btnW, btnH, "BOOT", "RST"); + + // Two USB Type-C connectors at bottom edge (UART on left, USB on right) + // Each is 7x7mm, centered 6mm from the board edge, protruding 0.5mm below + double usbSize = new Size(7.0d, SizeUnit.mm).convertToPixels(); + double usbEdgeDist = new Size(6.0d, SizeUnit.mm).convertToPixels(); + double usbOverhang = new Size(0.5d, SizeUnit.mm).convertToPixels(); + double usbLeftX = mainX + usbEdgeDist - usbSize / 2.0; + double usbRightX = mainX + boardW - usbEdgeDist - usbSize / 2.0; + double usbY = mainY + boardH - usbSize + usbOverhang; + drawMetalConnector(g2d, usbLeftX, usbY, usbSize, usbSize, "UART"); + drawMetalConnector(g2d, usbRightX, usbY, usbSize, usbSize, "USB"); + + // Silkscreen + g2d.setColor(SILK_COLOR); + g2d.setFont(SILK_FONT); + StringUtils.drawCenteredText(g2d, "ESP32-S3 DevKitC-1", centerX, shieldY + shieldH + 45, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + } else { + // 38-Pin (DevKitC V4) + double rowSpacing = ROW_SPACING.convertToPixels(); double mainW = BOARD_WIDTH_38.convertToPixels(); double mainH = MAIN_BODY_LENGTH_38.convertToPixels(); - double mainX = (x + rowSpacing / 2.0) - mainW / 2.0; + double mainX = centerX - mainW / 2.0; double mainY = (y + 18 * PIN_SPACING.convertToPixels() / 2.0) - mainH / 2.0; - antennaY = mainY - antennaH; - shieldY = mainY + 8; - usbY = mainY + mainH - 22; + double antennaW = ANTENNA_WIDTH.convertToPixels(); + double antennaH = ANTENNA_LENGTH_38.convertToPixels(); + double antennaX = centerX - antennaW / 2.0; + double antennaY = mainY - antennaH; - btnY = mainY + mainH - 25; - btnLeftX = mainX + 38; - btnRightX = mainX + mainW - 38 - btnW; + // Antenna (dark rectangle underneath + gold serpentine trace) + drawPcbAntenna(g2d, antennaX, antennaY, antennaW, antennaH); + + // ESP32-WROOM-32 metal shield module below antenna + double shieldW = SHIELD_WIDTH.convertToPixels(); + double shieldH = SHIELD_LENGTH.convertToPixels(); + double shieldX = centerX - shieldW / 2.0; + double shieldY = mainY + 8; + drawMetalConnector(g2d, shieldX, shieldY, shieldW, shieldH, "ESP32-WROOM-32"); + + // Micro-USB Jack at bottom + double usbW = 58; + double usbH = 34; + double usbX = centerX - usbW / 2.0; + double usbY = mainY + mainH - 22; + drawMetalConnector(g2d, usbX, usbY, usbW, usbH, "USB"); + + // EN & BOOT tactile buttons at bottom + double btnY = mainY + mainH - 25; + double btnLeftX = mainX + 38; + double btnRightX = mainX + mainW - 38 - btnW; + drawButtons(g2d, btnLeftX, btnRightX, btnY, btnW, btnH, "EN", "BOOT"); + + // Silkscreen + g2d.setColor(SILK_COLOR); + g2d.setFont(SILK_FONT); + StringUtils.drawCenteredText(g2d, "ESP32 DevKitC V4", centerX, shieldY + shieldH + 45, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); } - - // Antenna (dark rectangle underneath + gold serpentine trace) - drawPcbAntenna(g2d, antennaX, antennaY, antennaW, antennaH); - - // ESP32-WROOM-32 metal shield module below antenna - drawMetalConnector(g2d, shieldX, shieldY, shieldW, shieldH, "ESP32-WROOM-32"); - - // Micro-USB Jack at bottom - double usbW = 58; - double usbH = 34; - double usbX = (x + rowSpacing / 2.0) - usbW / 2.0; - drawMetalConnector(g2d, usbX, usbY, usbW, usbH, "USB"); - - // EN & BOOT tactile buttons at bottom (flanking the Micro-USB port) - g2d.setColor(BUTTON_BODY_COLOR); - g2d.fill(new RoundRectangle2D.Double(btnLeftX, btnY, btnW, btnH, 3, 3)); - g2d.fill(new RoundRectangle2D.Double(btnRightX, btnY, btnW, btnH, 3, 3)); - g2d.setColor(BUTTON_BORDER_COLOR); - g2d.draw(new RoundRectangle2D.Double(btnLeftX, btnY, btnW, btnH, 3, 3)); - g2d.draw(new RoundRectangle2D.Double(btnRightX, btnY, btnW, btnH, 3, 3)); - - // Button actuators - g2d.setColor(BUTTON_ACTUATOR_COLOR); - g2d.fillOval((int) (btnLeftX + btnW / 2.0 - 4), (int) (btnY + btnH / 2.0 - 4), 8, 8); - g2d.fillOval((int) (btnRightX + btnW / 2.0 - 4), (int) (btnY + btnH / 2.0 - 4), 8, 8); - - g2d.setColor(SILK_COLOR); - g2d.setFont(SILK_FONT_SMALL); - StringUtils.drawCenteredText(g2d, "EN", btnLeftX + btnW / 2.0, btnY - 8, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); - StringUtils.drawCenteredText(g2d, "BOOT", btnRightX + btnW / 2.0, btnY - 8, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); - - // Silkscreen - g2d.setFont(SILK_FONT); - String silkText = (version == DevKitVersion.DevKit_V1_30Pin) ? "ESP32 DevKit V1" : "ESP32 DevKitC V4"; - StringUtils.drawCenteredText(g2d, silkText, x + rowSpacing / 2.0, shieldY + shieldH + 45, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); } g2d.setTransform(oldTx); @@ -387,6 +526,29 @@ public class ESP32DevKit extends AbstractMakerBoard { g2d.setComposite(oldComposite); } + /** + * Helper to draw a pair of tactile buttons with labels. + */ + private void drawButtons(Graphics2D g2d, double btnLeftX, double btnRightX, double btnY, double btnW, double btnH, + String leftLabel, String rightLabel) { + g2d.setColor(BUTTON_BODY_COLOR); + g2d.fill(new RoundRectangle2D.Double(btnLeftX, btnY, btnW, btnH, 3, 3)); + g2d.fill(new RoundRectangle2D.Double(btnRightX, btnY, btnW, btnH, 3, 3)); + g2d.setColor(BUTTON_BORDER_COLOR); + g2d.draw(new RoundRectangle2D.Double(btnLeftX, btnY, btnW, btnH, 3, 3)); + g2d.draw(new RoundRectangle2D.Double(btnRightX, btnY, btnW, btnH, 3, 3)); + + // Button actuators + g2d.setColor(BUTTON_ACTUATOR_COLOR); + g2d.fillOval((int) (btnLeftX + btnW / 2.0 - 4), (int) (btnY + btnH / 2.0 - 4), 8, 8); + g2d.fillOval((int) (btnRightX + btnW / 2.0 - 4), (int) (btnY + btnH / 2.0 - 4), 8, 8); + + g2d.setColor(SILK_COLOR); + g2d.setFont(SILK_FONT_SMALL); + StringUtils.drawCenteredText(g2d, leftLabel, btnLeftX + btnW / 2.0, btnY - 8, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + StringUtils.drawCenteredText(g2d, rightLabel, btnRightX + btnW / 2.0, btnY - 8, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + } + @Override public void drawIcon(Graphics2D g2d, int width, int height) { g2d.setColor(ESP_BLACK); diff --git a/diylc/diylc-library/src/test/java/org/diylc/components/micro/ESP32DevKitTest.java b/diylc/diylc-library/src/test/java/org/diylc/components/micro/ESP32DevKitTest.java index 536f1805..a7f1da79 100644 --- a/diylc/diylc-library/src/test/java/org/diylc/components/micro/ESP32DevKitTest.java +++ b/diylc/diylc-library/src/test/java/org/diylc/components/micro/ESP32DevKitTest.java @@ -204,6 +204,115 @@ public class ESP32DevKitTest { Assert.assertEquals(expectedTotalLength, bounds.getHeight(), 0.1); } + @Test + public void testControlPointCountAndNamesS3_44Pin() { + ESP32DevKit devKit = new ESP32DevKit(); + devKit.setVersion(ESP32DevKit.DevKitVersion.ESP32_S3_DevKitC_44Pin); + Assert.assertEquals(ESP32DevKit.DevKitVersion.ESP32_S3_DevKitC_44Pin, devKit.getVersion()); + Assert.assertEquals(44, devKit.getControlPointCount()); + + for (int i = 0; i < devKit.getControlPointCount(); i++) { + String name = devKit.getControlPointNodeName(i); + Assert.assertNotNull("Pin " + i + " name should not be null", name); + Assert.assertFalse("Pin " + i + " name should not be empty", name.trim().isEmpty()); + } + + // Left row (0..21, top to bottom) + Assert.assertEquals("3V3_1", devKit.getControlPointNodeName(0)); + Assert.assertEquals("3V3_2", devKit.getControlPointNodeName(1)); + Assert.assertEquals("RST", devKit.getControlPointNodeName(2)); + Assert.assertEquals("GPIO4", devKit.getControlPointNodeName(3)); + Assert.assertEquals("GPIO5", devKit.getControlPointNodeName(4)); + Assert.assertEquals("GPIO6", devKit.getControlPointNodeName(5)); + Assert.assertEquals("GPIO7", devKit.getControlPointNodeName(6)); + Assert.assertEquals("GPIO15", devKit.getControlPointNodeName(7)); + Assert.assertEquals("GPIO16", devKit.getControlPointNodeName(8)); + Assert.assertEquals("GPIO17", devKit.getControlPointNodeName(9)); + Assert.assertEquals("GPIO18", devKit.getControlPointNodeName(10)); + Assert.assertEquals("GPIO8", devKit.getControlPointNodeName(11)); + Assert.assertEquals("GPIO3", devKit.getControlPointNodeName(12)); + Assert.assertEquals("GPIO46", devKit.getControlPointNodeName(13)); + Assert.assertEquals("GPIO9", devKit.getControlPointNodeName(14)); + Assert.assertEquals("GPIO10", devKit.getControlPointNodeName(15)); + Assert.assertEquals("GPIO11", devKit.getControlPointNodeName(16)); + Assert.assertEquals("GPIO12", devKit.getControlPointNodeName(17)); + Assert.assertEquals("GPIO13", devKit.getControlPointNodeName(18)); + Assert.assertEquals("GPIO14", devKit.getControlPointNodeName(19)); + Assert.assertEquals("5V0", devKit.getControlPointNodeName(20)); + Assert.assertEquals("GND1", devKit.getControlPointNodeName(21)); + + // Right row (22..43, top to bottom) + Assert.assertEquals("GND2", devKit.getControlPointNodeName(22)); + Assert.assertEquals("GPIO43 (U0TXD)", devKit.getControlPointNodeName(23)); + Assert.assertEquals("GPIO44 (U0RXD)", devKit.getControlPointNodeName(24)); + Assert.assertEquals("GPIO1", devKit.getControlPointNodeName(25)); + Assert.assertEquals("GPIO2", devKit.getControlPointNodeName(26)); + Assert.assertEquals("GPIO42", devKit.getControlPointNodeName(27)); + Assert.assertEquals("GPIO41", devKit.getControlPointNodeName(28)); + Assert.assertEquals("GPIO40", devKit.getControlPointNodeName(29)); + Assert.assertEquals("GPIO39", devKit.getControlPointNodeName(30)); + Assert.assertEquals("GPIO38", devKit.getControlPointNodeName(31)); + Assert.assertEquals("GPIO37", devKit.getControlPointNodeName(32)); + Assert.assertEquals("GPIO36", devKit.getControlPointNodeName(33)); + Assert.assertEquals("GPIO35", devKit.getControlPointNodeName(34)); + Assert.assertEquals("GPIO0 (BOOT)", devKit.getControlPointNodeName(35)); + Assert.assertEquals("GPIO45", devKit.getControlPointNodeName(36)); + Assert.assertEquals("GPIO48", devKit.getControlPointNodeName(37)); + Assert.assertEquals("GPIO47", devKit.getControlPointNodeName(38)); + Assert.assertEquals("GPIO21", devKit.getControlPointNodeName(39)); + Assert.assertEquals("GPIO20 (USB D+)", devKit.getControlPointNodeName(40)); + Assert.assertEquals("GPIO19 (USB D-)", devKit.getControlPointNodeName(41)); + Assert.assertEquals("GND3", devKit.getControlPointNodeName(42)); + Assert.assertEquals("GND4", devKit.getControlPointNodeName(43)); + } + + @Test + public void testPinGeometryAndSpacingS3_44Pin() { + ESP32DevKit devKit = new ESP32DevKit(); + devKit.setVersion(ESP32DevKit.DevKitVersion.ESP32_S3_DevKitC_44Pin); + + // Left row pitch (20px per pin = 0.10") + for (int i = 0; i < 21; i++) { + Point2D p1 = devKit.getControlPoint(i); + Point2D p2 = devKit.getControlPoint(i + 1); + Assert.assertEquals(20.0, p1.distance(p2), 0.01); + Assert.assertEquals(p1.getX(), p2.getX(), 0.01); + } + + // Right row pitch (20px per pin = 0.10") + for (int i = 22; i < 43; i++) { + Point2D p1 = devKit.getControlPoint(i); + Point2D p2 = devKit.getControlPoint(i + 1); + Assert.assertEquals(20.0, p1.distance(p2), 0.01); + Assert.assertEquals(p1.getX(), p2.getX(), 0.01); + } + + // Row spacing: 22.86mm (~0.9") between Left row and Right row + double expectedRowSpacing = new Size(22.86d, SizeUnit.mm).convertToPixels(); + Point2D pLeftTop = devKit.getControlPoint(0); + Point2D pRightTop = devKit.getControlPoint(22); + Assert.assertEquals(expectedRowSpacing, pLeftTop.distance(pRightTop), 0.01); + Assert.assertEquals(pLeftTop.getY(), pRightTop.getY(), 0.01); + } + + @Test + public void testBodyShapeDimensionsS3_44Pin() { + ESP32DevKit devKit = new ESP32DevKit(); + devKit.setVersion(ESP32DevKit.DevKitVersion.ESP32_S3_DevKitC_44Pin); + + Shape body = devKit.getBodyShape(); + Assert.assertNotNull(body); + Rectangle2D bounds = body.getBounds2D(); + + double expectedWidth = new Size(25.40d, SizeUnit.mm).convertToPixels(); + // Total height = main body (62.74mm) + antenna (6.0mm) + double expectedTotalLength = new Size(62.74d + 6.0d, SizeUnit.mm).convertToPixels(); + + // Width is the board width (antenna is 18mm, narrower than 25.4mm) + Assert.assertEquals(expectedWidth, bounds.getWidth(), 0.1); + Assert.assertEquals(expectedTotalLength, bounds.getHeight(), 1.0); + } + @Test public void testVersionSwitching() { ESP32DevKit devKit = new ESP32DevKit(); @@ -214,6 +323,11 @@ public class ESP32DevKitTest { Assert.assertEquals("5V", devKit.getControlPointNodeName(18)); Assert.assertEquals("CLK (GPIO6)", devKit.getControlPointNodeName(37)); + devKit.setVersion(ESP32DevKit.DevKitVersion.ESP32_S3_DevKitC_44Pin); + Assert.assertEquals(44, devKit.getControlPointCount()); + Assert.assertEquals("3V3_1", devKit.getControlPointNodeName(0)); + Assert.assertEquals("GND4", devKit.getControlPointNodeName(43)); + devKit.setVersion(ESP32DevKit.DevKitVersion.DevKit_V1_30Pin); Assert.assertEquals(30, devKit.getControlPointCount()); Assert.assertEquals("3V3", devKit.getControlPointNodeName(14)); @@ -231,4 +345,81 @@ public class ESP32DevKitTest { devKit.setHeaders(false); Assert.assertFalse("Headers should be false after setter", devKit.getHeaders()); } + + @Test + public void testS3RowSpacingDiffersFromOriginal() { + ESP32DevKit devKit = new ESP32DevKit(); + + // Original uses 1.0" (25.4mm) row spacing + Point2D p30_left = devKit.getControlPoint(0); + Point2D p30_right = devKit.getControlPoint(29); + double spacing30 = Math.abs(p30_right.getX() - p30_left.getX()); + + // S3 uses 22.86mm (~0.9") row spacing + devKit.setVersion(ESP32DevKit.DevKitVersion.ESP32_S3_DevKitC_44Pin); + Point2D pS3_left = devKit.getControlPoint(0); + Point2D pS3_right = devKit.getControlPoint(22); + double spacingS3 = Math.abs(pS3_right.getX() - pS3_left.getX()); + + // S3 row spacing should be narrower than original + Assert.assertTrue("S3 row spacing should be narrower than original", spacingS3 < spacing30); + + double expectedS3Spacing = new Size(22.86d, SizeUnit.mm).convertToPixels(); + Assert.assertEquals(expectedS3Spacing, spacingS3, 0.01); + } + + @Test + public void testS3BottomMargin() { + ESP32DevKit devKit = new ESP32DevKit(); + devKit.setVersion(ESP32DevKit.DevKitVersion.ESP32_S3_DevKitC_44Pin); + + Point2D p0 = devKit.getControlPoint(0); + Point2D p21 = devKit.getControlPoint(21); + Point2D p43 = devKit.getControlPoint(43); + + double mainY = p0.getY() - ESP32DevKit.TOP_MARGIN_S3.convertToPixels(); + double boardBottom = mainY + ESP32DevKit.BOARD_LENGTH_S3.convertToPixels(); + + double expectedBottomMargin = ESP32DevKit.BOTTOM_MARGIN_S3.convertToPixels(); + Assert.assertEquals(expectedBottomMargin, boardBottom - p21.getY(), 0.01); + Assert.assertEquals(expectedBottomMargin, boardBottom - p43.getY(), 0.01); + } + + @Test + public void testS3Drawing() { + java.awt.image.BufferedImage img = new java.awt.image.BufferedImage(400, 400, java.awt.image.BufferedImage.TYPE_INT_ARGB); + java.awt.Graphics2D g2d = img.createGraphics(); + org.diylc.core.Project project = new org.diylc.core.Project(); + org.diylc.core.IDrawingObserver observer = new org.diylc.core.IDrawingObserver() { + @Override public void startTracking() {} + @Override public void stopTracking() {} + @Override public void startTrackingContinuityArea(boolean positive) {} + @Override public void stopTrackingContinuityArea() {} + @Override public boolean isTrackingContinuityArea() { return false; } + @Override public void setContinuityMarker(String marker) {} + }; + + ESP32DevKit devKit = new ESP32DevKit(); + devKit.setVersion(ESP32DevKit.DevKitVersion.ESP32_S3_DevKitC_44Pin); + devKit.setControlPoint(new Point2D.Double(100, 100), 0); + + // Normal mode with solder pads + devKit.setHeaders(false); + devKit.draw(g2d, org.diylc.core.ComponentState.NORMAL, false, project, observer); + + // Normal mode with pin headers + devKit.setHeaders(true); + devKit.draw(g2d, org.diylc.core.ComponentState.NORMAL, false, project, observer); + + // Selected mode + devKit.draw(g2d, org.diylc.core.ComponentState.SELECTED, false, project, observer); + + // Outline mode + devKit.draw(g2d, org.diylc.core.ComponentState.NORMAL, true, project, observer); + + // Icon drawing + devKit.drawIcon(g2d, 32, 32); + + g2d.dispose(); + } }