feat: add ILI9341 Cap Touch display and related components; implement home screen and example sketches
parent
e5d3152488
commit
faa6f6b7b3
|
|
@ -0,0 +1,34 @@
|
||||||
|
#include <SD.h>
|
||||||
|
#include <adafruit_1947_Obj.h>
|
||||||
|
#include <screen.h>
|
||||||
|
#include "ourOSObj.h"
|
||||||
|
#include <blinker.h>
|
||||||
|
|
||||||
|
ourOSObj ourOS;
|
||||||
|
blinker deadMan;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
|
||||||
|
Serial.begin(57600); // Fire up serial for debugging.
|
||||||
|
if (!initScreen(ADAFRUIT_1947,ADA_1947_SHIELD_CS,PORTRAIT)) { // Init screen.
|
||||||
|
Serial.println("NO SCREEN!"); // Screen init failed. Tell user.
|
||||||
|
Serial.flush(); // Make sure the message gets out.
|
||||||
|
while(true); // Lock the process here.
|
||||||
|
}
|
||||||
|
if (!SD.begin(ADA_1947_SHIELD_SDCS)) { // With icons, we now MUST have an SD card.
|
||||||
|
Serial.println("NO SD CARD!"); // Tell user we have no SD card.
|
||||||
|
Serial.flush(); // Make sure the message gets out.
|
||||||
|
while(true); // Lock the process here.
|
||||||
|
}
|
||||||
|
ourEventMgr.begin(); // Startup our event manager.
|
||||||
|
ourOS.begin(); // Boot OS manager.
|
||||||
|
//nextPanel = breakoutApp; // <<-- URISH LOOK HERE!!
|
||||||
|
deadMan.setOnOff(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void loop() { // During loop..
|
||||||
|
|
||||||
|
idle(); // Idlers get their time.
|
||||||
|
ourOS.loop(); // ourOS gets time to pass on to the current panel.
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"author": "Jim Lee",
|
||||||
|
"editor": "wokwi",
|
||||||
|
"parts": [
|
||||||
|
{
|
||||||
|
"type": "wokwi-arduino-mega",
|
||||||
|
"id": "mega",
|
||||||
|
"top": 269.4,
|
||||||
|
"left": -22.8,
|
||||||
|
"attrs": { "__fakeRamSize": "65000" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "board-ili9341-cap-touch",
|
||||||
|
"id": "lcd1",
|
||||||
|
"top": -152.84,
|
||||||
|
"left": 86.02,
|
||||||
|
"attrs": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "wokwi-microsd-card",
|
||||||
|
"id": "sd1",
|
||||||
|
"top": 39.23,
|
||||||
|
"left": 395.41,
|
||||||
|
"rotate": 90,
|
||||||
|
"attrs": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "wokwi-buzzer",
|
||||||
|
"id": "bz1",
|
||||||
|
"top": 499.2,
|
||||||
|
"left": 105.6,
|
||||||
|
"rotate": 180,
|
||||||
|
"attrs": { "volume": "0.025" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": [
|
||||||
|
[ "mega:GND.1", "lcd1:GND", "black", [ "v-48", "h38.2" ] ],
|
||||||
|
[ "mega:52", "lcd1:SCK", "white", [ "h-11.8", "v-258.1", "h-144" ] ],
|
||||||
|
[ "mega:50", "lcd1:MISO", "green", [ "v-8.6", "h36.2", "v-222.8", "h-172.8" ] ],
|
||||||
|
[ "mega:51", "lcd1:MOSI", "blue", [ "v1", "h45.4", "v-242", "h-220.8" ] ],
|
||||||
|
[ "mega:53", "lcd1:D/C", "yellow", [ "v39.5", "h35.8", "v-271", "h-220.8" ] ],
|
||||||
|
[ "mega:5V", "lcd1:VCC", "red", [ "v26", "h261.5", "v-268.7", "h-297.6", "v-76.8" ] ],
|
||||||
|
[ "mega:10", "lcd1:CS", "red", [ "v-28.8", "h18.9" ] ],
|
||||||
|
[ "mega:20", "lcd1:SDA", "blue", [ "v-128.26", "h-83.64" ] ],
|
||||||
|
[ "mega:52", "sd1:SCK", "white", [ "h-11.8", "v-258.1", "h110.33" ] ],
|
||||||
|
[ "mega:50", "sd1:DO", "green", [ "v-8.6", "h36.2", "v-222.8", "h81.65" ] ],
|
||||||
|
[ "mega:53", "sd1:CD", "yellow", [ "v39.5", "h35.8", "v-271", "h81.54" ] ],
|
||||||
|
[ "mega:4", "sd1:CS", "green", [ "v-28.8", "h5.4", "v-28.8", "h206.4" ] ],
|
||||||
|
[ "mega:5V", "sd1:VCC", "red", [ "v26.06", "h261.5", "v-268.76", "h24.08" ] ],
|
||||||
|
[ "mega:GND.1", "sd1:GND", "black", [ "v-48", "h350.25" ] ],
|
||||||
|
[ "mega:51", "sd1:DI", "blue", [ "v1", "h45.4", "v-240", "h23.85" ] ],
|
||||||
|
[ "mega:21", "lcd1:SCL", "white", [ "v-120.21", "h-4.54" ] ],
|
||||||
|
[ "mega:14", "bz1:2", "green", [ "v-19.2", "h165.1", "v230.4", "h-269.2" ] ],
|
||||||
|
[ "mega:GND.2", "bz1:1", "black", [ "v0" ] ]
|
||||||
|
],
|
||||||
|
"dependencies": {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
#include "homeScr.h"
|
||||||
|
#include <lilOS.h>
|
||||||
|
|
||||||
|
#define BAR_Y 286
|
||||||
|
|
||||||
|
|
||||||
|
struct spacer {
|
||||||
|
float startPos;
|
||||||
|
float stepSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
spacer calcSpacer(float numItems, float itemLength, float areaLength) {
|
||||||
|
|
||||||
|
spacer aSpacer;
|
||||||
|
|
||||||
|
numItems++;
|
||||||
|
aSpacer.stepSize = areaLength/numItems;
|
||||||
|
aSpacer.startPos = aSpacer.stepSize - itemLength/2;
|
||||||
|
return aSpacer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
homeScr::homeScr(void)
|
||||||
|
: panel(homeApp,noMenuBar) { }
|
||||||
|
|
||||||
|
|
||||||
|
homeScr::~homeScr(void) { }
|
||||||
|
|
||||||
|
|
||||||
|
char* homeScr::iconPath(int appID,char* iconName) {
|
||||||
|
|
||||||
|
strcpy(pathBuff,ourOSPtr->getPanelFolder(appID));
|
||||||
|
strcat(pathBuff,iconName);
|
||||||
|
return pathBuff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void homeScr::setup(void) {
|
||||||
|
|
||||||
|
int traceY;
|
||||||
|
int traceX;
|
||||||
|
int stepX;
|
||||||
|
appIcon* theAppIcon;
|
||||||
|
spacer barSpacer;
|
||||||
|
|
||||||
|
barSpacer = calcSpacer(3,32,240); // Calculate bar spacing. Num apps, icon width, bar width.
|
||||||
|
traceX = barSpacer.startPos; // Set initial x position.
|
||||||
|
stepX = barSpacer.stepSize; // Set our step size.
|
||||||
|
traceY = BAR_Y; // Set our y location.
|
||||||
|
|
||||||
|
theAppIcon = new appIcon(traceX,traceY,calcApp,iconPath(calcApp,"calc32.bmp")); // Create icon for the calculator.
|
||||||
|
theAppIcon->setMask(&(ourOSPtr->icon32Mask)); // Add the mask to the icon.
|
||||||
|
addObj(theAppIcon); // Send the icon to our drawObj list to be displayed.
|
||||||
|
|
||||||
|
traceX = traceX + stepX;
|
||||||
|
theAppIcon = new appIcon(traceX,traceY,breakoutApp,iconPath(breakoutApp,"breakout.bmp")); // Create icon for the breakout game.
|
||||||
|
theAppIcon->setMask(&(ourOSPtr->icon32Mask)); // Add the mask to the icon. (Its the same mask over and over.)
|
||||||
|
addObj(theAppIcon); // Send the icon to our drawObj list to be displayed.
|
||||||
|
|
||||||
|
traceX = traceX + stepX;
|
||||||
|
theAppIcon = new appIcon(traceX,traceY,iconEditApp,iconPath(iconEditApp,"iconEdit.bmp")); // Create icon for the icon editor disaster.
|
||||||
|
theAppIcon->setMask(&(ourOSPtr->icon32Mask)); // Add the mask.
|
||||||
|
addObj(theAppIcon); // And its off to the races.
|
||||||
|
|
||||||
|
// traceX = traceX + stepX;
|
||||||
|
// theAppIcon = new appIcon(traceX,traceY,starTrekApp,iconPath(starTrekApp,"sTrek32.bmp")); // Create icon for the Star Trek game.
|
||||||
|
// theAppIcon->setMask(&(ourOSPtr->icon32Mask)); // Mask.
|
||||||
|
// addObj(theAppIcon); // Drop it into the list.
|
||||||
|
|
||||||
|
// traceX = traceX + stepX;
|
||||||
|
// theAppIcon = new appIcon(traceX,traceY,testApp,iconPath(testApp,"app32.bmp")); // Create icon for the testApp.
|
||||||
|
// theAppIcon->setMask(&(ourOSPtr->icon32Mask)); // Mask.
|
||||||
|
// addObj(theAppIcon); // Drop it into the list.
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void homeScr::loop(void) { }
|
||||||
|
|
||||||
|
|
||||||
|
void homeScr::doStarField(void) {
|
||||||
|
|
||||||
|
int randNum;
|
||||||
|
colorMapper ourCMapper;
|
||||||
|
colorObj aColor;
|
||||||
|
mapper yMapper(0,282,0,100);
|
||||||
|
float yPercent;
|
||||||
|
|
||||||
|
aColor.setColor(LC_LIGHT_BLUE);
|
||||||
|
aColor.blend(&blue,50);
|
||||||
|
ourCMapper.setColors(&white,&aColor);
|
||||||
|
randomSeed(analogRead(A10));
|
||||||
|
for (int sy=0;sy<282;sy++) {
|
||||||
|
for (int sx=0;sx<240;sx++) {
|
||||||
|
randNum = random(0,400);
|
||||||
|
if (randNum==300) {
|
||||||
|
yPercent = yMapper.map(sy);
|
||||||
|
aColor = ourCMapper.map(yPercent);
|
||||||
|
screen->drawPixel(sx,sy,&aColor);
|
||||||
|
}
|
||||||
|
if (sy<141 && randNum==250) {
|
||||||
|
yPercent = yMapper.map(sy);
|
||||||
|
aColor = ourCMapper.map(yPercent);
|
||||||
|
screen->drawPixel(sx,sy,&aColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void homeScr::drawSelf(void) {
|
||||||
|
|
||||||
|
|
||||||
|
//bmpObj theScreenImage(0,0,240,282,"/system/images/lake.bmp");
|
||||||
|
colorObj lineColor;
|
||||||
|
colorObj scrFadeColor;
|
||||||
|
|
||||||
|
lineColor.setColor(LC_CHARCOAL);
|
||||||
|
lineColor.blend(&blue,20);
|
||||||
|
screen->fillRectGradient(0,282,240,38,&lineColor,&black);
|
||||||
|
|
||||||
|
//theScreenImage.draw();
|
||||||
|
|
||||||
|
scrFadeColor.setColor(LC_LIGHT_BLUE);
|
||||||
|
scrFadeColor.blend(&blue,50);
|
||||||
|
screen->fillRectGradient(0,0,240,282,&black,&scrFadeColor);
|
||||||
|
doStarField();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef homePanel_h
|
||||||
|
#define homePanel_h
|
||||||
|
|
||||||
|
#include "ourOSObj.h"
|
||||||
|
#include <bmpObj.h>
|
||||||
|
|
||||||
|
// *****************************************************
|
||||||
|
// homeScr
|
||||||
|
// *****************************************************
|
||||||
|
|
||||||
|
|
||||||
|
class homeScr : public panel {
|
||||||
|
|
||||||
|
public:
|
||||||
|
homeScr(void);
|
||||||
|
virtual ~homeScr(void);
|
||||||
|
|
||||||
|
char* iconPath(int appID,char* iconName);
|
||||||
|
virtual void setup(void);
|
||||||
|
virtual void loop(void);
|
||||||
|
void doStarField(void);
|
||||||
|
virtual void drawSelf(void);
|
||||||
|
|
||||||
|
char pathBuff[80];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
Adafruit GFX Library
|
||||||
|
Adafruit FT6206 Library
|
||||||
|
Adafruit ILI9341
|
||||||
|
LC_Adafruit_1947@wokwi:b065451f35dab6e1021d78f0f79b6eda6910455d
|
||||||
|
LC_baseTools@wokwi:95340986110645c1b45e55597a7caf4d023d4b4a
|
||||||
|
LC_GUIbase@wokwi:cad247b5fc057dce02f20f7dd8902e0ab3464bb0
|
||||||
|
LC_GUIItems@wokwi:0dabc07df1078c562ee693693d51c280c68131ce
|
||||||
|
LC_GUITextTools@wokwi:0ea40c37c3578be382f9a915362413565a13779f
|
||||||
|
LC_keyboard@wokwi:2f79075f1332b48f679c2ab9f8199344458b2643
|
||||||
|
LC_RPNCalculator@wokwi:c670627d569e21d822d4bc5e3a04c299191e9dce
|
||||||
|
LC_scrollingList@wokwi:57d9e60a3fbfb0aee4291d3ad9b9bb4ff1ad0650
|
||||||
|
LCP_breakout@wokwi:93b61107ec6046ae81014971bbab8821411b066f
|
||||||
|
LCP_rpnCalc@wokwi:e87783abb639c397a4c3aa4c4410cd4912407d2c
|
||||||
|
SD
|
||||||
|
LC_lilOS@wokwi:f56250c6c13ec5691789b86c286c7c6058486d3d
|
||||||
|
LC_Adafruit_1431@wokwi:eedcd67a381fa43cda85fa1776a51984439ed091
|
||||||
|
Adafruit SSD1351 library
|
||||||
|
LC_Adafruit_684@wokwi:7897df6f1e2c1ac20b3d78215333acd765b9d6af
|
||||||
|
Adafruit SSD1331 OLED Driver Library for Arduino
|
||||||
|
LC_offscreen@wokwi:581db7e6748a58e254fd43c3c490143be2c944f3
|
||||||
|
LC_modalAlerts@wokwi:ad3762f077276900373fc61629c0ebc4711fa49d
|
||||||
|
LC_piezoTunes@wokwi:38e1678e85ffa5ddaa666a9fcd8bcc8c793ff430
|
||||||
|
LC_debug@wokwi:0f9acf8678bd2eee8a41d9346ae6c935b05d16f3
|
||||||
|
LC_stampObj@wokwi:cc7cc74900be1cbd4ec7e24b0fe835510be9facd
|
||||||
|
LC_docTools@wokwi:55bb6cfa6138aac31e50c0d60d6ab275e034954a
|
||||||
|
LCP_iconEdit@wokwi:a846c1543dc5f4727bc59057107d1045e475b994
|
||||||
|
LC_SPI@wokwi:78dfdc640f2aa6850c4db21f7fb43b086a87ba86
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
#include "ourOSObj.h"
|
||||||
|
#include <rpnCalc.h>
|
||||||
|
#include <breakout.h>
|
||||||
|
#include <iconEdit.h>
|
||||||
|
//#include <starTrek.h>
|
||||||
|
//#include "testAppPanel.h"
|
||||||
|
#include "homeScr.h"
|
||||||
|
|
||||||
|
#define BEEP_PIN 14 // The digital pin choosen for the beeper.
|
||||||
|
#define SCREEN_PIN 25 // The ananlog pin choosen for the screen backlight.
|
||||||
|
char systemFolder[] = "/system/"; // Where we decided to store the systemp folder on our SD card.
|
||||||
|
char panelFolder[] = "/system/appFiles/"; // Where we decided to store the app folders on our SD card.
|
||||||
|
|
||||||
|
|
||||||
|
// **************************************
|
||||||
|
// ************** ourOSObj **************
|
||||||
|
// **************************************
|
||||||
|
|
||||||
|
|
||||||
|
ourOSObj::ourOSObj(void)
|
||||||
|
: lilOS() { }
|
||||||
|
|
||||||
|
|
||||||
|
ourOSObj::~ourOSObj(void) { }
|
||||||
|
|
||||||
|
|
||||||
|
// The hardware is online, do hookups.
|
||||||
|
int ourOSObj::begin(void) {
|
||||||
|
|
||||||
|
pinMode(BEEP_PIN, OUTPUT); // Setup The beeper pin.
|
||||||
|
digitalWrite(BEEP_PIN, HIGH); // Means off.
|
||||||
|
return lilOS::begin(); // Return result of the inherited
|
||||||
|
}
|
||||||
|
|
||||||
|
//void backlightOn(void) { ourOSPtr->setBrightness(255); }
|
||||||
|
|
||||||
|
// We need to write our own panel creation method.
|
||||||
|
panel* ourOSObj::createPanel(int panelID) {
|
||||||
|
|
||||||
|
panel* result;
|
||||||
|
|
||||||
|
beep();
|
||||||
|
//setBrightness(0);
|
||||||
|
switch (panelID) {
|
||||||
|
case homeApp : result = new homeScr(); break;
|
||||||
|
case calcApp : result = new rpnCalc(this,panelID); break;
|
||||||
|
case iconEditApp : result = new iconEdit(this,panelID); break;
|
||||||
|
case breakoutApp : result = new breakout(this,panelID); break;
|
||||||
|
//case starTrekApp : result = new starTrekPanel(this,panelID); break;
|
||||||
|
//case testApp : result = new testAppPanel(this,panelID); break;
|
||||||
|
default : result = NULL;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Only WE know how to make it beep.
|
||||||
|
void ourOSObj::beep(void) { tone(BEEP_PIN, 750,20); }
|
||||||
|
|
||||||
|
|
||||||
|
// Here's the pin if you want to use it yourself.
|
||||||
|
int ourOSObj::getTonePin(void) {return BEEP_PIN; }
|
||||||
|
|
||||||
|
|
||||||
|
// And how to control the screen brightness.
|
||||||
|
void ourOSObj::setBrightness(byte brightness) { analogWrite(SCREEN_PIN,brightness); }
|
||||||
|
|
||||||
|
|
||||||
|
char* ourOSObj::getSystemFolder(void) { return systemFolder; }
|
||||||
|
|
||||||
|
|
||||||
|
// Hand this an appID and get back a pointer to the path of its data folder.
|
||||||
|
char* ourOSObj::getPanelFolder(int panelID) {
|
||||||
|
|
||||||
|
strcpy(pathBuff,panelFolder);
|
||||||
|
switch (panelID) {
|
||||||
|
case homeApp : return NULL;
|
||||||
|
case calcApp :
|
||||||
|
strcat(pathBuff,"rpnCalc/");
|
||||||
|
return pathBuff;
|
||||||
|
break;
|
||||||
|
case iconEditApp :
|
||||||
|
strcat(pathBuff,"iconEdit/");
|
||||||
|
return pathBuff;
|
||||||
|
break;
|
||||||
|
case breakoutApp :
|
||||||
|
strcat(pathBuff,"breakout/");
|
||||||
|
return pathBuff;
|
||||||
|
break;
|
||||||
|
// case starTrekApp :
|
||||||
|
// strcat(pathBuff,"starTrek/");
|
||||||
|
// return pathBuff;
|
||||||
|
// break;
|
||||||
|
// case testApp :
|
||||||
|
// strcpy(pathBuff,getSystemFolder());
|
||||||
|
// strcat(pathBuff,"icons/standard/");
|
||||||
|
// return pathBuff;
|
||||||
|
// break;
|
||||||
|
default : return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef ourOSObj_h
|
||||||
|
#define ourOSObj_h
|
||||||
|
|
||||||
|
#include <lilOS.h>
|
||||||
|
|
||||||
|
enum apps { homeApp = HOME_PANEL_ID, calcApp, breakoutApp, /*testApp*/iconEditApp/*, starTrekApp*/ };
|
||||||
|
|
||||||
|
class ourOSObj : public lilOS {
|
||||||
|
|
||||||
|
public:
|
||||||
|
ourOSObj(void);
|
||||||
|
virtual ~ourOSObj(void);
|
||||||
|
|
||||||
|
virtual int begin(void); // The hardware is online, do hookups.
|
||||||
|
virtual panel* createPanel(int panelID); // We need to write our own panel creation method.
|
||||||
|
//void backlightOn(void);
|
||||||
|
virtual void beep(void); // Only WE know how to make it beep.
|
||||||
|
virtual int getTonePin(void);
|
||||||
|
virtual void setBrightness(byte brightness); // 0 for full bright 255 for off.
|
||||||
|
virtual char* getSystemFolder(void);
|
||||||
|
virtual char* getPanelFolder(int panelID);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern bmpMask iconMask;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
Downloaded from https://wokwi.com/projects/320027251914572370
|
||||||
|
|
||||||
|
Simulate this project on https://wokwi.com
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
/**
|
||||||
|
* ili9341-test-sketch.ino
|
||||||
|
*
|
||||||
|
* Minimal Adafruit_ILI9341 sketch for emulation tests.
|
||||||
|
* Draws shapes and text on the ILI9341 via hardware SPI.
|
||||||
|
*
|
||||||
|
* Wiring (Arduino Nano, hardware SPI):
|
||||||
|
* SCK → D13 (pin 13, SPI CLK)
|
||||||
|
* MOSI → D11 (pin 11, SPI MOSI)
|
||||||
|
* MISO → D12 (pin 12, SPI MISO)
|
||||||
|
* CS → D10 (pin 10)
|
||||||
|
* DC → D9 (pin 9)
|
||||||
|
* RST → D8 (pin 8)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <Adafruit_GFX.h>
|
||||||
|
#include <Adafruit_ILI9341.h>
|
||||||
|
|
||||||
|
#define TFT_CS 10
|
||||||
|
#define TFT_DC 9
|
||||||
|
#define TFT_RST 8
|
||||||
|
|
||||||
|
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(9600);
|
||||||
|
tft.begin();
|
||||||
|
|
||||||
|
// Fill screen red
|
||||||
|
tft.fillScreen(ILI9341_RED);
|
||||||
|
|
||||||
|
// Draw a white rectangle
|
||||||
|
tft.fillRect(20, 20, 200, 80, ILI9341_WHITE);
|
||||||
|
|
||||||
|
// Draw text
|
||||||
|
tft.setTextColor(ILI9341_BLACK);
|
||||||
|
tft.setTextSize(2);
|
||||||
|
tft.setCursor(30, 40);
|
||||||
|
tft.println("ILI9341 Test");
|
||||||
|
|
||||||
|
// Draw a blue circle
|
||||||
|
tft.fillCircle(120, 200, 50, ILI9341_BLUE);
|
||||||
|
|
||||||
|
// Draw a yellow triangle
|
||||||
|
tft.fillTriangle(60, 280, 120, 260, 180, 280, ILI9341_YELLOW);
|
||||||
|
|
||||||
|
Serial.println("DONE");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Nothing
|
||||||
|
}
|
||||||
|
|
@ -223,6 +223,40 @@
|
||||||
"ili9341"
|
"ili9341"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "ili9341-cap-touch",
|
||||||
|
"tagName": "wokwi-ili9341",
|
||||||
|
"name": "ILI9341 Cap Touch",
|
||||||
|
"category": "displays",
|
||||||
|
"description": "ILI9341 TFT display with FT6206 capacitive touch controller",
|
||||||
|
"thumbnail": "<svg width=\"64\" height=\"64\" xmlns=\"http://www.w3.org/2000/svg\"><rect width=\"64\" height=\"64\" fill=\"#e0e0e0\" rx=\"4\"/><text x=\"50%\" y=\"50%\" text-anchor=\"middle\" dy=\".3em\" font-size=\"9\" fill=\"#666\">ILI9341 CAP</text></svg>",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "flipHorizontal",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": false,
|
||||||
|
"control": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flipVertical",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": false,
|
||||||
|
"control": "text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"defaultValues": {
|
||||||
|
"flipHorizontal": false,
|
||||||
|
"flipVertical": false
|
||||||
|
},
|
||||||
|
"pinCount": 0,
|
||||||
|
"tags": [
|
||||||
|
"ili9341",
|
||||||
|
"ili9341-cap-touch",
|
||||||
|
"tft",
|
||||||
|
"capacitive",
|
||||||
|
"touch"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "lcd2004",
|
"id": "lcd2004",
|
||||||
"tagName": "wokwi-lcd2004",
|
"tagName": "wokwi-lcd2004",
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,13 @@ export const DynamicComponent: React.FC<DynamicComponentProps> = ({
|
||||||
const handleComponentEvent = useSimulatorStore((s) => s.handleComponentEvent);
|
const handleComponentEvent = useSimulatorStore((s) => s.handleComponentEvent);
|
||||||
const running = useSimulatorStore((s) => s.running);
|
const running = useSimulatorStore((s) => s.running);
|
||||||
const simulator = useSimulatorStore((s) => s.simulator);
|
const simulator = useSimulatorStore((s) => s.simulator);
|
||||||
|
// hexEpoch increments each time a new hex is loaded, triggering a fresh
|
||||||
|
// attachEvents call (and re-registration of I2C devices on the new bus).
|
||||||
|
// We intentionally do NOT depend on `running` so that I2C displays and
|
||||||
|
// other protocol parts (SSD1306, DS1307 …) are NOT torn down and
|
||||||
|
// re-created on every stop/play cycle — which previously caused the
|
||||||
|
// display to flash blank and lose its frame buffer.
|
||||||
|
const hexEpoch = useSimulatorStore((s) => s.hexEpoch);
|
||||||
|
|
||||||
// Check if component is interactive (has simulation logic with attachEvents)
|
// Check if component is interactive (has simulation logic with attachEvents)
|
||||||
const logic = PartSimulationRegistry.get(metadata.id || id.split('-')[0]);
|
const logic = PartSimulationRegistry.get(metadata.id || id.split('-')[0]);
|
||||||
|
|
@ -187,7 +194,7 @@ export const DynamicComponent: React.FC<DynamicComponentProps> = ({
|
||||||
const logic = PartSimulationRegistry.get(metadata.id || id.split('-')[0]);
|
const logic = PartSimulationRegistry.get(metadata.id || id.split('-')[0]);
|
||||||
|
|
||||||
let cleanupSimulationEvents: (() => void) | undefined;
|
let cleanupSimulationEvents: (() => void) | undefined;
|
||||||
if (logic && logic.attachEvents && simulator && running) {
|
if (logic && logic.attachEvents && simulator) {
|
||||||
// Helper to find Arduino pin connected to a component pin
|
// Helper to find Arduino pin connected to a component pin
|
||||||
const getArduinoPin = (componentPinName: string): number | null => {
|
const getArduinoPin = (componentPinName: string): number | null => {
|
||||||
const wires = useSimulatorStore.getState().wires.filter(
|
const wires = useSimulatorStore.getState().wires.filter(
|
||||||
|
|
@ -216,7 +223,7 @@ export const DynamicComponent: React.FC<DynamicComponentProps> = ({
|
||||||
el.removeEventListener('button-press', onButtonPress);
|
el.removeEventListener('button-press', onButtonPress);
|
||||||
el.removeEventListener('button-release', onButtonRelease);
|
el.removeEventListener('button-release', onButtonRelease);
|
||||||
};
|
};
|
||||||
}, [id, handleComponentEvent, metadata.id, simulator, running]);
|
}, [id, handleComponentEvent, metadata.id, simulator, hexEpoch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -347,6 +347,11 @@ PartSimulationRegistry.register('buzzer', {
|
||||||
gainNode.gain.value = 0.1;
|
gainNode.gain.value = 0.1;
|
||||||
gainNode.connect(audioCtx.destination);
|
gainNode.connect(audioCtx.destination);
|
||||||
}
|
}
|
||||||
|
// Browser autoplay policy: AudioContext starts in 'suspended' state
|
||||||
|
// until a user gesture has occurred. Resume it here so sound plays.
|
||||||
|
if (audioCtx.state === 'suspended') {
|
||||||
|
audioCtx.resume();
|
||||||
|
}
|
||||||
if (oscillator) {
|
if (oscillator) {
|
||||||
oscillator.frequency.setTargetAtTime(freq, audioCtx.currentTime, 0.01);
|
oscillator.frequency.setTargetAtTime(freq, audioCtx.currentTime, 0.01);
|
||||||
return;
|
return;
|
||||||
|
|
@ -559,6 +564,7 @@ function createLcdSimulation(cols: number, rows: number) {
|
||||||
|
|
||||||
PartSimulationRegistry.register('lcd1602', createLcdSimulation(16, 2));
|
PartSimulationRegistry.register('lcd1602', createLcdSimulation(16, 2));
|
||||||
PartSimulationRegistry.register('lcd2004', createLcdSimulation(20, 4));
|
PartSimulationRegistry.register('lcd2004', createLcdSimulation(20, 4));
|
||||||
|
PartSimulationRegistry.register('lcd2002', createLcdSimulation(20, 2));
|
||||||
|
|
||||||
// ─── ILI9341 TFT Display (SPI) ───────────────────────────────────────────────
|
// ─── ILI9341 TFT Display (SPI) ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -574,7 +580,7 @@ PartSimulationRegistry.register('lcd2004', createLcdSimulation(20, 4));
|
||||||
*
|
*
|
||||||
* DC/RS pin: LOW = command byte, HIGH = data bytes.
|
* DC/RS pin: LOW = command byte, HIGH = data bytes.
|
||||||
*/
|
*/
|
||||||
PartSimulationRegistry.register('ili9341', {
|
const ili9341Simulation = {
|
||||||
attachEvents: (element, avrSimulator, getArduinoPinHelper) => {
|
attachEvents: (element, avrSimulator, getArduinoPinHelper) => {
|
||||||
const el = element as any;
|
const el = element as any;
|
||||||
const pinManager = (avrSimulator as any).pinManager;
|
const pinManager = (avrSimulator as any).pinManager;
|
||||||
|
|
@ -740,4 +746,8 @@ PartSimulationRegistry.register('ili9341', {
|
||||||
unsubscribers.forEach(u => u());
|
unsubscribers.forEach(u => u());
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
|
|
||||||
|
PartSimulationRegistry.register('ili9341', ili9341Simulation);
|
||||||
|
// board-ili9341-cap-touch (Wokwi type) maps to 'ili9341-cap-touch' metadataId — same SPI simulation
|
||||||
|
PartSimulationRegistry.register('ili9341-cap-touch', ili9341Simulation);
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,10 @@ interface SimulatorState {
|
||||||
pinManager: PinManager;
|
pinManager: PinManager;
|
||||||
running: boolean;
|
running: boolean;
|
||||||
compiledHex: string | null;
|
compiledHex: string | null;
|
||||||
|
/** Increments each time a new hex/binary is loaded — used to re-attach
|
||||||
|
* virtual devices (SSD1306, etc.) to the fresh I2C bus without toggling
|
||||||
|
* on every play/stop cycle. */
|
||||||
|
hexEpoch: number;
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
components: Component[];
|
components: Component[];
|
||||||
|
|
@ -115,6 +119,7 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
||||||
pinManager,
|
pinManager,
|
||||||
running: false,
|
running: false,
|
||||||
compiledHex: null,
|
compiledHex: null,
|
||||||
|
hexEpoch: 0,
|
||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
id: 'led-builtin',
|
id: 'led-builtin',
|
||||||
|
|
@ -221,7 +226,11 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
||||||
if (simulator && simulator instanceof AVRSimulator) {
|
if (simulator && simulator instanceof AVRSimulator) {
|
||||||
try {
|
try {
|
||||||
simulator.loadHex(hex);
|
simulator.loadHex(hex);
|
||||||
set({ compiledHex: hex });
|
// Re-register background I2C devices on the fresh bus created by loadHex
|
||||||
|
simulator.addI2CDevice(new VirtualDS1307());
|
||||||
|
simulator.addI2CDevice(new VirtualTempSensor());
|
||||||
|
simulator.addI2CDevice(new I2CMemoryDevice(0x50));
|
||||||
|
set((s) => ({ compiledHex: hex, hexEpoch: s.hexEpoch + 1 }));
|
||||||
console.log('HEX file loaded successfully');
|
console.log('HEX file loaded successfully');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load HEX:', error);
|
console.error('Failed to load HEX:', error);
|
||||||
|
|
@ -236,7 +245,11 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
||||||
if (simulator && simulator instanceof RP2040Simulator) {
|
if (simulator && simulator instanceof RP2040Simulator) {
|
||||||
try {
|
try {
|
||||||
simulator.loadBinary(base64);
|
simulator.loadBinary(base64);
|
||||||
set({ compiledHex: base64 }); // reuse compiledHex as "program loaded" flag
|
// Re-register background I2C devices on the fresh bus
|
||||||
|
simulator.addI2CDevice(new VirtualDS1307() as RP2040I2CDevice);
|
||||||
|
simulator.addI2CDevice(new VirtualTempSensor() as RP2040I2CDevice);
|
||||||
|
simulator.addI2CDevice(new I2CMemoryDevice(0x50) as RP2040I2CDevice);
|
||||||
|
set((s) => ({ compiledHex: base64, hexEpoch: s.hexEpoch + 1 }));
|
||||||
console.log('Binary loaded into RP2040 successfully');
|
console.log('Binary loaded into RP2040 successfully');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load binary:', error);
|
console.error('Failed to load binary:', error);
|
||||||
|
|
@ -249,16 +262,8 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
||||||
startSimulation: () => {
|
startSimulation: () => {
|
||||||
const { simulator } = get();
|
const { simulator } = get();
|
||||||
if (simulator) {
|
if (simulator) {
|
||||||
// Register virtual I2C devices before starting
|
// Background I2C devices are registered in loadHex/loadBinary,
|
||||||
if (simulator instanceof AVRSimulator && simulator.i2cBus) {
|
// so we just need to start the CPU loop here.
|
||||||
simulator.addI2CDevice(new VirtualDS1307());
|
|
||||||
simulator.addI2CDevice(new VirtualTempSensor());
|
|
||||||
simulator.addI2CDevice(new I2CMemoryDevice(0x50));
|
|
||||||
} else if (simulator instanceof RP2040Simulator) {
|
|
||||||
simulator.addI2CDevice(new VirtualDS1307() as RP2040I2CDevice);
|
|
||||||
simulator.addI2CDevice(new VirtualTempSensor() as RP2040I2CDevice);
|
|
||||||
simulator.addI2CDevice(new I2CMemoryDevice(0x50) as RP2040I2CDevice);
|
|
||||||
}
|
|
||||||
simulator.start();
|
simulator.start();
|
||||||
set({ running: true, serialMonitorOpen: true });
|
set({ running: true, serialMonitorOpen: true });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue