├── BLEDisplay.cpp ├── BLEDisplay.h ├── ESLDisplay.cpp ├── ESLDisplay.h ├── I2CDisplay.cpp ├── I2CDisplay.h ├── M5Core_Server └── M5Core_Server.ino ├── MacOS_Server ├── RemoteDisplay_Multi.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata │ │ │ └── laurencebank.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── laurencebank.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ └── xcschememanagement.plist └── RemoteDisplay_Multi │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── RemoteDisplay_Multi.entitlements │ ├── SceneDelegate.h │ ├── SceneDelegate.m │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── README.md ├── RemoteDisplay.cpp ├── RemoteDisplay.h ├── SPIDisplay.cpp ├── SPIDisplay.h ├── UARTDisplay.cpp ├── UARTDisplay.h ├── examples ├── nano33_camera │ └── nano33_camera.ino ├── remotedisplay_1bpp_demo │ └── remotedisplay_1bpp_demo.ino └── remotedisplay_demo │ ├── remotedisplay_demo.ino │ └── st_peters.h └── rd_constants.h /BLEDisplay.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // RemoteDisplay 3 | // 4 | // written by Larry Bank 5 | // bitbank@pobox.com 6 | // Project started 12/2/2020 7 | // Original JPEG code written 26+ years ago :) 8 | // The goal of this code is to decode baseline JPEG images 9 | // using no more than 18K of RAM (if sent directly to an LCD display) 10 | // 11 | // Copyright 2020 BitBank Software, Inc. All Rights Reserved. 12 | // Licensed under the Apache License, Version 2.0 (the "License"); 13 | // you may not use this file except in compliance with the License. 14 | // You may obtain a copy of the License at 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // Unless required by applicable law or agreed to in writing, software 17 | // distributed under the License is distributed on an "AS IS" BASIS, 18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | // See the License for the specific language governing permissions and 20 | // limitations under the License. 21 | //=========================================================================== 22 | // 23 | #include "RemoteDisplay.h" 24 | 25 | #ifdef HAL_ESP32_HAL_H_ 26 | // Bluetooth support 27 | #include 28 | #include 29 | #include 30 | static BLEUUID serviceUUID("0000fea0-1234-1000-8000-00805f9b34fb"); //Service 31 | static BLEUUID dataUUID("0000fea1-0000-1000-8000-00805f9b34fb"); // data characteristic 32 | //static BLEUUID nameUUID("0000fea2-0000-1000-8000-00805f9b34fb"); // name characteristic 33 | static std::string VD_BLE_Name = "RemoteDisplay"; 34 | char Scanned_Name[32]; 35 | String Scanned_Address; 36 | static BLEScanResults foundDevices; 37 | static BLEAddress *Server_BLE_Address; 38 | static volatile boolean paired = false; //boolean variable to togge light 39 | static BLEServer *pServer; 40 | static BLEScan *pBLEScan; 41 | static BLEService *pService; 42 | static BLERemoteCharacteristic *pCharacteristicData; 43 | #endif // HAL_ESP32_HAL_H_ 44 | 45 | // Bluetooth support for Adafruit nrf52 boards 46 | #ifdef ARDUINO_NRF52_ADAFRUIT 47 | #include 48 | const uint8_t myServiceUUID[16] = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x34, 0x12, 0xa0, 0xfe, 0x00, 0x00}; 49 | const uint8_t myDataUUID[16] = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x34, 0x12, 0xa1, 0xfe, 0x00, 0x00}; 50 | //#define myServiceUUID 0xFEA0 51 | //#define myDataUUID 0xFEA1 52 | static int isConnected; 53 | BLEClientCharacteristic myDataChar(myDataUUID); 54 | BLEClientService myService(myServiceUUID); 55 | 56 | /** 57 | * Callback invoked when an connection is established 58 | * @param conn_handle 59 | */ 60 | static void connect_callback(uint16_t conn_handle) 61 | { 62 | // Serial.println("Connected!"); 63 | // Serial.print("Discovering FEA0 Service ... "); 64 | 65 | // If FEA0 is not found, disconnect and return 66 | if ( !myService.discover(conn_handle) ) 67 | { 68 | // Serial.println("Found NONE"); 69 | // disconect since we couldn't find our service 70 | Bluefruit.disconnect(conn_handle); 71 | return; 72 | } 73 | 74 | // Once FEA0 service is found, we continue to discover its characteristics 75 | if ( !myDataChar.discover() ) 76 | { 77 | // Data char is mandatory, if it is not found (valid), then disconnect 78 | // Serial.println("Data characteristic is mandatory but not found"); 79 | Bluefruit.disconnect(conn_handle); 80 | return; 81 | } 82 | isConnected = 1; // success! 83 | } /* connect_callback() */ 84 | /** 85 | * Callback invoked when a connection is dropped 86 | * @param conn_handle 87 | * @param reason 88 | */ 89 | static void disconnect_callback(uint16_t conn_handle, uint8_t reason) 90 | { 91 | (void) conn_handle; 92 | (void) reason; 93 | isConnected = 0; 94 | // Serial.println("Disconnected"); 95 | } /* disconnect_callback() */ 96 | 97 | static void scan_callback(ble_gap_evt_adv_report_t* report) 98 | { 99 | // Serial.printf("found something %s\n", report->data.p_data); 100 | // if (Bluefruit.Scanner.checkReportForUuid(report, myServiceUUID)) 101 | char *name = (char *)report->data.p_data; 102 | int i; 103 | for (i=0; idata.len; i++) 104 | if (name[i] == 'R') break; // "parse" for the name in the adv data 105 | if (name && memcmp(&name[i], "RemoteDisplay", 13) == 0) 106 | { 107 | // Serial.println("Found RemoteDisplay"); 108 | Bluefruit.Scanner.stop(); 109 | // Serial.print("RemoteDisplay UUID detected. Connecting ... "); 110 | Bluefruit.Central.connect(report); 111 | } 112 | else // keep looking 113 | { 114 | // For Softdevice v6: after received a report, scanner will be paused 115 | // We need to call Scanner resume() to continue scanning 116 | Bluefruit.Scanner.resume(); 117 | } 118 | } /* scan_callback() */ 119 | 120 | static void notify_callback(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len) 121 | { 122 | } /* notify_callback() */ 123 | 124 | #endif // Adafruit nrf52 125 | 126 | // Bluetooth support for Nano 33 BLE 127 | #ifdef ARDUINO_ARDUINO_NANO33BLE 128 | #include 129 | 130 | static BLEDevice peripheral; 131 | static BLEService prtService; 132 | static BLECharacteristic pCharacteristicData; 133 | #endif // Nano 33 BLE 134 | #ifdef HAL_ESP32_HAL_H_ 135 | // Called for each device found during a BLE scan by the client 136 | class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks 137 | { 138 | void onResult(BLEAdvertisedDevice advertisedDevice) { 139 | Serial.printf("Scan Result: %s \n", advertisedDevice.toString().c_str()); 140 | if (Scanned_Name[0] == 0 && strcmp(VD_BLE_Name.c_str(), advertisedDevice.getName().c_str()) == 0) { // this is what we want 141 | Server_BLE_Address = new BLEAddress(advertisedDevice.getAddress()); 142 | Scanned_Address = Server_BLE_Address->toString().c_str(); 143 | strcpy(Scanned_Name, advertisedDevice.getName().c_str()); 144 | Serial.printf("Found what we're looking for!\n"); 145 | pBLEScan->stop(); // stop scanning 146 | } 147 | } 148 | }; 149 | 150 | // When the scan has found the BLE server device name we're looking for, we try to connect 151 | static bool connectToserver (BLEAddress pAddress) 152 | { 153 | BLEClient* pClient = BLEDevice::createClient(); 154 | Serial.println(" - Created client"); 155 | 156 | // Connect to the BLE Server. 157 | pClient->connect(pAddress, BLE_ADDR_TYPE_RANDOM); // needed for iOS/Android/MacOS 158 | Serial.print(" - Connected to "); Serial.println(VD_BLE_Name.c_str()); 159 | // Obtain a reference to the service we are after in the remote BLE server. 160 | BLERemoteService* pRemoteService = pClient->getService(serviceUUID); 161 | Serial.println("Returned from getService()"); 162 | if (pRemoteService != NULL) 163 | { 164 | Serial.println(" - Found our service"); 165 | if (pClient->isConnected()) 166 | { 167 | Serial.println(" - We're connected"); 168 | // Obtain a reference to the characteristic in the service of the remote BLE server. 169 | pCharacteristicData = pRemoteService->getCharacteristic(dataUUID); 170 | if (pCharacteristicData != nullptr) 171 | { 172 | Serial.println(" - Found our data characteristic"); 173 | } 174 | return true; 175 | } // client is connected 176 | } // if remote service is not NULL 177 | return false; 178 | } /* connectToserver() */ 179 | #endif // ESP32 180 | // 181 | // BLE implementation 182 | // 183 | uint16_t BLEDisplay::BLEReceive() 184 | { 185 | uint16_t value = 0; 186 | #ifdef HAL_ESP32_HAL_H_ 187 | value = pCharacteristicData->readUInt16(); 188 | #endif 189 | #ifdef ARDUINO_ARDUINO_NANO33BLE 190 | pCharacteristicData.readValue(&value, 2); 191 | #endif 192 | #ifdef ARDUINO_NRF52_ADAFRUIT 193 | myDataChar.read((void *)&value, (uint16_t)2); 194 | #endif 195 | return value; 196 | } /* BLEReceive() */ 197 | 198 | int BLEDisplay::BLESendVarData(uint16_t *data, int count, void *varData) 199 | { 200 | uint8_t ucTemp[256]; 201 | static int iCounter = 0; 202 | int iSize = count*2 + data[1]; 203 | 204 | data[0] |= (uint16_t)(iSize << 8); // total packet size in bytes 205 | iCounter++; // count packets to know when we need to ask for a response 206 | // If we send too many without responses, the BLE lib will hang 207 | memcpy(ucTemp, data, count*sizeof(uint16_t)); // non-payload part 208 | memcpy(&ucTemp[count*sizeof(uint16_t)], varData, data[1]); // var payload 209 | #ifdef HAL_ESP32_HAL_H_ 210 | pCharacteristicData->writeValue(ucTemp, iSize, (iCounter & 0x3f) == 0); 211 | #endif 212 | #ifdef ARDUINO_ARDUINO_NANO33BLE 213 | pCharacteristicData.writeValue(ucTemp, iSize, (iCounter & 0x3f) == 0); 214 | #endif 215 | #ifdef ARDUINO_NRF52_ADAFRUIT 216 | myDataChar.write((const void *)ucTemp, (uint16_t)iSize); 217 | #endif 218 | return RD_SUCCESS; 219 | } /* BLESendVarData() */ 220 | 221 | int BLEDisplay::BLESend(uint16_t *data, int count) 222 | { 223 | data[count] = crc_16((uint8_t *)data, count * sizeof(uint16_t)); 224 | #ifdef HAL_ESP32_HAL_H_ 225 | pCharacteristicData->writeValue((uint8_t *)data, (count+1)*sizeof(uint16_t), false); 226 | #endif 227 | #ifdef ARDUINO_ARDUINO_NANO33BLE 228 | pCharacteristicData.writeValue((uint8_t *)data, (count+1)*sizeof(uint16_t), false); 229 | #endif 230 | #ifdef ARDUINO_NRF52_ADAFRUIT 231 | myDataChar.write((const void *)data, (uint16_t)(count+1)*sizeof(uint16_t)); 232 | #endif 233 | return RD_SUCCESS; 234 | } /* BLESend() */ 235 | 236 | int BLEDisplay::begin(uint16_t display_type) 237 | { 238 | _display_type = display_type; 239 | _bConnected = 0; 240 | #ifdef ARDUINO_NRF52_ADAFRUIT 241 | isConnected = 0; 242 | // Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1 243 | // SRAM usage required by SoftDevice will increase dramatically with number of connections 244 | Bluefruit.begin(0, 1); 245 | /* Set the device name */ 246 | Bluefruit.setName("Bluefruit52"); 247 | /* Set the LED interval for blinky pattern on BLUE LED */ 248 | Bluefruit.setConnLedInterval(250); 249 | Bluefruit.configCentralBandwidth(BANDWIDTH_MAX); 250 | myService.begin(); // start my client service 251 | // Initialize client characteristics of VirtualDisplay. 252 | // Note: Client Chars will be added to the last service that is begin()ed. 253 | myDataChar.setNotifyCallback(notify_callback); 254 | myDataChar.begin(); 255 | // Callbacks for Central 256 | Bluefruit.Central.setConnectCallback(connect_callback); 257 | Bluefruit.Central.setDisconnectCallback(disconnect_callback); 258 | /* Start Central Scanning 259 | * - Enable auto scan if disconnected 260 | * - Filter out packet with a min rssi 261 | * - Interval = 100 ms, window = 50 ms 262 | * - Use active scan (used to retrieve the optional scan response adv packet) 263 | * - Start(0) = will scan forever since no timeout is given 264 | */ 265 | Bluefruit.Scanner.setRxCallback(scan_callback); 266 | Bluefruit.Scanner.restartOnDisconnect(true); 267 | // Bluefruit.Scanner.filterRssi(-72); 268 | // Bluefruit.Scanner.filterUuid(myServiceUUID); 269 | Bluefruit.Scanner.setInterval(160, 80); // in units of 0.625 ms 270 | Bluefruit.Scanner.useActiveScan(true); // Request scan response data 271 | Bluefruit.Scanner.start(0); // 0 = Don't stop scanning after n seconds 272 | { 273 | int iTimeout = 0; // scan / try for up to 10 seconds 274 | while (iTimeout < 10 && isConnected == 0) 275 | { 276 | iTimeout++; 277 | delay(1000); 278 | } 279 | if (isConnected == 1) 280 | { 281 | _bConnected = 1; 282 | delay(2000); // wait for things to settle 283 | } 284 | } 285 | #endif // Adafruit nrf52 286 | #ifdef HAL_ESP32_HAL_H_ 287 | pCharacteristicData = NULL; 288 | BLEDevice::init("ESP32BLE"); 289 | Scanned_Name[0] = 0; 290 | pBLEScan = BLEDevice::getScan(); //create new scan 291 | pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); //Call the class that is defined above 292 | pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster 293 | foundDevices = pBLEScan->start(3); //Scan for 3 seconds to find the Fitness band 294 | 295 | while (!paired && foundDevices.getCount() >= 1) 296 | { 297 | if (strcmp(Scanned_Name,VD_BLE_Name.c_str()) == 0) // found the device we want 298 | { 299 | // pBLEScan->stop(); // stop scanning 300 | yield(); 301 | Serial.println("Found Device :-)... connecting to Server as client"); 302 | Scanned_Name[0] = 0; // don't reconnect until we scan again 303 | if (connectToserver(*Server_BLE_Address)) 304 | { 305 | paired = true; 306 | break; 307 | } 308 | } 309 | } 310 | if (pCharacteristicData != nullptr) 311 | { 312 | _bConnected = 1; 313 | } 314 | else 315 | { 316 | return RD_NOT_CONNECTED; 317 | } 318 | #endif // ESP32 319 | 320 | #ifdef ARDUINO_ARDUINO_NANO33BLE 321 | long lTime; 322 | bool bFound = false; 323 | BLE.begin(); 324 | BLE.setLocalName("Nano33BLE"); 325 | Serial.println("About to start scan"); 326 | BLE.scanForName("RemoteDisplay", true); 327 | lTime = millis(); 328 | while (!bFound && (millis() - lTime) < 5000L) 329 | { 330 | // check if a peripheral has been discovered 331 | peripheral = BLE.available(); 332 | if (peripheral) 333 | { 334 | // discovered a peripheral, print out address, local name, and advertised service 335 | Serial.print("Found "); 336 | Serial.print(peripheral.address()); 337 | Serial.print(" '"); 338 | Serial.print(peripheral.localName()); 339 | Serial.print("' "); 340 | Serial.print(peripheral.advertisedServiceUuid()); 341 | Serial.println(); 342 | if (memcmp(peripheral.localName().c_str(), "RemoteDisplay", 14) == 0) 343 | { // found the one we're looking for 344 | BLE.stopScan(); 345 | bFound = 1; 346 | } // found it in scan 347 | } // peripheral located 348 | else 349 | { 350 | delay(50); 351 | } 352 | } // while scanning 353 | if (bFound) 354 | { 355 | // Connect to the BLE Server. 356 | Serial.println("connection attempt..."); 357 | if (peripheral.connect()) 358 | { 359 | Serial.println("Connected!"); 360 | peripheral.discoverAttributes(); 361 | Serial.println("discovered attributes"); 362 | if (peripheral.discoverService("0000fea0-1234-1000-8000-00805f9b34fb")) 363 | { 364 | Serial.println("Discovered fea0 service"); 365 | prtService = peripheral.service("0000fea0-1234-1000-8000-00805f9b34fb"); // get the remote display service 366 | if (prtService) 367 | // if (1) 368 | { 369 | Serial.println("Got the service"); 370 | pCharacteristicData = prtService.characteristic("0000fea1-1234-1000-8000-00805f9b34fb"); 371 | if (pCharacteristicData) 372 | { 373 | Serial.println("Got the characteristics"); 374 | Serial.print("Properties = 0x"); 375 | Serial.println(pCharacteristicData.properties(), HEX); 376 | _bConnected = 1; 377 | } 378 | } else { 379 | Serial.println("Couldn't get service"); 380 | } 381 | } 382 | } 383 | } 384 | #endif // Nano33 385 | 386 | #if defined( HAL_ESP32_HAL_H_ ) || defined( ARDUINO_ARDUINO_NANO33BLE ) || defined( ARDUINO_NRF52_ADAFRUIT ) 387 | if (_bConnected) 388 | { 389 | uint16_t u16Tmp[4]; 390 | u16Tmp[0] = RD_INIT; 391 | u16Tmp[1] = display_type; 392 | return BLESend(u16Tmp, 2); // send to the remote server 393 | } 394 | 395 | return RD_NOT_CONNECTED; 396 | #endif 397 | } /* begin() */ 398 | 399 | int BLEDisplay::fill(uint16_t u16Color) 400 | { 401 | uint16_t u16Tmp[4]; 402 | if (!_bConnected) 403 | return RD_NOT_CONNECTED; 404 | u16Tmp[0] = RD_FILL; 405 | u16Tmp[1] = u16Color; 406 | return BLESend(u16Tmp, 2); // send to the remote server 407 | } /* fill() */ 408 | 409 | int BLEDisplay::drawLine(int x1, int y1, int x2, int y2, uint16_t u16Color) 410 | { 411 | uint16_t u16Tmp[8]; 412 | if (!_bConnected) 413 | return RD_NOT_CONNECTED; 414 | u16Tmp[0] = RD_DRAW_LINE; 415 | u16Tmp[1] = (uint16_t)x1; 416 | u16Tmp[2] = (uint16_t)y1; 417 | u16Tmp[3] = (uint16_t)x2; 418 | u16Tmp[4] = (uint16_t)y2; 419 | u16Tmp[5] = u16Color; 420 | return BLESend(u16Tmp, 6); // send to the remote server 421 | } /* drawLine() */ 422 | 423 | // transmit font data to the display server 424 | int BLEDisplay::setFont(const GFXfont *pFont, int fontIndex) 425 | { 426 | int iMaxOffset, iMaxIndex; 427 | int rc, i, iSize, iCount; 428 | uint16_t u16Tmp[8]; 429 | GFXglyph *glyph; 430 | uint8_t *pData; 431 | // figure out the bitmap image data size 432 | glyph = pFont->glyph; // glyph table 433 | iMaxOffset = 0; 434 | // the data may not be in order (it probably is) 435 | iSize = pFont->last - pFont->first; 436 | for (i=0; i < iSize; i++) { 437 | if (glyph[i].bitmapOffset > iMaxOffset) { 438 | iMaxOffset = glyph[i].bitmapOffset; 439 | iMaxIndex = i; 440 | } 441 | } // for i 442 | // Now that we have the max bitmap offset, calculate the last bitmap byte 443 | // Serial.printf("max bitmap offset = %d\n", iMaxOffset); 444 | iSize = glyph[iMaxIndex].width * glyph[iMaxIndex].height; 445 | iSize = (iSize + 7)/8; // number of bytes 446 | iMaxOffset += iSize; // we now know how much bitmap data to send 447 | // send the font info first to allow the server to allocate memory 448 | u16Tmp[0] = RD_SET_FONT_INFO; 449 | u16Tmp[1] = fontIndex; 450 | u16Tmp[2] = pFont->first; ///< ASCII extents (first char) 451 | u16Tmp[3] = pFont->last; ///< ASCII extents (last char) 452 | u16Tmp[4] = pFont->yAdvance; ///< Newline distance (y axis) 453 | u16Tmp[5] = (uint16_t)iMaxOffset; // bitmap size 454 | u16Tmp[6] = (uint16_t)(iMaxOffset >> 16); 455 | rc = BLESend(u16Tmp, 7); 456 | 457 | iCount = (iMaxOffset + MAX_DATA_BLOCK-1) / MAX_DATA_BLOCK; // send max 230 bytes per block 458 | // Serial.printf("About to send font bitmap size %d, blocks: %d\n", iMaxOffset, iCount); 459 | pData = (uint8_t *)pFont->bitmap; // start of bitmap data 460 | // Transmit the font's bitmap data first 461 | rc = RD_SUCCESS; 462 | for (i=0; i MAX_DATA_BLOCK) ? MAX_DATA_BLOCK : iMaxOffset; 468 | u16Tmp[1] = iSize; // payload size 469 | rc = BLESendVarData(u16Tmp, 5, (void *)pData); // send to the remote server 470 | pData += iSize; 471 | iMaxOffset -= iSize; 472 | } // for each block of bitmap data 473 | // Now transmit the font index data 474 | iMaxOffset = pFont->last - pFont->first; // number of entries 475 | iMaxOffset *= sizeof(GFXglyph); // might be packed structure or not 476 | pData = (uint8_t *)pFont->glyph; 477 | iCount = (iMaxOffset + MAX_DATA_BLOCK-1) / MAX_DATA_BLOCK; // send max 230 bytes per block 478 | // Serial.printf("About to send font index size %d, blocks: %d\n", iMaxOffset, iCount); 479 | for (i=0; i MAX_DATA_BLOCK) ? MAX_DATA_BLOCK : iMaxOffset; 482 | u16Tmp[1] = iSize; // payload size 483 | u16Tmp[2] = (i | (iCount << 8)); // current block / total blocks 484 | u16Tmp[3] = fontIndex; // || (sizeof(GFXglyph) << 8); // send structure size too because it can be 7 or 8 485 | rc = BLESendVarData(u16Tmp, 4, (void *)pData); // send to the remote server 486 | pData += iSize; 487 | iMaxOffset -= iSize; 488 | } // for each block of bitmap data 489 | return rc; 490 | } /* BLEDisplay::setFont() */ 491 | 492 | int BLEDisplay::setBitmap(uint8_t bitmapIndex, const uint8_t *pBitmap, int iBitmapSize) 493 | { 494 | int i, iSize, iCount, rc = RD_SUCCESS; 495 | uint8_t *pData; 496 | uint16_t u16Tmp[6]; 497 | 498 | iCount = (iBitmapSize + MAX_DATA_BLOCK-1) / MAX_DATA_BLOCK; // send max 230 bytes per block 499 | pData = (uint8_t *)pBitmap; 500 | // Transmit the font's bitmap data first 501 | rc = RD_SUCCESS; 502 | for (i=0; i MAX_DATA_BLOCK) ? MAX_DATA_BLOCK : iBitmapSize; 508 | u16Tmp[1] = iSize; // payload size 509 | rc = BLESendVarData(u16Tmp, 5, (void *)pData); // send to the remote server 510 | pData += iSize; 511 | iBitmapSize -= iSize; 512 | } // for each block of bitmap data 513 | 514 | return rc; 515 | } /* setBitmap() */ 516 | 517 | int BLEDisplay::drawBitmap(int x, int y, int bitmapIndex, int stretch) 518 | { 519 | uint16_t u16Tmp[6]; 520 | 521 | if (bitmapIndex < 0 || bitmapIndex >= MAX_BITMAP_INDEX) 522 | return RD_INVALID_PARAMETER; 523 | u16Tmp[0] = RD_DRAW_BITMAP; 524 | u16Tmp[1] = (uint16_t)bitmapIndex; 525 | u16Tmp[2] = (uint16_t)x; 526 | u16Tmp[3] = (uint16_t)y; 527 | u16Tmp[4] = (uint16_t)stretch; 528 | return BLESend(u16Tmp, 5); 529 | } /* drawBitmap() */ 530 | 531 | int BLEDisplay::drawIcon(int x, int y, int iconIndex, int angle, uint16_t u16FGColor, uint16_t u16BGColor) 532 | { 533 | return RD_SUCCESS; 534 | } /* drawIcon() */ 535 | 536 | int BLEDisplay::drawPixel(int x, int y, uint16_t u16Color) 537 | { 538 | uint16_t u16Tmp[8]; 539 | if (!_bConnected) 540 | return RD_NOT_CONNECTED; 541 | u16Tmp[0] = RD_DRAW_PIXEL; 542 | u16Tmp[1] = (uint16_t)x; 543 | u16Tmp[2] = (uint16_t)y; 544 | u16Tmp[3] = u16Color; 545 | return BLESend(u16Tmp, 4); // send to the remote server 546 | } /* drawPixel() */ 547 | 548 | int BLEDisplay::setWindow(int x, int y, int w, int h) 549 | { 550 | uint16_t u16Tmp[8]; 551 | if (!_bConnected) 552 | return RD_NOT_CONNECTED; 553 | u16Tmp[0] = RD_SET_WINDOW; 554 | u16Tmp[1] = (uint16_t)x; 555 | u16Tmp[2] = (uint16_t)y; 556 | u16Tmp[3] = (uint16_t)w; 557 | u16Tmp[4] = (uint16_t)h; 558 | return BLESend(u16Tmp, 5); // send to the remote server 559 | } /* setWindow() */ 560 | 561 | int BLEDisplay::writePixels(uint16_t *pixels, int count, uint8_t bDMA) 562 | { 563 | uint16_t u16Tmp[8]; 564 | if (!_bConnected) 565 | return RD_NOT_CONNECTED; 566 | while (count >= 64) // don't send more than 64 at a time 567 | { 568 | u16Tmp[0] = RD_WRITE_PIXELS; 569 | u16Tmp[1] = 128; // payload size in bytes 570 | u16Tmp[2] = (uint16_t)bDMA; 571 | BLESendVarData(u16Tmp, 3, (void *)pixels); // send to the remote server 572 | pixels += 64; 573 | count -= 64; 574 | } 575 | if (count > 0) 576 | { 577 | u16Tmp[0] = RD_WRITE_PIXELS; 578 | u16Tmp[1] = count*2; // payload size in bytes 579 | u16Tmp[2] = (uint16_t)bDMA; 580 | BLESendVarData(u16Tmp, 3, (void *)pixels); // send to the remote 581 | } 582 | return RD_SUCCESS; 583 | } /* writePixels() */ 584 | 585 | int BLEDisplay::drawRect(int x, int y, int w, int h, uint16_t u16Color, int bFilled) 586 | { 587 | uint16_t u16Tmp[8]; 588 | if (!_bConnected) 589 | return RD_NOT_CONNECTED; 590 | u16Tmp[0] = RD_DRAW_RECT; 591 | u16Tmp[1] = (uint16_t)x; 592 | u16Tmp[2] = (uint16_t)y; 593 | u16Tmp[3] = (uint16_t)w; 594 | u16Tmp[4] = (uint16_t)h; 595 | u16Tmp[5] = u16Color; 596 | u16Tmp[6] = (uint16_t)bFilled; 597 | return BLESend(u16Tmp, 7); // send to the remote server 598 | } /* drawRect() */ 599 | 600 | int BLEDisplay::drawText(int x, int y, char *szText, uint8_t u8Font, uint16_t u16FGColor, uint16_t u16BGColor) 601 | { 602 | uint16_t u16Tmp[8]; 603 | if (!_bConnected) 604 | return RD_NOT_CONNECTED; 605 | u16Tmp[0] = RD_DRAW_TEXT; 606 | u16Tmp[1] = strlen(szText)+1; // payload size in bytes 607 | u16Tmp[2] = (uint16_t)x; 608 | u16Tmp[3] = (uint16_t)y; 609 | u16Tmp[4] = (uint16_t)u8Font; 610 | u16Tmp[5] = u16FGColor; 611 | u16Tmp[6] = u16BGColor; 612 | return BLESendVarData(u16Tmp, 7, szText); // send to the remote server 613 | } /* drawText() */ 614 | 615 | int BLEDisplay::drawEllipse(int x, int y, int r1, int r2, uint16_t u16Color, int bFilled) 616 | { 617 | uint16_t u16Tmp[8]; 618 | if (!_bConnected) 619 | return RD_NOT_CONNECTED; 620 | u16Tmp[0] = RD_DRAW_ELLIPSE; 621 | u16Tmp[1] = (uint16_t)x; 622 | u16Tmp[2] = (uint16_t)y; 623 | u16Tmp[3] = (uint16_t)r1; 624 | u16Tmp[4] = (uint16_t)r2; 625 | u16Tmp[5] = u16Color; 626 | u16Tmp[6] = bFilled; 627 | return BLESend(u16Tmp, 7); // send to the remote server 628 | } /* drawEllipse() */ 629 | 630 | int BLEDisplay::setOrientation(int angle) 631 | { 632 | int rc; 633 | uint16_t u16Tmp[4]; 634 | if (!_bConnected) 635 | return RD_NOT_CONNECTED; 636 | u16Tmp[0] = RD_SET_ORIENTATION; 637 | u16Tmp[1] = (uint16_t)angle; 638 | rc = BLESend(u16Tmp, 2); // send to the remote server 639 | if (rc == RD_SUCCESS) 640 | _orientation = angle; 641 | return rc; 642 | } /* setOrientation() */ 643 | 644 | uint16_t BLEDisplay::getButtons() 645 | { 646 | return BLEReceive(); 647 | } /* BLEDisplay::getButtons() */ 648 | 649 | int BLEDisplay::dumpBuffer(uint8_t * buffer) 650 | { 651 | if (_bpp == 1) { 652 | uint16_t u16Tmp[4]; 653 | int iCount = (_width * _height) / 8; 654 | u16Tmp[0] = RD_DUMP_BUFFER; 655 | u16Tmp[1] = iCount/2; // in terms of uint16_t's 656 | return BLESendVarData(u16Tmp, 2, (void *)buffer); 657 | } else { 658 | return RD_NOT_SUPPORTED; 659 | } 660 | } /* BLEDisplay::dumpBuffer() */ 661 | 662 | -------------------------------------------------------------------------------- /BLEDisplay.h: -------------------------------------------------------------------------------- 1 | // 2 | // Bluetooth Low Energy (remote) 3 | // 4 | class BLEDisplay : public RemoteDisplay 5 | { 6 | public: 7 | BLEDisplay() : RemoteDisplay() {} 8 | ~BLEDisplay() {} 9 | int begin(uint16_t display_type); 10 | int fill(uint16_t u16Color); 11 | int drawLine(int x1, int y1, int x2, int y2, uint16_t u16Color); 12 | int drawPixel(int x, int y, uint16_t u16Color); 13 | int setWindow(int x, int y, int w, int h); 14 | int writePixels(uint16_t *pixels, int count, uint8_t bDMA); 15 | int dumpBuffer(uint8_t * buffer); 16 | int drawRect(int x, int y, int w, int h, uint16_t u16Color, int bFilled); 17 | int drawBitmap(int x, int y, int bitmapIndex, int stretch); 18 | int drawIcon(int x, int y, int iconIndex, int angle, uint16_t u16FGColor, uint16_t u16BGColor); 19 | int drawText(int x, int y, char *szText, uint8_t u8Font, uint16_t u16FGColor, uint16_t u16BGColor); 20 | int drawEllipse(int x, int y, int r1, int r2, uint16_t u16Color, int bFilled); 21 | int setFont(const GFXfont *pFont, int fontIndex); 22 | int setBitmap(uint8_t bitmapIndex, const uint8_t *pBitmap, int iBitmapSize); 23 | int setOrientation(int angle); 24 | uint16_t getButtons(); 25 | private: 26 | int BLESendVarData(uint16_t *data, int count, void *varData); 27 | int BLESend(uint16_t *data, int count); 28 | uint16_t BLEReceive(); 29 | }; // class BLEDisplay 30 | 31 | -------------------------------------------------------------------------------- /ESLDisplay.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // RemoteDisplay 3 | // 4 | // written by Larry Bank 5 | // bitbank@pobox.com 6 | // Project started 12/2/2020 7 | // Original JPEG code written 26+ years ago :) 8 | // The goal of this code is to decode baseline JPEG images 9 | // using no more than 18K of RAM (if sent directly to an LCD display) 10 | // 11 | // Copyright 2020 BitBank Software, Inc. All Rights Reserved. 12 | // Licensed under the Apache License, Version 2.0 (the "License"); 13 | // you may not use this file except in compliance with the License. 14 | // You may obtain a copy of the License at 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // Unless required by applicable law or agreed to in writing, software 17 | // distributed under the License is distributed on an "AS IS" BASIS, 18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | // See the License for the specific language governing permissions and 20 | // limitations under the License. 21 | //=========================================================================== 22 | // 23 | #include "RemoteDisplay.h" 24 | // 25 | // Electronic Shelf Label Display implementation 26 | // 27 | int ESLDisplay::begin(uint16_t u16DisplayType, int SDAPin, int SCLPin, int bBitBang, uint32_t u32Speed) 28 | { 29 | int rc, iDisp; 30 | 31 | iDisp = u16DisplayType - RD_OLED_128x128 + 1; 32 | rc = obdI2CInit(&_obd, iDisp, -1, 0, 0, (bBitBang == 0), SDAPin, SCLPin, -1, u32Speed); 33 | if (rc >= 0) { 34 | _display_type = u16DisplayType; 35 | obdSetBackBuffer(&_obd, _buffer); 36 | return RD_SUCCESS; 37 | } else { 38 | return RD_INIT_FAILED; 39 | } 40 | } /* ESLDisplay::begin() */ 41 | 42 | int ESLDisplay::dumpBuffer(uint8_t * buffer) 43 | { 44 | obdDumpBuffer(&_obd, buffer); 45 | return RD_SUCCESS; 46 | } /* ESLDisplay::dumpBuffer() */ 47 | void ESLDisplay::shutdown() 48 | { 49 | obdPower(&_obd, 0); 50 | } /* ESLDisplay::shutdown() */ 51 | 52 | int ESLDisplay::setFont(const GFXfont *pFont, int fontIndex) 53 | { 54 | if (fontIndex >= 0 && fontIndex < MAX_FONT_INDEX) { 55 | _fonts[fontIndex] = (GFXfont *)pFont; 56 | return RD_SUCCESS; 57 | } 58 | return RD_INVALID_PARAMETER; 59 | } /* setFont() */ 60 | 61 | int ESLDisplay::setBitmap(uint8_t bitmapIndex, const uint8_t *pBitmap, int iBitmapSize) 62 | { 63 | if (bitmapIndex >= MAX_BITMAP_INDEX) 64 | return RD_INVALID_PARAMETER; 65 | _bitmaps[bitmapIndex] = (uint8_t *)pBitmap; 66 | return 0; 67 | } /* setBitmap() */ 68 | 69 | int ESLDisplay::drawBitmap(int x, int y, int bitmapIndex, int stretch) 70 | { 71 | (void)stretch; // not implemented for OLED displays 72 | if (bitmapIndex < 0 || bitmapIndex >= MAX_BITMAP_INDEX || _bitmaps[bitmapIndex] == NULL) 73 | return RD_INVALID_PARAMETER; 74 | obdLoadBMP(&_obd, _bitmaps[bitmapIndex], x, y, 0); 75 | return RD_SUCCESS; 76 | } /* drawBitmap() */ 77 | 78 | int ESLDisplay::drawIcon(int x, int y, int iconIndex, int angle, uint16_t u16FGColor, uint16_t u16BGColor) 79 | { 80 | if (iconIndex < 0 || iconIndex >= MAX_ICON_INDEX) 81 | return RD_INVALID_PARAMETER; 82 | return RD_SUCCESS; 83 | } /* drawIcon() */ 84 | 85 | int ESLDisplay::fill(uint16_t u16Color) 86 | { 87 | obdFill(&_obd, (u16Color > 0) ? 0xff:0x00, 1); 88 | return RD_SUCCESS; 89 | } /* ESLDisplay::fill() */ 90 | 91 | int ESLDisplay::drawLine(int x1, int y1, int x2, int y2, uint16_t u16Color) 92 | { 93 | obdDrawLine(&_obd, x1, y1, x2, y2, (uint8_t)(u16Color > 0), 1); 94 | return RD_SUCCESS; 95 | } /* ESLDisplay::drawLine() */ 96 | 97 | int ESLDisplay::drawPixel(int x, int y, uint16_t u16Color) 98 | { 99 | obdSetPixel(&_obd, x, y, (uint8_t)(u16Color > 0), 1); 100 | return RD_SUCCESS; 101 | } /* ESLDisplay::drawPixel() */ 102 | 103 | int ESLDisplay::drawText(int x, int y, char *szText, uint8_t u8Font, uint16_t u16FGColor, uint16_t u16BGColor) 104 | { 105 | obdWriteString(&_obd, 0, x, y/8, szText, u8Font, 0, 1); 106 | return RD_SUCCESS; 107 | } /* ESLDisplay::drawText() */ 108 | 109 | int ESLDisplay::drawEllipse(int x, int y, int r1, int r2, uint16_t u16Color, int bFilled) 110 | { 111 | obdEllipse(&_obd, x, y, r1, r2, (uint8_t)(u16Color > 0), bFilled); 112 | obdDumpBuffer(&_obd, NULL); // show it 113 | return RD_SUCCESS; 114 | } /* ESLDisplay::drawEllipse() */ 115 | 116 | int ESLDisplay::drawRect(int x, int y, int w, int h, uint16_t u16Color, int bFilled) 117 | { 118 | obdRectangle(&_obd, x, y, x+w-1, y+h-1, u16Color, bFilled); 119 | } /* drawRect() */ 120 | 121 | int ESLDisplay::setWindow(int x, int y, int w, int h) 122 | { 123 | _x = x; 124 | _y = y; 125 | _w = w; 126 | _h = h; 127 | return RD_SUCCESS; 128 | } /* ESLDisplay::setWindow() */ 129 | 130 | int ESLDisplay::writePixels(uint16_t *pixels, int count, uint8_t bDMA) 131 | { 132 | (void)pixels; 133 | (void)count; 134 | (void)bDMA; 135 | // DEBUG - missing implementation 136 | return RD_SUCCESS; 137 | } /* ESLDisplay::writePixels() */ 138 | 139 | int ESLDisplay::setOrientation(int angle) 140 | { 141 | // DEBUG 142 | int i = LCD_ORIENTATION_0; 143 | if (angle != 0 && angle != 90 && angle != 180 && angle != 270) 144 | return RD_INVALID_PARAMETER; 145 | if (angle == 0) 146 | i = LCD_ORIENTATION_0; 147 | // else if (angle == 90) 148 | // i = LCD_ORIENTATION_90; 149 | else if (angle == 180) 150 | i = LCD_ORIENTATION_180; 151 | // else i = LCD_ORIENTATION_270; 152 | // spilcdSetOrientation(&_lcd, i); 153 | _orientation = angle; 154 | return RD_SUCCESS; 155 | } /* ESLDisplay::setOrientation() */ 156 | 157 | uint16_t ESLDisplay::getButtons() 158 | { 159 | return _get_buttons(); 160 | } /* ESLDisplay::getButtons() */ 161 | 162 | -------------------------------------------------------------------------------- /ESLDisplay.h: -------------------------------------------------------------------------------- 1 | lass ESLDisplay : public RemoteDisplay 2 | { 3 | public: 4 | ESLDisplay() : RemoteDisplay() {} 5 | ~ESLDisplay() {} 6 | int begin(uint16_t u16DisplayType, int SDAPin = -1, int SCLPin = -1, int bBitBang = 0, uint32_t u32Speed = 400000); 7 | void shutdown(); 8 | int fill(uint16_t u16Color); 9 | int drawLine(int x1, int y1, int x2, int y2, uint16_t u16Color); 10 | int drawPixel(int x, int y, uint16_t u16Color); 11 | int setWindow(int x, int y, int w, int h); 12 | int writePixels(uint16_t *pixels, int count, uint8_t bDMA); 13 | int drawRect(int x, int y, int w, int h, uint16_t u16Color, int bFilled); 14 | int drawText(int x, int y, char *szText, uint8_t u8Font, uint16_t u16FGColor, uint16_t u16BGColor); 15 | int drawBitmap(int x, int y, int bitmapIndex, int stretch); 16 | int drawIcon(int x, int y, int iconIndex, int angle, uint16_t u16FGColor, uint16_t u16BGColor); 17 | int drawEllipse(int x, int y, int r1, int r2, uint16_t u16Color, int bFilled); 18 | int setFont(const GFXfont *pFont, int fontIndex); 19 | int setBitmap(uint8_t bitmapIndex, const uint8_t *pBitmap, int iBitmapSize); 20 | int setOrientation(int angle); 21 | int dumpBuffer(uint8_t * buffer); 22 | uint16_t getButtons(); 23 | private: 24 | OBDISP _obd; 25 | GFXfont *_fonts[MAX_FONT_INDEX]; 26 | uint8_t *_bitmaps[MAX_BITMAP_INDEX]; 27 | uint8_t *_icons[MAX_ICON_INDEX]; 28 | int _x, _y, _w, _h; // current window info 29 | uint8_t _buffer[1024]; // DEBUG 30 | 31 | }; // class ESLDisplay 32 | 33 | -------------------------------------------------------------------------------- /I2CDisplay.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // RemoteDisplay 3 | // 4 | // written by Larry Bank 5 | // bitbank@pobox.com 6 | // Project started 12/2/2020 7 | // Original JPEG code written 26+ years ago :) 8 | // The goal of this code is to decode baseline JPEG images 9 | // using no more than 18K of RAM (if sent directly to an LCD display) 10 | // 11 | // Copyright 2020 BitBank Software, Inc. All Rights Reserved. 12 | // Licensed under the Apache License, Version 2.0 (the "License"); 13 | // you may not use this file except in compliance with the License. 14 | // You may obtain a copy of the License at 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // Unless required by applicable law or agreed to in writing, software 17 | // distributed under the License is distributed on an "AS IS" BASIS, 18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | // See the License for the specific language governing permissions and 20 | // limitations under the License. 21 | //=========================================================================== 22 | // 23 | #include "RemoteDisplay.h" 24 | // 25 | // I2C Display implementation 26 | // 27 | int I2CDisplay::begin(uint16_t u16DisplayType, int SDAPin, int SCLPin, int bBitBang, uint32_t u32Speed) 28 | { 29 | int rc, iDisp; 30 | 31 | iDisp = u16DisplayType - RD_OLED_128x128 + 1; 32 | rc = obdI2CInit(&_obd, iDisp, -1, 0, 0, (bBitBang == 0), SDAPin, SCLPin, -1, u32Speed); 33 | if (rc >= 0) { 34 | _display_type = u16DisplayType; 35 | obdSetBackBuffer(&_obd, _buffer); 36 | return RD_SUCCESS; 37 | } else { 38 | return RD_INIT_FAILED; 39 | } 40 | } /* I2CDisplay::begin() */ 41 | 42 | int I2CDisplay::dumpBuffer(uint8_t * buffer) 43 | { 44 | obdDumpBuffer(&_obd, buffer); 45 | return RD_SUCCESS; 46 | } /* I2CDisplay::dumpBuffer() */ 47 | void I2CDisplay::shutdown() 48 | { 49 | obdPower(&_obd, 0); 50 | } /* I2CDisplay::shutdown() */ 51 | 52 | int I2CDisplay::setFont(const GFXfont *pFont, int fontIndex) 53 | { 54 | if (fontIndex >= 0 && fontIndex < MAX_FONT_INDEX) { 55 | _fonts[fontIndex] = (GFXfont *)pFont; 56 | return RD_SUCCESS; 57 | } 58 | return RD_INVALID_PARAMETER; 59 | } /* setFont() */ 60 | 61 | int I2CDisplay::setBitmap(uint8_t bitmapIndex, const uint8_t *pBitmap, int iBitmapSize) 62 | { 63 | if (bitmapIndex >= MAX_BITMAP_INDEX) 64 | return RD_INVALID_PARAMETER; 65 | _bitmaps[bitmapIndex] = (uint8_t *)pBitmap; 66 | return 0; 67 | } /* setBitmap() */ 68 | 69 | int I2CDisplay::drawBitmap(int x, int y, int bitmapIndex, int stretch) 70 | { 71 | (void)stretch; // not implemented for OLED displays 72 | if (bitmapIndex < 0 || bitmapIndex >= MAX_BITMAP_INDEX || _bitmaps[bitmapIndex] == NULL) 73 | return RD_INVALID_PARAMETER; 74 | obdLoadBMP(&_obd, _bitmaps[bitmapIndex], x, y, 0); 75 | return RD_SUCCESS; 76 | } /* drawBitmap() */ 77 | 78 | int I2CDisplay::drawIcon(int x, int y, int iconIndex, int angle, uint16_t u16FGColor, uint16_t u16BGColor) 79 | { 80 | if (iconIndex < 0 || iconIndex >= MAX_ICON_INDEX) 81 | return RD_INVALID_PARAMETER; 82 | return RD_SUCCESS; 83 | } /* drawIcon() */ 84 | 85 | int I2CDisplay::fill(uint16_t u16Color) 86 | { 87 | obdFill(&_obd, (u16Color > 0) ? 0xff:0x00, 1); 88 | return RD_SUCCESS; 89 | } /* I2CDisplay::fill() */ 90 | 91 | int I2CDisplay::drawLine(int x1, int y1, int x2, int y2, uint16_t u16Color) 92 | { 93 | obdDrawLine(&_obd, x1, y1, x2, y2, (uint8_t)(u16Color > 0), 1); 94 | return RD_SUCCESS; 95 | } /* I2CDisplay::drawLine() */ 96 | 97 | int I2CDisplay::drawPixel(int x, int y, uint16_t u16Color) 98 | { 99 | obdSetPixel(&_obd, x, y, (uint8_t)(u16Color > 0), 1); 100 | return RD_SUCCESS; 101 | } /* I2CDisplay::drawPixel() */ 102 | 103 | int I2CDisplay::drawText(int x, int y, char *szText, uint8_t u8Font, uint16_t u16FGColor, uint16_t u16BGColor) 104 | { 105 | obdWriteString(&_obd, 0, x, y/8, szText, u8Font, 0, 1); 106 | return RD_SUCCESS; 107 | } /* I2CDisplay::drawText() */ 108 | 109 | int I2CDisplay::drawEllipse(int x, int y, int r1, int r2, uint16_t u16Color, int bFilled) 110 | { 111 | obdEllipse(&_obd, x, y, r1, r2, (uint8_t)(u16Color > 0), bFilled); 112 | obdDumpBuffer(&_obd, NULL); // show it 113 | return RD_SUCCESS; 114 | } /* I2CDisplay::drawEllipse() */ 115 | 116 | int I2CDisplay::drawRect(int x, int y, int w, int h, uint16_t u16Color, int bFilled) 117 | { 118 | obdRectangle(&_obd, x, y, x+w-1, y+h-1, u16Color, bFilled); 119 | } /* drawRect() */ 120 | 121 | int I2CDisplay::setWindow(int x, int y, int w, int h) 122 | { 123 | _x = x; 124 | _y = y; 125 | _w = w; 126 | _h = h; 127 | return RD_SUCCESS; 128 | } /* I2CDisplay::setWindow() */ 129 | 130 | int I2CDisplay::writePixels(uint16_t *pixels, int count, uint8_t bDMA) 131 | { 132 | (void)pixels; 133 | (void)count; 134 | (void)bDMA; 135 | // DEBUG - missing implementation 136 | return RD_SUCCESS; 137 | } /* I2CDisplay::writePixels() */ 138 | 139 | int I2CDisplay::setOrientation(int angle) 140 | { 141 | // DEBUG 142 | int i = LCD_ORIENTATION_0; 143 | if (angle != 0 && angle != 90 && angle != 180 && angle != 270) 144 | return RD_INVALID_PARAMETER; 145 | if (angle == 0) 146 | i = LCD_ORIENTATION_0; 147 | // else if (angle == 90) 148 | // i = LCD_ORIENTATION_90; 149 | else if (angle == 180) 150 | i = LCD_ORIENTATION_180; 151 | // else i = LCD_ORIENTATION_270; 152 | // spilcdSetOrientation(&_lcd, i); 153 | _orientation = angle; 154 | return RD_SUCCESS; 155 | } /* I2CDisplay::setOrientation() */ 156 | 157 | uint16_t I2CDisplay::getButtons() 158 | { 159 | return _get_buttons(); 160 | } /* I2CDisplay::getButtons() */ 161 | 162 | -------------------------------------------------------------------------------- /I2CDisplay.h: -------------------------------------------------------------------------------- 1 | lass I2CDisplay : public RemoteDisplay 2 | { 3 | public: 4 | I2CDisplay() : RemoteDisplay() {} 5 | ~I2CDisplay() {} 6 | int begin(uint16_t u16DisplayType, int SDAPin = -1, int SCLPin = -1, int bBitBang = 0, uint32_t u32Speed = 400000); 7 | void shutdown(); 8 | int fill(uint16_t u16Color); 9 | int drawLine(int x1, int y1, int x2, int y2, uint16_t u16Color); 10 | int drawPixel(int x, int y, uint16_t u16Color); 11 | int setWindow(int x, int y, int w, int h); 12 | int writePixels(uint16_t *pixels, int count, uint8_t bDMA); 13 | int drawRect(int x, int y, int w, int h, uint16_t u16Color, int bFilled); 14 | int drawText(int x, int y, char *szText, uint8_t u8Font, uint16_t u16FGColor, uint16_t u16BGColor); 15 | int drawBitmap(int x, int y, int bitmapIndex, int stretch); 16 | int drawIcon(int x, int y, int iconIndex, int angle, uint16_t u16FGColor, uint16_t u16BGColor); 17 | int drawEllipse(int x, int y, int r1, int r2, uint16_t u16Color, int bFilled); 18 | int setFont(const GFXfont *pFont, int fontIndex); 19 | int setBitmap(uint8_t bitmapIndex, const uint8_t *pBitmap, int iBitmapSize); 20 | int setOrientation(int angle); 21 | int dumpBuffer(uint8_t * buffer); 22 | uint16_t getButtons(); 23 | private: 24 | OBDISP _obd; 25 | GFXfont *_fonts[MAX_FONT_INDEX]; 26 | uint8_t *_bitmaps[MAX_BITMAP_INDEX]; 27 | uint8_t *_icons[MAX_ICON_INDEX]; 28 | int _x, _y, _w, _h; // current window info 29 | uint8_t _buffer[1024]; // DEBUG 30 | 31 | }; // class I2CDisplay 32 | 33 | -------------------------------------------------------------------------------- /M5Core_Server/M5Core_Server.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "/Users/laurencebank/Documents/Arduino/libraries/RemoteDisplay/rd_constants.h" 6 | 7 | //static BLEUUID serviceUUID("fea0"); //Service 8 | static BLEUUID serviceUUID("0000fea0-1234-1000-8000-00805f9b34fb"); //Service 9 | static BLEUUID dataUUID1("0000fea1-1234-1000-8000-00805f9b34fb"); // data charact 10 | //static BLEUUID dataUUID2("0000fea1-0000-1000-8000-00805f9b34fb"); // data charact 11 | BLEServer *pServer; 12 | BLEService *pService; 13 | BLECharacteristic *pCharacteristic; 14 | BLEAdvertising *pAdvertising; 15 | TouchButton b0(0,200,106,40); 16 | TouchButton b1(106,200,106,40); 17 | TouchButton b2(212,200,106,40); 18 | //TouchZone topHalf(0,0,320,120); 19 | //TouchZone bottomHalf(0,120,320,160); 20 | //Gesture swipeDown(topHalf, bottomHalf, "Swipe Down"); 21 | static bool bRefresh = false; 22 | static int original_width, width, original_height, height, bpp, orientation; 23 | static int display_type; 24 | static int cursor_x, window_w, window_x; 25 | static int cursor_y, window_h, window_y; 26 | static uint16_t u16Buttons = 0; 27 | 28 | // Horizontal resolution of each display type 29 | const uint16_t xres_list[] = { 30 | 0, // invalid 31 | 240, // RD_LCD_ILI9341, // 240x320 32 | 176, // RD_LCD_ILI9225, // 176x220 33 | 320, // RD_LCD_HX8357, // 320x480 34 | 128, // RD_LCD_ST7735R, // 128x160 35 | 80, // RD_LCD_ST7735S, // 80x160 with offset of 24,0 36 | 80, // RD_LCD_ST7735S_B, // 80x160 with offset of 26,2 37 | 128, // RD_LCD_SSD1331, 38 | 128, // RD_LCD_SSD1351, 39 | 320, // RD_LCD_ILI9342, // 320x240 IPS 40 | 240, // RD_LCD_ST7789, // 240x320 41 | 240, // RD_LCD_ST7789_240, // 240x240 42 | 240, // RD_LCD_ST7789_135, // 135x240 43 | 240, // RD_LCD_ST7789_NOCS, // 240x240 without CS, vertical offset of 80, MODE3 44 | 132, // RD_LCD_SSD1283A, // 132x132 45 | 320, // RD_LCD_ILI9486, // 320x480 46 | // Monochrome LCDs/OLEDs 47 | 128, // RD_OLED_128x128, 48 | 128, // RD_OLED_128x32, 49 | 128, // RD_OLED_128x64, 50 | 128, // RD_OLED_132x64, 51 | 64, // RD_OLED_64x32, 52 | 96, // RD_OLED_96x16, 53 | 72, // RD_OLED_72x40, 54 | 128, // RD_LCD_UC1701, 55 | 192, // RD_LCD_UC1609, 56 | 96, // RD_LCD_HX1230, 57 | 84, // RD_LCD_NOKIA5110, 58 | 144, // RD_SHARP_144x168, 59 | 400, // RD_SHARP_400x240 60 | 224, // RD_EPOP_50 61 | 320, // RD_EPOP_500 62 | 480 // RD_EPOP_900 63 | }; 64 | // Vertical resolution of each display type 65 | const uint16_t yres_list[] = { 66 | 0, // invalid 67 | 320, // RD_LCD_ILI9341, // 240x320 68 | 220, // RD_LCD_ILI9225, // 176x220 69 | 480, // RD_LCD_HX8357, // 320x480 70 | 160, // RD_LCD_ST7735R, // 128x160 71 | 160, // RD_LCD_ST7735S, // 80x160 with offset of 24,0 72 | 160, // RD_LCD_ST7735S_B, // 80x160 with offset of 26,2 73 | 128, // RD_LCD_SSD1331, 74 | 128, // RD_LCD_SSD1351, 75 | 240, // RD_LCD_ILI9342, // 320x240 IPS 76 | 320, // RD_LCD_ST7789, // 240x320 77 | 240, // RD_LCD_ST7789_240, // 240x240 78 | 135, // RD_LCD_ST7789_135, // 135x240 79 | 240, // RD_LCD_ST7789_NOCS, // 240x240 without CS, vertical offset of 80, MODE3 80 | 132, // RD_LCD_SSD1283A, // 132x132 81 | 480, // RD_LCD_ILI9486, // 320x480 82 | // Monochrome LCDs/OLEDs 83 | 128, // RD_OLED_128x128, 84 | 32, // RD_OLED_128x32, 85 | 64, // RD_OLED_128x64, 86 | 64, // RD_OLED_132x64, 87 | 32, // RD_OLED_64x32, 88 | 16, // RD_OLED_96x16, 89 | 40, // RD_OLED_72x40, 90 | 64, // RD_LCD_UC1701, 91 | 64, // RD_LCD_UC1609, 92 | 68, // RD_LCD_HX1230, 93 | 48, // RD_LCD_NOKIA5110, 94 | 168, // RD_SHARP_144x168, 95 | 240, // RD_SHARP_400x240 96 | 90, // RD_EPOP_50 97 | 240, // RD_EPOP_500 98 | 360 // RD_EPOP_900 99 | }; 100 | // Bits per pixel of each display type 101 | const uint16_t bpp_list[] = { 102 | 0, // invalid 103 | 16, // RD_LCD_ILI9341, // 240x320 104 | 16, // RD_LCD_ILI9225, // 176x220 105 | 16, // RD_LCD_HX8357, // 320x480 106 | 16, // RD_LCD_ST7735R, // 128x160 107 | 16, // RD_LCD_ST7735S, // 80x160 with offset of 24,0 108 | 16, // RD_LCD_ST7735S_B, // 80x160 with offset of 26,2 109 | 16, // RD_LCD_SSD1331, 110 | 16, // RD_LCD_SSD1351, 111 | 16, // RD_LCD_ILI9342, // 320x240 IPS 112 | 16, // RD_LCD_ST7789, // 240x320 113 | 16, // RD_LCD_ST7789_240, // 240x240 114 | 16, // RD_LCD_ST7789_135, // 135x240 115 | 16, // RD_LCD_ST7789_NOCS, // 240x240 without CS, vertical offset of 80, MODE3 116 | 16, // RD_LCD_SSD1283A, // 132x132 117 | 16, // RD_LCD_ILI9486, // 320x480 118 | // Monochrome LCDs/OLEDs 119 | 1, // RD_OLED_128x128, 120 | 1, // RD_OLED_128x32, 121 | 1, // RD_OLED_128x64, 122 | 1, // RD_OLED_132x64, 123 | 1, // RD_OLED_64x32, 124 | 1, // RD_OLED_96x16, 125 | 1, // RD_OLED_72x40, 126 | 1, // RD_LCD_UC1701, 127 | 1, // RD_LCD_UC1609, 128 | 1, // RD_LCD_HX1230, 129 | 1, // RD_LCD_NOKIA5110, 130 | 1, // RD_SHARP_144x168, 131 | 1, // RD_SHARP_400x240 132 | 1, // RD_EPOP_50 133 | 1, // RD_EPOP_500 134 | 1 // RD_EPOP_900 135 | }; 136 | // Built-in font data 137 | const uint8_t ucFont[] = { 138 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x5f,0x5f,0x06,0x00,0x00, 139 | 0x00,0x07,0x07,0x00,0x07,0x07,0x00,0x00,0x14,0x7f,0x7f,0x14,0x7f,0x7f,0x14,0x00, 140 | 0x24,0x2e,0x2a,0x6b,0x6b,0x3a,0x12,0x00,0x46,0x66,0x30,0x18,0x0c,0x66,0x62,0x00, 141 | 0x30,0x7a,0x4f,0x5d,0x37,0x7a,0x48,0x00,0x00,0x04,0x07,0x03,0x00,0x00,0x00,0x00, 142 | 0x00,0x1c,0x3e,0x63,0x41,0x00,0x00,0x00,0x00,0x41,0x63,0x3e,0x1c,0x00,0x00,0x00, 143 | 0x08,0x2a,0x3e,0x1c,0x1c,0x3e,0x2a,0x08,0x00,0x08,0x08,0x3e,0x3e,0x08,0x08,0x00, 144 | 0x00,0x00,0x80,0xe0,0x60,0x00,0x00,0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, 145 | 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x60,0x30,0x18,0x0c,0x06,0x03,0x01,0x00, 146 | 0x3e,0x7f,0x59,0x4d,0x47,0x7f,0x3e,0x00,0x40,0x42,0x7f,0x7f,0x40,0x40,0x00,0x00, 147 | 0x62,0x73,0x59,0x49,0x6f,0x66,0x00,0x00,0x22,0x63,0x49,0x49,0x7f,0x36,0x00,0x00, 148 | 0x18,0x1c,0x16,0x53,0x7f,0x7f,0x50,0x00,0x27,0x67,0x45,0x45,0x7d,0x39,0x00,0x00, 149 | 0x3c,0x7e,0x4b,0x49,0x79,0x30,0x00,0x00,0x03,0x03,0x71,0x79,0x0f,0x07,0x00,0x00, 150 | 0x36,0x7f,0x49,0x49,0x7f,0x36,0x00,0x00,0x06,0x4f,0x49,0x69,0x3f,0x1e,0x00,0x00, 151 | 0x00,0x00,0x00,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x80,0xe6,0x66,0x00,0x00,0x00, 152 | 0x08,0x1c,0x36,0x63,0x41,0x00,0x00,0x00,0x00,0x14,0x14,0x14,0x14,0x14,0x14,0x00, 153 | 0x00,0x41,0x63,0x36,0x1c,0x08,0x00,0x00,0x00,0x02,0x03,0x59,0x5d,0x07,0x02,0x00, 154 | 0x3e,0x7f,0x41,0x5d,0x5d,0x5f,0x0e,0x00,0x7c,0x7e,0x13,0x13,0x7e,0x7c,0x00,0x00, 155 | 0x41,0x7f,0x7f,0x49,0x49,0x7f,0x36,0x00,0x1c,0x3e,0x63,0x41,0x41,0x63,0x22,0x00, 156 | 0x41,0x7f,0x7f,0x41,0x63,0x3e,0x1c,0x00,0x41,0x7f,0x7f,0x49,0x5d,0x41,0x63,0x00, 157 | 0x41,0x7f,0x7f,0x49,0x1d,0x01,0x03,0x00,0x1c,0x3e,0x63,0x41,0x51,0x33,0x72,0x00, 158 | 0x7f,0x7f,0x08,0x08,0x7f,0x7f,0x00,0x00,0x00,0x41,0x7f,0x7f,0x41,0x00,0x00,0x00, 159 | 0x30,0x70,0x40,0x41,0x7f,0x3f,0x01,0x00,0x41,0x7f,0x7f,0x08,0x1c,0x77,0x63,0x00, 160 | 0x41,0x7f,0x7f,0x41,0x40,0x60,0x70,0x00,0x7f,0x7f,0x0e,0x1c,0x0e,0x7f,0x7f,0x00, 161 | 0x7f,0x7f,0x06,0x0c,0x18,0x7f,0x7f,0x00,0x1c,0x3e,0x63,0x41,0x63,0x3e,0x1c,0x00, 162 | 0x41,0x7f,0x7f,0x49,0x09,0x0f,0x06,0x00,0x1e,0x3f,0x21,0x31,0x61,0x7f,0x5e,0x00, 163 | 0x41,0x7f,0x7f,0x09,0x19,0x7f,0x66,0x00,0x26,0x6f,0x4d,0x49,0x59,0x73,0x32,0x00, 164 | 0x03,0x41,0x7f,0x7f,0x41,0x03,0x00,0x00,0x7f,0x7f,0x40,0x40,0x7f,0x7f,0x00,0x00, 165 | 0x1f,0x3f,0x60,0x60,0x3f,0x1f,0x00,0x00,0x3f,0x7f,0x60,0x30,0x60,0x7f,0x3f,0x00, 166 | 0x63,0x77,0x1c,0x08,0x1c,0x77,0x63,0x00,0x07,0x4f,0x78,0x78,0x4f,0x07,0x00,0x00, 167 | 0x47,0x63,0x71,0x59,0x4d,0x67,0x73,0x00,0x00,0x7f,0x7f,0x41,0x41,0x00,0x00,0x00, 168 | 0x01,0x03,0x06,0x0c,0x18,0x30,0x60,0x00,0x00,0x41,0x41,0x7f,0x7f,0x00,0x00,0x00, 169 | 0x08,0x0c,0x06,0x03,0x06,0x0c,0x08,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 170 | 0x00,0x00,0x03,0x07,0x04,0x00,0x00,0x00,0x20,0x74,0x54,0x54,0x3c,0x78,0x40,0x00, 171 | 0x41,0x7f,0x3f,0x48,0x48,0x78,0x30,0x00,0x38,0x7c,0x44,0x44,0x6c,0x28,0x00,0x00, 172 | 0x30,0x78,0x48,0x49,0x3f,0x7f,0x40,0x00,0x38,0x7c,0x54,0x54,0x5c,0x18,0x00,0x00, 173 | 0x48,0x7e,0x7f,0x49,0x03,0x06,0x00,0x00,0x98,0xbc,0xa4,0xa4,0xf8,0x7c,0x04,0x00, 174 | 0x41,0x7f,0x7f,0x08,0x04,0x7c,0x78,0x00,0x00,0x44,0x7d,0x7d,0x40,0x00,0x00,0x00, 175 | 0x60,0xe0,0x80,0x84,0xfd,0x7d,0x00,0x00,0x41,0x7f,0x7f,0x10,0x38,0x6c,0x44,0x00, 176 | 0x00,0x41,0x7f,0x7f,0x40,0x00,0x00,0x00,0x7c,0x7c,0x18,0x78,0x1c,0x7c,0x78,0x00, 177 | 0x7c,0x78,0x04,0x04,0x7c,0x78,0x00,0x00,0x38,0x7c,0x44,0x44,0x7c,0x38,0x00,0x00, 178 | 0x84,0xfc,0xf8,0xa4,0x24,0x3c,0x18,0x00,0x18,0x3c,0x24,0xa4,0xf8,0xfc,0x84,0x00, 179 | 0x44,0x7c,0x78,0x4c,0x04,0x0c,0x18,0x00,0x48,0x5c,0x54,0x74,0x64,0x24,0x00,0x00, 180 | 0x04,0x04,0x3e,0x7f,0x44,0x24,0x00,0x00,0x3c,0x7c,0x40,0x40,0x3c,0x7c,0x40,0x00, 181 | 0x1c,0x3c,0x60,0x60,0x3c,0x1c,0x00,0x00,0x3c,0x7c,0x60,0x30,0x60,0x7c,0x3c,0x00, 182 | 0x44,0x6c,0x38,0x10,0x38,0x6c,0x44,0x00,0x9c,0xbc,0xa0,0xa0,0xfc,0x7c,0x00,0x00, 183 | 0x4c,0x64,0x74,0x5c,0x4c,0x64,0x00,0x00,0x08,0x08,0x3e,0x77,0x41,0x41,0x00,0x00, 184 | 0x00,0x00,0x00,0x77,0x77,0x00,0x00,0x00,0x41,0x41,0x77,0x3e,0x08,0x08,0x00,0x00, 185 | 0x02,0x03,0x01,0x03,0x02,0x03,0x01,0x00,0x70,0x78,0x4c,0x46,0x4c,0x78,0x70,0x00}; 186 | const uint8_t ucBigFont[] = { 187 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 188 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 189 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 190 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 191 | 0x00,0x00,0x00,0x00,0xfc,0xfc,0xff,0xff,0xff,0xff,0xfc,0xfc,0x00,0x00,0x00,0x00, 192 | 0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x3f,0x3f,0x3f,0x00,0x00,0x00,0x00,0x00,0x00, 193 | 0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00, 194 | 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00, 195 | 0x00,0x00,0x0f,0x0f,0x3f,0x3f,0x00,0x00,0x00,0x00,0x3f,0x3f,0x0f,0x0f,0x00,0x00, 196 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 197 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 198 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 199 | 0xc0,0xc0,0xfc,0xfc,0xfc,0xfc,0xc0,0xc0,0xfc,0xfc,0xfc,0xfc,0xc0,0xc0,0x00,0x00, 200 | 0xc0,0xc0,0xff,0xff,0xff,0xff,0xc0,0xc0,0xff,0xff,0xff,0xff,0xc0,0xc0,0x00,0x00, 201 | 0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00, 202 | 0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0x00,0x00, 203 | 0xfc,0xfc,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0x0f,0x0f,0x3c,0x3c,0x00,0x00, 204 | 0xf0,0xf0,0xc3,0xc3,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, 205 | 0x00,0x00,0x03,0x03,0x03,0x03,0x3f,0x3f,0x3f,0x3f,0x03,0x03,0x00,0x00,0x00,0x00, 206 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 207 | 0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xf0,0xf0,0x00,0x00, 208 | 0x00,0x00,0xc0,0xc0,0xf0,0xf0,0x3c,0x3c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, 209 | 0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, 210 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 211 | 0x00,0x00,0x3c,0x3c,0xff,0xff,0xc3,0xc3,0xff,0xff,0x3c,0x3c,0x00,0x00,0x00,0x00, 212 | 0xfc,0xfc,0xff,0xff,0x03,0x03,0x0f,0x0f,0xfc,0xfc,0xff,0xff,0x03,0x03,0x00,0x00, 213 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x00,0x00, 214 | 0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 215 | 0x00,0x00,0x30,0x30,0x3f,0x3f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 216 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 217 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 218 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 219 | 0x00,0x00,0x00,0x00,0xf0,0xf0,0xfc,0xfc,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, 220 | 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 221 | 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00, 222 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 223 | 0x00,0x00,0x00,0x00,0x03,0x03,0x0f,0x0f,0xfc,0xfc,0xf0,0xf0,0x00,0x00,0x00,0x00, 224 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, 225 | 0x00,0x00,0x00,0x00,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, 226 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 227 | 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00, 228 | 0x0c,0x0c,0xcc,0xcc,0xff,0xff,0x3f,0x3f,0x3f,0x3f,0xff,0xff,0xcc,0xcc,0x0c,0x0c, 229 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 230 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 231 | 0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, 232 | 0x00,0x00,0x0c,0x0c,0x0c,0x0c,0xff,0xff,0xff,0xff,0x0c,0x0c,0x0c,0x0c,0x00,0x00, 233 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 234 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 235 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 236 | 0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, 237 | 0x00,0x00,0x00,0x00,0x30,0x30,0x3f,0x3f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00, 238 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 239 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 240 | 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x00,0x00, 241 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 242 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 243 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 244 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 245 | 0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00, 246 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 247 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xf0,0xf0,0x00,0x00, 248 | 0x00,0x00,0xc0,0xc0,0xf0,0xf0,0x3c,0x3c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, 249 | 0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 250 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 251 | 0xfc,0xfc,0xff,0xff,0x03,0x03,0x03,0x03,0xc3,0xc3,0xff,0xff,0xfc,0xfc,0x00,0x00, 252 | 0xff,0xff,0xff,0xff,0x30,0x30,0x0f,0x0f,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 253 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, 254 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 255 | 0x00,0x00,0x30,0x30,0x3c,0x3c,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, 256 | 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, 257 | 0x00,0x00,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x00,0x00, 258 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 259 | 0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x03,0x03,0xc3,0xc3,0xff,0xff,0x3c,0x3c,0x00,0x00, 260 | 0xc0,0xc0,0xf0,0xf0,0x3c,0x3c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, 261 | 0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00, 262 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 263 | 0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, 264 | 0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, 265 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, 266 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 267 | 0x00,0x00,0xc0,0xc0,0xf0,0xf0,0x3c,0x3c,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, 268 | 0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0xff,0xff,0xff,0xff,0x0c,0x0c,0x00,0x00, 269 | 0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00, 270 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 271 | 0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00, 272 | 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x0f,0x0f,0xff,0xff,0xfc,0xfc,0x00,0x00, 273 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, 274 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 275 | 0xf0,0xf0,0xfc,0xfc,0x0f,0x0f,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, 276 | 0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, 277 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, 278 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 279 | 0x0f,0x0f,0x0f,0x0f,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xff,0xff,0x00,0x00, 280 | 0x00,0x00,0x00,0x00,0xf0,0xf0,0xfc,0xfc,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, 281 | 0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 282 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 283 | 0xfc,0xfc,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, 284 | 0xfc,0xfc,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, 285 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, 286 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 287 | 0xfc,0xfc,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, 288 | 0x00,0x00,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xff,0xff,0x00,0x00, 289 | 0x00,0x00,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, 290 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 291 | 0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0x00,0x00, 292 | 0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, 293 | 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, 294 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 295 | 0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0x00,0x00, 296 | 0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, 297 | 0x00,0x00,0x00,0x00,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, 298 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 299 | 0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xf0,0xf0,0x3c,0x3c,0x0c,0x0c,0x00,0x00, 300 | 0x00,0x00,0x0c,0x0c,0x3f,0x3f,0xf3,0xf3,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, 301 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x00,0x00, 302 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 303 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 304 | 0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0x00,0x00, 305 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 306 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 307 | 0x00,0x00,0x0c,0x0c,0x3c,0x3c,0xf0,0xf0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, 308 | 0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xf3,0xf3,0x3f,0x3f,0x0c,0x0c,0x00,0x00, 309 | 0x00,0x00,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 310 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 311 | 0x3c,0x3c,0x3f,0x3f,0x03,0x03,0x03,0x03,0xc3,0xc3,0xff,0xff,0x3c,0x3c,0x00,0x00, 312 | 0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x3f,0x3f,0x3f,0x00,0x00,0x00,0x00,0x00,0x00, 313 | 0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00, 314 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 315 | 0xf0,0xf0,0xfc,0xfc,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0xfc,0xfc,0xf0,0xf0,0x00,0x00, 316 | 0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x3f,0x3f,0x00,0x00, 317 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x00,0x00,0x00,0x00, 318 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 319 | 0xc0,0xc0,0xf0,0xf0,0x3c,0x3c,0x0f,0x0f,0x3c,0x3c,0xf0,0xf0,0xc0,0xc0,0x00,0x00, 320 | 0xff,0xff,0xff,0xff,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0xff,0xff,0xff,0xff,0x00,0x00, 321 | 0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, 322 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 323 | 0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, 324 | 0x00,0x00,0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, 325 | 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, 326 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 327 | 0xf0,0xf0,0xfc,0xfc,0x0f,0x0f,0x03,0x03,0x03,0x03,0x0f,0x0f,0x3c,0x3c,0x00,0x00, 328 | 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0x00,0x00, 329 | 0x00,0x00,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, 330 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 331 | 0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0x0f,0x0f,0xfc,0xfc,0xf0,0xf0,0x00,0x00, 332 | 0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 333 | 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, 334 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 335 | 0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0xc3,0xc3,0x0f,0x0f,0x3f,0x3f,0x00,0x00, 336 | 0x00,0x00,0xff,0xff,0xff,0xff,0x03,0x03,0x0f,0x0f,0x00,0x00,0xc0,0xc0,0x00,0x00, 337 | 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00, 338 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 339 | 0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0xc3,0xc3,0x0f,0x0f,0x3f,0x3f,0x00,0x00, 340 | 0x00,0x00,0xff,0xff,0xff,0xff,0x03,0x03,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00, 341 | 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 342 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 343 | 0xf0,0xf0,0xfc,0xfc,0x0f,0x0f,0x03,0x03,0x03,0x03,0x0f,0x0f,0x3c,0x3c,0x00,0x00, 344 | 0xff,0xff,0xff,0xff,0x00,0x00,0x0c,0x0c,0x0c,0x0c,0xfc,0xfc,0xfc,0xfc,0x00,0x00, 345 | 0x00,0x00,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x03,0x03,0x0f,0x0f,0x00,0x00, 346 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 347 | 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 348 | 0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xff,0xff,0x00,0x00, 349 | 0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, 350 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 351 | 0x00,0x00,0x00,0x00,0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0x00,0x00,0x00,0x00, 352 | 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, 353 | 0x00,0x00,0x00,0x00,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00, 354 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 355 | 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0x00,0x00, 356 | 0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, 357 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, 358 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 359 | 0x03,0x03,0xff,0xff,0xff,0xff,0x00,0x00,0xf0,0xf0,0xff,0xff,0x0f,0x0f,0x00,0x00, 360 | 0x00,0x00,0xff,0xff,0xff,0xff,0x0f,0x0f,0x3f,0x3f,0xf0,0xf0,0xc0,0xc0,0x00,0x00, 361 | 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, 362 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 363 | 0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 364 | 0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0x00,0x00, 365 | 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00, 366 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 367 | 0xff,0xff,0xff,0xff,0xfc,0xfc,0xf0,0xf0,0xfc,0xfc,0xff,0xff,0xff,0xff,0x00,0x00, 368 | 0xff,0xff,0xff,0xff,0x00,0x00,0x03,0x03,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 369 | 0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, 370 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 371 | 0xff,0xff,0xff,0xff,0xfc,0xfc,0xf0,0xf0,0xc0,0xc0,0xff,0xff,0xff,0xff,0x00,0x00, 372 | 0xff,0xff,0xff,0xff,0x00,0x00,0x03,0x03,0x0f,0x0f,0xff,0xff,0xff,0xff,0x00,0x00, 373 | 0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, 374 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 375 | 0xf0,0xf0,0xfc,0xfc,0x0f,0x0f,0x03,0x03,0x0f,0x0f,0xfc,0xfc,0xf0,0xf0,0x00,0x00, 376 | 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 377 | 0x00,0x00,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, 378 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 379 | 0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, 380 | 0x00,0x00,0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00, 381 | 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 382 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 383 | 0xfc,0xfc,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, 384 | 0xff,0xff,0xff,0xff,0x00,0x00,0xc0,0xc0,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 385 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0f,0x0f,0xff,0xff,0xff,0xff,0xc3,0xc3,0x00,0x00, 386 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 387 | 0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, 388 | 0x00,0x00,0xff,0xff,0xff,0xff,0x03,0x03,0x0f,0x0f,0xff,0xff,0xf0,0xf0,0x00,0x00, 389 | 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, 390 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 391 | 0x3c,0x3c,0xff,0xff,0xc3,0xc3,0x03,0x03,0x03,0x03,0x3f,0x3f,0x3c,0x3c,0x00,0x00, 392 | 0xc0,0xc0,0xc0,0xc0,0x03,0x03,0x03,0x03,0x0f,0x0f,0xfc,0xfc,0xf0,0xf0,0x00,0x00, 393 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, 394 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 395 | 0x00,0x00,0x3f,0x3f,0x0f,0x0f,0xff,0xff,0xff,0xff,0x0f,0x0f,0x3f,0x3f,0x00,0x00, 396 | 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, 397 | 0x00,0x00,0x00,0x00,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00, 398 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 399 | 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 400 | 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 401 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, 402 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 403 | 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 404 | 0x3f,0x3f,0xff,0xff,0xc0,0xc0,0x00,0x00,0xc0,0xc0,0xff,0xff,0x3f,0x3f,0x00,0x00, 405 | 0x00,0x00,0x00,0x00,0x03,0x03,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, 406 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 407 | 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 408 | 0xff,0xff,0xff,0xff,0xc0,0xc0,0xfc,0xfc,0xc0,0xc0,0xff,0xff,0xff,0xff,0x00,0x00, 409 | 0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00, 410 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 411 | 0x0f,0x0f,0xff,0xff,0xf0,0xf0,0x00,0x00,0xf0,0xf0,0xff,0xff,0x0f,0x0f,0x00,0x00, 412 | 0x00,0x00,0xf0,0xf0,0xff,0xff,0x0f,0x0f,0xff,0xff,0xf0,0xf0,0x00,0x00,0x00,0x00, 413 | 0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, 414 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 415 | 0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 416 | 0x00,0x00,0x00,0x00,0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0x00,0x00,0x00,0x00, 417 | 0x00,0x00,0x00,0x00,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00, 418 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 419 | 0x3f,0x3f,0x0f,0x0f,0x03,0x03,0x03,0x03,0xc3,0xc3,0xff,0xff,0x3f,0x3f,0x00,0x00, 420 | 0xc0,0xc0,0xf0,0xf0,0x3c,0x3c,0x0f,0x0f,0x03,0x03,0x00,0x00,0xc0,0xc0,0x00,0x00, 421 | 0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00, 422 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 423 | 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00, 424 | 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 425 | 0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x00,0x00,0x00,0x00, 426 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 427 | 0xfc,0xfc,0xf0,0xf0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 428 | 0x00,0x00,0x03,0x03,0x0f,0x0f,0x3f,0x3f,0xfc,0xfc,0xf0,0xf0,0xc0,0xc0,0x00,0x00, 429 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x0f,0x0f,0x00,0x00, 430 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 431 | 0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, 432 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, 433 | 0x00,0x00,0x00,0x00,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00, 434 | 0x00,0x00,0x00,0x00,0xc0,0xc0,0xf0,0xf0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, 435 | 0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x00,0x00, 436 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 437 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 438 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 439 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 440 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 441 | 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0, 442 | 0x00,0x00,0x00,0x00,0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 443 | 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, 444 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 445 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 446 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 447 | 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, 448 | 0xf0,0xf0,0xfc,0xfc,0x0c,0x0c,0x0c,0x0c,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, 449 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x00,0x00, 450 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 451 | 0x03,0x03,0xff,0xff,0xff,0xff,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, 452 | 0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, 453 | 0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, 454 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 455 | 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, 456 | 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x00,0x00, 457 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, 458 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 459 | 0x00,0x00,0x00,0x00,0xc0,0xc0,0xc3,0xc3,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, 460 | 0xfc,0xfc,0xff,0xff,0x03,0x03,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, 461 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x00,0x00, 462 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 463 | 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, 464 | 0xff,0xff,0xff,0xff,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00, 465 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, 466 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 467 | 0x00,0x00,0xfc,0xfc,0xff,0xff,0x03,0x03,0x0f,0x0f,0x3c,0x3c,0x00,0x00,0x00,0x00, 468 | 0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 469 | 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 470 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 471 | 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00, 472 | 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, 473 | 0xc3,0xc3,0xcf,0xcf,0x0c,0x0c,0x0c,0x0c,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, 474 | 0x00,0x00,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, 475 | 0x03,0x03,0xff,0xff,0xff,0xff,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, 476 | 0x00,0x00,0xff,0xff,0xff,0xff,0x03,0x03,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 477 | 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, 478 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 479 | 0x00,0x00,0x00,0x00,0xc0,0xc0,0xcf,0xcf,0xcf,0xcf,0x00,0x00,0x00,0x00,0x00,0x00, 480 | 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, 481 | 0x00,0x00,0x00,0x00,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00, 482 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 483 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xcf,0xcf,0xcf,0xcf,0x00,0x00, 484 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 485 | 0x00,0x00,0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 486 | 0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00, 487 | 0x03,0x03,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00, 488 | 0x00,0x00,0xff,0xff,0xff,0xff,0x3c,0x3c,0xff,0xff,0xc3,0xc3,0x00,0x00,0x00,0x00, 489 | 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, 490 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 491 | 0x00,0x00,0x00,0x00,0x03,0x03,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, 492 | 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, 493 | 0x00,0x00,0x00,0x00,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00, 494 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 495 | 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, 496 | 0xff,0xff,0xff,0xff,0x03,0x03,0xff,0xff,0x03,0x03,0xff,0xff,0xff,0xff,0x00,0x00, 497 | 0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x0f,0x0f,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, 498 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 499 | 0xc0,0xc0,0xc0,0xc0,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, 500 | 0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 501 | 0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, 502 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 503 | 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, 504 | 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 505 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, 506 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 507 | 0xc0,0xc0,0xc0,0xc0,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, 508 | 0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 509 | 0x00,0x00,0xff,0xff,0xff,0xff,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, 510 | 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 511 | 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00, 512 | 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, 513 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, 514 | 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00, 515 | 0xc0,0xc0,0xc0,0xc0,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, 516 | 0x00,0x00,0xff,0xff,0xff,0xff,0x03,0x03,0x00,0x00,0x03,0x03,0x0f,0x0f,0x00,0x00, 517 | 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 518 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 519 | 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, 520 | 0x03,0x03,0x0f,0x0f,0x3c,0x3c,0x30,0x30,0xf0,0xf0,0xc3,0xc3,0x03,0x03,0x00,0x00, 521 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, 522 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 523 | 0xc0,0xc0,0xc0,0xc0,0xfc,0xfc,0xff,0xff,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, 524 | 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 525 | 0x00,0x00,0x00,0x00,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, 526 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 527 | 0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, 528 | 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, 529 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x00,0x00, 530 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 531 | 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00, 532 | 0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 533 | 0x00,0x00,0x00,0x00,0x03,0x03,0x0f,0x0f,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, 534 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 535 | 0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00, 536 | 0xff,0xff,0xff,0xff,0x00,0x00,0xf0,0xf0,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 537 | 0x03,0x03,0x0f,0x0f,0x0f,0x0f,0x03,0x03,0x0f,0x0f,0x0f,0x0f,0x03,0x03,0x00,0x00, 538 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 539 | 0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00, 540 | 0x00,0x00,0x03,0x03,0xff,0xff,0xfc,0xfc,0xff,0xff,0x03,0x03,0x00,0x00,0x00,0x00, 541 | 0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x00,0x00, 542 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 543 | 0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00, 544 | 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, 545 | 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0xcc,0xcc,0xff,0xff,0x3f,0x3f,0x00,0x00, 546 | 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, 547 | 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00, 548 | 0x03,0x03,0xc3,0xc3,0xf0,0xf0,0x3c,0x3c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, 549 | 0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00, 550 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 551 | 0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0xfc,0xff,0xff,0x03,0x03,0x03,0x03,0x00,0x00, 552 | 0x00,0x00,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00,0x00,0x00,0x00,0x00, 553 | 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x00,0x00, 554 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 555 | 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, 556 | 0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0xfc,0xfc,0xfc,0x00,0x00,0x00,0x00,0x00,0x00, 557 | 0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00, 558 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 559 | 0x00,0x00,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00,0x00,0x00,0x00,0x00, 560 | 0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0xfc,0xff,0xff,0x03,0x03,0x03,0x03,0x00,0x00, 561 | 0x00,0x00,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, 562 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 563 | 0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, 564 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 565 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 566 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 567 | 0x00,0x00,0x00,0x00,0xc0,0xc0,0xf0,0xf0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, 568 | 0xfc,0xfc,0xff,0xff,0x03,0x03,0x00,0x00,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, 569 | 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00, 570 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 571 | 572 | // 5x7 font (in 6x8 cell) 573 | const uint8_t ucSmallFont[] = { 574 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x5f,0x06,0x00,0x00,0x07,0x03,0x00, 575 | 0x07,0x03,0x00,0x24,0x7e,0x24,0x7e,0x24,0x00,0x24,0x2b,0x6a,0x12,0x00,0x00,0x63, 576 | 0x13,0x08,0x64,0x63,0x00,0x36,0x49,0x56,0x20,0x50,0x00,0x00,0x07,0x03,0x00,0x00, 577 | 0x00,0x00,0x3e,0x41,0x00,0x00,0x00,0x00,0x41,0x3e,0x00,0x00,0x00,0x08,0x3e,0x1c, 578 | 0x3e,0x08,0x00,0x08,0x08,0x3e,0x08,0x08,0x00,0x00,0xe0,0x60,0x00,0x00,0x00,0x08, 579 | 0x08,0x08,0x08,0x08,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02, 580 | 0x00,0x3e,0x51,0x49,0x45,0x3e,0x00,0x00,0x42,0x7f,0x40,0x00,0x00,0x62,0x51,0x49, 581 | 0x49,0x46,0x00,0x22,0x49,0x49,0x49,0x36,0x00,0x18,0x14,0x12,0x7f,0x10,0x00,0x2f, 582 | 0x49,0x49,0x49,0x31,0x00,0x3c,0x4a,0x49,0x49,0x30,0x00,0x01,0x71,0x09,0x05,0x03, 583 | 0x00,0x36,0x49,0x49,0x49,0x36,0x00,0x06,0x49,0x49,0x29,0x1e,0x00,0x00,0x6c,0x6c, 584 | 0x00,0x00,0x00,0x00,0xec,0x6c,0x00,0x00,0x00,0x08,0x14,0x22,0x41,0x00,0x00,0x24, 585 | 0x24,0x24,0x24,0x24,0x00,0x00,0x41,0x22,0x14,0x08,0x00,0x02,0x01,0x59,0x09,0x06, 586 | 0x00,0x3e,0x41,0x5d,0x55,0x1e,0x00,0x7e,0x11,0x11,0x11,0x7e,0x00,0x7f,0x49,0x49, 587 | 0x49,0x36,0x00,0x3e,0x41,0x41,0x41,0x22,0x00,0x7f,0x41,0x41,0x41,0x3e,0x00,0x7f, 588 | 0x49,0x49,0x49,0x41,0x00,0x7f,0x09,0x09,0x09,0x01,0x00,0x3e,0x41,0x49,0x49,0x7a, 589 | 0x00,0x7f,0x08,0x08,0x08,0x7f,0x00,0x00,0x41,0x7f,0x41,0x00,0x00,0x30,0x40,0x40, 590 | 0x40,0x3f,0x00,0x7f,0x08,0x14,0x22,0x41,0x00,0x7f,0x40,0x40,0x40,0x40,0x00,0x7f, 591 | 0x02,0x04,0x02,0x7f,0x00,0x7f,0x02,0x04,0x08,0x7f,0x00,0x3e,0x41,0x41,0x41,0x3e, 592 | 0x00,0x7f,0x09,0x09,0x09,0x06,0x00,0x3e,0x41,0x51,0x21,0x5e,0x00,0x7f,0x09,0x09, 593 | 0x19,0x66,0x00,0x26,0x49,0x49,0x49,0x32,0x00,0x01,0x01,0x7f,0x01,0x01,0x00,0x3f, 594 | 0x40,0x40,0x40,0x3f,0x00,0x1f,0x20,0x40,0x20,0x1f,0x00,0x3f,0x40,0x3c,0x40,0x3f, 595 | 0x00,0x63,0x14,0x08,0x14,0x63,0x00,0x07,0x08,0x70,0x08,0x07,0x00,0x71,0x49,0x45, 596 | 0x43,0x00,0x00,0x00,0x7f,0x41,0x41,0x00,0x00,0x02,0x04,0x08,0x10,0x20,0x00,0x00, 597 | 0x41,0x41,0x7f,0x00,0x00,0x04,0x02,0x01,0x02,0x04,0x00,0x80,0x80,0x80,0x80,0x80, 598 | 0x00,0x00,0x03,0x07,0x00,0x00,0x00,0x20,0x54,0x54,0x54,0x78,0x00,0x7f,0x44,0x44, 599 | 0x44,0x38,0x00,0x38,0x44,0x44,0x44,0x28,0x00,0x38,0x44,0x44,0x44,0x7f,0x00,0x38, 600 | 0x54,0x54,0x54,0x08,0x00,0x08,0x7e,0x09,0x09,0x00,0x00,0x18,0xa4,0xa4,0xa4,0x7c, 601 | 0x00,0x7f,0x04,0x04,0x78,0x00,0x00,0x00,0x00,0x7d,0x40,0x00,0x00,0x40,0x80,0x84, 602 | 0x7d,0x00,0x00,0x7f,0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x7f,0x40,0x00,0x00,0x7c, 603 | 0x04,0x18,0x04,0x78,0x00,0x7c,0x04,0x04,0x78,0x00,0x00,0x38,0x44,0x44,0x44,0x38, 604 | 0x00,0xfc,0x44,0x44,0x44,0x38,0x00,0x38,0x44,0x44,0x44,0xfc,0x00,0x44,0x78,0x44, 605 | 0x04,0x08,0x00,0x08,0x54,0x54,0x54,0x20,0x00,0x04,0x3e,0x44,0x24,0x00,0x00,0x3c, 606 | 0x40,0x20,0x7c,0x00,0x00,0x1c,0x20,0x40,0x20,0x1c,0x00,0x3c,0x60,0x30,0x60,0x3c, 607 | 0x00,0x6c,0x10,0x10,0x6c,0x00,0x00,0x9c,0xa0,0x60,0x3c,0x00,0x00,0x64,0x54,0x54, 608 | 0x4c,0x00,0x00,0x08,0x3e,0x41,0x41,0x00,0x00,0x00,0x00,0x77,0x00,0x00,0x00,0x00, 609 | 0x41,0x41,0x3e,0x08,0x00,0x02,0x01,0x02,0x01,0x00,0x00,0x3c,0x26,0x23,0x26,0x3c}; 610 | 611 | // 612 | // Draw a string of small (8x8) or large (16x32) characters 613 | // At the given col+row 614 | // 615 | int DrawString(int x, int y, char *szMsg, uint16_t u16FG, uint16_t u16BG, int iFontSize) 616 | { 617 | int i, j, k, iLen; 618 | int l; 619 | unsigned char *s; 620 | uint16_t u16Tmp[512]; 621 | 622 | iLen = (int)strlen(szMsg); 623 | if (iFontSize == FONT_16x32) // draw 16x32 font 624 | { 625 | if (iLen*16 + x > width) iLen = (width - x) / 16; 626 | if (iLen < 0) return -1; 627 | for (i=0; i width) iLen = (width - x)/cx; // can't display it all 661 | if (iLen < 0)return -1; 662 | 663 | for (i=0; i width) iLen = (width - x)/12; // can't display it all 689 | if (iLen < 0)return -1; 690 | 691 | for (i=0; i width) iLen = (width - x)/16; // can't display it all 733 | if (iLen < 0)return -1; 734 | 735 | for (i=0; i 0 && iRadiusY > 0) 855 | { 856 | u16Color = pu16[5]; 857 | bFilled = pu16[6]; 858 | if (bFilled) 859 | M5.Lcd.fillEllipse(iCenterX, iCenterY, iRadiusX, iRadiusY, u16Color); 860 | else 861 | M5.Lcd.drawEllipse(iCenterX, iCenterY, iRadiusX, iRadiusY, u16Color); 862 | } 863 | } 864 | break; 865 | } // switch 866 | } /* RDWrite() */ 867 | 868 | class CharacteristicCallbacks: public BLECharacteristicCallbacks { 869 | void onWrite(BLECharacteristic *characteristic) { 870 | static uint8_t buffer[256]; 871 | static int iBufTotal, iBufLen = 0; 872 | int len; 873 | unsigned char *p, *pData = NULL, bSendIt = 0; 874 | 875 | std::string rxValue = characteristic->getValue(); 876 | len = rxValue.length(); 877 | if (len > 0) { 878 | p = (uint8_t *)rxValue.c_str(); 879 | // normal packet, pass it along 880 | if (iBufLen == 0 && p[1] == 0) { 881 | pData = p; 882 | bSendIt = 1; 883 | } else if (iBufLen == 0 && p[1] != 0) { // start of a long packet 884 | if (len != (int)p[1]) // we received partial data (probably Adafruit BLE sending it) 885 | { 886 | iBufLen = p[1]; 887 | memcpy(buffer, p, len); // copy what we're given 888 | iBufTotal += len; 889 | } 890 | else // we got everything, so no worries 891 | { 892 | bSendIt = 1; 893 | pData = p; 894 | } 895 | } else if (iBufLen != 0 && iBufTotal < iBufLen) { // we had partial data from a previous packet 896 | memcpy(&buffer[iBufTotal], p, len); // get new data 897 | iBufTotal += len; 898 | if (iBufTotal >= iBufLen) // we got everything 899 | { 900 | pData = buffer; 901 | len = iBufLen; 902 | iBufLen = iBufTotal = 0; 903 | bSendIt = 1; // send it 904 | } 905 | } 906 | if (bSendIt) { 907 | bSendIt = 0; 908 | RDWrite((uint16_t *)pData, len); 909 | } 910 | } // if (len > 0) 911 | }/* onWrite() */ 912 | 913 | void onRead (BLECharacteristic *characteristic) { 914 | characteristic->setValue(u16Buttons); 915 | } /* onRead() */ 916 | }; 917 | 918 | void setup() { 919 | M5.begin(); 920 | M5.Lcd.setRotation(1); 921 | orientation = 90; // default values 922 | width = 320; 923 | height = 240; 924 | bpp = 16; 925 | Serial.begin(115200); 926 | DrawString(48, 0, (char *)"Remote Display", 0xffe0, 0, FONT_16x16); 927 | DrawString(34, 40, (char *)"written by Larry Bank", 0x7e0, 0, FONT_12x16); 928 | 929 | BLEDevice::init("RemoteDisplay"); 930 | BLEDevice::setMTU(256); // allow bigger MTU size 931 | pServer = BLEDevice::createServer(); 932 | pService = pServer->createService(serviceUUID); 933 | 934 | pCharacteristic = pService->createCharacteristic( 935 | dataUUID1, 936 | BLECharacteristic::PROPERTY_READ | 937 | BLECharacteristic::PROPERTY_WRITE | 938 | BLECharacteristic::PROPERTY_WRITE_NR | 939 | BLECharacteristic::PROPERTY_INDICATE 940 | ); 941 | 942 | pCharacteristic->setCallbacks(new CharacteristicCallbacks()); 943 | pCharacteristic->setValue(u16Buttons); 944 | pService->start(); 945 | pAdvertising = BLEDevice::getAdvertising(); 946 | pAdvertising->addServiceUUID(serviceUUID); 947 | // pAdvertising->setScanResponse(true); 948 | // pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue 949 | // pAdvertising->setMinPreferred(0x12); 950 | BLEDevice::startAdvertising(); 951 | } 952 | 953 | void loop() { 954 | yield(); 955 | M5.update(); // update touch controller info 956 | if (M5.Touch.changed) { 957 | // check the touch buttons we defined 958 | u16Buttons = (b0.isPressed()); 959 | u16Buttons |= ((b1.isPressed()) << 1); 960 | u16Buttons |= ((b2.isPressed()) << 2); 961 | // Serial.println(u16Buttons, HEX); 962 | } 963 | } /* loop() */ 964 | -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1946A885258F813400712AD7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1946A884258F813400712AD7 /* AppDelegate.m */; }; 11 | 1946A888258F813400712AD7 /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1946A887258F813400712AD7 /* SceneDelegate.m */; }; 12 | 1946A88B258F813400712AD7 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1946A88A258F813400712AD7 /* ViewController.m */; }; 13 | 1946A88E258F813400712AD7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1946A88C258F813400712AD7 /* Main.storyboard */; }; 14 | 1946A890258F813500712AD7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1946A88F258F813500712AD7 /* Assets.xcassets */; }; 15 | 1946A893258F813500712AD7 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1946A891258F813500712AD7 /* LaunchScreen.storyboard */; }; 16 | 1946A896258F813500712AD7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 1946A895258F813500712AD7 /* main.m */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 1946A880258F813400712AD7 /* RemoteDisplay_Multi.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RemoteDisplay_Multi.app; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | 1946A883258F813400712AD7 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 22 | 1946A884258F813400712AD7 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 23 | 1946A886258F813400712AD7 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; 24 | 1946A887258F813400712AD7 /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; 25 | 1946A889258F813400712AD7 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 26 | 1946A88A258F813400712AD7 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 27 | 1946A88D258F813400712AD7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 28 | 1946A88F258F813500712AD7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 29 | 1946A892258F813500712AD7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 30 | 1946A894258F813500712AD7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | 1946A895258F813500712AD7 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 32 | 1946A89D258F815800712AD7 /* RemoteDisplay_Multi.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RemoteDisplay_Multi.entitlements; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | 1946A87D258F813400712AD7 /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | 1946A877258F813400712AD7 = { 47 | isa = PBXGroup; 48 | children = ( 49 | 1946A882258F813400712AD7 /* RemoteDisplay_Multi */, 50 | 1946A881258F813400712AD7 /* Products */, 51 | ); 52 | sourceTree = ""; 53 | }; 54 | 1946A881258F813400712AD7 /* Products */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | 1946A880258F813400712AD7 /* RemoteDisplay_Multi.app */, 58 | ); 59 | name = Products; 60 | sourceTree = ""; 61 | }; 62 | 1946A882258F813400712AD7 /* RemoteDisplay_Multi */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | 1946A89D258F815800712AD7 /* RemoteDisplay_Multi.entitlements */, 66 | 1946A883258F813400712AD7 /* AppDelegate.h */, 67 | 1946A884258F813400712AD7 /* AppDelegate.m */, 68 | 1946A886258F813400712AD7 /* SceneDelegate.h */, 69 | 1946A887258F813400712AD7 /* SceneDelegate.m */, 70 | 1946A889258F813400712AD7 /* ViewController.h */, 71 | 1946A88A258F813400712AD7 /* ViewController.m */, 72 | 1946A88C258F813400712AD7 /* Main.storyboard */, 73 | 1946A88F258F813500712AD7 /* Assets.xcassets */, 74 | 1946A891258F813500712AD7 /* LaunchScreen.storyboard */, 75 | 1946A894258F813500712AD7 /* Info.plist */, 76 | 1946A895258F813500712AD7 /* main.m */, 77 | ); 78 | path = RemoteDisplay_Multi; 79 | sourceTree = ""; 80 | }; 81 | /* End PBXGroup section */ 82 | 83 | /* Begin PBXNativeTarget section */ 84 | 1946A87F258F813400712AD7 /* RemoteDisplay_Multi */ = { 85 | isa = PBXNativeTarget; 86 | buildConfigurationList = 1946A899258F813500712AD7 /* Build configuration list for PBXNativeTarget "RemoteDisplay_Multi" */; 87 | buildPhases = ( 88 | 1946A87C258F813400712AD7 /* Sources */, 89 | 1946A87D258F813400712AD7 /* Frameworks */, 90 | 1946A87E258F813400712AD7 /* Resources */, 91 | ); 92 | buildRules = ( 93 | ); 94 | dependencies = ( 95 | ); 96 | name = RemoteDisplay_Multi; 97 | productName = RemoteDisplay_Multi; 98 | productReference = 1946A880258F813400712AD7 /* RemoteDisplay_Multi.app */; 99 | productType = "com.apple.product-type.application"; 100 | }; 101 | /* End PBXNativeTarget section */ 102 | 103 | /* Begin PBXProject section */ 104 | 1946A878258F813400712AD7 /* Project object */ = { 105 | isa = PBXProject; 106 | attributes = { 107 | LastUpgradeCheck = 1230; 108 | TargetAttributes = { 109 | 1946A87F258F813400712AD7 = { 110 | CreatedOnToolsVersion = 12.3; 111 | }; 112 | }; 113 | }; 114 | buildConfigurationList = 1946A87B258F813400712AD7 /* Build configuration list for PBXProject "RemoteDisplay_Multi" */; 115 | compatibilityVersion = "Xcode 9.3"; 116 | developmentRegion = en; 117 | hasScannedForEncodings = 0; 118 | knownRegions = ( 119 | en, 120 | Base, 121 | ); 122 | mainGroup = 1946A877258F813400712AD7; 123 | productRefGroup = 1946A881258F813400712AD7 /* Products */; 124 | projectDirPath = ""; 125 | projectRoot = ""; 126 | targets = ( 127 | 1946A87F258F813400712AD7 /* RemoteDisplay_Multi */, 128 | ); 129 | }; 130 | /* End PBXProject section */ 131 | 132 | /* Begin PBXResourcesBuildPhase section */ 133 | 1946A87E258F813400712AD7 /* Resources */ = { 134 | isa = PBXResourcesBuildPhase; 135 | buildActionMask = 2147483647; 136 | files = ( 137 | 1946A893258F813500712AD7 /* LaunchScreen.storyboard in Resources */, 138 | 1946A890258F813500712AD7 /* Assets.xcassets in Resources */, 139 | 1946A88E258F813400712AD7 /* Main.storyboard in Resources */, 140 | ); 141 | runOnlyForDeploymentPostprocessing = 0; 142 | }; 143 | /* End PBXResourcesBuildPhase section */ 144 | 145 | /* Begin PBXSourcesBuildPhase section */ 146 | 1946A87C258F813400712AD7 /* Sources */ = { 147 | isa = PBXSourcesBuildPhase; 148 | buildActionMask = 2147483647; 149 | files = ( 150 | 1946A88B258F813400712AD7 /* ViewController.m in Sources */, 151 | 1946A885258F813400712AD7 /* AppDelegate.m in Sources */, 152 | 1946A896258F813500712AD7 /* main.m in Sources */, 153 | 1946A888258F813400712AD7 /* SceneDelegate.m in Sources */, 154 | ); 155 | runOnlyForDeploymentPostprocessing = 0; 156 | }; 157 | /* End PBXSourcesBuildPhase section */ 158 | 159 | /* Begin PBXVariantGroup section */ 160 | 1946A88C258F813400712AD7 /* Main.storyboard */ = { 161 | isa = PBXVariantGroup; 162 | children = ( 163 | 1946A88D258F813400712AD7 /* Base */, 164 | ); 165 | name = Main.storyboard; 166 | sourceTree = ""; 167 | }; 168 | 1946A891258F813500712AD7 /* LaunchScreen.storyboard */ = { 169 | isa = PBXVariantGroup; 170 | children = ( 171 | 1946A892258F813500712AD7 /* Base */, 172 | ); 173 | name = LaunchScreen.storyboard; 174 | sourceTree = ""; 175 | }; 176 | /* End PBXVariantGroup section */ 177 | 178 | /* Begin XCBuildConfiguration section */ 179 | 1946A897258F813500712AD7 /* Debug */ = { 180 | isa = XCBuildConfiguration; 181 | buildSettings = { 182 | ALWAYS_SEARCH_USER_PATHS = NO; 183 | CLANG_ANALYZER_NONNULL = YES; 184 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 185 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 186 | CLANG_CXX_LIBRARY = "libc++"; 187 | CLANG_ENABLE_MODULES = YES; 188 | CLANG_ENABLE_OBJC_ARC = YES; 189 | CLANG_ENABLE_OBJC_WEAK = YES; 190 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 191 | CLANG_WARN_BOOL_CONVERSION = YES; 192 | CLANG_WARN_COMMA = YES; 193 | CLANG_WARN_CONSTANT_CONVERSION = YES; 194 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 195 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 196 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 197 | CLANG_WARN_EMPTY_BODY = YES; 198 | CLANG_WARN_ENUM_CONVERSION = YES; 199 | CLANG_WARN_INFINITE_RECURSION = YES; 200 | CLANG_WARN_INT_CONVERSION = YES; 201 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 202 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 203 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 204 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 205 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 206 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 207 | CLANG_WARN_STRICT_PROTOTYPES = YES; 208 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 209 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 210 | CLANG_WARN_UNREACHABLE_CODE = YES; 211 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 212 | COPY_PHASE_STRIP = NO; 213 | DEBUG_INFORMATION_FORMAT = dwarf; 214 | ENABLE_STRICT_OBJC_MSGSEND = YES; 215 | ENABLE_TESTABILITY = YES; 216 | GCC_C_LANGUAGE_STANDARD = gnu11; 217 | GCC_DYNAMIC_NO_PIC = NO; 218 | GCC_NO_COMMON_BLOCKS = YES; 219 | GCC_OPTIMIZATION_LEVEL = 0; 220 | GCC_PREPROCESSOR_DEFINITIONS = ( 221 | "DEBUG=1", 222 | "$(inherited)", 223 | ); 224 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 225 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 226 | GCC_WARN_UNDECLARED_SELECTOR = YES; 227 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 228 | GCC_WARN_UNUSED_FUNCTION = YES; 229 | GCC_WARN_UNUSED_VARIABLE = YES; 230 | IPHONEOS_DEPLOYMENT_TARGET = 14.3; 231 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 232 | MTL_FAST_MATH = YES; 233 | ONLY_ACTIVE_ARCH = YES; 234 | SDKROOT = iphoneos; 235 | }; 236 | name = Debug; 237 | }; 238 | 1946A898258F813500712AD7 /* Release */ = { 239 | isa = XCBuildConfiguration; 240 | buildSettings = { 241 | ALWAYS_SEARCH_USER_PATHS = NO; 242 | CLANG_ANALYZER_NONNULL = YES; 243 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 244 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 245 | CLANG_CXX_LIBRARY = "libc++"; 246 | CLANG_ENABLE_MODULES = YES; 247 | CLANG_ENABLE_OBJC_ARC = YES; 248 | CLANG_ENABLE_OBJC_WEAK = YES; 249 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 250 | CLANG_WARN_BOOL_CONVERSION = YES; 251 | CLANG_WARN_COMMA = YES; 252 | CLANG_WARN_CONSTANT_CONVERSION = YES; 253 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 254 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 255 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 256 | CLANG_WARN_EMPTY_BODY = YES; 257 | CLANG_WARN_ENUM_CONVERSION = YES; 258 | CLANG_WARN_INFINITE_RECURSION = YES; 259 | CLANG_WARN_INT_CONVERSION = YES; 260 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 261 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 262 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 263 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 264 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 265 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 266 | CLANG_WARN_STRICT_PROTOTYPES = YES; 267 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 268 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 269 | CLANG_WARN_UNREACHABLE_CODE = YES; 270 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 271 | COPY_PHASE_STRIP = NO; 272 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 273 | ENABLE_NS_ASSERTIONS = NO; 274 | ENABLE_STRICT_OBJC_MSGSEND = YES; 275 | GCC_C_LANGUAGE_STANDARD = gnu11; 276 | GCC_NO_COMMON_BLOCKS = YES; 277 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 278 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 279 | GCC_WARN_UNDECLARED_SELECTOR = YES; 280 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 281 | GCC_WARN_UNUSED_FUNCTION = YES; 282 | GCC_WARN_UNUSED_VARIABLE = YES; 283 | IPHONEOS_DEPLOYMENT_TARGET = 14.3; 284 | MTL_ENABLE_DEBUG_INFO = NO; 285 | MTL_FAST_MATH = YES; 286 | SDKROOT = iphoneos; 287 | VALIDATE_PRODUCT = YES; 288 | }; 289 | name = Release; 290 | }; 291 | 1946A89A258F813500712AD7 /* Debug */ = { 292 | isa = XCBuildConfiguration; 293 | buildSettings = { 294 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 295 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 296 | CODE_SIGN_ENTITLEMENTS = RemoteDisplay_Multi/RemoteDisplay_Multi.entitlements; 297 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; 298 | CODE_SIGN_STYLE = Automatic; 299 | DEVELOPMENT_TEAM = 2Y3PJ8D6W2; 300 | INFOPLIST_FILE = RemoteDisplay_Multi/Info.plist; 301 | IPHONEOS_DEPLOYMENT_TARGET = 14.2; 302 | LD_RUNPATH_SEARCH_PATHS = ( 303 | "$(inherited)", 304 | "@executable_path/Frameworks", 305 | ); 306 | PRODUCT_BUNDLE_IDENTIFIER = tdfsoftware.RemoteDisplayMulti; 307 | PRODUCT_NAME = "$(TARGET_NAME)"; 308 | SUPPORTS_MACCATALYST = YES; 309 | TARGETED_DEVICE_FAMILY = "1,2"; 310 | }; 311 | name = Debug; 312 | }; 313 | 1946A89B258F813500712AD7 /* Release */ = { 314 | isa = XCBuildConfiguration; 315 | buildSettings = { 316 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 317 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 318 | CODE_SIGN_ENTITLEMENTS = RemoteDisplay_Multi/RemoteDisplay_Multi.entitlements; 319 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; 320 | CODE_SIGN_STYLE = Automatic; 321 | DEVELOPMENT_TEAM = 2Y3PJ8D6W2; 322 | INFOPLIST_FILE = RemoteDisplay_Multi/Info.plist; 323 | IPHONEOS_DEPLOYMENT_TARGET = 14.2; 324 | LD_RUNPATH_SEARCH_PATHS = ( 325 | "$(inherited)", 326 | "@executable_path/Frameworks", 327 | ); 328 | PRODUCT_BUNDLE_IDENTIFIER = tdfsoftware.RemoteDisplayMulti; 329 | PRODUCT_NAME = "$(TARGET_NAME)"; 330 | SUPPORTS_MACCATALYST = YES; 331 | TARGETED_DEVICE_FAMILY = "1,2"; 332 | }; 333 | name = Release; 334 | }; 335 | /* End XCBuildConfiguration section */ 336 | 337 | /* Begin XCConfigurationList section */ 338 | 1946A87B258F813400712AD7 /* Build configuration list for PBXProject "RemoteDisplay_Multi" */ = { 339 | isa = XCConfigurationList; 340 | buildConfigurations = ( 341 | 1946A897258F813500712AD7 /* Debug */, 342 | 1946A898258F813500712AD7 /* Release */, 343 | ); 344 | defaultConfigurationIsVisible = 0; 345 | defaultConfigurationName = Release; 346 | }; 347 | 1946A899258F813500712AD7 /* Build configuration list for PBXNativeTarget "RemoteDisplay_Multi" */ = { 348 | isa = XCConfigurationList; 349 | buildConfigurations = ( 350 | 1946A89A258F813500712AD7 /* Debug */, 351 | 1946A89B258F813500712AD7 /* Release */, 352 | ); 353 | defaultConfigurationIsVisible = 0; 354 | defaultConfigurationName = Release; 355 | }; 356 | /* End XCConfigurationList section */ 357 | }; 358 | rootObject = 1946A878258F813400712AD7 /* Project object */; 359 | } 360 | -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi.xcodeproj/project.xcworkspace/xcuserdata/laurencebank.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitbank2/RemoteDisplay/8b6d49e75ef66ed4e3e227d668d0f7a8451aae63/MacOS_Server/RemoteDisplay_Multi.xcodeproj/project.xcworkspace/xcuserdata/laurencebank.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi.xcodeproj/xcuserdata/laurencebank.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 21 | 22 | 23 | 25 | 37 | 38 | 39 | 41 | 53 | 54 | 55 | 57 | 69 | 70 | 71 | 73 | 85 | 86 | 87 | 89 | 101 | 102 | 103 | 105 | 117 | 118 | 119 | 121 | 133 | 134 | 135 | 137 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi.xcodeproj/xcuserdata/laurencebank.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | RemoteDisplay_Multi.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // RemoteDisplay_Multi 4 | // 5 | // Created by Laurence Bank on 12/20/20. 6 | // 7 | 8 | #import 9 | #import "CoreBluetooth/CoreBluetooth.h" 10 | 11 | // Disable UART code on iOS 12 | //#if defined( TARGET_OS_MAC ) && !defined (TARGET_OS_IPHONE) 13 | #import 14 | #import 15 | #import 16 | #import 17 | #import 18 | #import 19 | #import 20 | #import 21 | #import 22 | //#endif 23 | 24 | @interface AppDelegate : UIResponder 25 | 26 | @property (nonatomic, strong) CBPeripheralManager *peripheralManager; 27 | @property (nonatomic, strong) CBMutableService *service; 28 | @property (nonatomic, strong) CBMutableService *service2; 29 | @end 30 | 31 | -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // RemoteDisplay_Multi 4 | // 5 | // Created by Laurence Bank on 12/20/20. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | #import "ViewController.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | static CBMutableCharacteristic *characteristic1, *characteristic2; 16 | static dispatch_source_t readPollSource; 17 | static CBMutableService *includedService; 18 | 19 | //#if defined( TARGET_OS_MAC ) && !defined (TARGET_OS_IPHONE) 20 | static int serialFileDescriptor = -1; 21 | static dispatch_source_t readPollSource; 22 | static char bsdPath[MAXPATHLEN]; 23 | 24 | //#endif 25 | 26 | @implementation AppDelegate 27 | 28 | 29 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 30 | // Override point for customization after application launch. 31 | self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil]; 32 | if ([self findModems]) 33 | [self openPort]; 34 | 35 | return YES; 36 | } 37 | 38 | 39 | #pragma mark - UISceneSession lifecycle 40 | 41 | 42 | - (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { 43 | // Called when a new scene session is being created. 44 | // Use this method to select a configuration to create the new scene with. 45 | return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; 46 | } 47 | 48 | 49 | - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions { 50 | // Called when the user discards a scene session. 51 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 52 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 53 | } 54 | 55 | - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral 56 | { 57 | // NSLog(@"peripheralManagerDidUpdateState: %d", (int)peripheral.state); 58 | NSString *stateString = nil; 59 | switch((int)peripheral.state) 60 | { 61 | case CBManagerStateResetting: stateString = @"The connection with the system service was momentarily lost, update imminent."; break; 62 | case CBManagerStateUnsupported: stateString = @"The platform doesn't support Bluetooth Low Energy."; break; 63 | case CBManagerStateUnauthorized: stateString = @"The app is not authorized to use Bluetooth Low Energy."; break; 64 | case CBManagerStatePoweredOff: stateString = @"Bluetooth is currently powered off."; break; 65 | case CBManagerStatePoweredOn: stateString = @"Bluetooth is currently powered on and available to use."; break; 66 | default: stateString = @"State unknown, update imminent."; break; 67 | } 68 | NSLog(@"%@", stateString); 69 | 70 | if (CBManagerStatePoweredOn == peripheral.state) { 71 | 72 | // NSData *zombie = [@"zombie" dataUsingEncoding:NSUTF8StringEncoding]; 73 | // CBMutableDescriptor *descriptor1 = [[CBMutableDescriptor alloc] initWithType:[CBUUID UUIDWithString:CBUUIDClientCharacteristicConfigurationString] value:@"0"]; 74 | // Our main data characteristic 75 | characteristic1 = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:@"0000fea1-1234-1000-8000-00805f9b34fb"] properties:CBCharacteristicPropertyWriteWithoutResponse | CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead | CBCharacteristicPropertyIndicate value:nil permissions:CBAttributePermissionsWriteable | CBAttributePermissionsReadable]; 76 | // characteristic1.descriptors = @[descriptor1]; 77 | 78 | // NSData *ghost = [@"ghost" dataUsingEncoding:NSUTF8StringEncoding]; 79 | // Our name characteristic (sets the window name) 80 | // Needed to create this additional characteristic with an invalid UUID to make it work on ESP32 81 | characteristic2 = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:@"0000fea1-0000-1000-8000-00805f9b34fb"] properties:CBCharacteristicPropertyWriteWithoutResponse | CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsWriteable | CBAttributePermissionsReadable]; 82 | 83 | // characteristic3 = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:@"0000fea3-0000-1000-8000-00805f9b34fb"] properties:CBCharacteristicPropertyWriteWithoutResponse | CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsWriteable | CBAttributePermissionsReadable]; 84 | 85 | // includedService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"0000FEA0-0000-1000-8000-00805F9B34FB"] primary:YES]; 86 | // includedService.characteristics = @[includedCharacteristic]; 87 | 88 | // [self.peripheralManager addService:includedService]; 89 | 90 | // self.service = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"0xFEA0"] primary:YES]; 91 | self.service = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"0000fea0-1234-1000-8000-00805f9b34fb"] primary:YES]; 92 | self.service.characteristics = @[characteristic1, characteristic2]; 93 | // [self.peripheralManager addService:self.service]; 94 | 95 | // self.service2 = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"0000fea0-0000-1000-8000-00805f9b34fb"] primary:NO]; 96 | // self.service2.characteristics = @[characteristic2]; 97 | // [self.peripheralManager addService:self.service2]; 98 | 99 | // self.service.includedServices = @[self.service2]; 100 | 101 | [self.peripheralManager addService:self.service]; 102 | 103 | [peripheral startAdvertising:@{ 104 | CBAdvertisementDataLocalNameKey: @"RemoteDisplay", 105 | CBAdvertisementDataServiceUUIDsKey: @[[CBUUID UUIDWithString:@"0000fea0-1234-1000-8000-00805f9b34fb"]] 106 | }]; 107 | 108 | } else { 109 | [peripheral stopAdvertising]; 110 | [peripheral removeAllServices]; 111 | } 112 | } 113 | 114 | - (void)peripheralManager:(CBPeripheralManager *)peripheral 115 | didReceiveReadRequest:(CBATTRequest *)request; 116 | { 117 | request.value = [self makeButtonPayload]; 118 | [peripheral respondToRequest:request withResult:CBATTErrorSuccess]; 119 | // NSLog(@"Got a read request"); 120 | } 121 | - (NSData *)makeButtonPayload 122 | { 123 | NSMutableData *payload = [NSMutableData dataWithLength:2]; 124 | uint8_t *bytes = [payload mutableBytes]; 125 | bytes[0] = [ViewController getButtons]; 126 | bytes[1] = 0; 127 | return payload; 128 | } 129 | 130 | - (void)peripheralManager:(CBPeripheralManager *)peripheral 131 | didReceiveWriteRequests:(NSArray *)requests; 132 | { 133 | for (CBATTRequest *request in requests) { 134 | NSData *data = [request value]; 135 | if (request.characteristic.UUID == characteristic1.UUID || request.characteristic.UUID == characteristic2.UUID) 136 | { 137 | [ViewController processBytes:data]; 138 | } 139 | // else 140 | // { // set the label name from this one 141 | // NSString *clientName = [[NSString alloc] initWithBytes:[data bytes] 142 | // length:[data length] encoding: NSUTF8StringEncoding]; 143 | // [ViewController showText:clientName]; 144 | // } 145 | // This function can handle writes with no response and writes which require a response 146 | // if no response is given, the client's waiting will time out / error 147 | // We always give a successful response to our characteristic for clients 148 | // which require a response 149 | // if (request.characteristic.UUID != characteristic1.UUID) 150 | [self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess]; 151 | } 152 | } 153 | - (void)peripheralManager:(CBPeripheralManager *)peripheral 154 | central:(CBCentral *)central 155 | didSubscribeToCharacteristic:(CBCharacteristic *)characteristic; 156 | { 157 | NSLog(@"didSubscribeToCharacteristic"); 158 | } 159 | - (void)peripheralManager:(CBPeripheralManager *)peripheral 160 | central:(CBCentral *)central 161 | didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic; 162 | { 163 | NSLog(@"didUnsubscribeFromCharacteristic"); 164 | } 165 | - (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error 166 | { 167 | NSLog(@"peripheralManagerDidStartAdvertising: %@", error); 168 | } 169 | 170 | - (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error 171 | { 172 | NSLog(@"peripheralManagerDidAddService: %@ %@", service, error); 173 | } 174 | 175 | //#if !defined (TARGET_OS_IPHONE) 176 | 177 | // UART code 178 | 179 | static bool getModemPath(io_iterator_t serialPortIterator, char *thePath, CFIndex maxPathSize) 180 | { 181 | io_object_t modemService; 182 | Boolean modemFound = false; 183 | 184 | // Initialize the returned path 185 | *thePath = '\0'; 186 | 187 | // Iterate across all modems found. In this example, we bail after finding the first modem. 188 | 189 | while ((modemService = IOIteratorNext(serialPortIterator)) && !modemFound) { 190 | CFTypeRef bsdPathAsCFString; 191 | 192 | // Get the callout device's path (/dev/cu.xxxxx). The callout device should almost always be 193 | // used: the dialin device (/dev/tty.xxxxx) would be used when monitoring a serial port for 194 | // incoming calls, e.g. a fax listener. 195 | 196 | bsdPathAsCFString = IORegistryEntryCreateCFProperty(modemService, 197 | CFSTR(kIOCalloutDeviceKey), 198 | kCFAllocatorDefault, 199 | 0); 200 | if (bsdPathAsCFString) { 201 | Boolean result; 202 | 203 | // Convert the path from a CFString to a C (NUL-terminated) string for use 204 | // with the POSIX open() call. 205 | 206 | result = CFStringGetCString(bsdPathAsCFString, 207 | thePath, 208 | maxPathSize, 209 | kCFStringEncodingUTF8); 210 | CFRelease(bsdPathAsCFString); 211 | 212 | if (strncmp(thePath, "/dev/cu.usbmodem", 16) == 0 || strncmp(thePath, "/dev/cu.usbserial", 17) == 0) { 213 | NSLog(@"Found a serial port!"); 214 | modemFound = true; 215 | } 216 | 217 | if (result) { 218 | printf("Modem found with BSD path: %s", thePath); 219 | } 220 | } 221 | 222 | printf("\n"); 223 | 224 | // Release the io_service_t now that we are done with it. 225 | 226 | (void) IOObjectRelease(modemService); 227 | } 228 | 229 | return modemFound; 230 | } /* getModemPath() */ 231 | 232 | -(bool) findModems 233 | { 234 | kern_return_t kernResult; 235 | CFMutableDictionaryRef classesToMatch; 236 | io_iterator_t serialPortIterator; 237 | 238 | classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); 239 | 240 | if (classesToMatch == NULL) { 241 | NSLog(@"IOServiceMatching returned a NULL dictionary."); 242 | } 243 | else { 244 | // Look for devices that claim to be modems. 245 | CFDictionarySetValue(classesToMatch, 246 | CFSTR(kIOSerialBSDTypeKey), 247 | CFSTR(kIOSerialBSDAllTypes)); 248 | } 249 | // Get an iterator across all matching devices. 250 | kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, &serialPortIterator); 251 | if (KERN_SUCCESS != kernResult) { 252 | NSLog(@"IOServiceGetMatchingServices returned %d\n", kernResult); 253 | goto exit; 254 | } 255 | if (!(getModemPath(serialPortIterator, bsdPath, sizeof(bsdPath)))) 256 | kernResult = KERN_FAILURE; 257 | 258 | IOObjectRelease(serialPortIterator); // Release the iterator. 259 | exit: 260 | return (kernResult == KERN_SUCCESS); 261 | 262 | } /* findModems() */ 263 | 264 | -(void)openPort 265 | { 266 | int result; 267 | speed_t baudRate; 268 | baudRate = 115200; 269 | // NSString *serialPortFile = @"/dev/cu.wchusbserial1410"; 270 | // const char *bsdPath = [serialPortFile cStringUsingEncoding:NSUTF8StringEncoding]; 271 | serialFileDescriptor = open(bsdPath, O_RDWR | O_NOCTTY | O_EXLOCK); // | O_NONBLOCK ); 272 | if (serialFileDescriptor < 1) 273 | { 274 | NSLog(@"Error opening serial port"); 275 | return; 276 | } 277 | NSLog(@"Opened serial port successfully"); 278 | 279 | // Now that the device is open, clear the O_NONBLOCK flag so subsequent I/O will block. 280 | // See fcntl(2) ("man 2 fcntl") for details. 281 | fcntl(serialFileDescriptor, 0); 282 | struct termios options; 283 | 284 | tcgetattr(serialFileDescriptor, &options); 285 | 286 | cfmakeraw(&options); 287 | options.c_cc[VMIN] = 1; // Wait for at least 1 character before returning 288 | options.c_cc[VTIME] = 1; // Wait 100 milliseconds between bytes before returning from read 289 | // Set 8 data bits 290 | options.c_cflag &= ~CSIZE; 291 | options.c_cflag |= CS8; // 8 data bits 292 | options.c_cflag |= HUPCL; // Turn on hangup on close 293 | options.c_cflag |= CLOCAL; // Set local mode on 294 | options.c_cflag |= CREAD; // Enable receiver 295 | options.c_lflag &= ~(ICANON /*| ECHO*/ | ISIG); // Turn off canonical mode and signals 296 | 297 | // Set baud rate 298 | result = cfsetspeed(&options, baudRate); 299 | 300 | result = tcsetattr(serialFileDescriptor, TCSANOW, &options); 301 | if (result != 0) { 302 | // Try to set baud rate via ioctl if normal port settings fail 303 | int new_baud = (int)baudRate; 304 | result = ioctl(serialFileDescriptor, IOSSIOSPEED, &new_baud, 1); 305 | } 306 | // Start a read dispatch source in the background 307 | readPollSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, serialFileDescriptor, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); 308 | dispatch_source_set_event_handler(readPollSource, ^{ 309 | 310 | // Data is available 311 | static unsigned char buf[1024]; 312 | static int iOldData = 0; 313 | long lengthRead = read(serialFileDescriptor, &buf[iOldData], sizeof(buf)-iOldData); 314 | iOldData += (int)lengthRead; 315 | // Check for a data sync error. If we miss the beginning of the packet 316 | // we have to scan forward until we find valid data 317 | if (buf[0] < 2 || buf[0] > 130 || (buf[1] != 0x00 && buf[1] != 0x40)) { // sync error 318 | int bInvalid = 1; 319 | int iLen, i = 1; 320 | while (i < iOldData-2 && bInvalid) { // search forward to sync 321 | if (buf[i] >= 2 && buf[i] <= 130 && (buf[i+1] == 0x00 || buf[i+1] == 0x40)) { // test a candidate 322 | iLen = buf[i]; 323 | if (i+iLen+2 < iOldData && buf[i+iLen+1] >= 2 && buf[i+iLen+1] <= 130 && (buf[i+iLen+2] == 0x00 || buf[i+iLen+2])) { // looks good 324 | memcpy(buf, &buf[i], iOldData-i); // slide the good data down 325 | iOldData -= i; 326 | bInvalid = 0; // mark it as OK to proceed 327 | } 328 | } 329 | i++; 330 | } // while 331 | if (bInvalid) // we can't resync, leave 332 | return; 333 | } 334 | while (iOldData > 0 && iOldData >= buf[0]+1 && buf[0] >= 2 && buf[0] <= 130 && (buf[1] == 0x00 || buf[1] == 0x40)) // enough data + valid to send at least 1 packet? 335 | { 336 | int iPacketLength = buf[0]; 337 | NSData *readData = [NSData dataWithBytes:&buf[1] length:iPacketLength]; 338 | [ViewController processBytes:readData]; 339 | iOldData -= (iPacketLength+1); 340 | memcpy(buf, &buf[iPacketLength+1], iOldData); 341 | } 342 | }); 343 | dispatch_source_set_cancel_handler(readPollSource, ^{ 344 | // Set port back the way it was before we used it 345 | // tcsetattr(serialFileDescriptor, TCSADRAIN, &originalPortAttributes); 346 | NSLog(@"Closing port.\n"); 347 | close(serialFileDescriptor); 348 | }); 349 | dispatch_resume(readPollSource); 350 | // self.readPollSource = readPollSource; 351 | 352 | } /* openPort() */ 353 | //#endif 354 | 355 | @end 356 | -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 28 | 37 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 59 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSBluetoothAlwaysUsageDescription 24 | Uses BLE to receive draw commands from an Arduino board 25 | UIApplicationSceneManifest 26 | 27 | UIApplicationSupportsMultipleScenes 28 | 29 | UISceneConfigurations 30 | 31 | UIWindowSceneSessionRoleApplication 32 | 33 | 34 | UISceneConfigurationName 35 | Default Configuration 36 | UISceneDelegateClassName 37 | SceneDelegate 38 | UISceneStoryboardFile 39 | Main 40 | 41 | 42 | 43 | 44 | UIApplicationSupportsIndirectInputEvents 45 | 46 | UILaunchStoryboardName 47 | LaunchScreen 48 | UIMainStoryboardFile 49 | Main 50 | UIRequiredDeviceCapabilities 51 | 52 | armv7 53 | 54 | UISupportedInterfaceOrientations 55 | 56 | UIInterfaceOrientationPortrait 57 | UIInterfaceOrientationLandscapeLeft 58 | UIInterfaceOrientationLandscapeRight 59 | 60 | UISupportedInterfaceOrientations~ipad 61 | 62 | UIInterfaceOrientationPortrait 63 | UIInterfaceOrientationPortraitUpsideDown 64 | UIInterfaceOrientationLandscapeLeft 65 | UIInterfaceOrientationLandscapeRight 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi/RemoteDisplay_Multi.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.device.bluetooth 8 | 9 | com.apple.security.network.client 10 | 11 | com.apple.security.network.server 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi/SceneDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.h 3 | // RemoteDisplay_Multi 4 | // 5 | // Created by Laurence Bank on 12/20/20. 6 | // 7 | 8 | #import 9 | 10 | @interface SceneDelegate : UIResponder 11 | 12 | @property (strong, nonatomic) UIWindow * window; 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi/SceneDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.m 3 | // RemoteDisplay_Multi 4 | // 5 | // Created by Laurence Bank on 12/20/20. 6 | // 7 | 8 | #import "SceneDelegate.h" 9 | 10 | @interface SceneDelegate () 11 | 12 | @end 13 | 14 | @implementation SceneDelegate 15 | 16 | 17 | - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { 18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 21 | } 22 | 23 | 24 | - (void)sceneDidDisconnect:(UIScene *)scene { 25 | // Called as the scene is being released by the system. 26 | // This occurs shortly after the scene enters the background, or when its session is discarded. 27 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 28 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 29 | } 30 | 31 | 32 | - (void)sceneDidBecomeActive:(UIScene *)scene { 33 | // Called when the scene has moved from an inactive state to an active state. 34 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 35 | } 36 | 37 | 38 | - (void)sceneWillResignActive:(UIScene *)scene { 39 | // Called when the scene will move from an active state to an inactive state. 40 | // This may occur due to temporary interruptions (ex. an incoming phone call). 41 | } 42 | 43 | 44 | - (void)sceneWillEnterForeground:(UIScene *)scene { 45 | // Called as the scene transitions from the background to the foreground. 46 | // Use this method to undo the changes made on entering the background. 47 | } 48 | 49 | 50 | - (void)sceneDidEnterBackground:(UIScene *)scene { 51 | // Called as the scene transitions from the foreground to the background. 52 | // Use this method to save data, release shared resources, and store enough scene-specific state information 53 | // to restore the scene back to its current state. 54 | } 55 | 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // RemoteDisplay_Multi 4 | // 5 | // Created by Laurence Bank on 12/20/20. 6 | // 7 | 8 | #import 9 | #import "../../rd_constants.h" 10 | 11 | @interface ViewController : UIViewController 12 | @property (weak, nonatomic) IBOutlet UIButton *Button0; 13 | @property (weak, nonatomic) IBOutlet UIButton *Button1; 14 | @property (weak, nonatomic) IBOutlet UIButton *Button2; 15 | @property (weak, nonatomic) IBOutlet UIImageView *theImage; 16 | + (void) processBytes:(NSData *)thedata; 17 | + (int) getButtons; 18 | - (void) showOLED; 19 | - (void) showOLEDNotification:(NSNotification *) notification; 20 | 21 | @end 22 | 23 | -------------------------------------------------------------------------------- /MacOS_Server/RemoteDisplay_Multi/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // RemoteDisplay_Multi 4 | // 5 | // Created by Laurence Bank on 12/20/20. 6 | // 7 | 8 | #import 9 | #import "AppDelegate.h" 10 | 11 | int main(int argc, char * argv[]) { 12 | NSString * appDelegateClassName; 13 | @autoreleasepool { 14 | // Setup code that might create autoreleased objects goes here. 15 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 16 | } 17 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

RemoteDisplay

2 |

This library has a lofty purpose - to allow microcontroller boards to serve 3 | as clients or servers for remote displays and input (buttons). Why? I 4 | frequently find myself experimenting with some code and then having to cobble 5 | up a display and some buttons to test a project idea. This leads to time wasted 6 | wiring up 0.1" perfboards with shaky connections and frustration. The idea of 7 | the library is to allow easy output and input for your microcontrollers with 8 | the simplest of interfaces that can be remote (over wired or wireless links) 9 | or the same code and work the same way with directly connected hardware. This 10 | allows you to easily prototype ideas without owning the final hardware to then 11 | turn it into a finished product/project on the actual hardware and have it work 12 | the same way.

13 |
14 |

Features

15 | - Supports all common OLED and LCD displays
16 | - Provides a rich set of graphics primitives
17 | - 5 built-in fonts (6x8, 8x8, 12x16, 16x16, 16x32)
18 | - Supports Adafruit_GFX format custom fonts
19 | - Efficient system for sending graphics assets over the air/wire
20 | - Works equally on the 3 main BLE APIs (Arduino, ESP32, Adafruit)
21 | - Includes example display servers for iOS/MacOS and ESP32
22 |
23 |

Ideas

24 | - Add LoRa support?
25 | - Convert API to Adafruit_GFX for wider adoption?
26 | - Add WiFi support?
27 |

