From f019cbdf943a3a1f26b18fb92738cd57ca682bcd Mon Sep 17 00:00:00 2001 From: TheBakedPotato <11617336+TheBakedPotato@users.noreply.github.com> Date: Thu, 30 Oct 2025 22:07:03 -0400 Subject: [PATCH 01/18] 271: First skeleton of a 'test' type for running a factory test --- Firmware/factoryTestNew/.gitignore | 5 + Firmware/factoryTestNew/include/README | 37 ++++++++ Firmware/factoryTestNew/lib/README | 46 +++++++++ Firmware/factoryTestNew/lib/uart/LoopBack.cpp | 15 +++ Firmware/factoryTestNew/lib/uart/LoopBack.h | 19 ++++ Firmware/factoryTestNew/platformio.ini | 14 +++ Firmware/factoryTestNew/src/main.ino | 93 +++++++++++++++++++ Firmware/factoryTestNew/test/README | 11 +++ 8 files changed, 240 insertions(+) create mode 100644 Firmware/factoryTestNew/.gitignore create mode 100644 Firmware/factoryTestNew/include/README create mode 100644 Firmware/factoryTestNew/lib/README create mode 100644 Firmware/factoryTestNew/lib/uart/LoopBack.cpp create mode 100644 Firmware/factoryTestNew/lib/uart/LoopBack.h create mode 100644 Firmware/factoryTestNew/platformio.ini create mode 100644 Firmware/factoryTestNew/src/main.ino create mode 100644 Firmware/factoryTestNew/test/README diff --git a/Firmware/factoryTestNew/.gitignore b/Firmware/factoryTestNew/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/Firmware/factoryTestNew/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/Firmware/factoryTestNew/include/README b/Firmware/factoryTestNew/include/README new file mode 100644 index 0000000..49819c0 --- /dev/null +++ b/Firmware/factoryTestNew/include/README @@ -0,0 +1,37 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the convention is to give header files names that end with `.h'. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/Firmware/factoryTestNew/lib/README b/Firmware/factoryTestNew/lib/README new file mode 100644 index 0000000..9379397 --- /dev/null +++ b/Firmware/factoryTestNew/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into the executable file. + +The source code of each library should be placed in a separate directory +("lib/your_library_name/[Code]"). + +For example, see the structure of the following example libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +Example contents of `src/main.c` using Foo and Bar: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +The PlatformIO Library Dependency Finder will find automatically dependent +libraries by scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/Firmware/factoryTestNew/lib/uart/LoopBack.cpp b/Firmware/factoryTestNew/lib/uart/LoopBack.cpp new file mode 100644 index 0000000..0845072 --- /dev/null +++ b/Firmware/factoryTestNew/lib/uart/LoopBack.cpp @@ -0,0 +1,15 @@ +#include "LoopBack.h" + +LoopBack::LoopBack(int8_t uart1ReceivePin, int8_t uart1TransmitPin, int8_t uart2ReceivePin, int8_t uart2TransmitPin, unsigned long baudRate) + : uart1(HardwareSerial(1)), + uart2(HardwareSerial(2)) +{ + uart1.begin(baudRate, SERIAL_8N1, uart1ReceivePin, uart1TransmitPin); + uart2.begin(baudRate, SERIAL_8N1, uart2ReceivePin, uart2TransmitPin); +} + +LoopBack::~LoopBack() +{ + uart1.end(); + uart2.end(); +} \ No newline at end of file diff --git a/Firmware/factoryTestNew/lib/uart/LoopBack.h b/Firmware/factoryTestNew/lib/uart/LoopBack.h new file mode 100644 index 0000000..431ca58 --- /dev/null +++ b/Firmware/factoryTestNew/lib/uart/LoopBack.h @@ -0,0 +1,19 @@ +#ifndef LOOP_BACK_H +#define LOOP_BACK_H + +#include + +class LoopBack +{ +public: + LoopBack(int8_t uart1ReceivePin, int8_t uart1TransmitPin, int8_t uart2ReceivePin, int8_t uart2TransmitPin, unsigned long baudRate = DEFAULT_BAUD_RATE); + ~LoopBack(); + +private: + static const unsigned long DEFAULT_BAUD_RATE = 115200; + + HardwareSerial uart1; + HardwareSerial uart2; +}; + +#endif \ No newline at end of file diff --git a/Firmware/factoryTestNew/platformio.ini b/Firmware/factoryTestNew/platformio.ini new file mode 100644 index 0000000..4b30716 --- /dev/null +++ b/Firmware/factoryTestNew/platformio.ini @@ -0,0 +1,14 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp32dev] +platform = espressif32 +board = esp32dev +framework = arduino diff --git a/Firmware/factoryTestNew/src/main.ino b/Firmware/factoryTestNew/src/main.ino new file mode 100644 index 0000000..8a2a062 --- /dev/null +++ b/Firmware/factoryTestNew/src/main.ino @@ -0,0 +1,93 @@ +/********* + Rui Santos & Sara Santos - Random Nerd Tutorials + Complete instructions at https://RandomNerdTutorials.com/esp32-uart-communication-serial-arduino/ + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files. + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +*********/ + +/** +This program demonstrates use of all three UARTs on the ESP32. + +Modified by Robert L. Read to perform a loop back, Dec. 15, 2024 +Modified to make UART 1 pins GPIO 2 and 5 by Forrest Lee Erickson, Dec. 16, 2024 +Modified to add UART 2 on pins GPIO 16 and 17 by Forrest Lee Erickson, Dec. 16, 2024 +**/ + +// Define TX and RX pins for UART (change if needed) +// On a ESP32 Dev Kit: https://lastminuteengineers.com/esp32-pinout-reference/ + +// The GPIO7 and GPIO8 are NOT exposed, even though Lee has asked me to test +// In this issue: https://github.com/PubInv/krake/issues/109 +// I am therefore testing with GPIO12 and GPIO13 (also labeled D12 and D13 in the ESP32 Dev Kit) + +// Tested and works !!! +#define TXD1 2 +#define RXD1 15 + +// Tested and works !!! +#define TXD2 17 +#define RXD2 16 + +// Tested and found to fail on 7 and 9. because of ESP32 flash memory conflict. +// #define TXD1 7 +// #define RXD1 8 + +#include + +// Use for UART communication +HardwareSerial mySerialUART1(1); +HardwareSerial mySerialUART2(2); + +int counter = 0; + +void setup() +{ + Serial.begin(115200); // UART 0 setup + while (!Serial) + { + ; // wait for serial port to connect. Needed for native USB + } + + mySerialUART1.begin(115200, SERIAL_8N1, RXD1, TXD1); // UART1 setup + while (!mySerialUART1) + { + ; // wait for UART1 to connect. + } + mySerialUART2.begin(115200, SERIAL_8N1, RXD2, TXD2); // UART2 setup + while (!mySerialUART2) + { + ; // wait for UART2 to connect. + } + + delay(500); + Serial.println("ESP32 UART0 Transmitter"); + mySerialUART1.println("ESP32 UART1 Transmitter"); + mySerialUART2.println("ESP32 UART2 Transmitter"); +} + +void loop() +{ + + while (mySerialUART1.available()) + { + Serial.println("Bytes Available UART1! :"); + Serial.write(mySerialUART1.read()); + } + + while (mySerialUART2.available()) + { + // Serial.println("Bytes Available! :"); + Serial.write(mySerialUART2.read()); + } + + // Send message over UART + mySerialUART1.print("UART1: "); + mySerialUART1.println(String(counter % 6)); + // mySerialUART1.println("\n"); + + Serial.println("Sent: " + String(counter % 6)); + + counter++; // increment the counter so the message changes. + + delay(1000); // Lets not got too fast to read. +} \ No newline at end of file diff --git a/Firmware/factoryTestNew/test/README b/Firmware/factoryTestNew/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/Firmware/factoryTestNew/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html From 583c59d952b18761b5edc81d9ffcd2181e25d793 Mon Sep 17 00:00:00 2001 From: TheBakedPotato <11617336+TheBakedPotato@users.noreply.github.com> Date: Sat, 1 Nov 2025 18:40:38 -0400 Subject: [PATCH 02/18] 271: Started laying out the 'loop back' test more --- Firmware/factoryTestNew/lib/uart/LoopBack.cpp | 30 +++++++++++- Firmware/factoryTestNew/lib/uart/LoopBack.h | 4 ++ Firmware/factoryTestNew/src/main.ino | 49 ++----------------- 3 files changed, 38 insertions(+), 45 deletions(-) diff --git a/Firmware/factoryTestNew/lib/uart/LoopBack.cpp b/Firmware/factoryTestNew/lib/uart/LoopBack.cpp index 0845072..5b43ee0 100644 --- a/Firmware/factoryTestNew/lib/uart/LoopBack.cpp +++ b/Firmware/factoryTestNew/lib/uart/LoopBack.cpp @@ -2,7 +2,8 @@ LoopBack::LoopBack(int8_t uart1ReceivePin, int8_t uart1TransmitPin, int8_t uart2ReceivePin, int8_t uart2TransmitPin, unsigned long baudRate) : uart1(HardwareSerial(1)), - uart2(HardwareSerial(2)) + uart2(HardwareSerial(2)), + counter(0) { uart1.begin(baudRate, SERIAL_8N1, uart1ReceivePin, uart1TransmitPin); uart2.begin(baudRate, SERIAL_8N1, uart2ReceivePin, uart2TransmitPin); @@ -12,4 +13,31 @@ LoopBack::~LoopBack() { uart1.end(); uart2.end(); +} + +void LoopBack::run(HardwareSerial &serial) +{ + + while (uart1.available()) + { + Serial.println("Bytes Available UART1! :"); + Serial.write(uart1.read()); + } + + while (uart2.available()) + { + // Serial.println("Bytes Available! :"); + Serial.write(uart2.read()); + } + + // Send message over UART + uart1.print("UART1: "); + uart1.println(String(counter % 6)); + // mySerialUART1.println("\n"); + + Serial.println("Sent: " + String(counter % 6)); + + counter++; // increment the counter so the message changes. + + delay(1000); // Lets not got too fast to read. } \ No newline at end of file diff --git a/Firmware/factoryTestNew/lib/uart/LoopBack.h b/Firmware/factoryTestNew/lib/uart/LoopBack.h index 431ca58..0e4e4cf 100644 --- a/Firmware/factoryTestNew/lib/uart/LoopBack.h +++ b/Firmware/factoryTestNew/lib/uart/LoopBack.h @@ -9,11 +9,15 @@ class LoopBack LoopBack(int8_t uart1ReceivePin, int8_t uart1TransmitPin, int8_t uart2ReceivePin, int8_t uart2TransmitPin, unsigned long baudRate = DEFAULT_BAUD_RATE); ~LoopBack(); + void run(HardwareSerial &serial); + private: static const unsigned long DEFAULT_BAUD_RATE = 115200; HardwareSerial uart1; HardwareSerial uart2; + + int counter; }; #endif \ No newline at end of file diff --git a/Firmware/factoryTestNew/src/main.ino b/Firmware/factoryTestNew/src/main.ino index 8a2a062..8d94526 100644 --- a/Firmware/factoryTestNew/src/main.ino +++ b/Firmware/factoryTestNew/src/main.ino @@ -34,60 +34,21 @@ Modified to add UART 2 on pins GPIO 16 and 17 by Forrest Lee Erickson, Dec. 16, #include -// Use for UART communication -HardwareSerial mySerialUART1(1); -HardwareSerial mySerialUART2(2); +#include -int counter = 0; +LoopBack loopBackTest(RXD1, TXD1, RXD2, TXD2); void setup() { Serial.begin(115200); // UART 0 setup - while (!Serial) - { - ; // wait for serial port to connect. Needed for native USB - } - - mySerialUART1.begin(115200, SERIAL_8N1, RXD1, TXD1); // UART1 setup - while (!mySerialUART1) - { - ; // wait for UART1 to connect. - } - mySerialUART2.begin(115200, SERIAL_8N1, RXD2, TXD2); // UART2 setup - while (!mySerialUART2) - { - ; // wait for UART2 to connect. - } delay(500); Serial.println("ESP32 UART0 Transmitter"); - mySerialUART1.println("ESP32 UART1 Transmitter"); - mySerialUART2.println("ESP32 UART2 Transmitter"); + // mySerialUART1.println("ESP32 UART1 Transmitter"); + // mySerialUART2.println("ESP32 UART2 Transmitter"); } void loop() { - - while (mySerialUART1.available()) - { - Serial.println("Bytes Available UART1! :"); - Serial.write(mySerialUART1.read()); - } - - while (mySerialUART2.available()) - { - // Serial.println("Bytes Available! :"); - Serial.write(mySerialUART2.read()); - } - - // Send message over UART - mySerialUART1.print("UART1: "); - mySerialUART1.println(String(counter % 6)); - // mySerialUART1.println("\n"); - - Serial.println("Sent: " + String(counter % 6)); - - counter++; // increment the counter so the message changes. - - delay(1000); // Lets not got too fast to read. + loopBackTest.run(Serial); } \ No newline at end of file From b81ea60c30bea265adfe71cda934ef2bd3ce1371 Mon Sep 17 00:00:00 2001 From: Nagham Date: Sun, 23 Nov 2025 06:50:00 -0500 Subject: [PATCH 03/18] FactoryTest_Krake01, Factory test files collage. --- Firmware/GPAD_API/GPAD_API.ino | 4 +- .../DFPlayerTD5580ATest.cpp | 280 ++++++ .../FactoryTest_Krake01/DFPlayerTD5580ATest.h | 17 + .../FactoryTest_Krake01.ino | 70 ++ .../FactoryTest_Krake01/Krake_MQTT.cpp | 279 ++++++ .../FactoryTest_Krake01/Krake_MQTT.h | 17 + .../FactoryTest_Krake01/LCD_Test_2.cpp | 50 + .../FactoryTest_Krake01/LCD_Test_2.h | 17 + .../FactoryTest_Krake01/LCD_nI2C_scanner.cpp | 43 + .../FactoryTest_Krake01/LCD_nI2C_scanner.h | 17 + .../factoryTest/FactoryTest_Krake01/OTA.cpp | 41 + .../factoryTest/FactoryTest_Krake01/OTA.h | 17 + .../FactoryTest_Krake01/RotaryEncoderTest.cpp | 94 ++ .../FactoryTest_Krake01/RotaryEncoderTest.h | 17 + .../FactoryTest_Krake01/Serial.cpp | 107 ++ .../factoryTest/FactoryTest_Krake01/Serial.h | 17 + .../WiFiAccessPointWithMAC.cpp | 114 +++ .../WiFiAccessPointWithMAC.h | 17 + PWA_REV2/Gerbers/PWA_REV2-B_Cu.gbr | 544 ++++++++--- PWA_REV2/Gerbers/PWA_REV2-B_Mask.gbr | 4 +- PWA_REV2/Gerbers/PWA_REV2-B_Paste.gbr | 4 +- PWA_REV2/Gerbers/PWA_REV2-B_Silkscreen.gbr | 4 +- PWA_REV2/Gerbers/PWA_REV2-Edge_Cuts.gbr | 4 +- PWA_REV2/Gerbers/PWA_REV2-F_Cu.gbr | 922 +++++++++--------- PWA_REV2/Gerbers/PWA_REV2-F_Mask.gbr | 4 +- PWA_REV2/Gerbers/PWA_REV2-F_Paste.gbr | 4 +- PWA_REV2/Gerbers/PWA_REV2-F_Silkscreen.gbr | 4 +- PWA_REV2/Gerbers/PWA_REV2.drl | 8 +- 28 files changed, 2081 insertions(+), 639 deletions(-) create mode 100644 Firmware/factoryTest/FactoryTest_Krake01/DFPlayerTD5580ATest.cpp create mode 100644 Firmware/factoryTest/FactoryTest_Krake01/DFPlayerTD5580ATest.h create mode 100644 Firmware/factoryTest/FactoryTest_Krake01/FactoryTest_Krake01.ino create mode 100644 Firmware/factoryTest/FactoryTest_Krake01/Krake_MQTT.cpp create mode 100644 Firmware/factoryTest/FactoryTest_Krake01/Krake_MQTT.h create mode 100644 Firmware/factoryTest/FactoryTest_Krake01/LCD_Test_2.cpp create mode 100644 Firmware/factoryTest/FactoryTest_Krake01/LCD_Test_2.h create mode 100644 Firmware/factoryTest/FactoryTest_Krake01/LCD_nI2C_scanner.cpp create mode 100644 Firmware/factoryTest/FactoryTest_Krake01/LCD_nI2C_scanner.h create mode 100644 Firmware/factoryTest/FactoryTest_Krake01/OTA.cpp create mode 100644 Firmware/factoryTest/FactoryTest_Krake01/OTA.h create mode 100644 Firmware/factoryTest/FactoryTest_Krake01/RotaryEncoderTest.cpp create mode 100644 Firmware/factoryTest/FactoryTest_Krake01/RotaryEncoderTest.h create mode 100644 Firmware/factoryTest/FactoryTest_Krake01/Serial.cpp create mode 100644 Firmware/factoryTest/FactoryTest_Krake01/Serial.h create mode 100644 Firmware/factoryTest/FactoryTest_Krake01/WiFiAccessPointWithMAC.cpp create mode 100644 Firmware/factoryTest/FactoryTest_Krake01/WiFiAccessPointWithMAC.h diff --git a/Firmware/GPAD_API/GPAD_API.ino b/Firmware/GPAD_API/GPAD_API.ino index a8cfa37..71c3839 100644 --- a/Firmware/GPAD_API/GPAD_API.ino +++ b/Firmware/GPAD_API/GPAD_API.ino @@ -145,8 +145,8 @@ const int LED_COUNT = sizeof(LED_PINS) / sizeof(LED_PINS[0]); // const char* password = "$Suve07$$"; // Austin network -const char* ssid = "readfamilynetwork"; -const char* password = "magicalsparrow96"; +// const char* ssid = "readfamilynetwork"; +// const char* password = "magicalsparrow96"; // MQTT Broker diff --git a/Firmware/factoryTest/FactoryTest_Krake01/DFPlayerTD5580ATest.cpp b/Firmware/factoryTest/FactoryTest_Krake01/DFPlayerTD5580ATest.cpp new file mode 100644 index 0000000..9996317 --- /dev/null +++ b/Firmware/factoryTest/FactoryTest_Krake01/DFPlayerTD5580ATest.cpp @@ -0,0 +1,280 @@ +#include "DFPlayerTD5580ATest.h" +#define COMPANY_NAME "pubinv.org " +#define PROG_NAME "DFPlayerTD5580ATest " +#define VERSION "V0.7 " +#define DEVICE_UNDER_TEST "Krake: DFPlayer and OTA" //A PMD model number +#define LICENSE "GNU Affero General Public License, version 3 " +#define ORIGIN "USA" + +/* + File: DFPlayerTD5580ATest + Author: Forrest Lee Erickson + Date: 20240929 + Date: 20250414 V0.2, added functionality from :https://github.com/DFRobot/DFRobotDFPlayerMini/blob/master/examples/FullFunction/FullFunction.ino + +*/ + +#include +#include +#include +#include +#include + +// const char* ssid = "VRX"; +// const char* password = "textinsert"; + +// const char* ssid = "Hometplink"; +// const char* password = "adt@1963#"; + +//Houstin network +// const char* ssid = "DOS_WIFI"; +// const char* password = "$Suve07$$"; + +// Austin network +const char* ssid = "readfamilynetwork"; +const char* password = "magicalsparrow96"; + + +AsyncWebServer server(80); + +#define BAUDRATE 115200 // For UART0 the DEBUG Serial Monitor + +#define BAUD_DFPLAYER 9600 //for UART2 to the DFPlayer +#define TXD2 17 +#define RXD2 16 + +#include +DFRobotDFPlayerMini dfPlayer; + +HardwareSerial mySerial1(2); // Use UART2 + +const int LED_PIN = 13; // Krake +//const int LED_PIN = 2; // ESP32 LED + +const int nDFPlayer_BUSY = 4; //not Busy from DFPLayer +bool isDFPlayerDetected = false; +int volumeDFPlayer = 20; // Set volume initial volume. Range is [0 to 30] +int numberFilesDF = 0; // Number of the audio files found. + +//Functions +void setupDFPlayer() { + // Setup UART for DFPlayer + Serial.println("UART2 Begin"); + mySerial1.begin(BAUD_DFPLAYER, SERIAL_8N1, RXD2, TXD2); + while (!mySerial1) { + Serial.println("UART2 inot initilaized."); + delay(100); + ; // wait for DFPlayer serial port to connect. + } + Serial.println("Begin DFPlayer: isACK true, doReset false."); + if (!dfPlayer.begin(mySerial1, true, false)) { + Serial.println("DFPlayer Mini not detected or not working."); + Serial.println("Check for missing SD Card."); + isDFPlayerDetected = false; + } else { + isDFPlayerDetected = true; + Serial.println("DFPlayer Mini detected!"); + } + + dfPlayer.volume(volumeDFPlayer); // Set initial volume + dfPlayer.setTimeOut(500); // Set serial communictaion time out 500ms + // delay(100); + + dfPlayer.start(); //Todo, ?? necessary for DFPlayer processing + delay(1000); +// dfPlayer.play(11); //DFPlayer Splash + dfPlayer.play(9); + delay(100); + dfPlayer.stop(); + delay(1000); + dfPlayer.previous(); + delay(1500); + dfPlayer.play(); //DFPlayer Splash + + displayDFPlayerStats(); + +} //setupDFPLayer + +void displayDFPlayerStats() { + Serial.print("================="); + Serial.print("dfPlayer State: "); + Serial.println(dfPlayer.readState()); //read mp3 state + Serial.print("dfPlayer Volume: "); + Serial.println(dfPlayer.readVolume()); //read current volume + Serial.print("dfPlayer EQ: "); + Serial.println(dfPlayer.readEQ()); //read EQ setting + Serial.print("SD Card FileCounts: "); + Serial.println(dfPlayer.readFileCounts()); //read all file counts in SD card + Serial.print("Current File Number: "); + numberFilesDF = dfPlayer.readCurrentFileNumber(); + Serial.println(numberFilesDF); //Display the read current play file number + Serial.print("File Counts In Folder: "); + Serial.println(dfPlayer.readFileCountsInFolder(3)); //read file counts in folder SD:/03 + // dfPlayer.EQ(0); // Normal equalization //Causes program lock up + Serial.print("================="); +} +void printDetail(uint8_t type, int value) { + switch (type) { + case TimeOut: + Serial.println(F("Time Out!")); + break; + case WrongStack: + Serial.println(F("Stack Wrong!")); + break; + case DFPlayerCardInserted: + Serial.println(F("Card Inserted!")); + break; + case DFPlayerCardRemoved: + Serial.println(F("Card Removed!")); + break; + case DFPlayerCardOnline: + Serial.println(F("Card Online!")); + break; + case DFPlayerUSBInserted: + Serial.println("USB Inserted!"); + break; + case DFPlayerUSBRemoved: + Serial.println("USB Removed!"); + break; + case DFPlayerPlayFinished: + Serial.print(F("Number:")); + Serial.print(value); + Serial.println(F(" Play Finished!")); + break; + case DFPlayerError: + Serial.print(F("DFPlayerError:")); + switch (value) { + case Busy: + Serial.println(F("Card not found")); + break; + case Sleeping: + Serial.println(F("Sleeping")); + break; + case SerialWrongStack: + Serial.println(F("Get Wrong Stack")); + break; + case CheckSumNotMatch: + Serial.println(F("Check Sum Not Match")); + break; + case FileIndexOut: + Serial.println(F("File Index Out of Bound")); + break; + case FileMismatch: + Serial.println(F("Cannot Find File")); + break; + case Advertise: + Serial.println(F("In Advertise")); + break; + default: + break; + } + break; + default: + break; + } +} //end printDetail for DFPlayer + + +void dfPlayerUpdate(void) { + unsigned long timePlay = 3000; //Plays 3 seconds of all files. + static unsigned long timer = millis(); + if (millis() - timer > timePlay) { + // if (millis() - timer > 10000) { + timer = millis(); + dfPlayer.next(); //Play next mp3 every 3 second. + } + if (dfPlayer.available()) { + printDetail(dfPlayer.readType(), dfPlayer.read()); //Print the detail message from DFPlayer to handle different errors and states. + } +} // end + + + + +// Functions to learn DFPlayer behavior +void playNotBusy() { + //Plays all files succsivly. + Serial.println("PlayNotBusy"); + if (HIGH == digitalRead(nDFPlayer_BUSY)) { + //mp3_next (); + dfPlayer.next(); + } + if (dfPlayer.available()) { + printDetail(dfPlayer.readType(), dfPlayer.read()); //Print the detail message from DFPlayer to handle different errors and states. + } + delay(1000); //This should be removed from code. We set a time for the next allowed message. + // Or better yet use interupt to find the end of BUSY and set time for the next allowed DFPlayer message +} // end playNotBusy + + + +// Play a track but not if the DFPlayer is busy +bool playAlarmLevel(int alarmNumberToPlay) { + static unsigned long timer = millis(); + + const unsigned long delayPlayLevel = 20; + // const int MAX_ALARM_NUMBER = 6; + int tracNumber = alarmNumberToPlay; + + if (millis() - timer > delayPlayLevel) { + // if (millis() - timer > 10000) { + timer = millis(); + + //If not busy play the alarm message + if ((0 > tracNumber < 0) || (numberFilesDF < tracNumber)) { + return false; + } else //Valid track number + if (HIGH == digitalRead(nDFPlayer_BUSY)) { // Should the test for busy be at the start of this function??? + //mp3_next (); + dfPlayer.play(tracNumber); + } else { + Serial.println("Not done playing previous file"); + } + if (dfPlayer.available()) { + printDetail(dfPlayer.readType(), dfPlayer.read()); //Print the detail message from DFPlayer to handle different errors and states. + } + return true; + } else { + return false; + } +} // end of playAlarmLevel + + +void DFPlayerTD5580ATest_setup() { + pinMode(LED_PIN, OUTPUT); + digitalWrite(LED_PIN, HIGH); + pinMode(nDFPlayer_BUSY, INPUT_PULLUP); + + // Start serial communication + Serial.begin(BAUDRATE); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB + } + + //delay(500); + serialSplash(); + + //DFPlayer Splash + setupDFPlayer(); + // dfPlayer.play(9); //DFPlayer Splash + + setupOTA(); + Serial.print("OTA setup done."); + + while (LOW == digitalRead(nDFPlayer_BUSY)) { + ; //Hold still till splash file plays + } + + Serial.println("End of setup"); + digitalWrite(LED_PIN, LOW); +} // end of setup() + +void DFPlayerTD5580ATest_loop() { + ElegantOTA.loop(); + //dfPlayerUpdate(); //Play all but for only a constant time. + // playNotBusy(); // Play all but only when not busy. + //playAlarmLevel(3); + digitalWrite(LED_PIN, !digitalRead(nDFPlayer_BUSY)); //Polarity of the LED must be inverted relative to the BUSY. + + checkSerial(); +} // end of loop() diff --git a/Firmware/factoryTest/FactoryTest_Krake01/DFPlayerTD5580ATest.h b/Firmware/factoryTest/FactoryTest_Krake01/DFPlayerTD5580ATest.h new file mode 100644 index 0000000..1e34334 --- /dev/null +++ b/Firmware/factoryTest/FactoryTest_Krake01/DFPlayerTD5580ATest.h @@ -0,0 +1,17 @@ +#ifndef DFPLAYERTD5580ATEST_H +#define DFPLAYERTD5580ATEST_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void DFPlayerTD5580ATest_setup(); +void DFPlayerTD5580ATest_loop(); + +#ifdef __cplusplus +} +#endif + +#endif // DFPLAYERTD5580ATEST_H diff --git a/Firmware/factoryTest/FactoryTest_Krake01/FactoryTest_Krake01.ino b/Firmware/factoryTest/FactoryTest_Krake01/FactoryTest_Krake01.ino new file mode 100644 index 0000000..98f9ace --- /dev/null +++ b/Firmware/factoryTest/FactoryTest_Krake01/FactoryTest_Krake01.ino @@ -0,0 +1,70 @@ +/* + * main.cpp selector + * Define one of the USE_ macros (e.g., -DUSE_Krake_MQTT) + * to build/run that converted module's setup/loop. + */ +#include + +#include "DFPlayerTD5580ATest.h" +#include "Krake_MQTT.h" +#include "LCD_Test_2.h" +#include "LCD_nI2C_scanner.h" +#include "OTA.h" +#include "RotaryEncoderTest.h" +#include "Serial.h" +#include "WiFiAccessPointWithMAC.h" +void setup() { + +#ifdef USE_DFPlayerTD5580ATest + DFPlayerTD5580ATest_setup(); +#endif +#ifdef USE_Krake_MQTT + Krake_MQTT_setup(); +#endif +#ifdef USE_LCD_Test_2 + LCD_Test_2_setup(); +#endif +#ifdef USE_LCD_nI2C_scanner + LCD_nI2C_scanner_setup(); +#endif +#ifdef USE_OTA + OTA_setup(); +#endif +#ifdef USE_RotaryEncoderTest + RotaryEncoderTest_setup(); +#endif +#ifdef USE_Serial + Serial_setup(); +#endif +#ifdef USE_WiFiAccessPointWithMAC + WiFiAccessPointWithMAC_setup(); +#endif +} + +void loop() { +#ifdef USE_DFPlayerTD5580ATest + DFPlayerTD5580ATest_loop(); +#endif +#ifdef USE_Krake_MQTT + Krake_MQTT_loop(); +#endif +#ifdef USE_LCD_Test_2 + LCD_Test_2_loop(); +#endif +#ifdef USE_LCD_nI2C_scanner + LCD_nI2C_scanner_loop(); +#endif +#ifdef USE_OTA + OTA_loop(); +#endif +#ifdef USE_RotaryEncoderTest + RotaryEncoderTest_loop(); +#endif +#ifdef USE_Serial + Serial_loop(); +#endif +#ifdef USE_WiFiAccessPointWithMAC + WiFiAccessPointWithMAC_loop(); +#endif + delay(1); +} diff --git a/Firmware/factoryTest/FactoryTest_Krake01/Krake_MQTT.cpp b/Firmware/factoryTest/FactoryTest_Krake01/Krake_MQTT.cpp new file mode 100644 index 0000000..302cf86 --- /dev/null +++ b/Firmware/factoryTest/FactoryTest_Krake01/Krake_MQTT.cpp @@ -0,0 +1,279 @@ +#include "Krake_MQTT.h" +#include +// File name: Krake_MQTT +// Author: Nagham Kheir, Forrest Lee Erickson +// Date: 20241014 +// LICENSE "GNU Affero General Public License, version 3 " +// Hardware ESP32 kit +// 20241101 Update for GPAD API and topics "_ALM" and "_ACK" + +// Customized this by changing these defines +#define COMPANY_NAME "Public Invention " +#define PROG_NAME "Krake_MQTT " +#define MODEL_NAME "KRAKE_" +#define DEVICE_UNDER_TEST "20240421_USA5" //A Serial Number +#define LICENSE "GNU Affero General Public License, version 3 " +#define ORIGIN "LB" + +// Description: +// The Krake (an ESP32) subscribes and publishes to an MQTT broker with topics +// defined in user manual "Topic is “KRAKE_” plus a serial number plus a suffix" + +#include +#include // From library https://github.com/knolleary/pubsubclient + +#define BAUDRATE 115200 + +// WiFi credentials +//const char* ssid = "ADT"; +//const char* password = "adt@12345"; + +//Maryville network +const char* ssid = "VRX"; +const char* password = "textinsert"; + +// Austin network +// const char* ssid = "readfamilynetwork"; +// const char* password = "magicalsparrow96"; + + +// MQTT Broker +const char* mqtt_server = "public.cloud.shiftr.io"; +const char* mqtt_user = "public"; +const char* mqtt_password = "public"; + +// MQTT Topics +// User must modify the device serial number. In this case change the part "USA4" as approprate. +const char* subscribe_Alarm_Topic = "KRAKE_20240421_USA5_ALM"; +const char* publish_Ack_Topic = "KRAKE_20240421_USA5_ACK"; + +// LED Pins +// To Do Define ESP32 pins for actual Krake LEDs +// Homework2 Assembly, akd PMD LAMPS aka Other LEDs +const int LED_D9 = 23; // Mute1 LED on PMD +const int LAMP1 = 15; // D5 cold food +const int LAMP2 = 4; // D3 baby crying +const int LAMP3 = 5; // D8 high BP1 +const int LAMP4 = 18; // D7 shield failure +const int LAMP5 = 19; // D6 lost sock + +// Initialize WiFi and MQTT clients +WiFiClient espClient; +PubSubClient client(espClient); + +//Functions + +void serialSplash(void) { + //Serial splash + Serial.println(F("===================================")); + Serial.println(COMPANY_NAME); + Serial.print("MODEL_NAME: "); + Serial.println(MODEL_NAME); + Serial.print("PROG_NAME&VERSION: "); + Serial.print(PROG_NAME); +// Serial.println(VERSION); + Serial.print("DEVICE_UNDER_TEST: "); + Serial.println(DEVICE_UNDER_TEST); + String mac = WiFi.macAddress(); // Get the MAC address and convert it to a string + mac.replace(":", ""); + Serial.print(F("MAC: ")); + Serial.println(mac); + Serial.println("My mDNS address: http://" + mac + ".local"); + Serial.print("Alarm Topic: "); + Serial.println(subscribe_Alarm_Topic); + Serial.print(F("Compiled at: ")); + Serial.println(F(__DATE__ " " __TIME__)); //compile date that is used for a unique identifier + Serial.println(LICENSE); + Serial.println(F("===================================")); +}// end serialSplash + +// Proccess sort of like the GPAD API payload +void proccessPayloadOnLamps(String &payload) { + + if (payload.charAt(0) == 'h') { + Serial.println("Got request for help"); + client.publish(publish_Ack_Topic, "Got request for help"); + } else if (payload.charAt(0) == 's') { + Serial.println("Got set mute"); + client.publish(publish_Ack_Topic, "Got set mute"); + } else if (payload.charAt(0) == 'u') { + Serial.println("Got set Unmute"); + client.publish(publish_Ack_Topic, "Got set Unmute"); + } else if (payload.charAt(0) == 'a') { + //Parse on second character + if ((payload.charAt(1) == '0')) { + // client.publish(publish_Ack_Topic, "Alarm " + payload.charAt(1)); + client.publish(publish_Ack_Topic, "Alarm 0" ); + //Turn none on + digitalWrite(LAMP1, LOW); + digitalWrite(LAMP2, LOW); + digitalWrite(LAMP3, LOW); + digitalWrite(LAMP4, LOW); + digitalWrite(LAMP5, LOW); + } else if ((payload.charAt(1) == '1')) { + client.publish(publish_Ack_Topic, "Alarm 1"); + //Turn on only LAMP 1 + digitalWrite(LAMP1, HIGH); + digitalWrite(LAMP2, LOW); + digitalWrite(LAMP3, LOW); + digitalWrite(LAMP4, LOW); + digitalWrite(LAMP5, LOW); + } else if ((payload.charAt(1) == '2')) { + client.publish(publish_Ack_Topic, "Alarm 2"); + //Turn on only LAMP 2 + digitalWrite(LAMP1, LOW); + digitalWrite(LAMP2, HIGH); + digitalWrite(LAMP3, LOW); + digitalWrite(LAMP4, LOW); + digitalWrite(LAMP5, LOW); + } else if ((payload.charAt(1) == '3')) { + client.publish(publish_Ack_Topic, "Alarm 3"); + //Turn on only LAMP 3 + digitalWrite(LAMP1, LOW); + digitalWrite(LAMP2, LOW); + digitalWrite(LAMP3, HIGH); + digitalWrite(LAMP4, LOW); + digitalWrite(LAMP5, LOW); + } else if ((payload.charAt(1) == '4')) { + client.publish(publish_Ack_Topic, "Alarm 4"); + //Turn on only LAMP 4 + digitalWrite(LAMP1, LOW); + digitalWrite(LAMP2, LOW); + digitalWrite(LAMP3, LOW); + digitalWrite(LAMP4, HIGH); + digitalWrite(LAMP5, LOW); + } else if ((payload.charAt(1) == '5')) { + client.publish(publish_Ack_Topic, "Alarm 5"); + //Turn on only LAMP 5 + digitalWrite(LAMP1, LOW); + digitalWrite(LAMP2, LOW); + digitalWrite(LAMP3, LOW); + digitalWrite(LAMP4, LOW); + digitalWrite(LAMP5, HIGH); + } else if ((payload.charAt(1) == '6')) { + client.publish(publish_Ack_Topic, "Alarm 6"); + //Turn on all lamps + turnOnAllLamps(); + } else {// other than 0-6 + Serial.println("Unrecognized alarm"); + client.publish(publish_Ack_Topic, "Unrecognized alarm"); + }// end parsing message + } else { + Serial.println("Unrecognized command: " + payload.charAt(0)); + client.publish(publish_Ack_Topic, "Unrecognized command: "); + } +}// end proccessPayloadOnLamps + + +// A periodic message identifying the subscriber (Krake) is on line. +// Toggles and LED. +void publishOnLineMsg(void) { + static unsigned long lastMillis = 0; // Sets timing for periodic MQTT publish message + // publish a message roughly every second. + if (millis() - lastMillis > 10000) { + lastMillis = millis(); + client.publish(publish_Ack_Topic, " is online"); + digitalWrite(LED_D9, !digitalRead(LED_D9)); // Toggle + } +} + +void setup_wifi() { + delay(10); + Serial.println(); + Serial.print("Connecting to WiFi: "); + Serial.println(ssid); + WiFi.begin(ssid, password); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.println("WiFi connected"); + int rssi = WiFi.RSSI(); + Serial.print("Signal Strength (RSSI): "); + Serial.println(rssi); +} + +void reconnect() { + while (!client.connected()) { + Serial.print("Attempting MQTT connection..."); + if (client.connect("ESP32_Receiver", mqtt_user, mqtt_password)) { + Serial.println("success!"); + client.subscribe(subscribe_Alarm_Topic); // Subscribe to GPAD API alarms + } else { + Serial.print("failed, rc="); + Serial.print(client.state()); + delay(5000); + } + } +} + +// Function to turn on all lamps +void turnOnAllLamps() { + digitalWrite(LAMP1, HIGH); + digitalWrite(LAMP2, HIGH); + digitalWrite(LAMP3, HIGH); + digitalWrite(LAMP4, HIGH); + digitalWrite(LAMP5, HIGH); +} +//end Functions + +// Handeler for MQTT subscrived messages +void callback(char* topic, byte* payload, unsigned int length) { + Serial.print("Message arrived ["); + Serial.print(topic); + Serial.print("] "); + String message = ""; + for (int i = 0; i < length; i++) { + message += (char)payload[i]; + } + Serial.println(message); + + if (String(topic) == subscribe_Alarm_Topic) { + Serial.println("Got MessageFromProcessing_PMD"); + proccessPayloadOnLamps(message); // Change LAMPS baised on the payload + } +}//end call back + +//Call backs + +//end Call backs + +void Krake_MQTT_setup() { + const int LED_BUILTIN = 2; // ESP32 Kit + pinMode(LED_BUILTIN, OUTPUT); // set the LED pin mode + digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) + + Serial.begin(BAUDRATE); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB + } + delay(500); + serialSplash(); + + // Set LED pins as outputs + pinMode(LED_D9, OUTPUT); + pinMode(LAMP1, OUTPUT); + pinMode(LAMP2, OUTPUT); + pinMode(LAMP3, OUTPUT); + pinMode(LAMP4, OUTPUT); + pinMode(LAMP5, OUTPUT); + + // Turn off all LEDs initially + turnOnAllLamps(); + + setup_wifi(); + client.setServer(mqtt_server, 1883); + client.setCallback(callback); + + digitalWrite(LED_BUILTIN, LOW); +}// end setup() + +void Krake_MQTT_loop() { + if (!client.connected()) { + reconnect(); + } + client.loop(); + publishOnLineMsg(); + wink(); +}//end loop(); diff --git a/Firmware/factoryTest/FactoryTest_Krake01/Krake_MQTT.h b/Firmware/factoryTest/FactoryTest_Krake01/Krake_MQTT.h new file mode 100644 index 0000000..f5e2b66 --- /dev/null +++ b/Firmware/factoryTest/FactoryTest_Krake01/Krake_MQTT.h @@ -0,0 +1,17 @@ +#ifndef KRAKE_MQTT_H +#define KRAKE_MQTT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void Krake_MQTT_setup(); +void Krake_MQTT_loop(); + +#ifdef __cplusplus +} +#endif + +#endif // KRAKE_MQTT_H diff --git a/Firmware/factoryTest/FactoryTest_Krake01/LCD_Test_2.cpp b/Firmware/factoryTest/FactoryTest_Krake01/LCD_Test_2.cpp new file mode 100644 index 0000000..92baa05 --- /dev/null +++ b/Firmware/factoryTest/FactoryTest_Krake01/LCD_Test_2.cpp @@ -0,0 +1,50 @@ +#include "LCD_Test_2.h" +#include +/********* +here we added Serial Prints to detect the bug +and on the hardware I tried to fix the contrast of the LCD by tilting the nob in the back of the chip. +*********/ + +#include +#include + +// set the LCD number of columns and rows +int lcdColumns = 20; +int lcdRows = 4; + +// set LCD address, number of columns and rows +// if you don't know your display address, run an I2C scanner sketch +LiquidCrystal_I2C lcd(0x3F, lcdColumns, lcdRows); + +void LCD_Test_2_setup() { + + Serial.begin(115200); + delay(1000); + Serial.println("\nI2C Scanner"); + Wire.begin(); + // initialize LCD + lcd.init(); + Serial.print("LCD init success"); + // turn on LCD backlight + // lcd.backlight(); + // Serial.print("Did you fail yet?"); +} + +void LCD_Test_2_loop() { + // set cursor to first column, first row + lcd.setCursor(1, 1); + Serial.println("Hello, World!"); + // print message + lcd.print("Hello, World!"); + + delay(1000); + // clears the display to print new message + // lcd.clear(); + // set cursor to first column, second row + lcd.setCursor(2,2); + Serial.print("Hello, World! AGAIN!\n"); + lcd.print("Hello, World!"); + + delay(1000); + // lcd.clear(); +} \ No newline at end of file diff --git a/Firmware/factoryTest/FactoryTest_Krake01/LCD_Test_2.h b/Firmware/factoryTest/FactoryTest_Krake01/LCD_Test_2.h new file mode 100644 index 0000000..56e7545 --- /dev/null +++ b/Firmware/factoryTest/FactoryTest_Krake01/LCD_Test_2.h @@ -0,0 +1,17 @@ +#ifndef LCD_TEST_2_H +#define LCD_TEST_2_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void LCD_Test_2_setup(); +void LCD_Test_2_loop(); + +#ifdef __cplusplus +} +#endif + +#endif // LCD_TEST_2_H diff --git a/Firmware/factoryTest/FactoryTest_Krake01/LCD_nI2C_scanner.cpp b/Firmware/factoryTest/FactoryTest_Krake01/LCD_nI2C_scanner.cpp new file mode 100644 index 0000000..f50397a --- /dev/null +++ b/Firmware/factoryTest/FactoryTest_Krake01/LCD_nI2C_scanner.cpp @@ -0,0 +1,43 @@ +#include "LCD_nI2C_scanner.h" +#include + +#include + +void LCD_nI2C_scanner_setup() { + Wire.begin(); + Serial.begin(115200); + Serial.println("\nI2C Scanner"); +} + +void LCD_nI2C_scanner_loop() { + byte error, address; + int nDevices; + Serial.println("Scanning..."); + nDevices = 0; + for(address = 1; address < 127; address++ ) { + Wire.beginTransmission(address); + error = Wire.endTransmission(); + if (error == 0) { + Serial.print("I2C device found at address 0x"); + if (address<16) { + Serial.print("0"); + } + Serial.println(address,HEX); + nDevices++; + } + else if (error==4) { + Serial.print("Unknow error at address 0x"); + if (address<16) { + Serial.print("0"); + } + Serial.println(address,HEX); + } + } + if (nDevices == 0) { + Serial.println("No I2C devices found\n"); + } + else { + Serial.println("done\n"); + } + delay(5000); +} \ No newline at end of file diff --git a/Firmware/factoryTest/FactoryTest_Krake01/LCD_nI2C_scanner.h b/Firmware/factoryTest/FactoryTest_Krake01/LCD_nI2C_scanner.h new file mode 100644 index 0000000..b57c251 --- /dev/null +++ b/Firmware/factoryTest/FactoryTest_Krake01/LCD_nI2C_scanner.h @@ -0,0 +1,17 @@ +#ifndef LCD_NI2C_SCANNER_H +#define LCD_NI2C_SCANNER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void LCD_nI2C_scanner_setup(); +void LCD_nI2C_scanner_loop(); + +#ifdef __cplusplus +} +#endif + +#endif // LCD_NI2C_SCANNER_H diff --git a/Firmware/factoryTest/FactoryTest_Krake01/OTA.cpp b/Firmware/factoryTest/FactoryTest_Krake01/OTA.cpp new file mode 100644 index 0000000..709f4d0 --- /dev/null +++ b/Firmware/factoryTest/FactoryTest_Krake01/OTA.cpp @@ -0,0 +1,41 @@ +#include "OTA.h" +#include +//OTA Stuff +/********* + Rui Santos & Sara Santos - Random Nerd Tutorials + Complete project details at https://RandomNerdTutorials.com/esp32-ota-elegantota-arduino/ + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Softwar +*********/ + + +void setupOTA() { + + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + Serial.println(""); + + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { + request->send(200, "text/plain", "Hi! I am DFPlayerTD5580ATest."); + }); + +// server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { +// request->send(LittleFS, "/index.html", "text/html"); // Serve index.html +// }); + + server.begin(); + Serial.println("HTTP server started"); + + ElegantOTA.begin(&server); // Start ElegantOTA + +} //end setupOTA diff --git a/Firmware/factoryTest/FactoryTest_Krake01/OTA.h b/Firmware/factoryTest/FactoryTest_Krake01/OTA.h new file mode 100644 index 0000000..15ef9bc --- /dev/null +++ b/Firmware/factoryTest/FactoryTest_Krake01/OTA.h @@ -0,0 +1,17 @@ +#ifndef OTA_H +#define OTA_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + + +#ifdef __cplusplus +} +#endif + +#endif // OTA_H diff --git a/Firmware/factoryTest/FactoryTest_Krake01/RotaryEncoderTest.cpp b/Firmware/factoryTest/FactoryTest_Krake01/RotaryEncoderTest.cpp new file mode 100644 index 0000000..4ed7803 --- /dev/null +++ b/Firmware/factoryTest/FactoryTest_Krake01/RotaryEncoderTest.cpp @@ -0,0 +1,94 @@ +#include "RotaryEncoderTest.h" +#include +#define COMPANY_NAME "pubinv.org " +#define PROG_NAME "RotaryEncoderTest" +#define VERSION "V0.2 " +#define DEVICE_UNDER_TEST "Krake: Rotary Encoder" //A PMD model number +#define LICENSE "GNU Affero General Public License, version 3 " +#define ORIGIN "USA" + +#define BAUDRATE 115200 + + + + +// Define the GPIO pins for CLK, DT, and SW +const int CLK = 39; // Connected to the CLK pin of the encoder +const int DT = 36; // Connected to the DT pin of the encoder +const int SW = 34; // Connected to the SW (Switch) pin of the encoder + +int currentStateCLK; +int previousStateCLK; +int encoderPos = 0; +bool isButtonPressed = false; + +void RotaryEncoderTest_setup() { + // Initialize serial communication for debugging + // Start serial communication + Serial.begin(BAUDRATE); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB + } + delay(500); + Serial.println("==================================="); + Serial.println(DEVICE_UNDER_TEST); + Serial.print(PROG_NAME); + Serial.println(VERSION); + Serial.print("Compiled at: "); + Serial.println(F(__DATE__ " " __TIME__)); //compile date that is used for a unique identifier + Serial.println("==================================="); + Serial.println(); + + + // Set CLK and DT as inputs + pinMode(CLK, INPUT); + pinMode(DT, INPUT); + + // Set SW (Switch) as input with internal pull-up resistor + pinMode(SW, INPUT_PULLUP); + + // Read the initial state of CLK + previousStateCLK = digitalRead(CLK); +} + +void RotaryEncoderTest_loop() { + // Read the current state of the CLK pin + currentStateCLK = digitalRead(CLK); + + // If the current state of CLK is different from the last state + // then the encoder has been rotated + if (currentStateCLK != previousStateCLK) { + + // If the DT state is different than the CLK state, the encoder + // is rotating clockwise, otherwise it's counterclockwise + if (digitalRead(DT) != currentStateCLK) { + encoderPos++; + Serial.println("Rotating clockwise"); + } else { + encoderPos--; + Serial.println("Rotating counterclockwise"); + } + + Serial.print("Encoder position: "); + Serial.println(encoderPos); + } + + // Update the previous state of the CLK with the current state + previousStateCLK = currentStateCLK; + + // Check if the switch is pressed + if (digitalRead(SW) == LOW) { + if (!isButtonPressed) { + isButtonPressed = true; + Serial.println("Button Pressed"); + } + } else { + if (isButtonPressed) { + isButtonPressed = false; + Serial.println("Button Released"); + } + } + + // Small delay to debounce the switch and encoder + // delay(300); +} diff --git a/Firmware/factoryTest/FactoryTest_Krake01/RotaryEncoderTest.h b/Firmware/factoryTest/FactoryTest_Krake01/RotaryEncoderTest.h new file mode 100644 index 0000000..7b4afc2 --- /dev/null +++ b/Firmware/factoryTest/FactoryTest_Krake01/RotaryEncoderTest.h @@ -0,0 +1,17 @@ +#ifndef ROTARYENCODERTEST_H +#define ROTARYENCODERTEST_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void RotaryEncoderTest_setup(); +void RotaryEncoderTest_loop(); + +#ifdef __cplusplus +} +#endif + +#endif // ROTARYENCODERTEST_H diff --git a/Firmware/factoryTest/FactoryTest_Krake01/Serial.cpp b/Firmware/factoryTest/FactoryTest_Krake01/Serial.cpp new file mode 100644 index 0000000..9180907 --- /dev/null +++ b/Firmware/factoryTest/FactoryTest_Krake01/Serial.cpp @@ -0,0 +1,107 @@ +#include "Serial.h" +#include + +// The serial command system is from: https://www.dfrobot.com/blog-1462.html?srsltid=AfmBOoqm5pHrLswInrTwZ9vcYdxxPC_zH2zXnoro2FyTLEL4L57IW3Sn + +char command; +int pausa = 0; + + +void serialSplash() { + Serial.println("==================================="); + Serial.println(DEVICE_UNDER_TEST); + Serial.print(PROG_NAME); + Serial.println(VERSION); + Serial.print("Compiled at: "); + Serial.println(F(__DATE__ " " __TIME__)); //compile date that is used for a unique identifier + Serial.println("==================================="); + Serial.println(); +} + +void menu_opcoes() { + Serial.println(); + Serial.println(F("==================================================================================================================================")); + Serial.println(F("Commands:")); + Serial.println(F(" [1-3] To select the MP3 file")); + Serial.println(F(" [s] stopping reproduction")); + Serial.println(F(" [p] pause/continue music")); + Serial.println(F(" [+ or -] increases or decreases the volume")); + Serial.println(F(" [< or >] forwards or backwards the track")); + Serial.println(); + Serial.println(F("=================================================================================================================================")); +} //end menu_opcoes() + + +void checkSerial(void) { + + //Waits for data entry via serial + while (Serial.available() > 0) { + command = Serial.read(); + + if ((command >= '1') && (command <= '9')) { + Serial.print("Music reproduction"); + Serial.println(command); + command = command - 48; + dfPlayer.play(command); + menu_opcoes(); + } + + //Reproduction + //Stop + + if (command == 's') { + dfPlayer.stop(); + Serial.println("Music Stopped!"); + menu_opcoes(); + } + + //Pausa/Continua a musica + if (command == 'p') { + pausa = !pausa; + if (pausa == 0) { + Serial.println("Continue..."); + dfPlayer.start(); + } + + if (pausa == 1) { + Serial.println("Music Paused!"); + dfPlayer.pause(); + } + + menu_opcoes(); + } + + + //Increases volume + if (command == '+') { + dfPlayer.volumeUp(); + Serial.print("Current volume:"); + Serial.println(dfPlayer.readVolume()); + menu_opcoes(); + } + + if (command == '<') { + dfPlayer.previous(); + Serial.println("Previous:"); + Serial.print("Current track:"); + Serial.println(dfPlayer.readCurrentFileNumber() - 1); + menu_opcoes(); + } + + if (command == '>') { + dfPlayer.next(); + Serial.println("next:"); + Serial.print("Current track:"); + Serial.println(dfPlayer.readCurrentFileNumber() + 1); + menu_opcoes(); + } + + //Decreases volume + if (command == '-') { + dfPlayer.volumeDown(); + Serial.print("Current Volume:"); + Serial.println(dfPlayer.readVolume()); + menu_opcoes(); + } + } +} // end checkSerial diff --git a/Firmware/factoryTest/FactoryTest_Krake01/Serial.h b/Firmware/factoryTest/FactoryTest_Krake01/Serial.h new file mode 100644 index 0000000..bd6c06a --- /dev/null +++ b/Firmware/factoryTest/FactoryTest_Krake01/Serial.h @@ -0,0 +1,17 @@ +#ifndef SERIAL_H +#define SERIAL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + + +#ifdef __cplusplus +} +#endif + +#endif // SERIAL_H diff --git a/Firmware/factoryTest/FactoryTest_Krake01/WiFiAccessPointWithMAC.cpp b/Firmware/factoryTest/FactoryTest_Krake01/WiFiAccessPointWithMAC.cpp new file mode 100644 index 0000000..1c75863 --- /dev/null +++ b/Firmware/factoryTest/FactoryTest_Krake01/WiFiAccessPointWithMAC.cpp @@ -0,0 +1,114 @@ +#include "WiFiAccessPointWithMAC.h" +#include +/* + WiFiAccessPoint.ino creates a WiFi access point and provides a web server on it. + + Steps: + 1. Connect to the access point "yourAp" + 2. Point your web browser to http://192.168.4.1/H to turn the LED on or http://192.168.4.1/L to turn it off + OR + Run raw TCP "GET /H" and "GET /L" on PuTTY terminal with 192.168.4.1 as IP address and 80 as port + + Created for arduino-esp32 on 04 July, 2018 + by Elochukwu Ifediora (fedy0) + + Modified to compose an AP SSID from a model name and the built in MAC. + A MAC is 17 characters long because of the delimiters. Each character is two bytes. + Forrest Lee Erickson, 20240913 + +*/ + +#include +#include +#include + +#define LED_BUILTIN 2 // Set the GPIO pin where you connected your test LED or comment this line out if your dev board has a built-in LED + +// Set a model name "12345678" for 8 characters +const char *modelName = "AbcdefgH"; // Must be less than 8 characters long. + +// Buffer for the access point name +char apName[sizeof(*modelName) + 24]; // Adjust size based on your needs. Two bytes per char. + +// Set these to your desired credentials. +const char *password = "yourPassword"; + +WiFiServer server(80); + +void WiFiAccessPointWithMAC_setup() { + pinMode(LED_BUILTIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); + Serial.print("\nBuiltin ESP32 MAC Address: "); + Serial.println(WiFi.macAddress()); + Serial.println("Configuring access point..."); + + // Initialize WiFi in AP mode + WiFi.mode(WIFI_AP); + String mac = WiFi.macAddress(); // Get the MAC address and convert it to a string + mac.replace(":", ""); // Removes colons ':' in MAC + snprintf(apName, sizeof(apName), "%s_%s", modelName, mac.c_str()); // Construct the AP name + WiFi.softAP(apName, password); // Remove the password if you want the AP to be open. + Serial.print("Access Point Name: "); + Serial.println(apName); + + IPAddress myIP = WiFi.softAPIP(); + Serial.print("AP IP address: "); + Serial.println(myIP); + server.begin(); + + Serial.println("Server started"); +} + +void WiFiAccessPointWithMAC_loop() { + WiFiClient client = server.available(); // listen for incoming clients + + if (client) { // if you get a client, + Serial.println("New Client."); // print a message out the serial port + String currentLine = ""; // make a String to hold incoming data from the client + while (client.connected()) { // loop while the client's connected + if (client.available()) { // if there's bytes to read from the client, + char c = client.read(); // read a byte, then + Serial.write(c); // print it out the serial monitor + if (c == '\n') { // if the byte is a newline character + + // if the current line is blank, you got two newline characters in a row. + // that's the end of the client HTTP request, so send a response: + if (currentLine.length() == 0) { + // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) + // and a content-type so the client knows what's coming, then a blank line: + client.println("HTTP/1.1 200 OK"); + client.println("Content-type:text/html"); + client.println(); + + // the content of the HTTP response follows the header: + client.print("

WiFiAccessPointWithMAC

"); + client.print("Click here to turn ON the LED.
"); + client.print("Click here to turn OFF the LED.
"); + + // The HTTP response ends with another blank line: + client.println(); + // break out of the while loop: + break; + } else { // if you got a newline, then clear currentLine: + currentLine = ""; + } + } else if (c != '\r') { // if you got anything else but a carriage return character, + currentLine += c; // add it to the end of the currentLine + } + + // Check to see if the client request was "GET /H" or "GET /L": + if (currentLine.endsWith("GET /H")) { + digitalWrite(LED_BUILTIN, HIGH); // GET /H turns the LED on + } + if (currentLine.endsWith("GET /L")) { + digitalWrite(LED_BUILTIN, LOW); // GET /L turns the LED off + } + } + } + // close the connection: + client.stop(); + Serial.println("Client Disconnected."); + } +} \ No newline at end of file diff --git a/Firmware/factoryTest/FactoryTest_Krake01/WiFiAccessPointWithMAC.h b/Firmware/factoryTest/FactoryTest_Krake01/WiFiAccessPointWithMAC.h new file mode 100644 index 0000000..22d27fa --- /dev/null +++ b/Firmware/factoryTest/FactoryTest_Krake01/WiFiAccessPointWithMAC.h @@ -0,0 +1,17 @@ +#ifndef WIFIACCESSPOINTWITHMAC_H +#define WIFIACCESSPOINTWITHMAC_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void WiFiAccessPointWithMAC_setup(); +void WiFiAccessPointWithMAC_loop(); + +#ifdef __cplusplus +} +#endif + +#endif // WIFIACCESSPOINTWITHMAC_H diff --git a/PWA_REV2/Gerbers/PWA_REV2-B_Cu.gbr b/PWA_REV2/Gerbers/PWA_REV2-B_Cu.gbr index 3d5923e..b6a71b4 100644 --- a/PWA_REV2/Gerbers/PWA_REV2-B_Cu.gbr +++ b/PWA_REV2/Gerbers/PWA_REV2-B_Cu.gbr @@ -1,12 +1,12 @@ %TF.GenerationSoftware,KiCad,Pcbnew,7.0.5*% -%TF.CreationDate,2025-09-17T13:25:06-04:00*% +%TF.CreationDate,2025-09-19T13:44:07-04:00*% %TF.ProjectId,PWA_REV2,5057415f-5245-4563-922e-6b696361645f,2.0*% %TF.SameCoordinates,Original*% %TF.FileFunction,Copper,L2,Bot*% %TF.FilePolarity,Positive*% %FSLAX46Y46*% G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* -G04 Created by KiCad (PCBNEW 7.0.5) date 2025-09-17 13:25:06* +G04 Created by KiCad (PCBNEW 7.0.5) date 2025-09-19 13:44:07* %MOMM*% %LPD*% G01* @@ -885,7 +885,6 @@ X68620000Y-79364000D03* %TD*% D40* %TO.N,GND*% -X112776000Y-82042000D03* X66040000Y-51054000D03* X89662000Y-43603000D03* X185166000Y-30480000D03* @@ -950,7 +949,7 @@ X131826000Y-33528000D03* X176784000Y-59436000D03* X149763701Y-55976297D03* X168570000Y-43688000D03* -X89154000Y-33147000D03* +X87884000Y-31115000D03* X165875000Y-58166000D03* X131826000Y-80010000D03* X155796202Y-74008202D03* @@ -990,6 +989,7 @@ X69850000Y-100076000D03* X129794000Y-80010000D03* X94996000Y-86614000D03* X148336000Y-64008000D03* +X112776000Y-82042000D03* X146812000Y-84836000D03* X133350000Y-69596000D03* X107347798Y-68231798D03* @@ -1120,7 +1120,7 @@ X100457000Y-77089000D03* X151763439Y-51920000D03* X151130000Y-54610002D03* D42* -X85090000Y-29718000D03* +X85031840Y-29648131D03* D40* %TO.N,Net-(D301-COM)*% X153924000Y-77724000D03* @@ -1203,59 +1203,17 @@ X115697000Y-82677000D03* X105320801Y-81073000D03* %TD*% D43* -%TO.N,Net-(Q207-E)*% -X110709383Y-82844000D02* -X115530000Y-82844000D01* -X109103964Y-82535505D02* -X110400888Y-82535505D01* -X110400888Y-82535505D02* -X110709383Y-82844000D01* -X115530000Y-82844000D02* -X115697000Y-82677000D01* -%TO.N,Net-(U501-RXD)*% -X132800999Y-51266985D02* -X132588000Y-51053986D01* -X132800999Y-52891656D02* -X132800999Y-51266985D01* -X132361165Y-53331490D02* -X132800999Y-52891656D01* -X127807343Y-57418915D02* -X131894768Y-53331490D01* -X127807343Y-62651000D02* -X127807343Y-57418915D01* -X131894768Y-53331490D02* -X132361165Y-53331490D01* -%TO.N,ControllerRX*% -X127254000Y-57394913D02* -X132036852Y-52612061D01* -X127254000Y-61546670D02* -X127254000Y-57394913D01* -X126238000Y-62562670D02* -X127254000Y-61546670D01* -X132036852Y-52612061D02* -X132125999Y-52612061D01* -%TO.N,Net-(Q206-E)*% -X116840000Y-80772000D02* -X112029648Y-80772000D01* -X112029648Y-80772000D02* -X110810813Y-81990835D01* %TO.N,GND*% -X101727000Y-93773000D02* -X101727000Y-92202000D01* X103884000Y-95930000D02* X101727000Y-93773000D01* -%TO.N,+3.3V*% -X135890000Y-53340000D02* -X135255000Y-52705000D01* -X135890000Y-55626000D02* -X135890000Y-53340000D01* -%TO.N,GND*% X112524000Y-95930000D02* X112524000Y-95121000D01* X112524000Y-100110000D02* X103884000Y-100110000D01* X103884000Y-95930000D02* X103884000Y-100110000D01* +X101727000Y-93773000D02* +X101727000Y-92202000D01* X112524000Y-95930000D02* X112524000Y-100110000D01* X112524000Y-95121000D02* @@ -1385,6 +1343,15 @@ X147647021Y-58424205D02* X148047020Y-58824204D01* X146543732Y-53489338D02* X147647021Y-54592627D01* +%TO.N,ControllerRX*% +X127254000Y-57394913D02* +X132036852Y-52612061D01* +X132036852Y-52612061D02* +X132125999Y-52612061D01* +X127254000Y-61546670D02* +X127254000Y-57394913D01* +X126238000Y-62562670D02* +X127254000Y-61546670D01* D47* %TO.N,/LCD And I2C Interface/Vcc_LCD*% X147320000Y-38862000D02* @@ -1426,6 +1393,19 @@ X133985000Y-26035000D02* X132588000Y-27432000D01* X132588000Y-27432000D02* X132588000Y-30226000D01* +%TO.N,Net-(U501-RXD)*% +X131894768Y-53331490D02* +X132361165Y-53331490D01* +X132361165Y-53331490D02* +X132800999Y-52891656D01* +X132800999Y-52891656D02* +X132800999Y-51266985D01* +X127807343Y-62651000D02* +X127807343Y-57418915D01* +X132800999Y-51266985D02* +X132588000Y-51053986D01* +X127807343Y-57418915D02* +X131894768Y-53331490D01* %TO.N,Net-(U501-~{CTS})*% X112014000Y-78740000D02* X114046000Y-78740000D01* @@ -1447,8 +1427,6 @@ X120650000Y-57893947D01* D44* X92712079Y-42418000D02* X93074079Y-42056000D01* -X85090000Y-29718000D02* -X85090000Y-37592000D01* X85090000Y-39624000D02* X87884000Y-42418000D01* X151130000Y-52553439D02* @@ -1469,6 +1447,8 @@ X99568000Y-78780000D01* X135890000Y-55626000D02* X135890000Y-62289000D01* D44* +X85090000Y-37592000D02* +X85090000Y-29706291D01* X151130000Y-54610000D02* X151130000Y-52553439D01* D43* @@ -1477,9 +1457,19 @@ X100457000Y-77891000D01* D44* X85090000Y-37592000D02* X85090000Y-39624000D01* +D43* +X135890000Y-55626000D02* +X135890000Y-53340000D01* +D44* X87884000Y-42418000D02* X92712079Y-42418000D01* D43* +X135890000Y-53340000D02* +X135255000Y-52705000D01* +D44* +X85090000Y-29706291D02* +X85031840Y-29648131D01* +D43* %TO.N,Net-(D301-COM)*% X151892000Y-73406000D02* X151638000Y-73406000D01* @@ -1639,6 +1629,10 @@ X128564929Y-87127680D01* %TO.N,Net-(Q206-E)*% X106193802Y-81748000D02* X102703000Y-81748000D01* +X112029648Y-80772000D02* +X110810813Y-81990835D01* +X116840000Y-80772000D02* +X112029648Y-80772000D01* X106416412Y-81970610D02* X106193802Y-81748000D01* X102703000Y-81748000D02* @@ -1671,10 +1665,18 @@ X141523263Y-80568122D01* X144272000Y-61468000D02* X144272000Y-59059465D01* %TO.N,Net-(Q207-E)*% +X115530000Y-82844000D02* +X115697000Y-82677000D01* X105320801Y-81073000D02* X107641459Y-81073000D01* X107641459Y-81073000D02* X109103964Y-82535505D01* +X110709383Y-82844000D02* +X115530000Y-82844000D01* +X109103964Y-82535505D02* +X110400888Y-82535505D01* +X110400888Y-82535505D02* +X110709383Y-82844000D01* %TD*% %TA.AperFunction,Conductor*% %TO.N,GND*% @@ -15502,9 +15504,9 @@ X159639419Y-29255826D01* X159639417Y-29255825D01* X159639416Y-29255824D01* X159436963Y-29131760D01* -X159217594Y-29040895D01* +X159321689Y-29084012D01* X159217592Y-29040894D01* -X159059651Y-29002976D01* +X159031017Y-28996102D01* X158986711Y-28985465D01* X158750000Y-28966835D01* X158513289Y-28985465D01* @@ -15531,9 +15533,9 @@ X136779419Y-29255826D01* X136779417Y-29255825D01* X136779416Y-29255824D01* X136576963Y-29131760D01* -X136357594Y-29040895D01* +X136461689Y-29084012D01* X136357592Y-29040894D01* -X136199651Y-29002976D01* +X136171017Y-28996102D01* X136126711Y-28985465D01* X135890000Y-28966835D01* X135653289Y-28985465D01* @@ -15555,11 +15557,11 @@ X133501504Y-30226000D01* X133481542Y-30036072D01* X133422527Y-29854444D01* X133327040Y-29689056D01* -X133276232Y-29632628D01* +X133309563Y-29669646D01* X133253862Y-29607783D01* X133223145Y-29543775D01* X133221499Y-29523473D01* -X133221499Y-28634956D01* +X133221499Y-28634122D01* X133221499Y-27746590D01* X133241501Y-27678473D01* X133258395Y-27657508D01* @@ -15699,7 +15701,8 @@ X131934498Y-29591595D01* X131922137Y-29607784D01* X131848957Y-29689059D01* X131753476Y-29854438D01* -X131753473Y-29854445D01* +X131753473Y-29854444D01* +X131751944Y-29859150D01* X131694457Y-30036072D01* X131674496Y-30226000D01* X120522613Y-30226000D01* @@ -15716,7 +15719,7 @@ X120572642Y-28940000D01* X120552130Y-28744845D01* X120491492Y-28558220D01* X120491492Y-28558219D01* -X120472196Y-28524798D01* +X120464082Y-28510745D01* X120459321Y-28502499D01* X120442583Y-28433505D01* X120459322Y-28376496D01* @@ -16038,7 +16041,7 @@ X96139411Y-28717925D01* X96109869Y-28808847D01* X96094403Y-28955999D01* X96089358Y-29004000D01* -X96103143Y-29135155D01* +X96107249Y-29174222D01* X96109871Y-29199160D01* X96170505Y-29385774D01* X96170508Y-29385781D01* @@ -16050,8 +16053,8 @@ X96170505Y-29622225D01* X96109871Y-29808839D01* X96109870Y-29808843D01* X96109870Y-29808845D01* -X96096085Y-29940000D01* -X96092722Y-29971999D01* +X96099127Y-29911055D01* +X96091345Y-29985100D01* X96089358Y-30004000D01* X96107875Y-30180176D01* X96109871Y-30199160D01* @@ -16265,41 +16268,162 @@ X88320748Y-41237595D01* X86270405Y-39187252D01* X86236379Y-39124940D01* X86233500Y-39098157D01* -X86233500Y-29773775D01* -X86233769Y-29767959D01* -X86238399Y-29718003D01* -X86238399Y-29717995D01* -X86218845Y-29506983D01* -X86217996Y-29504000D01* -X86160850Y-29303150D01* -X86066389Y-29113446D01* -X85938677Y-28944329D01* -X85938675Y-28944326D01* -X85782066Y-28801558D01* -X85601891Y-28689998D01* -X85601886Y-28689996D01* -X85601885Y-28689995D01* -X85572678Y-28678680D01* -X85404277Y-28613441D01* -X85404278Y-28613441D01* -X85404275Y-28613440D01* -X85404274Y-28613440D01* -X85195961Y-28574500D01* -X84984039Y-28574500D01* -X84775726Y-28613440D01* -X84775721Y-28613441D01* -X84578119Y-28689993D01* -X84578108Y-28689998D01* -X84397933Y-28801558D01* -X84241324Y-28944326D01* -X84113612Y-29113444D01* -X84019150Y-29303150D01* -X84019147Y-29303158D01* -X83961154Y-29506983D01* -X83941601Y-29717995D01* -X83941601Y-29718003D01* -X83946231Y-29767959D01* -X83946500Y-29773775D01* +X86233500Y-29734181D01* +X86233567Y-29731270D01* +X86234213Y-29717278D01* +X86237174Y-29653254D01* +X86226383Y-29575900D01* +X86226048Y-29573008D01* +X86220209Y-29509996D01* +X86218845Y-29495273D01* +X86212766Y-29473908D01* +X86210965Y-29465374D01* +X86207896Y-29443364D01* +X86183063Y-29369276D01* +X86182217Y-29366542D01* +X86160850Y-29291441D01* +X86157773Y-29285262D01* +X86150950Y-29271559D01* +X86147609Y-29263495D01* +X86140549Y-29242429D01* +X86112856Y-29192711D01* +X86102558Y-29174222D01* +X86101200Y-29171647D01* +X86087569Y-29144273D01* +X86066389Y-29101737D01* +X86052993Y-29083998D01* +X86048232Y-29076691D01* +X86037426Y-29057290D01* +X85993175Y-29004000D01* +X85987549Y-28997224D01* +X85985744Y-28994944D01* +X85944249Y-28939999D01* +X85938677Y-28932620D01* +X85901600Y-28898819D01* +X85885937Y-28881638D01* +X85880517Y-28874460D01* +X85880516Y-28874459D01* +X85880513Y-28874455D01* +X85822798Y-28821842D01* +X85820705Y-28819843D01* +X85803033Y-28802171D01* +X85783805Y-28786204D01* +X85781611Y-28784294D01* +X85746264Y-28752072D01* +X85723905Y-28731689D01* +X85712912Y-28724882D01* +X85705017Y-28719993D01* +X85697932Y-28714895D01* +X85680844Y-28700707D01* +X85680842Y-28700705D01* +X85680838Y-28700703D01* +X85680833Y-28700699D01* +X85612628Y-28662709D01* +X85610120Y-28661235D01* +X85550318Y-28624208D01* +X85543725Y-28620126D01* +X85543723Y-28620125D01* +X85543719Y-28620123D01* +X85523012Y-28612101D01* +X85515112Y-28608393D01* +X85504877Y-28602692D01* +X85495703Y-28597582D01* +X85495701Y-28597581D01* +X85452492Y-28583099D01* +X85421635Y-28572757D01* +X85418917Y-28571775D01* +X85346113Y-28543570D01* +X85324277Y-28539488D01* +X85315827Y-28537293D01* +X85294772Y-28530236D01* +X85294756Y-28530233D01* +X85217420Y-28519445D01* +X85214550Y-28518976D01* +X85137801Y-28504631D01* +X85115588Y-28504631D01* +X85106874Y-28504026D01* +X85084878Y-28500958D01* +X85084877Y-28500957D01* +X85006873Y-28504564D01* +X85003963Y-28504631D01* +X84925878Y-28504631D01* +X84904043Y-28508712D01* +X84895370Y-28509719D01* +X84887942Y-28510062D01* +X84873182Y-28510745D01* +X84797182Y-28528619D01* +X84794346Y-28529217D01* +X84717566Y-28543570D01* +X84696852Y-28551595D01* +X84688512Y-28554178D01* +X84666887Y-28559264D01* +X84666883Y-28559265D01* +X84595446Y-28590808D01* +X84592771Y-28591916D01* +X84578150Y-28597581D01* +X84519961Y-28620123D01* +X84519948Y-28620129D01* +X84501062Y-28631821D01* +X84493343Y-28635890D01* +X84473029Y-28644860D01* +X84473022Y-28644863D01* +X84408595Y-28688998D01* +X84406157Y-28690586D01* +X84339779Y-28731686D01* +X84339774Y-28731689D01* +X84323358Y-28746653D01* +X84316517Y-28752072D01* +X84298192Y-28764626D01* +X84298187Y-28764630D01* +X84242972Y-28819843D01* +X84240869Y-28821852D01* +X84183164Y-28874457D01* +X84169776Y-28892186D01* +X84164045Y-28898772D01* +X84148340Y-28914477D01* +X84104208Y-28978899D01* +X84102510Y-28981259D01* +X84055455Y-29043570D01* +X84055449Y-29043579D01* +X84045548Y-29063462D01* +X84041126Y-29070986D01* +X84028572Y-29089314D01* +X83997025Y-29160760D01* +X83995789Y-29163392D01* +X83960989Y-29233282D01* +X83954909Y-29254647D01* +X83951945Y-29262855D01* +X83942975Y-29283173D01* +X83942973Y-29283178D01* +X83925095Y-29359188D01* +X83924364Y-29362003D01* +X83902994Y-29437116D01* +X83900944Y-29459230D01* +X83899539Y-29467844D01* +X83894455Y-29489464D01* +X83894454Y-29489472D01* +X83894454Y-29489473D01* +X83891792Y-29547048D01* +X83890848Y-29567451D01* +X83890647Y-29570352D01* +X83883441Y-29648132D01* +X83885490Y-29670253D01* +X83885691Y-29678979D01* +X83884666Y-29701162D01* +X83884667Y-29701170D01* +X83895455Y-29778516D01* +X83895790Y-29781406D01* +X83902994Y-29859149D01* +X83902994Y-29859150D01* +X83909072Y-29880511D01* +X83910873Y-29889052D01* +X83913944Y-29911058D01* +X83913946Y-29911064D01* +X83913947Y-29911068D01* +X83938760Y-29985100D01* +X83939620Y-29987876D01* +X83941688Y-29995142D01* +X83946500Y-30029631D01* X83946500Y-36285029D01* X83926498Y-36353150D01* X83872842Y-36399643D01* @@ -16596,8 +16720,8 @@ X59653101Y-29067892D01* X59653102Y-29067891D01* X59721670Y-28956000D01* X75194835Y-28956000D01* -X75201517Y-29040894D01* -X75213465Y-29192710D01* +X75213465Y-29192711D01* +X75228617Y-29255824D01* X75268894Y-29423592D01* X75359031Y-29641202D01* X75359760Y-29642963D01* @@ -16704,7 +16828,7 @@ X75483825Y-28066582D01* X75359759Y-28269038D01* X75268894Y-28488407D01* X75213465Y-28719289D01* -X75195754Y-28944329D01* +X75199583Y-28895678D01* X75194835Y-28956000D01* X59721670Y-28956000D01* X59767791Y-28880738D01* @@ -30460,9 +30584,9 @@ X159639419Y-29255826D01* X159639417Y-29255825D01* X159639416Y-29255824D01* X159436963Y-29131760D01* -X159217594Y-29040895D01* +X159321689Y-29084012D01* X159217592Y-29040894D01* -X159059651Y-29002976D01* +X159031017Y-28996102D01* X158986711Y-28985465D01* X158750000Y-28966835D01* X158513289Y-28985465D01* @@ -30489,9 +30613,9 @@ X136779419Y-29255826D01* X136779417Y-29255825D01* X136779416Y-29255824D01* X136576963Y-29131760D01* -X136357594Y-29040895D01* +X136461689Y-29084012D01* X136357592Y-29040894D01* -X136199651Y-29002976D01* +X136171017Y-28996102D01* X136126711Y-28985465D01* X135890000Y-28966835D01* X135653289Y-28985465D01* @@ -30513,11 +30637,11 @@ X133501504Y-30226000D01* X133481542Y-30036072D01* X133422527Y-29854444D01* X133327040Y-29689056D01* -X133276232Y-29632628D01* +X133309563Y-29669646D01* X133253862Y-29607783D01* X133223145Y-29543775D01* X133221499Y-29523473D01* -X133221499Y-28634956D01* +X133221499Y-28634122D01* X133221499Y-27746590D01* X133241501Y-27678473D01* X133258395Y-27657508D01* @@ -30657,7 +30781,8 @@ X131934498Y-29591595D01* X131922137Y-29607784D01* X131848957Y-29689059D01* X131753476Y-29854438D01* -X131753473Y-29854445D01* +X131753473Y-29854444D01* +X131751944Y-29859150D01* X131694457Y-30036072D01* X131674496Y-30226000D01* X120522613Y-30226000D01* @@ -30674,7 +30799,7 @@ X120572642Y-28940000D01* X120552130Y-28744845D01* X120491492Y-28558220D01* X120491492Y-28558219D01* -X120472196Y-28524798D01* +X120464082Y-28510745D01* X120459321Y-28502499D01* X120442583Y-28433505D01* X120459322Y-28376496D01* @@ -30996,7 +31121,7 @@ X96139411Y-28717925D01* X96109869Y-28808847D01* X96094403Y-28955999D01* X96089358Y-29004000D01* -X96103143Y-29135155D01* +X96107249Y-29174222D01* X96109871Y-29199160D01* X96170505Y-29385774D01* X96170508Y-29385781D01* @@ -31008,8 +31133,8 @@ X96170505Y-29622225D01* X96109871Y-29808839D01* X96109870Y-29808843D01* X96109870Y-29808845D01* -X96096085Y-29940000D01* -X96092722Y-29971999D01* +X96099127Y-29911055D01* +X96091345Y-29985100D01* X96089358Y-30004000D01* X96107875Y-30180176D01* X96109871Y-30199160D01* @@ -31223,41 +31348,162 @@ X88320748Y-41237595D01* X86270405Y-39187252D01* X86236379Y-39124940D01* X86233500Y-39098157D01* -X86233500Y-29773775D01* -X86233769Y-29767959D01* -X86238399Y-29718003D01* -X86238399Y-29717995D01* -X86218845Y-29506983D01* -X86217996Y-29504000D01* -X86160850Y-29303150D01* -X86066389Y-29113446D01* -X85938677Y-28944329D01* -X85938675Y-28944326D01* -X85782066Y-28801558D01* -X85601891Y-28689998D01* -X85601886Y-28689996D01* -X85601885Y-28689995D01* -X85572678Y-28678680D01* -X85404277Y-28613441D01* -X85404278Y-28613441D01* -X85404275Y-28613440D01* -X85404274Y-28613440D01* -X85195961Y-28574500D01* -X84984039Y-28574500D01* -X84775726Y-28613440D01* -X84775721Y-28613441D01* -X84578119Y-28689993D01* -X84578108Y-28689998D01* -X84397933Y-28801558D01* -X84241324Y-28944326D01* -X84113612Y-29113444D01* -X84019150Y-29303150D01* -X84019147Y-29303158D01* -X83961154Y-29506983D01* -X83941601Y-29717995D01* -X83941601Y-29718003D01* -X83946231Y-29767959D01* -X83946500Y-29773775D01* +X86233500Y-29734181D01* +X86233567Y-29731270D01* +X86234213Y-29717278D01* +X86237174Y-29653254D01* +X86226383Y-29575900D01* +X86226048Y-29573008D01* +X86220209Y-29509996D01* +X86218845Y-29495273D01* +X86212766Y-29473908D01* +X86210965Y-29465374D01* +X86207896Y-29443364D01* +X86183063Y-29369276D01* +X86182217Y-29366542D01* +X86160850Y-29291441D01* +X86157773Y-29285262D01* +X86150950Y-29271559D01* +X86147609Y-29263495D01* +X86140549Y-29242429D01* +X86112856Y-29192711D01* +X86102558Y-29174222D01* +X86101200Y-29171647D01* +X86087569Y-29144273D01* +X86066389Y-29101737D01* +X86052993Y-29083998D01* +X86048232Y-29076691D01* +X86037426Y-29057290D01* +X85993175Y-29004000D01* +X85987549Y-28997224D01* +X85985744Y-28994944D01* +X85944249Y-28939999D01* +X85938677Y-28932620D01* +X85901600Y-28898819D01* +X85885937Y-28881638D01* +X85880517Y-28874460D01* +X85880516Y-28874459D01* +X85880513Y-28874455D01* +X85822798Y-28821842D01* +X85820705Y-28819843D01* +X85803033Y-28802171D01* +X85783805Y-28786204D01* +X85781611Y-28784294D01* +X85746264Y-28752072D01* +X85723905Y-28731689D01* +X85712912Y-28724882D01* +X85705017Y-28719993D01* +X85697932Y-28714895D01* +X85680844Y-28700707D01* +X85680842Y-28700705D01* +X85680838Y-28700703D01* +X85680833Y-28700699D01* +X85612628Y-28662709D01* +X85610120Y-28661235D01* +X85550318Y-28624208D01* +X85543725Y-28620126D01* +X85543723Y-28620125D01* +X85543719Y-28620123D01* +X85523012Y-28612101D01* +X85515112Y-28608393D01* +X85504877Y-28602692D01* +X85495703Y-28597582D01* +X85495701Y-28597581D01* +X85452492Y-28583099D01* +X85421635Y-28572757D01* +X85418917Y-28571775D01* +X85346113Y-28543570D01* +X85324277Y-28539488D01* +X85315827Y-28537293D01* +X85294772Y-28530236D01* +X85294756Y-28530233D01* +X85217420Y-28519445D01* +X85214550Y-28518976D01* +X85137801Y-28504631D01* +X85115588Y-28504631D01* +X85106874Y-28504026D01* +X85084878Y-28500958D01* +X85084877Y-28500957D01* +X85006873Y-28504564D01* +X85003963Y-28504631D01* +X84925878Y-28504631D01* +X84904043Y-28508712D01* +X84895370Y-28509719D01* +X84887942Y-28510062D01* +X84873182Y-28510745D01* +X84797182Y-28528619D01* +X84794346Y-28529217D01* +X84717566Y-28543570D01* +X84696852Y-28551595D01* +X84688512Y-28554178D01* +X84666887Y-28559264D01* +X84666883Y-28559265D01* +X84595446Y-28590808D01* +X84592771Y-28591916D01* +X84578150Y-28597581D01* +X84519961Y-28620123D01* +X84519948Y-28620129D01* +X84501062Y-28631821D01* +X84493343Y-28635890D01* +X84473029Y-28644860D01* +X84473022Y-28644863D01* +X84408595Y-28688998D01* +X84406157Y-28690586D01* +X84339779Y-28731686D01* +X84339774Y-28731689D01* +X84323358Y-28746653D01* +X84316517Y-28752072D01* +X84298192Y-28764626D01* +X84298187Y-28764630D01* +X84242972Y-28819843D01* +X84240869Y-28821852D01* +X84183164Y-28874457D01* +X84169776Y-28892186D01* +X84164045Y-28898772D01* +X84148340Y-28914477D01* +X84104208Y-28978899D01* +X84102510Y-28981259D01* +X84055455Y-29043570D01* +X84055449Y-29043579D01* +X84045548Y-29063462D01* +X84041126Y-29070986D01* +X84028572Y-29089314D01* +X83997025Y-29160760D01* +X83995789Y-29163392D01* +X83960989Y-29233282D01* +X83954909Y-29254647D01* +X83951945Y-29262855D01* +X83942975Y-29283173D01* +X83942973Y-29283178D01* +X83925095Y-29359188D01* +X83924364Y-29362003D01* +X83902994Y-29437116D01* +X83900944Y-29459230D01* +X83899539Y-29467844D01* +X83894455Y-29489464D01* +X83894454Y-29489472D01* +X83894454Y-29489473D01* +X83891792Y-29547048D01* +X83890848Y-29567451D01* +X83890647Y-29570352D01* +X83883441Y-29648132D01* +X83885490Y-29670253D01* +X83885691Y-29678979D01* +X83884666Y-29701162D01* +X83884667Y-29701170D01* +X83895455Y-29778516D01* +X83895790Y-29781406D01* +X83902994Y-29859149D01* +X83902994Y-29859150D01* +X83909072Y-29880511D01* +X83910873Y-29889052D01* +X83913944Y-29911058D01* +X83913946Y-29911064D01* +X83913947Y-29911068D01* +X83938760Y-29985100D01* +X83939620Y-29987876D01* +X83941688Y-29995142D01* +X83946500Y-30029631D01* X83946500Y-36285029D01* X83926498Y-36353150D01* X83872842Y-36399643D01* @@ -31554,8 +31800,8 @@ X59653101Y-29067892D01* X59653102Y-29067891D01* X59721670Y-28956000D01* X75194835Y-28956000D01* -X75201517Y-29040894D01* -X75213465Y-29192710D01* +X75213465Y-29192711D01* +X75228617Y-29255824D01* X75268894Y-29423592D01* X75359031Y-29641202D01* X75359760Y-29642963D01* @@ -31662,7 +31908,7 @@ X75483825Y-28066582D01* X75359759Y-28269038D01* X75268894Y-28488407D01* X75213465Y-28719289D01* -X75195754Y-28944329D01* +X75199583Y-28895678D01* X75194835Y-28956000D01* X59721670Y-28956000D01* X59767791Y-28880738D01* diff --git a/PWA_REV2/Gerbers/PWA_REV2-B_Mask.gbr b/PWA_REV2/Gerbers/PWA_REV2-B_Mask.gbr index b8280fe..2f8a7a2 100644 --- a/PWA_REV2/Gerbers/PWA_REV2-B_Mask.gbr +++ b/PWA_REV2/Gerbers/PWA_REV2-B_Mask.gbr @@ -1,12 +1,12 @@ %TF.GenerationSoftware,KiCad,Pcbnew,7.0.5*% -%TF.CreationDate,2025-09-17T13:25:07-04:00*% +%TF.CreationDate,2025-09-19T13:44:07-04:00*% %TF.ProjectId,PWA_REV2,5057415f-5245-4563-922e-6b696361645f,2.0*% %TF.SameCoordinates,Original*% %TF.FileFunction,Soldermask,Bot*% %TF.FilePolarity,Negative*% %FSLAX46Y46*% G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* -G04 Created by KiCad (PCBNEW 7.0.5) date 2025-09-17 13:25:07* +G04 Created by KiCad (PCBNEW 7.0.5) date 2025-09-19 13:44:07* %MOMM*% %LPD*% G01* diff --git a/PWA_REV2/Gerbers/PWA_REV2-B_Paste.gbr b/PWA_REV2/Gerbers/PWA_REV2-B_Paste.gbr index 47daa86..29f7ad2 100644 --- a/PWA_REV2/Gerbers/PWA_REV2-B_Paste.gbr +++ b/PWA_REV2/Gerbers/PWA_REV2-B_Paste.gbr @@ -1,12 +1,12 @@ %TF.GenerationSoftware,KiCad,Pcbnew,7.0.5*% -%TF.CreationDate,2025-09-17T13:25:06-04:00*% +%TF.CreationDate,2025-09-19T13:44:07-04:00*% %TF.ProjectId,PWA_REV2,5057415f-5245-4563-922e-6b696361645f,2.0*% %TF.SameCoordinates,Original*% %TF.FileFunction,Paste,Bot*% %TF.FilePolarity,Positive*% %FSLAX46Y46*% G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* -G04 Created by KiCad (PCBNEW 7.0.5) date 2025-09-17 13:25:06* +G04 Created by KiCad (PCBNEW 7.0.5) date 2025-09-19 13:44:07* %MOMM*% %LPD*% G01* diff --git a/PWA_REV2/Gerbers/PWA_REV2-B_Silkscreen.gbr b/PWA_REV2/Gerbers/PWA_REV2-B_Silkscreen.gbr index 5a90b5f..84192cd 100644 --- a/PWA_REV2/Gerbers/PWA_REV2-B_Silkscreen.gbr +++ b/PWA_REV2/Gerbers/PWA_REV2-B_Silkscreen.gbr @@ -1,12 +1,12 @@ %TF.GenerationSoftware,KiCad,Pcbnew,7.0.5*% -%TF.CreationDate,2025-09-17T13:25:07-04:00*% +%TF.CreationDate,2025-09-19T13:44:07-04:00*% %TF.ProjectId,PWA_REV2,5057415f-5245-4563-922e-6b696361645f,2.0*% %TF.SameCoordinates,Original*% %TF.FileFunction,Legend,Bot*% %TF.FilePolarity,Positive*% %FSLAX46Y46*% G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* -G04 Created by KiCad (PCBNEW 7.0.5) date 2025-09-17 13:25:07* +G04 Created by KiCad (PCBNEW 7.0.5) date 2025-09-19 13:44:07* %MOMM*% %LPD*% G01* diff --git a/PWA_REV2/Gerbers/PWA_REV2-Edge_Cuts.gbr b/PWA_REV2/Gerbers/PWA_REV2-Edge_Cuts.gbr index 617b9e7..658de20 100644 --- a/PWA_REV2/Gerbers/PWA_REV2-Edge_Cuts.gbr +++ b/PWA_REV2/Gerbers/PWA_REV2-Edge_Cuts.gbr @@ -1,11 +1,11 @@ %TF.GenerationSoftware,KiCad,Pcbnew,7.0.5*% -%TF.CreationDate,2025-09-17T13:25:07-04:00*% +%TF.CreationDate,2025-09-19T13:44:07-04:00*% %TF.ProjectId,PWA_REV2,5057415f-5245-4563-922e-6b696361645f,2.0*% %TF.SameCoordinates,Original*% %TF.FileFunction,Profile,NP*% %FSLAX46Y46*% G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* -G04 Created by KiCad (PCBNEW 7.0.5) date 2025-09-17 13:25:07* +G04 Created by KiCad (PCBNEW 7.0.5) date 2025-09-19 13:44:07* %MOMM*% %LPD*% G01* diff --git a/PWA_REV2/Gerbers/PWA_REV2-F_Cu.gbr b/PWA_REV2/Gerbers/PWA_REV2-F_Cu.gbr index c24dbb4..06b0e8b 100644 --- a/PWA_REV2/Gerbers/PWA_REV2-F_Cu.gbr +++ b/PWA_REV2/Gerbers/PWA_REV2-F_Cu.gbr @@ -1,12 +1,12 @@ %TF.GenerationSoftware,KiCad,Pcbnew,7.0.5*% -%TF.CreationDate,2025-09-17T13:25:06-04:00*% +%TF.CreationDate,2025-09-19T13:44:06-04:00*% %TF.ProjectId,PWA_REV2,5057415f-5245-4563-922e-6b696361645f,2.0*% %TF.SameCoordinates,Original*% %TF.FileFunction,Copper,L1,Top*% %TF.FilePolarity,Positive*% %FSLAX46Y46*% G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* -G04 Created by KiCad (PCBNEW 7.0.5) date 2025-09-17 13:25:06* +G04 Created by KiCad (PCBNEW 7.0.5) date 2025-09-19 13:44:06* %MOMM*% %LPD*% G01* @@ -2530,7 +2530,6 @@ X68620000Y-79364000D03* %TD*% D74* %TO.N,GND*% -X112776000Y-82042000D03* X66040000Y-51054000D03* X89662000Y-43603000D03* X185166000Y-30480000D03* @@ -2595,7 +2594,7 @@ X131826000Y-33528000D03* X176784000Y-59436000D03* X149763701Y-55976297D03* X168570000Y-43688000D03* -X89154000Y-33147000D03* +X87884000Y-31115000D03* X165875000Y-58166000D03* X131826000Y-80010000D03* X155796202Y-74008202D03* @@ -2635,6 +2634,7 @@ X69850000Y-100076000D03* X129794000Y-80010000D03* X94996000Y-86614000D03* X148336000Y-64008000D03* +X112776000Y-82042000D03* X146812000Y-84836000D03* X133350000Y-69596000D03* X107347798Y-68231798D03* @@ -2765,7 +2765,7 @@ X100457000Y-77089000D03* X151763439Y-51920000D03* X151130000Y-54610002D03* D76* -X85090000Y-29718000D03* +X85031840Y-29648131D03* D74* %TO.N,Net-(D301-COM)*% X153924000Y-77724000D03* @@ -2848,220 +2848,6 @@ X115697000Y-82677000D03* X105320801Y-81073000D03* %TD*% D77* -%TO.N,Net-(U501-~{CTS})*% -X111875500Y-85155000D02* -X111875500Y-81164500D01* -X111875500Y-81164500D02* -X113030000Y-80010000D01* -%TO.N,Net-(U501-RXD)*% -X132251000Y-50716986D02* -X132588000Y-51053986D01* -X125459543Y-34170012D02* -X129142754Y-34170012D01* -X123848500Y-32558969D02* -X125459543Y-34170012D01* -X123848500Y-31242000D02* -X123848500Y-32558969D01* -%TO.N,ControllerRX*% -X131851000Y-37443944D02* -X131851000Y-52337062D01* -X128093359Y-34570000D02* -X128093372Y-34570012D01* -X125293846Y-34570000D02* -X128093359Y-34570000D01* -X124467846Y-33744000D02* -X125293846Y-34570000D01* -%TO.N,SDA*% -X124764684Y-34970000D02* -X123938684Y-34144000D01* -X128811371Y-34970000D02* -X124764684Y-34970000D01* -X131451000Y-37609629D02* -X128811371Y-34970000D01* -X131450999Y-52933233D02* -X131451000Y-37609629D01* -%TO.N,Net-(U501-RXD)*% -X132251000Y-37278259D02* -X132251000Y-50716986D01* -%TO.N,SDA*% -X132146382Y-53628616D02* -X131450999Y-52933233D01* -X135539979Y-53628616D02* -X132146382Y-53628616D01* -X136144000Y-53024595D02* -X135539979Y-53628616D01* -X136144000Y-51204500D02* -X136144000Y-53024595D01* -%TO.N,ControllerRX*% -X128977068Y-34570012D02* -X131851000Y-37443944D01* -X121438000Y-33744000D02* -X124467846Y-33744000D01* -X131851000Y-52337062D02* -X132125999Y-52612061D01* -%TO.N,SDA*% -X120838000Y-34144000D02* -X119634000Y-32940000D01* -%TO.N,ControllerRX*% -X128093372Y-34570012D02* -X128977068Y-34570012D01* -X119634000Y-31940000D02* -X121438000Y-33744000D01* -%TO.N,Net-(U501-RXD)*% -X129142754Y-34170012D02* -X132251000Y-37278259D01* -%TO.N,SDA*% -X123938684Y-34144000D02* -X120838000Y-34144000D01* -%TO.N,Net-(Q207-E)*% -X117059784Y-82677000D02* -X115697000Y-82677000D01* -X119785000Y-77257500D02* -X119785000Y-79951784D01* -X119785000Y-79951784D02* -X117059784Y-82677000D01* -X118285500Y-75758000D02* -X119785000Y-77257500D01* -%TO.N,Net-(Q206-E)*% -X106416412Y-81970610D02* -X106726022Y-81661000D01* -X109448405Y-81955000D02* -X110774978Y-81955000D01* -X106726022Y-81661000D02* -X109154405Y-81661000D01* -X109154405Y-81661000D02* -X109448405Y-81955000D01* -X110774978Y-81955000D02* -X110810813Y-81990835D01* -%TO.N,GND*% -X152489500Y-29845000D02* -X149098000Y-29845000D01* -%TO.N,Net-(U301-P3)*% -X146685004Y-31115000D02* -X141605004Y-26035000D01* -X151970000Y-31115000D02* -X146685004Y-31115000D01* -%TO.N,GND*% -X110998000Y-80518000D02* -X110998000Y-80137000D01* -X110363000Y-81153000D02* -X110998000Y-80518000D01* -X110212500Y-80137000D02* -X110085500Y-80010000D01* -X110998000Y-80137000D02* -X110212500Y-80137000D01* -%TO.N,+3.3V*% -X135255000Y-50268500D02* -X135255000Y-52705000D01* -X136144000Y-49379500D02* -X135255000Y-50268500D01* -%TO.N,nCS*% -X146543732Y-53543145D02* -X146543732Y-53489338D01* -X145748930Y-54337947D02* -X146543732Y-53543145D01* -X144971111Y-54337947D02* -X145748930Y-54337947D01* -X144075058Y-55234000D02* -X144971111Y-54337947D01* -X134053279Y-55228616D02* -X134058664Y-55234000D01* -X131483638Y-55228616D02* -X134053279Y-55228616D01* -X129851000Y-53595978D02* -X131483638Y-55228616D01* -X125174000Y-39725802D02* -X129851000Y-44402802D01* -X129851000Y-44402802D02* -X129851000Y-53595978D01* -X125174000Y-38917500D02* -X125174000Y-39725802D01* -%TO.N,SPI_SCK*% -X145080425Y-53662947D02* -X145469335Y-53662947D01* -X134224349Y-54834000D02* -X143909372Y-54834000D01* -X134218966Y-54828617D02* -X134224349Y-54834000D01* -X125574000Y-39560116D02* -X130251000Y-44237116D01* -X125574000Y-37793500D02* -X125574000Y-39560116D01* -%TO.N,COPI*% -X130651000Y-40966002D02* -X125730000Y-36045002D01* -%TO.N,SPI_SCK*% -X130251000Y-44237116D02* -X130251000Y-53430292D01* -%TO.N,COPI*% -X130651000Y-53264605D02* -X130651000Y-40966002D01* -%TO.N,SPI_SCK*% -X130251000Y-53430292D02* -X131649326Y-54828616D01* -%TO.N,COPI*% -X148332581Y-51766774D02* -X146410912Y-51766774D01* -%TO.N,CIPO*% -X149135704Y-51091774D02* -X149212028Y-51168098D01* -X134555720Y-54034000D02* -X143578000Y-54034000D01* -%TO.N,COPI*% -X134390034Y-54434000D02* -X134384650Y-54428616D01* -%TO.N,CIPO*% -X128645697Y-35370012D02* -X131051000Y-37775315D01* -X124599011Y-35370012D02* -X128645697Y-35370012D01* -%TO.N,nCS*% -X123848500Y-37592000D02* -X125174000Y-38917500D01* -%TO.N,CIPO*% -X134550336Y-54028616D02* -X134555720Y-54034000D01* -X131051000Y-53098920D02* -X131980696Y-54028616D01* -X131051000Y-37775315D02* -X131051000Y-53098920D01* -X119634000Y-34940000D02* -X124168999Y-34940000D01* -%TO.N,SPI_SCK*% -X143909372Y-54834000D02* -X145080425Y-53662947D01* -X131649326Y-54828616D02* -X134218966Y-54828617D01* -%TO.N,COPI*% -X131815010Y-54428616D02* -X130651000Y-53264605D01* -%TO.N,CIPO*% -X124168999Y-34940000D02* -X124599011Y-35370012D01* -%TO.N,COPI*% -X143743686Y-54434000D02* -X134390034Y-54434000D01* -%TO.N,nCS*% -X134058664Y-55234000D02* -X144075058Y-55234000D01* -%TO.N,COPI*% -X146410912Y-51766774D02* -X143743686Y-54434000D01* -%TO.N,CIPO*% -X143578000Y-54034000D02* -X146520226Y-51091774D01* -%TO.N,COPI*% -X134384650Y-54428616D02* -X131815010Y-54428616D01* -%TO.N,CIPO*% -X146520226Y-51091774D02* -X149135704Y-51091774D01* -%TO.N,SPI_SCK*% -X123848500Y-36068000D02* -X125574000Y-37793500D01* -%TO.N,CIPO*% -X131980696Y-54028616D02* -X134550336Y-54028616D01* %TO.N,GND*% X89858284Y-37338000D02* X88842284Y-38354000D01* @@ -3108,6 +2894,9 @@ X157082206Y-42545000D01* D79* X90020000Y-50764000D02* X87770000Y-48514000D01* +D77* +X110363000Y-81153000D02* +X110998000Y-80518000D01* D78* X176530000Y-90755300D02* X174674700Y-88900000D01* @@ -3182,10 +2971,17 @@ X133279000Y-73071000D01* D81* X99400000Y-89322000D02* X100838000Y-87884000D01* +D77* +X110212500Y-80137000D02* +X110085500Y-80010000D01* D79* X167299996Y-33020000D02* X167299996Y-29550004D01* D77* +X110998000Y-80137000D02* +X110212500Y-80137000D01* +X110998000Y-80518000D02* +X110998000Y-80137000D01* X157480000Y-36830000D02* X159107500Y-36830000D01* D78* @@ -3226,6 +3022,8 @@ X82296000Y-38354000D02* X81280000Y-37338000D01* X108229500Y-77724000D02* X108229500Y-76479500D01* +X152489500Y-29845000D02* +X149098000Y-29845000D01* X111609000Y-95015000D02* X112524000Y-95930000D01* X101727000Y-79248000D02* @@ -3454,20 +3252,19 @@ X139700000Y-62738000D01* %TO.N,/3v3Controller*% X99708000Y-27189000D02* X99579000Y-27189000D01* +D79* +X87393156Y-29930000D02* +X89706000Y-29930000D01* +D77* X74168000Y-18346000D02* X67760000Y-18346000D01* -X86360000Y-30734000D02* -X90510000Y-30734000D01* X74497999Y-26745999D02* X74497999Y-18675999D01* X57790000Y-77301000D02* X57658000Y-77433000D01* -X82668000Y-33528000D02* -X83430000Y-32766000D01* -X83430000Y-32766000D02* -X85725000Y-32766000D01* X57658000Y-84435000D02* X58373000Y-85150000D01* +D79* X81129500Y-29464000D02* X81129500Y-31989500D01* D85* @@ -3478,8 +3275,10 @@ X74497999Y-18675999D02* X74168000Y-18346000D01* X77216000Y-29464000D02* X74497999Y-26745999D01* -X86106000Y-32615500D02* -X86106000Y-30988000D01* +D79* +X89706000Y-29930000D02* +X90510000Y-30734000D01* +D77* X65278000Y-20828000D02* X65278000Y-24384000D01* D85* @@ -3492,6 +3291,10 @@ X97689500Y-29056500D01* D77* X58373000Y-90170000D02* X58373000Y-87660000D01* +D79* +X86106000Y-32615500D02* +X86106000Y-31217156D01* +D77* X57790000Y-31872000D02* X57790000Y-77301000D01* X67760000Y-18346000D02* @@ -3501,7 +3304,7 @@ X90932000Y-31156000D01* D85* X94742000Y-29972000D02* X94742000Y-29056500D01* -D77* +D79* X81129500Y-29464000D02* X77216000Y-29464000D01* D85* @@ -3512,17 +3315,28 @@ X57658000Y-77433000D02* X57658000Y-84435000D01* X58373000Y-87660000D02* X58373000Y-85150000D01* +D79* X81129500Y-31989500D02* X82668000Y-33528000D01* +D77* X65278000Y-24384000D02* X57790000Y-31872000D01* -X86106000Y-30988000D02* -X86360000Y-30734000D01* +D79* +X85725000Y-32766000D02* +X83430000Y-32766000D01* +X83430000Y-32766000D02* +X82667999Y-33528001D01* +D77* +X82668000Y-33528000D02* +X82667999Y-33528001D01* D85* X101346000Y-27178000D02* X98298000Y-27178000D01* X90932000Y-31156000D02* X93558000Y-31156000D01* +D79* +X86106000Y-31217156D02* +X87393156Y-29930000D01* D78* %TO.N,Net-(D201-Pad1)*% X65174500Y-72390000D02* @@ -3804,14 +3618,24 @@ X160979702Y-73914298D02* X160655000Y-74239000D01* X143805310Y-63734095D02* X147247021Y-60292384D01* +X123848500Y-36068000D02* +X125574000Y-37793500D01* X154940000Y-85090000D02* X153924000Y-85090000D01* X160655000Y-76610752D02* X157480000Y-79785752D01* +X130251000Y-53430292D02* +X131649326Y-54828616D01* X154940000Y-84097500D02* X154940000Y-85090000D01* +X125574000Y-39560116D02* +X130251000Y-44237116D01* X144048703Y-83027297D02* X142113000Y-84963000D01* +X131649326Y-54828616D02* +X134218966Y-54828617D01* +X134224349Y-54834000D02* +X143909372Y-54834000D01* X162065000Y-68730500D02* X163985000Y-68730500D01* X163985000Y-68730500D02* @@ -3822,6 +3646,8 @@ X164338000Y-68922500D02* X164338000Y-70241500D01* X153924000Y-85090000D02* X153670000Y-84836000D01* +X143909372Y-54834000D02* +X145080425Y-53662947D01* X144048703Y-83027297D02* X142748000Y-81726594D01* X157480000Y-80944784D02* @@ -3836,14 +3662,20 @@ X157480000Y-79785752D02* X157480000Y-80944784D01* X164065500Y-68650000D02* X164338000Y-68922500D01* +X125574000Y-37793500D02* +X125574000Y-39560116D01* X142748000Y-76200000D02* X143805310Y-75142690D01* X154940000Y-85090000D02* X153580000Y-86450000D01* X162065000Y-68730500D02* X161393500Y-68730500D01* +X130251000Y-44237116D02* +X130251000Y-53430292D01* X142113000Y-84963000D02* X141605000Y-84963000D01* +X145080425Y-53662947D02* +X145469335Y-53662947D01* X160655000Y-74239000D02* X160655000Y-76610752D01* X154940000Y-83484784D02* @@ -3852,6 +3684,8 @@ X142748000Y-81726594D02* X142748000Y-76200000D01* X147247021Y-60292384D02* X147247021Y-60166215D01* +X134218966Y-54828617D02* +X134224349Y-54834000D01* X143805310Y-75142690D02* X143805310Y-63734095D01* X160979702Y-69144298D02* @@ -3988,20 +3822,40 @@ X71485000Y-66990000D02* X73622500Y-66990000D01* D77* %TO.N,SDA*% +X124764684Y-34970000D02* +X123938684Y-34144000D01* X136360500Y-50988000D02* X136144000Y-51204500D01* X138079000Y-50988000D02* X136360500Y-50988000D01* +X136144000Y-51204500D02* +X136144000Y-53024595D01* +X128811371Y-34970000D02* +X124764684Y-34970000D01* X117208000Y-32269000D02* X118208000Y-32269000D01* +X135539979Y-53628616D02* +X132146382Y-53628616D01* +X131450999Y-52933233D02* +X131451000Y-37609629D01* +X132146382Y-53628616D02* +X131450999Y-52933233D01* +X131451000Y-37609629D02* +X128811371Y-34970000D01* X118879000Y-32940000D02* X119634000Y-32940000D01* X138104000Y-51013000D02* X138079000Y-50988000D01* X118208000Y-32269000D02* X118879000Y-32940000D01* +X123938684Y-34144000D02* +X120838000Y-34144000D01* X138104000Y-52578000D02* X138104000Y-51013000D01* +X120838000Y-34144000D02* +X119634000Y-32940000D01* +X136144000Y-53024595D02* +X135539979Y-53628616D01* %TO.N,SCL*% X137825000Y-43876000D02* X136106500Y-43876000D01* @@ -4036,14 +3890,26 @@ X132651000Y-40268664D01* X132651000Y-40268664D02* X135890000Y-43507664D01* %TO.N,CIPO*% +X146520226Y-51091774D02* +X149135704Y-51091774D01* X164992500Y-58908751D02* X164992500Y-58045500D01* +X124168999Y-34940000D02* +X124599011Y-35370012D01* +X128645697Y-35370012D02* +X131051000Y-37775315D01* +X131980696Y-54028616D02* +X134550336Y-54028616D01* X164454500Y-60986500D02* X163930000Y-60462000D01* X161951500Y-60462000D02* X161811000Y-60602500D01* X163298751Y-60602500D02* X164992500Y-58908751D01* +X134555720Y-54034000D02* +X143578000Y-54034000D01* +X119634000Y-34940000D02* +X124168999Y-34940000D01* X161340505Y-57965000D02* X161192505Y-58113000D01* X163930000Y-60462000D02* @@ -4052,8 +3918,20 @@ X161192505Y-58113000D02* X158964000Y-58113000D01* X164912000Y-57965000D02* X161340505Y-57965000D01* +X131051000Y-53098920D02* +X131980696Y-54028616D01* +X131051000Y-37775315D02* +X131051000Y-53098920D01* +X134550336Y-54028616D02* +X134555720Y-54034000D01* X164454500Y-62230000D02* X164454500Y-60986500D01* +X143578000Y-54034000D02* +X146520226Y-51091774D01* +X149135704Y-51091774D02* +X149212028Y-51168098D01* +X124599011Y-35370012D02* +X128645697Y-35370012D01* X153560743Y-57290000D02* X152133701Y-55862958D01* X158141000Y-57290000D02* @@ -4069,18 +3947,34 @@ X163298751Y-60602500D01* X158964000Y-58113000D02* X158141000Y-57290000D01* %TO.N,COPI*% +X130651000Y-53264605D02* +X130651000Y-40966002D01* +X134384650Y-54428616D02* +X131815010Y-54428616D01* X148722020Y-58538397D02* X149199878Y-58060539D01* X152999500Y-60640000D02* X152999500Y-61839000D01* +X134390034Y-54434000D02* +X134384650Y-54428616D01* +X148332581Y-51766774D02* +X146410912Y-51766774D01* X149199878Y-58060539D02* X149199878Y-57670515D01* +X146410912Y-51766774D02* +X143743686Y-54434000D01* +X143743686Y-54434000D02* +X134390034Y-54434000D01* X152999500Y-61839000D02* X153356500Y-62196000D01* +X130651000Y-40966002D02* +X125730000Y-36045002D01* X152999500Y-60640000D02* X152920500Y-60719000D01* X150507501Y-60719000D02* X148722020Y-58933518D01* +X131815010Y-54428616D02* +X130651000Y-53264605D01* X152920500Y-60719000D02* X150507501Y-60719000D01* X148722020Y-58933518D02* @@ -4090,14 +3984,24 @@ X126238000Y-33020030D01* X123848500Y-28956000D02* X126238000Y-31345500D01* %TO.N,nCS*% +X145748930Y-54337947D02* +X146543732Y-53543145D01* X150114000Y-65278000D02* X150114000Y-60891184D01* X150114000Y-60891184D02* X148047020Y-58824204D01* +X134058664Y-55234000D02* +X144075058Y-55234000D01* +X129851000Y-53595978D02* +X131483638Y-55228616D01* +X144075058Y-55234000D02* +X144971111Y-54337947D01* X150381000Y-68730500D02* X152443500Y-68730500D01* X152485000Y-68772000D02* X152485000Y-70326500D01* +X144971111Y-54337947D02* +X145748930Y-54337947D01* X152516500Y-70358000D02* X147574000Y-70358000D01* X152485000Y-70326500D02* @@ -4106,10 +4010,24 @@ X147320000Y-70104000D02* X147320000Y-68072000D01* X152443500Y-68730500D02* X152485000Y-68772000D01* +X123848500Y-37592000D02* +X125174000Y-38917500D01* X147320000Y-68072000D02* X150114000Y-65278000D01* +X125174000Y-38917500D02* +X125174000Y-39725802D01* +X125174000Y-39725802D02* +X129851000Y-44402802D01* +X131483638Y-55228616D02* +X134053279Y-55228616D01* +X134053279Y-55228616D02* +X134058664Y-55234000D01* +X146543732Y-53543145D02* +X146543732Y-53489338D01* X147574000Y-70358000D02* X147320000Y-70104000D01* +X129851000Y-44402802D02* +X129851000Y-53595978D01* D87* %TO.N,Net-(Q203-B)*% X71522500Y-54252500D02* @@ -4127,8 +4045,22 @@ X117208000Y-30999000D02* X118632339Y-30999000D01* X122555000Y-63881000D02* X123531000Y-62905000D01* +X124467846Y-33744000D02* +X125293846Y-34570000D01* +X128977068Y-34570012D02* +X131851000Y-37443944D01* +X131851000Y-37443944D02* +X131851000Y-52337062D01* +X128093372Y-34570012D02* +X128977068Y-34570012D01* X118364000Y-67310000D02* X122555000Y-67310000D01* +X131851000Y-52337062D02* +X132125999Y-52612061D01* +X121438000Y-33744000D02* +X124467846Y-33744000D01* +X125293846Y-34570000D02* +X128093359Y-34570000D01* X122555000Y-67310000D02* X122555000Y-63881000D01* X114046000Y-81233000D02* @@ -4147,6 +4079,10 @@ X118632339Y-30999000D02* X119573339Y-31940000D01* X113176000Y-72498000D02* X118364000Y-67310000D01* +X128093359Y-34570000D02* +X128093372Y-34570012D01* +X119634000Y-31940000D02* +X121438000Y-33744000D01* %TO.N,Net-(Q205-B)*% X70424000Y-28890000D02* X71422500Y-28890000D01* @@ -4562,12 +4498,16 @@ X127961000Y-53198686D01* %TO.N,Net-(U301-P3)*% X130810000Y-31115000D02* X131699000Y-30226000D01* +X146685004Y-31115000D02* +X141605004Y-26035000D01* X130810000Y-31115000D02* X130810000Y-28852500D01* X130810000Y-31392500D02* X130810000Y-31115000D01* X129436500Y-32766000D02* X130810000Y-31392500D01* +X151970000Y-31115000D02* +X146685004Y-31115000D01* X131699000Y-30226000D02* X132588000Y-30226000D01* %TO.N,Net-(U501-TNOW)*% @@ -4593,6 +4533,10 @@ X114046000Y-83058000D01* X104255500Y-90105000D02* X104255500Y-89798959D01* %TO.N,Net-(U501-RXD)*% +X132251000Y-50716986D02* +X132588000Y-51053986D01* +X132251000Y-37278259D02* +X132251000Y-50716986D01* X122936000Y-68834000D02* X114796000Y-76974000D01* X127000000Y-68326000D02* @@ -4603,16 +4547,24 @@ X126492000Y-68834000D02* X122936000Y-68834000D01* X112668000Y-88246000D02* X107078459Y-88246000D01* +X123848500Y-32558969D02* +X125459543Y-34170012D01* +X125459543Y-34170012D02* +X129142754Y-34170012D01* X114796000Y-86118000D02* X112668000Y-88246000D01* X107078459Y-88246000D02* X105525500Y-89798959D01* X114796000Y-76974000D02* X114796000Y-86118000D01* +X129142754Y-34170012D02* +X132251000Y-37278259D01* X127000000Y-63458343D02* X127000000Y-68326000D01* X127807343Y-62651000D02* X127000000Y-63458343D01* +X123848500Y-31242000D02* +X123848500Y-32558969D01* %TO.N,Net-(U501-~{CTS})*% X114046000Y-78740000D02* X114046000Y-74930000D01* @@ -4622,6 +4574,10 @@ X113030000Y-80010000D02* X113030000Y-79756000D01* X114046000Y-74930000D02* X115951000Y-73025000D01* +X111875500Y-85155000D02* +X111875500Y-81164500D01* +X111875500Y-81164500D02* +X113030000Y-80010000D01* X113030000Y-79756000D02* X112014000Y-78740000D01* D86* @@ -4882,10 +4838,6 @@ X150948701Y-56467141D01* D77* X97258500Y-81089500D02* X99568000Y-78780000D01* -D79* -X83208500Y-29718000D02* -X82954500Y-29464000D01* -D77* X137517500Y-63554781D02* X136601359Y-62638640D01* D79* @@ -4901,6 +4853,12 @@ D87* X153707000Y-58032500D02* X158244276Y-58032500D01* D77* +X136144000Y-49379500D02* +X135255000Y-50268500D01* +D79* +X83138631Y-29648131D02* +X85031840Y-29648131D01* +D77* X137517500Y-67310000D02* X137517500Y-63554781D01* D79* @@ -4949,8 +4907,6 @@ X138662500Y-48504500D01* X135890000Y-57658000D02* X135890000Y-57658029D01* D79* -X85090000Y-29718000D02* -X83208500Y-29718000D01* X145218709Y-55377283D02* X142937992Y-57658000D01* X162781578Y-39370000D02* @@ -5027,6 +4983,9 @@ X137825000Y-41976000D01* D87* X159716176Y-58777500D02* X161811000Y-58777500D01* +D77* +X135255000Y-50268500D02* +X135255000Y-52705000D01* D79* X142937992Y-57658000D02* X135890000Y-57658000D01* @@ -5034,6 +4993,8 @@ D77* X162065000Y-66905500D02* X161821500Y-66905500D01* D79* +X82954500Y-29464000D02* +X83138631Y-29648131D01* X150948701Y-55485453D02* X150857000Y-55393752D01* D77* @@ -5834,16 +5795,26 @@ X93726000Y-86463500D02* X97893500Y-82296000D01* X100584000Y-81661000D02* X102616000Y-81661000D01* +X106726022Y-81661000D02* +X109154405Y-81661000D01* X105525500Y-85155000D02* X105525500Y-83958500D01* X105525500Y-83958500D02* X106416412Y-83067588D01* +X106416412Y-81970610D02* +X106726022Y-81661000D01* X106416412Y-83067588D02* X106416412Y-81970610D01* +X110774978Y-81955000D02* +X110810813Y-81990835D01* X116840000Y-79859500D02* X117197500Y-79502000D01* +X109154405Y-81661000D02* +X109448405Y-81955000D01* X97893500Y-82296000D02* X99949000Y-82296000D01* +X109448405Y-81955000D02* +X110774978Y-81955000D01* X116840000Y-80772000D02* X116840000Y-79859500D01* X99949000Y-82296000D02* @@ -6181,8 +6152,14 @@ X104748500Y-92837000D01* %TO.N,Net-(Q207-E)*% X104902000Y-79652500D02* X105320801Y-80071301D01* +X117059784Y-82677000D02* +X115697000Y-82677000D01* X107964495Y-82535505D02* X109103964Y-82535505D01* +X119785000Y-79951784D02* +X117059784Y-82677000D01* +X118285500Y-75758000D02* +X119785000Y-77257500D01* X106795500Y-85155000D02* X106795500Y-83704500D01* X103124000Y-79652500D02* @@ -6191,6 +6168,8 @@ X105320801Y-80071301D02* X105320801Y-81073000D01* X106795500Y-83704500D02* X107964495Y-82535505D01* +X119785000Y-77257500D02* +X119785000Y-79951784D01* %TO.N,Net-(U501-RST#)*% X110605500Y-90105000D02* X110605500Y-89798959D01* @@ -23416,7 +23395,7 @@ X178591465Y-32529289D01* X178573497Y-32757595D01* X178572835Y-32766000D01* X178591465Y-33002711D01* -X178638384Y-33198145D01* +X178643644Y-33220054D01* X178645060Y-33225949D01* X178641513Y-33296857D01* X178611636Y-33344458D01* @@ -23424,7 +23403,7 @@ X177317594Y-34638500D01* X177255282Y-34672526D01* X177184467Y-34667461D01* X177139404Y-34638500D01* -X173067581Y-30566677D01* +X173064492Y-30563588D01* X172472904Y-29972000D01* X187717337Y-29972000D01* X187735960Y-30208632D01* @@ -28592,7 +28571,7 @@ G36* X144412012Y-29738366D02* G01* X144418593Y-29744493D01* -X145298785Y-30624686D01* +X145298618Y-30624519D01* X146177759Y-31503660D01* X146187724Y-31516097D01* X146187951Y-31515910D01* @@ -28607,7 +28586,7 @@ X146265232Y-31591133D01* X146265234Y-31591135D01* X146270786Y-31595442D01* X146275273Y-31599273D01* -X146297345Y-31620000D01* +X146294948Y-31617749D01* X146309681Y-31631585D01* X146309683Y-31631586D01* X146327432Y-31641343D01* @@ -29003,7 +28982,7 @@ X144203500Y-30898489D01* X144203499Y-30898488D01* X144200562Y-30861171D01* X144200562Y-30861170D01* -X144198591Y-30854385D01* +X144200562Y-30861169D01* X144154145Y-30701399D01* X144154143Y-30701397D01* X144154143Y-30701394D01* @@ -29155,7 +29134,7 @@ X132745704Y-31813230D01* X132756907Y-31833094D01* X132760774Y-31841688D01* X132760776Y-31841691D01* -X132767198Y-31849888D01* +X132760777Y-31841692D01* X132788111Y-31876582D01* X132798045Y-31889261D01* X132800292Y-31892314D01* @@ -29690,7 +29669,7 @@ X159639416Y-29255824D01* X159436963Y-29131760D01* X159417271Y-29123603D01* X159217592Y-29040894D01* -X159059651Y-29002976D01* +X159041939Y-28998724D01* X158986711Y-28985465D01* X158750000Y-28966835D01* X158513289Y-28985465D01* @@ -29703,11 +29682,11 @@ X157525826Y-29590580D01* X157525825Y-29590582D01* X157401759Y-29793038D01* X157310894Y-30012407D01* -X157269360Y-30185412D01* +X157268739Y-30187999D01* X157255465Y-30243289D01* X157236835Y-30480000D01* X157255465Y-30716711D01* -X157258787Y-30730548D01* +X157259391Y-30733065D01* X157310894Y-30947592D01* X157396599Y-31154502D01* X157401760Y-31166963D01* @@ -29725,7 +29704,7 @@ X158986711Y-31974535D01* X159217594Y-31919105D01* X159436963Y-31828240D01* X159639416Y-31704176D01* -X159673688Y-31674905D01* +X159673270Y-31675262D01* X159698529Y-31653689D01* X159763318Y-31624658D01* X159780359Y-31623500D01* @@ -29970,7 +29949,7 @@ X156843500Y-33079775D01* X156845051Y-33060063D01* X156845614Y-33056507D01* X156848220Y-33040057D01* -X156845271Y-33008861D01* +X156844063Y-32996076D01* X156843780Y-32993080D01* X156843500Y-32987148D01* X156843500Y-26349593D01* @@ -30316,7 +30295,7 @@ X139209779Y-32155684D01* X139222617Y-32170714D01* X139230496Y-32181558D01* X139234528Y-32187107D01* -X139257980Y-32206508D01* +X139242435Y-32193648D01* X139270886Y-32217185D01* X139275267Y-32221171D01* X140646753Y-33592658D01* @@ -30371,7 +30350,7 @@ X136779416Y-29255824D01* X136576963Y-29131760D01* X136557271Y-29123603D01* X136357592Y-29040894D01* -X136199651Y-29002976D01* +X136181939Y-28998724D01* X136126711Y-28985465D01* X135890000Y-28966835D01* X135653289Y-28985465D01* @@ -30384,11 +30363,11 @@ X134665826Y-29590580D01* X134665825Y-29590582D01* X134541759Y-29793038D01* X134450894Y-30012407D01* -X134409360Y-30185412D01* +X134408739Y-30187999D01* X134395465Y-30243289D01* X134376835Y-30480000D01* X134395465Y-30716711D01* -X134398787Y-30730548D01* +X134399391Y-30733065D01* X134450894Y-30947592D01* X134536599Y-31154502D01* X134541760Y-31166963D01* @@ -30431,7 +30410,7 @@ X134018513Y-29402726D01* X134015166Y-29393902D01* X134009042Y-29371930D01* X134007344Y-29362663D01* -X134002276Y-29351403D01* +X134003200Y-29353455D01* X133982536Y-29307541D01* X133981084Y-29304034D01* X133975191Y-29288496D01* @@ -30598,16 +30577,14 @@ X165292000Y-32419483D01* X165292000Y-32766000D01* X164908499Y-32766000D01* X164908499Y-32419456D01* -X164904556Y-32380862D01* X164897887Y-32315574D01* -X164893698Y-32302931D01* X164842115Y-32147262D01* X164749030Y-31996348D01* X164749029Y-31996347D01* X164749024Y-31996341D01* X164623658Y-31870975D01* X164623652Y-31870970D01* -X164589473Y-31849888D01* +X164576183Y-31841691D01* X164472738Y-31777885D01* X164472737Y-31777884D01* X164472736Y-31777884D01* @@ -30753,14 +30730,16 @@ G37* %TD.AperFunction*% %TA.AperFunction,Conductor*% G36* -X89565622Y-31387502D02* +X89248278Y-31093502D02* G01* -X89612115Y-31441158D01* -X89623501Y-31493500D01* -X89623501Y-32456544D01* +X89269252Y-31110405D01* +X89586595Y-31427748D01* +X89620621Y-31490060D01* +X89623500Y-31516843D01* +X89623500Y-32456544D01* X89634113Y-32560427D01* X89634113Y-32560431D01* -X89677317Y-32690811D01* +X89682153Y-32705405D01* X89689885Y-32728738D01* X89712000Y-32764592D01* X89730737Y-32833070D01* @@ -30807,26 +30786,16 @@ X88146273Y-31630430D01* X88044184Y-31620000D01* X88011000Y-31620000D01* X87503000Y-31620000D01* -X87469815Y-31620000D01* -X87367726Y-31630430D01* -X87367723Y-31630431D01* -X87202292Y-31685248D01* -X87053966Y-31776737D01* -X87020947Y-31809756D01* -X86958635Y-31843780D01* -X86887819Y-31838714D01* -X86842758Y-31809754D01* -X86809346Y-31776342D01* -X86809344Y-31776340D01* -X86799350Y-31770176D01* -X86751873Y-31717389D01* -X86739500Y-31662937D01* -X86739500Y-31493500D01* -X86759502Y-31425379D01* -X86813158Y-31378886D01* -X86865500Y-31367500D01* -X89497501Y-31367500D01* -X89565622Y-31387502D01* +X87503000Y-31619999D01* +X87500750Y-31617749D01* +X87466724Y-31555437D01* +X87471789Y-31484622D01* +X87500747Y-31439561D01* +X87829905Y-31110404D01* +X87892218Y-31076379D01* +X87919001Y-31073500D01* +X89180157Y-31073500D01* +X89248278Y-31093502D01* G37* %TD.AperFunction*% %TA.AperFunction,Conductor*% @@ -30942,7 +30911,7 @@ X71017186Y-31694649D01* X71019961Y-31698227D01* X71034279Y-31712545D01* X71047117Y-31727575D01* -X71049648Y-31731058D01* +X71051352Y-31733403D01* X71059028Y-31743968D01* X71081052Y-31762188D01* X71095386Y-31774046D01* @@ -31514,7 +31483,7 @@ X127124011Y-28254800D01* X127139303Y-28395405D01* X127139297Y-28395405D01* X127141499Y-28415646D01* -X127141499Y-28648976D01* +X127141499Y-28649372D01* X127141500Y-28775500D01* X127121498Y-28843620D01* X127067843Y-28890113D01* @@ -31542,7 +31511,7 @@ X124847378Y-29030686D01* X124844499Y-29003903D01* X124844499Y-28668787D01* X124834062Y-28566619D01* -X124810929Y-28496807D01* +X124816577Y-28513853D01* X124779209Y-28401080D01* X124687658Y-28252654D01* X124687657Y-28252653D01* @@ -31839,192 +31808,19 @@ X91685198Y-29174274D01* X91636427Y-29158113D01* X91636420Y-29158112D01* X91532553Y-29147500D01* -X90331455Y-29147500D01* -X90227574Y-29158112D01* -X90059261Y-29213885D01* -X89908347Y-29306970D01* -X89908341Y-29306975D01* -X89782975Y-29432341D01* -X89782970Y-29432347D01* -X89689885Y-29583262D01* -X89634113Y-29751572D01* -X89634112Y-29751579D01* -X89623500Y-29855446D01* -X89623500Y-29974500D01* -X89603498Y-30042621D01* -X89549842Y-30089114D01* -X89497500Y-30100500D01* -X86443853Y-30100500D01* -X86428011Y-30098750D01* -X86427984Y-30099044D01* -X86420091Y-30098298D01* -X86351024Y-30100469D01* -X86349045Y-30100500D01* -X86336905Y-30100500D01* -X86268784Y-30080498D01* -X86222291Y-30026842D01* -X86212187Y-29956568D01* -X86215715Y-29940018D01* -X86218140Y-29931495D01* -X86218845Y-29929018D01* -X86223476Y-29879033D01* -X86224275Y-29873305D01* -X86233500Y-29823961D01* -X86233500Y-29773774D01* -X86233769Y-29767959D01* -X86235378Y-29750605D01* -X86238399Y-29718000D01* -X86237348Y-29706662D01* -X86235006Y-29681388D01* -X86233768Y-29668030D01* -X86233500Y-29662222D01* -X86233500Y-29612039D01* -X86230311Y-29594983D01* -X86224276Y-29562699D01* -X86223475Y-29556962D01* -X86218845Y-29506982D01* -X86205110Y-29458711D01* -X86203780Y-29453054D01* -X86203512Y-29451620D01* -X86194560Y-29403726D01* -X86176429Y-29356925D01* -X86174581Y-29351413D01* -X86160850Y-29303150D01* -X86138481Y-29258229D01* -X86136131Y-29252905D01* -X86132877Y-29244505D01* -X86118005Y-29206115D01* -X86118001Y-29206109D01* -X86118000Y-29206106D01* -X86091589Y-29163452D01* -X86088755Y-29158364D01* -X86085692Y-29152212D01* -X86066389Y-29113446D01* -X86036144Y-29073396D01* -X86032855Y-29068593D01* -X86006447Y-29025943D01* -X86006442Y-29025935D01* -X85997839Y-29016498D01* -X85972631Y-28988846D01* -X85968923Y-28984380D01* -X85938676Y-28944328D01* -X85938675Y-28944327D01* -X85903839Y-28912569D01* -X85901590Y-28910519D01* -X85897476Y-28906405D01* -X85893004Y-28901500D01* -X85863671Y-28869323D01* -X85863667Y-28869319D01* -X85823629Y-28839084D01* -X85819151Y-28835366D01* -X85782069Y-28801562D01* -X85782060Y-28801554D01* -X85739402Y-28775141D01* -X85734599Y-28771851D01* -X85694555Y-28741612D01* -X85694554Y-28741611D01* -X85649629Y-28719241D01* -X85644542Y-28716407D01* -X85615209Y-28698245D01* -X85601885Y-28689995D01* -X85555097Y-28671869D01* -X85549773Y-28669519D01* -X85548303Y-28668787D01* -X85504850Y-28647150D01* -X85497809Y-28645146D01* -X85456590Y-28633418D01* -X85451070Y-28631568D01* -X85404273Y-28613439D01* -X85354936Y-28604216D01* -X85349279Y-28602886D01* -X85318689Y-28594182D01* -X85301019Y-28589155D01* -X85292128Y-28588331D01* -X85251051Y-28584524D01* -X85245295Y-28583722D01* -X85228048Y-28580498D01* -X85195961Y-28574500D01* -X85195960Y-28574500D01* -X83718754Y-28574500D01* -X83650633Y-28554498D01* -X83638259Y-28545436D01* -X83603502Y-28516574D01* -X83603497Y-28516571D01* -X83418365Y-28413452D01* -X83418360Y-28413450D01* -X83217428Y-28346104D01* -X83217431Y-28346104D01* -X83007539Y-28316827D01* -X83007537Y-28316826D01* -X82795839Y-28326614D01* -X82589555Y-28375130D01* -X82589547Y-28375133D01* -X82395679Y-28460734D01* -X82220847Y-28580499D01* -X82093770Y-28707576D01* -X82031458Y-28741601D01* -X81960642Y-28736535D01* -X81915580Y-28707575D01* -X81845352Y-28637347D01* -X81845346Y-28637342D01* -X81815531Y-28618952D01* -X81696920Y-28545791D01* -X81531381Y-28490938D01* -X81531379Y-28490937D01* -X81531377Y-28490937D01* -X81429221Y-28480500D01* -X80829787Y-28480500D01* -X80727619Y-28490937D01* -X80562079Y-28545791D01* -X80413653Y-28637342D01* -X80413647Y-28637347D01* -X80290347Y-28760647D01* -X80290342Y-28760653D01* -X80288531Y-28763590D01* -X80284178Y-28770646D01* -X80231395Y-28818124D01* -X80176938Y-28830500D01* -X78327761Y-28830500D01* -X78259640Y-28810498D01* -X78213147Y-28756842D01* -X78204427Y-28724004D01* -X78203310Y-28724182D01* -X78202536Y-28719296D01* -X78202535Y-28719293D01* -X78202535Y-28719289D01* -X78147105Y-28488406D01* -X78056240Y-28269037D01* -X77932176Y-28066584D01* -X77928816Y-28062650D01* -X77777969Y-27886030D01* -X77597419Y-27731826D01* -X77597417Y-27731825D01* -X77597416Y-27731824D01* -X77394963Y-27607760D01* -X77363965Y-27594920D01* -X77175592Y-27516894D01* -X76979870Y-27469906D01* -X76944711Y-27461465D01* -X76708000Y-27442835D01* -X76471289Y-27461465D01* -X76248049Y-27515059D01* -X76177142Y-27511513D01* -X76129541Y-27481636D01* -X75657905Y-27010000D01* -X89624000Y-27010000D01* -X89624000Y-28056516D01* -X89634605Y-28160318D01* -X89634606Y-28160321D01* -X89690342Y-28328525D01* -X89783365Y-28479339D01* -X89783370Y-28479345D01* -X89908654Y-28604629D01* -X89908660Y-28604634D01* -X90059474Y-28697657D01* -X90227678Y-28753393D01* -X90227681Y-28753394D01* -X90331483Y-28763999D01* -X90331483Y-28764000D01* +X91532545Y-29147500D01* +X90595634Y-29147500D01* +X90527513Y-29127498D01* +X90502520Y-29106386D01* +X90479672Y-29081324D01* +X90417337Y-29034249D01* +X90415085Y-29032466D01* +X90360252Y-28986934D01* +X90320625Y-28928029D01* +X90319134Y-28857048D01* +X90356255Y-28796529D01* +X90420202Y-28765687D01* +X90440749Y-28764000D01* X90678000Y-28764000D01* X90678000Y-27010000D01* X91186000Y-27010000D01* @@ -32082,7 +31878,193 @@ X92240000Y-27010000D01* X91186000Y-27010000D01* X90678000Y-27010000D01* X89624000Y-27010000D01* -X75657905Y-27010000D01* +X89624000Y-28056516D01* +X89634605Y-28160318D01* +X89634606Y-28160321D01* +X89690342Y-28328525D01* +X89783365Y-28479339D01* +X89783370Y-28479345D01* +X89874168Y-28570143D01* +X89908194Y-28632455D01* +X89903129Y-28703270D01* +X89860582Y-28760106D01* +X89794062Y-28784917D01* +X89767670Y-28784030D01* +X89759046Y-28782827D01* +X89759036Y-28782827D01* +X89681033Y-28786433D01* +X89678123Y-28786500D01* +X87421032Y-28786500D01* +X87418122Y-28786433D01* +X87340117Y-28782826D01* +X87340113Y-28782827D01* +X87262773Y-28793615D01* +X87259883Y-28793950D01* +X87182138Y-28801154D01* +X87160770Y-28807234D01* +X87152225Y-28809036D01* +X87130235Y-28812103D01* +X87130227Y-28812105D01* +X87056185Y-28836920D01* +X87053407Y-28837781D01* +X86978302Y-28859151D01* +X86958424Y-28869049D01* +X86950359Y-28872389D01* +X86929300Y-28879449D01* +X86929295Y-28879451D01* +X86929294Y-28879452D01* +X86921637Y-28883717D01* +X86861096Y-28917436D01* +X86858524Y-28918792D01* +X86788601Y-28953611D01* +X86770874Y-28966998D01* +X86763558Y-28971765D01* +X86744156Y-28982571D01* +X86744155Y-28982573D01* +X86684065Y-29032469D01* +X86681785Y-29034274D01* +X86619485Y-29081323D01* +X86619482Y-29081325D01* +X86566877Y-29139029D01* +X86564868Y-29141133D01* +X86338959Y-29367041D01* +X86276647Y-29401067D01* +X86205832Y-29396002D01* +X86148996Y-29353455D01* +X86132372Y-29323461D01* +X86129674Y-29316498D01* +X86118266Y-29287049D01* +X86116421Y-29281544D01* +X86102690Y-29233281D01* +X86080321Y-29188360D01* +X86077971Y-29183036D01* +X86075165Y-29175793D01* +X86059845Y-29136246D01* +X86059841Y-29136240D01* +X86059840Y-29136237D01* +X86033429Y-29093583D01* +X86030595Y-29088495D01* +X86021805Y-29070842D01* +X86008229Y-29043577D01* +X85977984Y-29003527D01* +X85974695Y-28998724D01* +X85948287Y-28956074D01* +X85948282Y-28956066D01* +X85943532Y-28950856D01* +X85914471Y-28918977D01* +X85910763Y-28914511D01* +X85884286Y-28879451D01* +X85880517Y-28874460D01* +X85843430Y-28840650D01* +X85839316Y-28836536D01* +X85805512Y-28799455D01* +X85805507Y-28799450D01* +X85765469Y-28769215D01* +X85760991Y-28765497D01* +X85731812Y-28738897D01* +X85723905Y-28731689D01* +X85723903Y-28731687D01* +X85723900Y-28731685D01* +X85681242Y-28705272D01* +X85676439Y-28701982D01* +X85645581Y-28678680D01* +X85636394Y-28671742D01* +X85591469Y-28649372D01* +X85586382Y-28646538D01* +X85561248Y-28630976D01* +X85543725Y-28620126D01* +X85496937Y-28602000D01* +X85491613Y-28599650D01* +X85446690Y-28577281D01* +X85439649Y-28575277D01* +X85398430Y-28563549D01* +X85392910Y-28561699D01* +X85346113Y-28543570D01* +X85296776Y-28534347D01* +X85291119Y-28533017D01* +X85249378Y-28521141D01* +X85242859Y-28519286D01* +X85233968Y-28518462D01* +X85192891Y-28514655D01* +X85187135Y-28513853D01* +X85137801Y-28504631D01* +X85137800Y-28504631D01* +X83614784Y-28504631D01* +X83553471Y-28488707D01* +X83551505Y-28487612D01* +X83418362Y-28413451D01* +X83418361Y-28413450D01* +X83418360Y-28413450D01* +X83217428Y-28346104D01* +X83217431Y-28346104D01* +X83007539Y-28316827D01* +X83007537Y-28316826D01* +X82795839Y-28326614D01* +X82589555Y-28375130D01* +X82589547Y-28375133D01* +X82395679Y-28460734D01* +X82220847Y-28580499D01* +X82132739Y-28668607D01* +X82070427Y-28702632D01* +X81999611Y-28697566D01* +X81958758Y-28672626D01* +X81941090Y-28656519D01* +X81936977Y-28652406D01* +X81903172Y-28615324D01* +X81903167Y-28615319D01* +X81863129Y-28585084D01* +X81858651Y-28581366D01* +X81827291Y-28552778D01* +X81821565Y-28547558D01* +X81821563Y-28547556D01* +X81821560Y-28547554D01* +X81778902Y-28521141D01* +X81774099Y-28517851D01* +X81735505Y-28488707D01* +X81734054Y-28487611D01* +X81689129Y-28465241D01* +X81684042Y-28462407D01* +X81641388Y-28435997D01* +X81641389Y-28435997D01* +X81641385Y-28435995D01* +X81594597Y-28417869D01* +X81589273Y-28415519D01* +X81585118Y-28413450D01* +X81544350Y-28393150D01* +X81537309Y-28391146D01* +X81496090Y-28379418D01* +X81490570Y-28377568D01* +X81443773Y-28359439D01* +X81394436Y-28350216D01* +X81388779Y-28348886D01* +X81358189Y-28340182D01* +X81340519Y-28335155D01* +X81331628Y-28334331D01* +X81290551Y-28330524D01* +X81284795Y-28329722D01* +X81282393Y-28329273D01* +X81235461Y-28320500D01* +X81235460Y-28320500D01* +X78158340Y-28320500D01* +X78090219Y-28300498D01* +X78050907Y-28260335D01* +X78048473Y-28256363D01* +X77932176Y-28066584D01* +X77928816Y-28062650D01* +X77777969Y-27886030D01* +X77597419Y-27731826D01* +X77597417Y-27731825D01* +X77597416Y-27731824D01* +X77394963Y-27607760D01* +X77363965Y-27594920D01* +X77175592Y-27516894D01* +X76979870Y-27469906D01* +X76944711Y-27461465D01* +X76708000Y-27442835D01* +X76471289Y-27461465D01* +X76248049Y-27515059D01* +X76177142Y-27511513D01* +X76129541Y-27481636D01* X75168404Y-26520499D01* X75134378Y-26458187D01* X75131499Y-26431404D01* diff --git a/PWA_REV2/Gerbers/PWA_REV2-F_Mask.gbr b/PWA_REV2/Gerbers/PWA_REV2-F_Mask.gbr index e21d9a4..f39991e 100644 --- a/PWA_REV2/Gerbers/PWA_REV2-F_Mask.gbr +++ b/PWA_REV2/Gerbers/PWA_REV2-F_Mask.gbr @@ -1,12 +1,12 @@ %TF.GenerationSoftware,KiCad,Pcbnew,7.0.5*% -%TF.CreationDate,2025-09-17T13:25:07-04:00*% +%TF.CreationDate,2025-09-19T13:44:07-04:00*% %TF.ProjectId,PWA_REV2,5057415f-5245-4563-922e-6b696361645f,2.0*% %TF.SameCoordinates,Original*% %TF.FileFunction,Soldermask,Top*% %TF.FilePolarity,Negative*% %FSLAX46Y46*% G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* -G04 Created by KiCad (PCBNEW 7.0.5) date 2025-09-17 13:25:07* +G04 Created by KiCad (PCBNEW 7.0.5) date 2025-09-19 13:44:07* %MOMM*% %LPD*% G01* diff --git a/PWA_REV2/Gerbers/PWA_REV2-F_Paste.gbr b/PWA_REV2/Gerbers/PWA_REV2-F_Paste.gbr index eadfc9e..887d15a 100644 --- a/PWA_REV2/Gerbers/PWA_REV2-F_Paste.gbr +++ b/PWA_REV2/Gerbers/PWA_REV2-F_Paste.gbr @@ -1,12 +1,12 @@ %TF.GenerationSoftware,KiCad,Pcbnew,7.0.5*% -%TF.CreationDate,2025-09-17T13:25:06-04:00*% +%TF.CreationDate,2025-09-19T13:44:07-04:00*% %TF.ProjectId,PWA_REV2,5057415f-5245-4563-922e-6b696361645f,2.0*% %TF.SameCoordinates,Original*% %TF.FileFunction,Paste,Top*% %TF.FilePolarity,Positive*% %FSLAX46Y46*% G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* -G04 Created by KiCad (PCBNEW 7.0.5) date 2025-09-17 13:25:06* +G04 Created by KiCad (PCBNEW 7.0.5) date 2025-09-19 13:44:07* %MOMM*% %LPD*% G01* diff --git a/PWA_REV2/Gerbers/PWA_REV2-F_Silkscreen.gbr b/PWA_REV2/Gerbers/PWA_REV2-F_Silkscreen.gbr index 0ae4acd..989a160 100644 --- a/PWA_REV2/Gerbers/PWA_REV2-F_Silkscreen.gbr +++ b/PWA_REV2/Gerbers/PWA_REV2-F_Silkscreen.gbr @@ -1,12 +1,12 @@ %TF.GenerationSoftware,KiCad,Pcbnew,7.0.5*% -%TF.CreationDate,2025-09-17T13:25:06-04:00*% +%TF.CreationDate,2025-09-19T13:44:07-04:00*% %TF.ProjectId,PWA_REV2,5057415f-5245-4563-922e-6b696361645f,2.0*% %TF.SameCoordinates,Original*% %TF.FileFunction,Legend,Top*% %TF.FilePolarity,Positive*% %FSLAX46Y46*% G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* -G04 Created by KiCad (PCBNEW 7.0.5) date 2025-09-17 13:25:06* +G04 Created by KiCad (PCBNEW 7.0.5) date 2025-09-19 13:44:07* %MOMM*% %LPD*% G01* diff --git a/PWA_REV2/Gerbers/PWA_REV2.drl b/PWA_REV2/Gerbers/PWA_REV2.drl index 63b33ad..ae23db8 100644 --- a/PWA_REV2/Gerbers/PWA_REV2.drl +++ b/PWA_REV2/Gerbers/PWA_REV2.drl @@ -1,7 +1,7 @@ M48 -; DRILL file {KiCad 7.0.5} date Wed Sep 17 13:25:50 2025 +; DRILL file {KiCad 7.0.5} date Fri Sep 19 13:44:32 2025 ; FORMAT={-:-/ absolute / inch / decimal} -; #@! TF.CreationDate,2025-09-17T13:25:50-04:00 +; #@! TF.CreationDate,2025-09-19T13:44:32-04:00 ; #@! TF.GenerationSoftware,Kicad,Pcbnew,7.0.5 ; #@! TF.FileFunction,MixedPlating,1,2 FMAT,2 @@ -108,11 +108,11 @@ X3.3Y-0.74 X3.32Y-2.45 X3.325Y-3.425 X3.35Y-1.91 +X3.46Y-1.225 X3.47Y-2.31 X3.49Y-2.99 X3.5Y-1.0 X3.5098Y-3.84 -X3.51Y-1.305 X3.525Y-3.45 X3.53Y-1.7167 X3.55Y-3.355 @@ -324,7 +324,7 @@ X7.51Y-3.01 T3 X2.88Y-2.23 X3.01Y-2.09 -X3.35Y-1.17 +X3.3477Y-1.1672 X3.6643Y-1.6557 T4 X2.2967Y-0.9 From e77c815d3842f631ad61ca9ff3161b06aafe0981 Mon Sep 17 00:00:00 2001 From: Nagham Kheir Date: Thu, 4 Dec 2025 22:34:50 +0200 Subject: [PATCH 04/18] FactoryTest with Menu first draft. --- .../FactoryTest_wMenu/FactoryTest_wMenu.ino | 674 ++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino diff --git a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino new file mode 100644 index 0000000..62f8fe4 --- /dev/null +++ b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino @@ -0,0 +1,674 @@ +/* +------------------------------------------------------------------------------ +File: FactoryTest_Krake01.ino +Project: Krake / GPAD v2 – Factory Test Firmware +Document Type: Source Code (Factory Test) +Document ID: KRAKE-FT-ESP32-FT01 +Version: v0.1.0 +Date: 2025-12-03 +Author(s): Nagham Kheir, Public Invention +Status: Draft +------------------------------------------------------------------------------ +Revision History: +| Version | Date | Author | Description | +|---------|-----------|---------------|-------------------------------------| +| v0.1.0 | 2025-12-03| N. Kheir | Initial unified factory test code | +------------------------------------------------------------------------------ +Overview: +- Executes a repeatable factory test sequence for ESP32-WROOM-32D Krake/GPAD v2 + boards, validating: + 1) Power / ID + 2) Inputs (rotary encoder + push button) + 3) LCD (I²C, 20x4) + 4) LEDs / Lamps + 5) SD card via DFPlayer + 6) DFPlayer + Speaker (operator confirmation) + 7) Wi-Fi AP + 8) Wi-Fi STA (SSID & PASS entered via Serial console) + A) LittleFS Read/Write + B) UART0 (USB Serial, operator confirmation) + C) SPI loopback (MOSI <-> MISO jumper) + D) RS-232 loopback (TX <-> RX on DB9, via MAX3232) +- Operator interacts via USB Serial at 115200 baud. +- Intended for FACTORY USE ONLY (not for field deployment). +------------------------------------------------------------------------------ +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +// ----------------------------------------------------------------------------- +// Hardware configuration (ADJUST to match Krake / GPAD v2 wiring) +// ----------------------------------------------------------------------------- + +// Lamps / LEDs +const int LED_D9 = 23; // Mute LED on PMD / general status +const int LAMP1 = 15; // Lamp 1 (e.g. cold food) +const int LAMP2 = 4; // Lamp 2 (also DFPlayer BUSY input) +const int LAMP3 = 5; // Lamp 3 (also SPI CS) +const int LAMP4 = 18; // Lamp 4 (also SPI SCK) +const int LAMP5 = 19; // Lamp 5 (also SPI MISO) + +// Rotary encoder (input) +const int ENC_CLK = 39; +const int ENC_DT = 36; +const int ENC_SW = 34; + +// DFPlayer / speaker (UART2) +const int DF_TXD2 = 17; // ESP32 TX2 -> DFPlayer RX +const int DF_RXD2 = 16; // ESP32 RX2 <- DFPlayer TX +const int DF_BUSY_IN = LAMP2; // Shared with LAMP2, active LOW when playing + +// RS-232 (UART1) – ADJUST TO YOUR BOARD (pins via MAX3232 to DB9) +const int UART1_TXD1 = 2; // ESP32 TX1 -> MAX3232 -> DB9 +const int UART1_RXD1 = 15; // ESP32 RX1 <- MAX3232 <- DB9 +const long UART1_BAUD = 115200; + +// SPI (VSPI) – shared with lamps on Krake header +const int SPI_MOSI_PIN = LED_D9; // 23 +const int SPI_MISO_PIN = LAMP5; // 19 +const int SPI_SCK_PIN = LAMP4; // 18 +const int SPI_CS_PIN = LAMP3; // 5 + +// ----------------------------------------------------------------------------- +// DFPlayer & test bookkeeping +// ----------------------------------------------------------------------------- + +HardwareSerial dfSerial(2); +DFRobotDFPlayerMini dfPlayer; +bool dfInitialized = false; + +// Test indices (keep order as actual test order) +enum TestIndex { + T_POWER = 0, + T_INPUTS, + T_LCD, + T_LEDS, + T_SD, + T_DFPLAYER, + T_WIFI_AP, + T_WIFI_STA, + T_LITTLEFS, + T_UART0, + T_SPI, + T_RS232, + T_COUNT +}; + +const char* TEST_NAMES[T_COUNT] = { + "1 Power / ID", + "2 Inputs (Encoder / Button)", + "3 LCD (I2C)", + "4 LEDs / Lamps", + "5 SD (DFPlayer card)", + "6 DFPlayer + Speaker", + "7 Wi-Fi AP", + "8 Wi-Fi STA (manual SSID/PASS)", + "A LittleFS R/W", + "B UART0 (USB Serial)", + "C SPI loopback", + "D RS-232 loopback" +}; + +bool testResults[T_COUNT] = { false }; + +// ----------------------------------------------------------------------------- +// Utility +// ----------------------------------------------------------------------------- + +// Read one line from Serial with timeout (for SSID/PASS) +String readLine(uint32_t timeoutMs = 15000) { + String s; + uint32_t start = millis(); + while (millis() - start < timeoutMs) { + while (Serial.available()) { + char c = Serial.read(); + if (c == '\r') continue; + if (c == '\n') return s; + s += c; + } + } + return s; +} + +void printBanner() { + Serial.println(); + Serial.println(F("=======================================")); + Serial.println(F(" KRAKE FACTORY TEST - ESP32-WROOM ")); + Serial.println(F("=======================================")); + Serial.printf("Chip revision: %d\n", ESP.getChipRevision()); + Serial.printf("Flash size: %u bytes\n", ESP.getFlashChipSize()); + Serial.print(F("MAC (STA): ")); + Serial.println(WiFi.macAddress()); + Serial.println(); +} + +void printSummary() { + Serial.println(); + Serial.println(F("========== FACTORY TEST SUMMARY ==========")); + for (int i = 0; i < T_COUNT; ++i) { + Serial.printf("%-33s : %s\n", + TEST_NAMES[i], + testResults[i] ? "PASS" : "FAIL"); + } + Serial.println(F("==========================================")); + Serial.println(); +} + +void printMenu() { + Serial.println(F("Test menu (order matters):")); + Serial.println(F(" 1 Power / ID")); + Serial.println(F(" 2 Inputs (Encoder / Button)")); + Serial.println(F(" 3 LCD (I2C)")); + Serial.println(F(" 4 LEDs / Lamps")); + Serial.println(F(" 5 SD (DFPlayer card)")); + Serial.println(F(" 6 DFPlayer + Speaker")); + Serial.println(F(" 7 Wi-Fi AP")); + Serial.println(F(" 8 Wi-Fi STA (manual SSID/PASS)")); + Serial.println(F(" A LittleFS R/W")); + Serial.println(F(" B UART0 (USB Serial)")); + Serial.println(F(" C SPI loopback")); + Serial.println(F(" D RS-232 loopback")); + Serial.println(F(" P Run ALL (1 -> D)")); + Serial.println(F(" R Reboot")); + Serial.println(); + Serial.print(F("Enter command: ")); +} + +// ----------------------------------------------------------------------------- +// Individual tests +// ----------------------------------------------------------------------------- + +// 1) Power / ID +bool runTest_Power() { + Serial.println(F("\n[1] Power / ID")); + printBanner(); + Serial.println(F("Power OK: MCU running and Serial is active.")); + return true; +} + +// 2) Inputs (Encoder / Button) +bool runTest_Inputs() { + Serial.println(F("\n[2] Inputs (Encoder / Button)")); + pinMode(ENC_CLK, INPUT); + pinMode(ENC_DT, INPUT); + pinMode(ENC_SW, INPUT_PULLUP); + + Serial.println(F("Rotate encoder CLOCKWISE, then COUNTER-CLOCKWISE,")); + Serial.println(F("then PRESS the encoder button within 10 seconds.")); + delay(500); + + int lastCLK = digitalRead(ENC_CLK); + bool sawCW = false; + bool sawCCW = false; + bool sawPress = false; + + unsigned long start = millis(); + while (millis() - start < 10000UL) { + int clk = digitalRead(ENC_CLK); + if (clk != lastCLK) { + if (digitalRead(ENC_DT) != clk) { + sawCW = true; + Serial.println(F(" Detected rotation: CW")); + } else { + sawCCW = true; + Serial.println(F(" Detected rotation: CCW")); + } + lastCLK = clk; + } + + if (!sawPress && digitalRead(ENC_SW) == LOW) { + sawPress = true; + Serial.println(F(" Detected encoder button press")); + } + + if (sawCW && sawCCW && sawPress) break; + } + + bool pass = sawCW && sawCCW && sawPress; + Serial.printf("Inputs test: %s\n", + pass ? "PASS" : "FAIL (missing rotation and/or press)"); + return pass; +} + +// 3) LCD (I2C) +bool runTest_LCD() { + Serial.println(F("\n[3] LCD (I2C 20x4)")); + + Wire.begin(); + delay(100); + + uint8_t foundAddr = 0; + Serial.println(F("Scanning I2C bus...")); + for (uint8_t addr = 1; addr < 127; ++addr) { + Wire.beginTransmission(addr); + if (Wire.endTransmission() == 0) { + Serial.printf(" Found I2C device at 0x%02X\n", addr); + if (!foundAddr) foundAddr = addr; + } + } + + if (!foundAddr) { + Serial.println(F("No I2C devices found. LCD test FAIL.")); + return false; + } + + LiquidCrystal_I2C lcd(foundAddr, 20, 4); + lcd.init(); + lcd.backlight(); + lcd.clear(); + lcd.setCursor(0, 0); + lcd.print("KRAKE FACTORY TEST"); + lcd.setCursor(0, 1); + lcd.print("LCD OK @ 0x"); + lcd.print(foundAddr, HEX); + lcd.setCursor(0, 2); + lcd.print("Check all 4 lines."); + lcd.setCursor(0, 3); + lcd.print("SN: "); + lcd.print(WiFi.macAddress()); + + Serial.println(F("LCD initialized and test message written.")); + Serial.println(F("Operator: visually confirm 20x4 LCD content.")); + return true; +} + +// 4) LEDs / Lamps +bool runTest_LEDs() { + Serial.println(F("\n[4] LEDs / Lamps")); + int pins[] = { LAMP1, LAMP2, LAMP3, LAMP4, LAMP5, LED_D9 }; + const char* names[] = { + "LAMP1", + "LAMP2 / DF_BUSY", + "LAMP3 / SPI_CS", + "LAMP4 / SPI_SCK", + "LAMP5 / SPI_MISO", + "LED_D9 / SPI_MOSI" + }; + + for (int i = 0; i < 6; ++i) { + pinMode(pins[i], OUTPUT); + digitalWrite(pins[i], LOW); + } + delay(50); + + for (int i = 0; i < 6; ++i) { + Serial.print(F(" -> ")); + Serial.print(names[i]); + Serial.println(F(" ON")); + digitalWrite(pins[i], HIGH); + delay(300); + digitalWrite(pins[i], LOW); + delay(100); + } + + Serial.println(F("Operator: visually confirm each lamp/LED toggled ON/OFF.")); + return true; +} + +// DFPlayer init helper +bool initDFPlayer() { + if (dfInitialized) return true; + + Serial.println(F("Initializing DFPlayer (UART2)...")); + pinMode(DF_BUSY_IN, INPUT_PULLUP); + dfSerial.begin(9600, SERIAL_8N1, DF_RXD2, DF_TXD2); + delay(500); + + if (!dfPlayer.begin(dfSerial, true, false)) { + Serial.println(F(" DFPlayer not detected. Check wiring & SD card.")); + return false; + } + + dfPlayer.setTimeOut(500); + dfPlayer.volume(20); // volume 0..30 + dfInitialized = true; + + Serial.println(F(" DFPlayer detected and initialized.")); + return true; +} + +// 5) SD (via DFPlayer) +bool runTest_SD() { + Serial.println(F("\n[5] SD (DFPlayer card)")); + if (!initDFPlayer()) { + Serial.println(F("SD test FAIL (no DFPlayer).")); + return false; + } + + int fileCount = dfPlayer.readFileCounts(); + Serial.printf(" DFPlayer reports %d files on SD card.\n", fileCount); + bool pass = (fileCount > 0); + Serial.printf("SD test: %s\n", pass ? "PASS" : "FAIL (no files on SD)"); + return pass; +} + +// 6) DFPlayer + Speaker (operator confirms) +bool runTest_DFPlayer() { + Serial.println(F("\n[6] DFPlayer + Speaker")); + if (!initDFPlayer()) { + Serial.println(F("DFPlayer/Speaker test FAIL (no DFPlayer).")); + return false; + } + + Serial.println(F("Playing test track #1 for ~3 seconds. Listen for audio.")); + dfPlayer.play(1); + unsigned long start = millis(); + while (millis() - start < 3000UL) { + // Busy line is active LOW when track is playing + (void)digitalRead(DF_BUSY_IN); + delay(50); + } + dfPlayer.stop(); + + Serial.println(F("Did you hear audio from the speaker? (Y/N, 10 s timeout)")); + + unsigned long waitStart = millis(); + while (millis() - waitStart < 10000UL) { + if (Serial.available()) { + char c = tolower(Serial.read()); + if (c == 'y') { + Serial.println(F("DFPlayer/Speaker test: PASS")); + return true; + } else if (c == 'n') { + Serial.println(F("DFPlayer/Speaker test: FAIL (operator)")); + return false; + } + } + } + + Serial.println(F("No operator response. DFPlayer/Speaker test: FAIL.")); + return false; +} + +// 7) Wi-Fi AP +bool runTest_WifiAP() { + Serial.println(F("\n[7] Wi-Fi AP")); + + WiFi.mode(WIFI_AP); + String mac = WiFi.macAddress(); + mac.replace(":", ""); + String ssid = "KRAKE_" + mac; + + bool ok = WiFi.softAP(ssid.c_str(), "krakefactory"); + if (!ok) { + Serial.println(F("Failed to start Wi-Fi AP.")); + return false; + } + + IPAddress ip = WiFi.softAPIP(); + Serial.print(F("AP SSID: ")); + Serial.println(ssid); + Serial.print(F("AP IP: ")); + Serial.println(ip); + Serial.println(F("Operator: connect from phone/PC and verify AP is visible.")); + return true; +} + +// 8) Wi-Fi STA (manual SSID/PASS via Serial) +bool runTest_WifiSTA() { + Serial.println(F("\n[8] Wi-Fi STA (manual SSID/PASS)")); + + WiFi.mode(WIFI_STA); + WiFi.disconnect(true, true); + delay(500); + + Serial.println(F("Enter Wi-Fi SSID then press Enter (15 s timeout):")); + Serial.print(F("> ")); + String ssid = readLine(); + if (ssid.length() == 0) { + Serial.println(F("No SSID entered. Wi-Fi STA test: FAIL.")); + return false; + } + + Serial.println(F("Enter Wi-Fi PASSWORD then press Enter (can be empty):")); + Serial.print(F("> ")); + String pass = readLine(); // can be empty + + Serial.print(F("Connecting to: ")); + Serial.println(ssid); + + WiFi.begin(ssid.c_str(), pass.c_str()); + + uint32_t start = millis(); + while (WiFi.status() != WL_CONNECTED && millis() - start < 20000UL) { + delay(500); + Serial.print('.'); + } + Serial.println(); + + if (WiFi.status() != WL_CONNECTED) { + Serial.println(F("Wi-Fi STA connection FAILED.")); + return false; + } + + Serial.println(F("Wi-Fi STA connected successfully.")); + Serial.print(F("IP: ")); + Serial.println(WiFi.localIP()); + Serial.print(F("RSSI: ")); + Serial.println(WiFi.RSSI()); + + Serial.println(F("Operator: verify connectivity from your console if needed.")); + return true; +} + +// A) LittleFS R/W +bool runTest_LittleFS() { + Serial.println(F("\n[A] LittleFS R/W")); + + if (!LittleFS.begin(true)) { // true = format if mount fails + Serial.println(F("LittleFS mount FAILED.")); + return false; + } + + const char* path = "/factory_test.txt"; + const char* payload = "KRAKE_FACTORY_TEST"; + + Serial.print(F("Writing file ")); + Serial.println(path); + File f = LittleFS.open(path, FILE_WRITE); + if (!f) { + Serial.println(F("Failed to open file for write.")); + return false; + } + f.print(payload); + f.close(); + + Serial.println(F("Reading file back...")); + f = LittleFS.open(path, FILE_READ); + if (!f) { + Serial.println(F("Failed to open file for read.")); + return false; + } + String readBack = f.readString(); + f.close(); + + bool ok = (readBack == payload); + Serial.printf("LittleFS R/W test: %s\n", + ok ? "PASS" : "FAIL (content mismatch)"); + return ok; +} + +// B) UART0 (USB Serial) +bool runTest_UART0() { + Serial.println(F("\n[B] UART0 (USB Serial)")); + Serial.println(F("This test is interactive.")); + Serial.println(F("Type 'Y' then Enter to PASS, 'N' to FAIL (10 s timeout).")); + + unsigned long start = millis(); + while (millis() - start < 10000UL) { + if (Serial.available()) { + char c = tolower(Serial.read()); + if (c == 'y') { + Serial.println(F("UART0 test: PASS (operator confirmed).")); + return true; + } else if (c == 'n') { + Serial.println(F("UART0 test: FAIL (operator).")); + return false; + } else { + Serial.print(F("Echo: ")); + Serial.println(c); + } + } + } + + Serial.println(F("No operator response. UART0 test: FAIL.")); + return false; +} + +// C) SPI loopback (MOSI <-> MISO) +bool runTest_SPI() { + Serial.println(F("\n[C] SPI loopback")); + Serial.println(F("Connect MOSI <-> MISO on SPI header for automatic test.")); + Serial.printf("Using pins: MOSI=%d, MISO=%d, SCK=%d, CS=%d\n", + SPI_MOSI_PIN, SPI_MISO_PIN, SPI_SCK_PIN, SPI_CS_PIN); + + SPI.begin(SPI_SCK_PIN, SPI_MISO_PIN, SPI_MOSI_PIN, SPI_CS_PIN); + pinMode(SPI_CS_PIN, OUTPUT); + digitalWrite(SPI_CS_PIN, LOW); + + SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0)); + uint8_t out = 0xA5; + uint8_t in = SPI.transfer(out); + SPI.endTransaction(); + digitalWrite(SPI_CS_PIN, HIGH); + + Serial.printf("SPI sent 0x%02X, received 0x%02X\n", out, in); + bool ok = (in == out); + Serial.printf("SPI loopback test: %s\n", + ok ? "PASS" : "FAIL (check loopback wiring)"); + return ok; +} + +// D) RS-232 loopback (UART1) +bool runTest_RS232() { + Serial.println(F("\n[D] RS-232 loopback (UART1)")); + Serial.println(F("Connect RS-232 TX <-> RX at DB9 (pins 2 and 3) via MAX3232.")); + Serial.printf("Using UART1 TXD=%d, RXD=%d, BAUD=%ld\n", + UART1_TXD1, UART1_RXD1, UART1_BAUD); + + HardwareSerial rs232(1); + rs232.begin(UART1_BAUD, SERIAL_8N1, UART1_RXD1, UART1_TXD1); + delay(200); + + const uint8_t pattern = 0x55; // 01010101 + unsigned long start = millis(); + bool ok = false; + + while (millis() - start < 3000UL) { + rs232.write(pattern); + rs232.flush(); + delay(20); + if (rs232.available()) { + int b = rs232.read(); + if (b == pattern) { + ok = true; + break; + } + } + } + rs232.end(); + + Serial.printf("RS-232 loopback test: %s\n", + ok ? "PASS" : "FAIL (check wiring / MAX3232)"); + return ok; +} + +// ----------------------------------------------------------------------------- +// Test dispatcher +// ----------------------------------------------------------------------------- + +bool runSingleTestFromIndex(TestIndex idx) { + switch (idx) { + case T_POWER: return runTest_Power(); + case T_INPUTS: return runTest_Inputs(); + case T_LCD: return runTest_LCD(); + case T_LEDS: return runTest_LEDs(); + case T_SD: return runTest_SD(); + case T_DFPLAYER: return runTest_DFPlayer(); + case T_WIFI_AP: return runTest_WifiAP(); + case T_WIFI_STA: return runTest_WifiSTA(); + case T_LITTLEFS: return runTest_LittleFS(); + case T_UART0: return runTest_UART0(); + case T_SPI: return runTest_SPI(); + case T_RS232: return runTest_RS232(); + default: return false; + } +} + +void runAllTests() { + Serial.println(F("\n[P] Running ALL tests (1 -> D) in order...")); + for (int i = 0; i < T_COUNT; ++i) { + testResults[i] = runSingleTestFromIndex(static_cast(i)); + } + printSummary(); +} + +// ----------------------------------------------------------------------------- +// Arduino entry points +// ----------------------------------------------------------------------------- + +void setup() { + Serial.begin(115200); + while (!Serial) { + // wait for USB Serial (on some hosts this may always be true; OK) + delay(10); + } + + WiFi.mode(WIFI_OFF); // start from a clean Wi-Fi state + delay(100); + + printBanner(); + printSummary(); // initial (all FAIL) summary for the unit + printMenu(); +} + +void loop() { + if (Serial.available()) { + char c = Serial.read(); + if (c == '\r' || c == '\n') { + // ignore pure newlines + return; + } + c = toupper(c); + Serial.println(c); // echo + + bool recognized = true; + + switch (c) { + case '1': testResults[T_POWER] = runTest_Power(); break; + case '2': testResults[T_INPUTS] = runTest_Inputs(); break; + case '3': testResults[T_LCD] = runTest_LCD(); break; + case '4': testResults[T_LEDS] = runTest_LEDs(); break; + case '5': testResults[T_SD] = runTest_SD(); break; + case '6': testResults[T_DFPLAYER] = runTest_DFPlayer(); break; + case '7': testResults[T_WIFI_AP] = runTest_WifiAP(); break; + case '8': testResults[T_WIFI_STA] = runTest_WifiSTA(); break; + case 'A': testResults[T_LITTLEFS] = runTest_LittleFS(); break; + case 'B': testResults[T_UART0] = runTest_UART0(); break; + case 'C': testResults[T_SPI] = runTest_SPI(); break; + case 'D': testResults[T_RS232] = runTest_RS232(); break; + case 'P': runAllTests(); break; + case 'R': + Serial.println(F("Rebooting...")); + delay(200); + ESP.restart(); + break; + default: + recognized = false; + Serial.println(F("Unknown command.")); + break; + } + + if (recognized && c != 'P' && c != 'R') { + printSummary(); + } + printMenu(); + } +} From d6094b6017183bbf3b415e05e82027d2fd20091f Mon Sep 17 00:00:00 2001 From: nk25719 Date: Sat, 27 Dec 2025 17:14:57 +0200 Subject: [PATCH 05/18] Fix Serial prompt input (line-based),abort prompts via menu keys,and DO NOT play DF audio if SD missing. --- .../FactoryTest_wMenu/FactoryTest_wMenu.ino | 408 ++++++++++++------ 1 file changed, 270 insertions(+), 138 deletions(-) diff --git a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino index 62f8fe4..dabbd85 100644 --- a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino +++ b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino @@ -1,28 +1,32 @@ /* ------------------------------------------------------------------------------ -File: FactoryTest_Krake01.ino +File: FactoryTest_wMenu.ino Project: Krake / GPAD v2 – Factory Test Firmware Document Type: Source Code (Factory Test) Document ID: KRAKE-FT-ESP32-FT01 -Version: v0.1.0 -Date: 2025-12-03 +Version: v0.3.0 +Date: 2025-12-27 Author(s): Nagham Kheir, Public Invention Status: Draft ------------------------------------------------------------------------------ Revision History: -| Version | Date | Author | Description | -|---------|-----------|---------------|-------------------------------------| -| v0.1.0 | 2025-12-03| N. Kheir | Initial unified factory test code | +| Version | Date | Author | Description | +|---------|-----------|---------------|-----------------------------------------------| +| v0.1.0 | 2025-12-03| N. Kheir | Initial unified factory test code | +| v0.2.0 | 2025-12-27| N. Kheir | Add operator confirmation for LCD/LED tests | +| v0.3.0 | 2025-12-27| N. Kheir | Fix Serial prompt input (line-based), | +| | | | abort prompts via menu keys, | +| | | | and DO NOT play DF audio if SD missing | ------------------------------------------------------------------------------ Overview: - Executes a repeatable factory test sequence for ESP32-WROOM-32D Krake/GPAD v2 boards, validating: 1) Power / ID 2) Inputs (rotary encoder + push button) - 3) LCD (I²C, 20x4) - 4) LEDs / Lamps + 3) LCD (I²C, 20x4) [I2C addr detect + operator Y/N] + 4) LEDs / Lamps [operator Y/N] 5) SD card via DFPlayer - 6) DFPlayer + Speaker (operator confirmation) + 6) DFPlayer + Speaker [ONLY if SD has files + operator Y/N] 7) Wi-Fi AP 8) Wi-Fi STA (SSID & PASS entered via Serial console) A) LittleFS Read/Write @@ -32,6 +36,11 @@ Overview: - Operator interacts via USB Serial at 115200 baud. - Intended for FACTORY USE ONLY (not for field deployment). ------------------------------------------------------------------------------ +IMPORTANT BEHAVIOR: +- All operator prompts are LINE-BASED: type Y/N then press Enter. +- If you press a menu key (1..8/A..D/P/R) at the beginning of a prompt, + the prompt ABORTS (step FAILs) and that command runs next. +------------------------------------------------------------------------------ */ #include @@ -50,7 +59,7 @@ Overview: // Lamps / LEDs const int LED_D9 = 23; // Mute LED on PMD / general status const int LAMP1 = 15; // Lamp 1 (e.g. cold food) -const int LAMP2 = 4; // Lamp 2 (also DFPlayer BUSY input) +const int LAMP2 = 4; // Lamp 2 (often DFPlayer BUSY input on some builds) const int LAMP3 = 5; // Lamp 3 (also SPI CS) const int LAMP4 = 18; // Lamp 4 (also SPI SCK) const int LAMP5 = 19; // Lamp 5 (also SPI MISO) @@ -63,11 +72,13 @@ const int ENC_SW = 34; // DFPlayer / speaker (UART2) const int DF_TXD2 = 17; // ESP32 TX2 -> DFPlayer RX const int DF_RXD2 = 16; // ESP32 RX2 <- DFPlayer TX -const int DF_BUSY_IN = LAMP2; // Shared with LAMP2, active LOW when playing +const int DF_BUSY_IN = LAMP2; // active LOW when playing (if wired) // RS-232 (UART1) – ADJUST TO YOUR BOARD (pins via MAX3232 to DB9) +// WARNING: In this sample, UART1_RXD1 conflicts with LAMP1 (GPIO15). +// Fix by changing one of them to your actual board mapping. const int UART1_TXD1 = 2; // ESP32 TX1 -> MAX3232 -> DB9 -const int UART1_RXD1 = 15; // ESP32 RX1 <- MAX3232 <- DB9 +const int UART1_RXD1 = 15; // ESP32 RX1 <- MAX3232 <- DB9 (CONFLICT with LAMP1!) const long UART1_BAUD = 115200; // SPI (VSPI) – shared with lamps on Krake header @@ -76,6 +87,12 @@ const int SPI_MISO_PIN = LAMP5; // 19 const int SPI_SCK_PIN = LAMP4; // 18 const int SPI_CS_PIN = LAMP3; // 5 +// LCD expected I2C addresses (adjust if your backpack differs) +const uint8_t LCD_ALLOWED_ADDRS[] = { 0x27, 0x3F, 0x38 }; + +// If LAMP2 is physically wired to DFPlayer BUSY, DO NOT drive it as an output. +const bool SKIP_DRIVING_LAMP2 = true; + // ----------------------------------------------------------------------------- // DFPlayer & test bookkeeping // ----------------------------------------------------------------------------- @@ -119,24 +136,87 @@ const char* TEST_NAMES[T_COUNT] = { bool testResults[T_COUNT] = { false }; // ----------------------------------------------------------------------------- -// Utility +// Serial helpers + pending command capture // ----------------------------------------------------------------------------- +static char g_pendingCmd = 0; + +static bool isMenuKey(char c) { + c = toupper((unsigned char)c); + return (c >= '1' && c <= '8') || (c >= 'A' && c <= 'D') || c == 'P' || c == 'R'; +} + +static void flushSerialRx() { + while (Serial.available()) (void)Serial.read(); +} -// Read one line from Serial with timeout (for SSID/PASS) -String readLine(uint32_t timeoutMs = 15000) { - String s; +// Read a line, but if the FIRST typed char is a menu key, abort and queue it. +// Returns: +// - true : line completed by Enter OR timeout reached (out may be empty) +// - false : aborted by menu key (g_pendingCmd set) +static bool readLineOrMenuAbort(String &out, uint32_t timeoutMs = 15000) { + out = ""; uint32_t start = millis(); + while (millis() - start < timeoutMs) { while (Serial.available()) { char c = Serial.read(); if (c == '\r') continue; - if (c == '\n') return s; - s += c; + + // If first char is a menu key, abort this input capture + if (out.length() == 0 && c != '\n' && isMenuKey(c)) { + g_pendingCmd = (char)toupper((unsigned char)c); + return false; + } + + if (c == '\n') return true; // line complete + out += c; } + delay(5); } - return s; + + return true; // timeout (out may be empty or partial) +} + +// Ask operator Y/N using LINE input (type then Enter). +// If menu key is pressed as first char, prompt aborts (FAIL) and command runs next. +static bool promptYesNo(const __FlashStringHelper* question, + uint32_t timeoutMs = 15000, + bool defaultNo = true) { + Serial.println(question); + Serial.println(F("Type Y then press Enter to PASS, N then Enter to FAIL.")); + Serial.println(F("(Menu keys abort this step and run next.)")); + Serial.print(F("> ")); + + String line; + bool completed = readLineOrMenuAbort(line, timeoutMs); + if (!completed) { + Serial.println(F("\nAborted by menu key -> FAIL for this step.")); + return false; + } + + line.trim(); + if (line.length() == 0) { + Serial.println(defaultNo ? F("No response -> FAIL") : F("No response -> PASS")); + return !defaultNo; + } + + char c = (char)toupper((unsigned char)line[0]); + if (c == 'Y') return true; + if (c == 'N') return false; + + if (isMenuKey(c)) { + g_pendingCmd = c; + Serial.println(F("Menu key captured -> FAIL for this step.")); + return false; + } + + Serial.println(F("Unknown input -> FAIL")); + return false; } +// ----------------------------------------------------------------------------- +// UI +// ----------------------------------------------------------------------------- void printBanner() { Serial.println(); Serial.println(F("=======================================")); @@ -226,6 +306,7 @@ bool runTest_Inputs() { if (!sawPress && digitalRead(ENC_SW) == LOW) { sawPress = true; Serial.println(F(" Detected encoder button press")); + delay(150); // basic debounce } if (sawCW && sawCCW && sawPress) break; @@ -245,17 +326,20 @@ bool runTest_LCD() { delay(100); uint8_t foundAddr = 0; - Serial.println(F("Scanning I2C bus...")); - for (uint8_t addr = 1; addr < 127; ++addr) { + Serial.println(F("Scanning I2C bus for LCD addresses (0x27, 0x3F, 0x38)...")); + + for (uint8_t i = 0; i < sizeof(LCD_ALLOWED_ADDRS); ++i) { + uint8_t addr = LCD_ALLOWED_ADDRS[i]; Wire.beginTransmission(addr); if (Wire.endTransmission() == 0) { - Serial.printf(" Found I2C device at 0x%02X\n", addr); - if (!foundAddr) foundAddr = addr; + Serial.printf(" Found LCD candidate at 0x%02X\n", addr); + foundAddr = addr; + break; } } if (!foundAddr) { - Serial.println(F("No I2C devices found. LCD test FAIL.")); + Serial.println(F("No LCD detected at expected addresses. LCD test FAIL.")); return false; } @@ -263,53 +347,67 @@ bool runTest_LCD() { lcd.init(); lcd.backlight(); lcd.clear(); + lcd.setCursor(0, 0); lcd.print("KRAKE FACTORY TEST"); lcd.setCursor(0, 1); lcd.print("LCD OK @ 0x"); lcd.print(foundAddr, HEX); lcd.setCursor(0, 2); - lcd.print("Check all 4 lines."); + lcd.print("All 4 lines visible?"); lcd.setCursor(0, 3); - lcd.print("SN: "); - lcd.print(WiFi.macAddress()); + lcd.print("Type Y/N in Serial"); Serial.println(F("LCD initialized and test message written.")); - Serial.println(F("Operator: visually confirm 20x4 LCD content.")); - return true; + flushSerialRx(); + bool ok = promptYesNo(F("Visually confirm LCD shows the test message (all 4 lines)."), + 20000, /*defaultNo*/ true); + + Serial.printf("LCD test: %s\n", ok ? "PASS" : "FAIL"); + return ok; } // 4) LEDs / Lamps bool runTest_LEDs() { Serial.println(F("\n[4] LEDs / Lamps")); + int pins[] = { LAMP1, LAMP2, LAMP3, LAMP4, LAMP5, LED_D9 }; const char* names[] = { "LAMP1", - "LAMP2 / DF_BUSY", - "LAMP3 / SPI_CS", - "LAMP4 / SPI_SCK", - "LAMP5 / SPI_MISO", - "LED_D9 / SPI_MOSI" + "LAMP2 (maybe DF BUSY)", + "LAMP3 (SPI_CS)", + "LAMP4 (SPI_SCK)", + "LAMP5 (SPI_MISO)", + "LED_D9 (SPI_MOSI)" }; for (int i = 0; i < 6; ++i) { + if (SKIP_DRIVING_LAMP2 && pins[i] == LAMP2) continue; pinMode(pins[i], OUTPUT); digitalWrite(pins[i], LOW); } delay(50); for (int i = 0; i < 6; ++i) { + if (SKIP_DRIVING_LAMP2 && pins[i] == LAMP2) { + Serial.println(F(" -> Skipping LAMP2 drive (BUSY shared safety)")); + continue; + } Serial.print(F(" -> ")); Serial.print(names[i]); - Serial.println(F(" ON")); + Serial.println(F(" blink")); digitalWrite(pins[i], HIGH); delay(300); digitalWrite(pins[i], LOW); - delay(100); + delay(150); } - Serial.println(F("Operator: visually confirm each lamp/LED toggled ON/OFF.")); - return true; + flushSerialRx(); + bool ok = promptYesNo(F("Did you see the LEDs/Lamps blink as expected?"), + 20000, /*defaultNo*/ true); + + Serial.printf("LEDs/Lamps test: %s\n", ok ? "PASS" : "FAIL"); + return ok; } // DFPlayer init helper @@ -318,18 +416,19 @@ bool initDFPlayer() { Serial.println(F("Initializing DFPlayer (UART2)...")); pinMode(DF_BUSY_IN, INPUT_PULLUP); + dfSerial.begin(9600, SERIAL_8N1, DF_RXD2, DF_TXD2); delay(500); if (!dfPlayer.begin(dfSerial, true, false)) { - Serial.println(F(" DFPlayer not detected. Check wiring & SD card.")); + Serial.println(F(" DFPlayer not detected. Check wiring & power.")); return false; } dfPlayer.setTimeOut(500); - dfPlayer.volume(20); // volume 0..30 - dfInitialized = true; + dfPlayer.volume(20); // 0..30 + dfInitialized = true; Serial.println(F(" DFPlayer detected and initialized.")); return true; } @@ -344,47 +443,56 @@ bool runTest_SD() { int fileCount = dfPlayer.readFileCounts(); Serial.printf(" DFPlayer reports %d files on SD card.\n", fileCount); + + if (fileCount < 0) { + Serial.println(F("SD test FAIL: DFPlayer could not read SD/file index.")); + Serial.println(F("Common causes: no SD / SD not FAT32 / bad contacts / power issue.")); + return false; + } + bool pass = (fileCount > 0); Serial.printf("SD test: %s\n", pass ? "PASS" : "FAIL (no files on SD)"); return pass; } -// 6) DFPlayer + Speaker (operator confirms) +// 6) DFPlayer + Speaker (ONLY if SD is readable and has files) bool runTest_DFPlayer() { Serial.println(F("\n[6] DFPlayer + Speaker")); + if (!initDFPlayer()) { Serial.println(F("DFPlayer/Speaker test FAIL (no DFPlayer).")); return false; } - Serial.println(F("Playing test track #1 for ~3 seconds. Listen for audio.")); + int fileCount = dfPlayer.readFileCounts(); + Serial.printf(" SD file count = %d\n", fileCount); + + if (fileCount <= 0) { + Serial.println(F("No readable SD/files -> NOT playing audio. DFPlayer test FAIL.")); + Serial.println(F("Fix SD first (FAT32 + files present), then retry.")); + return false; + } + + Serial.println(F("Playing test track #1 for ~3 seconds...")); dfPlayer.play(1); + + // progress dots so it never looks frozen unsigned long start = millis(); while (millis() - start < 3000UL) { - // Busy line is active LOW when track is playing - (void)digitalRead(DF_BUSY_IN); - delay(50); + if (((millis() - start) % 250) < 10) Serial.print('.'); + delay(10); } + Serial.println(); + dfPlayer.stop(); + delay(200); - Serial.println(F("Did you hear audio from the speaker? (Y/N, 10 s timeout)")); - - unsigned long waitStart = millis(); - while (millis() - waitStart < 10000UL) { - if (Serial.available()) { - char c = tolower(Serial.read()); - if (c == 'y') { - Serial.println(F("DFPlayer/Speaker test: PASS")); - return true; - } else if (c == 'n') { - Serial.println(F("DFPlayer/Speaker test: FAIL (operator)")); - return false; - } - } - } + flushSerialRx(); + bool ok = promptYesNo(F("Did you hear audio from the speaker?"), + 20000, /*defaultNo*/ true); - Serial.println(F("No operator response. DFPlayer/Speaker test: FAIL.")); - return false; + Serial.printf("DFPlayer/Speaker test: %s\n", ok ? "PASS" : "FAIL"); + return ok; } // 7) Wi-Fi AP @@ -411,7 +519,7 @@ bool runTest_WifiAP() { return true; } -// 8) Wi-Fi STA (manual SSID/PASS via Serial) +// 8) Wi-Fi STA (manual SSID/PASS via Serial) with menu-abort bool runTest_WifiSTA() { Serial.println(F("\n[8] Wi-Fi STA (manual SSID/PASS)")); @@ -419,9 +527,16 @@ bool runTest_WifiSTA() { WiFi.disconnect(true, true); delay(500); + flushSerialRx(); Serial.println(F("Enter Wi-Fi SSID then press Enter (15 s timeout):")); Serial.print(F("> ")); - String ssid = readLine(); + + String ssid; + if (!readLineOrMenuAbort(ssid, 15000)) { + Serial.println(F("\nAborted by menu key.")); + return false; + } + ssid.trim(); if (ssid.length() == 0) { Serial.println(F("No SSID entered. Wi-Fi STA test: FAIL.")); return false; @@ -429,7 +544,13 @@ bool runTest_WifiSTA() { Serial.println(F("Enter Wi-Fi PASSWORD then press Enter (can be empty):")); Serial.print(F("> ")); - String pass = readLine(); // can be empty + + String pass; + if (!readLineOrMenuAbort(pass, 15000)) { + Serial.println(F("\nAborted by menu key.")); + return false; + } + pass.trim(); // allow empty Serial.print(F("Connecting to: ")); Serial.println(ssid); @@ -454,7 +575,6 @@ bool runTest_WifiSTA() { Serial.print(F("RSSI: ")); Serial.println(WiFi.RSSI()); - Serial.println(F("Operator: verify connectivity from your console if needed.")); return true; } @@ -462,7 +582,7 @@ bool runTest_WifiSTA() { bool runTest_LittleFS() { Serial.println(F("\n[A] LittleFS R/W")); - if (!LittleFS.begin(true)) { // true = format if mount fails + if (!LittleFS.begin(true)) { // true = format if mount fails Serial.println(F("LittleFS mount FAILED.")); return false; } @@ -472,6 +592,7 @@ bool runTest_LittleFS() { Serial.print(F("Writing file ")); Serial.println(path); + File f = LittleFS.open(path, FILE_WRITE); if (!f) { Serial.println(F("Failed to open file for write.")); @@ -490,36 +611,18 @@ bool runTest_LittleFS() { f.close(); bool ok = (readBack == payload); - Serial.printf("LittleFS R/W test: %s\n", - ok ? "PASS" : "FAIL (content mismatch)"); + Serial.printf("LittleFS R/W test: %s\n", ok ? "PASS" : "FAIL (content mismatch)"); return ok; } // B) UART0 (USB Serial) bool runTest_UART0() { Serial.println(F("\n[B] UART0 (USB Serial)")); - Serial.println(F("This test is interactive.")); - Serial.println(F("Type 'Y' then Enter to PASS, 'N' to FAIL (10 s timeout).")); - - unsigned long start = millis(); - while (millis() - start < 10000UL) { - if (Serial.available()) { - char c = tolower(Serial.read()); - if (c == 'y') { - Serial.println(F("UART0 test: PASS (operator confirmed).")); - return true; - } else if (c == 'n') { - Serial.println(F("UART0 test: FAIL (operator).")); - return false; - } else { - Serial.print(F("Echo: ")); - Serial.println(c); - } - } - } - - Serial.println(F("No operator response. UART0 test: FAIL.")); - return false; + flushSerialRx(); + bool ok = promptYesNo(F("UART0 check: can you see this prompt and respond?"), + 20000, /*defaultNo*/ true); + Serial.printf("UART0 test: %s\n", ok ? "PASS" : "FAIL"); + return ok; } // C) SPI loopback (MOSI <-> MISO) @@ -541,8 +644,7 @@ bool runTest_SPI() { Serial.printf("SPI sent 0x%02X, received 0x%02X\n", out, in); bool ok = (in == out); - Serial.printf("SPI loopback test: %s\n", - ok ? "PASS" : "FAIL (check loopback wiring)"); + Serial.printf("SPI loopback test: %s\n", ok ? "PASS" : "FAIL (check loopback wiring)"); return ok; } @@ -553,11 +655,16 @@ bool runTest_RS232() { Serial.printf("Using UART1 TXD=%d, RXD=%d, BAUD=%ld\n", UART1_TXD1, UART1_RXD1, UART1_BAUD); + if (UART1_RXD1 == LAMP1) { + Serial.println(F("WARNING: UART1_RXD1 conflicts with LAMP1 (GPIO15).")); + Serial.println(F(" Update pin mapping to match your PCB.")); + } + HardwareSerial rs232(1); rs232.begin(UART1_BAUD, SERIAL_8N1, UART1_RXD1, UART1_TXD1); delay(200); - const uint8_t pattern = 0x55; // 01010101 + const uint8_t pattern = 0x55; unsigned long start = millis(); bool ok = false; @@ -583,7 +690,6 @@ bool runTest_RS232() { // ----------------------------------------------------------------------------- // Test dispatcher // ----------------------------------------------------------------------------- - bool runSingleTestFromIndex(TestIndex idx) { switch (idx) { case T_POWER: return runTest_Power(); @@ -604,71 +710,97 @@ bool runSingleTestFromIndex(TestIndex idx) { void runAllTests() { Serial.println(F("\n[P] Running ALL tests (1 -> D) in order...")); + for (int i = 0; i < T_COUNT; ++i) { + if (i == T_DFPLAYER && testResults[T_SD] == false) { + Serial.println(F("\n[6] DFPlayer + Speaker")); + Serial.println(F("Skipping audio play because SD test FAILED.")); + testResults[T_DFPLAYER] = false; + continue; + } testResults[i] = runSingleTestFromIndex(static_cast(i)); } + printSummary(); } // ----------------------------------------------------------------------------- -// Arduino entry points +// Command handler // ----------------------------------------------------------------------------- +static void handleCommand(char c) { + c = toupper((unsigned char)c); + + bool recognized = true; + switch (c) { + case '1': testResults[T_POWER] = runTest_Power(); break; + case '2': testResults[T_INPUTS] = runTest_Inputs(); break; + case '3': testResults[T_LCD] = runTest_LCD(); break; + case '4': testResults[T_LEDS] = runTest_LEDs(); break; + case '5': testResults[T_SD] = runTest_SD(); break; + case '6': testResults[T_DFPLAYER] = runTest_DFPlayer(); break; + case '7': testResults[T_WIFI_AP] = runTest_WifiAP(); break; + case '8': testResults[T_WIFI_STA] = runTest_WifiSTA(); break; + case 'A': testResults[T_LITTLEFS] = runTest_LittleFS(); break; + case 'B': testResults[T_UART0] = runTest_UART0(); break; + case 'C': testResults[T_SPI] = runTest_SPI(); break; + case 'D': testResults[T_RS232] = runTest_RS232(); break; + case 'P': runAllTests(); break; + case 'R': + Serial.println(F("Rebooting...")); + delay(200); + ESP.restart(); + break; + default: + recognized = false; + Serial.println(F("Unknown command.")); + break; + } + + if (recognized && c != 'P' && c != 'R') { + printSummary(); + } + printMenu(); +} +// ----------------------------------------------------------------------------- +// Arduino entry points +// ----------------------------------------------------------------------------- void setup() { Serial.begin(115200); - while (!Serial) { - // wait for USB Serial (on some hosts this may always be true; OK) + + // Do not block forever waiting for Serial (some hosts never assert it) + uint32_t t0 = millis(); + while (!Serial && (millis() - t0 < 2000)) { delay(10); } - WiFi.mode(WIFI_OFF); // start from a clean Wi-Fi state + WiFi.mode(WIFI_OFF); delay(100); printBanner(); - printSummary(); // initial (all FAIL) summary for the unit + printSummary(); printMenu(); } void loop() { + // If a menu key was pressed during a prompt, execute it now. + if (g_pendingCmd) { + char c = g_pendingCmd; + g_pendingCmd = 0; + Serial.println(); + Serial.print(F("[Pending command executed] ")); + Serial.println(c); + handleCommand(c); + return; + } + if (Serial.available()) { char c = Serial.read(); - if (c == '\r' || c == '\n') { - // ignore pure newlines - return; - } - c = toupper(c); - Serial.println(c); // echo - - bool recognized = true; - - switch (c) { - case '1': testResults[T_POWER] = runTest_Power(); break; - case '2': testResults[T_INPUTS] = runTest_Inputs(); break; - case '3': testResults[T_LCD] = runTest_LCD(); break; - case '4': testResults[T_LEDS] = runTest_LEDs(); break; - case '5': testResults[T_SD] = runTest_SD(); break; - case '6': testResults[T_DFPLAYER] = runTest_DFPlayer(); break; - case '7': testResults[T_WIFI_AP] = runTest_WifiAP(); break; - case '8': testResults[T_WIFI_STA] = runTest_WifiSTA(); break; - case 'A': testResults[T_LITTLEFS] = runTest_LittleFS(); break; - case 'B': testResults[T_UART0] = runTest_UART0(); break; - case 'C': testResults[T_SPI] = runTest_SPI(); break; - case 'D': testResults[T_RS232] = runTest_RS232(); break; - case 'P': runAllTests(); break; - case 'R': - Serial.println(F("Rebooting...")); - delay(200); - ESP.restart(); - break; - default: - recognized = false; - Serial.println(F("Unknown command.")); - break; - } + if (c == '\r' || c == '\n') return; - if (recognized && c != 'P' && c != 'R') { - printSummary(); - } - printMenu(); + // echo + Serial.println((char)toupper((unsigned char)c)); + handleCommand(c); } } + From c28031b9bfb878636ed97c19261b389b741d69be Mon Sep 17 00:00:00 2001 From: nk25719 Date: Sat, 27 Dec 2025 21:51:48 +0200 Subject: [PATCH 06/18] At v0.4.0 bugs fixed. --- .../FactoryTest_wMenu/FactoryTest_wMenu.ino | 495 +++++++++--------- 1 file changed, 241 insertions(+), 254 deletions(-) diff --git a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino index dabbd85..aa8e117 100644 --- a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino +++ b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino @@ -4,42 +4,32 @@ File: FactoryTest_wMenu.ino Project: Krake / GPAD v2 – Factory Test Firmware Document Type: Source Code (Factory Test) Document ID: KRAKE-FT-ESP32-FT01 -Version: v0.3.0 +Version: v0.4.0 Date: 2025-12-27 Author(s): Nagham Kheir, Public Invention Status: Draft ------------------------------------------------------------------------------ Revision History: -| Version | Date | Author | Description | -|---------|-----------|---------------|-----------------------------------------------| -| v0.1.0 | 2025-12-03| N. Kheir | Initial unified factory test code | -| v0.2.0 | 2025-12-27| N. Kheir | Add operator confirmation for LCD/LED tests | -| v0.3.0 | 2025-12-27| N. Kheir | Fix Serial prompt input (line-based), | -| | | | abort prompts via menu keys, | -| | | | and DO NOT play DF audio if SD missing | +| Version | Date | Author | Description | +|---------|-----------|---------------|-------------------------------------------------| +| v0.1.0 | 2025-12-03| N. Kheir | Initial unified factory test code | +| v0.2.0 | 2025-12-27| N. Kheir | Operator confirmation for LCD/LED | +| v0.3.0 | 2025-12-27| N. Kheir | Prompt/menu abort handling improvements | +| v0.3.1 | 2025-12-27| N. Kheir | SD gating to avoid DF actions with no SD | +| v0.4.0 | 2025-12-27| N. Kheir | Cleaned: robust Y/N (Enter optional), | +| | | | safer Run-All (break on pending command), | +| | | | clearer I2C reporting, safer DF/SD flow | ------------------------------------------------------------------------------ Overview: -- Executes a repeatable factory test sequence for ESP32-WROOM-32D Krake/GPAD v2 - boards, validating: - 1) Power / ID - 2) Inputs (rotary encoder + push button) - 3) LCD (I²C, 20x4) [I2C addr detect + operator Y/N] - 4) LEDs / Lamps [operator Y/N] - 5) SD card via DFPlayer - 6) DFPlayer + Speaker [ONLY if SD has files + operator Y/N] - 7) Wi-Fi AP - 8) Wi-Fi STA (SSID & PASS entered via Serial console) - A) LittleFS Read/Write - B) UART0 (USB Serial, operator confirmation) - C) SPI loopback (MOSI <-> MISO jumper) - D) RS-232 loopback (TX <-> RX on DB9, via MAX3232) -- Operator interacts via USB Serial at 115200 baud. -- Intended for FACTORY USE ONLY (not for field deployment). +- Repeatable factory test sequence for ESP32-WROOM-32D Krake/GPAD v2 boards. +- Operator interacts via USB Serial @ 115200 baud. +- Y/N prompts accept Y/N immediately (Enter optional). CR/LF both supported. +- Menu keys (1..8/A..D/P/R) typed at the start of a prompt abort that step + (marks FAIL) and the command runs next. ------------------------------------------------------------------------------ -IMPORTANT BEHAVIOR: -- All operator prompts are LINE-BASED: type Y/N then press Enter. -- If you press a menu key (1..8/A..D/P/R) at the beginning of a prompt, - the prompt ABORTS (step FAILs) and that command runs next. +Build notes: +- Adjust pin mapping to match your PCB (especially UART1 pins). +- If LAMP2 shares DFPlayer BUSY, we skip driving LAMP2 in the LED test. ------------------------------------------------------------------------------ */ @@ -52,19 +42,36 @@ IMPORTANT BEHAVIOR: #include #include -// ----------------------------------------------------------------------------- +// ============================================================================ +// Configuration +// ============================================================================ + +static const uint32_t SERIAL_BAUD = 115200; +static const uint32_t PROMPT_TIMEOUT_MS = 25000; +static const uint32_t WIFI_CONNECT_MS = 20000; + +// If true: during LED test we do NOT drive LAMP2 because it may be DF BUSY +static const bool SKIP_DRIVING_LAMP2 = true; + +// LCD expected I2C addresses (common backpacks). Add/remove as needed. +static const uint8_t LCD_ALLOWED_ADDRS[] = { 0x27, 0x3F, 0x38 }; + +// Enable/disable optional tests +static const bool ENABLE_RS232_TEST = true; + +// ============================================================================ // Hardware configuration (ADJUST to match Krake / GPAD v2 wiring) -// ----------------------------------------------------------------------------- +// ============================================================================ // Lamps / LEDs -const int LED_D9 = 23; // Mute LED on PMD / general status -const int LAMP1 = 15; // Lamp 1 (e.g. cold food) -const int LAMP2 = 4; // Lamp 2 (often DFPlayer BUSY input on some builds) -const int LAMP3 = 5; // Lamp 3 (also SPI CS) -const int LAMP4 = 18; // Lamp 4 (also SPI SCK) -const int LAMP5 = 19; // Lamp 5 (also SPI MISO) - -// Rotary encoder (input) +const int LED_D9 = 23; // Mute LED / general status +const int LAMP1 = 15; // Lamp 1 +const int LAMP2 = 4; // Lamp 2 (often DFPlayer BUSY on some builds) +const int LAMP3 = 5; // Lamp 3 (also SPI CS) +const int LAMP4 = 18; // Lamp 4 (also SPI SCK) +const int LAMP5 = 19; // Lamp 5 (also SPI MISO) + +// Rotary encoder (input-only pins OK on ESP32) const int ENC_CLK = 39; const int ENC_DT = 36; const int ENC_SW = 34; @@ -72,36 +79,28 @@ const int ENC_SW = 34; // DFPlayer / speaker (UART2) const int DF_TXD2 = 17; // ESP32 TX2 -> DFPlayer RX const int DF_RXD2 = 16; // ESP32 RX2 <- DFPlayer TX -const int DF_BUSY_IN = LAMP2; // active LOW when playing (if wired) +const int DF_BUSY_IN = LAMP2; // Active LOW when playing (if wired) -// RS-232 (UART1) – ADJUST TO YOUR BOARD (pins via MAX3232 to DB9) -// WARNING: In this sample, UART1_RXD1 conflicts with LAMP1 (GPIO15). -// Fix by changing one of them to your actual board mapping. -const int UART1_TXD1 = 2; // ESP32 TX1 -> MAX3232 -> DB9 -const int UART1_RXD1 = 15; // ESP32 RX1 <- MAX3232 <- DB9 (CONFLICT with LAMP1!) -const long UART1_BAUD = 115200; - -// SPI (VSPI) – shared with lamps on Krake header +// SPI (VSPI) const int SPI_MOSI_PIN = LED_D9; // 23 const int SPI_MISO_PIN = LAMP5; // 19 const int SPI_SCK_PIN = LAMP4; // 18 const int SPI_CS_PIN = LAMP3; // 5 -// LCD expected I2C addresses (adjust if your backpack differs) -const uint8_t LCD_ALLOWED_ADDRS[] = { 0x27, 0x3F, 0x38 }; - -// If LAMP2 is physically wired to DFPlayer BUSY, DO NOT drive it as an output. -const bool SKIP_DRIVING_LAMP2 = true; +// RS-232 (UART1) – IMPORTANT: set these to YOUR PCB pins (via MAX3232) +// NOTE: Do NOT set these to GPIO15 if you use LAMP1=15. +const int UART1_TXD1 = 27; // placeholder safe GPIO +const int UART1_RXD1 = 26; // placeholder safe GPIO +const long UART1_BAUD = 115200; -// ----------------------------------------------------------------------------- +// ============================================================================ // DFPlayer & test bookkeeping -// ----------------------------------------------------------------------------- +// ============================================================================ HardwareSerial dfSerial(2); DFRobotDFPlayerMini dfPlayer; bool dfInitialized = false; -// Test indices (keep order as actual test order) enum TestIndex { T_POWER = 0, T_INPUTS, @@ -135,13 +134,16 @@ const char* TEST_NAMES[T_COUNT] = { bool testResults[T_COUNT] = { false }; -// ----------------------------------------------------------------------------- +// ============================================================================ // Serial helpers + pending command capture -// ----------------------------------------------------------------------------- +// ============================================================================ + static char g_pendingCmd = 0; +static char up(char c) { return (char)toupper((unsigned char)c); } + static bool isMenuKey(char c) { - c = toupper((unsigned char)c); + c = up(c); return (c >= '1' && c <= '8') || (c >= 'A' && c <= 'D') || c == 'P' || c == 'R'; } @@ -149,10 +151,8 @@ static void flushSerialRx() { while (Serial.available()) (void)Serial.read(); } -// Read a line, but if the FIRST typed char is a menu key, abort and queue it. -// Returns: -// - true : line completed by Enter OR timeout reached (out may be empty) -// - false : aborted by menu key (g_pendingCmd set) +// Read a line with menu-abort: if FIRST char is menu key, abort and queue it. +// Returns true if line ended (CR/LF) or timeout; false if aborted by menu key. static bool readLineOrMenuAbort(String &out, uint32_t timeoutMs = 15000) { out = ""; uint32_t start = millis(); @@ -160,64 +160,78 @@ static bool readLineOrMenuAbort(String &out, uint32_t timeoutMs = 15000) { while (millis() - start < timeoutMs) { while (Serial.available()) { char c = Serial.read(); - if (c == '\r') continue; + if (c == '\n' || c == '\r') return true; // accept CR or LF - // If first char is a menu key, abort this input capture - if (out.length() == 0 && c != '\n' && isMenuKey(c)) { - g_pendingCmd = (char)toupper((unsigned char)c); + if (out.length() == 0 && isMenuKey(c)) { + g_pendingCmd = up(c); return false; } - if (c == '\n') return true; // line complete out += c; } delay(5); } - - return true; // timeout (out may be empty or partial) + return true; // timeout (out may be empty) } -// Ask operator Y/N using LINE input (type then Enter). -// If menu key is pressed as first char, prompt aborts (FAIL) and command runs next. +// Bulletproof Y/N: +// - Accepts Y/N immediately (Enter optional) +// - CR/LF supported +// - If menu key is pressed at the start, aborts step (FAIL) and queues command static bool promptYesNo(const __FlashStringHelper* question, - uint32_t timeoutMs = 15000, + uint32_t timeoutMs = PROMPT_TIMEOUT_MS, bool defaultNo = true) { Serial.println(question); - Serial.println(F("Type Y then press Enter to PASS, N then Enter to FAIL.")); + Serial.println(F("Press Y to PASS or N to FAIL (Enter optional).")); Serial.println(F("(Menu keys abort this step and run next.)")); Serial.print(F("> ")); - String line; - bool completed = readLineOrMenuAbort(line, timeoutMs); - if (!completed) { - Serial.println(F("\nAborted by menu key -> FAIL for this step.")); - return false; - } + uint32_t start = millis(); + String buf; - line.trim(); - if (line.length() == 0) { - Serial.println(defaultNo ? F("No response -> FAIL") : F("No response -> PASS")); - return !defaultNo; - } + while (millis() - start < timeoutMs) { + while (Serial.available()) { + char c = Serial.read(); - char c = (char)toupper((unsigned char)line[0]); - if (c == 'Y') return true; - if (c == 'N') return false; + // Newline: if buffer has content, evaluate it + if (c == '\n' || c == '\r') { + buf.trim(); + if (buf.length() == 0) continue; // ignore empty Enter + char k = up(buf[0]); + if (k == 'Y') return true; + if (k == 'N') return false; + if (isMenuKey(k)) { g_pendingCmd = k; Serial.println(F("\nAborted -> FAIL.")); return false; } + Serial.println(F("\nUnknown input -> FAIL")); + return false; + } - if (isMenuKey(c)) { - g_pendingCmd = c; - Serial.println(F("Menu key captured -> FAIL for this step.")); - return false; + // First char menu key => abort now + if (buf.length() == 0 && isMenuKey(c)) { + g_pendingCmd = up(c); + Serial.println(F("\nAborted -> FAIL.")); + return false; + } + + // Immediate Y/N (no Enter needed) + char u = up(c); + if (u == 'Y') { Serial.println(F("Y")); return true; } + if (u == 'N') { Serial.println(F("N")); return false; } + + // Otherwise buffer + buf += c; + } + delay(5); } - Serial.println(F("Unknown input -> FAIL")); - return false; + Serial.println(defaultNo ? F("\nNo response -> FAIL") : F("\nNo response -> PASS")); + return !defaultNo; } -// ----------------------------------------------------------------------------- +// ============================================================================ // UI -// ----------------------------------------------------------------------------- -void printBanner() { +// ============================================================================ + +static void printBanner() { Serial.println(); Serial.println(F("=======================================")); Serial.println(F(" KRAKE FACTORY TEST - ESP32-WROOM ")); @@ -229,19 +243,17 @@ void printBanner() { Serial.println(); } -void printSummary() { +static void printSummary() { Serial.println(); Serial.println(F("========== FACTORY TEST SUMMARY ==========")); for (int i = 0; i < T_COUNT; ++i) { - Serial.printf("%-33s : %s\n", - TEST_NAMES[i], - testResults[i] ? "PASS" : "FAIL"); + Serial.printf("%-33s : %s\n", TEST_NAMES[i], testResults[i] ? "PASS" : "FAIL"); } Serial.println(F("==========================================")); Serial.println(); } -void printMenu() { +static void printMenu() { Serial.println(F("Test menu (order matters):")); Serial.println(F(" 1 Power / ID")); Serial.println(F(" 2 Inputs (Encoder / Button)")); @@ -261,28 +273,27 @@ void printMenu() { Serial.print(F("Enter command: ")); } -// ----------------------------------------------------------------------------- -// Individual tests -// ----------------------------------------------------------------------------- +// ============================================================================ +// Tests +// ============================================================================ -// 1) Power / ID -bool runTest_Power() { +static bool runTest_Power() { Serial.println(F("\n[1] Power / ID")); printBanner(); Serial.println(F("Power OK: MCU running and Serial is active.")); return true; } -// 2) Inputs (Encoder / Button) -bool runTest_Inputs() { +static bool runTest_Inputs() { Serial.println(F("\n[2] Inputs (Encoder / Button)")); + pinMode(ENC_CLK, INPUT); pinMode(ENC_DT, INPUT); pinMode(ENC_SW, INPUT_PULLUP); Serial.println(F("Rotate encoder CLOCKWISE, then COUNTER-CLOCKWISE,")); Serial.println(F("then PRESS the encoder button within 10 seconds.")); - delay(500); + delay(200); int lastCLK = digitalRead(ENC_CLK); bool sawCW = false; @@ -306,72 +317,75 @@ bool runTest_Inputs() { if (!sawPress && digitalRead(ENC_SW) == LOW) { sawPress = true; Serial.println(F(" Detected encoder button press")); - delay(150); // basic debounce + delay(150); } if (sawCW && sawCCW && sawPress) break; } - bool pass = sawCW && sawCCW && sawPress; - Serial.printf("Inputs test: %s\n", - pass ? "PASS" : "FAIL (missing rotation and/or press)"); + bool pass = (sawCW && sawCCW && sawPress); + Serial.printf("Inputs test: %s\n", pass ? "PASS" : "FAIL (missing rotation and/or press)"); return pass; } -// 3) LCD (I2C) -bool runTest_LCD() { +static bool addrInAllowList(uint8_t addr) { + for (uint8_t i = 0; i < sizeof(LCD_ALLOWED_ADDRS); ++i) { + if (LCD_ALLOWED_ADDRS[i] == addr) return true; + } + return false; +} + +static bool runTest_LCD() { Serial.println(F("\n[3] LCD (I2C 20x4)")); Wire.begin(); - delay(100); + delay(80); - uint8_t foundAddr = 0; - Serial.println(F("Scanning I2C bus for LCD addresses (0x27, 0x3F, 0x38)...")); + // Full scan for operator visibility + Serial.println(F("Scanning I2C bus (show all devices)...")); + bool any = false; + uint8_t firstAllowed = 0; - for (uint8_t i = 0; i < sizeof(LCD_ALLOWED_ADDRS); ++i) { - uint8_t addr = LCD_ALLOWED_ADDRS[i]; + for (uint8_t addr = 1; addr < 127; ++addr) { Wire.beginTransmission(addr); if (Wire.endTransmission() == 0) { - Serial.printf(" Found LCD candidate at 0x%02X\n", addr); - foundAddr = addr; - break; + any = true; + Serial.printf(" I2C device at 0x%02X%s\n", addr, addrInAllowList(addr) ? " (allowed LCD addr)" : ""); + if (!firstAllowed && addrInAllowList(addr)) firstAllowed = addr; } } - if (!foundAddr) { - Serial.println(F("No LCD detected at expected addresses. LCD test FAIL.")); + if (!any) { + Serial.println(F("No I2C devices found. LCD test FAIL.")); + return false; + } + + if (!firstAllowed) { + Serial.println(F("No LCD found at expected addresses (0x27/0x3F/0x38). LCD test FAIL.")); return false; } - LiquidCrystal_I2C lcd(foundAddr, 20, 4); + // Try to write something; final PASS is operator-confirmed + LiquidCrystal_I2C lcd(firstAllowed, 20, 4); lcd.init(); lcd.backlight(); lcd.clear(); - lcd.setCursor(0, 0); - lcd.print("KRAKE FACTORY TEST"); - lcd.setCursor(0, 1); - lcd.print("LCD OK @ 0x"); - lcd.print(foundAddr, HEX); - lcd.setCursor(0, 2); - lcd.print("All 4 lines visible?"); - lcd.setCursor(0, 3); - lcd.print("Type Y/N in Serial"); - - Serial.println(F("LCD initialized and test message written.")); - flushSerialRx(); - bool ok = promptYesNo(F("Visually confirm LCD shows the test message (all 4 lines)."), - 20000, /*defaultNo*/ true); + lcd.setCursor(0, 0); lcd.print("KRAKE FACTORY TEST"); + lcd.setCursor(0, 1); lcd.print("LCD @ 0x"); lcd.print(firstAllowed, HEX); + lcd.setCursor(0, 2); lcd.print("Confirm display OK"); + lcd.setCursor(0, 3); lcd.print("Press Y/N on Serial"); + Serial.println(F("LCD initialized and message written.")); + bool ok = promptYesNo(F("Visually confirm LCD shows the message on all lines."), PROMPT_TIMEOUT_MS, true); Serial.printf("LCD test: %s\n", ok ? "PASS" : "FAIL"); return ok; } -// 4) LEDs / Lamps -bool runTest_LEDs() { +static bool runTest_LEDs() { Serial.println(F("\n[4] LEDs / Lamps")); - int pins[] = { LAMP1, LAMP2, LAMP3, LAMP4, LAMP5, LED_D9 }; + const int pins[] = { LAMP1, LAMP2, LAMP3, LAMP4, LAMP5, LED_D9 }; const char* names[] = { "LAMP1", "LAMP2 (maybe DF BUSY)", @@ -393,32 +407,24 @@ bool runTest_LEDs() { Serial.println(F(" -> Skipping LAMP2 drive (BUSY shared safety)")); continue; } - Serial.print(F(" -> ")); - Serial.print(names[i]); - Serial.println(F(" blink")); - digitalWrite(pins[i], HIGH); - delay(300); - digitalWrite(pins[i], LOW); - delay(150); + Serial.print(F(" -> ")); Serial.print(names[i]); Serial.println(F(" blink")); + digitalWrite(pins[i], HIGH); delay(300); + digitalWrite(pins[i], LOW); delay(150); } - flushSerialRx(); - bool ok = promptYesNo(F("Did you see the LEDs/Lamps blink as expected?"), - 20000, /*defaultNo*/ true); - + bool ok = promptYesNo(F("Did you see the LEDs/Lamps blink as expected?"), PROMPT_TIMEOUT_MS, true); Serial.printf("LEDs/Lamps test: %s\n", ok ? "PASS" : "FAIL"); return ok; } -// DFPlayer init helper -bool initDFPlayer() { +static bool initDFPlayer() { if (dfInitialized) return true; Serial.println(F("Initializing DFPlayer (UART2)...")); pinMode(DF_BUSY_IN, INPUT_PULLUP); dfSerial.begin(9600, SERIAL_8N1, DF_RXD2, DF_TXD2); - delay(500); + delay(400); if (!dfPlayer.begin(dfSerial, true, false)) { Serial.println(F(" DFPlayer not detected. Check wiring & power.")); @@ -426,27 +432,35 @@ bool initDFPlayer() { } dfPlayer.setTimeOut(500); - dfPlayer.volume(20); // 0..30 + dfPlayer.volume(20); dfInitialized = true; Serial.println(F(" DFPlayer detected and initialized.")); return true; } -// 5) SD (via DFPlayer) -bool runTest_SD() { +// SD test is gated: if SD not inserted, we do NOT query DFPlayer for file counts. +static bool runTest_SD() { Serial.println(F("\n[5] SD (DFPlayer card)")); + + // Gate: avoids DF queries when SD is absent (and avoids “hang” behavior) + if (!promptYesNo(F("Is the SD card inserted with audio files?"), PROMPT_TIMEOUT_MS, true)) { + Serial.println(F("SD test: FAIL (operator says SD not inserted).")); + return false; + } + if (!initDFPlayer()) { Serial.println(F("SD test FAIL (no DFPlayer).")); return false; } + Serial.println(F("Querying SD file count...")); int fileCount = dfPlayer.readFileCounts(); - Serial.printf(" DFPlayer reports %d files on SD card.\n", fileCount); + Serial.printf(" DFPlayer reports %d files.\n", fileCount); if (fileCount < 0) { - Serial.println(F("SD test FAIL: DFPlayer could not read SD/file index.")); - Serial.println(F("Common causes: no SD / SD not FAT32 / bad contacts / power issue.")); + Serial.println(F("SD test FAIL: could not read SD/file index.")); + Serial.println(F("Check: FAT32 format, good contacts, stable power.")); return false; } @@ -455,28 +469,25 @@ bool runTest_SD() { return pass; } -// 6) DFPlayer + Speaker (ONLY if SD is readable and has files) -bool runTest_DFPlayer() { +static bool runTest_DFPlayer() { Serial.println(F("\n[6] DFPlayer + Speaker")); if (!initDFPlayer()) { - Serial.println(F("DFPlayer/Speaker test FAIL (no DFPlayer).")); + Serial.println(F("DFPlayer test FAIL (no DFPlayer).")); return false; } + // Must have SD + files (otherwise playing is pointless / can misbehave) int fileCount = dfPlayer.readFileCounts(); Serial.printf(" SD file count = %d\n", fileCount); - if (fileCount <= 0) { Serial.println(F("No readable SD/files -> NOT playing audio. DFPlayer test FAIL.")); - Serial.println(F("Fix SD first (FAT32 + files present), then retry.")); return false; } - Serial.println(F("Playing test track #1 for ~3 seconds...")); + Serial.println(F("Playing track #1 for ~3 seconds...")); dfPlayer.play(1); - // progress dots so it never looks frozen unsigned long start = millis(); while (millis() - start < 3000UL) { if (((millis() - start) % 250) < 10) Serial.print('.'); @@ -487,21 +498,16 @@ bool runTest_DFPlayer() { dfPlayer.stop(); delay(200); - flushSerialRx(); - bool ok = promptYesNo(F("Did you hear audio from the speaker?"), - 20000, /*defaultNo*/ true); - + bool ok = promptYesNo(F("Did you hear audio from the speaker?"), PROMPT_TIMEOUT_MS, true); Serial.printf("DFPlayer/Speaker test: %s\n", ok ? "PASS" : "FAIL"); return ok; } -// 7) Wi-Fi AP -bool runTest_WifiAP() { +static bool runTest_WifiAP() { Serial.println(F("\n[7] Wi-Fi AP")); WiFi.mode(WIFI_AP); - String mac = WiFi.macAddress(); - mac.replace(":", ""); + String mac = WiFi.macAddress(); mac.replace(":", ""); String ssid = "KRAKE_" + mac; bool ok = WiFi.softAP(ssid.c_str(), "krakefactory"); @@ -511,26 +517,23 @@ bool runTest_WifiAP() { } IPAddress ip = WiFi.softAPIP(); - Serial.print(F("AP SSID: ")); - Serial.println(ssid); - Serial.print(F("AP IP: ")); - Serial.println(ip); - Serial.println(F("Operator: connect from phone/PC and verify AP is visible.")); + Serial.print(F("AP SSID: ")); Serial.println(ssid); + Serial.print(F("AP IP: ")); Serial.println(ip); + Serial.println(F("Operator: verify AP is visible from phone/PC.")); return true; } -// 8) Wi-Fi STA (manual SSID/PASS via Serial) with menu-abort -bool runTest_WifiSTA() { +static bool runTest_WifiSTA() { Serial.println(F("\n[8] Wi-Fi STA (manual SSID/PASS)")); WiFi.mode(WIFI_STA); WiFi.disconnect(true, true); - delay(500); + delay(300); + + flushSerialRx(); // only here (line entry) - flushSerialRx(); Serial.println(F("Enter Wi-Fi SSID then press Enter (15 s timeout):")); Serial.print(F("> ")); - String ssid; if (!readLineOrMenuAbort(ssid, 15000)) { Serial.println(F("\nAborted by menu key.")); @@ -538,27 +541,24 @@ bool runTest_WifiSTA() { } ssid.trim(); if (ssid.length() == 0) { - Serial.println(F("No SSID entered. Wi-Fi STA test: FAIL.")); + Serial.println(F("No SSID entered. Wi-Fi STA test FAIL.")); return false; } Serial.println(F("Enter Wi-Fi PASSWORD then press Enter (can be empty):")); Serial.print(F("> ")); - String pass; if (!readLineOrMenuAbort(pass, 15000)) { Serial.println(F("\nAborted by menu key.")); return false; } - pass.trim(); // allow empty - - Serial.print(F("Connecting to: ")); - Serial.println(ssid); + pass.trim(); + Serial.print(F("Connecting to: ")); Serial.println(ssid); WiFi.begin(ssid.c_str(), pass.c_str()); uint32_t start = millis(); - while (WiFi.status() != WL_CONNECTED && millis() - start < 20000UL) { + while (WiFi.status() != WL_CONNECTED && millis() - start < WIFI_CONNECT_MS) { delay(500); Serial.print('.'); } @@ -570,19 +570,15 @@ bool runTest_WifiSTA() { } Serial.println(F("Wi-Fi STA connected successfully.")); - Serial.print(F("IP: ")); - Serial.println(WiFi.localIP()); - Serial.print(F("RSSI: ")); - Serial.println(WiFi.RSSI()); - + Serial.print(F("IP: ")); Serial.println(WiFi.localIP()); + Serial.print(F("RSSI: ")); Serial.println(WiFi.RSSI()); return true; } -// A) LittleFS R/W -bool runTest_LittleFS() { +static bool runTest_LittleFS() { Serial.println(F("\n[A] LittleFS R/W")); - if (!LittleFS.begin(true)) { // true = format if mount fails + if (!LittleFS.begin(true)) { Serial.println(F("LittleFS mount FAILED.")); return false; } @@ -590,9 +586,7 @@ bool runTest_LittleFS() { const char* path = "/factory_test.txt"; const char* payload = "KRAKE_FACTORY_TEST"; - Serial.print(F("Writing file ")); - Serial.println(path); - + Serial.print(F("Writing ")); Serial.println(path); File f = LittleFS.open(path, FILE_WRITE); if (!f) { Serial.println(F("Failed to open file for write.")); @@ -601,7 +595,7 @@ bool runTest_LittleFS() { f.print(payload); f.close(); - Serial.println(F("Reading file back...")); + Serial.println(F("Reading back...")); f = LittleFS.open(path, FILE_READ); if (!f) { Serial.println(F("Failed to open file for read.")); @@ -611,22 +605,18 @@ bool runTest_LittleFS() { f.close(); bool ok = (readBack == payload); - Serial.printf("LittleFS R/W test: %s\n", ok ? "PASS" : "FAIL (content mismatch)"); + Serial.printf("LittleFS test: %s\n", ok ? "PASS" : "FAIL (content mismatch)"); return ok; } -// B) UART0 (USB Serial) -bool runTest_UART0() { +static bool runTest_UART0() { Serial.println(F("\n[B] UART0 (USB Serial)")); - flushSerialRx(); - bool ok = promptYesNo(F("UART0 check: can you see this prompt and respond?"), - 20000, /*defaultNo*/ true); + bool ok = promptYesNo(F("UART0 check: can you see this prompt and respond?"), PROMPT_TIMEOUT_MS, true); Serial.printf("UART0 test: %s\n", ok ? "PASS" : "FAIL"); return ok; } -// C) SPI loopback (MOSI <-> MISO) -bool runTest_SPI() { +static bool runTest_SPI() { Serial.println(F("\n[C] SPI loopback")); Serial.println(F("Connect MOSI <-> MISO on SPI header for automatic test.")); Serial.printf("Using pins: MOSI=%d, MISO=%d, SCK=%d, CS=%d\n", @@ -644,25 +634,23 @@ bool runTest_SPI() { Serial.printf("SPI sent 0x%02X, received 0x%02X\n", out, in); bool ok = (in == out); - Serial.printf("SPI loopback test: %s\n", ok ? "PASS" : "FAIL (check loopback wiring)"); + Serial.printf("SPI loopback test: %s\n", ok ? "PASS" : "FAIL (check jumper)"); return ok; } -// D) RS-232 loopback (UART1) -bool runTest_RS232() { +static bool runTest_RS232() { + if (!ENABLE_RS232_TEST) { + Serial.println(F("\n[D] RS-232 loopback disabled (ENABLE_RS232_TEST=false)")); + return false; + } + Serial.println(F("\n[D] RS-232 loopback (UART1)")); Serial.println(F("Connect RS-232 TX <-> RX at DB9 (pins 2 and 3) via MAX3232.")); - Serial.printf("Using UART1 TXD=%d, RXD=%d, BAUD=%ld\n", - UART1_TXD1, UART1_RXD1, UART1_BAUD); - - if (UART1_RXD1 == LAMP1) { - Serial.println(F("WARNING: UART1_RXD1 conflicts with LAMP1 (GPIO15).")); - Serial.println(F(" Update pin mapping to match your PCB.")); - } + Serial.printf("Using UART1 TXD=%d, RXD=%d, BAUD=%ld\n", UART1_TXD1, UART1_RXD1, UART1_BAUD); HardwareSerial rs232(1); rs232.begin(UART1_BAUD, SERIAL_8N1, UART1_RXD1, UART1_TXD1); - delay(200); + delay(150); const uint8_t pattern = 0x55; unsigned long start = millis(); @@ -674,23 +662,20 @@ bool runTest_RS232() { delay(20); if (rs232.available()) { int b = rs232.read(); - if (b == pattern) { - ok = true; - break; - } + if (b == pattern) { ok = true; break; } } } - rs232.end(); - Serial.printf("RS-232 loopback test: %s\n", - ok ? "PASS" : "FAIL (check wiring / MAX3232)"); + rs232.end(); + Serial.printf("RS-232 loopback test: %s\n", ok ? "PASS" : "FAIL (wiring/MAX3232/pins)"); return ok; } -// ----------------------------------------------------------------------------- -// Test dispatcher -// ----------------------------------------------------------------------------- -bool runSingleTestFromIndex(TestIndex idx) { +// ============================================================================ +// Dispatcher / Run All +// ============================================================================ + +static bool runSingleTestFromIndex(TestIndex idx) { switch (idx) { case T_POWER: return runTest_Power(); case T_INPUTS: return runTest_Inputs(); @@ -708,27 +693,33 @@ bool runSingleTestFromIndex(TestIndex idx) { } } -void runAllTests() { +static void runAllTests() { Serial.println(F("\n[P] Running ALL tests (1 -> D) in order...")); for (int i = 0; i < T_COUNT; ++i) { + // If operator pressed a menu key during any prompt, stop Run-All and execute it + if (g_pendingCmd) break; + + // If SD failed, skip DFPlayer play test if (i == T_DFPLAYER && testResults[T_SD] == false) { Serial.println(F("\n[6] DFPlayer + Speaker")); Serial.println(F("Skipping audio play because SD test FAILED.")); testResults[T_DFPLAYER] = false; continue; } + testResults[i] = runSingleTestFromIndex(static_cast(i)); } printSummary(); } -// ----------------------------------------------------------------------------- +// ============================================================================ // Command handler -// ----------------------------------------------------------------------------- +// ============================================================================ + static void handleCommand(char c) { - c = toupper((unsigned char)c); + c = up(c); bool recognized = true; switch (c) { @@ -756,26 +747,23 @@ static void handleCommand(char c) { break; } - if (recognized && c != 'P' && c != 'R') { - printSummary(); - } + if (recognized && c != 'P' && c != 'R') printSummary(); printMenu(); } -// ----------------------------------------------------------------------------- +// ============================================================================ // Arduino entry points -// ----------------------------------------------------------------------------- +// ============================================================================ + void setup() { - Serial.begin(115200); + Serial.begin(SERIAL_BAUD); - // Do not block forever waiting for Serial (some hosts never assert it) + // Do not block forever waiting for Serial uint32_t t0 = millis(); - while (!Serial && (millis() - t0 < 2000)) { - delay(10); - } + while (!Serial && (millis() - t0 < 2000)) delay(10); WiFi.mode(WIFI_OFF); - delay(100); + delay(50); printBanner(); printSummary(); @@ -798,8 +786,7 @@ void loop() { char c = Serial.read(); if (c == '\r' || c == '\n') return; - // echo - Serial.println((char)toupper((unsigned char)c)); + Serial.println(up(c)); // echo handleCommand(c); } } From 7b777a6342beae7ae78a692b8f57f97b0fa6fcb5 Mon Sep 17 00:00:00 2001 From: Forrest Erickson Date: Sun, 28 Dec 2025 06:15:38 -0500 Subject: [PATCH 07/18] FactoryTest_wMenu.ino, Add date and time of build to Serial Splash (banner). --- .../factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino index aa8e117..efa1433 100644 --- a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino +++ b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino @@ -1,3 +1,4 @@ +#define FIRMWARE_VERSION "v0.4.0" /* ------------------------------------------------------------------------------ File: FactoryTest_wMenu.ino @@ -235,8 +236,12 @@ static void printBanner() { Serial.println(); Serial.println(F("=======================================")); Serial.println(F(" KRAKE FACTORY TEST - ESP32-WROOM ")); + Serial.print("Firmware Version: "); + Serial.println(F(FIRMWARE_VERSION)); Serial.println(F("=======================================")); - Serial.printf("Chip revision: %d\n", ESP.getChipRevision()); + Serial.print("Compiled at: "); + Serial.println(F(__DATE__ " " __TIME__)); //compile date that is used for a unique identifier + Serial.printf("Chip revision: %d\n", ESP.getChipRevision()); Serial.printf("Flash size: %u bytes\n", ESP.getFlashChipSize()); Serial.print(F("MAC (STA): ")); Serial.println(WiFi.macAddress()); From 4faa8da147fc44833f396ee9e4143af5e76d71fb Mon Sep 17 00:00:00 2001 From: nk25719 Date: Sun, 28 Dec 2025 21:37:58 +0200 Subject: [PATCH 08/18] debugging and cleaning the factory test for the dfplayer sdcard and speaker. --- .../FactoryTest_wMenu/FactoryTest_wMenu.ino | 186 +++++++++++------- 1 file changed, 110 insertions(+), 76 deletions(-) diff --git a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino index efa1433..b761009 100644 --- a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino +++ b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino @@ -5,7 +5,7 @@ File: FactoryTest_wMenu.ino Project: Krake / GPAD v2 – Factory Test Firmware Document Type: Source Code (Factory Test) Document ID: KRAKE-FT-ESP32-FT01 -Version: v0.4.0 +Version: v0.4.1 Date: 2025-12-27 Author(s): Nagham Kheir, Public Invention Status: Draft @@ -17,16 +17,18 @@ Revision History: | v0.2.0 | 2025-12-27| N. Kheir | Operator confirmation for LCD/LED | | v0.3.0 | 2025-12-27| N. Kheir | Prompt/menu abort handling improvements | | v0.3.1 | 2025-12-27| N. Kheir | SD gating to avoid DF actions with no SD | -| v0.4.0 | 2025-12-27| N. Kheir | Cleaned: robust Y/N (Enter optional), | -| | | | safer Run-All (break on pending command), | -| | | | clearer I2C reporting, safer DF/SD flow | +| v0.4.0 | 2025-12-27| N. Kheir | Robust Y/N, safer Run-All, clearer I2C, DF/SD | +| v0.4.1 | 2025-12-27| N. Kheir | Cleanup: remove duplicate DF flags, fix | +| | | | LCD allow-list loop, clean line input parsing | ------------------------------------------------------------------------------ Overview: - Repeatable factory test sequence for ESP32-WROOM-32D Krake/GPAD v2 boards. - Operator interacts via USB Serial @ 115200 baud. -- Y/N prompts accept Y/N immediately (Enter optional). CR/LF both supported. -- Menu keys (1..8/A..D/P/R) typed at the start of a prompt abort that step +- Y/N prompts accept Y/N immediately (Enter optional). CR/LF supported. +- During prompts, menu keys typed as the FIRST character abort that step (marks FAIL) and the command runs next. +- WiFi SSID/PASS input does NOT abort just because SSID starts with A/B/C/D. + It aborts only when the entire line is exactly one menu key (e.g., "C"+Enter). ------------------------------------------------------------------------------ Build notes: - Adjust pin mapping to match your PCB (especially UART1 pins). @@ -47,17 +49,12 @@ Build notes: // Configuration // ============================================================================ -static const uint32_t SERIAL_BAUD = 115200; -static const uint32_t PROMPT_TIMEOUT_MS = 25000; -static const uint32_t WIFI_CONNECT_MS = 20000; +static const uint32_t SERIAL_BAUD = 115200; +static const uint32_t PROMPT_TIMEOUT_MS = 65000; +static const uint32_t WIFI_CONNECT_MS = 20000; -// If true: during LED test we do NOT drive LAMP2 because it may be DF BUSY -static const bool SKIP_DRIVING_LAMP2 = true; - -// LCD expected I2C addresses (common backpacks). Add/remove as needed. +static const bool SKIP_DRIVING_LAMP2 = true; // shared with DF BUSY on some builds static const uint8_t LCD_ALLOWED_ADDRS[] = { 0x27, 0x3F, 0x38 }; - -// Enable/disable optional tests static const bool ENABLE_RS232_TEST = true; // ============================================================================ @@ -89,18 +86,27 @@ const int SPI_SCK_PIN = LAMP4; // 18 const int SPI_CS_PIN = LAMP3; // 5 // RS-232 (UART1) – IMPORTANT: set these to YOUR PCB pins (via MAX3232) -// NOTE: Do NOT set these to GPIO15 if you use LAMP1=15. -const int UART1_TXD1 = 27; // placeholder safe GPIO -const int UART1_RXD1 = 26; // placeholder safe GPIO +const int UART1_TXD1 = 27; // placeholder safe GPIO +const int UART1_RXD1 = 26; // placeholder safe GPIO const long UART1_BAUD = 115200; // ============================================================================ -// DFPlayer & test bookkeeping +// DFPlayer bookkeeping + SD cache // ============================================================================ HardwareSerial dfSerial(2); DFRobotDFPlayerMini dfPlayer; -bool dfInitialized = false; + +enum DfState { DF_UNKNOWN, DF_OK, DF_FAIL }; +static DfState dfState = DF_UNKNOWN; + +// SD cache: read file count ONCE during test [5], reuse during test [6] +static bool g_sdChecked = false; +static int g_sdFileCount = -999; + +// ============================================================================ +// Test bookkeeping +// ============================================================================ enum TestIndex { T_POWER = 0, @@ -152,8 +158,15 @@ static void flushSerialRx() { while (Serial.available()) (void)Serial.read(); } -// Read a line with menu-abort: if FIRST char is menu key, abort and queue it. -// Returns true if line ended (CR/LF) or timeout; false if aborted by menu key. +/* + Read a line (SSID/PASS) with “menu abort” behavior: + - We DO NOT abort on first character anymore (SSID can start with C, etc.) + - We abort ONLY if the *entire entered line* is exactly ONE menu key, + e.g., user types "C" then Enter to jump to SPI test. + Returns: + - true = line completed (or timeout) + - false = aborted by menu key (queued into g_pendingCmd) +*/ static bool readLineOrMenuAbort(String &out, uint32_t timeoutMs = 15000) { out = ""; uint32_t start = millis(); @@ -161,24 +174,34 @@ static bool readLineOrMenuAbort(String &out, uint32_t timeoutMs = 15000) { while (millis() - start < timeoutMs) { while (Serial.available()) { char c = Serial.read(); - if (c == '\n' || c == '\r') return true; // accept CR or LF - if (out.length() == 0 && isMenuKey(c)) { - g_pendingCmd = up(c); - return false; + if (c == '\n' || c == '\r') { + out.trim(); + + if (out.length() == 1 && isMenuKey(out[0])) { + g_pendingCmd = up(out[0]); + out = ""; + return false; // aborted + } + + return true; // completed normally } out += c; } delay(5); } + + out.trim(); return true; // timeout (out may be empty) } -// Bulletproof Y/N: -// - Accepts Y/N immediately (Enter optional) -// - CR/LF supported -// - If menu key is pressed at the start, aborts step (FAIL) and queues command +/* + Bulletproof Y/N: + - Accepts Y/N immediately (Enter optional) + - CR/LF supported + - If menu key is pressed as first char, aborts step (FAIL) and queues command +*/ static bool promptYesNo(const __FlashStringHelper* question, uint32_t timeoutMs = PROMPT_TIMEOUT_MS, bool defaultNo = true) { @@ -194,31 +217,29 @@ static bool promptYesNo(const __FlashStringHelper* question, while (Serial.available()) { char c = Serial.read(); - // Newline: if buffer has content, evaluate it if (c == '\n' || c == '\r') { buf.trim(); - if (buf.length() == 0) continue; // ignore empty Enter + if (buf.length() == 0) continue; + char k = up(buf[0]); if (k == 'Y') return true; if (k == 'N') return false; if (isMenuKey(k)) { g_pendingCmd = k; Serial.println(F("\nAborted -> FAIL.")); return false; } + Serial.println(F("\nUnknown input -> FAIL")); return false; } - // First char menu key => abort now if (buf.length() == 0 && isMenuKey(c)) { g_pendingCmd = up(c); Serial.println(F("\nAborted -> FAIL.")); return false; } - // Immediate Y/N (no Enter needed) char u = up(c); if (u == 'Y') { Serial.println(F("Y")); return true; } if (u == 'N') { Serial.println(F("N")); return false; } - // Otherwise buffer buf += c; } delay(5); @@ -334,7 +355,8 @@ static bool runTest_Inputs() { } static bool addrInAllowList(uint8_t addr) { - for (uint8_t i = 0; i < sizeof(LCD_ALLOWED_ADDRS); ++i) { + const size_t n = sizeof(LCD_ALLOWED_ADDRS) / sizeof(LCD_ALLOWED_ADDRS[0]); + for (size_t i = 0; i < n; ++i) { if (LCD_ALLOWED_ADDRS[i] == addr) return true; } return false; @@ -346,7 +368,6 @@ static bool runTest_LCD() { Wire.begin(); delay(80); - // Full scan for operator visibility Serial.println(F("Scanning I2C bus (show all devices)...")); bool any = false; uint8_t firstAllowed = 0; @@ -364,13 +385,11 @@ static bool runTest_LCD() { Serial.println(F("No I2C devices found. LCD test FAIL.")); return false; } - if (!firstAllowed) { Serial.println(F("No LCD found at expected addresses (0x27/0x3F/0x38). LCD test FAIL.")); return false; } - // Try to write something; final PASS is operator-confirmed LiquidCrystal_I2C lcd(firstAllowed, 20, 4); lcd.init(); lcd.backlight(); @@ -423,69 +442,88 @@ static bool runTest_LEDs() { } static bool initDFPlayer() { - if (dfInitialized) return true; + if (dfState == DF_OK) return true; + if (dfState == DF_FAIL) return false; Serial.println(F("Initializing DFPlayer (UART2)...")); pinMode(DF_BUSY_IN, INPUT_PULLUP); dfSerial.begin(9600, SERIAL_8N1, DF_RXD2, DF_TXD2); - delay(400); + delay(300); - if (!dfPlayer.begin(dfSerial, true, false)) { + // doReset=true helps a LOT with SD indexing reliability + if (!dfPlayer.begin(dfSerial, true, true)) { Serial.println(F(" DFPlayer not detected. Check wiring & power.")); + dfState = DF_FAIL; return false; } - dfPlayer.setTimeOut(500); + dfPlayer.setTimeOut(1000); // give replies more time (some modules are slow) dfPlayer.volume(20); - dfInitialized = true; - Serial.println(F(" DFPlayer detected and initialized.")); + // Force device selection (important on some clones) + dfPlayer.outputDevice(DFPLAYER_DEVICE_SD); + delay(1200); // let TF card mount + index after reset/device select + + dfState = DF_OK; + Serial.println(F(" DFPlayer detected and initialized (SD selected).")); return true; } -// SD test is gated: if SD not inserted, we do NOT query DFPlayer for file counts. static bool runTest_SD() { Serial.println(F("\n[5] SD (DFPlayer card)")); - // Gate: avoids DF queries when SD is absent (and avoids “hang” behavior) + // reset SD cache each time test [5] runs (so it stays consistent per run) + g_sdChecked = false; + g_sdFileCount = -999; + if (!promptYesNo(F("Is the SD card inserted with audio files?"), PROMPT_TIMEOUT_MS, true)) { Serial.println(F("SD test: FAIL (operator says SD not inserted).")); + g_sdChecked = true; + g_sdFileCount = -1; return false; } if (!initDFPlayer()) { - Serial.println(F("SD test FAIL (no DFPlayer).")); + Serial.println(F("SD test: FAIL (DFPlayer not detected).")); + g_sdChecked = true; + g_sdFileCount = -1; return false; } - Serial.println(F("Querying SD file count...")); - int fileCount = dfPlayer.readFileCounts(); - Serial.printf(" DFPlayer reports %d files.\n", fileCount); + Serial.println(F("Reading SD file count (once)...")); + g_sdFileCount = dfPlayer.readFileCounts(); - if (fileCount < 0) { - Serial.println(F("SD test FAIL: could not read SD/file index.")); - Serial.println(F("Check: FAT32 format, good contacts, stable power.")); - return false; + if (g_sdFileCount < 0) { + Serial.println(F(" No reply / SD not ready. Waiting 1.5s and retrying ONCE...")); + delay(1500); + g_sdFileCount = dfPlayer.readFileCounts(); } - bool pass = (fileCount > 0); - Serial.printf("SD test: %s\n", pass ? "PASS" : "FAIL (no files on SD)"); - return pass; + g_sdChecked = true; + Serial.printf(" DFPlayer reports %d files.\n", g_sdFileCount); + + + Serial.println(F("SD test: PASS")); + return true; } static bool runTest_DFPlayer() { Serial.println(F("\n[6] DFPlayer + Speaker")); if (!initDFPlayer()) { - Serial.println(F("DFPlayer test FAIL (no DFPlayer).")); + Serial.println(F("DFPlayer test: FAIL (DFPlayer not detected).")); return false; } - // Must have SD + files (otherwise playing is pointless / can misbehave) - int fileCount = dfPlayer.readFileCounts(); - Serial.printf(" SD file count = %d\n", fileCount); - if (fileCount <= 0) { + if (!g_sdChecked) { + Serial.println(F("DFPlayer test requires SD test first.")); + Serial.println(F("Run [5] SD test, then run [6].")); + return false; + } + + Serial.printf(" Using cached SD file count = %d\n", g_sdFileCount); + if (g_sdFileCount <= 0) { Serial.println(F("No readable SD/files -> NOT playing audio. DFPlayer test FAIL.")); return false; } @@ -493,12 +531,10 @@ static bool runTest_DFPlayer() { Serial.println(F("Playing track #1 for ~3 seconds...")); dfPlayer.play(1); - unsigned long start = millis(); - while (millis() - start < 3000UL) { - if (((millis() - start) % 250) < 10) Serial.print('.'); + uint32_t start = millis(); + while (millis() - start < 3000) { delay(10); } - Serial.println(); dfPlayer.stop(); delay(200); @@ -535,12 +571,12 @@ static bool runTest_WifiSTA() { WiFi.disconnect(true, true); delay(300); - flushSerialRx(); // only here (line entry) + flushSerialRx(); - Serial.println(F("Enter Wi-Fi SSID then press Enter (15 s timeout):")); + Serial.println(F("Enter Wi-Fi SSID then press Enter (25 s timeout):")); Serial.print(F("> ")); String ssid; - if (!readLineOrMenuAbort(ssid, 15000)) { + if (!readLineOrMenuAbort(ssid, 25000)) { Serial.println(F("\nAborted by menu key.")); return false; } @@ -550,10 +586,10 @@ static bool runTest_WifiSTA() { return false; } - Serial.println(F("Enter Wi-Fi PASSWORD then press Enter (can be empty):")); + Serial.println(F("Enter Wi-Fi PASSWORD then press Enter (25 s timeout, can be empty):")); Serial.print(F("> ")); String pass; - if (!readLineOrMenuAbort(pass, 15000)) { + if (!readLineOrMenuAbort(pass, 25000)) { Serial.println(F("\nAborted by menu key.")); return false; } @@ -702,10 +738,9 @@ static void runAllTests() { Serial.println(F("\n[P] Running ALL tests (1 -> D) in order...")); for (int i = 0; i < T_COUNT; ++i) { - // If operator pressed a menu key during any prompt, stop Run-All and execute it if (g_pendingCmd) break; - // If SD failed, skip DFPlayer play test + // skip audio play if SD failed if (i == T_DFPLAYER && testResults[T_SD] == false) { Serial.println(F("\n[6] DFPlayer + Speaker")); Serial.println(F("Skipping audio play because SD test FAILED.")); @@ -794,5 +829,4 @@ void loop() { Serial.println(up(c)); // echo handleCommand(c); } -} - +} \ No newline at end of file From 4acac73249713b7068ead173f6ac0f9fa019ff12 Mon Sep 17 00:00:00 2001 From: nk25719 Date: Mon, 29 Dec 2025 20:13:07 +0200 Subject: [PATCH 09/18] bump[ing the firmware version to indicate updates. --- Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino index b761009..d0a38a0 100644 --- a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino +++ b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino @@ -1,4 +1,4 @@ -#define FIRMWARE_VERSION "v0.4.0" +#define FIRMWARE_VERSION "v0.4.1" /* ------------------------------------------------------------------------------ File: FactoryTest_wMenu.ino From 66b385c6d23f08d3028388cb0b67186fb57cb091 Mon Sep 17 00:00:00 2001 From: nk25719 Date: Thu, 1 Jan 2026 20:06:48 +0200 Subject: [PATCH 10/18] FactoryTest_wMenu, UART1 and SPI factory test loopback working. --- .../FactoryTest_wMenu/FactoryTest_wMenu.ino | 136 +++++++++++++----- .../TestUARTLoopBack/TestUARTLoopBack.ino | 6 +- 2 files changed, 104 insertions(+), 38 deletions(-) diff --git a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino index d0a38a0..14f5603 100644 --- a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino +++ b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino @@ -62,12 +62,12 @@ static const bool ENABLE_RS232_TEST = true; // ============================================================================ // Lamps / LEDs -const int LED_D9 = 23; // Mute LED / general status -const int LAMP1 = 15; // Lamp 1 -const int LAMP2 = 4; // Lamp 2 (often DFPlayer BUSY on some builds) -const int LAMP3 = 5; // Lamp 3 (also SPI CS) -const int LAMP4 = 18; // Lamp 4 (also SPI SCK) -const int LAMP5 = 19; // Lamp 5 (also SPI MISO) +const int LED_Status = 13; // Mute LED / general status +const int LAMP1 = 12; // Lamp 1 +const int LAMP2 = 14; // Lamp 2 (often DFPlayer BUSY on some builds) +const int LAMP3 = 27; // Lamp 3 (also SPI CS) +const int LAMP4 = 26; // Lamp 4 (also SPI SCK) +const int LAMP5 = 25; // Lamp 5 (also SPI MISO) // Rotary encoder (input-only pins OK on ESP32) const int ENC_CLK = 39; @@ -80,15 +80,21 @@ const int DF_RXD2 = 16; // ESP32 RX2 <- DFPlayer TX const int DF_BUSY_IN = LAMP2; // Active LOW when playing (if wired) // SPI (VSPI) -const int SPI_MOSI_PIN = LED_D9; // 23 -const int SPI_MISO_PIN = LAMP5; // 19 -const int SPI_SCK_PIN = LAMP4; // 18 -const int SPI_CS_PIN = LAMP3; // 5 +const int SPI_MOSI_PIN = 23 ; +const int SPI_MISO_PIN = 19 ; +const int SPI_SCK_PIN = 18 ; +const int SPI_CS_PIN = 5 ; +// ===== TEST PARAMETERS ===== +static const uint32_t SPI_SPEED = 1000000; // 1 MHz (safe for factory) +static const uint8_t TEST_PATTERN[] = { + 0x55, 0xAA, 0x00, 0xFF, 0x12, 0x34, 0xA5 +}; // RS-232 (UART1) – IMPORTANT: set these to YOUR PCB pins (via MAX3232) -const int UART1_TXD1 = 27; // placeholder safe GPIO -const int UART1_RXD1 = 26; // placeholder safe GPIO +const int UART1_TXD1 = 2; // placeholder safe GPIO +const int UART1_RXD1 = 15; // placeholder safe GPIO const long UART1_BAUD = 115200; +static const uint32_t TIMEOUT_MS = 600; // ============================================================================ // DFPlayer bookkeeping + SD cache @@ -409,14 +415,14 @@ static bool runTest_LCD() { static bool runTest_LEDs() { Serial.println(F("\n[4] LEDs / Lamps")); - const int pins[] = { LAMP1, LAMP2, LAMP3, LAMP4, LAMP5, LED_D9 }; + const int pins[] = { LAMP1, LAMP2, LAMP3, LAMP4, LAMP5, LED_Status }; const char* names[] = { "LAMP1", - "LAMP2 (maybe DF BUSY)", - "LAMP3 (SPI_CS)", - "LAMP4 (SPI_SCK)", - "LAMP5 (SPI_MISO)", - "LED_D9 (SPI_MOSI)" + "LAMP2 (LAMP2)", + "LAMP3 (LAMP3)", + "LAMP4 (LAMP4)", + "LAMP5 (LAMP5)", + "LED_Status (LED_Status)" }; for (int i = 0; i < 6; ++i) { @@ -665,49 +671,107 @@ static bool runTest_SPI() { SPI.begin(SPI_SCK_PIN, SPI_MISO_PIN, SPI_MOSI_PIN, SPI_CS_PIN); pinMode(SPI_CS_PIN, OUTPUT); + + digitalWrite(SPI_CS_PIN, HIGH); + delay(20); + + Serial.println("[SPI] Starting RJ12 loopback test"); + + SPI.beginTransaction(SPISettings(SPI_SPEED, MSBFIRST, SPI_MODE0)); digitalWrite(SPI_CS_PIN, LOW); - SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0)); - uint8_t out = 0xA5; - uint8_t in = SPI.transfer(out); - SPI.endTransaction(); + bool pass = true; + + for (size_t i = 0; i < sizeof(TEST_PATTERN); i++) { + uint8_t tx = TEST_PATTERN[i]; + uint8_t rx = SPI.transfer(tx); + + if (rx != tx) { + Serial.printf("[SPI] FAIL at byte %u: TX=0x%02X RX=0x%02X\n", + (unsigned)i, tx, rx); + pass = false; + break; + } + } + digitalWrite(SPI_CS_PIN, HIGH); + SPI.endTransaction(); + SPI.end(); + + // if (pass) { + // Serial.println("[SPI] PASS: RJ12 loopback OK"); + // } + // return pass; - Serial.printf("SPI sent 0x%02X, received 0x%02X\n", out, in); - bool ok = (in == out); + Serial.printf("SPI sent 0x%02X, received 0x%02X\n", RX, TX); + bool ok = (RX == RX); Serial.printf("SPI loopback test: %s\n", ok ? "PASS" : "FAIL (check jumper)"); return ok; } static bool runTest_RS232() { + if (!ENABLE_RS232_TEST) { Serial.println(F("\n[D] RS-232 loopback disabled (ENABLE_RS232_TEST=false)")); return false; } Serial.println(F("\n[D] RS-232 loopback (UART1)")); - Serial.println(F("Connect RS-232 TX <-> RX at DB9 (pins 2 and 3) via MAX3232.")); + Serial.println(F("Connected RS-232 TX <-> RX at DB9 (pins 2 and 3) via MAX3232.")); Serial.printf("Using UART1 TXD=%d, RXD=%d, BAUD=%ld\n", UART1_TXD1, UART1_RXD1, UART1_BAUD); HardwareSerial rs232(1); rs232.begin(UART1_BAUD, SERIAL_8N1, UART1_RXD1, UART1_TXD1); delay(150); + // Clear any pending garbage + while (rs232.available()) rs232.read(); + + const char *payload = "KRAKE_rs232_UART1_LOOPBACK_123\r\n"; + const size_t n = strlen(payload); + + // Send + size_t written = rs232.write((const uint8_t*)payload, n); + rs232.flush(); + if (written != n) { + Serial.println("[rs232] FAIL: write length mismatch"); + rs232.end(); + return false; + } - const uint8_t pattern = 0x55; - unsigned long start = millis(); - bool ok = false; - - while (millis() - start < 3000UL) { - rs232.write(pattern); - rs232.flush(); - delay(20); - if (rs232.available()) { - int b = rs232.read(); - if (b == pattern) { ok = true; break; } + // Receive + String rx; + uint32_t t0 = millis(); + while (millis() - t0 < TIMEOUT_MS && rx.length() < n) { + while (rs232.available()) { + char c = (char)rs232.read(); + rx += c; } + delay(2); } rs232.end(); + + // Validate + if (rx.length() != n) { + Serial.printf("[rs232] FAIL: expected %u bytes, got %u\n", + (unsigned)n, (unsigned)rx.length()); + Serial.println("Tip: ensure rs232 TX<->RX loopback plug is installed."); + return false; + } + + if (rx != payload) { + Serial.println("[rs232] FAIL: payload mismatch"); + Serial.print("Sent: "); Serial.print(payload); + Serial.print("Recv: "); Serial.print(rx); + return false; + } + + Serial.print("Sent: "); Serial.print(payload); + Serial.print("Recv: "); Serial.print(rx); + Serial.println("[rs232] PASS: UART1 loopback OK"); + return true; + + bool ok = (rx == payload) ; Serial.printf("RS-232 loopback test: %s\n", ok ? "PASS" : "FAIL (wiring/MAX3232/pins)"); return ok; } diff --git a/Firmware/factoryTest/UART/TestUARTLoopBack/TestUARTLoopBack.ino b/Firmware/factoryTest/UART/TestUARTLoopBack/TestUARTLoopBack.ino index b6d3dda..faa9e94 100644 --- a/Firmware/factoryTest/UART/TestUARTLoopBack/TestUARTLoopBack.ino +++ b/Firmware/factoryTest/UART/TestUARTLoopBack/TestUARTLoopBack.ino @@ -10,7 +10,9 @@ This program demonstrates use of all three UARTs on the ESP32. Modified by Robert L. Read to perform a loop back, Dec. 15, 2024 Modified to make UART 1 pins GPIO 2 and 5 by Forrest Lee Erickson, Dec. 16, 2024 -Modified to add UART 2 on pins GPIO 16 and 17 by Forrest Lee Erickson, Dec. 16, 2024 +Modified to add UART 2 on pins GPIO 16 and 17 by of UART1 by Nagham Kheir, Jan. 1, 2026 + + **/ // Define TX and RX pins for UART (change if needed) @@ -63,7 +65,7 @@ void setup() { void loop() { while (mySerialUART1.available()) { - Serial.println("Bytes Available UART1! :"); + // Serial.println(" Bytes Available UART1!"); // coomented out Nagham 20260101 Serial.write(mySerialUART1.read()); } From c98eb14fb5ebc814c6b7c099ab6f0fc7ecd71672 Mon Sep 17 00:00:00 2001 From: nk25719 Date: Fri, 2 Jan 2026 00:23:53 +0200 Subject: [PATCH 11/18] SPI Backloop working, updated dfplayer, sd card and speaker tests. --- .../FactoryTest_wMenu/FactoryTest_wMenu.ino | 219 ++++++++++++------ 1 file changed, 152 insertions(+), 67 deletions(-) diff --git a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino index 14f5603..c0a3e52 100644 --- a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino +++ b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino @@ -1,4 +1,4 @@ -#define FIRMWARE_VERSION "v0.4.1" +#define FIRMWARE_VERSION "v0.4.2.1" /* ------------------------------------------------------------------------------ File: FactoryTest_wMenu.ino @@ -95,17 +95,12 @@ const int UART1_TXD1 = 2; // placeholder safe GPIO const int UART1_RXD1 = 15; // placeholder safe GPIO const long UART1_BAUD = 115200; static const uint32_t TIMEOUT_MS = 600; - -// ============================================================================ + // DFPlayer bookkeeping + SD cache -// ============================================================================ - HardwareSerial dfSerial(2); DFRobotDFPlayerMini dfPlayer; - -enum DfState { DF_UNKNOWN, DF_OK, DF_FAIL }; -static DfState dfState = DF_UNKNOWN; - +enum dfState { DF_UNKNOWN, DF_OK, DF_FAIL }; +static bool dfState = DF_UNKNOWN; // SD cache: read file count ONCE during test [5], reuse during test [6] static bool g_sdChecked = false; static int g_sdFileCount = -999; @@ -119,8 +114,9 @@ enum TestIndex { T_INPUTS, T_LCD, T_LEDS, - T_SD, T_DFPLAYER, + T_SD, + T_Speaker, T_WIFI_AP, T_WIFI_STA, T_LITTLEFS, @@ -131,12 +127,13 @@ enum TestIndex { }; const char* TEST_NAMES[T_COUNT] = { - "1 Power / ID", - "2 Inputs (Encoder / Button)", - "3 LCD (I2C)", - "4 LEDs / Lamps", + "0 Power / ID", + "1 Inputs (Encoder / Button)", + "2 LCD (I2C)", + "3 LEDs / Lamps", + "4 DFPlayer ", "5 SD (DFPlayer card)", - "6 DFPlayer + Speaker", + "6 Speaker", "7 Wi-Fi AP", "8 Wi-Fi STA (manual SSID/PASS)", "A LittleFS R/W", @@ -164,15 +161,7 @@ static void flushSerialRx() { while (Serial.available()) (void)Serial.read(); } -/* - Read a line (SSID/PASS) with “menu abort” behavior: - - We DO NOT abort on first character anymore (SSID can start with C, etc.) - - We abort ONLY if the *entire entered line* is exactly ONE menu key, - e.g., user types "C" then Enter to jump to SPI test. - Returns: - - true = line completed (or timeout) - - false = aborted by menu key (queued into g_pendingCmd) -*/ + static bool readLineOrMenuAbort(String &out, uint32_t timeoutMs = 15000) { out = ""; uint32_t start = millis(); @@ -202,12 +191,7 @@ static bool readLineOrMenuAbort(String &out, uint32_t timeoutMs = 15000) { return true; // timeout (out may be empty) } -/* - Bulletproof Y/N: - - Accepts Y/N immediately (Enter optional) - - CR/LF supported - - If menu key is pressed as first char, aborts step (FAIL) and queues command -*/ + static bool promptYesNo(const __FlashStringHelper* question, uint32_t timeoutMs = PROMPT_TIMEOUT_MS, bool defaultNo = true) { @@ -287,12 +271,13 @@ static void printSummary() { static void printMenu() { Serial.println(F("Test menu (order matters):")); - Serial.println(F(" 1 Power / ID")); - Serial.println(F(" 2 Inputs (Encoder / Button)")); - Serial.println(F(" 3 LCD (I2C)")); - Serial.println(F(" 4 LEDs / Lamps")); + Serial.println(F(" 0 Power / ID")); + Serial.println(F(" 1 Inputs (Encoder / Button)")); + Serial.println(F(" 2 LCD (I2C)")); + Serial.println(F(" 3 LEDs / Lamps")); + Serial.println(F(" 4 DFPlayer")); Serial.println(F(" 5 SD (DFPlayer card)")); - Serial.println(F(" 6 DFPlayer + Speaker")); + Serial.println(F(" 6 Speaker")); Serial.println(F(" 7 Wi-Fi AP")); Serial.println(F(" 8 Wi-Fi STA (manual SSID/PASS)")); Serial.println(F(" A LittleFS R/W")); @@ -447,7 +432,8 @@ static bool runTest_LEDs() { return ok; } -static bool initDFPlayer() { + + static bool initDFPlayer() { if (dfState == DF_OK) return true; if (dfState == DF_FAIL) return false; @@ -463,32 +449,116 @@ static bool initDFPlayer() { dfState = DF_FAIL; return false; } - + dfPlayer.setTimeOut(1000); // give replies more time (some modules are slow) - dfPlayer.volume(20); + dfState = DF_OK; + Serial.println(F(" DFPlayer detected and initialized (SD selected).")); + return true; +} + + +// Put this OUTSIDE of runTest_DFPlayer() (global scope). +// Call it when dfPlayer.available() is true. +void printDetail(uint8_t type, int value) { + switch (type) { + case TimeOut: + Serial.println(F("Time Out!")); + break; + case WrongStack: + Serial.println(F("Stack Wrong!")); + break; + case DFPlayerCardInserted: + Serial.println(F("Card Inserted!")); + break; + case DFPlayerCardRemoved: + Serial.println(F("Card Removed!")); + break; + case DFPlayerCardOnline: + Serial.println(F("Card Online!")); + break; + case DFPlayerUSBInserted: + Serial.println("USB Inserted!"); + break; + case DFPlayerUSBRemoved: + Serial.println("USB Removed!"); + break; + case DFPlayerPlayFinished: + Serial.print(F("Number:")); + Serial.print(value); + Serial.println(F(" Play Finished!")); + break; + case DFPlayerError: + Serial.print(F("DFPlayerError:")); + switch (value) { + case Busy: + Serial.println(F("Card not found")); + break; + case Sleeping: + Serial.println(F("Sleeping")); + break; + case SerialWrongStack: + Serial.println(F("Get Wrong Stack")); + break; + case CheckSumNotMatch: + Serial.println(F("Check Sum Not Match")); + break; + case FileIndexOut: + Serial.println(F("File Index Out of Bound")); + break; + case FileMismatch: + Serial.println(F("Cannot Find File")); + break; + case Advertise: + Serial.println(F("In Advertise")); + break; + default: + break; + } + break; + default: + break; + } +} + + +static bool runTest_DFPlayer() { + // If you want live DFPlayer diagnostics, call this elsewhere during waits: + // if (dfPlayer.available()) printDetail(dfPlayer.readType(), dfPlayer.read()); + + if (!initDFPlayer()) { + Serial.println(F(" DFPlayer initializing Failed (DFPlayer not detected).")); + return false; + } + + // Serial.println(F("\n[6] DFPlayer + Speaker")); + Serial.println(F("DFPlayer Mini online.")); + return true; // Force device selection (important on some clones) dfPlayer.outputDevice(DFPLAYER_DEVICE_SD); delay(1200); // let TF card mount + index after reset/device select - dfState = DF_OK; - Serial.println(F(" DFPlayer detected and initialized (SD selected).")); - return true; + bool ok = initDFPlayer(); + Serial.printf("DFPLAYER Initialized: %s\n", ok ? "PASS" : "FAIL (wiring/MAX3232/pins)"); + // return ok; } static bool runTest_SD() { Serial.println(F("\n[5] SD (DFPlayer card)")); // reset SD cache each time test [5] runs (so it stays consistent per run) - g_sdChecked = false; - g_sdFileCount = -999; + // g_sdChecked = false; + // g_sdFileCount = -999; - if (!promptYesNo(F("Is the SD card inserted with audio files?"), PROMPT_TIMEOUT_MS, true)) { - Serial.println(F("SD test: FAIL (operator says SD not inserted).")); - g_sdChecked = true; - g_sdFileCount = -1; - return false; - } + g_sdChecked = true; + g_sdFileCount = -1; + + // if (!promptYesNo(F("Is the SD card inserted with audio files?"), PROMPT_TIMEOUT_MS, true)) { + // Serial.println(F("SD test: FAIL (operator says SD not inserted).")); + // g_sdChecked = true; + // g_sdFileCount = -1; + // return false; + // } if (!initDFPlayer()) { Serial.println(F("SD test: FAIL (DFPlayer not detected).")); @@ -497,7 +567,7 @@ static bool runTest_SD() { return false; } - Serial.println(F("Reading SD file count (once)...")); + Serial.println(F("Reading SD file count (once)...")); g_sdFileCount = dfPlayer.readFileCounts(); if (g_sdFileCount < 0) { @@ -509,36 +579,50 @@ static bool runTest_SD() { g_sdChecked = true; Serial.printf(" DFPlayer reports %d files.\n", g_sdFileCount); - - Serial.println(F("SD test: PASS")); + if (g_sdFileCount <= 0) { + Serial.println(F("SD test: FAIL (no readable files).")); + return false; + } return true; + + bool ok = runTest_SD(); + Serial.printf("SD test: %s\n", ok ? "PASS" : "FAIL"); + + void printDetail(uint8_t type, int value); + } -static bool runTest_DFPlayer() { - Serial.println(F("\n[6] DFPlayer + Speaker")); +static bool runTest_Speaker() { + Serial.println(F("Playing track #1 for ~3 seconds...")); if (!initDFPlayer()) { - Serial.println(F("DFPlayer test: FAIL (DFPlayer not detected).")); + Serial.println(F("Speaker test: FAIL (DFPlayer not detected).")); return false; } if (!g_sdChecked) { Serial.println(F("DFPlayer test requires SD test first.")); - Serial.println(F("Run [5] SD test, then run [6].")); + Serial.println(F("Run [5] SD test, then run speaker test.")); return false; } Serial.printf(" Using cached SD file count = %d\n", g_sdFileCount); if (g_sdFileCount <= 0) { - Serial.println(F("No readable SD/files -> NOT playing audio. DFPlayer test FAIL.")); + Serial.println(F("No readable SD/files -> NOT playing audio. Speaker test FAIL.")); return false; } + const int VOL = 20; + dfPlayer.volume(VOL); + Serial.printf("DFPlayer volume set to: %d\n", VOL); - Serial.println(F("Playing track #1 for ~3 seconds...")); dfPlayer.play(1); uint32_t start = millis(); while (millis() - start < 3000) { + // Optional: report DFPlayer events while playing + if (dfPlayer.available()) { + printDetail(dfPlayer.readType(), dfPlayer.read()); + } delay(10); } @@ -546,7 +630,7 @@ static bool runTest_DFPlayer() { delay(200); bool ok = promptYesNo(F("Did you hear audio from the speaker?"), PROMPT_TIMEOUT_MS, true); - Serial.printf("DFPlayer/Speaker test: %s\n", ok ? "PASS" : "FAIL"); + Serial.printf("Speaker test: %s\n", ok ? "PASS" : "FAIL"); return ok; } @@ -723,7 +807,7 @@ static bool runTest_RS232() { HardwareSerial rs232(1); rs232.begin(UART1_BAUD, SERIAL_8N1, UART1_RXD1, UART1_TXD1); delay(150); - // Clear any pending garbage + // Clear any pending garbage while (rs232.available()) rs232.read(); const char *payload = "KRAKE_rs232_UART1_LOOPBACK_123\r\n"; @@ -748,7 +832,6 @@ static bool runTest_RS232() { } delay(2); } - rs232.end(); // Validate @@ -786,8 +869,9 @@ static bool runSingleTestFromIndex(TestIndex idx) { case T_INPUTS: return runTest_Inputs(); case T_LCD: return runTest_LCD(); case T_LEDS: return runTest_LEDs(); - case T_SD: return runTest_SD(); case T_DFPLAYER: return runTest_DFPlayer(); + case T_SD: return runTest_SD(); + case T_Speaker: return runTest_Speaker(); case T_WIFI_AP: return runTest_WifiAP(); case T_WIFI_STA: return runTest_WifiSTA(); case T_LITTLEFS: return runTest_LittleFS(); @@ -827,12 +911,13 @@ static void handleCommand(char c) { bool recognized = true; switch (c) { - case '1': testResults[T_POWER] = runTest_Power(); break; - case '2': testResults[T_INPUTS] = runTest_Inputs(); break; - case '3': testResults[T_LCD] = runTest_LCD(); break; - case '4': testResults[T_LEDS] = runTest_LEDs(); break; + case '0': testResults[T_POWER] = runTest_Power(); break; + case '1': testResults[T_INPUTS] = runTest_Inputs(); break; + case '2': testResults[T_LCD] = runTest_LCD(); break; + case '3': testResults[T_LEDS] = runTest_LEDs(); break; + case '4': testResults[T_DFPLAYER] = runTest_DFPlayer(); break; case '5': testResults[T_SD] = runTest_SD(); break; - case '6': testResults[T_DFPLAYER] = runTest_DFPlayer(); break; + case '6': testResults[T_Speaker] = runTest_Speaker(); break; case '7': testResults[T_WIFI_AP] = runTest_WifiAP(); break; case '8': testResults[T_WIFI_STA] = runTest_WifiSTA(); break; case 'A': testResults[T_LITTLEFS] = runTest_LittleFS(); break; From 93a6b09be8f7dcfdb245d6ec788d62d54deba9e5 Mon Sep 17 00:00:00 2001 From: nk25719 Date: Fri, 2 Jan 2026 18:36:38 +0200 Subject: [PATCH 12/18] dfplayer code cleanup. --- .../FactoryTest_wMenu/FactoryTest_wMenu.ino | 91 +++++++++++++++++-- 1 file changed, 81 insertions(+), 10 deletions(-) diff --git a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino index c0a3e52..d44c97a 100644 --- a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino +++ b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino @@ -20,7 +20,10 @@ Revision History: | v0.4.0 | 2025-12-27| N. Kheir | Robust Y/N, safer Run-All, clearer I2C, DF/SD | | v0.4.1 | 2025-12-27| N. Kheir | Cleanup: remove duplicate DF flags, fix | | | | | LCD allow-list loop, clean line input parsing | ------------------------------------------------------------------------------- +|v0.4.1.1 | 2025-12-30| N. Kheir | UART1 backloop working and final. | +| v0.4.2 | 2026-1-1 | N. Kheir | SPI backloop working and final. DFP debug | +|v0.4.2.1 | 2026-1-1 | N. Kheir | DFplayer cleanup test. | +----------------------------------------------------------------------------------------| Overview: - Repeatable factory test sequence for ESP32-WROOM-32D Krake/GPAD v2 boards. - Operator interacts via USB Serial @ 115200 baud. @@ -433,8 +436,34 @@ static bool runTest_LEDs() { } - static bool initDFPlayer() { - if (dfState == DF_OK) return true; +static void clearDFPlayerCache() { + dfState = DF_UNKNOWN; + // optional: hard reset the UART session as well + dfSerial.end(); + delay(50); +} + +// Quick "ping" to verify the module is actually responding NOW. +// If module is removed, most DF commands return < 0 (no reply). +static bool dfPlayerResponding() { + int c = dfPlayer.readFileCounts(); + if (c >= 0) return true; + + // One small retry (some modules are slow / SD busy) + delay(150); + c = dfPlayer.readFileCounts(); + return (c >= 0); +} + +static bool initDFPlayer() { + + if (dfState == DF_OK) { + if (dfPlayerResponding()) return true; + + Serial.println(F(" DFPlayer was OK but no reply now -> clearing cache & re-init...")); + clearDFPlayerCache(); + } + if (dfState == DF_FAIL) return false; Serial.println(F("Initializing DFPlayer (UART2)...")); @@ -449,15 +478,52 @@ static bool runTest_LEDs() { dfState = DF_FAIL; return false; } - + dfPlayer.setTimeOut(1000); // give replies more time (some modules are slow) + // Force device selection (important on some clones) + dfPlayer.outputDevice(DFPLAYER_DEVICE_SD); + delay(1200); // let TF card mount + index after reset/device select + + // Optional: verify it actually replies after init + if (!dfPlayerResponding()) { + Serial.println(F(" DFPlayer init done, but still no reply -> FAIL")); + dfState = DF_FAIL; + return false; + } + dfState = DF_OK; Serial.println(F(" DFPlayer detected and initialized (SD selected).")); return true; } +// static bool initDFPlayer() { +// if (dfState == DF_OK) return true; +// if (dfState == DF_FAIL) return false; + +// Serial.println(F("Initializing DFPlayer (UART2)...")); +// pinMode(DF_BUSY_IN, INPUT_PULLUP); + +// dfSerial.begin(9600, SERIAL_8N1, DF_RXD2, DF_TXD2); +// delay(300); + +// // doReset=true helps a LOT with SD indexing reliability +// if (!dfPlayer.begin(dfSerial, true, true)) { +// Serial.println(F(" DFPlayer not detected. Check wiring & power.")); +// dfState = DF_FAIL; +// return false; +// }else { + +// dfState = DF_OK; +// Serial.println(F(" DFPlayer detected and initialized (SD selected).")); +// } + +// dfPlayer.setTimeOut(1000); // give replies more time (some modules are slow) +// return true; +// } + + // Put this OUTSIDE of runTest_DFPlayer() (global scope). // Call it when dfPlayer.available() is true. void printDetail(uint8_t type, int value) { @@ -522,25 +588,30 @@ void printDetail(uint8_t type, int value) { } -static bool runTest_DFPlayer() { +static bool runTest_DFPlayer(bool forceReinit = false) { // If you want live DFPlayer diagnostics, call this elsewhere during waits: // if (dfPlayer.available()) printDetail(dfPlayer.readType(), dfPlayer.read()); - + if (forceReinit) { + clearDFPlayerCache(); + } + if (!initDFPlayer()) { Serial.println(F(" DFPlayer initializing Failed (DFPlayer not detected).")); + dfState = DF_FAIL; return false; - } - + }else { // Serial.println(F("\n[6] DFPlayer + Speaker")); Serial.println(F("DFPlayer Mini online.")); + dfState = DF_OK; + } return true; // Force device selection (important on some clones) dfPlayer.outputDevice(DFPLAYER_DEVICE_SD); delay(1200); // let TF card mount + index after reset/device select - bool ok = initDFPlayer(); + bool ok = (dfState); Serial.printf("DFPLAYER Initialized: %s\n", ok ? "PASS" : "FAIL (wiring/MAX3232/pins)"); - // return ok; + return ok; } static bool runTest_SD() { From a314b97f5e48abef36e56fc34b79cf0220185b76 Mon Sep 17 00:00:00 2001 From: nk25719 Date: Fri, 2 Jan 2026 19:44:07 +0200 Subject: [PATCH 13/18] spi typo fix. --- Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino index d44c97a..f701625 100644 --- a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino +++ b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino @@ -859,7 +859,7 @@ static bool runTest_SPI() { // return pass; Serial.printf("SPI sent 0x%02X, received 0x%02X\n", RX, TX); - bool ok = (RX == RX); + bool ok = (RX == TX); Serial.printf("SPI loopback test: %s\n", ok ? "PASS" : "FAIL (check jumper)"); return ok; } From ac4b0f649d2cbab9811726cf67f76104bfd51642 Mon Sep 17 00:00:00 2001 From: nk25719 Date: Fri, 2 Jan 2026 20:06:40 +0200 Subject: [PATCH 14/18] fixed SPI typpo. --- .../FactoryTest_wMenu/FactoryTest_wMenu.ino | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino index f701625..2237fbc 100644 --- a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino +++ b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino @@ -829,7 +829,6 @@ static bool runTest_SPI() { digitalWrite(SPI_CS_PIN, HIGH); delay(20); - Serial.println("[SPI] Starting RJ12 loopback test"); SPI.beginTransaction(SPISettings(SPI_SPEED, MSBFIRST, SPI_MODE0)); @@ -853,13 +852,13 @@ static bool runTest_SPI() { SPI.endTransaction(); SPI.end(); - // if (pass) { - // Serial.println("[SPI] PASS: RJ12 loopback OK"); - // } - // return pass; - + if (pass) { + Serial.println("[SPI] PASS: RJ12 loopback OK"); + } + return pass; + Serial.printf("SPI sent 0x%02X, received 0x%02X\n", RX, TX); - bool ok = (RX == TX); + bool ok = runTest_UART0(); Serial.printf("SPI loopback test: %s\n", ok ? "PASS" : "FAIL (check jumper)"); return ok; } From 5e28ff2a990fe058f41b60b05dcf916aae46167c Mon Sep 17 00:00:00 2001 From: nk25719 Date: Fri, 2 Jan 2026 20:08:14 +0200 Subject: [PATCH 15/18] fixed SPi, version v 0.4.2.2 --- Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino index 2237fbc..3bc57f6 100644 --- a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino +++ b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino @@ -1,4 +1,4 @@ -#define FIRMWARE_VERSION "v0.4.2.1" +#define FIRMWARE_VERSION "v0.4.2.2" /* ------------------------------------------------------------------------------ File: FactoryTest_wMenu.ino @@ -23,6 +23,7 @@ Revision History: |v0.4.1.1 | 2025-12-30| N. Kheir | UART1 backloop working and final. | | v0.4.2 | 2026-1-1 | N. Kheir | SPI backloop working and final. DFP debug | |v0.4.2.1 | 2026-1-1 | N. Kheir | DFplayer cleanup test. | +|v0.4.2.2 | 2026-1-1 | N. Kheir | SPI cleanup test. | ----------------------------------------------------------------------------------------| Overview: - Repeatable factory test sequence for ESP32-WROOM-32D Krake/GPAD v2 boards. From 5dcb8b62911e96e8cece92e6472ee6f28ac43e54 Mon Sep 17 00:00:00 2001 From: Forrest Erickson Date: Mon, 5 Jan 2026 11:24:44 -0500 Subject: [PATCH 16/18] FactoryTest_wMenu.ino, Make the test menu into two colums for better fit in Serial Monitor during test.' --- .../FactoryTest_wMenu/FactoryTest_wMenu.ino | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino index 3bc57f6..30b6c6c 100644 --- a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino +++ b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino @@ -1,4 +1,4 @@ -#define FIRMWARE_VERSION "v0.4.2.2" +#define FIRMWARE_VERSION "v0.4.2.3" /* ------------------------------------------------------------------------------ File: FactoryTest_wMenu.ino @@ -23,7 +23,8 @@ Revision History: |v0.4.1.1 | 2025-12-30| N. Kheir | UART1 backloop working and final. | | v0.4.2 | 2026-1-1 | N. Kheir | SPI backloop working and final. DFP debug | |v0.4.2.1 | 2026-1-1 | N. Kheir | DFplayer cleanup test. | -|v0.4.2.2 | 2026-1-1 | N. Kheir | SPI cleanup test. | +|v0.4.2.2 | 2026-1-1 | N. Kheir | SPI cleanup test. | +|v0.4.2.3 | 2026-1-5 | L. Erickson | Make menu two colums. | ----------------------------------------------------------------------------------------| Overview: - Repeatable factory test sequence for ESP32-WROOM-32D Krake/GPAD v2 boards. @@ -274,24 +275,18 @@ static void printSummary() { } static void printMenu() { - Serial.println(F("Test menu (order matters):")); - Serial.println(F(" 0 Power / ID")); - Serial.println(F(" 1 Inputs (Encoder / Button)")); - Serial.println(F(" 2 LCD (I2C)")); - Serial.println(F(" 3 LEDs / Lamps")); - Serial.println(F(" 4 DFPlayer")); - Serial.println(F(" 5 SD (DFPlayer card)")); - Serial.println(F(" 6 Speaker")); - Serial.println(F(" 7 Wi-Fi AP")); - Serial.println(F(" 8 Wi-Fi STA (manual SSID/PASS)")); - Serial.println(F(" A LittleFS R/W")); - Serial.println(F(" B UART0 (USB Serial)")); - Serial.println(F(" C SPI loopback")); - Serial.println(F(" D RS-232 loopback")); - Serial.println(F(" P Run ALL (1 -> D)")); - Serial.println(F(" R Reboot")); - Serial.println(); - Serial.print(F("Enter command: ")); +Serial.println(F("Test menu (order matters):")); +Serial.println(F(" 0 Power / ID 1 Inputs (Encoder / Button)")); +Serial.println(F(" 2 LCD (I2C) 3 LEDs / Lamps")); +Serial.println(F(" 4 DFPlayer 5 SD (DFPlayer card)")); +Serial.println(F(" 6 Speaker 7 Wi-Fi AP")); +Serial.println(F(" 8 Wi-Fi STA (manual SSID/PASS) A LittleFS R/W")); +Serial.println(F(" B UART0 (USB Serial) C SPI loopback")); +Serial.println(F(" D RS-232 loopback P Run ALL (1 -> D)")); +Serial.println(F(" R Reboot")); +Serial.println(); +Serial.print(F("Enter command: ")); + } // ============================================================================ From ed0c91f247a38ebe69ddf42a14730395a1198826 Mon Sep 17 00:00:00 2001 From: Forrest Erickson Date: Tue, 6 Jan 2026 13:57:50 -0500 Subject: [PATCH 17/18] FactoryTest_wMenu.ino, Make results menu more compact. --- .../FactoryTest_wMenu/FactoryTest_wMenu.ino | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino index 30b6c6c..52e3ae7 100644 --- a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino +++ b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino @@ -267,9 +267,28 @@ static void printBanner() { static void printSummary() { Serial.println(); Serial.println(F("========== FACTORY TEST SUMMARY ==========")); - for (int i = 0; i < T_COUNT; ++i) { - Serial.printf("%-33s : %s\n", TEST_NAMES[i], testResults[i] ? "PASS" : "FAIL"); + + for (int i = 0; i < T_COUNT; i += 2) { + + // Left column + Serial.printf( + "%-33s : %-4s", + TEST_NAMES[i], + testResults[i] ? "PASS" : "FAIL" + ); + + // Right column (if present) + if (i + 1 < T_COUNT) { + Serial.printf( + " %-33s : %-4s", + TEST_NAMES[i + 1], + testResults[i + 1] ? "PASS" : "FAIL" + ); + } + + Serial.println(); } + Serial.println(F("==========================================")); Serial.println(); } From 5c220b8dc11db19bef8f7181f38fe5ae1e2a50fc Mon Sep 17 00:00:00 2001 From: Forrest Erickson Date: Wed, 7 Jan 2026 10:42:11 -0500 Subject: [PATCH 18/18] FactoryTest_wMenu.ino, Trying to make DFPlayer command 4 more verbose. Working again. --- .../FactoryTest_wMenu/FactoryTest_wMenu.ino | 369 +++++++++++------- 1 file changed, 224 insertions(+), 145 deletions(-) diff --git a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino index 52e3ae7..4f68b68 100644 --- a/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino +++ b/Firmware/factoryTest/FactoryTest_wMenu/FactoryTest_wMenu.ino @@ -1,4 +1,4 @@ -#define FIRMWARE_VERSION "v0.4.2.3" +#define FIRMWARE_VERSION "v0.4.2.5" /* ------------------------------------------------------------------------------ File: FactoryTest_wMenu.ino @@ -25,6 +25,8 @@ Revision History: |v0.4.2.1 | 2026-1-1 | N. Kheir | DFplayer cleanup test. | |v0.4.2.2 | 2026-1-1 | N. Kheir | SPI cleanup test. | |v0.4.2.3 | 2026-1-5 | L. Erickson | Make menu two colums. | +|v0.4.2.4 | 2026-1-6 | L. Erickson | Make volume full, 30. | +|v0.4.2.5 | 2026-1-7 | L. Erickson | Use myDFPlayer.getVersion() | ----------------------------------------------------------------------------------------| Overview: - Repeatable factory test sequence for ESP32-WROOM-32D Krake/GPAD v2 boards. @@ -54,9 +56,9 @@ Build notes: // Configuration // ============================================================================ -static const uint32_t SERIAL_BAUD = 115200; +static const uint32_t SERIAL_BAUD = 115200; static const uint32_t PROMPT_TIMEOUT_MS = 65000; -static const uint32_t WIFI_CONNECT_MS = 20000; +static const uint32_t WIFI_CONNECT_MS = 20000; static const bool SKIP_DRIVING_LAMP2 = true; // shared with DF BUSY on some builds static const uint8_t LCD_ALLOWED_ADDRS[] = { 0x27, 0x3F, 0x38 }; @@ -67,48 +69,50 @@ static const bool ENABLE_RS232_TEST = true; // ============================================================================ // Lamps / LEDs -const int LED_Status = 13; // Mute LED / general status -const int LAMP1 = 12; // Lamp 1 -const int LAMP2 = 14; // Lamp 2 (often DFPlayer BUSY on some builds) -const int LAMP3 = 27; // Lamp 3 (also SPI CS) -const int LAMP4 = 26; // Lamp 4 (also SPI SCK) -const int LAMP5 = 25; // Lamp 5 (also SPI MISO) +const int LED_Status = 13; // Mute LED / general status +const int LAMP1 = 12; // Lamp 1 +const int LAMP2 = 14; // Lamp 2 (often DFPlayer BUSY on some builds) +const int LAMP3 = 27; // Lamp 3 (also SPI CS) +const int LAMP4 = 26; // Lamp 4 (also SPI SCK) +const int LAMP5 = 25; // Lamp 5 (also SPI MISO) // Rotary encoder (input-only pins OK on ESP32) const int ENC_CLK = 39; -const int ENC_DT = 36; -const int ENC_SW = 34; +const int ENC_DT = 36; +const int ENC_SW = 34; // DFPlayer / speaker (UART2) -const int DF_TXD2 = 17; // ESP32 TX2 -> DFPlayer RX -const int DF_RXD2 = 16; // ESP32 RX2 <- DFPlayer TX -const int DF_BUSY_IN = LAMP2; // Active LOW when playing (if wired) +const int DF_TXD2 = 17; // ESP32 TX2 -> DFPlayer RX +const int DF_RXD2 = 16; // ESP32 RX2 <- DFPlayer TX +const int DF_BUSY_IN = LAMP2; // Active LOW when playing (if wired) // SPI (VSPI) -const int SPI_MOSI_PIN = 23 ; -const int SPI_MISO_PIN = 19 ; -const int SPI_SCK_PIN = 18 ; -const int SPI_CS_PIN = 5 ; +const int SPI_MOSI_PIN = 23; +const int SPI_MISO_PIN = 19; +const int SPI_SCK_PIN = 18; +const int SPI_CS_PIN = 5; // ===== TEST PARAMETERS ===== -static const uint32_t SPI_SPEED = 1000000; // 1 MHz (safe for factory) +static const uint32_t SPI_SPEED = 1000000; // 1 MHz (safe for factory) static const uint8_t TEST_PATTERN[] = { 0x55, 0xAA, 0x00, 0xFF, 0x12, 0x34, 0xA5 }; // RS-232 (UART1) – IMPORTANT: set these to YOUR PCB pins (via MAX3232) -const int UART1_TXD1 = 2; // placeholder safe GPIO -const int UART1_RXD1 = 15; // placeholder safe GPIO +const int UART1_TXD1 = 2; // placeholder safe GPIO +const int UART1_RXD1 = 15; // placeholder safe GPIO const long UART1_BAUD = 115200; -static const uint32_t TIMEOUT_MS = 600; - +static const uint32_t TIMEOUT_MS = 600; + // DFPlayer bookkeeping + SD cache HardwareSerial dfSerial(2); DFRobotDFPlayerMini dfPlayer; -enum dfState { DF_UNKNOWN, DF_OK, DF_FAIL }; +enum dfState { DF_UNKNOWN, + DF_OK, + DF_FAIL }; static bool dfState = DF_UNKNOWN; // SD cache: read file count ONCE during test [5], reuse during test [6] -static bool g_sdChecked = false; -static int g_sdFileCount = -999; +static bool g_sdChecked = false; +static int g_sdFileCount = -999; // ============================================================================ // Test bookkeeping @@ -155,7 +159,9 @@ bool testResults[T_COUNT] = { false }; static char g_pendingCmd = 0; -static char up(char c) { return (char)toupper((unsigned char)c); } +static char up(char c) { + return (char)toupper((unsigned char)c); +} static bool isMenuKey(char c) { c = up(c); @@ -166,8 +172,8 @@ static void flushSerialRx() { while (Serial.available()) (void)Serial.read(); } - -static bool readLineOrMenuAbort(String &out, uint32_t timeoutMs = 15000) { + +static bool readLineOrMenuAbort(String& out, uint32_t timeoutMs = 15000) { out = ""; uint32_t start = millis(); @@ -181,10 +187,10 @@ static bool readLineOrMenuAbort(String &out, uint32_t timeoutMs = 15000) { if (out.length() == 1 && isMenuKey(out[0])) { g_pendingCmd = up(out[0]); out = ""; - return false; // aborted + return false; // aborted } - return true; // completed normally + return true; // completed normally } out += c; @@ -193,10 +199,10 @@ static bool readLineOrMenuAbort(String &out, uint32_t timeoutMs = 15000) { } out.trim(); - return true; // timeout (out may be empty) + return true; // timeout (out may be empty) } - + static bool promptYesNo(const __FlashStringHelper* question, uint32_t timeoutMs = PROMPT_TIMEOUT_MS, bool defaultNo = true) { @@ -219,7 +225,11 @@ static bool promptYesNo(const __FlashStringHelper* question, char k = up(buf[0]); if (k == 'Y') return true; if (k == 'N') return false; - if (isMenuKey(k)) { g_pendingCmd = k; Serial.println(F("\nAborted -> FAIL.")); return false; } + if (isMenuKey(k)) { + g_pendingCmd = k; + Serial.println(F("\nAborted -> FAIL.")); + return false; + } Serial.println(F("\nUnknown input -> FAIL")); return false; @@ -232,8 +242,14 @@ static bool promptYesNo(const __FlashStringHelper* question, } char u = up(c); - if (u == 'Y') { Serial.println(F("Y")); return true; } - if (u == 'N') { Serial.println(F("N")); return false; } + if (u == 'Y') { + Serial.println(F("Y")); + return true; + } + if (u == 'N') { + Serial.println(F("N")); + return false; + } buf += c; } @@ -257,7 +273,7 @@ static void printBanner() { Serial.println(F("=======================================")); Serial.print("Compiled at: "); Serial.println(F(__DATE__ " " __TIME__)); //compile date that is used for a unique identifier - Serial.printf("Chip revision: %d\n", ESP.getChipRevision()); + Serial.printf("Chip revision: %d\n", ESP.getChipRevision()); Serial.printf("Flash size: %u bytes\n", ESP.getFlashChipSize()); Serial.print(F("MAC (STA): ")); Serial.println(WiFi.macAddress()); @@ -274,16 +290,14 @@ static void printSummary() { Serial.printf( "%-33s : %-4s", TEST_NAMES[i], - testResults[i] ? "PASS" : "FAIL" - ); + testResults[i] ? "PASS" : "FAIL"); // Right column (if present) if (i + 1 < T_COUNT) { Serial.printf( " %-33s : %-4s", TEST_NAMES[i + 1], - testResults[i + 1] ? "PASS" : "FAIL" - ); + testResults[i + 1] ? "PASS" : "FAIL"); } Serial.println(); @@ -294,18 +308,17 @@ static void printSummary() { } static void printMenu() { -Serial.println(F("Test menu (order matters):")); -Serial.println(F(" 0 Power / ID 1 Inputs (Encoder / Button)")); -Serial.println(F(" 2 LCD (I2C) 3 LEDs / Lamps")); -Serial.println(F(" 4 DFPlayer 5 SD (DFPlayer card)")); -Serial.println(F(" 6 Speaker 7 Wi-Fi AP")); -Serial.println(F(" 8 Wi-Fi STA (manual SSID/PASS) A LittleFS R/W")); -Serial.println(F(" B UART0 (USB Serial) C SPI loopback")); -Serial.println(F(" D RS-232 loopback P Run ALL (1 -> D)")); -Serial.println(F(" R Reboot")); -Serial.println(); -Serial.print(F("Enter command: ")); - + Serial.println(F("Test menu (order matters):")); + Serial.println(F(" 0 Power / ID 1 Inputs (Encoder / Button)")); + Serial.println(F(" 2 LCD (I2C) 3 LEDs / Lamps")); + Serial.println(F(" 4 DFPlayer 5 SD (DFPlayer card)")); + Serial.println(F(" 6 Speaker 7 Wi-Fi AP")); + Serial.println(F(" 8 Wi-Fi STA (manual SSID/PASS) A LittleFS R/W")); + Serial.println(F(" B UART0 (USB Serial) C SPI loopback")); + Serial.println(F(" D RS-232 loopback P Run ALL (1 -> D)")); + Serial.println(F(" R Reboot")); + Serial.println(); + Serial.print(F("Enter command: ")); } // ============================================================================ @@ -323,16 +336,16 @@ static bool runTest_Inputs() { Serial.println(F("\n[2] Inputs (Encoder / Button)")); pinMode(ENC_CLK, INPUT); - pinMode(ENC_DT, INPUT); - pinMode(ENC_SW, INPUT_PULLUP); + pinMode(ENC_DT, INPUT); + pinMode(ENC_SW, INPUT_PULLUP); Serial.println(F("Rotate encoder CLOCKWISE, then COUNTER-CLOCKWISE,")); Serial.println(F("then PRESS the encoder button within 10 seconds.")); delay(200); - int lastCLK = digitalRead(ENC_CLK); - bool sawCW = false; - bool sawCCW = false; + int lastCLK = digitalRead(ENC_CLK); + bool sawCW = false; + bool sawCCW = false; bool sawPress = false; unsigned long start = millis(); @@ -404,10 +417,15 @@ static bool runTest_LCD() { lcd.backlight(); lcd.clear(); - lcd.setCursor(0, 0); lcd.print("KRAKE FACTORY TEST"); - lcd.setCursor(0, 1); lcd.print("LCD @ 0x"); lcd.print(firstAllowed, HEX); - lcd.setCursor(0, 2); lcd.print("Confirm display OK"); - lcd.setCursor(0, 3); lcd.print("Press Y/N on Serial"); + lcd.setCursor(0, 0); + lcd.print("KRAKE FACTORY TEST"); + lcd.setCursor(0, 1); + lcd.print("LCD @ 0x"); + lcd.print(firstAllowed, HEX); + lcd.setCursor(0, 2); + lcd.print("Confirm display OK"); + lcd.setCursor(0, 3); + lcd.print("Press Y/N on Serial"); Serial.println(F("LCD initialized and message written.")); bool ok = promptYesNo(F("Visually confirm LCD shows the message on all lines."), PROMPT_TIMEOUT_MS, true); @@ -440,9 +458,13 @@ static bool runTest_LEDs() { Serial.println(F(" -> Skipping LAMP2 drive (BUSY shared safety)")); continue; } - Serial.print(F(" -> ")); Serial.print(names[i]); Serial.println(F(" blink")); - digitalWrite(pins[i], HIGH); delay(300); - digitalWrite(pins[i], LOW); delay(150); + Serial.print(F(" -> ")); + Serial.print(names[i]); + Serial.println(F(" blink")); + digitalWrite(pins[i], HIGH); + delay(300); + digitalWrite(pins[i], LOW); + delay(150); } bool ok = promptYesNo(F("Did you see the LEDs/Lamps blink as expected?"), PROMPT_TIMEOUT_MS, true); @@ -470,7 +492,15 @@ static bool dfPlayerResponding() { return (c >= 0); } +/* Function Description +Initialized the UART to the Mini MP3 player (DF Player) +Initilize the DF player +Set for file output from SD card. +On subsiquent calls, will report of Mini MP3 player BECOMES unresponsive. +*/ static bool initDFPlayer() { + Serial.print("initDFPlayer() called at: "); + Serial.println(millis()); if (dfState == DF_OK) { if (dfPlayerResponding()) return true; @@ -486,6 +516,15 @@ static bool initDFPlayer() { dfSerial.begin(9600, SERIAL_8N1, DF_RXD2, DF_TXD2); delay(300); + dfPlayer.begin(dfSerial, true, true); + delay(300); + dfPlayer.setTimeOut(1000); // give replies more time (some modules are slow) + delay(300); + dfPlayer.outputDevice(DFPLAYER_DEVICE_SD); + + int version = dfPlayer.available(); // + Serial.print("InitDFPlayer, dfPlayer.available(): "); + Serial.println(version); // doReset=true helps a LOT with SD indexing reliability if (!dfPlayer.begin(dfSerial, true, true)) { @@ -494,11 +533,15 @@ static bool initDFPlayer() { return false; } - dfPlayer.setTimeOut(1000); // give replies more time (some modules are slow) + dfPlayer.setTimeOut(1000); // give replies more time (some modules are slow) // Force device selection (important on some clones) dfPlayer.outputDevice(DFPLAYER_DEVICE_SD); - delay(1200); // let TF card mount + index after reset/device select + delay(1200); // let TF card mount + index after reset/device select + version = dfPlayer.available(); // + Serial.print("Afer OutputDevice InitDFPOlayer, dfPlayer.available(): "); + Serial.println(version); + // Optional: verify it actually replies after init if (!dfPlayerResponding()) { @@ -533,7 +576,7 @@ static bool initDFPlayer() { // dfState = DF_OK; // Serial.println(F(" DFPlayer detected and initialized (SD selected).")); // } - + // dfPlayer.setTimeOut(1000); // give replies more time (some modules are slow) // return true; // } @@ -606,23 +649,40 @@ void printDetail(uint8_t type, int value) { static bool runTest_DFPlayer(bool forceReinit = false) { // If you want live DFPlayer diagnostics, call this elsewhere during waits: // if (dfPlayer.available()) printDetail(dfPlayer.readType(), dfPlayer.read()); - if (forceReinit) { + if (forceReinit) { clearDFPlayerCache(); } - if (!initDFPlayer()) { - Serial.println(F(" DFPlayer initializing Failed (DFPlayer not detected).")); - dfState = DF_FAIL; - return false; - }else { - // Serial.println(F("\n[6] DFPlayer + Speaker")); - Serial.println(F("DFPlayer Mini online.")); - dfState = DF_OK; + // initDFPlayer(); + // //Check Mini MP3 Player availability. Works on TD5580A too. + // int version = dfPlayer.available(); // + // Serial.print("dfPlayer.available(): "); + // Serial.println(version); + + if (!initDFPlayer()) { + Serial.println(F(" DFPlayer initializing Failed (DFPlayer not detected).")); + dfState = DF_FAIL; + // //Check Mini MP3 Player availability. Works on TD5580A too. + delay(1000); + int version = dfPlayer.available(); // + Serial.print("dfPlayer.available(): "); + Serial.println(version); + return false; + } else { + // Serial.println(F("\n[6] DFPlayer + Speaker")); + Serial.println(F("DFPlayer Mini online.")); + dfState = DF_OK; + int version = dfPlayer.available(); // + Serial.print("dfPlayer.available(): "); + Serial.println(version); + if (dfPlayer.available()) printDetail(dfPlayer.readType(), dfPlayer.read()); } + + return true; // Force device selection (important on some clones) dfPlayer.outputDevice(DFPLAYER_DEVICE_SD); - delay(1200); // let TF card mount + index after reset/device select + delay(1200); // let TF card mount + index after reset/device select bool ok = (dfState); Serial.printf("DFPLAYER Initialized: %s\n", ok ? "PASS" : "FAIL (wiring/MAX3232/pins)"); @@ -636,7 +696,7 @@ static bool runTest_SD() { // g_sdChecked = false; // g_sdFileCount = -999; - g_sdChecked = true; + g_sdChecked = true; g_sdFileCount = -1; // if (!promptYesNo(F("Is the SD card inserted with audio files?"), PROMPT_TIMEOUT_MS, true)) { @@ -646,9 +706,18 @@ static bool runTest_SD() { // return false; // } + initDFPlayer(); + + //Check Mini MP3 Player availability. Works on TD5580A too. + int version = dfPlayer.available(); // + Serial.print("dfPlayer.available(): "); + Serial.println(version); + + + if (!initDFPlayer()) { Serial.println(F("SD test: FAIL (DFPlayer not detected).")); - g_sdChecked = true; + g_sdChecked = true; g_sdFileCount = -1; return false; } @@ -666,38 +735,37 @@ static bool runTest_SD() { Serial.printf(" DFPlayer reports %d files.\n", g_sdFileCount); if (g_sdFileCount <= 0) { - Serial.println(F("SD test: FAIL (no readable files).")); + Serial.println(F("SD test: FAIL (no readable files).")); return false; } return true; bool ok = runTest_SD(); Serial.printf("SD test: %s\n", ok ? "PASS" : "FAIL"); - - void printDetail(uint8_t type, int value); - + + void printDetail(uint8_t type, int value); } static bool runTest_Speaker() { Serial.println(F("Playing track #1 for ~3 seconds...")); - if (!initDFPlayer()) { - Serial.println(F("Speaker test: FAIL (DFPlayer not detected).")); - return false; - } + // if (!initDFPlayer()) { + // Serial.println(F("Speaker test: FAIL (DFPlayer not detected).")); + // return false; + // } - if (!g_sdChecked) { - Serial.println(F("DFPlayer test requires SD test first.")); - Serial.println(F("Run [5] SD test, then run speaker test.")); - return false; - } + // if (!g_sdChecked) { + // Serial.println(F("DFPlayer test requires SD test first.")); + // Serial.println(F("Run [5] SD test, then run speaker test.")); + // return false; + // } - Serial.printf(" Using cached SD file count = %d\n", g_sdFileCount); - if (g_sdFileCount <= 0) { - Serial.println(F("No readable SD/files -> NOT playing audio. Speaker test FAIL.")); - return false; - } - const int VOL = 20; + // Serial.printf(" Using cached SD file count = %d\n", g_sdFileCount); + // if (g_sdFileCount <= 0) { + // Serial.println(F("No readable SD/files -> NOT playing audio. Speaker test FAIL.")); + // return false; + // } + const int VOL = 30; dfPlayer.volume(VOL); Serial.printf("DFPlayer volume set to: %d\n", VOL); @@ -724,7 +792,8 @@ static bool runTest_WifiAP() { Serial.println(F("\n[7] Wi-Fi AP")); WiFi.mode(WIFI_AP); - String mac = WiFi.macAddress(); mac.replace(":", ""); + String mac = WiFi.macAddress(); + mac.replace(":", ""); String ssid = "KRAKE_" + mac; bool ok = WiFi.softAP(ssid.c_str(), "krakefactory"); @@ -734,8 +803,10 @@ static bool runTest_WifiAP() { } IPAddress ip = WiFi.softAPIP(); - Serial.print(F("AP SSID: ")); Serial.println(ssid); - Serial.print(F("AP IP: ")); Serial.println(ip); + Serial.print(F("AP SSID: ")); + Serial.println(ssid); + Serial.print(F("AP IP: ")); + Serial.println(ip); Serial.println(F("Operator: verify AP is visible from phone/PC.")); return true; } @@ -771,7 +842,8 @@ static bool runTest_WifiSTA() { } pass.trim(); - Serial.print(F("Connecting to: ")); Serial.println(ssid); + Serial.print(F("Connecting to: ")); + Serial.println(ssid); WiFi.begin(ssid.c_str(), pass.c_str()); uint32_t start = millis(); @@ -787,8 +859,10 @@ static bool runTest_WifiSTA() { } Serial.println(F("Wi-Fi STA connected successfully.")); - Serial.print(F("IP: ")); Serial.println(WiFi.localIP()); - Serial.print(F("RSSI: ")); Serial.println(WiFi.RSSI()); + Serial.print(F("IP: ")); + Serial.println(WiFi.localIP()); + Serial.print(F("RSSI: ")); + Serial.println(WiFi.RSSI()); return true; } @@ -800,10 +874,11 @@ static bool runTest_LittleFS() { return false; } - const char* path = "/factory_test.txt"; + const char* path = "/factory_test.txt"; const char* payload = "KRAKE_FACTORY_TEST"; - Serial.print(F("Writing ")); Serial.println(path); + Serial.print(F("Writing ")); + Serial.println(path); File f = LittleFS.open(path, FILE_WRITE); if (!f) { Serial.println(F("Failed to open file for write.")); @@ -841,7 +916,7 @@ static bool runTest_SPI() { SPI.begin(SPI_SCK_PIN, SPI_MISO_PIN, SPI_MOSI_PIN, SPI_CS_PIN); pinMode(SPI_CS_PIN, OUTPUT); - + digitalWrite(SPI_CS_PIN, HIGH); delay(20); Serial.println("[SPI] Starting RJ12 loopback test"); @@ -871,7 +946,7 @@ static bool runTest_SPI() { Serial.println("[SPI] PASS: RJ12 loopback OK"); } return pass; - + Serial.printf("SPI sent 0x%02X, received 0x%02X\n", RX, TX); bool ok = runTest_UART0(); Serial.printf("SPI loopback test: %s\n", ok ? "PASS" : "FAIL (check jumper)"); @@ -895,7 +970,7 @@ static bool runTest_RS232() { // Clear any pending garbage while (rs232.available()) rs232.read(); - const char *payload = "KRAKE_rs232_UART1_LOOPBACK_123\r\n"; + const char* payload = "KRAKE_rs232_UART1_LOOPBACK_123\r\n"; const size_t n = strlen(payload); // Send @@ -929,17 +1004,21 @@ static bool runTest_RS232() { if (rx != payload) { Serial.println("[rs232] FAIL: payload mismatch"); - Serial.print("Sent: "); Serial.print(payload); - Serial.print("Recv: "); Serial.print(rx); + Serial.print("Sent: "); + Serial.print(payload); + Serial.print("Recv: "); + Serial.print(rx); return false; } - Serial.print("Sent: "); Serial.print(payload); - Serial.print("Recv: "); Serial.print(rx); + Serial.print("Sent: "); + Serial.print(payload); + Serial.print("Recv: "); + Serial.print(rx); Serial.println("[rs232] PASS: UART1 loopback OK"); return true; - - bool ok = (rx == payload) ; + + bool ok = (rx == payload); Serial.printf("RS-232 loopback test: %s\n", ok ? "PASS" : "FAIL (wiring/MAX3232/pins)"); return ok; } @@ -950,20 +1029,20 @@ static bool runTest_RS232() { static bool runSingleTestFromIndex(TestIndex idx) { switch (idx) { - case T_POWER: return runTest_Power(); - case T_INPUTS: return runTest_Inputs(); - case T_LCD: return runTest_LCD(); - case T_LEDS: return runTest_LEDs(); - case T_DFPLAYER: return runTest_DFPlayer(); - case T_SD: return runTest_SD(); - case T_Speaker: return runTest_Speaker(); - case T_WIFI_AP: return runTest_WifiAP(); - case T_WIFI_STA: return runTest_WifiSTA(); - case T_LITTLEFS: return runTest_LittleFS(); - case T_UART0: return runTest_UART0(); - case T_SPI: return runTest_SPI(); - case T_RS232: return runTest_RS232(); - default: return false; + case T_POWER: return runTest_Power(); + case T_INPUTS: return runTest_Inputs(); + case T_LCD: return runTest_LCD(); + case T_LEDS: return runTest_LEDs(); + case T_DFPLAYER: return runTest_DFPlayer(); + case T_SD: return runTest_SD(); + case T_Speaker: return runTest_Speaker(); + case T_WIFI_AP: return runTest_WifiAP(); + case T_WIFI_STA: return runTest_WifiSTA(); + case T_LITTLEFS: return runTest_LittleFS(); + case T_UART0: return runTest_UART0(); + case T_SPI: return runTest_SPI(); + case T_RS232: return runTest_RS232(); + default: return false; } } @@ -996,20 +1075,20 @@ static void handleCommand(char c) { bool recognized = true; switch (c) { - case '0': testResults[T_POWER] = runTest_Power(); break; - case '1': testResults[T_INPUTS] = runTest_Inputs(); break; - case '2': testResults[T_LCD] = runTest_LCD(); break; - case '3': testResults[T_LEDS] = runTest_LEDs(); break; - case '4': testResults[T_DFPLAYER] = runTest_DFPlayer(); break; - case '5': testResults[T_SD] = runTest_SD(); break; - case '6': testResults[T_Speaker] = runTest_Speaker(); break; - case '7': testResults[T_WIFI_AP] = runTest_WifiAP(); break; - case '8': testResults[T_WIFI_STA] = runTest_WifiSTA(); break; - case 'A': testResults[T_LITTLEFS] = runTest_LittleFS(); break; - case 'B': testResults[T_UART0] = runTest_UART0(); break; - case 'C': testResults[T_SPI] = runTest_SPI(); break; - case 'D': testResults[T_RS232] = runTest_RS232(); break; - case 'P': runAllTests(); break; + case '0': testResults[T_POWER] = runTest_Power(); break; + case '1': testResults[T_INPUTS] = runTest_Inputs(); break; + case '2': testResults[T_LCD] = runTest_LCD(); break; + case '3': testResults[T_LEDS] = runTest_LEDs(); break; + case '4': testResults[T_DFPLAYER] = runTest_DFPlayer(); break; + case '5': testResults[T_SD] = runTest_SD(); break; + case '6': testResults[T_Speaker] = runTest_Speaker(); break; + case '7': testResults[T_WIFI_AP] = runTest_WifiAP(); break; + case '8': testResults[T_WIFI_STA] = runTest_WifiSTA(); break; + case 'A': testResults[T_LITTLEFS] = runTest_LittleFS(); break; + case 'B': testResults[T_UART0] = runTest_UART0(); break; + case 'C': testResults[T_SPI] = runTest_SPI(); break; + case 'D': testResults[T_RS232] = runTest_RS232(); break; + case 'P': runAllTests(); break; case 'R': Serial.println(F("Rebooting...")); delay(200); @@ -1060,7 +1139,7 @@ void loop() { char c = Serial.read(); if (c == '\r' || c == '\n') return; - Serial.println(up(c)); // echo + Serial.println(up(c)); // echo handleCommand(c); } } \ No newline at end of file