Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add GLCD12864 element #143

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions src/glcd-128x64-element.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { html } from 'lit';
import { storiesOf } from '@storybook/web-components';
import './glcd-128x64-element';

const logoBitmap = new Uint8Array([
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 63, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 248, 31, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143,
255, 255, 159, 240, 31, 252, 63, 255, 127, 255, 255, 255, 255, 255, 191, 7, 231, 254, 7, 240, 15,
240, 7, 254, 63, 243, 195, 255, 207, 255, 14, 3, 195, 254, 7, 241, 143, 192, 3, 254, 31, 225, 195,
255, 143, 254, 14, 35, 129, 252, 3, 241, 143, 128, 1, 254, 31, 225, 195, 255, 135, 254, 14, 51, 0,
252, 99, 241, 143, 0, 1, 254, 31, 195, 225, 255, 7, 254, 30, 51, 24, 252, 99, 240, 15, 3, 224,
254, 31, 131, 225, 255, 7, 254, 62, 35, 24, 252, 99, 248, 30, 15, 240, 254, 31, 131, 225, 255, 7,
252, 62, 3, 24, 252, 3, 248, 30, 31, 248, 254, 63, 7, 225, 254, 7, 252, 63, 3, 8, 254, 3, 248, 60,
31, 248, 126, 63, 7, 241, 254, 7, 252, 63, 7, 129, 255, 7, 240, 252, 127, 252, 126, 62, 31, 240,
254, 7, 252, 127, 255, 193, 255, 135, 240, 252, 127, 252, 126, 60, 31, 240, 254, 3, 252, 127, 223,
225, 255, 199, 241, 248, 127, 252, 126, 56, 63, 240, 252, 3, 248, 127, 159, 241, 255, 195, 241,
248, 255, 252, 126, 48, 127, 248, 252, 3, 248, 127, 31, 241, 255, 227, 241, 248, 255, 252, 126,
16, 127, 248, 124, 35, 248, 255, 31, 248, 255, 227, 227, 240, 255, 252, 126, 0, 255, 248, 124, 99,
240, 254, 31, 248, 255, 227, 227, 240, 255, 252, 126, 1, 255, 248, 120, 99, 240, 254, 31, 248,
255, 243, 227, 240, 255, 252, 126, 3, 255, 252, 120, 99, 241, 254, 31, 248, 255, 225, 227, 241,
255, 252, 124, 99, 255, 252, 120, 99, 241, 254, 31, 248, 255, 225, 199, 241, 255, 252, 124, 99,
255, 252, 120, 99, 225, 254, 31, 252, 127, 193, 199, 241, 255, 252, 124, 99, 255, 252, 56, 225,
227, 254, 31, 252, 127, 129, 199, 241, 255, 252, 124, 3, 255, 252, 48, 225, 227, 254, 63, 252,
127, 0, 199, 241, 255, 252, 126, 3, 255, 252, 48, 241, 227, 254, 63, 252, 126, 8, 143, 241, 255,
252, 126, 1, 255, 254, 48, 241, 195, 254, 63, 254, 60, 24, 143, 241, 255, 252, 126, 1, 255, 254,
49, 241, 199, 254, 63, 254, 60, 56, 15, 241, 255, 248, 126, 32, 255, 254, 33, 241, 199, 254, 63,
254, 56, 124, 15, 240, 255, 248, 126, 48, 127, 254, 1, 241, 135, 254, 63, 254, 16, 252, 31, 240,
255, 248, 254, 56, 63, 254, 3, 240, 143, 254, 63, 254, 1, 248, 15, 248, 255, 240, 254, 56, 63,
254, 3, 240, 143, 254, 63, 254, 1, 248, 15, 248, 127, 225, 254, 60, 31, 254, 3, 240, 15, 254, 63,
254, 3, 248, 15, 248, 63, 225, 254, 62, 15, 255, 7, 248, 15, 254, 63, 254, 3, 249, 143, 252, 31,
193, 254, 63, 7, 255, 7, 248, 31, 254, 63, 252, 3, 249, 207, 252, 15, 131, 254, 63, 3, 255, 7,
248, 31, 254, 63, 252, 99, 248, 143, 254, 0, 3, 254, 63, 129, 255, 7, 248, 31, 254, 63, 252, 99,
248, 15, 254, 0, 7, 254, 63, 192, 255, 7, 248, 63, 254, 63, 252, 35, 248, 15, 255, 0, 15, 254, 63,
224, 255, 7, 248, 63, 254, 63, 252, 3, 252, 31, 255, 128, 31, 254, 63, 240, 255, 135, 252, 63,
254, 63, 254, 7, 254, 63, 255, 224, 127, 254, 63, 253, 255, 143, 252, 63, 254, 63, 254, 7, 255,
255, 255, 255, 255, 255, 63, 255, 255, 255, 254, 127, 254, 63, 255, 31, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
]);

function toImageData(bitmap: Uint8Array, width: number, height: number) {
const result = new ImageData(width, height);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const pixIndex = Math.floor((y * width + x) / 8);
const pixValue = bitmap[pixIndex] & (1 << (7 - (x % 8))) ? 0xff : 0;
const pixOffset = (y * width + x) * 4;
result.data.fill(pixValue, pixOffset, pixOffset + 3);
result.data[pixOffset + 3] = 0xff;
}
}
return result;
}

