Libraries for GUI supported by Photon?

I was wondering if anyone knew of a good library for doing GUIs on a SSD1351 1.5" display? Nothing too fancy, just fancier than having to draw it all by hand lol

Hi @tjp

I’d recommend using the Adafruit_mfGFX library. There’s quite a bit of info in the forums about it and it’s available via Particle Build.
You’ll need to edit the device driver from the library example in Particle Build. (I think it’s called SSD1306.cpp & .SSD1306.h) I’ve used this process for both a SSD1325 and a SSD1305. I found it quite time consuming to get it right (you’ll have to go through the SSD1351 datasheet and copy all the initialization commands over etc). but still easier than doing it all yourself :smile:

That’s what I’m using now, I was hoping for something a little more high level than that.

@tjp, did you have an Arduino library in mind?

Look at the Nextion displays that have a GUI layout software included to create nice user interfaces.

There is a library for the Nextion displays already in the online library list.

@peekay123, well, I used to use u8glib on Arduino (which I saw recently has a GUI lib on top of it), but I’ve read about your adventures with that, so that’s out. I found one called uGFX that looks pretty cool, but didn’t see anything on the forums about that. And so rather than continuing to look for Arduino options that might have been ported, I decided to ask here and see if anyone else has had the same itch. lol I was also kind of hoping someone might have written something on top of Adafruit_[mf]GFX already since that seems to largely be what the community uses (other than Nextion stuff)

@RWB, that is certainly an option for a future upgrade, but I was hoping to prototype my project with the parts on hand lol

I totally understand but also know how much time that can be wasted trying to get something working without the proper tools and support.

@tjp, uGFX looks great but would require some serious porting for the Particle platform. I believe you won’t find a lot of GUI stuff for a simple reason - with Cloud integration, the GUI can be done somewhere else! Besides, the Photon’s resources don’t really lend themselves to a doing a full out GUI IMO. I am using Digole and Nextion displays to reduce that overhead.

@peekay123, my project will certainly have an interface through the Cloud, but since it’s an alarm clock, it kind of needs to work even if the internet goes down. lol I will look into Digole and Nextion displays. They certainly have bigger ones than I can get from Adafruit lol

@tjp, you may want to consider the newest Digole touchscreen LCD units with onboard flash. They allow you to store a sequence of display commands and recall them with a single command from the Photon. You can build your GUI elements that way.

http://digole.com/index.php?productID=1225

What I like is that it can be driven entirely using Serial1’s RX/TX lines. :wink:

That is shiny! Though, I do kind of wish it was OLED instead, I know they make OLEDs. I was hoping to have a LiPo backup for when the power goes out, that would limit it quite a bit more. And I also kind of wish it was Cap Touch rather than resistive. But that is quite a compelling product! So it has 16MB built in plus the SD card slot?

@tjp, you need to download their manual. The displays I linked you to has a couple of low power modes that are like “deep sleep”. The SD card is a “side show” where you can use it via SPI. There is even the ability to read your batteries voltage (up to 10V) via the vbat input and RDBAT command!

I’ll have to get one of those and play with it, I might be able to use that. The only issue is that it might be annoying to interact with the resistive touch if my WiFi or power go out. But that is generally to be an edge case. The idea is to use a Web/Mobile interface for the most part.

@tjp, the touch action is actually pretty good and if your buttons are decently sized, they work well. It is well worth the cost to investigate :smiley:

1 Like

@tjp I am using a ST7735 1.8" TFT display. With the SD card on the back of this module you can store .bmp files and then load them on the screen. This is how most GUI displays work - albeit the images are stored in RAM for speed. I have ported a function called bmpDraw() that I found had been implemented for the adafruit ST7735 but isn’t in the library.

