Mini E-Paper | Ep1. Uploading Emoji to E-Paper from Flutter App(iOS & Android)

This commit is contained in:
Eric
2021-09-12 22:11:12 -07:00
parent 6ca8c510be
commit ce83662ede
23 changed files with 3207 additions and 0 deletions

View File

@@ -0,0 +1,368 @@
/////////////////////////////////////////////////////////////////
/*
Mini E-Paper | Ep1. Uploading Emoji to E-Paper from Flutter App(iOS & Android)
Video Tutorial: https://youtu.be/-RhEiKldfDc
Created by Eric N. (ThatProject)
*/
/////////////////////////////////////////////////////////////////
#include <GxEPD2_BW.h>
#include "GxEPD2_display_selection_new_style.h"
#include <Fonts/FreeMonoBold9pt7b.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include "SPIFFS.h"
#define FileClass fs::File
BLEServer *pServer = NULL;
BLECharacteristic *pTxCharacteristic;
#define SERVICE_UUID "713d0001-503e-4c75-ba94-3148f18d941e"
#define CHARACTERISTIC_UUID_RX "713d0002-503e-4c75-ba94-3148f18d941e"
const char temp_filename[] = "/new_image.bmp";
const char BLE_Device_Name[] = "MINI E-PAPER";
class BLEServerCB : public BLEServerCallbacks {
void onConnect(BLEServer *pServer){};
void onDisconnect(BLEServer *pServer) {
pServer->startAdvertising();
}
};
class BLERxCallback : public BLECharacteristicCallbacks {
uint16_t read16(fs::File &f) {
// BMP data is stored little-endian, same as Arduino.
uint16_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read(); // MSB
return result;
}
uint32_t read32(fs::File &f) {
// BMP data is stored little-endian, same as Arduino.
uint32_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read();
((uint8_t *)&result)[2] = f.read();
((uint8_t *)&result)[3] = f.read(); // MSB
return result;
}
static const uint16_t input_buffer_pixels = 800; // may affect performance
static const uint16_t max_row_width = 80; // for up to 6" display 1448x1072
static const uint16_t max_palette_pixels = 256; // for depth <= 8
uint8_t input_buffer[3 * input_buffer_pixels]; // up to depth 24
uint8_t output_row_mono_buffer[max_row_width / 8]; // buffer for at least one row of b/w bits
uint8_t output_row_color_buffer[max_row_width / 8]; // buffer for at least one row of color bits
uint8_t mono_palette_buffer[max_palette_pixels / 8]; // palette buffer for depth <= 8 b/w
uint8_t color_palette_buffer[max_palette_pixels / 8]; // palette buffer for depth <= 8 c/w
uint16_t rgb_palette_buffer[max_palette_pixels]; // palette buffer for depth <= 8 for buffered graphics, needed for 7-color display
bool isEnd = true;
File file;
void onWrite(BLECharacteristic *pCharacteristic) {
std::string rxValue = pCharacteristic->getValue();
Serial.printf("RX length: %d\n", rxValue.length());
if (rxValue.length() > 0) {
file.write((const byte *)pCharacteristic->getData(), rxValue.length());
} else {
Serial.println("***GET START/END TRIGGER****");
isEnd = !isEnd;
if (!isEnd) {
file = SPIFFS.open(temp_filename, FILE_WRITE);
if (!file) {
Serial.println("File is not available!");
}else{
Serial.println("File is ready to write!");
}
} else {
file.close();
Serial.println("File Closed!");
drawBitmapFromSpiffs_Buffered(temp_filename, 0, 0, false, false, false);
}
}
}
void drawBitmapFromSpiffs_Buffered(const char *filename, int16_t x, int16_t y, bool with_color, bool partial_update, bool overwrite) {
fs::File file;
bool valid = false; // valid format to be handled
bool flip = true; // bitmap is stored bottom-to-top
bool has_multicolors = display.epd2.panel == GxEPD2::ACeP565;
uint32_t startTime = millis();
if ((x >= display.width()) || (y >= display.height())) return;
Serial.println();
Serial.print("Loading image '");
Serial.print(filename);
Serial.println('\'');
file = SPIFFS.open(filename, "r");
if (!file) {
Serial.print("File not found");
return;
}
// Parse BMP header
if (read16(file) == 0x4D42) // BMP signature
{
uint32_t fileSize = read32(file);
uint32_t creatorBytes = read32(file);
uint32_t imageOffset = read32(file); // Start of image data
uint32_t headerSize = read32(file);
uint32_t width = read32(file);
uint32_t height = read32(file);
uint16_t planes = read16(file);
uint16_t depth = read16(file); // bits per pixel
uint32_t format = read32(file);
if ((planes == 1) && ((format == 0) || (format == 3))) // uncompressed is handled, 565 also
{
Serial.print("File size: ");
Serial.println(fileSize);
Serial.print("Image Offset: ");
Serial.println(imageOffset);
Serial.print("Header size: ");
Serial.println(headerSize);
Serial.print("Bit Depth: ");
Serial.println(depth);
Serial.print("Image size: ");
Serial.print(width);
Serial.print('x');
Serial.println(height);
// BMP rows are padded (if needed) to 4-byte boundary
uint32_t rowSize = (width * depth / 8 + 3) & ~3;
if (depth < 8) rowSize = ((width * depth + 8 - depth) / 8 + 3) & ~3;
if (height < 0) {
height = -height;
flip = false;
}
uint16_t w = width;
uint16_t h = height;
if ((x + w - 1) >= display.width()) w = display.width() - x;
if ((y + h - 1) >= display.height()) h = display.height() - y;
//if (w <= max_row_width) // handle with direct drawing
{
valid = true;
uint8_t bitmask = 0xFF;
uint8_t bitshift = 8 - depth;
uint16_t red, green, blue;
bool whitish, colored;
if (depth == 1) with_color = false;
if (depth <= 8) {
if (depth < 8) bitmask >>= depth;
//file.seek(54); //palette is always @ 54
file.seek(imageOffset - (4 << depth)); // 54 for regular, diff for colorsimportant
for (uint16_t pn = 0; pn < (1 << depth); pn++) {
blue = file.read();
green = file.read();
red = file.read();
file.read();
whitish = with_color ? ((red > 0x80) && (green > 0x80) && (blue > 0x80)) : ((red + green + blue) > 3 * 0x80); // whitish
colored = (red > 0xF0) || ((green > 0xF0) && (blue > 0xF0)); // reddish or yellowish?
if (0 == pn % 8) mono_palette_buffer[pn / 8] = 0;
mono_palette_buffer[pn / 8] |= whitish << pn % 8;
if (0 == pn % 8) color_palette_buffer[pn / 8] = 0;
color_palette_buffer[pn / 8] |= colored << pn % 8;
rgb_palette_buffer[pn] = ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | ((blue & 0xF8) >> 3);
}
}
if (partial_update) display.setPartialWindow(x, y, w, h);
else
display.setFullWindow();
display.firstPage();
do {
if (!overwrite) display.fillScreen(GxEPD_WHITE);
uint32_t rowPosition = flip ? imageOffset + (height - h) * rowSize : imageOffset;
for (uint16_t row = 0; row < h; row++, rowPosition += rowSize) // for each line
{
uint32_t in_remain = rowSize;
uint32_t in_idx = 0;
uint32_t in_bytes = 0;
uint8_t in_byte = 0; // for depth <= 8
uint8_t in_bits = 0; // for depth <= 8
uint16_t color = GxEPD_WHITE;
file.seek(rowPosition);
for (uint16_t col = 0; col < w; col++) // for each pixel
{
// Time to read more pixel data?
if (in_idx >= in_bytes) // ok, exact match for 24bit also (size IS multiple of 3)
{
in_bytes = file.read(input_buffer, in_remain > sizeof(input_buffer) ? sizeof(input_buffer) : in_remain);
in_remain -= in_bytes;
in_idx = 0;
}
switch (depth) {
case 24:
blue = input_buffer[in_idx++];
green = input_buffer[in_idx++];
red = input_buffer[in_idx++];
whitish = with_color ? ((red > 0x80) && (green > 0x80) && (blue > 0x80)) : ((red + green + blue) > 3 * 0x80); // whitish
colored = (red > 0xF0) || ((green > 0xF0) && (blue > 0xF0)); // reddish or yellowish?
color = ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | ((blue & 0xF8) >> 3);
break;
case 16:
{
uint8_t lsb = input_buffer[in_idx++];
uint8_t msb = input_buffer[in_idx++];
if (format == 0) // 555
{
blue = (lsb & 0x1F) << 3;
green = ((msb & 0x03) << 6) | ((lsb & 0xE0) >> 2);
red = (msb & 0x7C) << 1;
color = ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | ((blue & 0xF8) >> 3);
} else // 565
{
blue = (lsb & 0x1F) << 3;
green = ((msb & 0x07) << 5) | ((lsb & 0xE0) >> 3);
red = (msb & 0xF8);
color = (msb << 8) | lsb;
}
whitish = with_color ? ((red > 0x80) && (green > 0x80) && (blue > 0x80)) : ((red + green + blue) > 3 * 0x80); // whitish
colored = (red > 0xF0) || ((green > 0xF0) && (blue > 0xF0)); // reddish or yellowish?
}
break;
case 1:
case 4:
case 8:
{
if (0 == in_bits) {
in_byte = input_buffer[in_idx++];
in_bits = 8;
}
uint16_t pn = (in_byte >> bitshift) & bitmask;
whitish = mono_palette_buffer[pn / 8] & (0x1 << pn % 8);
colored = color_palette_buffer[pn / 8] & (0x1 << pn % 8);
in_byte <<= depth;
in_bits -= depth;
color = rgb_palette_buffer[pn];
}
break;
}
if (with_color && has_multicolors) {
// keep color
} else if (whitish) {
color = GxEPD_WHITE;
} else if (colored && with_color) {
color = GxEPD_COLORED;
} else {
color = GxEPD_BLACK;
}
uint16_t yrow = y + (flip ? h - row - 1 : row);
display.drawPixel(x + col, yrow, color);
} // end pixel
} // end line
} while (display.nextPage());
Serial.print("loaded in ");
Serial.print(millis() - startTime);
Serial.println(" ms");
}
}
}
file.close();
if (!valid) {
Serial.println("bitmap format not handled.");
}
}
};
void setup() {
Serial.begin(115200);
Serial.println();
initDisplay();
initSPIFFS();
initBLE();
}
void loop(void) {}
void initDisplay() {
SPI.begin(EPD_SCLK, EPD_MISO, EPD_MOSI);
display.init(115200);
}
void initSPIFFS() {
if (!SPIFFS.begin()) {
Serial.println("SPIFFS initialisation failed!");
while (1) yield();
}
Serial.println("SPIFFS started");
listSPIFFS();
}
void listSPIFFS(void) {
Serial.println(F("\r\nListing SPIFFS files:"));
static const char line[] PROGMEM = "=================================================";
Serial.println(FPSTR(line));
Serial.println(F(" File name Size"));
Serial.println(FPSTR(line));
fs::File root = SPIFFS.open("/");
if (!root) {
Serial.println(F("Failed to open directory"));
return;
}
if (!root.isDirectory()) {
Serial.println(F("Not a directory"));
return;
}
fs::File file = root.openNextFile();
while (file) {
if (file.isDirectory()) {
Serial.print("DIR : ");
String fileName = file.name();
Serial.print(fileName);
} else {
String fileName = file.name();
Serial.print(" " + fileName);
// File path can be 31 characters maximum in SPIFFS
int spaces = 33 - fileName.length(); // Tabulate nicely
if (spaces < 1) spaces = 1;
while (spaces--) Serial.print(" ");
String fileSize = (String) file.size();
spaces = 10 - fileSize.length(); // Tabulate nicely
if (spaces < 1) spaces = 1;
while (spaces--) Serial.print(" ");
Serial.println(fileSize + " bytes");
}
file = root.openNextFile();
}
Serial.println(FPSTR(line));
Serial.println();
delay(1000);
}
void initBLE() {
Serial.println("**** INIT BLE ****");
// Create the BLE Device
BLEDevice::init(BLE_Device_Name);
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new BLEServerCB());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
BLECharacteristic *pRxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,
BLECharacteristic::PROPERTY_WRITE);
pRxCharacteristic->setCallbacks(new BLERxCallback());
// Start the service
pService->start();
// Start advertising
pServer->getAdvertising()->start();
Serial.println("Start advertising");
}

View File

@@ -0,0 +1,222 @@
// Display Library example for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
// Requires HW SPI and Adafruit_GFX. Caution: the e-paper panels require 3.3V supply AND data lines!
//
// Display Library based on Demo Example from Good Display: http://www.e-paper-display.com/download_list/downloadcategoryid=34&isMode=false.html
//
// Author: Jean-Marc Zingg
//
// Version: see library.properties
//
// Library: https://github.com/ZinggJM/GxEPD2
// Supporting Arduino Forum Topics:
// Waveshare e-paper displays with SPI: http://forum.arduino.cc/index.php?topic=487007.0
// Good Display ePaper for Arduino: https://forum.arduino.cc/index.php?topic=436411.0
// select the display class (only one), matching the kind of display panel
#define EPD_MOSI (21)
#define EPD_MISO (-1)
#define EPD_SCLK (22)
#define EPD_CS (5)
#define EPD_BUSY (34)
#define EPD_RSET (4)
#define EPD_DC (19)
#define GxEPD2_DISPLAY_CLASS GxEPD2_BW
//#define GxEPD2_DISPLAY_CLASS GxEPD2_3C
//#define GxEPD2_DISPLAY_CLASS GxEPD2_7C
// select the display driver class (only one) for your panel
#define GxEPD2_DRIVER_CLASS GxEPD2_102 // GxEPD2_102 80x128, UC8175
//#define GxEPD2_DRIVER_CLASS GxEPD2_154 // GDEP015OC1 200x200, IL3829, no longer available
//#define GxEPD2_DRIVER_CLASS GxEPD2_154_D67 // GDEH0154D67 200x200, SSD1681
//#define GxEPD2_DRIVER_CLASS GxEPD2_154_T8 // GDEW0154T8 152x152, UC8151 (IL0373)
//#define GxEPD2_DRIVER_CLASS GxEPD2_154_M09 // GDEW0154M09 200x200, JD79653A
//#define GxEPD2_DRIVER_CLASS GxEPD2_154_M10 // GDEW0154M10 152x152, UC8151D
//#define GxEPD2_DRIVER_CLASS GxEPD2_213 // GDE0213B1 128x250, IL3895, phased out
//#define GxEPD2_DRIVER_CLASS GxEPD2_213_B72 // GDEH0213B72 128x250, SSD1675A (IL3897)
//#define GxEPD2_DRIVER_CLASS GxEPD2_213_B73 // GDEH0213B73 128x250, SSD1675B
//#define GxEPD2_DRIVER_CLASS GxEPD2_213_B74 // GDEM0213B74 128x250, SSD1680
//#define GxEPD2_DRIVER_CLASS GxEPD2_213_flex // GDEW0213I5F 104x212, UC8151 (IL0373)
//#define GxEPD2_DRIVER_CLASS GxEPD2_213_M21 // GDEW0213M21 104x212, UC8151 (IL0373)
//#define GxEPD2_DRIVER_CLASS GxEPD2_213_T5D // GDEW0213T5D 104x212, UC8151D
//#define GxEPD2_DRIVER_CLASS GxEPD2_290 // GDEH029A1 128x296, SSD1608 (IL3820)
//#define GxEPD2_DRIVER_CLASS GxEPD2_290_T5 // GDEW029T5 128x296, UC8151 (IL0373)
//#define GxEPD2_DRIVER_CLASS GxEPD2_290_T5D // GDEW029T5D 128x296, UC8151D
//#define GxEPD2_DRIVER_CLASS GxEPD2_290_T94 // GDEM029T94 128x296, SSD1680
//#define GxEPD2_DRIVER_CLASS GxEPD2_290_T94_V2 // GDEM029T94 128x296, SSD1680, Waveshare 2.9" V2 variant
//#define GxEPD2_DRIVER_CLASS GxEPD2_290_M06 // GDEW029M06 128x296, UC8151 (IL0373)
//#define GxEPD2_DRIVER_CLASS GxEPD2_260 // GDEW026T0 152x296, UC8151 (IL0373)
//#define GxEPD2_DRIVER_CLASS GxEPD2_260_M01 // GDEW026M01 152x296, UC8151 (IL0373)
//#define GxEPD2_DRIVER_CLASS GxEPD2_270 // GDEW027W3 176x264, EK79652 (IL91874)
//#define GxEPD2_DRIVER_CLASS GxEPD2_371 // GDEW0371W7 240x416, UC8171 (IL0324)
//#define GxEPD2_DRIVER_CLASS GxEPD2_420 // GDEW042T2 400x300, UC8176 (IL0398)
//#define GxEPD2_DRIVER_CLASS GxEPD2_420_M01 // GDEW042M01 400x300, UC8176 (IL0398)
//#define GxEPD2_DRIVER_CLASS GxEPD2_583 // GDEW0583T7 600x448, UC8179 (IL0371)
//#define GxEPD2_DRIVER_CLASS GxEPD2_583_T8 // GDEW0583T8 648x480, GD7965
//#define GxEPD2_DRIVER_CLASS GxEPD2_750 // GDEW075T8 640x384, UC8179 (IL0371)
//#define GxEPD2_DRIVER_CLASS GxEPD2_750_T7 // GDEW075T7 800x480, GD7965
//#define GxEPD2_DRIVER_CLASS GxEPD2_1160_T91 // GDEH116T91 960x640, SSD1677
// 3-color e-papers
//#define GxEPD2_DRIVER_CLASS GxEPD2_154c // GDEW0154Z04 200x200, IL0376F, no longer available
//#define GxEPD2_DRIVER_CLASS GxEPD2_154_Z90c // GDEH0154Z90 200x200, SSD1682
//#define GxEPD2_DRIVER_CLASS GxEPD2_213c // GDEW0213Z16 104x212, UC8151 (IL0373)
//#define GxEPD2_DRIVER_CLASS GxEPD2_213_Z19c // GDEW0213Z19 104x212, UC8151D
//#define GxEPD2_DRIVER_CLASS GxEPD2_290c // GDEW029Z10 128x296, UC8151 (IL0373)
//#define GxEPD2_DRIVER_CLASS GxEPD2_290_Z13c // GDEH029Z13 128x296, UC8151D
//#define GxEPD2_DRIVER_CLASS GxEPD2_290_C90c // GDEM029C90 128x296, SSD1680
//#define GxEPD2_DRIVER_CLASS GxEPD2_270c // GDEW027C44 176x264, IL91874
//#define GxEPD2_DRIVER_CLASS GxEPD2_420c // GDEW042Z15 400x300, UC8176 (IL0398)
//#define GxEPD2_DRIVER_CLASS GxEPD2_583c // GDEW0583Z21 600x448, UC8179 (IL0371)
//#define GxEPD2_DRIVER_CLASS GxEPD2_750c // GDEW075Z09 640x384, UC8179 (IL0371)
//#define GxEPD2_DRIVER_CLASS GxEPD2_750c_Z08 // GDEW075Z08 800x480, GD7965
//#define GxEPD2_DRIVER_CLASS GxEPD2_750c_Z90 // GDEH075Z90 880x528, SSD1677
//#define GxEPD2_DRIVER_CLASS GxEPD2_1248 // GDEW1248T3 1303x984, UC8179
// 7-color e-paper
//#define GxEPD2_DRIVER_CLASS GxEPD2_565c // Waveshare 5.65" 7-color (3C graphics)
// grey levels parallel IF e-papers on Waveshare e-Paper IT8951 Driver HAT
//#define GxEPD2_DRIVER_CLASS GxEPD2_it60 // ED060SCT 800x600
//#define GxEPD2_DRIVER_CLASS GxEPD2_it60_1448x1072 // ED060KC1 1448x1072
//#define GxEPD2_DRIVER_CLASS GxEPD2_it78_1872x1404 // ED078KC2 1872x1404
// SS is usually used for CS. define here for easy change
#ifndef EPD_CS
#define EPD_CS SS
#endif
#if defined(GxEPD2_DISPLAY_CLASS) && defined(GxEPD2_DRIVER_CLASS)
// somehow there should be an easier way to do this
#define GxEPD2_BW_IS_GxEPD2_BW true
#define GxEPD2_3C_IS_GxEPD2_3C true
#define GxEPD2_7C_IS_GxEPD2_7C true
#define GxEPD2_1248_IS_GxEPD2_1248 true
#define IS_GxEPD(c, x) (c##x)
#define IS_GxEPD2_BW(x) IS_GxEPD(GxEPD2_BW_IS_, x)
#define IS_GxEPD2_3C(x) IS_GxEPD(GxEPD2_3C_IS_, x)
#define IS_GxEPD2_7C(x) IS_GxEPD(GxEPD2_7C_IS_, x)
#define IS_GxEPD2_1248(x) IS_GxEPD(GxEPD2_1248_IS_, x)
#include "GxEPD2_selection_check.h"
#if defined (ESP8266)
#define MAX_DISPLAY_BUFFER_SIZE (81920ul-34000ul-5000ul) // ~34000 base use, change 5000 to your application use
#if IS_GxEPD2_BW(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
#elif IS_GxEPD2_3C(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))
#elif IS_GxEPD2_7C(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
#endif
// adapt the constructor parameters to your wiring
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
// mapping of Waveshare e-Paper ESP8266 Driver Board, new version
//GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=15*/ EPD_CS, /*DC=4*/ 4, /*RST=2*/ 2, /*BUSY=5*/ 5));
// mapping of Waveshare e-Paper ESP8266 Driver Board, old version
//GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=15*/ EPD_CS, /*DC=4*/ 4, /*RST=5*/ 5, /*BUSY=16*/ 16));
#endif
#if defined(ESP32)
#define MAX_DISPLAY_BUFFER_SIZE 65536ul // e.g.
#if IS_GxEPD2_BW(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
#elif IS_GxEPD2_3C(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))
#elif IS_GxEPD2_7C(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
#endif
// adapt the constructor parameters to your wiring
#if !IS_GxEPD2_1248(GxEPD2_DRIVER_CLASS)
#if defined(ARDUINO_LOLIN_D32_PRO)
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=5*/ EPD_CS, /*DC=*/ 0, /*RST=*/ 2, /*BUSY=*/ 15));
#elif defined(ARDUINO_ESP32_DEV) // e.g. TTGO T8 ESP32-WROVER
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=5*/ EPD_CS, /*DC=*/ 2, /*RST=*/ 0, /*BUSY=*/ 4));
#else
//GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=5*/ EPD_CS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4));
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=5*/ EPD_CS, /*DC=*/ EPD_DC, /*RST=*/ EPD_RSET, /*BUSY=*/ EPD_BUSY));
#endif
#else // GxEPD2_1248
// Waveshare 12.48 b/w SPI display board and frame or Good Display 12.48 b/w panel GDEW1248T3
// general constructor for use with all parameters, e.g. for Waveshare ESP32 driver board mounted on connection board
GxEPD2_BW < GxEPD2_1248, GxEPD2_1248::HEIGHT / 4 > display(GxEPD2_1248(/*sck=*/ 13, /*miso=*/ 12, /*mosi=*/ 14,
/*cs_m1=*/ 23, /*cs_s1=*/ 22, /*cs_m2=*/ 16, /*cs_s2=*/ 19,
/*dc1=*/ 25, /*dc2=*/ 17, /*rst1=*/ 33, /*rst2=*/ 5,
/*busy_m1=*/ 32, /*busy_s1=*/ 26, /*busy_m2=*/ 18, /*busy_s2=*/ 4));
#endif
#endif
// can't use package "STMF1 Boards (STM32Duino.com)" (Roger Clark) anymore with Adafruit_GFX, use "STM32 Boards (selected from submenu)" (STMicroelectronics)
#if defined(ARDUINO_ARCH_STM32)
#define MAX_DISPLAY_BUFFER_SIZE 15000ul // ~15k is a good compromise
#if IS_GxEPD2_BW(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
#elif IS_GxEPD2_3C(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))
#elif IS_GxEPD2_7C(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
#endif
// adapt the constructor parameters to your wiring
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
#endif
#if defined(__AVR)
#if defined (ARDUINO_AVR_MEGA2560) // Note: SS is on 53 on MEGA
#define MAX_DISPLAY_BUFFER_SIZE 5000 // e.g. full height for 200x200
#else // Note: SS is on 10 on UNO, NANO
#define MAX_DISPLAY_BUFFER_SIZE 800 //
#endif
#if IS_GxEPD2_BW(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
#elif IS_GxEPD2_3C(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))
#elif IS_GxEPD2_7C(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
#endif
// adapt the constructor parameters to your wiring
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
#endif
#if defined(ARDUINO_ARCH_SAM)
#define MAX_DISPLAY_BUFFER_SIZE 32768ul // e.g., up to 96k
#if IS_GxEPD2_BW(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
#elif IS_GxEPD2_3C(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))
#elif IS_GxEPD2_7C(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
#endif
// adapt the constructor parameters to your wiring
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=77*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
#endif
#if defined(ARDUINO_ARCH_SAMD)
#define MAX_DISPLAY_BUFFER_SIZE 15000ul // ~15k is a good compromise
#if IS_GxEPD2_BW(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
#elif IS_GxEPD2_3C(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))
#elif IS_GxEPD2_7C(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
#endif
// adapt the constructor parameters to your wiring
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=4*/ 4, /*DC=*/ 7, /*RST=*/ 6, /*BUSY=*/ 5));
//GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=4*/ 4, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1)); // my Seed XIOA0
//GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=4*/ 3, /*DC=*/ 2, /*RST=*/ 1, /*BUSY=*/ 0)); // my other Seed XIOA0
#endif
#if defined(ARDUINO_ARCH_RP2040)
#define MAX_DISPLAY_BUFFER_SIZE 131072ul // e.g. half of available ram
#if IS_GxEPD2_BW(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
#elif IS_GxEPD2_3C(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))
#elif IS_GxEPD2_7C(GxEPD2_DISPLAY_CLASS)
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
#endif
// adapt the constructor parameters to your wiring
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=4*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
#endif
#endif

