Skip to main content

Peripheral mode (UART)

What this example shows​

Use Inkplate as a display for another device. With this sketch you do not write any drawing code: you send short text commands over the serial port, and Inkplate draws shapes and text, shows images from the microSD card and reports the battery voltage. Because the commands are plain text over UART, any platform that can write to a serial port can control the screen.

The sketch's comments state the settings: 115200 baud, standard parity, each command ending with both \n and \r. You can send the commands over the USB port, or by connecting directly to the ESP32's TX and RX pins. The sketch also says that, because UART is slow, drawing many single pixels is not recommended; load bitmaps and pictures onto the microSD card and draw them from there instead. Every Inkplate comes with this mode from the factory.

Command format​

Each command is #, a command character, the arguments in round brackets, and * at the end, for example #8(10,10,100,50,0)*. Numbers are written in decimal and separated by commas. Nothing is shown on the screen until you send #L(1)* after the drawing commands, which is the same as calling display().

CommandArgumentsFunction it calls
#?(…)*noneReplies OK
#0(x,y,c)*position, colordrawPixel
#1(x1,y1,x2,y2,c)*two end points, colordrawLine
#2(x,y,l,c)*start, length, colordrawFastVLine
#3(x,y,l,c)*start, length, colordrawFastHLine
#4(x,y,w,h,c)*top-left corner, size, colordrawRect
#5(x,y,r,c)*center, radius, colordrawCircle
#6(x1,y1,x2,y2,x3,y3,c)*three corners, colordrawTriangle
#7(x,y,w,h,r,c)*top-left corner, size, corner radius, colordrawRoundRect
#8(x,y,w,h,c)*top-left corner, size, colorfillRect
#9(x,y,r,c)*center, radius, colorfillCircle
#A(x1,y1,x2,y2,x3,y3,c)*three corners, colorfillTriangle
#B(x,y,w,h,r,c)*top-left corner, size, corner radius, colorfillRoundRect
#C("hex")*text as hexadecimal byte pairs in quotesprint
#D(s)*text sizesetTextSize
#E(x,y)*cursor positionsetCursor
#F(T)* or #F(F)*T for wrapping on, F for offsetTextWrap
#G(r)*rotation, only the lowest two bits are usedsetRotation
#H(x,y,"hex")*position, file name as hexadecimal byte pairs in quotesimage.drawBitmapFromSd
#K(1)*1clearDisplay
#L(1)*1display
#M(a,b,c)*three numbers that are read and not useddisplay
#P(?)*?Replies #P( battery voltage )* from readBattery
#S(x,y,"hex")*position, file name as hexadecimal byte pairs in quotesimage.draw
#T(x1,y1,x2,y2,c,t)*two end points, color, thicknessdrawThickLine
#U(rx,ry,xc,yc,c)*radii, center, colordrawElipse
#V(rx,ry,xc,yc,c)*radii, center, colorfillElipse

Text and file names are sent as hexadecimal pairs: each pair of digits becomes one character, so Hi is 4869. For #H and #S the sketch initializes the microSD card, draws the file, and answers #H( followed by the result and )*; when the card cannot be initialized it answers #H(-1)*. The #S command also answers with #H(, as the code writes it.

Example code​

Select Board
Inkplate7SPECTRA_Peripheral_Mode.ino
#include <Inkplate.h>
Inkplate inkplate;

#define BUFFER_SIZE 1000
char commandBuffer[BUFFER_SIZE + 1];
char strTemp[2001];

void setup()
{
inkplate.begin();
Serial.begin(115200);
memset(commandBuffer, 0, BUFFER_SIZE);
}

