From 4f63ea8a420d1c8132de70441d8d7131e578562d Mon Sep 17 00:00:00 2001 From: minolmal Date: Fri, 18 Nov 2022 21:25:15 +0530 Subject: [PATCH] feat: add GLCD12864 element --- package-lock.json | 2 +- src/glcd-128x64-element.stories.ts | 79 ++++++++++++ src/glcd-128x64-element.ts | 191 +++++++++++++++++++++++++++++ src/index.ts | 1 + src/react-types.ts | 2 + 5 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 src/glcd-128x64-element.stories.ts create mode 100644 src/glcd-128x64-element.ts diff --git a/package-lock.json b/package-lock.json index 18a0069..f93229e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.3.1", "license": "MIT", "dependencies": { - "@types/react": "^18.0.25", + "@types/react": ">=16", "lit": "^2.0.0" }, "devDependencies": { diff --git a/src/glcd-128x64-element.stories.ts b/src/glcd-128x64-element.stories.ts new file mode 100644 index 0000000..f5c87a5 --- /dev/null +++ b/src/glcd-128x64-element.stories.ts @@ -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``) + .add( + 'Wokwi logo', + () => + html`` + ); diff --git a/src/glcd-128x64-element.ts b/src/glcd-128x64-element.ts new file mode 100644 index 0000000..02c127e --- /dev/null +++ b/src/glcd-128x64-element.ts @@ -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 <canvas>. + * 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`
+ + + + + + + + + + + + + + + + + + + + + LED- + LED+ + VEE + RST + CS2 + CS1 + DB7 + DB6 + DB5 + DB4 + DB3 + DB2 + DB1 + DB0 + EN + R/W + D/I + VO + VDD + VSS + + + + + + + + + + + + + + + + + + + + + + + + + + +
`; + } +} diff --git a/src/index.ts b/src/index.ts index 386fcbe..b40dbca 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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'; diff --git a/src/react-types.ts b/src/react-types.ts index 0316ea9..7f9728b 100644 --- a/src/react-types.ts +++ b/src/react-types.ts @@ -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 = Partial & React.ClassAttributes; @@ -106,6 +107,7 @@ declare global { 'wokwi-hx711': WokwiElement; 'wokwi-ks2e-m-dc5': WokwiElement; 'wokwi-biaxial-stepper': WokwiElement; + 'wokwi-glcd-128x64': WokwiElement; } } }