View File

@@ -0,0 +1,75 @@
// Display Library example for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
// Requires HW SPI and Adafruit_GFX. Caution: the e-paper panels require 3.3V supply AND data lines!
//
// Display Library based on Demo Example from Good Display: http://www.e-paper-display.com/download_list/downloadcategoryid=34&isMode=false.html
//
// Author: Jean-Marc Zingg
//
// Version: see library.properties
//
// Library: https://github.com/ZinggJM/GxEPD2
// Supporting Arduino Forum Topics:
// Waveshare e-paper displays with SPI: http://forum.arduino.cc/index.php?topic=487007.0
// Good Display ePaper for Arduino: https://forum.arduino.cc/index.php?topic=436411.0
#define GxEPD2_102_IS_BW true
#define GxEPD2_154_IS_BW true
#define GxEPD2_154_D67_IS_BW true
#define GxEPD2_154_T8_IS_BW true
#define GxEPD2_154_M09_IS_BW true
#define GxEPD2_154_M10_IS_BW true
#define GxEPD2_213_IS_BW true
#define GxEPD2_213_B72_IS_BW true
#define GxEPD2_213_B73_IS_BW true
#define GxEPD2_213_B74_IS_BW true
#define GxEPD2_213_flex_IS_BW true
#define GxEPD2_213_M21_IS_BW true
#define GxEPD2_213_T5D_IS_BW true
#define GxEPD2_290_IS_BW true
#define GxEPD2_290_T5_IS_BW true
#define GxEPD2_290_T5D_IS_BW true
#define GxEPD2_290_T94_IS_BW true
#define GxEPD2_290_T94_V2_IS_BW true
#define GxEPD2_290_M06_IS_BW true
#define GxEPD2_260_IS_BW true
#define GxEPD2_260_M01_IS_BW true
#define GxEPD2_270_IS_BW true
#define GxEPD2_371_IS_BW true
#define GxEPD2_420_IS_BW true
#define GxEPD2_420_M01_IS_BW true
#define GxEPD2_583_IS_BW true
#define GxEPD2_583_T8_IS_BW true
#define GxEPD2_750_IS_BW true
#define GxEPD2_750_T7_IS_BW true
#define GxEPD2_1160_T91_IS_BW true
// 3-color e-papers
#define GxEPD2_154c_IS_3C true
#define GxEPD2_154_Z90c_IS_3C true
#define GxEPD2_213c_IS_3C true
#define GxEPD2_213_Z19c_IS_3C true
#define GxEPD2_290c_IS_3C true
#define GxEPD2_290_Z13c_IS_3C true
#define GxEPD2_290_C90c_IS_3C true
#define GxEPD2_270c_IS_3C true
#define GxEPD2_420c_IS_3C true
#define GxEPD2_583c_IS_3C true
#define GxEPD2_750c_IS_3C true
#define GxEPD2_750c_Z08_IS_3C true
#define GxEPD2_750c_Z90_IS_3C true
#define GxEPD2_1248_IS_3C true
// 7-color e-paper
#define GxEPD2_565c_IS_7C true
#if defined(GxEPD2_DISPLAY_CLASS) && defined(GxEPD2_DRIVER_CLASS)
#define IS_GxEPD2_DRIVER(c, x) (c##x)
#define IS_GxEPD2_DRIVER_BW(x) IS_GxEPD2_DRIVER(x, _IS_BW)
#define IS_GxEPD2_DRIVER_3C(x) IS_GxEPD2_DRIVER(x, _IS_3C)
#if IS_GxEPD2_BW(GxEPD2_DISPLAY_CLASS) && IS_GxEPD2_DRIVER_3C(GxEPD2_DRIVER_CLASS)
#error "GxEPD2_BW used with 3-color driver class"
#endif
#if IS_GxEPD2_3C(GxEPD2_DISPLAY_CLASS) && IS_GxEPD2_DRIVER_BW(GxEPD2_DRIVER_CLASS)
#error "GxEPD2_3C used with b/w driver class"
#endif
#endif

