From cb3bd4a3a98a18b6146d31aca802698b783ecd86 Mon Sep 17 00:00:00 2001 From: Nils Date: Thu, 14 Dec 2023 00:28:44 +0100 Subject: [PATCH] initial commit --- .gitignore | 3 + bmp.cpp | 46 +++++++++++++ e-paper-project.ino | 57 ++++++++++++++++ epd_spi.cpp | 155 ++++++++++++++++++++++++++++++++++++++++++++ include/bmp.h | 38 +++++++++++ include/epd_spi.h | 55 ++++++++++++++++ include/sd_card.h | 40 ++++++++++++ sd_card.cpp | 93 ++++++++++++++++++++++++++ 8 files changed, 487 insertions(+) create mode 100644 .gitignore create mode 100644 bmp.cpp create mode 100644 e-paper-project.ino create mode 100644 epd_spi.cpp create mode 100644 include/bmp.h create mode 100644 include/epd_spi.h create mode 100644 include/sd_card.h create mode 100644 sd_card.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..83bda18 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +credentials.h +*.txt +.vscode \ No newline at end of file diff --git a/bmp.cpp b/bmp.cpp new file mode 100644 index 0000000..e32dfca --- /dev/null +++ b/bmp.cpp @@ -0,0 +1,46 @@ +#include "include/bmp.h" +#include "include/sd_card.h" + +BMP::BMP(const char* filename) : + bmp_file{SD_card_fs::get_file(filename)}, + data_idx{0} +{ + + bmp_file->seek(10); // idx of 4 bytes representing start of image data + + data_idx = bmp_file->read(); + + for(uint8_t i{0}; i < 3; i++) + { + data_idx << 8; + data_idx |= bmp_file->read(); + } + + bmp_file->seek(data_idx); +} + +byte BMP::read_pixel() +{ + return bmp_file->read(); +} + +void BMP::read_pixel(const char* buffer, const uint32_t len) +{ + bmp_file->read(buffer, len); +} + +uint32_t BMP::position() const +{ + return bmp_file->position(); +} + +uint32_t BMP::available() const +{ + return bmp_file->available(); +} + +void BMP::seek(const uint32_t pos) +{ + if (pos < data_idx) bmp_file->seek(data_idx); + else bmp_file->seek(pos); +} diff --git a/e-paper-project.ino b/e-paper-project.ino new file mode 100644 index 0000000..66d5cd3 --- /dev/null +++ b/e-paper-project.ino @@ -0,0 +1,57 @@ +#include "include/sd_card.h" +#include "include/bmp.h" +#include "Arduino.h" + +/** + * @file e-paper-project.ino + * @author Nils Forssén (nilsforssen.se) + * @brief + * Project-sketch to fetch images from an web-server, process them (scaling and dithering) + * and then display them on an E-paper-display (SPI). Should switch image every 24 hours + * and go to sleep in between. Should support adding, removing and listing images on SD-card + * via a webserver as long as a physical switch is set high. When switched low, the + * device go back to the daily sleep->image->sleep cycle. + * + * @version 0.1 + * @date 2023-12-13 + * + * @copyright Copyright (c) 2023 + */ + + +/** + * Connect the SPI bus to SD-card and E-paper + * MOSI - GPIO 23 + * MISO - GPIO 19 + * SCLK - GPIO 18 + * CS - [SD to GPIO 5] or [E-paper to GPIO 17] +*/ + + +void setup(){ + Serial.begin(115200); + + if(!SD.begin(5)){ + Serial.println("Card Mount Failed"); + return; + } + uint8_t cardType = SD.cardType(); + + if(cardType == CARD_NONE){ + Serial.println("No SD card attached"); + return; + } + + Serial.printf("SD card initialized!\nSize: %lluMB\n", SD.cardSize() / (1024 * 1024)); +} + +void loop() +{ + BMP bmp_file{"/download.bmp"}; + while (true) + { + Serial.printf("Read pixel of txt file: %x\n", bmp_file.read_pixel() & 0xff); + delay(1000); + } + +} \ No newline at end of file diff --git a/epd_spi.cpp b/epd_spi.cpp new file mode 100644 index 0000000..b2bab5d --- /dev/null +++ b/epd_spi.cpp @@ -0,0 +1,155 @@ +#include "include/epd_spi.h" +#include "include/bmp.h" + +EPD_SPI_interface::EPD_SPI_interface(const epd_spi_pinout& p) : pinout{p} +{ + // init SPI pins + pinMode(pinout.pin_busy, INPUT); + pinMode(pinout.pin_rst, OUTPUT); + pinMode(pinout.pin_dc, OUTPUT); + pinMode(pinout.pin_clk, OUTPUT); + pinMode(pinout.pin_din, OUTPUT); + pinMode(pinout.pin_cs, OUTPUT); + + digitalWrite(pinout.pin_cs , HIGH); + digitalWrite(pinout.pin_clk, LOW); + + // init EPD + digitalWrite(pinout.pin_rst, HIGH); + delay(200); + digitalWrite(pinout.pin_rst, LOW); + delay(5); + digitalWrite(pinout.pin_rst, HIGH); + delay(200); + + // send init-data + waitfor_busyhigh(); + send_command(0x00); + send_byte(0xEF); + send_byte(0x08); + send_command(0x01); + send_byte(0x37); + send_byte(0x00); + send_byte(0x23); + send_byte(0x23); + send_command(0x03); + send_byte(0x00); + send_command(0x06); + send_byte(0xC7); + send_byte(0xC7); + send_byte(0x1D); + send_command(0x30); + send_byte(0x3C); + send_command(0x41); + send_byte(0x00); + send_command(0x50); + send_byte(0x37); + send_command(0x60); + send_byte(0x22); + send_command(0x61); + send_byte(0x02); + send_byte(0x58); + send_byte(0x01); + send_byte(0xC0); + send_command(0xE3); + send_byte(0xAA); + delay(100); + send_command(0x50); + send_byte(0x37); + send_command(0x61); + send_byte(0x02); + send_byte(0x58); + send_byte(0x01); + send_byte(0xC0); + send_command(0x10); +} + +void EPD_SPI_interface::send_byte(byte data) +{ + digitalWrite(pinout.pin_dc, HIGH); + send_spi_data(data); +} + +void EPD_SPI_interface::send_command(byte command) +{ + digitalWrite(pinout.pin_dc, LOW); + send_spi_data(command); +} + +void EPD_SPI_interface::show() +{ + send_command(0x04); + waitfor_busyhigh(); + send_command(0x12); + waitfor_busyhigh(); + send_command(0x02); + waitfor_busylow(); + delay(200); +} + +void EPD_SPI_interface::sleep() +{ + delay(100); + send_command(0x07); + send_byte(0xA5); + delay(100); + digitalWrite(pinout.pin_rst, LOW); +} + +void EPD_SPI_interface::send_spi_data(byte data) +{ + digitalWrite(pinout.pin_cs, LOW); + + for (int i = 0; i < 8; i++) + { + if ((data & 0x80)) + { + digitalWrite(pinout.pin_din, HIGH); + } + else + { + digitalWrite(pinout.pin_din, LOW); + } + + data <<= 1; + digitalWrite(pinout.pin_clk, HIGH); + digitalWrite(pinout.pin_clk, LOW); + } + + digitalWrite(pinout.pin_cs, HIGH); +} + +void EPD_SPI_interface::waitfor_busyhigh() const +{ + while(!(digitalRead(pinout.pin_busy))); +} + +void EPD_SPI_interface::waitfor_busylow() const +{ + while(digitalRead(pinout.pin_busy)); +} + +void EPD_SPI_interface::display(std::unique_ptr bmp) +{ + bmp->seek(0); + for (uint32_t i{0}; i < EPD_HEIGHT; i++) + { + for (uint32_t j{0}; j < EPD_WIDTH / 2; i++) // Every byte is 2 pixels + { + send_byte(bmp->read_pixel()); + } + } + show(); +} + +void EPD_SPI_interface::fill(color c) +{ + for (uint32_t i{0}; i < EPD_HEIGHT; i++) + { + for (uint32_t j{0}; j < EPD_WIDTH / 2; i++) // Every byte is 2 pixels + { + send_byte((c << 4)|c); + } + } + show(); +} \ No newline at end of file diff --git a/include/bmp.h b/include/bmp.h new file mode 100644 index 0000000..bcb048c --- /dev/null +++ b/include/bmp.h @@ -0,0 +1,38 @@ +#ifndef BMP_H_ +#define BMP_H_ + +/** + * @file sd_card.h + * @author Nils Forssén (nilsforssen.se) + * @brief + * Useful filesystem functions for interacting with an SD-card + * + * @version 0.1 + * @date 2023-12-13 + * + * @copyright Copyright (c) 2023 + */ + +#include +#include + +class BMP +{ +public: + BMP(const char* filename); + ~BMP() = default; // Smart pointer closes itself! + + byte read_pixel(); + void read_pixel(const char* buffer, const uint32_t len); + uint32_t position() const; + uint32_t available() const; + void seek(const uint32_t pos); + +protected: +private: + std::unique_ptr bmp_file; + uint32_t data_idx; +}; + + +#endif \ No newline at end of file diff --git a/include/epd_spi.h b/include/epd_spi.h new file mode 100644 index 0000000..d19be33 --- /dev/null +++ b/include/epd_spi.h @@ -0,0 +1,55 @@ +#ifndef SIMPLE_SPI_H_ +#define SIMPLE_SPI_H_ + +#include +#include + +#define EPD_WIDTH 600 +#define EPD_HEIGHT 448 + +enum color: byte +{ + EPD_5IN65F_BLACK, /// 000 + EPD_5IN65F_WHITE, /// 001 + EPD_5IN65F_GREEN, /// 010 + EPD_5IN65F_BLUE, /// 011 + EPD_5IN65F_RED, /// 100 + EPD_5IN65F_YELLOW, /// 101 + EPD_5IN65F_ORANGE /// 110 +}; + +struct epd_spi_pinout +{ + const uint8_t pin_busy; + const uint8_t pin_rst; + const uint8_t pin_dc; + const uint8_t pin_cs; + const uint8_t pin_clk; + const uint8_t pin_din; +}; + +class BMP; + +class EPD_SPI_interface +{ +public: + EPD_SPI_interface(const epd_spi_pinout& p); + ~EPD_SPI_interface() = default; + + void send_byte(byte data); + void send_command(byte command); + void display(std::unique_ptr bmp); + void fill(color c); + + void show(); + void sleep(); +protected: +private: + void send_spi_data(byte data); + void waitfor_busyhigh() const; + void waitfor_busylow() const; + + const epd_spi_pinout pinout; +}; + +#endif diff --git a/include/sd_card.h b/include/sd_card.h new file mode 100644 index 0000000..5677c3c --- /dev/null +++ b/include/sd_card.h @@ -0,0 +1,40 @@ +#ifndef SD_CARD_H_ +#define SD_CARD_H_ + +/** + * @file sd_card.h + * @author Nils Forssén (nilsforssen.se) + * @brief + * Useful filesystem functions for interacting with an SD-card + * + * @version 0.1 + * @date 2023-12-13 + * + * @copyright Copyright (c) 2023 + */ + +#include +#include + +// #include "SPI.h" + +typedef enum +{ + FS_OK = 0, + FS_FAIL = 1, + FS_WARNING = 2, +} fs_error_t; + +namespace SD_card_fs { + + fs_error_t create_dir(const char* path); + fs_error_t remove_dir(const char* path); + std::unique_ptr get_file(const char* path); + fs_error_t write_file(const char* path, const char* data); // Make sure data is null-terminated + fs_error_t append_file(const char* path, const char* data); // Make sure data is null-terminated + fs_error_t rename_file(const char* path1, const char* path2); + fs_error_t delete_file(const char* path); +}; + + +#endif \ No newline at end of file diff --git a/sd_card.cpp b/sd_card.cpp new file mode 100644 index 0000000..e1b292d --- /dev/null +++ b/sd_card.cpp @@ -0,0 +1,93 @@ +#include "include/sd_card.h" + +namespace SD_card_fs +{ + fs_error_t create_dir(const char* path) + { + if(SD.mkdir(path)){ + Serial.printf("Created directory: %s\n", path); + return FS_OK; + } else { + Serial.printf("failed to create directory: %s\n", path); + return FS_FAIL; + } + } + + fs_error_t remove_dir(const char* path) + { + if(SD.rmdir(path)){ + Serial.printf("Removed directory: %s\n", path); + return FS_OK; + } else { + Serial.printf("failed to remove directory: %s\n", path); + return FS_FAIL; + } + } + + std::unique_ptr get_file(const char* path) + { + File* file = new File{SD.open(path)}; + if(!(*file)){ + Serial.printf("failed to open file: %s\n", path); + return nullptr; + } + return std::unique_ptr{file}; + } + + fs_error_t write_file(const char* path, const char* data) + { + File file = SD.open(path, FILE_WRITE); + if(!file){ + Serial.printf("failed to open file: %s\n", path); + return FS_FAIL; + } + + if(file.print(data)){ + Serial.printf("File written: %s\n", path); + file.close(); + return FS_OK; + } else { + Serial.printf("Write failed: %s\n", path); + file.close(); + return FS_FAIL; + } + } + + fs_error_t append_file(const char* path, const char* data){ + File file = SD.open(path, FILE_APPEND); + if(!file){ + Serial.printf("failed to open file for appending: %s\n", path); + return FS_FAIL; + } + + if(file.print(data)){ + Serial.printf("Message appended: %s\n", path); + file.close(); + return FS_OK; + } else { + Serial.printf("Append failed: %s\n", path); + file.close(); + return FS_FAIL; + } + } + + fs_error_t rename_file(const char* path1, const char* path2){ + if (SD.rename(path1, path2)) { + Serial.printf("File renamed: %s to %s\n", path1, path2); + return FS_OK; + } else { + Serial.printf("Rename failed: %s to %s\n", path1, path2); + return FS_FAIL; + } + } + + fs_error_t delete_file(const char* path){ + if(SD.remove(path)){ + Serial.printf("File deleted: %s\n", path); + return FS_OK; + } else { + Serial.printf("Delete failed: %s\n", path); + return FS_FAIL; + } + } +} \ No newline at end of file -- 2.30.2