void loop()
{
// put your main code here, to run repeatedly:
if (Serial.available())
{
while (Serial.available())
{
for (int i = 0; i < (BUFFER_SIZE - 1); i++)
{
commandBuffer[i] = commandBuffer[i + 1];
}
commandBuffer[BUFFER_SIZE - 1] = Serial.read();
}
}
char *s = NULL;
char *e = NULL;
for (int i = 0; i < BUFFER_SIZE; i++)
{
if (commandBuffer[i] == '#' && s == NULL)
s = &commandBuffer[i];
if (commandBuffer[i] == '*' && e == NULL)
e = &commandBuffer[i];
}
if (s != NULL && e != NULL)
{
if ((e - s) > 0)
{
int x, x1, x2, y, y1, y2, x3, y3, l, c, w, h, r, n, rx, ry, xc, yc;
char b;
char temp[150];
switch (*(s + 1))
{
case '?':
Serial.print("OK");
break;

case '0':
sscanf(s + 3, "%d,%d,%d", &x, &y, &c);
inkplate.drawPixel(x, y, c);
break;

case '1':
sscanf(s + 3, "%d,%d,%d,%d,%d", &x1, &y1, &x2, &y2, &c);
inkplate.drawLine(x1, y1, x2, y2, c);
break;

case '2':
sscanf(s + 3, "%d,%d,%d,%d", &x, &y, &l, &c);
inkplate.drawFastVLine(x, y, l, c);
break;

case '3':
sscanf(s + 3, "%d,%d,%d,%d", &x, &y, &l, &c);
inkplate.drawFastHLine(x, y, l, c);
break;

case '4':
sscanf(s + 3, "%d,%d,%d,%d,%d", &x, &y, &w, &h, &c);
inkplate.drawRect(x, y, w, h, c);
break;

case '5':
sscanf(s + 3, "%d,%d,%d,%d", &x, &y, &r, &c);
inkplate.drawCircle(x, y, r, c);
break;

case '6':
sscanf(s + 3, "%d,%d,%d,%d,%d,%d,%d", &x1, &y1, &x2, &y2, &x3, &y3, &c);
inkplate.drawTriangle(x1, y1, x2, y2, x3, y3, c);
break;

case '7':
sscanf(s + 3, "%d,%d,%d,%d,%d,%d", &x, &y, &w, &h, &r, &c);
inkplate.drawRoundRect(x, y, w, h, r, c);
break;

case '8':
sscanf(s + 3, "%d,%d,%d,%d,%d", &x, &y, &w, &h, &c);
inkplate.fillRect(x, y, w, h, c);
break;

case '9':
sscanf(s + 3, "%d,%d,%d,%d", &x, &y, &r, &c);
inkplate.fillCircle(x, y, r, c);
break;

case 'A':
sscanf(s + 3, "%d,%d,%d,%d,%d,%d,%d", &x1, &y1, &x2, &y2, &x3, &y3, &c);
inkplate.fillTriangle(x1, y1, x2, y2, x3, y3, c);
break;

case 'B':
sscanf(s + 3, "%d,%d,%d,%d,%d,%d", &x, &y, &w, &h, &r, &c);
inkplate.fillRoundRect(x, y, w, h, r, c);
break;

case 'C':
sscanf(s + 3, "\"%2000[^\"]\"", strTemp);
n = strlen(strTemp);
for (int i = 0; i < n; i++)
{
strTemp[i] = toupper(strTemp[i]);
}
for (int i = 0; i < n; i += 2)
{
strTemp[i / 2] = (hexToChar(strTemp[i]) << 4) | (hexToChar(strTemp[i + 1]) & 0x0F);
}
strTemp[n / 2] = 0;
inkplate.print(strTemp);
break;

case 'D':
sscanf(s + 3, "%d", &c);
inkplate.setTextSize(c);
break;

case 'E':
sscanf(s + 3, "%d,%d", &x, &y);
inkplate.setCursor(x, y);
break;

case 'F':
sscanf(s + 3, "%c", &b);
if (b == 'T')
inkplate.setTextWrap(true);
if (b == 'F')
inkplate.setTextWrap(false);
break;

case 'G':
sscanf(s + 3, "%d", &c);
c &= 3;
inkplate.setRotation(c);
break;

case 'H':
sscanf(s + 3, "%d,%d,\"%149[^\"]\"", &x, &y, strTemp);
n = strlen(strTemp);
for (int i = 0; i < n; i++)
{
strTemp[i] = toupper(strTemp[i]);
}
for (int i = 0; i < n; i += 2)
{
strTemp[i / 2] = (hexToChar(strTemp[i]) << 4) | (hexToChar(strTemp[i + 1]) & 0x0F);
}
strTemp[n / 2] = 0;
r = inkplate.sdCardInit();
if (r)
{
r = inkplate.image.drawBitmapFromSd(strTemp, x, y);
Serial.print("#H(");
Serial.print(r, DEC);
Serial.println(")*");
Serial.flush();
}
else
{
Serial.println("#H(-1)*");
Serial.flush();
}
break;

case 'K':
sscanf(s + 3, "%c", &b);
if (b == '1')
{
inkplate.clearDisplay();
}
break;

case 'L':
sscanf(s + 3, "%c", &b);
if (b == '1')
{
inkplate.display();
}
break;

case 'M':
sscanf(s + 3, "%d,%d,%d", &y1, &x2, &y2);
inkplate.display();
break;

case 'P':
sscanf(s + 3, "%c", &b);
if (b == '?')
{
Serial.print("#P(");
Serial.print(inkplate.readBattery(), 2);
Serial.println(")*");
Serial.flush();
}
break;

case 'S':
sscanf(s + 3, "%d,%d,\"%149[^\"]\"", &x, &y, strTemp);
n = strlen(strTemp);
for (int i = 0; i < n; i++)
{
strTemp[i] = toupper(strTemp[i]);
}
for (int i = 0; i < n; i += 2)
{
strTemp[i / 2] = (hexToChar(strTemp[i]) << 4) | (hexToChar(strTemp[i + 1]) & 0x0F);
}
strTemp[n / 2] = 0;
r = inkplate.sdCardInit();
if (r)
{
r = inkplate.image.draw(strTemp, x, y);
Serial.print("#H(");
Serial.print(r, DEC);
Serial.println(")*");
Serial.flush();
}
else
{
Serial.println("#H(-1)*");
Serial.flush();
}
break;
case 'T':
int t;
sscanf(s + 3, "%d,%d,%d,%d,%d,%d", &x1, &y1, &x2, &y2, &c, &t);
inkplate.drawThickLine(x1, y1, x2, y2, c, t);
break;
case 'U':
sscanf(s + 3, "%d,%d,%d,%d,%d", &rx, &ry, &xc, &yc, &c);
inkplate.drawElipse(rx, ry, xc, yc, c);
break;
case 'V':
sscanf(s + 3, "%d,%d,%d,%d,%d", &rx, &ry, &xc, &yc, &c);
inkplate.fillElipse(rx, ry, xc, yc, c);
break;
}
*s = 0;
*e = 0;
}
}
}