View File

@@ -0,0 +1,46 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

View File

@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 4cc385b4b84ac2f816d939a49ea1f328c4e0b48e
channel: stable
project_type: app

View File

@@ -0,0 +1,5 @@
# Mini E-Paper | Ep1. Uploading Emoji to E-Paper from Flutter App(iOS & Android)
https://youtu.be/-RhEiKldfDc
# My Channel
www.that-project.com

View File

@@ -0,0 +1,29 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -0,0 +1,6 @@
import 'package:flutter/material.dart';
// Colors
const kMainBG = Color(0xff191720);
const kTextFieldFill = Color(0xff1E1C24);
const kCardBG = Color(0xffffffff);

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
const kHeadline = TextStyle(
color: Colors.white,
fontSize: 34,
fontWeight: FontWeight.bold,
);
const kBodyText = TextStyle(
color: Colors.grey,
fontSize: 15,
);
const kButtonText = TextStyle(
color: Colors.black87,
fontSize: 16,
fontWeight: FontWeight.bold,
);
const kBodyText2 =
TextStyle(fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white);
const kSubtitleText = TextStyle(
color: Colors.grey,
fontSize: 12,
);

View File

@@ -0,0 +1,848 @@
const emojiArray = [
"😄",
"😆",
"😊",
"😃",
"😏",
"😍",
"😘",
"😚",
"😳",
"😌",
"😆",
"😁",
"😉",
"😜",
"😝",
"😀",
"😗",
"😙",
"😛",
"😴",
"😟",
"😦",
"😧",
"😮",
"😬",
"😕",
"😯",
"😑",
"😒",
"😅",
"😓",
"😥",
"😩",
"😔",
"😞",
"😖",
"😨",
"😰",
"😣",
"😢",
"😭",
"😂",
"😲",
"😱",
"😫",
"😠",
"😡",
"😤",
"😪",
"😋",
"😷",
"😎",
"😵",
"👿",
"😈",
"😐",
"😶",
"😇",
"👽",
"💛",
"💙",
"💜",
"💚",
"💔",
"💓",
"💗",
"💕",
"💞",
"💘",
"💖",
"",
"",
"🌟",
"💫",
"💥",
"💢",
"",
"",
"",
"",
"💤",
"💨",
"💦",
"🎶",
"🎵",
"🔥",
"💩",
"👍",
"👎",
"👌",
"👊",
"",
"✌️",
"👋",
"",
"👐",
"☝️",
"👇",
"👈",
"👉",
"🙌",
"🙏",
"👆",
"👏",
"💪",
"🤘",
"🖕",
"🚶",
"🏃",
"👫",
"👪",
"👬",
"👭",
"💃",
"👯",
"🙆",
"🙅",
"💁",
"🙋",
"👰",
"🙇",
"💏",
"💑",
"💆",
"💇",
"💅",
"👦",
"👧",
"👩",
"👨",
"👶",
"👵",
"👴",
"👲",
"👳",
"👷",
"👮",
"👼",
"👸",
"😺",
"😸",
"😻",
"😽",
"😼",
"🙀",
"😿",
"😹",
"😾",
"👹",
"👺",
"🙈",
"🙉",
"🙊",
"💂",
"💀",
"🐾",
"👄",
"💋",
"💧",
"👂",
"👀",
"👃",
"👅",
"💌",
"👤",
"👥",
"💬",
"💭",
"☀️",
"☁️",
"❄️",
"",
"",
"🌀",
"🌁",
"🌊",
"🐱",
"🐶",
"🐭",
"🐹",
"🐰",
"🐺",
"🐸",
"🐯",
"🐨",
"🐻",
"🐷",
"🐽",
"🐮",
"🐗",
"🐵",
"🐒",
"🐴",
"🐎",
"🐫",
"🐑",
"🐘",
"🐼",
"🐍",
"🐦",
"🐤",
"🐥",
"🐣",
"🐔",
"🐧",
"🐢",
"🐛",
"🐝",
"🐜",
"🐞",
"🐌",
"🐙",
"🐠",
"🐟",
"🐳",
"🐋",
"🐬",
"🐄",
"🐏",
"🐀",
"🐃",
"🐅",
"🐇",
"🐉",
"🐐",
"🐓",
"🐕",
"🐖",
"🐁",
"🐂",
"🐲",
"🐡",
"🐊",
"🐪",
"🐆",
"🐈",
"🐩",
"🐾",
"💐",
"🌸",
"🌷",
"🍀",
"🌹",
"🌻",
"🌺",
"🍁",
"🍃",
"🍂",
"🌿",
"🍄",
"🌵",
"🌴",
"🌲",
"🌳",
"🌰",
"🌱",
"🌼",
"🌾",
"🐚",
"🌐",
"🌞",
"🌝",
"🌚",
"🌑",
"🌒",
"🌓",
"🌔",
"🌕",
"🌖",
"🌗",
"🌘",
"🌜",
"🌛",
"🌔",
"🌍",
"🌎",
"🌏",
"🌋",
"🌌",
"",
"🎍",
"💝",
"🎎",
"🎒",
"🎓",
"🎏",
"🎆",
"🎇",
"🎐",
"🎑",
"🎃",
"👻",
"🎅",
"🎄",
"🎁",
"🔔",
"🔕",
"🎋",
"🎉",
"🎊",
"🎈",
"🔮",
"💿",
"📀",
"💾",
"📷",
"📹",
"🎥",
"💻",
"📺",
"📱",
"☎️",
"☎️",
"📞",
"📟",
"📠",
"💽",
"📼",
"🔉",
"🔈",
"🔇",
"📢",
"📣",
"",
"",
"",
"",
"📻",
"📡",
"",
"🔍",
"🔎",
"🔓",
"🔒",
"🔏",
"🔐",
"🔑",
"💡",
"🔦",
"🔆",
"🔅",
"🔌",
"🔋",
"📲",
"✉️",
"📫",
"📮",
"🛀",
"🛁",
"🚿",
"🚽",
"🔧",
"🔩",
"🔨",
"💺",
"💰",
"💴",
"💵",
"💷",
"💶",
"💳",
"💸",
"📧",
"📥",
"📤",
"✉️",
"📨",
"📯",
"📪",
"📬",
"📭",
"📦",
"🚪",
"🚬",
"💣",
"🔫",
"🔪",
"💊",
"💉",
"📄",
"📃",
"📑",
"📊",
"📈",
"📉",
"📜",
"📋",
"📆",
"📅",
"📇",
"📁",
"📂",
"✂️",
"📌",
"📎",
"✒️",
"✏️",
"📏",
"📐",
"📕",
"📗",
"📘",
"📙",
"📓",
"📔",
"📒",
"📚",
"🔖",
"📛",
"🔬",
"🔭",
"📰",
"🏈",
"🏀",
"",
"",
"🎾",
"🎱",
"🏉",
"🎳",
"",
"🚵",
"🚴",
"🏇",
"🏂",
"🏊",
"🏄",
"🎿",
"♠️",
"♥️",
"♣️",
"♦️",
"💎",
"💍",
"🏆",
"🎼",
"🎹",
"🎻",
"👾",
"🎮",
"🃏",
"🎴",
"🎲",
"🎯",
"🀄",
"🎬",
"📝",
"📝",
"📖",
"🎨",
"🎤",
"🎧",
"🎺",
"🎷",
"🎸",
"👞",
"👡",
"👠",
"💄",
"👢",
"👕",
"👕",
"👔",
"👚",
"👗",
"🎽",
"👖",
"👘",
"👙",
"🎀",
"🎩",
"👑",
"👒",
"👞",
"🌂",
"💼",
"👜",
"👝",
"👛",
"👓",
"🎣",
"",
"🍵",
"🍶",
"🍼",
"🍺",
"🍻",
"🍸",
"🍹",
"🍷",
"🍴",
"🍕",
"🍔",
"🍟",
"🍗",
"🍖",
"🍝",
"🍛",
"🍤",
"🍱",
"🍣",
"🍥",
"🍙",
"🍘",
"🍚",
"🍜",
"🍲",
"🍢",
"🍡",
"🥚",
"🍞",
"🍩",
"🍮",
"🍦",
"🍨",
"🍧",
"🎂",
"🍰",
"🍪",
"🍫",
"🍬",
"🍭",
"🍯",
"🍎",
"🍏",
"🍊",
"🍋",
"🍒",
"🍇",
"🍉",
"🍓",
"🍑",
"🍈",
"🍌",
"🍐",
"🍍",
"🍠",
"🍆",
"🍅",
"🌽",
"🏠",
"🏡",
"🏫",
"🏢",
"🏣",
"🏥",
"🏦",
"🏪",
"🏩",
"🏨",
"💒",
"",
"🏬",
"🏤",
"🌇",
"🌆",
"🏯",
"🏰",
"",
"🏭",
"🗼",
"🗾",
"🗻",
"🌄",
"🌅",
"🌠",
"🗽",
"🌉",
"🎠",
"🌈",
"🎡",
"",
"🎢",
"🚢",
"🚤",
"",
"",
"🚣",
"",
"🚀",
"✈️",
"🚁",
"🚂",
"🚊",
"🚞",
"🚲",
"🚡",
"🚟",
"🚠",
"🚜",
"🚙",
"🚘",
"🚗",
"🚗",
"🚕",
"🚖",
"🚛",
"🚌",
"🚍",
"🚨",
"🚓",
"🚔",
"🚒",
"🚑",
"🚐",
"🚚",
"🚋",
"🚉",
"🚆",
"🚅",
"🚄",
"🚈",
"🚝",
"🚃",
"🚎",
"🎫",
"",
"🚦",
"🚥",
"⚠️",
"🚧",
"🔰",
"🏧",
"🎰",
"🚏",
"💈",
"♨️",
"🏁",
"🎌",
"🏮",
"🗿",
"🎪",
"🎭",
"📍",
"🚩",
"🇯🇵",
"🇰🇷",
"🇨🇳",
"🇺🇸",
"🇫🇷",
"🇪🇸",
"🇮🇹",
"🇷🇺",
"🇬🇧",
"🇩🇪",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"🔟",
"🔢",
"0",
"#️⃣",
"🔣",
"◀️",
"⬇️",
"▶️",
"⬅️",
"🔠",
"🔡",
"🔤",
"↙️",
"↘️",
"➡️",
"⬆️",
"↖️",
"↗️",
"",
"",
"🔽",
"⤵️",
"⤴️",
"↩️",
"↪️",
"↔️",
"↕️",
"🔼",
"🔃",
"🔄",
"",
"",
"",
"🆗",
"🔀",
"🔁",
"🔂",
"🆕",
"🔝",
"🆙",
"🆒",
"🆓",
"🆖",
"🎦",
"🈁",
"📶",
"🈹",
"🈴",
"🈺",
"🈯",
"🈷️",
"🈶",
"🈵",
"🈚",
"🈸",
"🈳",
"🈲",
"🈂️",
"🚻",
"🚹",
"🚺",
"🚼",
"🚭",
"🅿️",
"",
"🚇",
"🛄",
"🉑",
"🚾",
"🚰",
"🚮",
"㊙️",
"㊗️",
"Ⓜ️",
"🛂",
"🛅",
"🛃",
"🉐",
"🆑",
"🆘",
"🆔",
"🚫",
"🔞",
"📵",
"🚯",
"🚱",
"🚳",
"🚷",
"🚸",
"",
"✳️",
"❇️",
"✴️",
"💟",
"🆚",
"📳",
"📴",
"💹",
"💱",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"🔯",
"",
"🅰️",
"🅱️",
"🆎",
"🅾️",
"💠",
"♻️",
"🔚",
"🔙",
"🔛",
"🔜",
"🕐",
"🕜",
"🕙",
"🕥",
"🕚",
"🕦",
"🕛",
"🕧",
"🕑",
"🕝",
"🕒",
"🕞",
"🕓",
"🕟",
"🕔",
"🕠",
"🕕",
"🕡",
"🕖",
"🕢",
"🕗",
"🕣",
"🕘",
"🕤",
"💲",
"©️",
"®️",
"™️",
"",
"",
"‼️",
"⁉️",
"",
"✖️",
"",
"",
"",
"💮",
"💯",
"✔️",
"☑️",
"🔘",
"🔗",
"",
"〰️",
"〽️",
"🔱",
"▪️",
"▫️",
"",
"",
"◼️",
"◻️",
"",
"",
"",
"🔲",
"🔳",
"",
"",
"🔴",
"🔵",
"🔷",
"🔶",
"🔹",
"🔸",
"🔺",
"🔻"
];