Goals

28 | Initially, this project was to explore using Bluetooth low energy (BLE) 29 | as a transport for a responsive and interactive system on low cost MCUs. I've 30 | learned a lot along the journey and had to create some workarounds to bugs and 31 | limitations that I found. After all the time I've spent on BLE, I've come to 32 | the conclusion that it is fast enough and reliable enough to use for a lot 33 | of purposes that the creators might not have imagined. The simplicity of 34 | having short range wireless communication without the need for authentication 35 | or user intervention makes it ideal for simple point-to-point 36 | activities. Since creating the basic infrastructure of this library, I 37 | welcome community involvement to grow this into something truly useful for 38 | the whole embedded/Arduino community.
39 |
40 | 41 | API
42 | --- 43 | Please consult the [Wiki](https://github.com/bitbank2/RemoteDisplay/wiki/API) for detailed info about each class and method
44 | -------------------------------------------------------------------------------- /RemoteDisplay.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // RemoteDisplay 3 | // 4 | // written by Larry Bank 5 | // bitbank@pobox.com 6 | // Project started 12/2/2020 7 | // Original JPEG code written 26+ years ago :) 8 | // The goal of this code is to decode baseline JPEG images 9 | // using no more than 18K of RAM (if sent directly to an LCD display) 10 | // 11 | // Copyright 2020 BitBank Software, Inc. All Rights Reserved. 12 | // Licensed under the Apache License, Version 2.0 (the "License"); 13 | // you may not use this file except in compliance with the License. 14 | // You may obtain a copy of the License at 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // Unless required by applicable law or agreed to in writing, software 17 | // distributed under the License is distributed on an "AS IS" BASIS, 18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | // See the License for the specific language governing permissions and 20 | // limitations under the License. 21 | //=========================================================================== 22 | // 23 | #include "RemoteDisplay.h" 24 | 25 | static const uint16_t __crc_16_table[256] PROGMEM = 26 | { 27 | 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 28 | 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 29 | 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 30 | 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 31 | 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 32 | 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 33 | 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 34 | 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 35 | 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 36 | 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 37 | 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 38 | 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 39 | 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 40 | 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 41 | 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 42 | 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 43 | 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 44 | 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 45 | 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 46 | 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 47 | 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 48 | 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 49 | 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 50 | 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 51 | 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 52 | 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 53 | 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 54 | 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 55 | 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 56 | 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 57 | 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 58 | 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 59 | }; 60 | 61 | uint16_t RemoteDisplay::crc_16(uint8_t *data, size_t size) 62 | { 63 | uint16_t crc = 0xFFFF; 64 | 65 | for (size_t i = 0; i < size; i++) crc = pgm_read_word_near(__crc_16_table + (((crc >> 8) ^ data[i]) & 0xff)) ^ (crc << 8); 66 | 67 | return crc; 68 | } /* crc_16() */ 69 | 70 | uint16_t RemoteDisplay::_get_buttons(void) 71 | { 72 | uint16_t buttons = 0; 73 | 74 | for (int i=0; i<_button_count; i++) 75 | if (digitalRead(_buttons[i]) == _button_active) 76 | buttons |= (1 << i); 77 | 78 | return buttons; 79 | } 80 | 81 | -------------------------------------------------------------------------------- /RemoteDisplay.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020 BitBank Software, Inc. All Rights Reserved. 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // Unless required by applicable law or agreed to in writing, software 8 | // distributed under the License is distributed on an "AS IS" BASIS, 9 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | // See the License for the specific language governing permissions and 11 | // limitations under the License. 12 | //=========================================================================== 13 | // 14 | #ifndef __REMOTEDISPLAY__ 15 | #define __REMOTEDISPLAY__ 16 | #if defined( __MACH__ ) || defined( __LINUX__ ) || defined( __MCUXPRESSO ) 17 | #include 18 | #include 19 | #include 20 | #include 21 | #define memcpy_P memcpy 22 | #define PROGMEM 23 | #else 24 | #include 25 | #include 26 | #include 27 | #endif 28 | #include "rd_constants.h" 29 | // 30 | // RemoteDisplay 31 | // Written by Larry Bank 32 | // Copyright (c) 2020 BitBank Software, Inc. 33 | // 34 | // Designed to allow remote or local displays and input 35 | // for easy prototyping without owning the final hardware 36 | // 37 | // 38 | // The base class which implements a few methods 39 | // and leaves the rest as virtual to implement in each specific 40 | // use case 41 | // 42 | class RemoteDisplay 43 | { 44 | public: 45 | RemoteDisplay() {_bConnected = false;} 46 | ~RemoteDisplay() {} 47 | virtual int begin() {return 0;} 48 | virtual void shutdown() {} 49 | virtual void defineButtons(int *list, int count, int active) 50 | { if (count > MAX_BUTTONS) count = MAX_BUTTONS; 51 | memcpy(_buttons, list, sizeof(int)*count); 52 | _button_active = active; 53 | _button_count = count; 54 | for (int i=0; i= 0 && fontIndex < MAX_FONT_INDEX) { 37 | _fonts[fontIndex] = (GFXfont *)pFont; 38 | return RD_SUCCESS; 39 | } 40 | return RD_INVALID_PARAMETER; 41 | } /* setFont() */ 42 | 43 | int SPIDisplay::setBitmap(uint8_t bitmapIndex, const uint8_t *pBitmap, int iBitmapSize) 44 | { 45 | if (bitmapIndex >= MAX_BITMAP_INDEX) 46 | return RD_INVALID_PARAMETER; 47 | _bitmaps[bitmapIndex] = (uint8_t *)pBitmap; 48 | return 0; 49 | } /* setBitmap() */ 50 | int SPIDisplay::dumpBuffer(uint8_t * buffer) 51 | { 52 | // not implemented yet 53 | (void)buffer; 54 | return RD_SUCCESS; 55 | } /* SPIDisplay::dumpBuffer() */ 56 | 57 | void SPIDisplay::shutdown() 58 | { 59 | spilcdShutdown(&_lcd); 60 | } /* SPIDisplay::shutdown() */ 61 | 62 | int SPIDisplay::fill(uint16_t u16Color) 63 | { 64 | spilcdFill(&_lcd, u16Color, DRAW_TO_LCD); 65 | return RD_SUCCESS; 66 | } /* SPIDisplay::fill() */ 67 | 68 | int SPIDisplay::drawLine(int x1, int y1, int x2, int y2, uint16_t u16Color) 69 | { 70 | spilcdDrawLine(&_lcd, x1, y1, x2, y2, u16Color, DRAW_TO_LCD); 71 | return RD_SUCCESS; 72 | } /* SPIDisplay::drawLine() */ 73 | 74 | int SPIDisplay::drawPixel(int x, int y, uint16_t u16Color) 75 | { 76 | spilcdSetPixel(&_lcd, x, y, u16Color, DRAW_TO_LCD); 77 | return RD_SUCCESS; 78 | } /* SPIDisplay::drawPixel() */ 79 | 80 | int SPIDisplay::drawBitmap(int x, int y, int bitmapIndex, int stretch) 81 | { 82 | if (bitmapIndex < 0 || bitmapIndex >= MAX_BITMAP_INDEX || _bitmaps[bitmapIndex] == NULL) 83 | return RD_INVALID_PARAMETER; 84 | spilcdDrawBMP(&_lcd, _bitmaps[bitmapIndex], x, y, stretch, 0, DRAW_TO_LCD); 85 | return RD_SUCCESS; 86 | } /* drawBitmap() */ 87 | 88 | int SPIDisplay::setWindow(int x, int y, int w, int h) 89 | { 90 | spilcdSetPosition(&_lcd, x, y, w, h, DRAW_TO_LCD); 91 | return RD_SUCCESS; 92 | } /* SPIDisplay::setWindow() */ 93 | 94 | int SPIDisplay::writePixels(uint16_t *pixels, int count, uint8_t bDMA) 95 | { 96 | spilcdWriteDataBlock(&_lcd, (uint8_t *)pixels, count*2, (bDMA)? (DRAW_TO_LCD | DRAW_WITH_DMA) : DRAW_TO_LCD); 97 | return RD_SUCCESS; 98 | } /* SPIDisplay::writePixels() */ 99 | 100 | int SPIDisplay::drawRect(int x, int y, int w, int h, uint16_t u16Color, int bFilled) 101 | { 102 | spilcdRectangle(&_lcd, x, y, w, h, u16Color, u16Color, bFilled, DRAW_TO_LCD); 103 | return RD_SUCCESS; 104 | } /* SPIDisplay::drawRect() */ 105 | 106 | int SPIDisplay::drawText(int x, int y, char *szText, uint8_t u8Font, uint16_t u16FGColor, uint16_t u16BGColor) 107 | { 108 | if (u8Font < RD_FONT_CUSTOM_0) 109 | spilcdWriteString(&_lcd, x, y, szText, u16FGColor, u16BGColor, u8Font, DRAW_TO_LCD); 110 | else // draw custom font 111 | spilcdWriteStringCustom(&_lcd, _fonts[u8Font - RD_FONT_CUSTOM_0], x, y, szText, u16FGColor, u16BGColor, 1, DRAW_TO_LCD); 112 | return RD_SUCCESS; 113 | } /* SPIDisplay::drawText() */ 114 | 115 | int SPIDisplay::drawEllipse(int x, int y, int r1, int r2, uint16_t u16Color, int bFilled) 116 | { 117 | spilcdEllipse(&_lcd, x, y, r1, r2, u16Color, bFilled, DRAW_TO_LCD); 118 | return RD_SUCCESS; 119 | } /* SPIDisplay::drawEllipse() */ 120 | 121 | int SPIDisplay::setOrientation(int angle) 122 | { 123 | int i = LCD_ORIENTATION_0; 124 | if (angle != 0 && angle != 90 && angle != 180 && angle != 270) 125 | return RD_INVALID_PARAMETER; 126 | if (angle == 0) 127 | i = LCD_ORIENTATION_0; 128 | else if (angle == 90) 129 | i = LCD_ORIENTATION_90; 130 | else if (angle == 180) 131 | i = LCD_ORIENTATION_180; 132 | else i = LCD_ORIENTATION_270; 133 | spilcdSetOrientation(&_lcd, i); 134 | _orientation = angle; 135 | return RD_SUCCESS; 136 | } /* SPIDisplay::setOrientation() */ 137 | 138 | uint16_t SPIDisplay::getButtons() 139 | { 140 | return _get_buttons(); 141 | } /* SPIDisplay::getButtons()*/ 142 | 143 | -------------------------------------------------------------------------------- /SPIDisplay.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPI (locally connected) 3 | // 4 | class SPIDisplay : public RemoteDisplay 5 | { 6 | public: 7 | SPIDisplay() : RemoteDisplay() {} 8 | ~SPIDisplay() {} 9 | int begin(uint16_t u16LCDType, uint16_t u16Flags, uint32_t u32Speed, int CS_Pin, int DC_Pin, int RESET_Pin, int LED_Pin); 10 | void shutdown(); 11 | int fill(uint16_t u16Color); 12 | int drawLine(int x1, int y1, int x2, int y2, uint16_t u16Color); 13 | int drawPixel(int x, int y, uint16_t u16Color); 14 | int setWindow(int x, int y, int w, int h); 15 | int writePixels(uint16_t *pixels, int count, uint8_t bDMA); 16 | int drawRect(int x, int y, int w, int h, uint16_t u16Color, int bFilled); 17 | int drawText(int x, int y, char *szText, uint8_t u8Font, uint16_t u16FGColor, uint16_t u16BGColor); 18 | int drawEllipse(int x, int y, int r1, int r2, uint16_t u16Color, int bFilled); 19 | int drawBitmap(int x, int y, int bitmapIndex, int stretch); 20 | int setFont(const GFXfont *pFont, int fontIndex); 21 | int setBitmap(uint8_t bitmapIndex, const uint8_t *pBitmap, int iBitmapSize); 22 | int setOrientation(int angle); 23 | int dumpBuffer(uint8_t * buffer); 24 | uint16_t getButtons(); 25 | private: 26 | SPILCD _lcd; 27 | GFXfont *_fonts[MAX_FONT_INDEX]; 28 | uint8_t *_bitmaps[MAX_BITMAP_INDEX]; 29 | uint8_t *_icons[MAX_ICON_INDEX]; 30 | 31 | }; // class SPIDisplay 32 | 33 | -------------------------------------------------------------------------------- /UARTDisplay.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // RemoteDisplay 3 | // 4 | // written by Larry Bank 5 | // bitbank@pobox.com 6 | // Project started 12/2/2020 7 | // Original JPEG code written 26+ years ago :) 8 | // The goal of this code is to decode baseline JPEG images 9 | // using no more than 18K of RAM (if sent directly to an LCD display) 10 | // 11 | // Copyright 2020 BitBank Software, Inc. All Rights Reserved. 12 | // Licensed under the Apache License, Version 2.0 (the "License"); 13 | // you may not use this file except in compliance with the License. 14 | // You may obtain a copy of the License at 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // Unless required by applicable law or agreed to in writing, software 17 | // distributed under the License is distributed on an "AS IS" BASIS, 18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | // See the License for the specific language governing permissions and 20 | // limitations under the License. 21 | //=========================================================================== 22 | // 23 | #include "RemoteDisplay.h" 24 | // 25 | // UART implementation 26 | // 27 | int UARTDisplay::UARTSendVarData(uint16_t *data, int count, void *varData) 28 | { 29 | uint8_t ucTemp[512]; 30 | int iSize = count*2 + data[1]; 31 | memcpy(ucTemp, data, count*sizeof(uint16_t)); // non-payload part 32 | memcpy(&ucTemp[count*sizeof(uint16_t)], varData, data[1]); // var payload 33 | // Serial needs to know the length first since it buffers the read 34 | Serial.write(0xff); // start with a couple of sync bytes 35 | Serial.write(0xff); 36 | Serial.write((uint8_t)iSize); // low byte of length 37 | Serial.write((uint8_t)(iSize >> 8)); // high byte 38 | Serial.write(ucTemp, iSize); 39 | Serial.flush(); // wait for transmission to complete 40 | return RD_SUCCESS; 41 | } /* UARTSendVarData() */ 42 | 43 | int UARTDisplay::UARTSend(uint16_t *data, int count) 44 | { 45 | data[count] = crc_16((uint8_t *)data, count * sizeof(uint16_t)); 46 | // Serial needs to know the length first since it buffers the read 47 | Serial.write(0xff); // start with a couple of sync bytes 48 | Serial.write(0xff); 49 | Serial.write(count * 2); // low byte of length 50 | Serial.write((uint8_t)0); // high byte 51 | Serial.write((uint8_t *)data, (count+1)*sizeof(uint16_t)); 52 | Serial.flush(); // wait for transmission to complete 53 | return RD_SUCCESS; 54 | } /* BLESend() */ 55 | 56 | int UARTDisplay::begin(uint16_t u16DisplayType) 57 | { 58 | uint16_t u16Tmp[4]; 59 | 60 | _display_type = u16DisplayType; 61 | Serial.begin(115200); // this baud rate should work for all situations 62 | while (!Serial) {delay(10);}; // wait until fully initialized 63 | // Send the remote server the type of display we want 64 | u16Tmp[0] = RD_INIT; 65 | u16Tmp[1] = u16DisplayType; 66 | return UARTSend(u16Tmp, 2); 67 | } /* UARTDisplay::begin() */ 68 | 69 | void UARTDisplay::shutdown() 70 | { 71 | } /* UARTDisplay::shutdown() */ 72 | 73 | int UARTDisplay::fill(uint16_t u16Color) 74 | { 75 | uint16_t u16Tmp[4]; 76 | u16Tmp[0] = RD_FILL; 77 | u16Tmp[1] = u16Color; 78 | return UARTSend(u16Tmp, 2); // send to the remote server 79 | } 80 | int UARTDisplay::drawLine(int x1, int y1, int x2, int y2, uint16_t u16Color) 81 | { 82 | uint16_t u16Tmp[8]; 83 | u16Tmp[0] = RD_DRAW_LINE; 84 | u16Tmp[1] = (uint16_t)x1; 85 | u16Tmp[2] = (uint16_t)y1; 86 | u16Tmp[3] = (uint16_t)x2; 87 | u16Tmp[4] = (uint16_t)y2; 88 | u16Tmp[5] = u16Color; 89 | return UARTSend(u16Tmp, 6); // send to the remote server 90 | } /* UARTDisplay::drawLine() */ 91 | 92 | int UARTDisplay::drawPixel(int x, int y, uint16_t u16Color) 93 | { 94 | uint16_t u16Tmp[8]; 95 | u16Tmp[0] = RD_DRAW_PIXEL; 96 | u16Tmp[1] = (uint16_t)x; 97 | u16Tmp[2] = (uint16_t)y; 98 | u16Tmp[3] = u16Color; 99 | return UARTSend(u16Tmp, 4); // send to the remote server 100 | } /* UARTDisplay::drawPixel() */ 101 | 102 | int UARTDisplay::setWindow(int x, int y, int w, int h) 103 | { 104 | uint16_t u16Tmp[8]; 105 | u16Tmp[0] = RD_SET_WINDOW; 106 | u16Tmp[1] = (uint16_t)x; 107 | u16Tmp[2] = (uint16_t)y; 108 | u16Tmp[3] = (uint16_t)w; 109 | u16Tmp[4] = (uint16_t)h; 110 | return UARTSend(u16Tmp, 5); // send to the remote server 111 | } /* UARTDisplay::setWindow() */ 112 | 113 | int UARTDisplay::writePixels(uint16_t *pixels, int count, uint8_t bDMA) 114 | { 115 | uint16_t u16Tmp[8]; 116 | u16Tmp[0] = RD_WRITE_PIXELS; 117 | u16Tmp[1] = (uint16_t)count*2; // payload size in bytes 118 | u16Tmp[2] = (uint16_t)bDMA; 119 | return UARTSendVarData(u16Tmp, 3, pixels); // send to the remote server 120 | } /* UARTDisplay::writePixels() */ 121 | 122 | int UARTDisplay::dumpBuffer(uint8_t * buffer) 123 | { 124 | if (_bpp == 1) { 125 | uint16_t u16Tmp[4]; 126 | int iCount = (_width * _height) / 8; 127 | u16Tmp[0] = RD_DUMP_BUFFER; 128 | u16Tmp[1] = iCount/2; // in terms of uint16_t's 129 | return UARTSendVarData(u16Tmp, 2, (void *)buffer); 130 | } else { 131 | return RD_NOT_SUPPORTED; 132 | } 133 | } /* UARTDisplay::dumpBuffer() */ 134 | 135 | int UARTDisplay::drawRect(int x, int y, int w, int h, uint16_t u16Color, int bFilled) 136 | { 137 | uint16_t u16Tmp[8]; 138 | u16Tmp[0] = RD_DRAW_RECT; 139 | u16Tmp[1] = (uint16_t)x; 140 | u16Tmp[2] = (uint16_t)y; 141 | u16Tmp[3] = (uint16_t)w; 142 | u16Tmp[4] = (uint16_t)h; 143 | u16Tmp[5] = u16Color; 144 | u16Tmp[6] = (uint16_t)bFilled; 145 | return UARTSend(u16Tmp, 7); // send to the remote server 146 | } /* UARTDisplay::drawRect() */ 147 | 148 | int UARTDisplay::drawText(int x, int y, char *szText, uint8_t u8Font, uint16_t u16FGColor, uint16_t u16BGColor) 149 | { 150 | uint16_t u16Tmp[8]; 151 | u16Tmp[0] = RD_DRAW_TEXT; 152 | u16Tmp[1] = strlen(szText); // payload size in bytes 153 | u16Tmp[2] = (uint16_t)x; 154 | u16Tmp[3] = (uint16_t)y; 155 | u16Tmp[4] = (uint16_t)u8Font; 156 | u16Tmp[5] = u16FGColor; 157 | u16Tmp[6] = u16BGColor; 158 | return UARTSendVarData(u16Tmp, 7, szText); // send to the remote server 159 | } /* UARTDisplay::drawText() */ 160 | 161 | int UARTDisplay::drawEllipse(int x, int y, int r1, int r2, uint16_t u16Color, int bFilled) 162 | { 163 | uint16_t u16Tmp[8]; 164 | u16Tmp[0] = RD_DRAW_ELLIPSE; 165 | u16Tmp[1] = (uint16_t)x; 166 | u16Tmp[2] = (uint16_t)y; 167 | u16Tmp[3] = (uint16_t)r1; 168 | u16Tmp[4] = (uint16_t)r2; 169 | u16Tmp[5] = u16Color; 170 | u16Tmp[6] = bFilled; 171 | return UARTSend(u16Tmp, 7); // send to the remote server 172 | } /* UARTDisplay::drawEllipse() */ 173 | 174 | int UARTDisplay::setOrientation(int angle) 175 | { 176 | int rc; 177 | uint16_t u16Tmp[4]; 178 | u16Tmp[0] = RD_SET_ORIENTATION; 179 | u16Tmp[1] = (uint16_t)angle; 180 | rc = UARTSend(u16Tmp, 2); // send to the remote server 181 | if (rc == RD_SUCCESS) 182 | _orientation = angle; 183 | return rc; 184 | } /* UARTDisplay::setOrientation() */ 185 | 186 | uint16_t UARTDisplay::getButtons() 187 | { 188 | // Need to explicitly ask for the buttons over serial 189 | uint16_t u16Tmp[4]; 190 | uint8_t buttons = 0; 191 | u16Tmp[0] = RD_GET_BUTTONS; 192 | UARTSend(u16Tmp, 1); // send to the remote server 193 | buttons = Serial.read(); // get 1 byte returned 194 | return buttons; 195 | 196 | } /* UARTDisplay::getButtons() */ 197 | 198 | -------------------------------------------------------------------------------- /UARTDisplay.h: -------------------------------------------------------------------------------- 1 | // 2 | // Serial connected (remote) display 3 | // 4 | class UARTDisplay : public RemoteDisplay 5 | { 6 | public: 7 | UARTDisplay() : RemoteDisplay() {} 8 | ~UARTDisplay() {} 9 | int begin(uint16_t u16DisplayType); 10 | void shutdown(); 11 | int fill(uint16_t u16Color); 12 | int drawLine(int x1, int y1, int x2, int y2, uint16_t u16Color); 13 | int drawPixel(int x, int y, uint16_t u16Color); 14 | int setWindow(int x, int y, int w, int h); 15 | int writePixels(uint16_t *pixels, int count, uint8_t bDMA); 16 | int drawRect(int x, int y, int w, int h, uint16_t u16Color, int bFilled); 17 | int drawText(int x, int y, char *szText, uint8_t u8Font, uint16_t u16FGColor, uint16_t u16BGColor); 18 | int drawEllipse(int x, int y, int r1, int r2, uint16_t u16Color, int bFilled); 19 | int setOrientation(int angle); 20 | int dumpBuffer(uint8_t * buffer); 21 | uint16_t getButtons(); 22 | private: 23 | int UARTSendVarData(uint16_t *data, int count, void *varData); 24 | int UARTSend(uint16_t *data, int count); 25 | 26 | }; // class UARTDisplay 27 | 28 | -------------------------------------------------------------------------------- /examples/nano33_camera/nano33_camera.ino: -------------------------------------------------------------------------------- 1 | #include 2 | //#include 3 | 4 | /* 5 | OV767X - Camera Capture Raw Bytes 6 | 7 | This sketch reads a frame from the OmniVision OV7670 camera 8 | and writes the bytes to the Serial port. Use the Procesing 9 | sketch in the extras folder to visualize the camera output. 10 | 11 | Circuit: 12 | - Arduino Nano 33 BLE board 13 | - OV7670 camera module: 14 | - 3.3 connected to 3.3 15 | - GND connected GND 16 | - SIOC connected to A5 17 | - SIOD connected to A4 18 | - VSYNC connected to 8 19 | - HREF connected to 10 20 | - PCLK connected to 12 21 | - XCLK connected to 9 22 | - D7 connected to 7 23 | - D6 connected to 6 24 | - D5 connected to 5 25 | - D4 connected to 4 26 | - D3 connected to 3 27 | - D2 connected to 2 28 | - D1 connected to 1 / RX 29 | - D0 connected to 0 / TX 30 | 31 | This example code is in the public domain. 32 | */ 33 | uint8_t ucTXBuf[1024]; 34 | #define CS_PIN -1 35 | #define DC_PIN A3 36 | #define RESET_PIN A2 37 | #define LED_PIN -1 38 | #define MISO_PIN -1 39 | #define MOSI_PIN -1 40 | #define SCK_PIN -1 41 | #include 42 | 43 | BLEDisplay disp; 44 | //SPIDisplay disp; 45 | 46 | //Arduino_CRC32 crc32; 47 | int errors = 0; 48 | int count = 0; 49 | 50 | int bytesPerFrame; 51 | int iWidth, iHeight; 52 | 53 | //byte data[320 * 240 * 2]; // QVGA: 320x240 X 2 bytes per pixel (RGB565) 54 | byte data[160 * 120 * 2]; // QVGA: 320x240 X 2 bytes per pixel (RGB565) 55 | 56 | void setup() { 57 | Serial.begin(115200); 58 | while (!Serial); 59 | 60 | disp.begin(RD_LCD_ILI9341); 61 | // disp.begin(RD_LCD_ILI9341, FLAGS_NONE, 8000000, CS_PIN, DC_PIN, RESET_PIN, LED_PIN); 62 | disp.setOrientation(90); 63 | disp.fill(0); 64 | if (!Camera.begin(QQVGA, RGB565, 5)) { 65 | Serial.println("Failed to initialize camera!"); 66 | while (1); 67 | } 68 | iWidth = Camera.width(); 69 | iHeight = Camera.height(); 70 | bytesPerFrame = iWidth * iHeight * Camera.bytesPerPixel(); 71 | 72 | // Optionally, enable the test pattern for testing 73 | // Camera.testPattern(); 74 | } 75 | 76 | void loop() { 77 | int32_t i, iFrameSize, r, g, b; 78 | uint16_t pixel, *s = (uint16_t *)&data[0]; 79 | int32_t iTime; 80 | 81 | iTime = millis(); 82 | Camera.readFrame(data); 83 | iTime = millis() - iTime; 84 | Serial.printf("Capture time = %d us\n", iTime); 85 | 86 | // uint32_t const crc32_res = crc32.calc(data, bytesPerFrame); 87 | // Serial.print("0x"); 88 | // Serial.print(crc32_res, HEX); 89 | // if (crc32_res != 0x15AB2939 && crc32_res != 0xD3EC95E) { 90 | // errors++; 91 | // }; 92 | 93 | // count++; 94 | // Serial.print(" errors:"); 95 | // Serial.print(errors); 96 | // Serial.print("/"); 97 | // Serial.println(count); 98 | 99 | iFrameSize = iWidth * iHeight; 100 | // average the pixels together and show a single color value 101 | //r = g = b = 0; 102 | //for (i=0; i> 11); 106 | // g += ((pixel & 0x7e0) >> 5); 107 | // b += (pixel & 0x1f); 108 | //} 109 | // r = r / iFrameSize; 110 | // g = g / iFrameSize; 111 | // b = b / iFrameSize; 112 | // Serial.printf("Average R:%d, G:%d, B:%d\n", r, g, b); 113 | // Serial.write(data, bytesPerFrame); 114 | // Serial.flush(); 115 | for (i=0; i 5 | 6 | //BLEDisplay disp; 7 | //SPIDisplay disp; 8 | I2CDisplay disp; 9 | #define CS_PIN -1 10 | #define DC_PIN A3 11 | #define RESET_PIN A2 12 | #define LED_PIN -1 13 | int button_list[] = {15, 16}; // 2 push buttons 14 | 15 | void setup() { 16 | // Serial.begin(115200); 17 | // while (!Serial) {}; 18 | 19 | disp.defineButtons(button_list, 2, LOW); 20 | disp.begin(RD_OLED_128x64); 21 | // disp.begin(RD_LCD_ILI9341, FLAGS_NONE, 8000000, CS_PIN, DC_PIN, RESET_PIN, LED_PIN); 22 | disp.setOrientation(0); 23 | } /* setup() */ 24 | 25 | void loop() { 26 | int x = 0; 27 | 28 | disp.fill(0); 29 | disp.drawText(0, 0, (char *)"0 Circles", FONT_12x16, 0x7e0, 0); 30 | disp.drawText(0, 16, (char *)"1 Lines", FONT_12x16, 0xffe0, 0); 31 | while (x == 0) 32 | { 33 | x = disp.getButtons(); 34 | delay(100); 35 | } 36 | disp.fill(0); 37 | if (x & 1) // button 0 38 | { 39 | for (x=1; x<32; x++) 40 | { 41 | disp.drawEllipse(64, 32, x, x, 0x7e0, 0); 42 | } 43 | } 44 | else if (x & 2) // button 1 45 | { 46 | for (x=0; x<127; x += 2) 47 | { 48 | disp.drawLine(x, 0, 127-x, 63, 0xffe0); 49 | } 50 | for (x=0; x<63; x += 2) 51 | { 52 | disp.drawLine(127, x, 0, 63-x, 0xffe0); 53 | } 54 | } 55 | delay(2000); 56 | } /* loop() */ 57 | -------------------------------------------------------------------------------- /examples/remotedisplay_demo/remotedisplay_demo.ino: -------------------------------------------------------------------------------- 1 | // 2 | // RemoteDisplay demo 3 | // 4 | #include 5 | #include 6 | #include "/Users/laurencebank/Documents/Arduino/libraries/JPEGDEC/test_images/st_peters.h" 7 | 8 | BLEDisplay disp; 9 | //SPIDisplay disp; 10 | #define CS_PIN -1 11 | #define DC_PIN A3 12 | #define RESET_PIN A2 13 | #define LED_PIN -1 14 | //int button_list[] = {35,39}; 15 | int button_list[] = {A6, A7}; // 2 push buttons 16 | JPEGDEC jpeg; 17 | 18 | void JPEGDraw(JPEGDRAW *pDraw) 19 | { 20 | disp.setWindow(pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight); 21 | disp.writePixels(pDraw->pPixels, pDraw->iWidth * pDraw->iHeight, true); // Use DMA 22 | // return 1; // continue decoding 23 | } /* JPEGDraw() */ 24 | 25 | 26 | void setup() { 27 | Serial.begin(115200); 28 | while (!Serial) {}; 29 | 30 | disp.defineButtons(button_list, 2, LOW); 31 | disp.begin(RD_LCD_ILI9341); 32 | // disp.begin(RD_LCD_ILI9341, FLAGS_NONE, 8000000, CS_PIN, DC_PIN, RESET_PIN, LED_PIN); 33 | disp.setOrientation(0); 34 | } /* setup() */ 35 | 36 | void loop() { 37 | int x = 0; 38 | 39 | disp.fill(0); 40 | disp.drawText(0, 64, (char *)"0 = Circles", FONT_12x16, 0x7e0, 0); 41 | disp.drawText(0, 96, (char *)"1 = Lines", FONT_12x16, 0xffe0, 0); 42 | disp.drawText(0, 128, (char *)"2 = JPEG", FONT_12x16, 0xffff, 0); 43 | while (x == 0) 44 | { 45 | x = disp.getButtons(); 46 | delay(100); 47 | } 48 | disp.fill(0); 49 | if (x & 1) // button 0 50 | { 51 | for (x=1; x<120; x++) 52 | { 53 | disp.drawEllipse(120, 160, x, x, 0x7e0, 0); 54 | } 55 | } 56 | else if (x & 2) // button 1 57 | { 58 | for (x=0; x<239; x += 2) 59 | { 60 | disp.drawLine(x, 0, 239-x, 319, 0xffe0); 61 | } 62 | for (x=0; x<319; x += 2) 63 | { 64 | disp.drawLine(239, x, 0, 319-x, 0xffe0); 65 | } 66 | } 67 | else if (x & 4) // button 2 68 | { 69 | if (jpeg.openFLASH((uint8_t *)st_peters, sizeof(st_peters), JPEGDraw)) 70 | { 71 | // lTime = micros(); 72 | // jpeg.setMaxOutputSize(1); 73 | if (jpeg.decode(0,0,0)) 74 | { 75 | // lTime = micros() - lTime; 76 | // Serial.printf("%d x %d image, decode time = %d us\n", jpeg.getWidth() >> i, jpeg.getHeight() >> i, (int)lTime); 77 | } 78 | jpeg.close(); 79 | } 80 | } 81 | delay(2000); 82 | } /* loop() */ 83 | -------------------------------------------------------------------------------- /rd_constants.h: -------------------------------------------------------------------------------- 1 | #define MAX_BUTTONS 4 2 | #define MAX_FONT_INDEX 4 3 | #define MAX_BITMAP_INDEX 4 4 | #define MAX_ICON_INDEX 16 5 | #define MAX_DATA_BLOCK 230 6 | /* Defines and variables */ 7 | enum { 8 | RD_NOP=0, 9 | RD_INIT, 10 | RD_DRAW_TEXT, 11 | RD_DRAW_LINE, 12 | RD_DRAW_ELLIPSE, 13 | RD_DRAW_BITMAP, 14 | RD_DRAW_ICON, 15 | RD_DRAW_PIXEL, 16 | RD_WRITE_PIXELS, 17 | RD_DUMP_BUFFER, 18 | RD_FILL, 19 | RD_DRAW_RECT, 20 | RD_SET_WINDOW, 21 | RD_SET_ORIENTATION, 22 | RD_GET_INFO, 23 | RD_GET_BUTTONS, 24 | RD_BACKLIGHT, 25 | RD_SET_FONT_BITMAP, 26 | RD_SET_FONT_INDEX, 27 | RD_SET_FONT_INFO, 28 | RD_SET_BITMAP, 29 | RD_SET_ICON, 30 | RD_API_COUNT 31 | }; 32 | 33 | // Supported display types 34 | enum { 35 | RD_LCD_INVALID=0, 36 | // Color LCDs/OLEDs 37 | RD_LCD_ILI9341, // 240x320 38 | RD_LCD_ILI9225, // 176x220 39 | RD_LCD_HX8357, // 320x480 40 | RD_LCD_ST7735R, // 128x160 41 | RD_LCD_ST7735S, // 80x160 with offset of 24,0 42 | RD_LCD_ST7735S_B, // 80x160 with offset of 26,2 43 | RD_LCD_SSD1331, 44 | RD_LCD_SSD1351, 45 | RD_LCD_ILI9342, // 320x240 IPS 46 | RD_LCD_ST7789, // 240x320 47 | RD_LCD_ST7789_240, // 240x240 48 | RD_LCD_ST7789_135, // 135x240 49 | RD_LCD_ST7789_NOCS, // 240x240 without CS, vertical offset of 80, MODE3 50 | RD_LCD_SSD1283A, // 132x132 51 | RD_LCD_ILI9486, // 320x480 52 | // Monochrome LCDs/OLEDs 53 | RD_OLED_128x128, 54 | RD_OLED_128x32, 55 | RD_OLED_128x64, 56 | RD_OLED_132x64, 57 | RD_OLED_64x32, 58 | RD_OLED_96x16, 59 | RD_OLED_72x40, 60 | RD_LCD_UC1701, 61 | RD_LCD_UC1609, 62 | RD_LCD_HX1230, 63 | RD_LCD_NOKIA5110, 64 | RD_SHARP_144x168, 65 | RD_SHARP_400x240, 66 | RD_EPOP_50, 67 | RD_EPOP_500, 68 | RD_EPOP_900, 69 | RD_LCD_COUNT 70 | }; 71 | 72 | // Error codes returned by getLastError() 73 | enum { 74 | RD_SUCCESS = 0, 75 | RD_INVALID_PARAMETER, 76 | RD_INIT_FAILED, 77 | RD_BUSY, 78 | RD_NOT_CONNECTED, 79 | RD_NOT_SUPPORTED 80 | }; 81 | 82 | // Built-in font sizes 83 | enum { 84 | RD_FONT_6x8 = 0, 85 | RD_FONT_8x8, 86 | RD_FONT_12x16, 87 | RD_FONT_16x16, 88 | RD_FONT_16x32, 89 | RD_FONT_CUSTOM_0, 90 | RD_FONT_CUSTOM_1, 91 | RD_FONT_CUSTOM_2, 92 | RD_FONT_CUSTOM_3, 93 | RD_FONT_COUNT 94 | }; 95 | 96 | 97 | --------------------------------------------------------------------------------