Waveshare e-paper displays with SPI

// mapping suggestion for ESP32, e.g. LOLIN32, see .../variants/.../pins_arduino.h for your board
// NOTE: there are variants with different pins for SPI ! CHECK SPI PINS OF YOUR BOARD
// BUSY -> 4, RST -> 16, DC -> 17, CS -> SS(5), CLK -> SCK(18), DIN -> MOSI(23), GND -> GND, 3.3V -> 3.3V

from e.g. GxEPD2/GxEPD2_wiring_examples.h at master · ZinggJM/GxEPD2 · GitHub

D/C a.k.a. DC
CS a.k.a. SS (Slave Select)
RES a.k.a. RST (Reset)
SDI a.k.a. DIN (Serial or Slave Data In) drive from MOSI (Master Out Slave IN)

I am struggling with a bug with my e-paper project. I have been banging my head against it for a few days without any signs of meaningful progress, so I am here to ask for help.

Hardware

Screen:
880×528, 7.5inch E-Ink display HAT for Raspberry Pi, red / black / white three-color
SKU: 17960
Part Number: 7.5inch HD e-Paper HAT (B)
Brand: Waveshare

Controller:
ESP8266

Description of project so far

I want my ESP8266 to naively display 880x528 black/white/red images that it downloads over wifi from my raspberry pi, which is running a web server on the LAN. I am able to successfully display 880x528 black/white/red images to my screen using my raspberry pi directly, but ultimately I want to use my ESP8266 so that I can use battery power.

Description of issue

I am not yet able to successfully display 880x528 color images with my ESP8266.

I am using GxEPD2. I started with the GxEPD2_WiFi_Example, which does most of what I want to do. I modified it to connect to my wifi, and display the example images. It runs successfully, and the images look fine. This indicates to me that WiFi is working, that my wiring is set up properly, and that the screen is functioning properly.

First issue:
With more code tweaks to make the GxEPD2_WiFi_Example pull an image from my raspberry pi instead, the first issue arises: the image is displayed as completely black. Logs indicate that the WiFi connection is successful, and that my raspberry pi is getting a request, so the connection and download is successful. It is just displaying the image that seems to be the problem.

This forum is limiting me to two links in this post since I am a new user, so I put the links in the github README instead. Check the first section there to find:

  • Link to the 880x528 bmp I am trying to display
  • Link to code
  • Link to diff from example
  • Link to picture of screen for 880x528 image
  • Link to picture of screen for 200x200 image

Second issue:
With a code tweak to fix the first issue with the color (I am stumbling around in the dark here, since I am completely unfamiliar with arduino programming, bitmaps, etc, so I found this fix somewhere else on github), the second issue arises: I am able to display color images, however they are mangled with a transposition issue. The red also looks pretty dark. This is as close as I can get.

Find these links in the second section of the README:

  • Link to the 880x528 bmp I am trying to display
  • Link to code
  • Link to diff
  • Link to picture of screen for 880x528 image
  • Link to picture of screen for 200x200 image

Other points of data: the bmp image displays in my browser just fine when I hit the raspberry pi web server, so that part seems to be working. I also see this log statement in the logs consistently: "Error: got no more after 464708 bytes read!", which matches the size of the bmp file.

My analysis

I have spent a lot of time trying different code changes, checking other projects, etc, but none of those other changes was able to fix the transposed image issue.

It's possible that I did not modify the WifiExample correctly to support my 3-color eink screen.

Given that the example images display just fine, I thought maybe that there could be an issue with my bmp file. I have tried with several bit depths, black and white images, 3 color palettes and 7 color palettes, different image sizes, etc. None of these fix the issue.

I thought maybe there is a memory issue on the ESP8266 with displaying a 880x528 image, which is larger than all of the example images. However, the code seems to stream the image through memory, not store it all at once. Also, the full image is ultimately being displayed, just with the transposition issue. So that makes me think memory isn't the issue.

Any pointers? Especially with the bmp file. Also, any tips for further troubleshooting? Thanks.

Hi @gtsioni , welcome to the forum!

BMP handling in GxEPD2 examples is known to be incomplete and may be wrong for some formats. The purpose of these examples is to show how BMP content can be transferred to the controller memory by writing line per line.