View File

@@ -0,0 +1,36 @@
/////////////////////////////////////////////////////////////////
/*
Mini E-Paper | Ep1. Uploading Emoji to E-Paper from Flutter App(iOS & Android)
Video Tutorial: https://youtu.be/-RhEiKldfDc
Created by Eric N. (ThatProject)
*/
/////////////////////////////////////////////////////////////////
import 'package:epaper_emoji_ble/route/router.dart' as router;
import 'package:epaper_emoji_ble/route/routing_constants.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'const/custom_colors.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Emoji -> E-Paper App',
theme: ThemeData(
textTheme: GoogleFonts.poppinsTextTheme(Theme.of(context).textTheme),
scaffoldBackgroundColor: kMainBG,
primarySwatch: Colors.amber,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
onGenerateRoute: router.generateRoute,
initialRoute: SplashScreenRoute,
);
}
}

View File

@@ -0,0 +1,35 @@
import 'dart:typed_data';
import 'package:epaper_emoji_ble/route/routing_constants.dart';
import 'package:epaper_emoji_ble/screens/ble_list_screen.dart';
import 'package:epaper_emoji_ble/screens/emji_converter_screen.dart';
import 'package:epaper_emoji_ble/screens/emoji_screen.dart';
import 'package:epaper_emoji_ble/screens/splash_screen.dart';
import 'package:epaper_emoji_ble/screens/undefined_screen.dart';
import 'package:flutter/material.dart';
Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case SplashScreenRoute:
return MaterialPageRoute(builder: (context) => const SplashScreen());
case EmojiScreenRoute:
return MaterialPageRoute(builder: (context) => const EmojiScreen());
case EmojiConverterScreenRoute:
return MaterialPageRoute(
builder: (context) => EmojiConverterScreen(
selectedEmoji: settings.arguments as String));
case BLEScreenRoute:
return MaterialPageRoute(
builder: (context) =>
BleListScreen(headedBitmap: settings.arguments as Uint8List));
default:
return MaterialPageRoute(
builder: (context) => UndefinedView(
name: settings.name!,
));
}
}