int hexToChar(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
return -1;
}

Functions used​

Inkplate()returns None

Creates the Inkplate object. Inkplate 7SPECTRA has a single six-color mode, so the constructor takes no mode argument.

ReturnsConstructor; no return value.

inkplate.begin()returns void

Initializes the library, the peripherals and the frame buffer. Call it once in setup().

ReturnsNothing.

inkplate.drawPixel(int16_t x, int16_t y, uint16_t color)returns void

Sets one pixel in the frame buffer. Command 0.

ReturnsNothing.

inkplate.drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color)returns void

Draws a line between two points. Command 1. From the Adafruit GFX class the library builds on.

ReturnsNothing.

inkplate.drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color)returns void

Draws a vertical line of the given length. Command 2.

ReturnsNothing.

inkplate.drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color)returns void

Draws a horizontal line of the given length. Command 3.

ReturnsNothing.

inkplate.drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)returns void

Draws the outline of a rectangle. Command 4.

ReturnsNothing.

inkplate.drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color)returns void

Draws the outline of a circle. Command 5.

ReturnsNothing.

inkplate.drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color)returns void

Draws the outline of a triangle. Command 6.

ReturnsNothing.

inkplate.drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color)returns void

Draws the outline of a rectangle with rounded corners. Command 7.

ReturnsNothing.

inkplate.fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)returns void

Draws a filled rectangle with its top-left corner at (x, y). Command 8.

