more components

This commit is contained in:
Bane Stojkovic
2026-08-14 14:20:38 +02:00
parent bc13f0b015
commit f210434765
18 changed files with 3180 additions and 36 deletions
@@ -0,0 +1,200 @@
/*
*
* DIY Layout Creator (DIYLC).
* Copyright (c) 2009-2025 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.components.displays;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.ObjectCache;
import org.diylc.common.Orientation;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(name = "NeoPixel Stick (8x WS2812B)", category = "Displays & Outputs",
author = "Branislav Stojkovic", description = "8-LED Addressable RGB WS2812B NeoPixel Stick",
instanceNamePrefix = "LED", zOrder = IDIYComponent.COMPONENT,
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
enableCache = true)
public class WS2812BStick extends AbstractMakerBoard {
private static final long serialVersionUID = 1L;
public static Color NEO_BLACK = Color.decode("#111111");
public static Color LED_PACKAGE = Color.decode("#FDFEFE");
public static Color LED_DIFFUSER = Color.decode("#EAECEE");
public static Size BOARD_WIDTH = new Size(53.0d, SizeUnit.mm);
public static Size BOARD_HEIGHT = new Size(10.2d, SizeUnit.mm);
public WS2812BStick() {
super();
this.bodyColor = NEO_BLACK;
updateControlPoints();
}
@Override
protected void updateControlPoints() {
Point2D firstPoint = controlPoints[0];
double spacing = PIN_SPACING.convertToPixels();
double w = BOARD_WIDTH.convertToPixels();
// 8 pins total:
// Left input header (4 pins): 0 = DIN, 1 = +5V, 2 = GND, 3 = GND
// Right output header (4 pins): 4 = DOUT, 5 = +5V, 6 = GND, 7 = GND
double[][] relativeOffsets = new double[][] {
{ 0, 0 },
{ 0, spacing },
{ 0, spacing * 2 },
{ 0, spacing * 3 },
{ w - 20, 0 },
{ w - 20, spacing },
{ w - 20, spacing * 2 },
{ w - 20, spacing * 3 }
};
rotatePoints(firstPoint, relativeOffsets);
}
@Override
public Shape getBodyShape() {
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
return new RoundRectangle2D.Double(x - 10, y - 10, boardW, boardH, 4, 4);
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
if (checkPointsClipped(g2d.getClip())) {
return;
}
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
AffineTransform oldTx = g2d.getTransform();
if (orientation != Orientation.DEFAULT) {
g2d.rotate(orientation.toRadians(), x, y);
}
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - 10;
double boardY = y - 10;
Shape boardShape = getBodyShape();
Composite oldComposite = applyAlpha(g2d, componentState);
drawingObserver.startTracking();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(boardShape);
drawingObserver.stopTracking();
g2d.setColor(getFinalBorderColor(componentState, outlineMode));
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
g2d.draw(boardShape);
if (!outlineMode) {
// 8x 5050 SMD WS2812B RGB LEDs
double startX = boardX + 45;
double ledPitch = (boardW - 90) / 7.0;
for (int i = 0; i < 8; i++) {
double lx = startX + i * ledPitch;
double ly = boardY + boardH / 2.0;
// 5050 White Package
g2d.setColor(LED_PACKAGE);
g2d.fill(new RoundRectangle2D.Double(lx - 16, ly - 16, 32, 32, 3, 3));
g2d.setColor(Color.LIGHT_GRAY);
g2d.draw(new RoundRectangle2D.Double(lx - 16, ly - 16, 32, 32, 3, 3));
// Circular milky phosphor/lens
g2d.setColor(LED_DIFFUSER);
g2d.fill(new Ellipse2D.Double(lx - 11, ly - 11, 22, 22));
// Internal tiny IC dot
g2d.setColor(Color.decode("#333333"));
g2d.fill(new Rectangle2D.Double(lx - 3, ly - 3, 6, 6));
// Bypass capacitor
if (i < 7) {
g2d.setColor(Color.decode("#8B6508"));
g2d.fill(new Rectangle2D.Double(lx + ledPitch / 2.0 - 4, ly - 14, 8, 4));
}
}
// Silk Screen Pin Labels
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT_SMALL);
StringUtils.drawCenteredText(g2d, "DI", boardX + 22, y, HorizontalAlignment.LEFT, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "5V", boardX + 22, y + PIN_SPACING.convertToPixels(), HorizontalAlignment.LEFT, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "GND", boardX + 22, y + PIN_SPACING.convertToPixels() * 2, HorizontalAlignment.LEFT, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "DO", boardX + boardW - 22, y, HorizontalAlignment.RIGHT, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "5V", boardX + boardW - 22, y + PIN_SPACING.convertToPixels(), HorizontalAlignment.RIGHT, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "GND", boardX + boardW - 22, y + PIN_SPACING.convertToPixels() * 2, HorizontalAlignment.RIGHT, VerticalAlignment.CENTER);
}
g2d.setTransform(oldTx);
// Draw input and output header pins
drawPins(g2d, 0, controlPoints.length, false, outlineMode, drawingObserver);
g2d.setComposite(oldComposite);
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.setColor(NEO_BLACK);
g2d.fill(new RoundRectangle2D.Double(2, 10, width - 4, height - 20, 2, 2));
Color[] rainbow = new Color[] { Color.RED, Color.YELLOW, Color.GREEN, Color.CYAN, Color.MAGENTA };
for (int i = 0; i < 5; i++) {
g2d.setColor(rainbow[i]);
g2d.fill(new Ellipse2D.Double(4 + i * 6, height / 2 - 2, 4, 4));
}
}
}
@@ -0,0 +1,187 @@
/*
*
* DIY Layout Creator (DIYLC).
* Copyright (c) 2009-2025 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.components.modules;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.ObjectCache;
import org.diylc.common.Orientation;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(name = "Analog Joystick (KY-023)", category = "Modules & Breakouts",
author = "Branislav Stojkovic", description = "KY-023 Dual-Axis PS2 Thumb Joystick Module with Push Switch",
instanceNamePrefix = "JOY", zOrder = IDIYComponent.COMPONENT,
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
enableCache = true)
public class AnalogJoystickKY023 extends AbstractMakerBoard {
private static final long serialVersionUID = 1L;
public static Color JOY_PCB = Color.decode("#1B2631");
public static Color POT_GREEN = Color.decode("#27AE60");
public static Color THUMB_CAP = Color.decode("#17202A");
public static Size BOARD_WIDTH = new Size(34.0d, SizeUnit.mm);
public static Size BOARD_HEIGHT = new Size(26.0d, SizeUnit.mm);
public AnalogJoystickKY023() {
super();
this.bodyColor = JOY_PCB;
updateControlPoints();
}
@Override
protected void updateControlPoints() {
Point2D firstPoint = controlPoints[0];
double spacing = PIN_SPACING.convertToPixels();
// 5-pin 0.1" header: GND, +5V, VRx, VRy, SW
double[][] relativeOffsets = new double[5][2];
for (int i = 0; i < 5; i++) {
relativeOffsets[i][0] = 0;
relativeOffsets[i][1] = i * spacing;
}
rotatePoints(firstPoint, relativeOffsets);
}
@Override
public Shape getBodyShape() {
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - boardW + 20;
double boardY = y - (boardH - 4 * PIN_SPACING.convertToPixels()) / 2.0;
return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 6, 6);
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
if (checkPointsClipped(g2d.getClip())) {
return;
}
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
AffineTransform oldTx = g2d.getTransform();
if (orientation != Orientation.DEFAULT) {
g2d.rotate(orientation.toRadians(), x, y);
}
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - boardW + 20;
double boardY = y - (boardH - 4 * PIN_SPACING.convertToPixels()) / 2.0;
Shape boardShape = getBodyShape();
Composite oldComposite = applyAlpha(g2d, componentState);
drawingObserver.startTracking();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(boardShape);
drawingObserver.stopTracking();
g2d.setColor(getFinalBorderColor(componentState, outlineMode));
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
g2d.draw(boardShape);
if (!outlineMode) {
// 4 Mounting Holes in corners
drawMountingHole(g2d, boardX + 16, boardY + 16, 16);
drawMountingHole(g2d, boardX + 16, boardY + boardH - 16, 16);
drawMountingHole(g2d, boardX + boardW - 40, boardY + 16, 16);
drawMountingHole(g2d, boardX + boardW - 40, boardY + boardH - 16, 16);
// Metal Gimbal Frame (Center)
double cx = boardX + (boardW - 30) / 2.0;
double cy = boardY + boardH / 2.0;
g2d.setColor(Color.decode("#7F8C8D"));
g2d.fill(new RoundRectangle2D.Double(cx - 50, cy - 50, 100, 100, 8, 8));
// Potentiometer cases (X and Y side blocks)
g2d.setColor(POT_GREEN);
g2d.fill(new RoundRectangle2D.Double(cx - 56, cy - 30, 12, 60, 3, 3));
g2d.fill(new RoundRectangle2D.Double(cx - 30, cy + 44, 60, 12, 3, 3));
// Circular Rubber Thumbstick Cap
g2d.setColor(THUMB_CAP);
g2d.fill(new Ellipse2D.Double(cx - 42, cy - 42, 84, 84));
g2d.setColor(Color.decode("#2C3E50"));
g2d.fill(new Ellipse2D.Double(cx - 30, cy - 30, 60, 60));
g2d.setColor(Color.decode("#111111"));
g2d.fill(new Ellipse2D.Double(cx - 20, cy - 20, 40, 40));
// Silk Screen Pin Labels
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT_SMALL);
String[] labels = new String[] { "GND", "+5V", "VRx", "VRy", "SW" };
double spacing = PIN_SPACING.convertToPixels();
for (int i = 0; i < 5; i++) {
StringUtils.drawCenteredText(g2d, labels[i], boardX + boardW - 32, y + i * spacing, HorizontalAlignment.RIGHT, VerticalAlignment.CENTER);
}
}
g2d.setTransform(oldTx);
// Draw 5 header pins
drawPins(g2d, 0, controlPoints.length, false, outlineMode, drawingObserver);
g2d.setComposite(oldComposite);
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.setColor(JOY_PCB);
g2d.fill(new RoundRectangle2D.Double(2, 2, width - 4, height - 4, 3, 3));
g2d.setColor(THUMB_CAP);
g2d.fill(new Ellipse2D.Double(width / 2.0 - 10, height / 2.0 - 10, 20, 20));
g2d.setColor(Color.BLACK);
g2d.fill(new Ellipse2D.Double(width / 2.0 - 5, height / 2.0 - 5, 10, 10));
}
}
@@ -0,0 +1,195 @@
/*
*
* DIY Layout Creator (DIYLC).
* Copyright (c) 2009-2025 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.components.modules;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.ObjectCache;
import org.diylc.common.Orientation;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(name = "Bluetooth Module (HC-05 / HC-06)", category = "Modules & Breakouts",
author = "Branislav Stojkovic", description = "HC-05 / HC-06 Serial Bluetooth SPP Module Breakout",
instanceNamePrefix = "BT", zOrder = IDIYComponent.COMPONENT,
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
enableCache = true)
public class HC05Bluetooth extends AbstractMakerBoard {
private static final long serialVersionUID = 1L;
public static Color BT_BLUE = Color.decode("#1B4F72");
public static Color DAUGHTER_BOARD = Color.decode("#154360");
public static Size BOARD_WIDTH = new Size(37.0d, SizeUnit.mm);
public static Size BOARD_HEIGHT = new Size(16.0d, SizeUnit.mm);
public HC05Bluetooth() {
super();
this.bodyColor = BT_BLUE;
updateControlPoints();
}
@Override
protected void updateControlPoints() {
Point2D firstPoint = controlPoints[0];
double spacing = PIN_SPACING.convertToPixels();
// 6-pin 0.1" header: STATE, RXD, TXD, GND, VCC, EN
double[][] relativeOffsets = new double[6][2];
for (int i = 0; i < 6; i++) {
relativeOffsets[i][0] = 0;
relativeOffsets[i][1] = i * spacing;
}
rotatePoints(firstPoint, relativeOffsets);
}
@Override
public Shape getBodyShape() {
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - boardW + 20;
double boardY = y - (boardH - 5 * PIN_SPACING.convertToPixels()) / 2.0;
return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 4, 4);
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
if (checkPointsClipped(g2d.getClip())) {
return;
}
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
AffineTransform oldTx = g2d.getTransform();
if (orientation != Orientation.DEFAULT) {
g2d.rotate(orientation.toRadians(), x, y);
}
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - boardW + 20;
double boardY = y - (boardH - 5 * PIN_SPACING.convertToPixels()) / 2.0;
Shape boardShape = getBodyShape();
Composite oldComposite = applyAlpha(g2d, componentState);
drawingObserver.startTracking();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(boardShape);
drawingObserver.stopTracking();
g2d.setColor(getFinalBorderColor(componentState, outlineMode));
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
g2d.draw(boardShape);
if (!outlineMode) {
// Bluetooth Daughterboard (HC-05 Core Module)
double dbX = boardX + 15;
double dbY = boardY + 12;
double dbW = boardW - 55;
double dbH = boardH - 24;
g2d.setColor(DAUGHTER_BOARD);
g2d.fill(new RoundRectangle2D.Double(dbX, dbY, dbW, dbH, 3, 3));
g2d.setColor(Color.decode("#2E86C1"));
g2d.draw(new RoundRectangle2D.Double(dbX, dbY, dbW, dbH, 3, 3));
// Metal RF Shield on daughterboard
g2d.setColor(METAL_SHIELD_COLOR);
g2d.fill(new RoundRectangle2D.Double(dbX + 15, dbY + 10, dbW - 40, dbH - 20, 3, 3));
g2d.setColor(METAL_SHIELD_BORDER);
g2d.draw(new RoundRectangle2D.Double(dbX + 15, dbY + 10, dbW - 40, dbH - 20, 3, 3));
g2d.setColor(Color.decode("#555555"));
g2d.setFont(SILK_FONT_SMALL);
StringUtils.drawCenteredText(g2d, "HC-05", dbX + 15 + (dbW - 40) / 2.0, dbY + dbH / 2.0, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
// Gold PCB Antenna on left tip of daughterboard
g2d.setColor(Color.decode("#D4AC0D"));
g2d.fillRect((int)(dbX + 4), (int)(dbY + 8), 8, (int)(dbH - 16));
// Tactile Key/EN Push Button (Top Right)
g2d.setColor(Color.LIGHT_GRAY);
g2d.fill(new RoundRectangle2D.Double(boardX + boardW - 35, boardY + 10, 14, 14, 2, 2));
g2d.setColor(Color.DARK_GRAY);
g2d.fill(new Ellipse2D.Double(boardX + boardW - 32, boardY + 13, 8, 8));
// Status LED (Blue, Bottom Right)
g2d.setColor(Color.CYAN);
g2d.fill(new RoundRectangle2D.Double(boardX + boardW - 34, boardY + boardH - 24, 12, 10, 2, 2));
// Silk Screen Pin Labels
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT_SMALL);
String[] labels = new String[] { "STATE", "RXD", "TXD", "GND", "VCC", "EN" };
double spacing = PIN_SPACING.convertToPixels();
for (int i = 0; i < 6; i++) {
StringUtils.drawCenteredText(g2d, labels[i], boardX + boardW - 24, y + i * spacing, HorizontalAlignment.RIGHT, VerticalAlignment.CENTER);
}
}
g2d.setTransform(oldTx);
// Draw 6 header pins
drawPins(g2d, 0, controlPoints.length, false, outlineMode, drawingObserver);
g2d.setComposite(oldComposite);
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.setColor(BT_BLUE);
g2d.fill(new RoundRectangle2D.Double(2, 4, width - 4, height - 8, 3, 3));
g2d.setColor(METAL_SHIELD_COLOR);
g2d.fillRect(6, height / 2 - 4, 10, 8);
g2d.setColor(Color.CYAN);
g2d.fill(new Ellipse2D.Double(width - 10, height / 2 - 3, 6, 6));
}
}
@@ -16,7 +16,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
* along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.components.modules;
@@ -89,33 +89,30 @@ public class L298NMotorDriver extends AbstractMakerBoard {
@Override
protected void updateControlPoints() {
Point2D firstPoint = controlPoints[0];
double termSpacing = new Size(5.08d, SizeUnit.mm).convertToPixels(); // ~40px
double termSpacing = new Size(5.08d, SizeUnit.mm).convertToPixels(); // 40px
double pinSpacing = PIN_SPACING.convertToPixels(); // 20px
double boardSizePx = BOARD_SIZE.convertToPixels();
double boardX = -45;
double[][] relativeOffsets = new double[13][2];
// Power screw terminal (0..2) at bottom-left
for (int i = 0; i < 3; i++) {
relativeOffsets[i][0] = i * termSpacing;
relativeOffsets[i][1] = 0;
}
// Motor A terminal (3..4) on left edge
double leftX = -40;
double leftY = -120;
relativeOffsets[3] = new double[] {leftX, leftY};
relativeOffsets[4] = new double[] {leftX, leftY - termSpacing};
// Motor B terminal (5..6) on right edge
double rightX = 240;
relativeOffsets[5] = new double[] {rightX, leftY};
relativeOffsets[6] = new double[] {rightX, leftY - termSpacing};
// Logic header (7..12) at bottom-right
double logicX = 120;
for (int i = 0; i < 6; i++) {
relativeOffsets[7 + i][0] = logicX + i * pinSpacing;
relativeOffsets[7 + i][1] = 0;
}
double[][] relativeOffsets = new double[][] {
// Power screw terminal (0..2) at bottom-left: 12V, GND, 5V
{ 0, 0 },
{ termSpacing, 0 },
{ termSpacing * 2, 0 },
// Motor A terminal (3..4) on left edge: OUT1, OUT2
{ boardX + 22.5, -140 },
{ boardX + 22.5, -180 },
// Motor B terminal (5..6) on right edge: OUT3, OUT4
{ boardX + boardSizePx - 22.5, -140 },
{ boardX + boardSizePx - 22.5, -180 },
// Logic header (7..12) at bottom-right: ENA, IN1, IN2, IN3, IN4, ENB
{ 140, 0 },
{ 140 + pinSpacing, 0 },
{ 140 + pinSpacing * 2, 0 },
{ 140 + pinSpacing * 3, 0 },
{ 140 + pinSpacing * 4, 0 },
{ 140 + pinSpacing * 5, 0 }
};
rotatePoints(firstPoint, relativeOffsets);
}
@@ -126,8 +123,8 @@ public class L298NMotorDriver extends AbstractMakerBoard {
double x = p0.getX();
double y = p0.getY();
double boardSizePx = BOARD_SIZE.convertToPixels();
double boardX = x - 55;
double boardY = y - boardSizePx + 20;
double boardX = x - 45;
double boardY = y - boardSizePx + 25;
return new RoundRectangle2D.Double(boardX, boardY, boardSizePx, boardSizePx, 10, 10);
}
@@ -148,8 +145,8 @@ public class L298NMotorDriver extends AbstractMakerBoard {
}
double boardSizePx = BOARD_SIZE.convertToPixels();
double boardX = x - 55;
double boardY = y - boardSizePx + 20;
double boardX = x - 45;
double boardY = y - boardSizePx + 25;
Shape boardShape = getBodyShape();
@@ -192,17 +189,27 @@ public class L298NMotorDriver extends AbstractMakerBoard {
// Green screw terminal block bodies
g2d.setColor(SCREW_TERMINAL_COLOR);
// Power terminal
g2d.fill(new RoundRectangle2D.Double(x - 15, y - 25, 110, 35, 3, 3));
// Motor A terminal
g2d.fill(new RoundRectangle2D.Double(x - 55, y - 175, 35, 75, 3, 3));
// Motor B terminal
g2d.fill(new RoundRectangle2D.Double(x + 225, y - 175, 35, 75, 3, 3));
// Power terminal (3 pins)
g2d.fill(new RoundRectangle2D.Double(x - 15, y - 25, 120, 35, 3, 3));
// Motor A terminal (2 pins)
g2d.fill(new RoundRectangle2D.Double(boardX + 5, y - 195, 35, 75, 3, 3));
// Motor B terminal (2 pins)
g2d.fill(new RoundRectangle2D.Double(boardX + boardSizePx - 40, y - 195, 35, 75, 3, 3));
// Logic header black block
g2d.setColor(HEADER_BODY_COLOR);
g2d.fill(new RoundRectangle2D.Double(x + 130, y - 10, 128, 20, 2, 2));
// Silkscreen
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT);
StringUtils.drawCenteredText(g2d, "L298N DRIVER", boardX + boardSizePx / 2.0, boardY + 30, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
// Terminal Silk labels
g2d.setFont(SILK_FONT_SMALL);
StringUtils.drawCenteredText(g2d, "12V GND 5V", x + 40, y - 32, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "OUT1/2", boardX + 50, y - 160, HorizontalAlignment.LEFT, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "OUT3/4", boardX + boardSizePx - 50, y - 160, HorizontalAlignment.RIGHT, VerticalAlignment.CENTER);
}
g2d.setTransform(oldTx);
@@ -0,0 +1,204 @@
/*
*
* DIY Layout Creator (DIYLC).
* Copyright (c) 2009-2025 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.components.modules;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.ObjectCache;
import org.diylc.common.Orientation;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(name = "Buck Converter (LM2596)", category = "Modules & Breakouts",
author = "Branislav Stojkovic", description = "LM2596 Adjustable DC-DC Step-Down Buck Converter Module",
instanceNamePrefix = "MOD", zOrder = IDIYComponent.COMPONENT,
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
enableCache = true)
public class LM2596BuckConverter extends AbstractMakerBoard {
private static final long serialVersionUID = 1L;
public static Color BUCK_BLUE = Color.decode("#1F618D");
public static Color POT_BLUE = Color.decode("#0055AA");
public static Color COPPER_COIL = Color.decode("#D35400");
public static Size BOARD_WIDTH = new Size(43.0d, SizeUnit.mm);
public static Size BOARD_HEIGHT = new Size(21.0d, SizeUnit.mm);
public LM2596BuckConverter() {
super();
this.bodyColor = BUCK_BLUE;
updateControlPoints();
}
@Override
protected void updateControlPoints() {
Point2D firstPoint = controlPoints[0];
double h = BOARD_HEIGHT.convertToPixels();
double w = BOARD_WIDTH.convertToPixels();
// 4 Solder / Screw terminal pads:
// Input (Left): 0 = IN+, 1 = IN-
// Output (Right): 2 = OUT+, 3 = OUT-
double[][] relativeOffsets = new double[][] {
{ 0, 0 }, // Pin 0: IN+
{ 0, h - 20 }, // Pin 1: IN-
{ w - 20, 0 }, // Pin 2: OUT+
{ w - 20, h - 20 } // Pin 3: OUT-
};
rotatePoints(firstPoint, relativeOffsets);
}
@Override
public Shape getBodyShape() {
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
return new RoundRectangle2D.Double(x - 10, y - 10, boardW, boardH, 6, 6);
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
if (checkPointsClipped(g2d.getClip())) {
return;
}
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
AffineTransform oldTx = g2d.getTransform();
if (orientation != Orientation.DEFAULT) {
g2d.rotate(orientation.toRadians(), x, y);
}
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - 10;
double boardY = y - 10;
Shape boardShape = getBodyShape();
Composite oldComposite = applyAlpha(g2d, componentState);
drawingObserver.startTracking();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(boardShape);
drawingObserver.stopTracking();
g2d.setColor(getFinalBorderColor(componentState, outlineMode));
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
g2d.draw(boardShape);
if (!outlineMode) {
// 2 Diagonal Mounting holes
drawMountingHole(g2d, boardX + 45, boardY + 16, 16);
drawMountingHole(g2d, boardX + boardW - 45, boardY + boardH - 16, 16);
// Input Capacitor (Left)
g2d.setColor(Color.LIGHT_GRAY);
g2d.fill(new Ellipse2D.Double(boardX + 22, boardY + boardH / 2.0 - 18, 36, 36));
g2d.setColor(Color.DARK_GRAY);
g2d.draw(new Ellipse2D.Double(boardX + 22, boardY + boardH / 2.0 - 18, 36, 36));
// Output Capacitor (Right)
g2d.setColor(Color.LIGHT_GRAY);
g2d.fill(new Ellipse2D.Double(boardX + boardW - 58, boardY + boardH / 2.0 - 18, 36, 36));
g2d.setColor(Color.DARK_GRAY);
g2d.draw(new Ellipse2D.Double(boardX + boardW - 58, boardY + boardH / 2.0 - 18, 36, 36));
// LM2596 IC (Center Left) with tab
g2d.setColor(Color.decode("#333333"));
g2d.fill(new RoundRectangle2D.Double(boardX + 70, boardY + 15, 38, 48, 3, 3));
g2d.setColor(Color.decode("#CCCCCC"));
g2d.fill(new RoundRectangle2D.Double(boardX + 70, boardY + 12, 38, 10, 2, 2));
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT_SMALL);
StringUtils.drawCenteredText(g2d, "LM2596", boardX + 89, boardY + 38, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
// Toroidal Power Inductor (Center Right)
g2d.setColor(Color.decode("#222222"));
g2d.fill(new Ellipse2D.Double(boardX + 120, boardY + boardH / 2.0 - 24, 48, 48));
g2d.setColor(COPPER_COIL);
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(3));
g2d.draw(new Ellipse2D.Double(boardX + 124, boardY + boardH / 2.0 - 20, 40, 40));
// Blue 3296W Trimmer Potentiometer (Top Center)
g2d.setColor(POT_BLUE);
g2d.fill(new RoundRectangle2D.Double(boardX + 185, boardY + 15, 45, 26, 3, 3));
// Brass screw head
g2d.setColor(Color.decode("#D4AC0D"));
g2d.fill(new Ellipse2D.Double(boardX + 190, boardY + 22, 12, 12));
g2d.setColor(Color.BLACK);
g2d.drawLine((int)(boardX + 192), (int)(boardY + 28), (int)(boardX + 200), (int)(boardY + 28));
// Silk Screen Text
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT_SMALL);
StringUtils.drawCenteredText(g2d, "IN+", boardX + 14, boardY + 10, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "IN-", boardX + 14, boardY + boardH - 10, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "OUT+", boardX + boardW - 14, boardY + 10, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "OUT-", boardX + boardW - 14, boardY + boardH - 10, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
}
g2d.setTransform(oldTx);
// Draw connection pads
drawScrewTerminals(g2d, 0, controlPoints.length, 0, outlineMode, drawingObserver);
g2d.setComposite(oldComposite);
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.setColor(BUCK_BLUE);
g2d.fill(new RoundRectangle2D.Double(2, 6, width - 4, height - 12, 3, 3));
g2d.setColor(COPPER_COIL);
g2d.fill(new Ellipse2D.Double(width / 2 - 4, height / 2 - 4, 8, 8));
g2d.setColor(POT_BLUE);
g2d.fillRect(width - 12, height / 2 - 5, 8, 10);
}
}
@@ -0,0 +1,207 @@
/*
*
* DIY Layout Creator (DIYLC).
* Copyright (c) 2009-2025 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.components.modules;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.ObjectCache;
import org.diylc.common.Orientation;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(name = "Breadboard Power Supply (MB102)", category = "Modules & Breakouts",
author = "Branislav Stojkovic", description = "MB102 Dual 3.3V/5V Breadboard Power Supply Module",
instanceNamePrefix = "PWR", zOrder = IDIYComponent.BOARD,
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
enableCache = true)
public class MB102PowerSupply extends AbstractMakerBoard {
private static final long serialVersionUID = 1L;
public static Color MB102_PCB = Color.decode("#E8E4D9");
public static Color JUMPER_YELLOW = Color.decode("#F1C40F");
public static Color SWITCH_RED = Color.decode("#E74C3C");
public static Size BOARD_WIDTH = new Size(32.0d, SizeUnit.mm);
public static Size BOARD_HEIGHT = new Size(54.0d, SizeUnit.mm);
public MB102PowerSupply() {
super();
this.bodyColor = MB102_PCB;
updateControlPoints();
}
@Override
protected void updateControlPoints() {
Point2D firstPoint = controlPoints[0];
double spacing = PIN_SPACING.convertToPixels();
// 8 output pins to plug directly into breadboard power rails:
// Top Power Rail (4 pins: 0..3): +, -, +, -
// Bottom Power Rail (4 pins: 4..7): +, -, +, - spaced 18 grid units apart
double railSpan = 18 * spacing;
double[][] relativeOffsets = new double[][] {
{ 0, 0 },
{ spacing, 0 },
{ 0, spacing },
{ spacing, spacing },
{ 0, railSpan },
{ spacing, railSpan },
{ 0, railSpan + spacing },
{ spacing, railSpan + spacing }
};
rotatePoints(firstPoint, relativeOffsets);
}
@Override
public Shape getBodyShape() {
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - boardW + PIN_SPACING.convertToPixels() + 25;
double boardY = y - 20;
return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 8, 8);
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
if (checkPointsClipped(g2d.getClip())) {
return;
}
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
AffineTransform oldTx = g2d.getTransform();
if (orientation != Orientation.DEFAULT) {
g2d.rotate(orientation.toRadians(), x, y);
}
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - boardW + PIN_SPACING.convertToPixels() + 25;
double boardY = y - 20;
Shape boardShape = getBodyShape();
Composite oldComposite = applyAlpha(g2d, componentState);
drawingObserver.startTracking();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(boardShape);
drawingObserver.stopTracking();
g2d.setColor(getFinalBorderColor(componentState, outlineMode));
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
g2d.draw(boardShape);
if (!outlineMode) {
// DC Barrel Jack (Left Edge Center)
g2d.setColor(Color.decode("#111111"));
g2d.fill(new RoundRectangle2D.Double(boardX - 8, boardY + boardH / 2.0 - 25, 45, 50, 4, 4));
g2d.setColor(Color.decode("#333333"));
g2d.draw(new RoundRectangle2D.Double(boardX - 8, boardY + boardH / 2.0 - 25, 45, 50, 4, 4));
g2d.setColor(Color.LIGHT_GRAY);
g2d.fill(new Ellipse2D.Double(boardX - 4, boardY + boardH / 2.0 - 10, 20, 20));
g2d.setColor(Color.BLACK);
g2d.fill(new Ellipse2D.Double(boardX + 1, boardY + boardH / 2.0 - 5, 10, 10));
// USB-A Connector (Center)
drawMetalConnector(g2d, boardX + 45, boardY + boardH / 2.0 - 22, 48, 44, "USB");
// Push Button Latch Switch (Left-ish)
g2d.setColor(Color.LIGHT_GRAY);
g2d.fill(new RoundRectangle2D.Double(boardX + 48, boardY + 45, 28, 28, 4, 4));
g2d.setColor(SWITCH_RED);
g2d.fill(new Ellipse2D.Double(boardX + 53, boardY + 50, 18, 18));
// Power Green LED
g2d.setColor(Color.decode("#2ECC71"));
g2d.fill(new Ellipse2D.Double(boardX + 85, boardY + 54, 10, 10));
// AMS1117 Voltage Regulators (3.3V and 5.0V)
drawChip(g2d, boardX + 110, boardY + 70, 28, 22, "3.3V");
drawChip(g2d, boardX + 110, boardY + boardH - 92, 28, 22, "5.0V");
// Voltage Selection Jumpers (Top & Bottom rail selectors)
g2d.setColor(JUMPER_YELLOW);
g2d.fill(new RoundRectangle2D.Double(boardX + boardW - 75, boardY + 20, 26, 18, 3, 3));
g2d.fill(new RoundRectangle2D.Double(boardX + boardW - 75, boardY + boardH - 38, 26, 18, 3, 3));
// Silk Screen Text
g2d.setColor(Color.DARK_GRAY);
g2d.setFont(SILK_FONT_SMALL);
StringUtils.drawCenteredText(g2d, "MB102", boardX + boardW / 2.0, boardY + boardH - 60, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "3.3V / 5V", boardX + boardW / 2.0, boardY + boardH - 48, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
// Rail markings (+ / -)
g2d.setColor(Color.RED);
g2d.drawString("+", (int)(x - 12), (int)(y + 8));
g2d.drawString("+", (int)(x - 12), (int)(y + 18 * PIN_SPACING.convertToPixels() + 8));
g2d.setColor(Color.BLUE);
g2d.drawString("-", (int)(x + PIN_SPACING.convertToPixels() + 6), (int)(y + 8));
g2d.drawString("-", (int)(x + PIN_SPACING.convertToPixels() + 6), (int)(y + 18 * PIN_SPACING.convertToPixels() + 8));
}
g2d.setTransform(oldTx);
// Draw header pins connecting to breadboard rails
drawPins(g2d, 0, controlPoints.length, false, outlineMode, drawingObserver);
g2d.setComposite(oldComposite);
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.setColor(MB102_PCB);
g2d.fill(new RoundRectangle2D.Double(4, 2, width - 8, height - 4, 3, 3));
g2d.setColor(SWITCH_RED);
g2d.fill(new Ellipse2D.Double(width / 2.0 - 4, height / 2.0 - 4, 8, 8));
g2d.setColor(Color.DARK_GRAY);
g2d.draw(new RoundRectangle2D.Double(4, 2, width - 8, height - 4, 3, 3));
}
}
@@ -0,0 +1,205 @@
/*
*
* DIY Layout Creator (DIYLC).
* Copyright (c) 2009-2025 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.components.modules;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.ObjectCache;
import org.diylc.common.Orientation;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(name = "MOSFET Driver Module (IRF520)", category = "Modules & Breakouts",
author = "Branislav Stojkovic", description = "IRF520 High-Current DC MOSFET Power Switch Driver Module",
instanceNamePrefix = "MOD", zOrder = IDIYComponent.COMPONENT,
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
enableCache = true)
public class MOSFETSwitchModule extends AbstractMakerBoard {
private static final long serialVersionUID = 1L;
public static Color MOSFET_RED = Color.decode("#922B21");
public static Size BOARD_WIDTH = new Size(33.0d, SizeUnit.mm);
public static Size BOARD_HEIGHT = new Size(24.0d, SizeUnit.mm);
public MOSFETSwitchModule() {
super();
this.bodyColor = MOSFET_RED;
updateControlPoints();
}
@Override
protected void updateControlPoints() {
Point2D firstPoint = controlPoints[0];
double spacing = PIN_SPACING.convertToPixels();
double w = BOARD_WIDTH.convertToPixels();
double h = BOARD_HEIGHT.convertToPixels();
// 7 pins total:
// Pin 0..2: Control header (SIG, VCC, GND on left)
// Pin 3, 4: VIN, GND (Power in screw terminal, top right)
// Pin 5, 6: V+, V- (Load out screw terminal, bottom right)
double[][] relativeOffsets = new double[][] {
{ 0, 0 },
{ 0, spacing },
{ 0, spacing * 2 },
{ w - 30, -spacing * 1.5 },
{ w - 30, -spacing * 0.5 },
{ w - 30, spacing * 1.5 },
{ w - 30, spacing * 2.5 }
};
rotatePoints(firstPoint, relativeOffsets);
}
@Override
public Shape getBodyShape() {
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - 15;
double boardY = y - (boardH - 2 * PIN_SPACING.convertToPixels()) / 2.0;
return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 6, 6);
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
if (checkPointsClipped(g2d.getClip())) {
return;
}
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
AffineTransform oldTx = g2d.getTransform();
if (orientation != Orientation.DEFAULT) {
g2d.rotate(orientation.toRadians(), x, y);
}
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - 15;
double boardY = y - (boardH - 2 * PIN_SPACING.convertToPixels()) / 2.0;
Shape boardShape = getBodyShape();
Composite oldComposite = applyAlpha(g2d, componentState);
drawingObserver.startTracking();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(boardShape);
drawingObserver.stopTracking();
g2d.setColor(getFinalBorderColor(componentState, outlineMode));
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
g2d.draw(boardShape);
if (!outlineMode) {
// 2 Mounting Holes
drawMountingHole(g2d, boardX + 14, boardY + 14, 14);
drawMountingHole(g2d, boardX + 14, boardY + boardH - 14, 14);
// TO-220 Power MOSFET (Center)
double mosX = boardX + 50;
double mosY = boardY + boardH / 2.0 - 24;
g2d.setColor(Color.decode("#222222"));
g2d.fill(new RoundRectangle2D.Double(mosX, mosY, 36, 48, 3, 3));
// Heatsink tab
g2d.setColor(Color.decode("#BDC3C7"));
g2d.fill(new RoundRectangle2D.Double(mosX, mosY - 10, 36, 12, 2, 2));
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT_SMALL);
StringUtils.drawCenteredText(g2d, "IRF520", mosX + 18, mosY + 24, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
// Dual Screw Terminal Blocks (Right side)
double stX = boardX + boardW - 48;
g2d.setColor(SCREW_TERMINAL_COLOR);
g2d.fill(new RoundRectangle2D.Double(stX, boardY + 8, 42, 35, 3, 3));
g2d.fill(new RoundRectangle2D.Double(stX, boardY + boardH - 43, 42, 35, 3, 3));
// Screw heads
g2d.setColor(Color.LIGHT_GRAY);
g2d.fill(new Ellipse2D.Double(stX + 8, boardY + 18, 12, 12));
g2d.fill(new Ellipse2D.Double(stX + 24, boardY + 18, 12, 12));
g2d.fill(new Ellipse2D.Double(stX + 8, boardY + boardH - 32, 12, 12));
g2d.fill(new Ellipse2D.Double(stX + 24, boardY + boardH - 32, 12, 12));
// Gate Trigger Status LED (Red)
g2d.setColor(Color.RED);
g2d.fill(new RoundRectangle2D.Double(boardX + 32, boardY + boardH / 2.0 - 5, 10, 10, 2, 2));
// Silk Screen Pin Labels
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT_SMALL);
String[] labels = new String[] { "SIG", "VCC", "GND" };
double spacing = PIN_SPACING.convertToPixels();
for (int i = 0; i < 3; i++) {
StringUtils.drawCenteredText(g2d, labels[i], x + 16, y + i * spacing, HorizontalAlignment.LEFT, VerticalAlignment.CENTER);
}
StringUtils.drawCenteredText(g2d, "VIN", stX - 10, boardY + 24, HorizontalAlignment.RIGHT, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "OUT", stX - 10, boardY + boardH - 26, HorizontalAlignment.RIGHT, VerticalAlignment.CENTER);
}
g2d.setTransform(oldTx);
// Draw control header pins and terminal contacts
drawPins(g2d, 0, 3, false, outlineMode, drawingObserver);
drawScrewTerminals(g2d, 3, 4, 0, outlineMode, drawingObserver);
g2d.setComposite(oldComposite);
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.setColor(MOSFET_RED);
g2d.fill(new RoundRectangle2D.Double(2, 4, width - 4, height - 8, 3, 3));
g2d.setColor(Color.decode("#222222"));
g2d.fillRect(width / 2 - 4, height / 2 - 6, 8, 12);
g2d.setColor(SCREW_TERMINAL_COLOR);
g2d.fillRect(width - 10, height / 2 - 6, 8, 12);
}
}
@@ -0,0 +1,181 @@
/*
*
* DIY Layout Creator (DIYLC).
* Copyright (c) 2009-2025 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.components.modules;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.ObjectCache;
import org.diylc.common.Orientation;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(name = "Boost Converter (MT3608)", category = "Modules & Breakouts",
author = "Branislav Stojkovic", description = "MT3608 2A Adjustable DC-DC Step-Up Boost Converter Module",
instanceNamePrefix = "MOD", zOrder = IDIYComponent.COMPONENT,
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
enableCache = true)
public class MT3608BoostConverter extends AbstractMakerBoard {
private static final long serialVersionUID = 1L;
public static Color BOOST_BLUE = Color.decode("#1B4F72");
public static Color POT_BLUE = Color.decode("#0055AA");
public static Size BOARD_WIDTH = new Size(36.0d, SizeUnit.mm);
public static Size BOARD_HEIGHT = new Size(17.0d, SizeUnit.mm);
public MT3608BoostConverter() {
super();
this.bodyColor = BOOST_BLUE;
updateControlPoints();
}
@Override
protected void updateControlPoints() {
Point2D firstPoint = controlPoints[0];
double h = BOARD_HEIGHT.convertToPixels();
double w = BOARD_WIDTH.convertToPixels();
// 4 Solder pads:
// Input (Left): 0 = VIN+, 1 = VIN-
// Output (Right): 2 = VOUT+, 3 = VOUT-
double[][] relativeOffsets = new double[][] {
{ 0, 0 }, // Pin 0: VIN+
{ 0, h - 20 }, // Pin 1: VIN-
{ w - 20, 0 }, // Pin 2: VOUT+
{ w - 20, h - 20 } // Pin 3: VOUT-
};
rotatePoints(firstPoint, relativeOffsets);
}
@Override
public Shape getBodyShape() {
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
return new RoundRectangle2D.Double(x - 10, y - 10, boardW, boardH, 6, 6);
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
if (checkPointsClipped(g2d.getClip())) {
return;
}
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
AffineTransform oldTx = g2d.getTransform();
if (orientation != Orientation.DEFAULT) {
g2d.rotate(orientation.toRadians(), x, y);
}
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - 10;
double boardY = y - 10;
Shape boardShape = getBodyShape();
Composite oldComposite = applyAlpha(g2d, componentState);
drawingObserver.startTracking();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(boardShape);
drawingObserver.stopTracking();
g2d.setColor(getFinalBorderColor(componentState, outlineMode));
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
g2d.draw(boardShape);
if (!outlineMode) {
// Power Inductor (Square SMD, Center Left)
g2d.setColor(Color.decode("#2C3E50"));
g2d.fill(new RoundRectangle2D.Double(boardX + 35, boardY + boardH / 2.0 - 20, 40, 40, 4, 4));
g2d.setColor(Color.LIGHT_GRAY);
g2d.setFont(SILK_FONT_SMALL);
StringUtils.drawCenteredText(g2d, "220", boardX + 55, boardY + boardH / 2.0, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
// MT3608 SOT23-6 Boost Controller IC
drawChip(g2d, boardX + 85, boardY + boardH / 2.0 - 10, 20, 20, "3608");
// Blue 3296W Trimmer Potentiometer (Right Center)
g2d.setColor(POT_BLUE);
g2d.fill(new RoundRectangle2D.Double(boardX + 115, boardY + 12, 45, 26, 3, 3));
// Brass screw head
g2d.setColor(Color.decode("#D4AC0D"));
g2d.fill(new Ellipse2D.Double(boardX + 120, boardY + 19, 12, 12));
g2d.setColor(Color.BLACK);
g2d.drawLine((int)(boardX + 122), (int)(boardY + 25), (int)(boardX + 130), (int)(boardY + 25));
// Silk Screen Text
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT_SMALL);
StringUtils.drawCenteredText(g2d, "VIN+", boardX + 15, boardY + 10, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "VIN-", boardX + 15, boardY + boardH - 10, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "VOUT+", boardX + boardW - 18, boardY + 10, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "VOUT-", boardX + boardW - 18, boardY + boardH - 10, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
}
g2d.setTransform(oldTx);
// Draw connection pads
drawScrewTerminals(g2d, 0, controlPoints.length, 0, outlineMode, drawingObserver);
g2d.setComposite(oldComposite);
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.setColor(BOOST_BLUE);
g2d.fill(new RoundRectangle2D.Double(2, 6, width - 4, height - 12, 3, 3));
g2d.setColor(Color.decode("#2C3E50"));
g2d.fillRect(6, height / 2 - 4, 8, 8);
g2d.setColor(POT_BLUE);
g2d.fillRect(width - 12, height / 2 - 4, 8, 8);
}
}
@@ -0,0 +1,201 @@
/*
*
* DIY Layout Creator (DIYLC).
* Copyright (c) 2009-2025 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.components.modules;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.ObjectCache;
import org.diylc.common.Orientation;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(name = "2.4GHz RF Transceiver (NRF24L01+)", category = "Modules & Breakouts",
author = "Branislav Stojkovic", description = "NRF24L01+ 2.4GHz Wireless SPI Transceiver Module",
instanceNamePrefix = "RF", zOrder = IDIYComponent.COMPONENT,
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
enableCache = true)
public class NRF24L01Transceiver extends AbstractMakerBoard {
private static final long serialVersionUID = 1L;
public static Color NRF_PCB = Color.decode("#111111");
public static Color ANTENNA_GOLD = Color.decode("#D4AC0D");
public static Size BOARD_WIDTH = new Size(29.0d, SizeUnit.mm);
public static Size BOARD_HEIGHT = new Size(15.2d, SizeUnit.mm);
public NRF24L01Transceiver() {
super();
this.bodyColor = NRF_PCB;
updateControlPoints();
}
@Override
protected void updateControlPoints() {
Point2D firstPoint = controlPoints[0];
double spacing = PIN_SPACING.convertToPixels();
// 2x4 Header (8 pins):
// Row 1 (Top): 0 = GND, 1 = CE, 2 = SCK, 3 = MISO
// Row 2 (Bottom): 4 = VCC (3.3V), 5 = CSN, 6 = MOSI, 7 = IRQ
double[][] relativeOffsets = new double[][] {
{ 0, 0 },
{ spacing, 0 },
{ spacing * 2, 0 },
{ spacing * 3, 0 },
{ 0, spacing },
{ spacing, spacing },
{ spacing * 2, spacing },
{ spacing * 3, spacing }
};
rotatePoints(firstPoint, relativeOffsets);
}
@Override
public Shape getBodyShape() {
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - boardW + 4 * PIN_SPACING.convertToPixels() + 10;
double boardY = y - (boardH - PIN_SPACING.convertToPixels()) / 2.0;
return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 4, 4);
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
if (checkPointsClipped(g2d.getClip())) {
return;
}
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
AffineTransform oldTx = g2d.getTransform();
if (orientation != Orientation.DEFAULT) {
g2d.rotate(orientation.toRadians(), x, y);
}
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - boardW + 4 * PIN_SPACING.convertToPixels() + 10;
double boardY = y - (boardH - PIN_SPACING.convertToPixels()) / 2.0;
Shape boardShape = getBodyShape();
Composite oldComposite = applyAlpha(g2d, componentState);
drawingObserver.startTracking();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(boardShape);
drawingObserver.stopTracking();
g2d.setColor(getFinalBorderColor(componentState, outlineMode));
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
g2d.draw(boardShape);
if (!outlineMode) {
// Serpentine PCB Trace Antenna (Left edge)
g2d.setColor(ANTENNA_GOLD);
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2.0f));
Path2D antenna = new Path2D.Double();
double ax = boardX + 8;
double ay = boardY + 12;
antenna.moveTo(ax, ay);
antenna.lineTo(ax + 35, ay);
antenna.lineTo(ax + 35, ay + 16);
antenna.lineTo(ax + 8, ay + 16);
antenna.lineTo(ax + 8, ay + 32);
antenna.lineTo(ax + 35, ay + 32);
antenna.lineTo(ax + 35, ay + 48);
antenna.lineTo(ax + 8, ay + 48);
antenna.lineTo(ax + 8, ay + 64);
antenna.lineTo(ax + 35, ay + 64);
g2d.draw(antenna);
// NRF24L01+ QFN IC (Center)
drawChip(g2d, boardX + 60, boardY + boardH / 2.0 - 15, 30, 30, "NRF");
// 16.000 MHz Crystal (Silver can)
g2d.setColor(Color.decode("#BDC3C7"));
g2d.fill(new RoundRectangle2D.Double(boardX + 98, boardY + boardH / 2.0 - 10, 24, 38, 4, 4));
g2d.setColor(Color.decode("#7F8C8D"));
g2d.draw(new RoundRectangle2D.Double(boardX + 98, boardY + boardH / 2.0 - 10, 24, 38, 4, 4));
// 2x4 Header Body (Black Shroud)
g2d.setColor(Color.decode("#1C1C1C"));
double spacing = PIN_SPACING.convertToPixels();
g2d.fill(new RoundRectangle2D.Double(x - 6, y - 6, spacing * 3 + 12, spacing + 12, 3, 3));
g2d.setColor(Color.decode("#333333"));
g2d.draw(new RoundRectangle2D.Double(x - 6, y - 6, spacing * 3 + 12, spacing + 12, 3, 3));
// Silk Screen Pin Labels
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT_SMALL);
StringUtils.drawCenteredText(g2d, "NRF24L01+", boardX + boardW / 2.0, boardY + 12, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
}
g2d.setTransform(oldTx);
// Draw 8 header pins
drawPins(g2d, 0, controlPoints.length, false, outlineMode, drawingObserver);
g2d.setComposite(oldComposite);
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.setColor(NRF_PCB);
g2d.fill(new RoundRectangle2D.Double(2, 4, width - 4, height - 8, 3, 3));
g2d.setColor(ANTENNA_GOLD);
g2d.drawLine(4, 8, 12, 8);
g2d.drawLine(12, 8, 12, 14);
g2d.drawLine(12, 14, 4, 14);
g2d.setColor(Color.GRAY);
g2d.fillRect(width - 12, height / 2 - 4, 8, 8);
}
}
@@ -0,0 +1,216 @@
/*
*
* DIY Layout Creator (DIYLC).
* Copyright (c) 2009-2025 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.components.modules;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.ObjectCache;
import org.diylc.common.Orientation;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(name = "16-Channel PWM/Servo Driver (PCA9685)", category = "Modules & Breakouts",
author = "Branislav Stojkovic", description = "PCA9685 16-Channel 12-Bit PWM I2C Servo Controller Board",
instanceNamePrefix = "MOD", zOrder = IDIYComponent.BOARD,
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
enableCache = true)
public class PCA9685ServoDriver extends AbstractMakerBoard {
private static final long serialVersionUID = 1L;
public static Color PCA_BLUE = Color.decode("#1B4F72");
public static Color PWM_YELLOW = Color.decode("#F1C40F");
public static Color V_RED = Color.decode("#E74C3C");
public static Color GND_BLACK = Color.decode("#1C1C1C");
public static Size BOARD_WIDTH = new Size(62.5d, SizeUnit.mm);
public static Size BOARD_HEIGHT = new Size(25.4d, SizeUnit.mm);
public PCA9685ServoDriver() {
super();
this.bodyColor = PCA_BLUE;
updateControlPoints();
}
@Override
protected void updateControlPoints() {
Point2D firstPoint = controlPoints[0];
double spacing = PIN_SPACING.convertToPixels();
double w = BOARD_WIDTH.convertToPixels();
// 60 pins total:
// Left I2C header (6 pins): 0 = GND, 1 = OE, 2 = SCL, 3 = SDA, 4 = VCC, 5 = V+
// Right I2C cascade (6 pins): 6..11
// 16 Servo Channels x 3 rows (GND, V+, PWM) = 48 pins (12..59)
double[][] relativeOffsets = new double[60][2];
// Left I2C
for (int i = 0; i < 6; i++) {
relativeOffsets[i][0] = 0;
relativeOffsets[i][1] = i * spacing;
}
// Right I2C
for (int i = 0; i < 6; i++) {
relativeOffsets[6 + i][0] = w - 20;
relativeOffsets[6 + i][1] = i * spacing;
}
// 16-Channel 3-row Servo header (Center area)
double startX = spacing * 2.5;
int idx = 12;
for (int col = 0; col < 16; col++) {
for (int row = 0; row < 3; row++) {
relativeOffsets[idx][0] = startX + col * spacing;
relativeOffsets[idx][1] = row * spacing + spacing * 1.5;
idx++;
}
}
rotatePoints(firstPoint, relativeOffsets);
}
@Override
public Shape getBodyShape() {
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - 10;
double boardY = y - (boardH - 5 * PIN_SPACING.convertToPixels()) / 2.0;
return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 6, 6);
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
if (checkPointsClipped(g2d.getClip())) {
return;
}
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
AffineTransform oldTx = g2d.getTransform();
if (orientation != Orientation.DEFAULT) {
g2d.rotate(orientation.toRadians(), x, y);
}
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - 10;
double boardY = y - (boardH - 5 * PIN_SPACING.convertToPixels()) / 2.0;
Shape boardShape = getBodyShape();
Composite oldComposite = applyAlpha(g2d, componentState);
drawingObserver.startTracking();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(boardShape);
drawingObserver.stopTracking();
g2d.setColor(getFinalBorderColor(componentState, outlineMode));
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
g2d.draw(boardShape);
if (!outlineMode) {
// 4 Mounting holes
drawMountingHole(g2d, boardX + 16, boardY + 16, 16);
drawMountingHole(g2d, boardX + 16, boardY + boardH - 16, 16);
drawMountingHole(g2d, boardX + boardW - 16, boardY + 16, 16);
drawMountingHole(g2d, boardX + boardW - 16, boardY + boardH - 16, 16);
// 2-Pin Screw Terminal Block (External Servo Power V+, GND on Top Left)
g2d.setColor(SCREW_TERMINAL_COLOR);
g2d.fill(new RoundRectangle2D.Double(boardX + 45, boardY + 8, 40, 30, 3, 3));
g2d.setColor(Color.LIGHT_GRAY);
g2d.fill(new Ellipse2D.Double(boardX + 50, boardY + 15, 12, 12));
g2d.fill(new Ellipse2D.Double(boardX + 68, boardY + 15, 12, 12));
// Large Filter Electrolytic Capacitor (Top)
g2d.setColor(Color.LIGHT_GRAY);
g2d.fill(new Ellipse2D.Double(boardX + 100, boardY + 6, 32, 32));
g2d.setColor(Color.DARK_GRAY);
g2d.draw(new Ellipse2D.Double(boardX + 100, boardY + 6, 32, 32));
// PCA9685 TSSOP-28 IC (Center)
drawChip(g2d, boardX + boardW / 2.0 - 25, boardY + 10, 50, 24, "PCA9685");
// Color-coded headers background strips for 16 channels
double spacing = PIN_SPACING.convertToPixels();
double startX = x + spacing * 2.5;
double chanW = spacing * 16;
g2d.setColor(GND_BLACK);
g2d.fill(new Rectangle2D.Double(startX - 8, y + spacing * 1.5 - 6, chanW + 6, 12));
g2d.setColor(V_RED);
g2d.fill(new Rectangle2D.Double(startX - 8, y + spacing * 2.5 - 6, chanW + 6, 12));
g2d.setColor(PWM_YELLOW);
g2d.fill(new Rectangle2D.Double(startX - 8, y + spacing * 3.5 - 6, chanW + 6, 12));
// Silk Screen Text
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT_SMALL);
StringUtils.drawCenteredText(g2d, "PCA9685 16-CH SERVO DRIVER", boardX + boardW / 2.0, boardY + boardH - 10, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
}
g2d.setTransform(oldTx);
// Draw all 60 pins
drawPins(g2d, 0, controlPoints.length, false, outlineMode, drawingObserver);
g2d.setComposite(oldComposite);
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.setColor(PCA_BLUE);
g2d.fill(new RoundRectangle2D.Double(2, 4, width - 4, height - 8, 3, 3));
g2d.setColor(PWM_YELLOW);
g2d.fillRect(6, height / 2 - 2, width - 12, 4);
g2d.setColor(V_RED);
g2d.fillRect(6, height / 2 + 2, width - 12, 4);
}
}
@@ -0,0 +1,195 @@
/*
*
* DIY Layout Creator (DIYLC).
* Copyright (c) 2009-2025 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.components.modules;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.ObjectCache;
import org.diylc.common.Orientation;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(name = "RFID Reader (RC522)", category = "Modules & Breakouts",
author = "Branislav Stojkovic", description = "MFRC522 13.56MHz RFID Reader/Writer SPI Module",
instanceNamePrefix = "RFID", zOrder = IDIYComponent.BOARD,
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
enableCache = true)
public class RFIDRC522 extends AbstractMakerBoard {
private static final long serialVersionUID = 1L;
public static Color RFID_BLUE = Color.decode("#1B4F72");
public static Color ANTENNA_GOLD = Color.decode("#D4AC0D");
public static Size BOARD_WIDTH = new Size(60.0d, SizeUnit.mm);
public static Size BOARD_HEIGHT = new Size(40.0d, SizeUnit.mm);
public RFIDRC522() {
super();
this.bodyColor = RFID_BLUE;
updateControlPoints();
}
@Override
protected void updateControlPoints() {
Point2D firstPoint = controlPoints[0];
double spacing = PIN_SPACING.convertToPixels();
// 8-pin 0.1" header: SDA, SCK, MOSI, MISO, IRQ, GND, RST, 3.3V
double[][] relativeOffsets = new double[8][2];
for (int i = 0; i < 8; i++) {
relativeOffsets[i][0] = 0;
relativeOffsets[i][1] = i * spacing;
}
rotatePoints(firstPoint, relativeOffsets);
}
@Override
public Shape getBodyShape() {
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - boardW + 20;
double boardY = y - (boardH - 7 * PIN_SPACING.convertToPixels()) / 2.0;
return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 8, 8);
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
if (checkPointsClipped(g2d.getClip())) {
return;
}
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
AffineTransform oldTx = g2d.getTransform();
if (orientation != Orientation.DEFAULT) {
g2d.rotate(orientation.toRadians(), x, y);
}
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - boardW + 20;
double boardY = y - (boardH - 7 * PIN_SPACING.convertToPixels()) / 2.0;
Shape boardShape = getBodyShape();
Composite oldComposite = applyAlpha(g2d, componentState);
drawingObserver.startTracking();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(boardShape);
drawingObserver.stopTracking();
g2d.setColor(getFinalBorderColor(componentState, outlineMode));
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
g2d.draw(boardShape);
if (!outlineMode) {
// 4 Mounting Holes
drawMountingHole(g2d, boardX + 16, boardY + 16, 16);
drawMountingHole(g2d, boardX + 16, boardY + boardH - 16, 16);
drawMountingHole(g2d, boardX + boardW - 40, boardY + 16, 16);
drawMountingHole(g2d, boardX + boardW - 40, boardY + boardH - 16, 16);
// Large PCB Loop Antenna (Left side)
double antX = boardX + 25;
double antY = boardY + 20;
double antW = boardW * 0.45;
double antH = boardH - 40;
g2d.setColor(ANTENNA_GOLD);
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
for (int i = 0; i < 4; i++) {
g2d.draw(new RoundRectangle2D.Double(antX + i * 4, antY + i * 4, antW - i * 8, antH - i * 8, 4, 4));
}
// MFRC522 QFN IC (Center Right)
double icX = boardX + boardW * 0.62;
double icY = boardY + boardH / 2.0 - 18;
drawChip(g2d, icX, icY, 36, 36, "RC522");
// 27.120 MHz Crystal
g2d.setColor(Color.decode("#BDC3C7"));
g2d.fill(new RoundRectangle2D.Double(icX - 25, icY + 6, 18, 24, 3, 3));
g2d.setColor(Color.decode("#7F8C8D"));
g2d.draw(new RoundRectangle2D.Double(icX - 25, icY + 6, 18, 24, 3, 3));
// Silk Screen Text
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT);
StringUtils.drawCenteredText(g2d, "RFID-RC522", antX + antW / 2.0, antY + antH / 2.0, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
// Silk Screen Pin Labels
g2d.setFont(SILK_FONT_SMALL);
String[] labels = new String[] { "SDA", "SCK", "MOSI", "MISO", "IRQ", "GND", "RST", "3.3V" };
double spacing = PIN_SPACING.convertToPixels();
for (int i = 0; i < 8; i++) {
StringUtils.drawCenteredText(g2d, labels[i], boardX + boardW - 24, y + i * spacing, HorizontalAlignment.RIGHT, VerticalAlignment.CENTER);
}
}
g2d.setTransform(oldTx);
// Draw 8 header pins
drawPins(g2d, 0, controlPoints.length, false, outlineMode, drawingObserver);
g2d.setComposite(oldComposite);
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.setColor(RFID_BLUE);
g2d.fill(new RoundRectangle2D.Double(2, 2, width - 4, height - 4, 3, 3));
g2d.setColor(ANTENNA_GOLD);
g2d.drawRect(6, 6, width / 2 - 4, height - 12);
g2d.drawRect(8, 8, width / 2 - 8, height - 16);
g2d.setColor(Color.BLACK);
g2d.fillRect(width - 12, height / 2 - 4, 8, 8);
}
}
@@ -0,0 +1,188 @@
/*
*
* DIY Layout Creator (DIYLC).
* Copyright (c) 2009-2025 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.components.modules;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.ObjectCache;
import org.diylc.common.Orientation;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(name = "Rotary Encoder (KY-040)", category = "Modules & Breakouts",
author = "Branislav Stojkovic", description = "KY-040 Incremental Rotary Encoder Module with Push Button",
instanceNamePrefix = "ENC", zOrder = IDIYComponent.COMPONENT,
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
enableCache = true)
public class RotaryEncoderKY040 extends AbstractMakerBoard {
private static final long serialVersionUID = 1L;
public static Color ENCODER_PCB = Color.decode("#1B2631");
public static Color METAL_SHAFT = Color.decode("#BDC3C7");
public static Size BOARD_WIDTH = new Size(32.0d, SizeUnit.mm);
public static Size BOARD_HEIGHT = new Size(19.0d, SizeUnit.mm);
public RotaryEncoderKY040() {
super();
this.bodyColor = ENCODER_PCB;
updateControlPoints();
}
@Override
protected void updateControlPoints() {
Point2D firstPoint = controlPoints[0];
double spacing = PIN_SPACING.convertToPixels();
// 5-pin 0.1" header: CLK, DT, SW, +, GND
double[][] relativeOffsets = new double[5][2];
for (int i = 0; i < 5; i++) {
relativeOffsets[i][0] = 0;
relativeOffsets[i][1] = i * spacing;
}
rotatePoints(firstPoint, relativeOffsets);
}
@Override
public Shape getBodyShape() {
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - boardW + 20;
double boardY = y - (boardH - 4 * PIN_SPACING.convertToPixels()) / 2.0;
return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 6, 6);
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
if (checkPointsClipped(g2d.getClip())) {
return;
}
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
AffineTransform oldTx = g2d.getTransform();
if (orientation != Orientation.DEFAULT) {
g2d.rotate(orientation.toRadians(), x, y);
}
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - boardW + 20;
double boardY = y - (boardH - 4 * PIN_SPACING.convertToPixels()) / 2.0;
Shape boardShape = getBodyShape();
Composite oldComposite = applyAlpha(g2d, componentState);
drawingObserver.startTracking();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(boardShape);
drawingObserver.stopTracking();
g2d.setColor(getFinalBorderColor(componentState, outlineMode));
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
g2d.draw(boardShape);
if (!outlineMode) {
// 2 Mounting Holes
drawMountingHole(g2d, boardX + 16, boardY + 16, 16);
drawMountingHole(g2d, boardX + 16, boardY + boardH - 16, 16);
// Rotary Encoder Metal Body (Center Left)
double cx = boardX + 90;
double cy = boardY + boardH / 2.0;
g2d.setColor(Color.decode("#7F8C8D"));
g2d.fill(new RoundRectangle2D.Double(cx - 36, cy - 36, 72, 72, 6, 6));
// Threaded Collar / Bushing
g2d.setColor(METAL_SHAFT);
g2d.fill(new Ellipse2D.Double(cx - 24, cy - 24, 48, 48));
g2d.setColor(Color.decode("#95A5A6"));
g2d.draw(new Ellipse2D.Double(cx - 24, cy - 24, 48, 48));
// D-Shaft (Flatted center shaft)
g2d.setColor(Color.decode("#34495E"));
g2d.fill(new Ellipse2D.Double(cx - 15, cy - 15, 30, 30));
g2d.setColor(METAL_SHAFT);
g2d.fillRect((int)(cx + 4), (int)(cy - 14), 10, 28);
// SMD Pull-up Resistors (10k)
drawChip(g2d, boardX + 145, boardY + 25, 12, 18, "");
drawChip(g2d, boardX + 145, boardY + boardH - 43, 12, 18, "");
// Silk Screen Pin Labels
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT_SMALL);
String[] labels = new String[] { "CLK", "DT", "SW", "+", "GND" };
double spacing = PIN_SPACING.convertToPixels();
for (int i = 0; i < 5; i++) {
StringUtils.drawCenteredText(g2d, labels[i], boardX + boardW - 32, y + i * spacing, HorizontalAlignment.RIGHT, VerticalAlignment.CENTER);
}
}
g2d.setTransform(oldTx);
// Draw 5 header pins
drawPins(g2d, 0, controlPoints.length, false, outlineMode, drawingObserver);
g2d.setComposite(oldComposite);
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.setColor(ENCODER_PCB);
g2d.fill(new RoundRectangle2D.Double(2, 4, width - 4, height - 8, 3, 3));
g2d.setColor(METAL_SHAFT);
g2d.fill(new Ellipse2D.Double(width / 2.0 - 8, height / 2.0 - 8, 16, 16));
g2d.setColor(Color.decode("#34495E"));
g2d.fill(new Ellipse2D.Double(width / 2.0 - 4, height / 2.0 - 4, 8, 8));
}
}
@@ -0,0 +1,183 @@
/*
*
* DIY Layout Creator (DIYLC).
* Copyright (c) 2009-2025 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.components.modules;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.ObjectCache;
import org.diylc.common.Orientation;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(name = "LiPo Charger (TP4056)", category = "Modules & Breakouts",
author = "Branislav Stojkovic", description = "TP4056 1A Li-ion Battery Charging and Protection Module",
instanceNamePrefix = "MOD", zOrder = IDIYComponent.COMPONENT,
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
enableCache = true)
public class TP4056Charger extends AbstractMakerBoard {
private static final long serialVersionUID = 1L;
public static Color TP4056_BLUE = Color.decode("#1B4F72");
public static Size BOARD_WIDTH = new Size(28.0d, SizeUnit.mm);
public static Size BOARD_HEIGHT = new Size(17.5d, SizeUnit.mm);
public TP4056Charger() {
super();
this.bodyColor = TP4056_BLUE;
updateControlPoints();
}
@Override
protected void updateControlPoints() {
Point2D firstPoint = controlPoints[0];
double spacing = PIN_SPACING.convertToPixels();
double h = BOARD_HEIGHT.convertToPixels();
double w = BOARD_WIDTH.convertToPixels();
// 6 Solder pads:
// Input (Left): 0 = IN+, 1 = IN-
// Output (Right): 2 = OUT+, 3 = B+, 4 = B-, 5 = OUT-
double[][] relativeOffsets = new double[][] {
{ 0, 0 }, // Pin 0: IN+ (Top-Left)
{ 0, h - 20 }, // Pin 1: IN- (Bottom-Left)
{ w - 20, 0 }, // Pin 2: OUT+ (Top-Right)
{ w - 20, spacing }, // Pin 3: B+
{ w - 20, h - 20 - spacing }, // Pin 4: B-
{ w - 20, h - 20 } // Pin 5: OUT- (Bottom-Right)
};
rotatePoints(firstPoint, relativeOffsets);
}
@Override
public Shape getBodyShape() {
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
return new RoundRectangle2D.Double(x - 10, y - 10, boardW, boardH, 6, 6);
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
if (checkPointsClipped(g2d.getClip())) {
return;
}
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
AffineTransform oldTx = g2d.getTransform();
if (orientation != Orientation.DEFAULT) {
g2d.rotate(orientation.toRadians(), x, y);
}
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - 10;
double boardY = y - 10;
Shape boardShape = getBodyShape();
Composite oldComposite = applyAlpha(g2d, componentState);
drawingObserver.startTracking();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(boardShape);
drawingObserver.stopTracking();
g2d.setColor(getFinalBorderColor(componentState, outlineMode));
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
g2d.draw(boardShape);
if (!outlineMode) {
// USB Type-C Connector (Left Edge)
drawMetalConnector(g2d, boardX - 6, boardY + boardH / 2.0 - 18, 32, 36, "Type-C");
// TP4056 Linear Charger IC (SOP-8)
drawChip(g2d, boardX + 60, boardY + 20, 26, 32, "4056");
// DW01A Battery Protection IC & Dual MOSFET
drawChip(g2d, boardX + 110, boardY + 22, 18, 20, "DW01");
drawChip(g2d, boardX + 110, boardY + boardH - 44, 20, 24, "8205");
// Dual Status LEDs (Red = CHRG, Blue = STDBY)
g2d.setColor(Color.RED);
g2d.fill(new RoundRectangle2D.Double(boardX + 45, boardY + 16, 8, 12, 2, 2));
g2d.setColor(Color.CYAN);
g2d.fill(new RoundRectangle2D.Double(boardX + 45, boardY + boardH - 28, 8, 12, 2, 2));
// Silk Screen Labels
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT_SMALL);
StringUtils.drawCenteredText(g2d, "IN+", boardX + 18, boardY + 12, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "IN-", boardX + 18, boardY + boardH - 12, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "OUT+", boardX + boardW - 22, boardY + 12, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "B+", boardX + boardW - 22, boardY + 32, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "B-", boardX + boardW - 22, boardY + boardH - 32, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "OUT-", boardX + boardW - 22, boardY + boardH - 12, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
}
g2d.setTransform(oldTx);
// Draw solder pads
drawScrewTerminals(g2d, 0, controlPoints.length, 0, outlineMode, drawingObserver);
g2d.setComposite(oldComposite);
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.setColor(TP4056_BLUE);
g2d.fill(new RoundRectangle2D.Double(2, 6, width - 4, height - 12, 3, 3));
g2d.setColor(Color.LIGHT_GRAY);
g2d.fillRect(2, height / 2 - 4, 8, 8);
g2d.setColor(Color.RED);
g2d.fill(new Ellipse2D.Double(14, height / 2 - 3, 6, 6));
g2d.setColor(Color.CYAN);
g2d.fill(new Ellipse2D.Double(22, height / 2 - 3, 6, 6));
}
}
@@ -0,0 +1,201 @@
/*
*
* DIY Layout Creator (DIYLC).
* Copyright (c) 2009-2025 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.components.modules;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.ObjectCache;
import org.diylc.common.Orientation;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(name = "Stepper Driver Board (ULN2003)", category = "Modules & Breakouts",
author = "Branislav Stojkovic", description = "ULN2003 Stepper Motor Driver Board with 4 Status LEDs",
instanceNamePrefix = "MOD", zOrder = IDIYComponent.BOARD,
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
enableCache = true)
public class ULN2003Driver extends AbstractMakerBoard {
private static final long serialVersionUID = 1L;
public static Color ULN_BLUE = Color.decode("#1F618D");
public static Color JST_WHITE = Color.decode("#EAEDED");
public static Color LED_YELLOW = Color.decode("#F1C40F");
public static Size BOARD_WIDTH = new Size(35.0d, SizeUnit.mm);
public static Size BOARD_HEIGHT = new Size(31.5d, SizeUnit.mm);
public ULN2003Driver() {
super();
this.bodyColor = ULN_BLUE;
updateControlPoints();
}
@Override
protected void updateControlPoints() {
Point2D firstPoint = controlPoints[0];
double spacing = PIN_SPACING.convertToPixels();
// 11 pins total:
// Pin 0..3: IN1, IN2, IN3, IN4 (Bottom left control header)
// Pin 4, 5: +5-12V, GND (Power input header)
// Pin 6..10: 5-pin JST motor socket (Top right)
double[][] relativeOffsets = new double[][] {
{ 0, 0 },
{ spacing, 0 },
{ spacing * 2, 0 },
{ spacing * 3, 0 },
{ spacing * 5, 0 },
{ spacing * 6, 0 },
// JST motor connector pins
{ spacing * 4, -spacing * 7 },
{ spacing * 5, -spacing * 7 },
{ spacing * 6, -spacing * 7 },
{ spacing * 7, -spacing * 7 },
{ spacing * 8, -spacing * 7 }
};
rotatePoints(firstPoint, relativeOffsets);
}
@Override
public Shape getBodyShape() {
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - 20;
double boardY = y - boardH + 20;
return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 6, 6);
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
if (checkPointsClipped(g2d.getClip())) {
return;
}
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
AffineTransform oldTx = g2d.getTransform();
if (orientation != Orientation.DEFAULT) {
g2d.rotate(orientation.toRadians(), x, y);
}
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - 20;
double boardY = y - boardH + 20;
Shape boardShape = getBodyShape();
Composite oldComposite = applyAlpha(g2d, componentState);
drawingObserver.startTracking();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(boardShape);
drawingObserver.stopTracking();
g2d.setColor(getFinalBorderColor(componentState, outlineMode));
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
g2d.draw(boardShape);
if (!outlineMode) {
// 4 Mounting Holes in corners
drawMountingHole(g2d, boardX + 16, boardY + 16, 16);
drawMountingHole(g2d, boardX + 16, boardY + boardH - 16, 16);
drawMountingHole(g2d, boardX + boardW - 16, boardY + 16, 16);
drawMountingHole(g2d, boardX + boardW - 16, boardY + boardH - 16, 16);
// ULN2003A DIP-16 IC Chip (Center)
drawChip(g2d, boardX + 35, boardY + 45, 95, 42, "ULN2003A");
// 4 Status LEDs (A, B, C, D)
for (int i = 0; i < 4; i++) {
double lx = boardX + 42 + i * 20;
double ly = boardY + 25;
g2d.setColor(LED_YELLOW);
g2d.fill(new RoundRectangle2D.Double(lx - 5, ly - 5, 10, 10, 2, 2));
}
// JST-XH 5-Pin Shrouded Motor Header (Top Right)
double spacing = PIN_SPACING.convertToPixels();
g2d.setColor(JST_WHITE);
g2d.fill(new RoundRectangle2D.Double(x + spacing * 3.5, y - spacing * 7.8, spacing * 5 + 10, spacing + 14, 3, 3));
g2d.setColor(Color.LIGHT_GRAY);
g2d.draw(new RoundRectangle2D.Double(x + spacing * 3.5, y - spacing * 7.8, spacing * 5 + 10, spacing + 14, 3, 3));
// Silk Screen Text
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT_SMALL);
StringUtils.drawCenteredText(g2d, "IN1", x, y - 12, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "IN2", x + spacing, y - 12, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "IN3", x + spacing * 2, y - 12, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "IN4", x + spacing * 3, y - 12, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "5-12V", x + spacing * 5, y - 12, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "GND", x + spacing * 6, y - 12, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
}
g2d.setTransform(oldTx);
// Draw header pins and JST contacts
drawPins(g2d, 0, controlPoints.length, false, outlineMode, drawingObserver);
g2d.setComposite(oldComposite);
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.setColor(ULN_BLUE);
g2d.fill(new RoundRectangle2D.Double(2, 2, width - 4, height - 4, 3, 3));
g2d.setColor(Color.DARK_GRAY);
g2d.fillRect(6, height / 2 - 4, width - 12, 8);
for (int i = 0; i < 4; i++) {
g2d.setColor(LED_YELLOW);
g2d.fill(new Ellipse2D.Double(6 + i * 5, 4, 3, 3));
}
}
}
@@ -0,0 +1,174 @@
/*
*
* DIY Layout Creator (DIYLC).
* Copyright (c) 2009-2025 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.components.sensors;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.ObjectCache;
import org.diylc.common.Orientation;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(name = "Temp/Pressure/Humidity Sensor (BME280)", category = "Sensors",
author = "Branislav Stojkovic", description = "BME280 / BMP280 Temperature, Humidity, and Pressure Sensor (I2C/SPI)",
instanceNamePrefix = "SEN", zOrder = IDIYComponent.COMPONENT,
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
enableCache = true)
public class BME280Sensor extends AbstractMakerBoard {
private static final long serialVersionUID = 1L;
public static Color BME_PURPLE = Color.decode("#5B2C6F");
public static Color SENSOR_METAL = Color.decode("#C0C0C0");
public static Size BOARD_WIDTH = new Size(15.2d, SizeUnit.mm);
public static Size BOARD_HEIGHT = new Size(12.0d, SizeUnit.mm);
public BME280Sensor() {
super();
this.bodyColor = BME_PURPLE;
updateControlPoints();
}
@Override
protected void updateControlPoints() {
Point2D firstPoint = controlPoints[0];
double spacing = PIN_SPACING.convertToPixels();
// 6-pin 0.1" header: VCC, GND, SCL, SDA, CSB, SDO
double[][] relativeOffsets = new double[6][2];
for (int i = 0; i < 6; i++) {
relativeOffsets[i][0] = i * spacing;
relativeOffsets[i][1] = 0;
}
rotatePoints(firstPoint, relativeOffsets);
}
@Override
public Shape getBodyShape() {
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - (boardW - 5 * PIN_SPACING.convertToPixels()) / 2.0;
double boardY = y - 16;
return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 4, 4);
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
if (checkPointsClipped(g2d.getClip())) {
return;
}
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
AffineTransform oldTx = g2d.getTransform();
if (orientation != Orientation.DEFAULT) {
g2d.rotate(orientation.toRadians(), x, y);
}
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - (boardW - 5 * PIN_SPACING.convertToPixels()) / 2.0;
double boardY = y - 16;
Shape boardShape = getBodyShape();
Composite oldComposite = applyAlpha(g2d, componentState);
drawingObserver.startTracking();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(boardShape);
drawingObserver.stopTracking();
g2d.setColor(getFinalBorderColor(componentState, outlineMode));
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
g2d.draw(boardShape);
if (!outlineMode) {
// 1 Mounting Hole (Right side)
drawMountingHole(g2d, boardX + boardW - 16, boardY + boardH - 20, 14);
// BME280 Metal Sensor Can with vent hole (Center)
double sx = boardX + 35;
double sy = boardY + boardH - 35;
g2d.setColor(SENSOR_METAL);
g2d.fill(new RoundRectangle2D.Double(sx, sy, 24, 20, 2, 2));
g2d.setColor(Color.DARK_GRAY);
g2d.draw(new RoundRectangle2D.Double(sx, sy, 24, 20, 2, 2));
// Vent hole
g2d.setColor(Color.BLACK);
g2d.fill(new Ellipse2D.Double(sx + 5, sy + 5, 4, 4));
// LDO Voltage Regulator (Left)
drawChip(g2d, boardX + 12, boardY + boardH - 32, 16, 16, "");
// Silk Screen Text
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT_SMALL);
StringUtils.drawCenteredText(g2d, "BME280", boardX + boardW / 2.0, boardY + 18, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
}
g2d.setTransform(oldTx);
// Draw 6 header pins
drawPins(g2d, 0, controlPoints.length, false, outlineMode, drawingObserver);
g2d.setComposite(oldComposite);
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.setColor(BME_PURPLE);
g2d.fill(new RoundRectangle2D.Double(2, 4, width - 4, height - 8, 3, 3));
g2d.setColor(SENSOR_METAL);
g2d.fill(new RoundRectangle2D.Double(width / 2 - 5, height / 2 - 4, 10, 8, 2, 2));
g2d.setColor(Color.BLACK);
g2d.fill(new Ellipse2D.Double(width / 2 - 3, height / 2 - 2, 2, 2));
}
}
@@ -0,0 +1,196 @@
/*
*
* DIY Layout Creator (DIYLC).
* Copyright (c) 2009-2025 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.components.sensors;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.ObjectCache;
import org.diylc.common.Orientation;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(name = "Soil Moisture Sensor (Capacitive)", category = "Sensors",
author = "Branislav Stojkovic", description = "Capacitive Corrosion-Resistant Soil Moisture Sensor Module",
instanceNamePrefix = "SEN", zOrder = IDIYComponent.COMPONENT,
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
enableCache = true)
public class SoilMoistureSensor extends AbstractMakerBoard {
private static final long serialVersionUID = 1L;
public static Color SOIL_PCB = Color.decode("#17202A");
public static Color PROBE_TRACE = Color.decode("#273746");
public static Size BOARD_WIDTH = new Size(60.0d, SizeUnit.mm);
public static Size BOARD_HEIGHT = new Size(20.0d, SizeUnit.mm);
public SoilMoistureSensor() {
super();
this.bodyColor = SOIL_PCB;
updateControlPoints();
}
@Override
protected void updateControlPoints() {
Point2D firstPoint = controlPoints[0];
double spacing = PIN_SPACING.convertToPixels();
// 3-pin 0.1" header on handle: VCC, GND, AOUT
double[][] relativeOffsets = new double[][] {
{ 0, 0 },
{ 0, spacing },
{ 0, spacing * 2 }
};
rotatePoints(firstPoint, relativeOffsets);
}
@Override
public Shape getBodyShape() {
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - 15;
double boardY = y - (boardH - 2 * PIN_SPACING.convertToPixels()) / 2.0;
Path2D path = new Path2D.Double();
path.moveTo(boardX, boardY);
path.lineTo(boardX + boardW * 0.35, boardY);
path.lineTo(boardX + boardW - 15, boardY + 4);
path.lineTo(boardX + boardW, boardY + boardH / 2.0);
path.lineTo(boardX + boardW - 15, boardY + boardH - 4);
path.lineTo(boardX + boardW * 0.35, boardY + boardH);
path.lineTo(boardX, boardY + boardH);
path.closePath();
return path;
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
if (checkPointsClipped(g2d.getClip())) {
return;
}
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
AffineTransform oldTx = g2d.getTransform();
if (orientation != Orientation.DEFAULT) {
g2d.rotate(orientation.toRadians(), x, y);
}
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - 15;
double boardY = y - (boardH - 2 * PIN_SPACING.convertToPixels()) / 2.0;
Shape boardShape = getBodyShape();
Composite oldComposite = applyAlpha(g2d, componentState);
drawingObserver.startTracking();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(boardShape);
drawingObserver.stopTracking();
g2d.setColor(getFinalBorderColor(componentState, outlineMode));
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
g2d.draw(boardShape);
if (!outlineMode) {
// Capacitive Sensing Copper Area (Right blade section)
double bladeX = boardX + boardW * 0.38;
double bladeW = boardW * 0.55;
g2d.setColor(PROBE_TRACE);
g2d.fill(new RoundRectangle2D.Double(bladeX, boardY + 12, bladeW, boardH - 24, 6, 6));
// White silk dividing line & warning
g2d.setColor(Color.WHITE);
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
g2d.drawLine((int)(boardX + boardW * 0.35), (int)boardY + 4, (int)(boardX + boardW * 0.35), (int)(boardY + boardH - 4));
// NE555 / TLC555 Oscillator Timer IC (Handle area)
drawChip(g2d, boardX + 45, boardY + boardH / 2.0 - 14, 28, 28, "555");
// 3.3V Voltage Regulator
drawChip(g2d, boardX + 85, boardY + boardH / 2.0 - 10, 18, 20, "");
// Silk Screen Text
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT_SMALL);
StringUtils.drawCenteredText(g2d, "Capacitive Soil Sensor v1.2", bladeX + bladeW / 2.0, boardY + boardH / 2.0, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
// Pin labels
String[] labels = new String[] { "VCC", "GND", "AOUT" };
double spacing = PIN_SPACING.convertToPixels();
for (int i = 0; i < 3; i++) {
StringUtils.drawCenteredText(g2d, labels[i], x + 16, y + i * spacing, HorizontalAlignment.LEFT, VerticalAlignment.CENTER);
}
}
g2d.setTransform(oldTx);
// Draw 3 header pins
drawPins(g2d, 0, controlPoints.length, false, outlineMode, drawingObserver);
g2d.setComposite(oldComposite);
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.setColor(SOIL_PCB);
Path2D p = new Path2D.Double();
p.moveTo(2, 6);
p.lineTo(width - 10, 6);
p.lineTo(width - 2, height / 2.0);
p.lineTo(width - 10, height - 6);
p.lineTo(2, height - 6);
p.closePath();
g2d.fill(p);
g2d.setColor(PROBE_TRACE);
g2d.fillRect(width / 2, 8, width / 2 - 8, height - 16);
}
}
@@ -0,0 +1,188 @@
/*
*
* DIY Layout Creator (DIYLC).
* Copyright (c) 2009-2025 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.components.sensors;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.ObjectCache;
import org.diylc.common.Orientation;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(name = "Line / Obstacle Sensor (TCRT5000)", category = "Sensors",
author = "Branislav Stojkovic", description = "TCRT5000 Infrared Line Tracking and Optical Obstacle Avoidance Sensor",
instanceNamePrefix = "SEN", zOrder = IDIYComponent.COMPONENT,
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
enableCache = true)
public class TCRT5000Sensor extends AbstractMakerBoard {
private static final long serialVersionUID = 1L;
public static Color SENSOR_BLUE = Color.decode("#1B4F72");
public static Color POT_BLUE = Color.decode("#0055AA");
public static Size BOARD_WIDTH = new Size(32.0d, SizeUnit.mm);
public static Size BOARD_HEIGHT = new Size(14.0d, SizeUnit.mm);
public TCRT5000Sensor() {
super();
this.bodyColor = SENSOR_BLUE;
updateControlPoints();
}
@Override
protected void updateControlPoints() {
Point2D firstPoint = controlPoints[0];
double spacing = PIN_SPACING.convertToPixels();
// 4-pin 0.1" header: VCC, GND, DO, AO
double[][] relativeOffsets = new double[4][2];
for (int i = 0; i < 4; i++) {
relativeOffsets[i][0] = 0;
relativeOffsets[i][1] = i * spacing;
}
rotatePoints(firstPoint, relativeOffsets);
}
@Override
public Shape getBodyShape() {
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - boardW + 20;
double boardY = y - (boardH - 3 * PIN_SPACING.convertToPixels()) / 2.0;
return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 4, 4);
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
if (checkPointsClipped(g2d.getClip())) {
return;
}
Point2D p0 = controlPoints[0];
double x = p0.getX();
double y = p0.getY();
AffineTransform oldTx = g2d.getTransform();
if (orientation != Orientation.DEFAULT) {
g2d.rotate(orientation.toRadians(), x, y);
}
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - boardW + 20;
double boardY = y - (boardH - 3 * PIN_SPACING.convertToPixels()) / 2.0;
Shape boardShape = getBodyShape();
Composite oldComposite = applyAlpha(g2d, componentState);
drawingObserver.startTracking();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(boardShape);
drawingObserver.stopTracking();
g2d.setColor(getFinalBorderColor(componentState, outlineMode));
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
g2d.draw(boardShape);
if (!outlineMode) {
// TCRT5000 Optical Package (Left Edge)
g2d.setColor(Color.decode("#2C3E50"));
g2d.fill(new RoundRectangle2D.Double(boardX - 10, boardY + boardH / 2.0 - 20, 36, 40, 4, 4));
// Blue IR LED
g2d.setColor(Color.decode("#3498DB"));
g2d.fill(new Ellipse2D.Double(boardX - 6, boardY + boardH / 2.0 - 15, 14, 14));
// Black Phototransistor
g2d.setColor(Color.BLACK);
g2d.fill(new Ellipse2D.Double(boardX - 6, boardY + boardH / 2.0 + 1, 14, 14));
// LM393 Comparator IC (Center)
drawChip(g2d, boardX + 45, boardY + boardH / 2.0 - 14, 30, 28, "393");
// Sensitivity Trimmer Potentiometer (Right Center)
g2d.setColor(POT_BLUE);
g2d.fill(new RoundRectangle2D.Double(boardX + 90, boardY + boardH / 2.0 - 16, 32, 32, 3, 3));
g2d.setColor(Color.decode("#BDC3C7"));
g2d.fill(new Ellipse2D.Double(boardX + 96, boardY + boardH / 2.0 - 10, 20, 20));
g2d.setColor(Color.BLACK);
g2d.drawLine((int)(boardX + 99), (int)(boardY + boardH / 2.0), (int)(boardX + 113), (int)(boardY + boardH / 2.0));
// Status LEDs (Power & Signal)
g2d.setColor(Color.RED);
g2d.fill(new RoundRectangle2D.Double(boardX + 135, boardY + 12, 10, 8, 2, 2));
g2d.setColor(Color.GREEN);
g2d.fill(new RoundRectangle2D.Double(boardX + 135, boardY + boardH - 20, 10, 8, 2, 2));
// Silk Screen Pin Labels
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT_SMALL);
String[] labels = new String[] { "VCC", "GND", "DO", "AO" };
double spacing = PIN_SPACING.convertToPixels();
for (int i = 0; i < 4; i++) {
StringUtils.drawCenteredText(g2d, labels[i], boardX + boardW - 24, y + i * spacing, HorizontalAlignment.RIGHT, VerticalAlignment.CENTER);
}
}
g2d.setTransform(oldTx);
// Draw 4 header pins
drawPins(g2d, 0, controlPoints.length, false, outlineMode, drawingObserver);
g2d.setComposite(oldComposite);
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.setColor(SENSOR_BLUE);
g2d.fill(new RoundRectangle2D.Double(2, 4, width - 4, height - 8, 3, 3));
g2d.setColor(Color.CYAN);
g2d.fill(new Ellipse2D.Double(4, height / 2 - 6, 6, 6));
g2d.setColor(Color.BLACK);
g2d.fill(new Ellipse2D.Double(4, height / 2, 6, 6));
}
}
@@ -75,6 +75,9 @@ public class MakerComponentsTest {
DHTSensor.class,
MPU6050.class,
LDRSensorModule.class,
BME280Sensor.class,
SoilMoistureSensor.class,
TCRT5000Sensor.class,
// Displays & Outputs
CharacterLCD.class,
@@ -82,6 +85,7 @@ public class MakerComponentsTest {
SevenSegmentDisplay.class,
LEDMatrix8x8.class,
TFTDisplay.class,
WS2812BStick.class,
// Modules & Breakouts
LogicLevelConverter.class,
@@ -90,6 +94,18 @@ public class MakerComponentsTest {
RelayModule.class,
SDCardModule.class,
RTCModule.class,
MB102PowerSupply.class,
TP4056Charger.class,
LM2596BuckConverter.class,
MT3608BoostConverter.class,
AnalogJoystickKY023.class,
RotaryEncoderKY040.class,
NRF24L01Transceiver.class,
HC05Bluetooth.class,
RFIDRC522.class,
ULN2003Driver.class,
PCA9685ServoDriver.class,
MOSFETSwitchModule.class,
// Robotics
MicroServoSG90.class,