// This function opens a Windows Bitmap (BMP) file and
// displays it at the given coordinates. Needs 24 bit/1 plane BMP.
// Returns error code as follows:
// 0 = OK
// 1 = x or y outside screen
// 2 = not BMP file
// 3 = more than 1 plane
// 4 = not 24 bits and uncompressed
// 5 = SD card in use and timeout after 50msec
//
int bmpDraw(char filename, uint8_t x, uint16_t y)
{
File bmpFile;
int bmpWidth, bmpHeight; // W+H in pixels
uint8_t bmpDepth; // Bit depth (currently must be 24)
uint16_t bmpType;
uint32_t bmpImageoffset; // Start of image data in file
uint32_t rowSize; // Not always = bmpWidth; may have padding
uint8_t sdbuffer[3
BUFFPIXEL]; // pixel buffer (R+G+B per pixel)
uint8_t buffidx = sizeof(sdbuffer); // Current position in sdbuffer
boolean flip = true; // BMP is stored bottom-to-top
int w, h, row, col;
uint8_t r, g, b;
uint32_t pos = 0;
uint32_t ms;
int returnCode = 0;

if ((x >= tft.width()) || (y >= tft.height())) return 1;
ms = millis();
while (SDinUse && ((millis() - ms) < 100))  //while SD file open and not timeout 100mS
if (SDinUse) return 5;                      //SD file open still
bmpFile = SD.open(filename, FILE_READ);
SDinUse = TRUE;
bmpType = read16(bmpFile);
if (bmpType == 0x4D42)
{
    (void)read32(bmpFile); // Read & ignore file size
    (void)read32(bmpFile); // Read & ignore creator bytes
    bmpImageoffset = read32(bmpFile); // Start of image data
    (void)read32(bmpFile); //Read & ignore header size
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if(read16(bmpFile) == 1) { // # planes -- must be '1'
        bmpDepth = read16(bmpFile); // bits per pixel
        if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed
            rowSize = (bmpWidth * 3 + 3) & ~3; // BMP rows are padded (if needed) to 4-byte boundary
            if(bmpHeight < 0) { // If bmpHeight is negative, image is in top-down order.
                bmpHeight = -bmpHeight;
                flip      = false;
            }
            // Crop area to be loaded
            w = bmpWidth;
            h = bmpHeight;
            if((x+w-1) >= tft.width())  w = tft.width()  - x;
            if((y+h-1) >= tft.height()) h = tft.height() - y;
            // Set TFT address window to clipped image bounds
            tft.setAddrWindow(x, y, x+w-1, y+h-1);
            for (row=0; row<h; row++) { // For each scanline...
                if(flip) // Bitmap is stored bottom-to-top order (normal BMP)
                    pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
                else     // Bitmap is stored top-to-bottom
                    pos = bmpImageoffset + row * rowSize;
                if(bmpFile.position() != pos) { // Need seek?
                    bmpFile.seek(pos);
                    buffidx = sizeof(sdbuffer); // Force buffer reload
                }
                for (col=0; col<w; col++) { // For each pixel...
                // Time to read more pixel data?
                    if (buffidx >= sizeof(sdbuffer)) { // Indeed
                        bmpFile.read(sdbuffer, sizeof(sdbuffer));
                        buffidx = 0; // Set index to beginning
                    }
                    // Convert pixel from BMP to TFT format, push to display
                    b = sdbuffer[buffidx++];
                    g = sdbuffer[buffidx++];
                    r = sdbuffer[buffidx++];
                    tft.pushColor(tft.Color565(r,g,b));
                } // end pixel
            } // end scanline
        } else {
            returnCode = 4;
        }
    } else {
        returnCode = 3;
    }
} else {
    returnCode = 2;
}
bmpFile.close();
SDinUse = FALSE;    
return returnCode;

}
// read unsigned 16 bit little endian
uint16_t read16(File &f)
{
uint16_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read(); // MSB
return result;
}
// read unsigned 32 bit little endian
uint32_t read32(File &f)
{
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;
}

I use photoshop to edit images and then save as .BMP files. It works well enough for a 1.8" screen and the photon is capable enough to run a display like this via SPI.

@armor, that’s a good point, I guess that would be the easiest way to make a nice GUI. The SSD1351 has a SD card slot on the back as well. Thanks for the tip!

Hey, by the way, looking in Adafruit_mfGFX there is a drawBitmap function, the major difference is that you read the entire bitmap into memory, then draw it with that function. Just throwing that out there :smile:

@tjp One thing to consider with the OLED displays is that what I have noticed on all of them, even the Digole OLED screens is that they all eventually burn the image of what ever is shown most into the screen and that starts to look pretty funky after a little bit of time. Even my wifes Samsung S5 does this.

1 Like

@tjp, one thing to note about the GFX/mfGFX library is that the drawBitmap() function only draws in a single color. You could always write a function that reads a color bitmap from SD and draws it one pixel at a time. I used this approach in a Sharp Memory Color LCD library (but not using an SD) I wrote for @RWB. The GFX library uses the drawPixel() primitive to avoid using display hardware-specific features.

@RWB, that’s bad. That definitely makes me rethink my screen choice, since my application is basically an always on screen. That Digole IPS LCD is looking better and better! lol

@peekay123, that sounds rather uC intensive, I assume the Digole library does a better job with this?