View File

@@ -0,0 +1,4 @@
const String SplashScreenRoute = '/';
const String EmojiScreenRoute = '/Emoji';
const String EmojiConverterScreenRoute = '/EmojiConverter';
const String BLEScreenRoute = '/BLEList';

View File

@@ -0,0 +1,235 @@
import 'dart:typed_data';
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:epaper_emoji_ble/const/custom_styles.dart';
import 'package:epaper_emoji_ble/widgets/ble_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:quiver/iterables.dart';
import 'package:sn_progress_dialog/progress_dialog.dart';
import 'package:wakelock/wakelock.dart';
class BleListScreen extends StatelessWidget {
final Uint8List headedBitmap;
const BleListScreen({Key? key, required this.headedBitmap}) : super(key: key);
@override
Widget build(BuildContext context) {
return StreamBuilder<BluetoothState>(
stream: FlutterBlue.instance.state,
initialData: BluetoothState.unknown,
builder: (c, snapshot) {
final state = snapshot.data;
if (state == BluetoothState.on) {
return FindDevicesScreen(
headedBitmap: headedBitmap,
);
}
return BluetoothOffScreen(state: state);
});
}
}
class BluetoothOffScreen extends StatelessWidget {
const BluetoothOffScreen({Key? key, this.state}) : super(key: key);
final BluetoothState? state;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.lightBlue,
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Icon(
Icons.bluetooth_disabled,
size: 200.0,
color: Colors.white54,
),
Text(
'Bluetooth Adapter is ${state != null ? state.toString().substring(15) : 'not available'}.',
style: Theme.of(context)
.primaryTextTheme
.headline1
?.copyWith(color: Colors.white),
),
],
),
),
);
}
}
class FindDevicesScreen extends StatelessWidget {
final Uint8List headedBitmap;
const FindDevicesScreen({Key? key, required this.headedBitmap})
: super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Find Devices'),
),
body: RefreshIndicator(
onRefresh: () =>
FlutterBlue.instance.startScan(timeout: const Duration(seconds: 4)),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
StreamBuilder<List<BluetoothDevice>>(
stream: Stream.periodic(const Duration(seconds: 2))
.asyncMap((_) => FlutterBlue.instance.connectedDevices),
initialData: [],
builder: (c, snapshot) => Column(
children: snapshot.data!
.map((d) => ListTile(
title: Text(
d.name,
style: kButtonText.copyWith(color: Colors.white),
),
subtitle: Text(
d.id.toString(),
style:
kSubtitleText.copyWith(color: Colors.white),
),
trailing: StreamBuilder<BluetoothDeviceState>(
stream: d.state,
initialData: BluetoothDeviceState.disconnected,
builder: (c, snapshot) {
if (snapshot.data ==
BluetoothDeviceState.connected) {
return TextButton(
child: Text(
'Send',
style: kButtonText.copyWith(
color: Colors.white),
),
onPressed: () {
_findTargetUUID(context, d);
});
}
return Text(snapshot.data.toString());
},
),
))
.toList(),
),
),
StreamBuilder<List<ScanResult>>(
stream: FlutterBlue.instance.scanResults,
initialData: [],
builder: (c, snapshot) => Column(
children: snapshot.data!
.map(
(r) => ScanResultTile(
result: r,
onTap: () {
r.device.connect();
},
),
)
.toList(),
),
),
],
),
),
),
floatingActionButton: StreamBuilder<bool>(
stream: FlutterBlue.instance.isScanning,
initialData: false,
builder: (c, snapshot) {
if (snapshot.data!) {
return FloatingActionButton(
child: const Icon(Icons.stop),
onPressed: () => FlutterBlue.instance.stopScan(),
backgroundColor: Colors.red,
);
} else {
return FloatingActionButton(
child: const Icon(Icons.search),
onPressed: () => FlutterBlue.instance
.startScan(timeout: const Duration(seconds: 4)));
}
},
),
);
}
final String SERVICE_UUID = "713d0001-503e-4c75-ba94-3148f18d941e";
final String CHARACTERISTIC_UUID = "713d0002-503e-4c75-ba94-3148f18d941e";
_findTargetUUID(BuildContext context, BluetoothDevice device) async {
print("device :${device.id}");
try {
List<BluetoothService> services = await device.discoverServices();
for (var service in services) {
print('service!!! : ${service}');
print('service last uuid!!! : ${service.uuid.toString()}');
if (service.uuid.toString() == SERVICE_UUID) {
for (var characteristic in service.characteristics) {
if (characteristic.uuid.toString() == CHARACTERISTIC_UUID) {
print(
'characteristic uuid!!! : ${characteristic.uuid.toString()}');
_sendData(context, characteristic);
// for (var pair in pairs) {
// print(pair);
// await characteristic.write(pair, withoutResponse: false);
//
// print('SENT!');
// }
}
}
}
}
} catch (e) {
await showOkAlertDialog(
context: context,
title: 'Error',
message: e.toString(),
);
}
}
_sendData(
BuildContext context, BluetoothCharacteristic characteristic) async {
Wakelock.enable();
print('headedBitmap length: ${headedBitmap.length}');
var pairs = partition(headedBitmap, 20);
var startSignal = await characteristic.write([], withoutResponse: false);
print('startSignal: $startSignal');
ProgressDialog pd = ProgressDialog(context: context);
pd.show(
max: pairs.length,
msg: 'Preparing Upload...',
progressType: ProgressType.valuable,
backgroundColor: const Color(0xff212121),
progressValueColor: Colors.amberAccent,
progressBgColor: Colors.white70,
msgColor: Colors.white,
valueColor: Colors.white);
int i = 0;
for (var pair in pairs) {
++i;
print(pair);
pd.update(value: i, msg: 'Bitmap Uploading...');
await characteristic.write(pair, withoutResponse: false);
}
var endSignal = await characteristic.write([], withoutResponse: false);
print('endSignal: $endSignal');
Wakelock.disable();
}
}

View File

@@ -0,0 +1,166 @@
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:bitmap/bitmap.dart';
import 'package:epaper_emoji_ble/const/custom_styles.dart';
import 'package:epaper_emoji_ble/route/routing_constants.dart';
import 'package:epaper_emoji_ble/util/bitmap24.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:image/image.dart' as img;
class EmojiConverterScreen extends StatefulWidget {
final String selectedEmoji;
const EmojiConverterScreen({Key? key, required this.selectedEmoji})
: super(key: key);
@override
_EmojiConverterScreenState createState() => _EmojiConverterScreenState();
}
class _EmojiConverterScreenState extends State<EmojiConverterScreen> {
GlobalKey globalKey = GlobalKey();
Uint8List? bitmap24WithHeader;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Emoji Converter'),
),
body: ListView(children: [
Center(
child: Column(
children: [
const SizedBox(
height: 40,
),
RepaintBoundary(
key: globalKey,
child: Container(
color: Colors.transparent,
width: 200,
height: 200,
child: Center(
child: Text(
widget.selectedEmoji,
style: const TextStyle(fontSize: 160),
)),
),
),
const SizedBox(
height: 40,
),
bitmap24WithHeader == null
? Container(
height: 60,
width: MediaQuery.of(context).size.width * 0.8,
decoration: BoxDecoration(
color: Colors.grey[850],
borderRadius: BorderRadius.circular(18),
),
child: TextButton(
style: ButtonStyle(
overlayColor: MaterialStateProperty.resolveWith(
(states) => Colors.black12,
),
),
onPressed: _convertImage,
child: Text(
'Convert to Bitmap',
style: kButtonText.copyWith(color: Colors.white),
),
))
: Container(
height: 100,
width: 100,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(18),
),
child: Image.memory(
bitmap24WithHeader!,
)),
const SizedBox(
height: 40,
),
bitmap24WithHeader != null
? Container(
height: 60,
width: MediaQuery.of(context).size.width * 0.8,
decoration: BoxDecoration(
color: Colors.grey[850],
borderRadius: BorderRadius.circular(18),
),
child: TextButton(
style: ButtonStyle(
overlayColor: MaterialStateProperty.resolveWith(
(states) => Colors.black12,
),
),
onPressed: _findBLEDevice,
child: Text(
'Find & Select Your Mini E-Paper',
style: kButtonText.copyWith(color: Colors.white),
),
))
: Container()
],
),
),
]),
);
}
_convertImage() async {
final RenderRepaintBoundary boundary =
globalKey.currentContext!.findRenderObject()! as RenderRepaintBoundary;
final ui.Image image = await boundary.toImage();
final ByteData? byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
final Uint8List pngBytes = byteData!.buffer.asUint8List();
var pngImage;
try {
pngImage = img.decodePng(pngBytes);
} catch (e) {
await showOkAlertDialog(
context: context,
title: 'Error',
message: e.toString(),
);
return;
}
var resizedImage = img.copyResize(pngImage, width: 80, height: 80);
var grayImage = img.grayscale(resizedImage);
var imgThreeColor = img.quantize(grayImage, numberOfColors: 3);
var flipImage = img.flip(imgThreeColor, img.Flip.vertical);
Bitmap bitmap = Bitmap.fromHeadless(
resizedImage.width, resizedImage.height, flipImage.getBytes());
Uint8List bitmap24Data = Bitmap24.fromBitMap32(
resizedImage.width, resizedImage.height, bitmap.content);
Bitmap24 bitmap24 = Bitmap24.fromHeadless(
resizedImage.width, resizedImage.height, bitmap24Data);
setState(() {
bitmap24WithHeader = bitmap24.buildHeaded();
});
}
_findBLEDevice() async {
if (bitmap24WithHeader == null) {
await showOkAlertDialog(
context: context,
title: 'Error',
message: 'Check the Bitmap Emoji',
);
return;
}
Navigator.pushNamed(context, BLEScreenRoute, arguments: bitmap24WithHeader);
}
}

