Skip to content

Commit d65f1cd

Browse files
committed
add image_eink_upload sketch
tested with EInk Gizmo
1 parent 6d7b755 commit d65f1cd

File tree

2 files changed

+310
-4
lines changed

2 files changed

+310
-4
lines changed
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
/*********************************************************************
2+
This is an example for our nRF52 based Bluefruit LE modules
3+
4+
Pick one up today in the adafruit shop!
5+
6+
Adafruit invests time and resources providing this open source code,
7+
please support Adafruit and open-source hardware by purchasing
8+
products from Adafruit!
9+
10+
MIT license, check LICENSE for more information
11+
All text above, and the splash screen below must be included in
12+
any redistribution
13+
*********************************************************************/
14+
15+
#include <bluefruit.h>
16+
#include <SPI.h>
17+
#include <Adafruit_GFX.h>
18+
#include "Adafruit_EPD.h"
19+
20+
/* This sketch demonstrates the "Image Upload" feature of Bluefruit Mobile App.
21+
* Following TFT Display are supported
22+
* - https://www.adafruit.com/product/4428
23+
*/
24+
25+
#define EINK_GIZMO 1
26+
27+
// one of above supported TFT add-on
28+
#define DISPLAY_IN_USE EINK_GIZMO
29+
30+
#define EPD_CS 0
31+
#define EPD_DC 1
32+
#define SRAM_CS -1
33+
#define EPD_RESET PIN_A3 // can set to -1 and share with microcontroller Reset!
34+
#define EPD_BUSY -1 // can set to -1 to not use a pin (will wait a fixed delay)
35+
36+
Adafruit_IL0373 display(152, 152, EPD_DC, EPD_RESET, EPD_CS, SRAM_CS, EPD_BUSY);
37+
38+
39+
// Declaring Uart over BLE with large buffer to hold image data
40+
// Depending on the Image Resolution and Transfer Mode especially without response
41+
// or Interleaved with high ratio. You may need to increase this buffer size
42+
BLEUart bleuart(10*1024);
43+
44+
/* The Image Transfer module sends the image of your choice to Bluefruit LE over UART.
45+
* Each image sent begins with
46+
* - A single byte char '!' (0x21) followed by 'I' helper for image
47+
* - Color depth: 24-bit for RGB 888, 16-bit for RGB 565
48+
* - Image width (uint16 little endian, 2 bytes)
49+
* - Image height (uint16 little endian, 2 bytes)
50+
* - Pixel data encoded as RGB 16/24 bit and suffixed by a single byte CRC.
51+
*
52+
* Format: [ '!' ] [ 'I' ] [uint8_t color bit] [ uint16 width ] [ uint16 height ] [ r g b ] [ r g b ] [ r g b ] … [ CRC ]
53+
*/
54+
55+
uint16_t imageWidth = 0;
56+
uint16_t imageHeight = 0;
57+
uint8_t imageColorBit = 0;
58+
59+
uint32_t totalPixel = 0; // received pixel
60+
61+
// pixel line buffer, should be large enough to hold an image width
62+
uint16_t pixel_buf[512];
63+
64+
// Statistics for speed testing
65+
uint32_t rxStartTime = 0;
66+
uint32_t rxLastTime = 0;
67+
68+
void setup()
69+
{
70+
Serial.begin(115200);
71+
72+
display.begin();
73+
display.clearBuffer();
74+
75+
#if DISPLAY_IN_USE == EINK_GIZMO
76+
display.setRotation(3);
77+
#else
78+
// todo more eink displays
79+
#endif
80+
81+
// Config the peripheral connection with maximum bandwidth
82+
// more SRAM required by SoftDevice
83+
// Note: All config***() function must be called before begin()
84+
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
85+
86+
Bluefruit.begin();
87+
Bluefruit.setTxPower(4); // Check bluefruit.h for supported values
88+
Bluefruit.setName("Bluefruit52");
89+
Bluefruit.Periph.setConnectCallback(connect_callback);
90+
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
91+
Bluefruit.Periph.setConnInterval(6, 12); // 7.5 - 15 ms
92+
93+
// Configure and Start BLE Uart Service
94+
bleuart.begin();
95+
96+
// Due to huge amount of image data
97+
// NRF52832 doesn't have enough SRAM to queue up received packets using deferred callbacks.
98+
// Therefore it must process data as soon as it comes, this can be done by
99+
// changing the default "deferred" option to false to invoke callback immediately.
100+
// However, the transfer speed will be affected since immediate callback will block BLE task
101+
// to process data especially when tft.drawRGBBitmap() is calling.
102+
#ifdef NRF52840_XXAA
103+
// 2nd argument is true to deferred callbacks i.e queue it up in separated callback Task
104+
bleuart.setRxCallback(bleuart_rx_callback, true);
105+
#else
106+
// 2nd argument is false to invoke callbacks immediately (thus blocking other ble events)
107+
bleuart.setRxCallback(bleuart_rx_callback, false);
108+
#endif
109+
110+
bleuart.setRxOverflowCallback(bleuart_overflow_callback);
111+
112+
// Set up and start advertising
113+
startAdv();
114+
115+
Serial.println("Advertising ... ");
116+
}
117+
118+
void startAdv(void)
119+
{
120+
// Advertising packet
121+
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
122+
Bluefruit.Advertising.addTxPower();
123+
Bluefruit.Advertising.addAppearance(BLE_APPEARANCE_GENERIC_CLOCK);
124+
125+
// Include bleuart 128-bit uuid
126+
Bluefruit.Advertising.addService(bleuart);
127+
128+
// There is no room for Name in Advertising packet
129+
// Use Scan response for Name
130+
Bluefruit.ScanResponse.addName();
131+
132+
/* Start Advertising
133+
* - Enable auto advertising if disconnected
134+
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
135+
* - Timeout for fast mode is 30 seconds
136+
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
137+
*
138+
* For recommended advertising interval
139+
* https://developer.apple.com/library/content/qa/qa1931/_index.html
140+
*/
141+
Bluefruit.Advertising.restartOnDisconnect(true);
142+
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
143+
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
144+
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
145+
}
146+
147+
void loop()
148+
{
149+
// nothing to do
150+
}
151+
152+
// Invoked when receiving data from bleuart
153+
// Pull data from bleuart fifo & draw image as soon as possible,
154+
// Otherwise bleuart fifo can be overflowed
155+
void bleuart_rx_callback(uint16_t conn_hdl)
156+
{
157+
(void) conn_hdl;
158+
159+
rxLastTime = millis();
160+
161+
// Received new Image
162+
if ( (imageWidth == 0) && (imageHeight == 0) )
163+
{
164+
// take note of time of first packet
165+
rxStartTime = millis();
166+
167+
// Skip all data until '!I' is found
168+
while( bleuart.available() && bleuart.read() != '!' ) { }
169+
if (bleuart.read() != 'I') return;
170+
171+
if ( !bleuart.available() ) return;
172+
173+
imageColorBit = bleuart.read8();
174+
imageWidth = bleuart.read16();
175+
imageHeight = bleuart.read16();
176+
177+
totalPixel = 0;
178+
179+
Serial.printf("Image resolution: %dx%d with %d bit color\n", imageWidth, imageHeight, imageColorBit);
180+
181+
display.clearBuffer();
182+
display.fillScreen(EPD_WHITE);
183+
display.setCursor(0, 0);
184+
}
185+
186+
// Extract pixel data to buffer and draw image line by line
187+
while ( bleuart.available() >= 3 )
188+
{
189+
uint8_t red, green, blue;
190+
191+
if ( imageColorBit == 24 )
192+
{
193+
// Application send 24-bit color
194+
red = bleuart.read();
195+
green = bleuart.read();
196+
blue = bleuart.read();
197+
}
198+
else if ( imageColorBit == 16 )
199+
{
200+
// Application send 16-bit 565 color
201+
uint16_t c565 = bleuart.read16();
202+
203+
red = (c565 & 0xf800) >> 8;
204+
green = (c565 & 0x07e0) >> 3;
205+
blue = (c565 & 0x001f) << 3;
206+
}
207+
208+
// Convert RGB into Eink Color
209+
uint8_t c = 0;
210+
if ((red < 0x80) && (green < 0x80) && (blue < 0x80)) {
211+
c = EPD_BLACK; // try to infer black
212+
} else if ((red >= 0x80) && (green >= 0x80) && (blue >= 0x80)) {
213+
c = EPD_WHITE;
214+
} else if (red >= 0x80) {
215+
c = EPD_RED; // try to infer red color
216+
}
217+
218+
// store to pixel buffer
219+
pixel_buf[totalPixel % imageWidth] = c;
220+
221+
totalPixel++;
222+
223+
// have enough to draw an image line
224+
if ( (totalPixel % imageWidth) == 0 )
225+
{
226+
display.drawRGBBitmap(0, totalPixel/imageWidth, pixel_buf, imageWidth, 1);
227+
}
228+
}
229+
230+
// all pixel data is received
231+
if ( totalPixel == imageWidth*imageHeight )
232+
{
233+
uint8_t crc = bleuart.read();
234+
(void) crc;
235+
// do checksum later
236+
237+
// print speed summary
238+
print_summary(totalPixel*(imageColorBit/8) + 8, rxLastTime-rxStartTime);
239+
240+
// Display on Eink, will probably take dozens of seconds
241+
display.display();
242+
243+
// reset and waiting for new image
244+
imageColorBit = 0;
245+
imageWidth = imageHeight = 0;
246+
totalPixel = 0;
247+
248+
Serial.println("Ready to receive new image");
249+
}
250+
}
251+
252+
void connect_callback(uint16_t conn_handle)
253+
{
254+
BLEConnection* conn = Bluefruit.Connection(conn_handle);
255+
256+
Serial.println("Connected");
257+
258+
// Requesting to Switching to 2MB PHY, larger data length and MTU
259+
// will increase the throughput on supported central. This should be already done
260+
// with latest Bluefruit app, but still put here for user reference
261+
conn->requestPHY();
262+
Serial.println("Switching PHY");
263+
264+
conn->requestDataLengthUpdate();
265+
Serial.println("Updating Data Length");
266+
267+
conn->requestMtuExchange(247);
268+
Serial.println("Exchanging MTU");
269+
270+
Serial.println("Ready to receive new image");
271+
}
272+
273+
void print_summary(uint32_t count, uint32_t ms)
274+
{
275+
float sec = ms / 1000.0F;
276+
277+
Serial.printf("Received %d bytes in %.2f seconds\n", count, sec);
278+
Serial.printf("Speed: %.2f KB/s for %dx%d Image\n", (count / 1024.0F) / sec, imageWidth, imageHeight);
279+
}
280+
281+
void bleuart_overflow_callback(uint16_t conn_hdl, uint16_t leftover)
282+
{
283+
(void) conn_hdl;
284+
(void) leftover;
285+
286+
Serial.println("BLEUART rx buffer OVERFLOWED!");
287+
Serial.println("Please increase buffer size for bleuart");
288+
}
289+
290+
/**
291+
* invoked when a connection is dropped
292+
* @param conn_handle connection where this event happens
293+
* @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
294+
*/
295+
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
296+
{
297+
(void) conn_handle;
298+
(void) reason;
299+
300+
imageColorBit = 0;
301+
imageWidth = imageHeight = 0;
302+
totalPixel = 0;
303+
304+
bleuart.flush();
305+
}

