@@ -100,6 +100,22 @@ MyBLECharacteristicCallbacks* charCallbacks = nullptr;
100100
101101void setup () {
102102 Serial.begin (115200 );
103+ delay (100 ); // Give serial time to initialize
104+ writeSerial (" === FIRMWARE INFO ===" );
105+ writeSerial (" Firmware Version: " + String (getFirmwareMajor ()) + " ." + String (getFirmwareMinor ()));
106+ // SHA_STRING is already a string literal from the macro, so we can use it directly
107+ const char * shaCStr = SHA_STRING;
108+ String shaStr = String (shaCStr);
109+ // Remove surrounding quotes if present (from stringification when SHA is empty string "")
110+ if (shaStr.length () >= 2 && shaStr.charAt (0 ) == ' "' && shaStr.charAt (shaStr.length () - 1 ) == ' "' ) {
111+ shaStr = shaStr.substring (1 , shaStr.length () - 1 );
112+ }
113+ if (shaStr.length () > 0 && shaStr != " \"\" " && shaStr != " " ) {
114+ writeSerial (" Git SHA: " + shaStr);
115+ } else {
116+ writeSerial (" Git SHA: (not set)" );
117+ }
118+ writeSerial (" =====================" );
103119 writeSerial (" Starting setup..." );
104120 writeSerial (" Initializing full config..." );
105121 full_config_init ();
@@ -209,6 +225,24 @@ void initio(){
209225 initSensors ();
210226}
211227
228+ uint8_t getFirmwareMajor (){
229+ String version = String (BUILD_VERSION);
230+ int dotIndex = version.indexOf (' .' );
231+ if (dotIndex > 0 ) {
232+ return version.substring (0 , dotIndex).toInt ();
233+ }
234+ return 0 ;
235+ }
236+
237+ uint8_t getFirmwareMinor (){
238+ String version = String (BUILD_VERSION);
239+ int dotIndex = version.indexOf (' .' );
240+ if (dotIndex > 0 && dotIndex < version.length () - 1 ) {
241+ return version.substring (dotIndex + 1 ).toInt ();
242+ }
243+ return 0 ;
244+ }
245+
212246void initDataBuses (){
213247 writeSerial (" === Initializing Data Buses ===" );
214248 if (globalConfig.data_bus_count == 0 ){
@@ -917,6 +951,21 @@ void initDisplay(){
917951 epd.print (" Name: OEPL" + chipId);
918952 epd.setCursor (100 , 300 );
919953 epd.print (" Epaper driver by Larry Bank https://github.com/bitbank2" );
954+ epd.setCursor (100 , 400 );
955+ const char * shaCStr = SHA_STRING;
956+ String shaStr = String (shaCStr);
957+ // Remove surrounding quotes if present (from stringification when SHA is empty string "")
958+ if (shaStr.length () >= 2 && shaStr.charAt (0 ) == ' "' && shaStr.charAt (shaStr.length () - 1 ) == ' "' ) {
959+ shaStr = shaStr.substring (1 , shaStr.length () - 1 );
960+ }
961+ if (shaStr.length () == 0 || shaStr == " \"\" " || shaStr == " " ) {
962+ shaStr = " (not set)" ;
963+ }
964+ epd.print (" Firmware: " + String (getFirmwareMajor ()) + " ." + String (getFirmwareMinor ()) + " SHA: " + shaStr);
965+ if (globalConfig.displays [0 ].color_scheme == 0 ){
966+ writeSerial (" 2 color test" );
967+ epd.fillRect (100 ,320 ,10 ,10 ,BBEP_BLACK);
968+ }
920969 if (globalConfig.displays [0 ].color_scheme == 4 ){
921970 writeSerial (" 6 color test" );
922971 epd.fillRect (100 ,320 ,10 ,10 ,BBEP_RED);
@@ -1080,6 +1129,10 @@ void imageDataWritten(void* conn_hdl, void* chr, uint8_t* data, uint16_t len){
10801129 delay (100 );
10811130 reboot ();
10821131 break ;
1132+ case 0x0043 : // Firmware Version command
1133+ writeSerial (" === FIRMWARE VERSION COMMAND (0x0043) ===" );
1134+ handleFirmwareVersion ();
1135+ break ;
10831136 default :
10841137 writeSerial (" ERROR: Unknown command: 0x" + String (command, HEX));
10851138 writeSerial (" Expected: 0x0011 (read config), 0x0064 (image info), 0x0065 (block data), or 0x0003 (finalize)" );
@@ -1951,13 +2004,11 @@ bool decompressImageDataChunked(){
19512004 uint8_t invertedFirstPlane = ~firstPlaneByte;
19522005 uint16_t planeY = thirdPlaneIndex / (imgWidth / 8 );
19532006 uint16_t planeX = (thirdPlaneIndex % (imgWidth / 8 )) * 8 ;
1954-
19552007 for (int bit = 7 ; bit >= 0 ; bit--) {
19562008 if (planeY < ((imgHeight < displayWidth) ? imgHeight : displayWidth) && planeX + (7 -bit) < ((imgWidth < displayHeight) ? imgWidth : displayHeight)) {
19572009 bool plane1 = (invertedFirstPlane >> bit) & 0x01 ; // B/W base
19582010 bool plane2 = (secondPlaneByte >> bit) & 0x01 ; // R/Y
19592011 bool plane3 = (byte >> bit) & 0x01 ; // G/B
1960-
19612012 // Decode 6 colors: BLACK=000, WHITE=100, RED=110, YELLOW=010, GREEN=101, BLUE=001
19622013 if (!plane1 && !plane2 && !plane3) {
19632014 epd.drawPixel (displayWidth - planeY, planeX + (7 -bit), BBEP_BLACK);
@@ -1988,13 +2039,10 @@ bool decompressImageDataChunked(){
19882039 writeSerial (" ERROR: uzlib data error" );
19892040 break ;
19902041 }
1991-
19922042 } while (res == TINF_OK && totalDecompressed < originalLength);
19932043 writeSerial (" Chunked decompression complete: " + String (totalDecompressed) + " bytes" );
19942044 writeSerial (" Expected: " + String (originalLength) + " bytes" );
19952045 writeSerial (" Pixel bytes processed: " + String (pixelBytesProcessed));
1996-
1997- // Cleanup allocated buffers
19982046 if (firstPlaneBuffer) {
19992047 free (firstPlaneBuffer);
20002048 writeSerial (" Freed first plane buffer" );
@@ -2252,6 +2300,34 @@ void handleReadConfig(){
22522300 writeSerial (" handleReadConfig function completed successfully" );
22532301}
22542302
2303+ void handleFirmwareVersion (){
2304+ writeSerial (" Building Firmware Version response..." );
2305+ uint8_t major = getFirmwareMajor ();
2306+ uint8_t minor = getFirmwareMinor ();
2307+ const char * shaCStr = SHA_STRING;
2308+ String shaStr = String (shaCStr);
2309+ // Remove surrounding quotes if present (from stringification when SHA is empty string "")
2310+ if (shaStr.length () >= 2 && shaStr.charAt (0 ) == ' "' && shaStr.charAt (shaStr.length () - 1 ) == ' "' ) {
2311+ shaStr = shaStr.substring (1 , shaStr.length () - 1 );
2312+ }
2313+ writeSerial (" Firmware version: " + String (major) + " ." + String (minor));
2314+ writeSerial (" SHA: " + shaStr);
2315+ uint8_t shaLen = shaStr.length ();
2316+ if (shaLen > 40 ) shaLen = 40 ;
2317+ uint8_t response[2 + 1 + 1 + 1 + 40 ];
2318+ uint16_t offset = 0 ;
2319+ response[offset++] = 0x00 ;
2320+ response[offset++] = 0x43 ;
2321+ response[offset++] = major;
2322+ response[offset++] = minor;
2323+ response[offset++] = shaLen;
2324+ for (uint8_t i = 0 ; i < shaLen && i < 40 ; i++) {
2325+ response[offset++] = shaStr.charAt (i);
2326+ }
2327+ sendResponse (response, offset);
2328+ writeSerial (" Firmware version response sent" );
2329+ }
2330+
22552331void handleWriteConfig (uint8_t * data, uint16_t len){
22562332 if (len == 0 ) {
22572333 writeSerial (" ERROR: No config data received" );
@@ -2354,6 +2430,11 @@ void handleWriteConfigChunk(uint8_t* data, uint16_t len){
23542430bool loadGlobalConfig (){
23552431 writeSerial (" Loading global configuration..." );
23562432 memset (&globalConfig, 0 , sizeof (globalConfig));
2433+ // Reset WiFi configuration flag
2434+ wifiConfigured = false ;
2435+ wifiSsid[0 ] = ' \0 ' ;
2436+ wifiPassword[0 ] = ' \0 ' ;
2437+ wifiEncryptionType = 0 ;
23572438 static uint8_t configData[MAX_CONFIG_SIZE];
23582439 static uint32_t configLen = MAX_CONFIG_SIZE;
23592440 if (!loadConfig (configData, &configLen)) {
@@ -2496,6 +2577,58 @@ bool loadGlobalConfig(){
24962577 return false ;
24972578 }
24982579 break ;
2580+ case 0x26 : // wifi_config
2581+ {
2582+ const uint16_t WIFI_CONFIG_SIZE = 162 ; // 32 (SSID) + 32 (password) + 1 (encryption) + 95 (reserved)
2583+ if (offset + WIFI_CONFIG_SIZE <= configLen) {
2584+ // Parse SSID (32 bytes, null-terminated)
2585+ memcpy (wifiSsid, &configData[offset], 32 );
2586+ wifiSsid[32 ] = ' \0 ' ; // Ensure null termination
2587+ // Find actual string length (up to first null byte)
2588+ uint8_t ssidLen = 0 ;
2589+ while (ssidLen < 32 && wifiSsid[ssidLen] != ' \0 ' ) ssidLen++;
2590+ offset += 32 ;
2591+
2592+ // Parse password (32 bytes, null-terminated)
2593+ memcpy (wifiPassword, &configData[offset], 32 );
2594+ wifiPassword[32 ] = ' \0 ' ; // Ensure null termination
2595+ // Find actual string length (up to first null byte)
2596+ uint8_t passwordLen = 0 ;
2597+ while (passwordLen < 32 && wifiPassword[passwordLen] != ' \0 ' ) passwordLen++;
2598+ offset += 32 ;
2599+
2600+ // Parse encryption type (1 byte)
2601+ wifiEncryptionType = configData[offset++];
2602+
2603+ // Skip reserved bytes (95 bytes)
2604+ offset += 95 ;
2605+
2606+ // Mark WiFi as configured
2607+ wifiConfigured = true ;
2608+
2609+ // Print WiFi config to console
2610+ writeSerial (" === WiFi Configuration Loaded ===" );
2611+ writeSerial (" SSID: \" " + String (wifiSsid) + " \" " );
2612+ writeSerial (" Password: " + String (passwordLen > 0 ? " ***" : " (empty)" ));
2613+ String encTypeStr = " Unknown" ;
2614+ switch (wifiEncryptionType) {
2615+ case 0x00 : encTypeStr = " None (Open)" ; break ;
2616+ case 0x01 : encTypeStr = " WEP" ; break ;
2617+ case 0x02 : encTypeStr = " WPA" ; break ;
2618+ case 0x03 : encTypeStr = " WPA2" ; break ;
2619+ case 0x04 : encTypeStr = " WPA3" ; break ;
2620+ }
2621+ writeSerial (" Encryption Type: 0x" + String (wifiEncryptionType, HEX) + " (" + encTypeStr + " )" );
2622+ writeSerial (" SSID length: " + String (ssidLen) + " bytes" );
2623+ writeSerial (" Password length: " + String (passwordLen) + " bytes" );
2624+ writeSerial (" WiFi configured: true" );
2625+ } else {
2626+ writeSerial (" ERROR: Not enough data for wifi_config" );
2627+ globalConfig.loaded = false ;
2628+ return false ;
2629+ }
2630+ }
2631+ break ;
24992632 default :
25002633 writeSerial (" WARNING: Unknown packet ID 0x" + String (packetId, HEX) + " , skipping" );
25012634 // Skip unknown packet - we can't determine its size, so we'll stop parsing
0 commit comments