storiesOf('GLCD12864', module)
.addParameters({ component: 'wokwi-glcd-128x64' })
.add('Default', () => html`<wokwi-glcd-128x64></wokwi-glcd-128x64>`)
.add(
'Wokwi logo',
() =>
html`<wokwi-glcd-128x64 .imageData=${toImageData(logoBitmap, 128, 64)}></wokwi-glcd-128x64>`
);
191 changes: 191 additions & 0 deletions src/glcd-128x64-element.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import { html, LitElement, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { ElementPin } from './pin';

type CanvasContext = CanvasRenderingContext2D | null | undefined;
@customElement('wokwi-glcd-128x64')
export class GLCD12864Element extends LitElement {
/**
* The pixel data to draw on the element's internal &lt;canvas&gt;.
* If you change the underlaying pixel data without updating the
* `imageData` reference, call the `redraw()` method to update the
* screen with your changes.
*/
@property() imageData: ImageData;

readonly width = 93;
readonly height = 70;
private displayWidth = 128; // multiplied with 1.473125 to adjust for screen size
private displayHeight = 64;
readonly pixelScaler = 0.5; // pixel size
private screenWidth = this.displayWidth * this.pixelScaler;
private screenHeight = this.displayHeight * this.pixelScaler;

private canvas: HTMLCanvasElement | null | undefined = void 0;
private ctx: CanvasContext = null;

readonly pinInfo: ElementPin[] = [
{ name: 'LED-', x: 38.5, y: 185, signals: [{ type: 'power', signal: 'GND' }] },
{ name: 'LED+', x: 44.5, y: 185, signals: [{ type: 'power', signal: 'VCC' }] },
{ name: 'VEE', x: 50.5, y: 185, signals: [] },
{ name: 'RST', x: 56.5, y: 185, signals: [] },
{ name: 'CS2', x: 62.5, y: 185, signals: [] },
{ name: 'CS1', x: 68.5, y: 185, signals: [] },
{ name: 'DB7', x: 74.5, y: 185, signals: [] },
{ name: 'DB6', x: 80.5, y: 185, signals: [] },
{ name: 'DB5', x: 86.5, y: 185, signals: [] },
{ name: 'DB4', x: 92.5, y: 185, signals: [] },
{ name: 'DB3', x: 98.5, y: 185, signals: [] },
{ name: 'DB2', x: 104.5, y: 185, signals: [] },
{ name: 'DB1', x: 110.5, y: 185, signals: [] },
{ name: 'DB0', x: 116.5, y: 185, signals: [] },
{ name: 'EN', x: 122.5, y: 185, signals: [] },
{ name: 'RW', x: 128.5, y: 185, signals: [] },
{ name: 'DI', x: 134.5, y: 185, signals: [] },
{ name: 'VO', x: 140.5, y: 185, signals: [] },
{ name: 'VDD', x: 146.5, y: 185, signals: [{ type: 'power', signal: 'VCC' }] },
{ name: 'VSS', x: 152.5, y: 185, signals: [{ type: 'power', signal: 'GND' }] },
];

static get styles() {
return css`
.container {
position: relative;
}

.container > canvas {
position: absolute;
left: 54px;
top: 70.5px;
}

.pixelated {
image-rendering: crisp-edges; /* firefox */
image-rendering: pixelated; /* chrome/webkit */
}
`;
}

constructor() {
super();
this.imageData = new ImageData(this.displayWidth, this.displayHeight);
}

/**
* Used for initiating update of an imageData data which its reference wasn't changed
*/
public redraw() {
this.ctx?.putImageData(this.imageData, 0, 0);
}

private initContext() {
this.canvas = this.shadowRoot?.querySelector('canvas');
// No need to clear canvas rect - all images will have full opacity
this.ctx = this.canvas?.getContext('2d');
}

firstUpdated() {
this.initContext();
this.ctx?.putImageData(this.imageData, 0, 0);
}

updated() {
if (this.imageData) {
this.redraw();
}
}

render() {
const { width, height, screenWidth, screenHeight } = this;
return html` <div class="container">
<svg
width="${width}mm"
height="${height}mm"
viewBox="0 0 93 70"
xmlns="http://www.w3.org/2000/svg"
>
<!-- svg decoratives -->
<rect width="93" height="70" style="fill:#529f5c" />
<rect width="84" height="50.5" x="4.5" y="9.75" rx="2" style="fill:#3d4956" />
<rect width="70.7" height="38.8" x="11.15" y="15.6" rx="2" style="fill:#28303d" />
<rect width="74" height="3" x="9.5" y="11.5" rx="1" style="fill:#28303d" />
<rect width="74" height="3" x="9.5" y="55.25" rx="1" style="fill:#28303d" />
<rect width="74" height="1" x="9.5" y="13.5" rx=".5" style="fill:#4a5866" />
<rect width="74" height="1" x="9.5" y="57.25" rx=".5" style="fill:#4a5866" />
<!-- mounting holes -->
<g fill="#ececec">
<circle cx="2.5" cy="2.5" r="1.35" />
<circle cx="90.5" cy="2.5" r="1.35" />
<circle cx="90.5" cy="67.5" r="1.35" />
<circle cx="2.5" cy="67.5" r="1.35" />
</g>
<!-- display panel -->
<rect
width="${screenWidth}"
height="${screenHeight}"
x="14.5"
y="19"
style="fill:#215da2"
/>
<!-- text -->
<text
fill="#fff"
text-anchor="start"
font-size="2"
font-weight="300"
font-family="monospace"
transform="rotate(-90)"
>
<tspan x="-66.5" y="14.5">LED-</tspan>
<tspan x="-66.5" y="17.04">LED+</tspan>
<tspan x="-66.5" y="19.58">VEE</tspan>
<tspan x="-66.5" y="22.12">RST</tspan>
<tspan x="-66.5" y="24.66">CS2</tspan>
<tspan x="-66.5" y="27.2">CS1</tspan>
<tspan x="-66.5" y="29.74">DB7</tspan>
<tspan x="-66.5" y="32.28">DB6</tspan>
<tspan x="-66.5" y="34.82">DB5</tspan>
<tspan x="-66.5" y="37.36">DB4</tspan>
<tspan x="-66.5" y="39.9">DB3</tspan>
<tspan x="-66.5" y="42.44">DB2</tspan>
<tspan x="-66.5" y="44.98">DB1</tspan>
<tspan x="-66.5" y="47.52">DB0</tspan>
<tspan x="-66.5" y="50.06">EN</tspan>
<tspan x="-66.5" y="52.6">R/W</tspan>
<tspan x="-66.5" y="55.14">D/I</tspan>
<tspan x="-66.5" y="57.68">VO</tspan>
<tspan x="-66.5" y="60.22">VDD</tspan>
<tspan x="-66.5" y="62.76">VSS</tspan>
</text>
<!-- PINS -->
<g transform="translate(14 65.5)" fill="#9D9D9A" stroke-width="0.2">
<circle stroke="#262626" cx="0" cy="2.5" r="0.5" />
<circle stroke="#ff0000" cx="2.54" cy="2.5" r="0.5" />
<circle stroke="#c0c0c0" cx="5.08" cy="2.5" r="0.5" />
<circle stroke="#808080" cx="7.62" cy="2.5" r="0.5" />
<circle stroke="#ffff00" cx="10.16" cy="2.5" r="0.5" />
<circle stroke="#ffff00" cx="12.7" cy="2.5" r="0.5" />
<circle stroke="#FF00FF" cx="15.24" cy="2.5" r="0.5" />
<circle stroke="#FF00FF" cx="17.78" cy="2.5" r="0.5" />
<circle stroke="#FF00FF" cx="20.32" cy="2.5" r="0.5" />
<circle stroke="#FF00FF" cx="22.86" cy="2.5" r="0.5" />
<circle stroke="#FF00FF" cx="25.4" cy="2.5" r="0.5" />
<circle stroke="#FF00FF" cx="27.94" cy="2.5" r="0.5" />
<circle stroke="#FF00FF" cx="30.48" cy="2.5" r="0.5" />
<circle stroke="#FF00FF" cx="33.02" cy="2.5" r="0.5" />
<circle stroke="#008080" cx="35.02" cy="2.5" r="0.5" />
<circle stroke="#0000FF" cx="38.1" cy="2.5" r="0.5" />
<circle stroke="#00FFFF" cx="40.64" cy="2.5" r="0.5" />
<circle stroke="#C08540" cx="43.18" cy="2.5" r="0.5" />
<circle stroke="#ff0000" cx="45.72" cy="2.5" r="0.5" />
<circle stroke="#262626" cx="48.26" cy="2.5" r="0.5" />
</g>
</svg>
<canvas
width="${this.displayWidth}"
height="${this.displayHeight}"
style="width:${this.screenWidth * 3.78}; height:${this.screenHeight * 3.78};"
></canvas>
</div>`;
}
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,4 @@ export { StepperMotorElement } from './stepper-motor-element';
export { HX711Element } from './hx711-element';
export { KS2EMDC5Element } from './ks2e-m-dc5-element';
export { BiaxialStepperElement } from './biaxial-stepper-element';
export { Glcd128x64Element } from './glcd-128x64-element';
2 changes: 2 additions & 0 deletions src/react-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import { HX711Element } from './hx711-element';
import { KS2EMDC5Element } from './ks2e-m-dc5-element';
import { BiaxialStepperElement } from './biaxial-stepper-element';
import type React from 'react';
import { GLCD12864Element } from './glcd-128x64-element';

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

Expand Down Expand Up @@ -106,6 +107,7 @@ declare global {
'wokwi-hx711': WokwiElement<HX711Element>;
'wokwi-ks2e-m-dc5': WokwiElement<KS2EMDC5Element>;
'wokwi-biaxial-stepper': WokwiElement<BiaxialStepperElement>;
'wokwi-glcd-128x64': WokwiElement<GLCD12864Element>;
}
}
}