libraries/Bluefruit52Lib/examples/Peripheral/image_upload/image_upload.ino

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
any redistribution
1313
*********************************************************************/
1414

15+
#include <bluefruit.h>
16+
#include <SPI.h>
17+
#include <Adafruit_GFX.h>
18+
1519
/* This sketch demonstrates the "Image Upload" feature of Bluefruit Mobile App.
1620
* Following TFT Display are supported
1721
* - https://www.adafruit.com/product/3315
@@ -26,9 +30,6 @@
2630
// [Configurable] Please select one of above supported Display to match your hardware setup
2731
#define TFT_IN_USE TFT_35_FEATHERWING
2832

29-
#include <bluefruit.h>
30-
#include <SPI.h>
31-
#include <Adafruit_GFX.h>
3233

3334
#if defined(ARDUINO_NRF52832_FEATHER)
3435
// Feather nRF52832
@@ -245,7 +246,7 @@ void bleuart_rx_callback(uint16_t conn_hdl)
245246

246247
totalPixel++;
247248

248-
// enough to draw an image line
249+
// have enough to draw an image line
249250
if ( (totalPixel % imageWidth) == 0 )
250251
{
251252
tft.drawRGBBitmap(0, totalPixel/imageWidth, pixel_buf, imageWidth, 1);

0 commit comments

Comments
 (0)