--- /dev/null
+credentials.h
+*.txt
+.vscode
\ No newline at end of file
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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)
+{
+ 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
--- /dev/null
+#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 <SD.h>
+#include <string>
+
+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<File> bmp_file;
+ uint32_t data_idx;
+};
+
+
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef SIMPLE_SPI_H_
+#define SIMPLE_SPI_H_
+
+#include <Arduino.h>
+#include <memory>
+
+#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> 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
--- /dev/null
+#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 <memory>
+#include <SD.h>
+
+// #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<File> 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
--- /dev/null
+#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<File> 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>{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