The ultimate source of knowledge for BMP format may be Wikipedia; I looked up there occasionally. An other source I consulted is e.g. MCUFRIEND_kbv/examples/showBMP_not_Uno at master · prenticedavid/MCUFRIEND_kbv · GitHub, this is this source I originally started from (but it was not easy to understand, at least then).

I am always short of time. Make it easy for me if you would like your issue to be analyzed.
You could put your example bitmap to your GitHub location, then it could be accessed by the GxEPD2_WiFi_Example directly.
And you could provide diagnostic output from Serial Monitor, in a code window please, so we can already see some format information.

Jean-Marc

See also:
1 bit depth BMP created with ImageMagick all black using GxEPD2 - Using Arduino / Displays - Arduino Forum

I figured out the problem, and a workaround. I am now able to successfully display 880x528 color images from my ESP8266 to my e-ink screen.

The issue was that the example code expects a BMP file where the size of the color table equals the square of the bit depth. For example, the example images use a bit depth of 4 and a color table with 16 colors, which will display as expected. However, the BMP files that I was providing were using a bit depth of 8 and a color table with 3 colors (black, white, and red), which will not display as expected.

Here is how I think that explains the issues I was having (I can't make any promises that the math is correct, but I think this is the general idea): In the error case where the BMP file's bit depth is 8 but the color table only has 3 colors, the for loop that I linked would've traversed 256 bytes while attempting to decode the color table: (bit_depth^2)*number_of_bytes_per_color = (8^2)*4 bytes = 256 bytes, which is 244 bytes further than the end of the actual color table. This caused a 244 byte offset in each row while the actual image was being processed and sent to the screen, which was probably what was responsible for the transposed image. The blacked-out image was probably due to having a garbage value in the color table. This also most likely explains the "Error: got no more after 464708 bytes read!" message that I was receiving, as the final row of bytes wouldn't have been a complete row after the pointer was offset by 244 bytes, which would have triggered that error message.

As a workaround, I padded the color table in my images with black until I had 16 colors, and changed the bit depth from 8 to 4. This caused the images to display correctly. (I also tried padding the color table with white, and that caused issues. I don't fully understand why, I suspect it has something to do with the logic that separates white values from color values on lines L589-L594, but I encourage someone that is more adventurous than me to figure that one out). Note that the "bytes_read" fix that I mentioned in my previous post was unnecessary, I updated the github repo to reflect that.

hi
Please help me, I can't start the display.
I have E-paper 1.54 V2. I would like to connect with Attiny serries2 3224 32kB.
The program compiles and loads, but the display shows nothing, I don't know what I'm doing wrong. I checked with Arduino pro mini 3.3V and it works.
I'm sending an Attiny pinout.
Please help

Hi @brendy32530, welcome to the forum and to this topic!

Please read How to get the best out of this forum, if you haven't done yet.

In this topic I ask every poster to provide clickable links to the devices in question.
This is a common request by most competent responders in the Displays section.

The ATtinyX24 doesn't seem to be a widely known processor here, so a link to the package for the Arduino IDE would also be helpful. In there we would find something like pins_arduino.h, to learn how the pins are named for the Arduino IDE, and confirm what pins are used for HW SPI.

You tell your display works with Arduino pro mini, but you didn't tell what library or example you used.

Jean-Marc

Hi ZinggJM thanks for your reply.
Sorry for giving me little detail.
This is the link to the Arduino board manager

http://drazzy.com/package_drazzy.com_index.json
I am using board manager version 2.5.4

Recommended Arduino for Attiny Serries 0,1,2, Arduino 1.8.13, others may be bugs.
I am using the Gx-EPD library for example epd1in54_V2.ino
I found the file you wrote about.

/*
  pins_arduino.h - Pin definition functions for Arduino
  Part of Arduino - http://www.arduino.cc/

  Copyright (c) 2007 David A. Mellis

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General
  Public License along with this library; if not, write to the
  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  Boston, MA  02111-1307  USA
*/

#ifndef Pins_Arduino_h
#define Pins_Arduino_h

#include <avr/pgmspace.h>
#include "timers.h"

#define NUM_DIGITAL_PINS                  (6) //
#define NUM_ANALOG_INPUTS                 (9)
#define NUM_I2C_PINS                      (2) // (SDA / SCL)
#define NUM_SPI_PINS                      (3) // (MISO / MOSI / SCK)
#define NUM_TOTAL_FREE_PINS               (NUM_DIGITAL_PINS)
#define NUM_TOTAL_PINS                    (6)
#define EXTERNAL_NUM_INTERRUPTS           (8)
#define PINS_COUNT                        (6)

#define PIN_PA6   (0)
#define PIN_PA7   (1)
#define PIN_PA1   (2)
#define PIN_PA2   (3)
#define PIN_PA3   (4)
#define PIN_PA0   (5)

#define digitalPinHasPWM(p)               ((p) != 0 && (p) != 5)

#ifdef DAC0
  #define DAC_PIN                         (PIN_PA6)
#endif

#ifndef LED_BUILTIN
  #define LED_BUILTIN    (PIN_PA7)
#endif

/*
      ####   ###  ####  ##### #   # #   # #   #
      #   # #   # #   #   #   ## ## #   #  # #
      ####  #   # ####    #   # # # #   #   #
      #     #   # # #     #   #   # #   #  # #
      #      ###  #  #    #   #   #  ###  #   #
*/

#ifdef PORTMUX_SPI0_bm
  #define PIN_SPI_MISO_PINSWAP_1          (PIN_PA7)
  #define PIN_SPI_SCK_PINSWAP_1           (PIN_PA3)
  #define PIN_SPI_MOSI_PINSWAP_1          (PIN_PA6)
  #define PIN_SPI_SS_PINSWAP_1            (PIN_PA6)
#endif
#define PIN_SPI_MISO                      (PIN_PA2)
#define PIN_SPI_SCK                       (PIN_PA3)
#define PIN_SPI_MOSI                      (PIN_PA1)
#define PIN_SPI_SS                        (PIN_PA6)

#define SPI_INTERFACES_COUNT              (1)

#define PIN_WIRE_SDA                      (PIN_PA1)
#define PIN_WIRE_SCL                      (PIN_PA2)

/* Serial pin values here are not used by the core. They don't get picked up correctly in UART.h where they're needed and I can't figure out why. */
// Serial (USART0)
#define HWSERIAL0_MUX_DEFAULT             (0)
#define PIN_HWSERIAL0_RX                  (PIN_PA7)
#define PIN_HWSERIAL0_TX                  (PIN_PA6)
#define PIN_HWSERIAL0_XCK                 (PIN_PA3)
#define PIN_HWSERIAL0_XDIR                (PIN_PA0)
#define PIN_HWSERIAL0_RX_PINSWAP_1        (PIN_PA2)
#define PIN_HWSERIAL0_TX_PINSWAP_1        (PIN_PA1)
// Alt pins don't include the rarely used functions on 8-pin parts.
#define PIN_HWSERIAL0_XCK_PINSWAP_1       (NOT_A_PIN)
#define PIN_HWSERIAL0_XDIR_PINSWAP_1      (NOT_A_PIN)

/*
       ##  #   #  ##  #     ###   ###      ####  ### #   #  ###
      #  # ##  # #  # #    #   # #         #   #  #  ##  # #
      #### # # # #### #    #   # #  ##     ####   #  # # #  ###
      #  # #  ## #  # #    #   # #   #     #      #  #  ##     #
      #  # #   # #  # ####  ###   ###      #     ### #   #  ###
*/

#define PIN_A0   (A0)
#define PIN_A1   (A1)
#define PIN_A2   (A2)
#define PIN_A3   (A3)
#define PIN_A6   (A6)
#define PIN_A7   (A7)

static const uint8_t A0 = PIN_PA0;
static const uint8_t A1 = PIN_PA1;
static const uint8_t A2 = PIN_PA2;
static const uint8_t A3 = PIN_PA3;
static const uint8_t A6 = PIN_PA6;
static const uint8_t A7 = PIN_PA7;


/*
            ####  ### #   #      ##  ####  ####   ##  #   #  ###
            #   #  #  ##  #     #  # #   # #   # #  #  # #  #
            ####   #  # # #     #### ####  ####  ####   #    ###
            #      #  #  ##     #  # # #   # #   #  #   #       #
            #     ### #   #     #  # #  #  #  #  #  #   #    ###
*/

#ifdef ARDUINO_MAIN

// On the Arduino board, digital pins are also used
// for the analog output (software PWM).  Analog input
// pins are a separate set.

// ATtiny412 / ARDUINO
//                                     _____
//                             VDD   1|*    |20  GND
//             (DAC) (AIN6) PA6  0   2|     |19  4~  PA3 (AIN3)(SCK)(EXTCLK)
//                   (AIN7) PA7  1   3|     |18  5   PA0 (nRESET/UPDI)
// (MOSI)(TXD*)(SDA) (AIN1) PA1  2   4|_____|17  3   PA2 (AIN2)(MISO)(RXD*)(SCL)
//
//

/*
  PIN#   DESC         Pin Name  Other/Sp  ADC0      ADC1      PTC       AC0       AC1       AC2       DAC0      USART0    SPI0      TWI0      TCA(PWM)  TCBn      TCD0      CCL
  0      A2 or DAC    PA6                 AIN6      AIN2      X2/Y2     AINN0     AINP1     AINP0     OUT       RxD                                               WOA
  1      A3           PA7                 AIN7      AIN3      X3/Y3     AINP0     AINP0     AINN0               TxD                           *WO0                WOB       LUT1-OUT
  2      MOSI         PA1                 AIN1                                                                  *TxD      MOSI      SDA       WO1                           LUT0-IN1
  3      MISO         PA2       EVOUT0    AIN2                                                                  *RxD      MISO      SCL       WO2                           LUT0-IN2
  4      SCK          PA3       EXTCLK    AIN3                                                                  *XCK      SCK                 WO3       TCB1 WO
  5      UPDI         PA0       RESET/    AIN0                                                                                                                              LUT1-IN0
                                UPDI
*/

const uint8_t digital_pin_to_port[] = {
  PA,           // 0  PA6
  PA,           // 1  PA7
  PA,           // 2  PA1
  PA,           // 3  PA2
  PA,           // 4  PA3
  PA            // 5  PA0
};

/* Use this for accessing PINnCTRL register */
const uint8_t digital_pin_to_bit_position[] = {
  PIN6_bp,      // 0  PA6
  PIN7_bp,      // 1  PA7
  PIN1_bp,      // 2  PA1
  PIN2_bp,      // 3  PA2
  PIN3_bp,      // 4  PA3
  PIN0_bp       // 5  PA0
};

/* Use this for accessing PINnCTRL register */
const uint8_t digital_pin_to_bit_mask[] = {
  PIN6_bm,      // 0  PA6
  PIN7_bm,      // 1  PA7
  PIN1_bm,      // 2  PA1
  PIN2_bm,      // 3  PA2
  PIN3_bm,      // 4  PA3
  PIN0_bm       // 5  PA0
};

const uint8_t digital_pin_to_timer[] = {
  #if defined(DAC0)
  DACOUT,       // 0  PA6
  #else
  NOT_ON_TIMER, // 0  PA6
  #endif
  TIMERA0,      // 1  PA7
  TIMERA0,      // 2  PA1
  TIMERA0,      // 3  PA2
  TIMERA0,      // 4  PA3
  NOT_ON_TIMER  // 5  PA0
};



#endif

#define digitalPinToAnalogInput(p)      ((p<2)?(p+6):(p<5?(p-1):(p==5?0:NOT_A_PIN)))


// These serial port names are intended to allow libraries and architecture-neutral
// sketches to automatically default to the correct port name for a particular type
// of use.  For example, a GPS module would normally connect to SERIAL_PORT_HARDWARE_OPEN,
// the first hardware serial port whose RX/TX pins are not dedicated to another use.
//
// SERIAL_PORT_MONITOR        Port which normally prints to the Arduino Serial Monitor
//
// SERIAL_PORT_USBVIRTUAL     Port which is USB virtual serial
//
// SERIAL_PORT_LINUXBRIDGE    Port which connects to a Linux system via Bridge library
//
// SERIAL_PORT_HARDWARE       Hardware serial port, physical RX & TX pins.
//
// SERIAL_PORT_HARDWARE_OPEN  Hardware serial ports which are open for use.  Their RX & TX
//                            pins are NOT connected to anything by default.
#define SERIAL_PORT_MONITOR     Serial
#define SERIAL_PORT_HARDWARE    Serial

#endif

regards @brendy32530

The most common BMP formats are 24-bit colour, 8-bit colour, 4-bit colour, 1-bit colour.
24-bit is just a set of RGB values.
8-bit, 4-bit, 1-bit provide a colour palette. And the pixel data is packed e.g. 2 pixels of 4-bit colour in a byte.

Since the BMP header contains all the important format data you know how to decode and display the pixels. I guess that you have a Grayscale and possibly Red with appropriate values in the Palette.

You will probably store as much colour data as you can in the BMP file.
However you can only render what your display can support.

e.g. you read 24-bit RGB. Convert it to 16-Grayscale. Which looks surprisingly good on a colour TFT or OLED.
I have never used E-INK

Using 8-bit or 4-bit saves disk space. You can adjust the Palette for the best picture.
1-bit is very economical. Just that monochrome pictures look horrible. Text is fine.

I don't know the Gx-EPD library.
I know https://github.com/ZinggJM/GxEPD
and GitHub - ZinggJM/GxEPD2: Arduino Display Library for SPI E-Paper Displays.

epd1in54_V2.ino looks like an example from Waveshare. But you provided no link.
So I don't go and check if the Waveshare code uses SS for CS, and if it accepts 0 for a pin number.

Jean-Marc

@gtsioni

I did not check if you added your BMP file to your GitHub repository. If you tell you did, I may take a look at it.

Please report how you created that BMP file, by which program and what settings.
Then we may consider if we want to support that format.

Jean-Marc

I downloaded two libraries from the link, thank you for the links.
Which pins in Attiny do you propose to connect the Display?
From this link I had a library under 1.54 200x200 V2

You can download these libraries and code using this link.

I don't like to have to download from obscure places to try to help a poster.
Therefore I can't help you.

The original Waveshare library is found here: GitHub - waveshare/e-Paper

It won't work on your library. Is there really nothing you can do?

From Waveshare epd1in54_V2/epdif.h:

// Pin definition
#define RST_PIN         8
#define DC_PIN          9
#define CS_PIN          10
#define BUSY_PIN        7

It looks like these also exist on your ATtinyX24 and are digital I/O capable; you could check.
You didn't provide a link to the processor board, so I don't even know if it is 3.3V or 5V.

No, I don't have any ATtinyX24, and my Waveshare 1.54" V2 has not survived a pin reversal; BUSY is stuck.

It used to work on Arduino UNO. Needs the shortened reset pulse with GxEPD2.

I checked on the pro mini 3.3V and the GxEPD2 works.
https://www.microchip.com/en-us/product/ATTINY3224

Thank you for the link!

If you want to try with GxEPD2 on ATtiny3224, you would need to add a constructor line for your display, e.g. in GxEPD2_display_selection.h, either outside of the processor dependent conditional sections, or in a new section for your processor. The parameters must match your wiring, and you need to connect SCK and DIN to HW SPI SCK and MOSI.

Sorry for asking. I'm a beginner. Could you write exactly what and where I have to enter. I really care, I'd like to run this from Attina

Sorry, I am a bad teacher. And always short of time and patience. But I can try.
My (broken) Waveshare 1.54" has a sticker added by me with GDEH0154D67 on it.

So you would take this line from GxEPD2_display_selection.h from the #if defined(__AVR) section:

//GxEPD2_BW<GxEPD2_154_D67, MAX_HEIGHT(GxEPD2_154_D67)> display(GxEPD2_154_D67(/*CS=*/ SS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7)); // GDEH0154D67

copy it outside any of these sections, e.g. just before #if defined(__AVR) and uncomment it.
Then you need to change the pins for DC and RST, as these are MOSI and MISO on ATtiny, e.g. to 5 and 6:

#define MAX_DISPLAY_BUFFER_SIZE 800
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
GxEPD2_BW<GxEPD2_154_D67, MAX_HEIGHT(GxEPD2_154_D67)> display(GxEPD2_154_D67(/*CS=*/ SS, /*DC=*/ 5, /*RST=*/ 6, /*BUSY=*/ 7)); // GDEH0154D67

added lines for MAX_HEIGHT
and wire accordingly.

Untested; I try to avoid untested recommendations.

Thank you very much for any help, I really appreciate it. I changed the processor to Attiny 3226 is the same 3224 only has more pins. I plugged it in, I didn't change anything and something started to work. After uploading the program, the Hello word animation appeared, but when the processor resets, nothing happens. I have uploaded an example from the epd2 Example library

ZinggJM I have a question, I ran it with Attiny, but I don't know if this is the correct display behavior, I tested a few examples from the library and there is a blinking effect and red. How to get such a simple effect as in the picture.
Could you please take a look at the link.

https://drive.google.com/drive/folders/1Hi6iAU4yusGKVR7PbitcRNhDwznT_t9R