ReturnsNothing.

inkplate.fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color)returns void

Draws a filled circle. Command 9.

ReturnsNothing.

inkplate.fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color)returns void

Draws a filled triangle. Command A.

ReturnsNothing.

inkplate.fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color)returns void

Draws a filled rectangle with rounded corners. Command B.

ReturnsNothing.

inkplate.print(text)returns size_t

Prints the decoded text into the frame buffer at the cursor, like Serial.print(). Command C.

ReturnsNumber of bytes printed.

inkplate.setTextSize(uint8_t s)returns void

Sets the text magnification. Command D.

ReturnsNothing.

inkplate.setCursor(int16_t x, int16_t y)returns void

Sets where the next printed text starts. Command E.

ReturnsNothing.

inkplate.setTextWrap(bool w)returns void

Chooses whether text that does not fit on a line continues on the next line. Command F.

ReturnsNothing.

inkplate.setRotation(uint8_t r)returns void

Sets the screen rotation. The sketch passes only the lowest two bits of the number it receives. Command G.

ReturnsNothing.

inkplate.sdCardInit()returns int16_t

Switches on the microSD card's supply, starts its SPI bus and mounts the card. Commands H and S call it before they read a file.

Returns1 if the card was mounted, 0 if it was not.

inkplate.image.drawBitmapFromSd(const char *fileName, int x, int y, bool dither = 0, bool invert = 0)returns bool

Opens a bitmap file on the microSD card by name and draws it into the frame buffer with its top-left corner at (x, y). Command H. The sketch sends the result back, 1 on success.

Returnstrue if the bitmap was drawn.

inkplate.image.draw(const char *path, int x, int y, bool dither = 1, bool invert = 0)returns bool

Draws an image file from the microSD card at x, y; the library picks the decoder from the file extension (bmp, dib, jpg, jpeg or png). Command S.

Returnstrue if the image was drawn.

inkplate.clearDisplay()returns void

Fills the frame buffer with white. It does not change the panel. Command K.

ReturnsNothing.

inkplate.display(bool _leaveOn = 0)returns void

Sends the whole frame buffer to the panel and refreshes it. Commands L and M.

ReturnsNothing.

inkplate.readBattery()returns double

Measures the battery voltage. The sketch sends it back with two decimals. Command P.

ReturnsThe battery voltage in volts.

inkplate.drawThickLine(int x1, int y1, int x2, int y2, int color, float thickness)returns void

Draws a line of the given thickness between two points. Command T.

ReturnsNothing.

inkplate.drawElipse(int rx, int ry, int xc, int yc, int c)returns void

Draws the outline of an ellipse with radii rx and ry around the center (xc, yc). The name is spelled this way in the library. Command U.

ReturnsNothing.

inkplate.fillElipse(int rx, int ry, int xc, int yc, int c)returns void

Draws a filled ellipse with radii rx and ry around the center (xc, yc). Command V.

ReturnsNothing.

Serial.available()returns int

Arduino core: the number of bytes waiting in the serial receive buffer. The sketch reads them all into its command buffer.

ReturnsNumber of bytes.

Serial.read()returns int

Arduino core: reads one byte from the serial port.

ReturnsThe byte, or -1 if none is available.

Serial.print(value, format)returns size_t

Arduino core: writes a value to the serial port. The sketch uses it for the replies. DEC prints a number in decimal.

ReturnsNumber of bytes written.

Serial.println(value)returns size_t

Arduino core: like Serial.print(), followed by a line break.

ReturnsNumber of bytes written.

Serial.flush()returns void

Arduino core: waits until everything written has been sent. The sketch calls it after each reply.

ReturnsNothing.

What to expect​

After upload the screen stays as it is until you send commands. Send #?(…)* and Inkplate answers OK. Send drawing commands and then #L(1)*, and the screen refreshes with the result. Send #P(?)* and it answers #P( and the battery voltage with two decimals, then )*.

🔗Open the complete example

examples/Inkplate7SPECTRA/Diagnostics/Inkplate7SPECTRA_Peripheral_Mode

→