View File

@@ -0,0 +1,51 @@
import 'package:epaper_emoji_ble/const/emoji_data.dart';
import 'package:epaper_emoji_ble/route/routing_constants.dart';
import 'package:flutter/material.dart';
class EmojiScreen extends StatelessWidget {
const EmojiScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.only(top: 40, bottom: 10),
child: Center(child: emojiGridView(context)),
),
);
}
Widget emojiGridView(BuildContext context) {
return MediaQuery.removePadding(
context: context,
removeTop: true,
child: GridView.builder(
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.all(8),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: MediaQuery.of(context).size.width >
MediaQuery.of(context).size.height
? 10
: 5,
),
itemCount: emojiArray.length,
itemBuilder: (BuildContext context, int index) {
return Card(
color: Colors.amber,
child: InkWell(
onTap: () {
Navigator.pushNamed(context, EmojiConverterScreenRoute,
arguments: emojiArray[index]);
},
child: Center(
child: Text(
emojiArray[index],
style: const TextStyle(fontSize: 30),
),
),
),
);
}),
);
}
}

View File

@@ -0,0 +1,87 @@
import 'package:epaper_emoji_ble/const/custom_styles.dart';
import 'package:epaper_emoji_ble/route/routing_constants.dart';
import 'package:flutter/material.dart';
class SplashScreen extends StatelessWidget {
const SplashScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: [
_getScreen(context),
],
),
);
}
_getScreen(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 20, right: 20, top: 40, bottom: 10),
child: Column(
children: [
Column(
children: [
Center(
child: Container(
child: const Image(
image: AssetImage(
'assets/images/iot_image.png',
),
height: 200,
color: Colors.white,
),
),
),
const SizedBox(
height: 20,
),
const Text(
"E-Paper\n^\n|\nBLE\n|\nEmoji😃",
style: kHeadline,
textAlign: TextAlign.center,
),
const SizedBox(
height: 20,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.8,
child: const Text(
"It's a project that allows you to send emojis to Mini E-Paper. For detail, check out my channel. www.that-project.com",
style: kBodyText,
textAlign: TextAlign.center,
),
)
],
),
const SizedBox(
height: 50,
),
Container(
height: 60,
width: MediaQuery.of(context).size.width * 0.8,
decoration: BoxDecoration(
color: Colors.grey[850],
borderRadius: BorderRadius.circular(18),
),
child: TextButton(
style: ButtonStyle(
overlayColor: MaterialStateProperty.resolveWith(
(states) => Colors.black12,
),
),
onPressed: () {
Navigator.pushNamedAndRemoveUntil(context, EmojiScreenRoute,
(Route<dynamic> route) => false);
},
child: Text(
'GET STARTED',
style: kButtonText.copyWith(color: Colors.white),
),
))
],
),
);
}
}

View File

@@ -0,0 +1,16 @@
import 'package:flutter/material.dart';
class UndefinedView extends StatelessWidget {
final String name;
const UndefinedView({Key? key, required this.name}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('Route for $name is not defined'),
),
);
}
}

View File

@@ -0,0 +1,88 @@
import 'dart:typed_data';
class Bitmap24 {
/// The width in pixels of the image.
final int width;
/// The width in pixels of the image.
final int height;
/// A [Uint8List] of bytes in a RGB format.
final Uint8List content;
int get size => (width * height) * RGB24BitmapHeader.pixelLength;
Bitmap24.fromHeadless(this.width, this.height, this.content);
Bitmap24.fromHeadful(this.width, this.height, Uint8List headedIntList)
: content = headedIntList.sublist(
RGB24BitmapHeader.RGB24HeaderSize,
headedIntList.length,
);
Bitmap24.blank(
this.width,
this.height,
) : content = Uint8List.fromList(
List.filled(width * height * RGB24BitmapHeader.pixelLength, 0),
);
Uint8List buildHeaded() {
final header = RGB24BitmapHeader(size, width, height)
..applyContent(content);
return header.headerIntList;
}
static Uint8List fromBitMap32(int width, int height, Uint8List bitmap32) {
Uint8List R8G8B8 = Uint8List(width * height * 3);
int j = 0;
for (int i = 0; i < (bitmap32.length - 3); i = i + 4) {
var Red = bitmap32[i + 2];
var Green = bitmap32[i + 1];
var Blue = bitmap32[i];
R8G8B8[j] = Red;
R8G8B8[j + 1] = Green;
R8G8B8[j + 2] = Blue;
j = j + 3;
}
return R8G8B8;
}
}
class RGB24BitmapHeader {
static const int pixelLength = 3;
static const int RGB24HeaderSize = 54;
RGB24BitmapHeader(this.contentSize, int width, int height) {
headerIntList = Uint8List(fileLength);
final ByteData bd = headerIntList.buffer.asByteData();
bd.setUint8(0x0, 0x42);
bd.setUint8(0x1, 0x4d);
bd.setInt32(0x2, fileLength, Endian.little);
bd.setInt32(0xa, RGB24HeaderSize, Endian.little);
bd.setUint32(0xe, 28, Endian.little);
bd.setUint32(0x12, width, Endian.little);
bd.setUint32(0x16, height, Endian.little);
bd.setUint16(0x1a, 1, Endian.little);
bd.setUint32(0x1c, 24, Endian.little); // pixel size
bd.setUint32(0x1e, 0, Endian.little); //BI_BITFIELDS
bd.setUint32(0x22, contentSize, Endian.little);
}
int contentSize;
void applyContent(Uint8List contentIntList) {
headerIntList.setRange(
RGB24HeaderSize,
fileLength,
contentIntList,
);
}
late Uint8List headerIntList;
int get fileLength => contentSize + RGB24HeaderSize;
}

View File

