Skip to content

Commit 0a11841

Browse files
committed
feat(lcd2004): add wokwi-lcd2004 element
LCD with 20 columns and 4 rows (lines). It has the same features as the wokwi-lcd1602.
1 parent cde7f4d commit 0a11841

File tree

5 files changed

+141
-61
lines changed

5 files changed

+141
-61
lines changed

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ export { Ds1307Element } from './ds1307-element';
2323
export { LEDRingElement } from './led-ring-element';
2424
export { SlideSwitchElement } from './slide-switch-element';
2525
export { HCSR04Element } from './hc-sr04-element';
26+
export { LCD2004Element } from './lcd2004-element';

src/lcd1602-element.ts

Lines changed: 105 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { customElement, html, LitElement, property, css, svg } from 'lit-element';
22
import { fontA00 } from './lcd1602-font-a00';
33
import { ElementPin, i2c } from './pin';
4-
5-
const ROWS = 2;
6-
const COLS = 16;
4+
import { mmToPix } from './utils/units';
75

86
const charXSpacing = 3.55;
97
const charYSpacing = 5.95;
@@ -26,6 +24,9 @@ export class LCD1602Element extends LitElement {
2624
@property() backlight = true;
2725
@property() pins: 'full' | 'i2c' | 'none' = 'full';
2826

27+
protected numCols = 16;
28+
protected numRows = 2;
29+
2930
@property()
3031
get text() {
3132
return Array.from(this.characters)
@@ -60,7 +61,13 @@ export class LCD1602Element extends LitElement {
6061
`;
6162
}
6263

64+
protected get panelHeight() {
65+
return this.rows * 5.75;
66+
}
67+
6368
get pinInfo(): ElementPin[] {
69+
const { panelHeight } = this;
70+
const y = 87.5 + panelHeight * mmToPix;
6471
switch (this.pins) {
6572
case 'i2c':
6673
return [
@@ -73,33 +80,42 @@ export class LCD1602Element extends LitElement {
7380
case 'full':
7481
default:
7582
return [
76-
{ name: 'VSS', x: 32, y: 131, number: 1, signals: [{ type: 'power', signal: 'GND' }] },
77-
{ name: 'VDD', x: 41.5, y: 131, number: 2, signals: [{ type: 'power', signal: 'VCC' }] },
78-
{ name: 'V0', x: 51.5, y: 131, number: 3, signals: [] },
79-
{ name: 'RS', x: 60.5, y: 131, number: 4, signals: [] },
80-
{ name: 'RW', x: 70.5, y: 131, number: 5, signals: [] },
81-
{ name: 'E', x: 80, y: 131, number: 6, signals: [] },
82-
{ name: 'D0', x: 89.5, y: 131, number: 7, signals: [] },
83-
{ name: 'D1', x: 99.5, y: 131, number: 8, signals: [] },
84-
{ name: 'D2', x: 109, y: 131, number: 9, signals: [] },
85-
{ name: 'D3', x: 118.5, y: 131, number: 10, signals: [] },
86-
{ name: 'D4', x: 128, y: 131, number: 11, signals: [] },
87-
{ name: 'D5', x: 137.5, y: 131, number: 12, signals: [] },
88-
{ name: 'D6', x: 147, y: 131, number: 13, signals: [] },
89-
{ name: 'D7', x: 156.5, y: 131, number: 14, signals: [] },
90-
{ name: 'A', x: 166.5, y: 131, number: 15, signals: [] },
91-
{ name: 'K', x: 176, y: 131, number: 16, signals: [] },
83+
{ name: 'VSS', x: 32, y, number: 1, signals: [{ type: 'power', signal: 'GND' }] },
84+
{ name: 'VDD', x: 41.5, y, number: 2, signals: [{ type: 'power', signal: 'VCC' }] },
85+
{ name: 'V0', x: 51.5, y, number: 3, signals: [] },
86+
{ name: 'RS', x: 60.5, y, number: 4, signals: [] },
87+
{ name: 'RW', x: 70.5, y, number: 5, signals: [] },
88+
{ name: 'E', x: 80, y, number: 6, signals: [] },
89+
{ name: 'D0', x: 89.5, y, number: 7, signals: [] },
90+
{ name: 'D1', x: 99.5, y, number: 8, signals: [] },
91+
{ name: 'D2', x: 109, y, number: 9, signals: [] },
92+
{ name: 'D3', x: 118.5, y, number: 10, signals: [] },
93+
{ name: 'D4', x: 128, y, number: 11, signals: [] },
94+
{ name: 'D5', x: 137.5, y, number: 12, signals: [] },
95+
{ name: 'D6', x: 147, y, number: 13, signals: [] },
96+
{ name: 'D7', x: 156.5, y, number: 14, signals: [] },
97+
{ name: 'A', x: 166.5, y, number: 15, signals: [] },
98+
{ name: 'K', x: 176, y, number: 16, signals: [] },
9299
];
93100
}
94101
}
95102

103+
get cols() {
104+
return this.numCols;
105+
}
106+
107+
get rows() {
108+
return this.numRows;
109+
}
110+
96111
path(characters: Uint8Array | number[]) {
97112
const xSpacing = 0.6;
98113
const ySpacing = 0.7;
99114
const result = [];
115+
const { cols } = this;
100116
for (let i = 0; i < characters.length; i++) {
101-
const charX = (i % COLS) * charXSpacing;
102-
const charY = Math.floor(i / COLS) * charYSpacing;
117+
const charX = (i % cols) * charXSpacing;
118+
const charY = Math.floor(i / cols) * charYSpacing;
103119

104120
for (let py = 0; py < 8; py++) {
105121
const row = this.font[characters[i] * 8 + py];
@@ -116,17 +132,18 @@ export class LCD1602Element extends LitElement {
116132
}
117133

118134
renderCursor() {
119-
const xOffset = 12.45 + this.cursorX * charXSpacing;
120-
const yOffset = 12.55 + this.cursorY * charYSpacing;
121-
if (this.cursorX < 0 || this.cursorX >= COLS || this.cursorY < 0 || this.cursorY >= ROWS) {
135+
const { cols, rows, cursor, cursorX, cursorY, blink, color } = this;
136+
const xOffset = 12.45 + cursorX * charXSpacing;
137+
const yOffset = 12.55 + cursorY * charYSpacing;
138+
if (cursorX < 0 || cursorX >= cols || cursorY < 0 || cursorY >= rows) {
122139
return null;
123140
}
124141

125142
const result = [];
126143

127-
if (this.blink) {
144+
if (blink) {
128145
result.push(svg`
129-
<rect x="${xOffset}" y="${yOffset}" width="2.95" height="5.55" fill="${this.color}">
146+
<rect x="${xOffset}" y="${yOffset}" width="2.95" height="5.55" fill="${color}">
130147
<animate
131148
attributeName="opacity"
132149
values="0;0;0;0;1;1;0;0;0;0"
@@ -138,11 +155,9 @@ export class LCD1602Element extends LitElement {
138155
`);
139156
}
140157

141-
if (this.cursor) {
158+
if (cursor) {
142159
const y = yOffset + 0.7 * 7;
143-
result.push(
144-
svg`<rect x="${xOffset}" y="${y}" width="2.95" height="0.65" fill="${this.color}" />`
145-
);
160+
result.push(svg`<rect x="${xOffset}" y="${y}" width="2.95" height="0.65" fill="${color}" />`);
146161
}
147162

148163
return result;
@@ -159,45 +174,52 @@ export class LCD1602Element extends LitElement {
159174
`;
160175
}
161176

162-
renderPins() {
177+
renderPins(panelHeight: number) {
178+
const y = panelHeight + 21.1;
163179
return svg`
164-
<rect x="7.55" y="33.5" height="2.5" width="40.64" fill="url(#pins)" />
165-
<text x="6" y="35.3" fill="white">1</text>
166-
<text x="7.2" y="33.3" fill="white">VSS</text>
167-
<text x="9.9" y="33.3" fill="white">VDD</text>
168-
<text x="12.7" y="33.3" fill="white">V0</text>
169-
<text x="15.2" y="33.3" fill="white">RS</text>
170-
<text x="17.8" y="33.3" fill="white">RW</text>
171-
<text x="20.8" y="33.3" fill="white">E</text>
172-
<text x="22.7" y="33.3" fill="white">D0</text>
173-
<text x="25.3" y="33.3" fill="white">D1</text>
174-
<text x="27.9" y="33.3" fill="white">D2</text>
175-
<text x="30.4" y="33.3" fill="white">D3</text>
176-
<text x="33" y="33.3" fill="white">D4</text>
177-
<text x="35.6" y="33.3" fill="white">D5</text>
178-
<text x="38.2" y="33.3" fill="white">D6</text>
179-
<text x="40.8" y="33.3" fill="white">D7</text>
180-
<text x="43.6" y="33.3" fill="white">A</text>
181-
<text x="46.2" y="33.3" fill="white">K</text>
182-
<text x="48" y="35.3" fill="white">16</text>
180+
<g transform="translate(0, ${y})">
181+
<rect x="7.55" y="1" height="2.5" width="40.64" fill="url(#pins)" />
182+
<text x="6" y="2.7" fill="white">1</text>
183+
<text x="7.2" y="0.7" fill="white">VSS</text>
184+
<text x="9.9" y="0.7" fill="white">VDD</text>
185+
<text x="12.7" y="0.7" fill="white">V0</text>
186+
<text x="15.2" y="0.7" fill="white">RS</text>
187+
<text x="17.8" y="0.7" fill="white">RW</text>
188+
<text x="20.8" y="0.7" fill="white">E</text>
189+
<text x="22.7" y="0.7" fill="white">D0</text>
190+
<text x="25.3" y="0.7" fill="white">D1</text>
191+
<text x="27.9" y="0.7" fill="white">D2</text>
192+
<text x="30.4" y="0.7" fill="white">D3</text>
193+
<text x="33" y="0.7" fill="white">D4</text>
194+
<text x="35.6" y="0.7" fill="white">D5</text>
195+
<text x="38.2" y="0.7" fill="white">D6</text>
196+
<text x="40.8" y="0.7" fill="white">D7</text>
197+
<text x="43.6" y="0.7" fill="white">A</text>
198+
<text x="46.2" y="0.7" fill="white">K</text>
199+
<text x="48" y="2.7" fill="white">16</text>
200+
</g>
183201
`;
184202
}
185203

186204
render() {
187-
const { color, characters, background } = this;
205+
const { color, characters, background, pins, panelHeight, cols } = this;
188206

189207
const darken = this.backlight ? 0 : 0.5;
190208
const actualBgColor =
191209
background in backgroundColors ? backgroundColors[background] : backgroundColors;
192210

211+
const panelWidth = cols * 3.5125;
212+
const width = panelWidth + 23.8;
213+
const height = panelHeight + 24.5;
214+
193215
// Dimensions according to:
194216
// https://www.winstar.com.tw/products/character-lcd-display-module/16x2-lcd.html
195217
return html`
196218
<svg
197-
width="80mm"
198-
height="36mm"
219+
width="${width}mm"
220+
height="${height}mm"
199221
version="1.1"
200-
viewBox="0 0 80 36"
222+
viewBox="0 0 ${width} ${height}"
201223
style="font-size: 1.5px; font-family: monospace"
202224
xmlns="http://www.w3.org/2000/svg"
203225
>
@@ -220,13 +242,35 @@ export class LCD1602Element extends LitElement {
220242
<circle r="0.45" cx="0.827" cy="0.9" color="black" />
221243
</pattern>
222244
</defs>
223-
<rect width="80" height="36" fill="#087f45" />
224-
<rect x="4.95" y="5.7" width="71.2" height="25.2" />
225-
<rect x="7.55" y="10.3" width="66" height="16" rx="1.5" ry="1.5" fill="${actualBgColor}" />
226-
<rect x="7.55" y="10.3" width="66" height="16" rx="1.5" ry="1.5" opacity="${darken}" />
227-
${this.pins === 'i2c' ? this.renderI2CPins() : null}
228-
${this.pins === 'full' ? this.renderPins() : null}
229-
<rect x="12.45" y="12.55" width="56.2" height="11.5" fill="url(#characters)" />
245+
<rect width="${width}" height="${height}" fill="#087f45" />
246+
<rect x="4.95" y="5.7" width="${panelWidth + 15}" height="${panelHeight + 13.7}" />
247+
<rect
248+
x="7.55"
249+
y="10.3"
250+
width="${panelWidth + 9.8}"
251+
height="${panelHeight + 4.5}"
252+
rx="1.5"
253+
ry="1.5"
254+
fill="${actualBgColor}"
255+
/>
256+
<rect
257+
x="7.55"
258+
y="10.3"
259+
width="${panelWidth + 9.8}"
260+
height="${panelHeight + 4.5}"
261+
rx="1.5"
262+
ry="1.5"
263+
opacity="${darken}"
264+
/>
265+
${pins === 'i2c' ? this.renderI2CPins() : null}
266+
${pins === 'full' ? this.renderPins(panelHeight) : null}
267+
<rect
268+
x="12.45"
269+
y="12.55"
270+
width="${panelWidth}"
271+
height="${panelHeight}"
272+
fill="url(#characters)"
273+
/>
230274
<path d="${this.path(characters)}" transform="translate(12.45, 12.55)" fill="${color}" />
231275
${this.renderCursor()}
232276
</svg>

src/lcd2004-element.stories.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { html } from 'lit-html';
2+
import { fontA02 } from './lcd1602-font-a02';
3+
import './lcd2004-element';
4+
5+
export default {
6+
title: 'LCD2004',
7+
component: 'wokwi-lcd2004',
8+
};
9+
10+
export const lcd2004 = () => html`<wokwi-lcd2004 text="Hello World! LCD2004"></wokwi-lcd2004>`;
11+
lcd2004.storyName = 'Hello World';
12+
13+
export const blueBackground = () =>
14+
html`<wokwi-lcd2004
15+
background="blue"
16+
color="white"
17+
text="Line 1 Line 2 Line 3 Line 4"
18+
></wokwi-lcd2004>`;
19+
20+
export const i2cPins = () => html`<wokwi-lcd2004
21+
pins="i2c"
22+
text="I²C Pins"
23+
.font=${fontA02}
24+
></wokwi-lcd2004>`;
25+
i2cPins.storyName = 'I2C Pins';

src/lcd2004-element.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { customElement } from 'lit-element';
2+
import { LCD1602Element } from './lcd1602-element';
3+
4+
@customElement('wokwi-lcd2004')
5+
export class LCD2004Element extends LCD1602Element {
6+
protected numCols = 20;
7+
protected numRows = 4;
8+
}

src/react-types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { Ds1307Element } from './ds1307-element';
2222
import { LEDRingElement } from './led-ring-element';
2323
import { SlideSwitchElement } from './slide-switch-element';
2424
import { HCSR04Element } from './hc-sr04-element';
25+
import { LCD2004Element } from './lcd2004-element';
2526

2627
type WokwiElement<T> = Partial<T> & React.ClassAttributes<T>;
2728

@@ -49,6 +50,7 @@ declare global {
4950
'wokwi-neopixel-ring': WokwiElement<LEDRingElement>;
5051
'wokwi-slide-switch': WokwiElement<SlideSwitchElement>;
5152
'wokwi-hc-sr04': WokwiElement<HCSR04Element>;
53+
'wokwi-lcd2004': WokwiElement<LCD2004Element>;
5254
}
5355
}
5456
}

0 commit comments

Comments
 (0)