Hi, I am currently trying to follow a video on youtube that uses a TTGO ESP-32-WROVER to connect to a library called the FabGL library that allows you to use a PS2 mouse, PS2 keyboard, and simulate Windows 3.0 OS all at the same time. I am using this exact OS simulation example that uses an emulator to simulate the Windows 3.0 OS to achieve what is done in the YouTube video but I am having a hard time getting the PS2 Mouse to work with the TTGO Esp32-Wrover board as was shown in the video. This is because when I try and connect the PS2 Mouse to pins 26 and 27 to the Esp32 board the mouse itself responds, however when I try and plug in the Monitor to the VGA connector on pins 18 (Green_0), 19 (Green_1), 22 (Red_1), 21 (Red_0), 5 (Blue_1), and 4 (Blue_0), the mouse pointer is not present on the screen as seen in this picture:
I have also used the proper 120 Ohm and 2k Ohm resistors that are connected to Power (3.3v) in the former diagram and pins 26 and 27 as seen in the first diagram, as well as using the connections in the latter diagram which was posted on the FabGl Documentation Website:
Here is the full schematic of what I have built using the previous diagrams on the FabGL Website mentioned above:
Here is some code to the FabGL example portion of the library that I am using currently and that the person in the video is using:
/*
Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - <http://www.fabgl.com>
Copyright (c) 2019-2021 Fabrizio Di Vittorio.
All rights reserved.
* Please contact fdivitto2013@gmail.com if you need a commercial license.
* This library and related software is available under GPL v3.
FabGL 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.
FabGL 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 FabGL. If not, see <http://www.gnu.org/licenses/>.
*/
/* Instructions:
- to run this application you need an ESP32 with PSRAM installed and an SD-CARD slot (ie TTGO VGA32 v1.4 or FabGL Development Board with WROVER)
- open this with Arduino and make sure PSRAM is DISABLED
- partition scheme must be: Huge App
- compile and upload the sketch
*/
#pragma message "This sketch requires Tools->Partition Scheme = Huge APP"
#include <memory>
#include "esp32-hal-psram.h"
extern "C" {
#include "esp_spiram.h"
}
#include "esp_sntp.h"
#include <Preferences.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include "fabgl.h"
#include "mconf.h"
#include "machine.h"
using std::unique_ptr;
using fabgl::StringList;
using fabgl::imin;
using fabgl::imax;
Preferences preferences;
InputBox ibox;
Machine * machine;
// noinit! Used to maintain datetime between reboots
__NOINIT_ATTR static timeval savedTimeValue;
static bool wifiConnected = false;
static bool downloadOK = false;
// try to connected using saved parameters
bool tryToConnect()
{
bool connected = WiFi.status() == WL_CONNECTED;
if (!connected) {
char SSID[32] = "";
char psw[32] = "";
if (preferences.getString("SSID", SSID, sizeof(SSID)) && preferences.getString("WiFiPsw", psw, sizeof(psw))) {
ibox.progressBox("", "Abort", true, 200, [&](fabgl::ProgressForm * form) {
WiFi.begin(SSID, psw);
for (int i = 0; i < 32 && WiFi.status() != WL_CONNECTED; ++i) {
if (!form->update(i * 100 / 32, "Connecting to %s...", SSID))
break;
delay(500);
if (i == 16)
WiFi.reconnect();
}
connected = (WiFi.status() == WL_CONNECTED);
});
// show to user the connection state
if (!connected) {
WiFi.disconnect();
ibox.message("", "WiFi Connection failed!");
}
}
}
return connected;
}
bool checkWiFi()
{
wifiConnected = tryToConnect();
if (!wifiConnected) {
// configure WiFi?
if (ibox.message("WiFi Configuration", "Configure WiFi?", "No", "Yes") == InputResult::Enter) {
// repeat until connected or until user cancels
do {
// yes, scan for networks showing a progress dialog box
int networksCount = 0;
ibox.progressBox("", nullptr, false, 200, [&](fabgl::ProgressForm * form) {
form->update(0, "Scanning WiFi networks...");
networksCount = WiFi.scanNetworks();
});
// are there available WiFi?
if (networksCount > 0) {
// yes, show a selectable list
StringList list;
for (int i = 0; i < networksCount; ++i)
list.appendFmt("%s (%d dBm)", WiFi.SSID(i).c_str(), WiFi.RSSI(i));
int s = ibox.menu("WiFi Configuration", "Please select a WiFi network", &list);
// user selected something?
if (s > -1) {
// yes, ask for WiFi password
char psw[32] = "";
if (ibox.textInput("WiFi Configuration", "Insert WiFi password", psw, 31, "Cancel", "OK", true) == InputResult::Enter) {
// user pressed OK, connect to WiFi...
preferences.putString("SSID", WiFi.SSID(s).c_str());
preferences.putString("WiFiPsw", psw);
wifiConnected = tryToConnect();
// show to user the connection state
if (wifiConnected)
ibox.message("", "Connection succeeded!");
} else
break;
} else
break;
} else {
// there is no WiFi
ibox.message("", "No WiFi network found!");
break;
}
WiFi.scanDelete();
} while (!wifiConnected);
}
}
return wifiConnected;
}
// handle soft restart
void shutdownHandler()
{
// save current datetime into Preferences
gettimeofday(&savedTimeValue, nullptr);
}
void updateDateTime()
{
// Set timezone
setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0/3", 1);
tzset();
// get datetime from savedTimeValue? (noinit section)
if (esp_reset_reason() == ESP_RST_SW) {
// adjust time taking account elapsed time since ESP32 started
savedTimeValue.tv_usec += (int) esp_timer_get_time();
savedTimeValue.tv_sec += savedTimeValue.tv_usec / 1000000;
savedTimeValue.tv_usec %= 1000000;
settimeofday(&savedTimeValue, nullptr);
return;
}
if (checkWiFi()) {
// we need time right now
ibox.progressBox("", nullptr, true, 200, [&](fabgl::ProgressForm * form) {
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, (char*)"pool.ntp.org");
sntp_init();
for (int i = 0; i < 12 && sntp_get_sync_status() != SNTP_SYNC_STATUS_COMPLETED; ++i) {
form->update(i * 100 / 12, "Getting date-time from SNTP...");
delay(500);
}
sntp_stop();
ibox.setAutoOK(2);
ibox.message("", "Date and Time updated. Restarting...");
esp_restart();
});
} else {
// set default time
auto tm = (struct tm){ .tm_sec = 0, .tm_min = 0, .tm_hour = 8, .tm_mday = 14, .tm_mon = 7, .tm_year = 84 };
auto now = (timeval){ .tv_sec = mktime(&tm) };
settimeofday(&now, nullptr);
}
}
// download specified filename from URL
bool downloadURL(char const * URL, FILE * file)
{
downloadOK = false;
char const * filename = strrchr(URL, '/') + 1;
ibox.progressBox("", "Abort", true, 380, [&](fabgl::ProgressForm * form) {
form->update(0, "Preparing to download %s", filename);
HTTPClient http;
http.begin(URL);
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
if (file) {
int tlen = http.getSize();
int len = tlen;
auto buf = (uint8_t*) SOC_EXTRAM_DATA_LOW; // use PSRAM as buffer
WiFiClient * stream = http.getStreamPtr();
int dsize = 0;
while (http.connected() && (len > 0 || len == -1)) {
size_t size = stream->available();
if (size) {
int c = stream->readBytes(buf, size);
auto wr = fwrite(buf, 1, c, file);
if (wr != c) {
dsize = 0;
break; // writing failure!
}
dsize += c;
if (len > 0)
len -= c;
if (!form->update((int64_t)dsize * 100 / tlen, "Downloading %s (%.2f / %.2f MB)", filename, (double)dsize / 1048576.0, tlen / 1048576.0))
break;
}
}
downloadOK = (len == 0 || (len == -1 && dsize > 0));
}
}
http.end();
});
return downloadOK;
}
// return filename if successfully downloaded or already exist
char const * getDisk(char const * url)
{
FileBrowser fb(SD_MOUNT_PATH);
char const * filename = nullptr;
if (url) {
if (strncmp("://", url + 4, 3) == 0) {
// this is actually an URL
filename = strrchr(url, '/') + 1;
if (filename && !fb.exists(filename, false)) {
// disk doesn't exist, try to download
if (!checkWiFi())
return nullptr;
auto file = fb.openFile(filename, "wb");
bool success = downloadURL(url, file);
fclose(file);
if (!success) {
fb.remove(filename);
return nullptr;
}
}
} else {
// this is just a file
if (fb.filePathExists(url))
filename = url;
}
}
return filename;
}
// user pressed SYSREQ (ALT + PRINTSCREEN)
void sysReqCallback()
{
machine->graphicsAdapter()->enableVideo(false);
ibox.begin(VGA_640x480_60Hz, 500, 400, 4);
int s = ibox.menu("", "Select a command", "Restart (Boot Menu);Continue;Mount Disk");
switch (s) {
// Restart
case 0:
esp_restart();
break;
// Mount Disk
case 2:
{
int s = ibox.menu("", "Select Drive", "Floppy A (fd0);Floppy B (fd1)");
if (s > -1) {
constexpr int MAXNAMELEN = 256;
unique_ptr<char[]> dir(new char[MAXNAMELEN + 1] { '/', 'S', 'D', 0 } );
unique_ptr<char[]> filename(new char[MAXNAMELEN + 1] { 0 } );
if (machine->diskFilename(s))
strcpy(filename.get(), machine->diskFilename(s));
if (ibox.fileSelector("Select Disk Image", "Image Filename", dir.get(), MAXNAMELEN, filename.get(), MAXNAMELEN) == InputResult::Enter) {
machine->setDriveImage(s, filename.get());
}
}
break;
}
// Continue
default:
break;
}
ibox.end();
PS2Controller::keyboard()->enableVirtualKeys(false, false); // don't use virtual keys
machine->graphicsAdapter()->enableVideo(true);
}
void setup()
{
Serial.begin(115200); delay(500); printf("\n\n\nReset\n\n");// DEBUG ONLY
disableCore0WDT();
delay(100); // experienced crashes without this delay!
disableCore1WDT();
preferences.begin("PCEmulator", false);
// uncomment to clear preferences
//preferences.clear();
ibox.begin(VGA_640x480_60Hz, 500, 400, 4);
ibox.setBackgroundColor(RGB888(0, 0, 0));
ibox.onPaint = [&](Canvas * canvas) { drawInfo(canvas); };
// we need PSRAM for this app, but we will handle it manually, so please DO NOT enable PSRAM on your development env
#ifdef BOARD_HAS_PSRAM
ibox.message("Warning!", "Please disable PSRAM to improve performance!");
#endif
// note: we use just 2MB of PSRAM so the infamous PSRAM bug should not happen. But to avoid gcc compiler hack (-mfix-esp32-psram-cache-issue)
// we enable PSRAM at runtime, otherwise the hack slows down CPU too much (PSRAM_HACK is no more required).
if (esp_spiram_init() != ESP_OK)
ibox.message("Error!", "This app requires a board with PSRAM!", nullptr, nullptr);
#ifndef BOARD_HAS_PSRAM
esp_spiram_init_cache();
#endif
if (!FileBrowser::mountSDCard(false, SD_MOUNT_PATH, 8)) // @TODO: reduce to 4?
ibox.message("Error!", "This app requires a SD-CARD!", nullptr, nullptr);
// uncomment to format SD!
//FileBrowser::format(fabgl::DriveType::SDCard, 0);
esp_register_shutdown_handler(shutdownHandler);
updateDateTime();
// machine configurations
MachineConf mconf;
// show a list of machine configurations
ibox.setAutoOK(6);
int idx = preferences.getInt("dconf", 0);
for (bool showDialog = true; showDialog; ) {
loadMachineConfiguration(&mconf);
StringList dconfs;
for (auto conf = mconf.getFirstItem(); conf; conf = conf->next)
dconfs.append(conf->desc);
dconfs.select(idx, true);
ibox.setupButton(0, "Browse Files");
ibox.setupButton(1, "Options", "Edit;New;Remove", 52);
auto r = ibox.select("Machine Configurations", "Please select a machine configuration", &dconfs, nullptr, "Run");
idx = dconfs.getFirstSelected();
switch (r) {
case InputResult::ButtonExt0:
// Browse Files
ibox.folderBrowser("Browse Files", SD_MOUNT_PATH);
break;
case InputResult::ButtonExt1:
// Options
switch (ibox.selectedSubItem()) {
// Edit
case 0:
editConfigDialog(&ibox, &mconf, idx);
break;
// New
case 1:
newConfigDialog(&ibox, &mconf, idx);
break;
// Remove
case 2:
delConfigDialog(&ibox, &mconf, idx);
break;
};
break;
case InputResult::Enter:
// Run
showDialog = false;
break;
default:
break;
}
// next selection will not have timeout
ibox.setAutoOK(0);
}
idx = imax(idx, 0);
preferences.putInt("dconf", idx);
// setup selected configuration
auto conf = mconf.getItem(idx);
char const * diskFilename[DISKCOUNT];
downloadOK = true;
for (int i = 0; i < DISKCOUNT && downloadOK; ++i)
diskFilename[i] = getDisk(conf->disk[i]);
if (!downloadOK || (!diskFilename[0] && !diskFilename[2])) {
// unable to get boot disks
ibox.message("Error!", "Unable to get system disks!");
esp_restart();
}
if (wifiConnected) {
// disk downloaded from the Internet, need to reboot to fully disable wifi
ibox.setAutoOK(2);
ibox.message("", "Disks downloaded. Restarting...");
esp_restart();
}
ibox.end();
machine = new Machine;
machine->setBaseDirectory(SD_MOUNT_PATH);
for (int i = 0; i < DISKCOUNT; ++i)
machine->setDriveImage(i, diskFilename[i], conf->cylinders[i], conf->heads[i], conf->sectors[i]);
machine->setBootDrive(conf->bootDrive);
/*
printf("MALLOC_CAP_32BIT : %d bytes (largest %d bytes)\r\n", heap_caps_get_free_size(MALLOC_CAP_32BIT), heap_caps_get_largest_free_block(MALLOC_CAP_32BIT));
printf("MALLOC_CAP_8BIT : %d bytes (largest %d bytes)\r\n", heap_caps_get_free_size(MALLOC_CAP_8BIT), heap_caps_get_largest_free_block(MALLOC_CAP_8BIT));
printf("MALLOC_CAP_DMA : %d bytes (largest %d bytes)\r\n\n", heap_caps_get_free_size(MALLOC_CAP_DMA), heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
heap_caps_dump_all();
*/
machine->setSysReqCallback(sysReqCallback);
machine->run();
}
#if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK
namespace fabgl {
extern volatile uint64_t s_vgapalctrlcycles;
}
using fabgl::s_vgapalctrlcycles;
#endif
void loop()
{
#if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK
static uint32_t tcpu = 0, s1 = 0, count = 0;
tcpu = machine->ticksCounter();
s_vgapalctrlcycles = 0;
s1 = fabgl::getCycleCount();
delay(1000);
printf("%d\tCPU: %d", count, machine->ticksCounter() - tcpu);
printf(" Graph: %lld / %d (%d%%)\n", s_vgapalctrlcycles, fabgl::getCycleCount() - s1, (int)((double)s_vgapalctrlcycles/240000000*100));
++count;
#else
vTaskDelete(NULL);
#endif
}
Here is the Esp32-Wrover T8 v.1.7 pinout that that I am also using in the schematic above in this project to get the PS2 Mouse to work:
Any help as to what I am doing wrong would be greatly appreciated, thank you very much!