@@ -0,0 +1,314 @@
// Copyright 2017, Paul DeMarco.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:epaper_emoji_ble/const/custom_styles.dart';
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
class ScanResultTile extends StatelessWidget {
const ScanResultTile({Key? key, required this.result, this.onTap})
: super(key: key);
final ScanResult result;
final VoidCallback? onTap;
Widget _buildTitle(BuildContext context) {
if (result.device.name.isNotEmpty) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
result.device.name,
overflow: TextOverflow.ellipsis,
style: kButtonText.copyWith(color: Colors.white),
),
Text(
result.device.id.toString(),
style: kSubtitleText.copyWith(color: Colors.white),
)
],
);
} else {
return Text(result.device.id.toString(),
style: kSubtitleText.copyWith(color: Colors.white));
}
}
Widget _buildAdvRow(BuildContext context, String title, String value) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
title,
style: kSubtitleText.copyWith(color: Colors.white),
),
const SizedBox(
width: 12.0,
),
Expanded(
child: Text(
value,
style: kSubtitleText.copyWith(color: Colors.white),
softWrap: true,
),
),
],
),
);
}
String getNiceHexArray(List<int> bytes) {
return '[${bytes.map((i) => i.toRadixString(16).padLeft(2, '0')).join(', ')}]'
.toUpperCase();
}
String getNiceManufacturerData(Map<int, List<int>> data) {
if (data.isEmpty) {
return 'N/A';
}
List<String> res = [];
data.forEach((id, bytes) {
res.add(
'${id.toRadixString(16).toUpperCase()}: ${getNiceHexArray(bytes)}');
});
return res.join(', ');
}
String getNiceServiceData(Map<String, List<int>> data) {
if (data.isEmpty) {
return 'N/A';
}
List<String> res = [];
data.forEach((id, bytes) {
res.add('${id.toUpperCase()}: ${getNiceHexArray(bytes)}');
});
return res.join(', ');
}
@override
Widget build(BuildContext context) {
return ExpansionTile(
title: _buildTitle(context),
leading: Text(
result.rssi.toString(),
style: kButtonText.copyWith(color: Colors.white),
),
trailing: TextButton(
style: ButtonStyle(
overlayColor: MaterialStateProperty.resolveWith(
(states) => Colors.black12,
),
),
child: Text(
'CONNECT',
style: kButtonText.copyWith(color: Colors.white),
),
onPressed: (result.advertisementData.connectable) ? onTap : null,
),
children: <Widget>[
_buildAdvRow(
context, 'Complete Local Name', result.advertisementData.localName),
_buildAdvRow(context, 'Tx Power Level',
'${result.advertisementData.txPowerLevel ?? 'N/A'}'),
_buildAdvRow(context, 'Manufacturer Data',
getNiceManufacturerData(result.advertisementData.manufacturerData)),
_buildAdvRow(
context,
'Service UUIDs',
(result.advertisementData.serviceUuids.isNotEmpty)
? result.advertisementData.serviceUuids.join(', ').toUpperCase()
: 'N/A'),
_buildAdvRow(context, 'Service Data',
getNiceServiceData(result.advertisementData.serviceData)),
],
);
}
}
class ServiceTile extends StatelessWidget {
final BluetoothService service;
final List<CharacteristicTile> characteristicTiles;
const ServiceTile(
{Key? key, required this.service, required this.characteristicTiles})
: super(key: key);
@override
Widget build(BuildContext context) {
if (characteristicTiles.isNotEmpty) {
return ExpansionTile(
title: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text('Service'),
Text(
'0x${service.uuid.toString().toUpperCase().substring(4, 8)}',
style: kSubtitleText.copyWith(color: Colors.white),
)
],
),
children: characteristicTiles,
);
} else {
return ListTile(
title: const Text('Service'),
subtitle:
Text('0x${service.uuid.toString().toUpperCase().substring(4, 8)}'),
);
}
}
}
class CharacteristicTile extends StatelessWidget {
final BluetoothCharacteristic characteristic;
final List<DescriptorTile> descriptorTiles;
final VoidCallback? onReadPressed;
final VoidCallback? onWritePressed;
final VoidCallback? onNotificationPressed;
const CharacteristicTile(
{Key? key,
required this.characteristic,
required this.descriptorTiles,
this.onReadPressed,
this.onWritePressed,
this.onNotificationPressed})
: super(key: key);
@override
Widget build(BuildContext context) {
return StreamBuilder<List<int>>(
stream: characteristic.value,
initialData: characteristic.lastValue,
builder: (c, snapshot) {
final value = snapshot.data;
return ExpansionTile(
title: ListTile(
title: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text('Characteristic'),
Text(
'0x${characteristic.uuid.toString().toUpperCase().substring(4, 8)}',
style: Theme.of(context).textTheme.bodyText1?.copyWith(
color: Theme.of(context).textTheme.caption?.color))
],
),
subtitle: Text(value.toString()),
contentPadding: const EdgeInsets.all(0.0),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(
Icons.file_download,
color: Theme.of(context).iconTheme.color?.withOpacity(0.5),
),
onPressed: onReadPressed,
),
IconButton(
icon: Icon(Icons.file_upload,
color: Theme.of(context).iconTheme.color?.withOpacity(0.5)),
onPressed: onWritePressed,
),
IconButton(
icon: Icon(
characteristic.isNotifying
? Icons.sync_disabled
: Icons.sync,
color: Theme.of(context).iconTheme.color?.withOpacity(0.5)),
onPressed: onNotificationPressed,
)
],
),
children: descriptorTiles,
);
},
);
}
}
class DescriptorTile extends StatelessWidget {
final BluetoothDescriptor descriptor;
final VoidCallback? onReadPressed;
final VoidCallback? onWritePressed;
const DescriptorTile(
{Key? key,
required this.descriptor,
this.onReadPressed,
this.onWritePressed})
: super(key: key);
@override
Widget build(BuildContext context) {
return ListTile(
title: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text('Descriptor'),
Text('0x${descriptor.uuid.toString().toUpperCase().substring(4, 8)}',
style: Theme.of(context)
.textTheme
.bodyText1
?.copyWith(color: Theme.of(context).textTheme.caption?.color))
],
),
subtitle: StreamBuilder<List<int>>(
stream: descriptor.value,
initialData: descriptor.lastValue,
builder: (c, snapshot) => Text(snapshot.data.toString()),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(
Icons.file_download,
color: Theme.of(context).iconTheme.color?.withOpacity(0.5),
),
onPressed: onReadPressed,
),
IconButton(
icon: Icon(
Icons.file_upload,
color: Theme.of(context).iconTheme.color?.withOpacity(0.5),
),
onPressed: onWritePressed,
)
],
),
);
}
}
class AdapterStateTile extends StatelessWidget {
const AdapterStateTile({Key? key, required this.state}) : super(key: key);
final BluetoothState state;
@override
Widget build(BuildContext context) {
return Container(
color: Colors.redAccent,
child: ListTile(
title: Text(
'Bluetooth adapter is ${state.toString().substring(15)}',
style: Theme.of(context).primaryTextTheme.headline1,
),
trailing: Icon(
Icons.error,
color: Theme.of(context).primaryTextTheme.subtitle1?.color,
),
),
);
}
}

View File

@@ -0,0 +1,439 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
adaptive_dialog:
dependency: "direct main"
description:
name: adaptive_dialog
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
animations:
dependency: transitive
description:
name: animations
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
archive:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.2"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.1"
bitmap:
dependency: "direct main"
description:
name: bitmap
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
ffi:
dependency: transitive
description:
name: ffi
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.2"
file:
dependency: transitive
description:
name: file
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.2"
fixnum:
dependency: transitive
description:
name: fixnum
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_blue:
dependency: "direct main"
description:
name: flutter_blue
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
font_awesome_flutter:
dependency: "direct main"
description:
name: font_awesome_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "9.1.0"
google_fonts:
dependency: "direct main"
description:
name: google_fonts
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
http:
dependency: transitive
description:
name: http
url: "https://pub.dartlang.org"
source: hosted
version: "0.13.3"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
image:
dependency: "direct main"
description:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3"
lints:
dependency: transitive
description:
name: lints
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
path_provider:
dependency: transitive
description:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
path_provider_macos:
dependency: transitive
description:
name: path_provider_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.11.1"
petitparser:
dependency: transitive
description:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "4.2.0"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
process:
dependency: transitive
description:
name: process
url: "https://pub.dartlang.org"
source: hosted
version: "4.2.3"
protobuf:
dependency: transitive
description:
name: protobuf
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
quiver:
dependency: "direct main"
description:
name: quiver
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
rxdart:
dependency: transitive
description:
name: rxdart
url: "https://pub.dartlang.org"
source: hosted
version: "0.26.0"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
sn_progress_dialog:
dependency: "direct main"
description:
name: sn_progress_dialog
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.2"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
wakelock:
dependency: "direct main"
description:
name: wakelock
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.3+3"
wakelock_macos:
dependency: transitive
description:
name: wakelock_macos
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.0+2"
wakelock_platform_interface:
dependency: transitive
description:
name: wakelock_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.1+2"
wakelock_web:
dependency: transitive
description:
name: wakelock_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0+2"
wakelock_windows:
dependency: transitive
description:
name: wakelock_windows
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.0+1"
win32:
dependency: transitive
description:
name: win32
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.9"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
xml:
dependency: transitive
description:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "5.2.0"
sdks:
dart: ">=2.13.0 <3.0.0"
flutter: ">=2.0.0"

View File

@@ -0,0 +1,101 @@
name: epaper_emoji_ble
description: App allows to send the emoji you choose to your e-paper via ble
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1
environment:
sdk: ">=2.12.0 <3.0.0"
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
google_fonts: ^2.1.0
adaptive_dialog: ^1.1.0
font_awesome_flutter: ^9.1.0
bitmap: ^0.1.2
image: ^3.0.2
flutter_blue: ^0.8.0
quiver: ^3.0.1
sn_progress_dialog: ^1.0.3
wakelock: ^0.5.3+3
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^1.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
assets:
- assets/images/
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages