Skip to content

Commit

Permalink
Waveshare 6-color Spectra e-Paper displays (#118)
Browse files Browse the repository at this point in the history
  • Loading branch information
mariusandra authored Jan 15, 2025
1 parent 14f615d commit fa2e102
Show file tree
Hide file tree
Showing 76 changed files with 7,275 additions and 1,004 deletions.
11 changes: 9 additions & 2 deletions backend/app/drivers/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,20 @@ def drivers_for_device(device: str) -> dict[str, Driver]:
if waveshare.variant not in get_variant_keys():
raise Exception(f"Unknown waveshare driver variant {waveshare.variant}")

if waveshare.variant in ("EPD_12in48", "EPD_12in48b", "EPD_12in48b_V2"):
if waveshare.variant in ("EPD_12in48", "EPD_12in48b", "EPD_12in48b_V2", "EPD_13in3e"):
device_drivers = {"waveshare": waveshare, "noSpi": DRIVERS["noSpi"]}
else:
device_drivers = {"waveshare": waveshare, "spi": DRIVERS["spi"]}

if waveshare.variant == "EPD_13in3e":
device_drivers["bootconfig"] = DRIVERS["bootConfig"]
device_drivers["bootconfig"].lines = [
"gpio=7=op,dl",
"gpio=8=op,dl",
]

# Always enable evdev if not eink
if device != "pimoroni.inky_imporession" and not device.startswith("waveshare."):
if device != "pimoroni.inky_impression" and not device.startswith("waveshare."):
device_drivers['evdev'] = DRIVERS['evdev']

return device_drivers
5 changes: 5 additions & 0 deletions backend/app/drivers/drivers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class Driver:
can_render: bool = False # add render(image)
can_png: bool = False # add toPng(rotate)
can_turn_on_off: bool = False # add turnOn() and turnOff()
lines: list[str] = None # extra config lines for drivers

DRIVERS = {
"inkyPython": Driver(
Expand Down Expand Up @@ -54,4 +55,8 @@ class Driver:
"i2c": Driver( # enables i2c on deploy
name="i2c",
),
"bootConfig": Driver( # assures lines in /boot/firmware/config.txt
name="bootConfig",
lines=[],
)
}
22 changes: 18 additions & 4 deletions backend/app/drivers/waveshare.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,19 @@ class WaveshareVariant:
display_function: Optional[str] = None
display_arguments: Optional[list[str]] = None
init_returns_zero: bool = False
color_option: Literal["Unknown", "Black", "BlackWhiteRed", "BlackWhiteYellow", "FourGray", "SevenColor", "BlackWhiteYellowRed"] = "Unknown"
color_option: Literal["Unknown", "Black", "BlackWhiteRed", "BlackWhiteYellow", "FourGray", "SpectraSixColor", "SevenColor", "BlackWhiteYellowRed"] = "Unknown"

# Colors if we can't autodetect
VARIANT_COLORS = {
"EPD_1in64g": "BlackWhiteYellowRed",
"EPD_2in13g": "BlackWhiteYellowRed",
"EPD_2in13g_V2": "BlackWhiteYellowRed",
"EPD_2in15g": "BlackWhiteYellowRed",
"EPD_2in36g": "BlackWhiteYellowRed",
"EPD_2in66g": "BlackWhiteYellowRed",
"EPD_2in13g": "BlackWhiteYellowRed",
"EPD_3in0g": "BlackWhiteYellowRed",
"EPD_4in37g": "BlackWhiteYellowRed",
"EPD_5in79g": "BlackWhiteYellowRed",
"EPD_7in3g": "BlackWhiteYellowRed",

"EPD_1in02d": "Black",
Expand Down Expand Up @@ -59,6 +62,10 @@ class WaveshareVariant:
"EPD_4in01f": "SevenColor",
"EPD_7in3f": "SevenColor",
"EPD_5in65f": "SevenColor",

"EPD_4in0e": "SpectraSixColor",
"EPD_7in3e": "SpectraSixColor",
"EPD_13in3e": "SpectraSixColor",
}

def get_variant_keys_for(folder: str) -> list[str]:
Expand All @@ -70,10 +77,15 @@ def get_variant_keys_for(folder: str) -> list[str]:
]

def get_variant_keys() -> list[str]:
return [*get_variant_keys_for("ePaper"), *get_variant_keys_for("epd12in48")]
return [*get_variant_keys_for("ePaper"), *get_variant_keys_for("epd12in48"), *get_variant_keys_for("epd13in3e")]

def get_variant_folder(variant_key: str) -> str:
return "ePaper" if variant_key in get_variant_keys_for("ePaper") else "epd12in48"
if variant_key in get_variant_keys_for("ePaper") :
return "ePaper"
elif variant_key == "EPD_13in3e":
return "epd13in3e"
else:
return "epd12in48"

def get_proc_arguments(line: str, variant_key: str) -> list[str]:
unknown_color = "FourGray" if "4Gray" in line else "Unknown"
Expand Down Expand Up @@ -183,6 +195,8 @@ def convert_waveshare_source(variant_key: Optional[str]) -> WaveshareVariant:
variant.color_option = "FourGray"
elif variant.display_arguments == ["SevenColor"]:
variant.color_option = "SevenColor"
elif variant.display_arguments == ["SpectraSixColor"]:
variant.color_option = "SpectraSixColor"
else:
print(f"Unknown color: {variant_key} - {variant.display_function} -- {variant.display_arguments}" )

Expand Down
44 changes: 35 additions & 9 deletions backend/app/tasks/deploy_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,11 +251,15 @@ async def install_if_necessary(pkg: str, raise_on_error=True) -> int:
"ls -dt1 release_* | grep -v \"$(basename $(readlink ../current))\" "
"| tail -n +11 | xargs rm -rf")

boot_config = "/boot/config.txt"
if await exec_command(db, redis, frame, ssh, "test -f /boot/firmware/config.txt", raise_on_error=False) == 0:
boot_config = "/boot/firmware/config.txt"

# Additional device config
if drivers.get("i2c"):
await exec_command(db, redis, frame, ssh,
'grep -q "^dtparam=i2c_vc=on$" /boot/config.txt '
'|| echo "dtparam=i2c_vc=on" | sudo tee -a /boot/config.txt')
'grep -q "^dtparam=i2c_vc=on$" ' + boot_config + ' '
'|| echo "dtparam=i2c_vc=on" | sudo tee -a ' + boot_config)
await exec_command(db, redis, frame, ssh,
'command -v raspi-config > /dev/null && '
'sudo raspi-config nonint get_i2c | grep -q "1" && { '
Expand Down Expand Up @@ -286,16 +290,29 @@ async def install_if_necessary(pkg: str, raise_on_error=True) -> int:
else:
await exec_command(db, redis, frame, ssh, "sudo rm -f /etc/cron.d/frameos-reboot")

# restart frame
if drivers.get("bootconfig"):
must_reboot = False
for line in drivers["bootconfig"].lines:
if await exec_command(db, redis, frame, ssh,
f'grep -q "^{line}" ' + boot_config, raise_on_error=False) != 0:
await exec_command(db, redis, frame, ssh, command=f'echo "{line}" | sudo tee -a ' + boot_config, log_output=False)
must_reboot = True

await exec_command(db, redis, frame, ssh, "sudo systemctl daemon-reload")
await exec_command(db, redis, frame, ssh, "sudo systemctl enable frameos.service")
await exec_command(db, redis, frame, ssh, "sudo systemctl restart frameos.service")
await exec_command(db, redis, frame, ssh, "sudo systemctl status frameos.service")

frame.status = 'starting'
frame.last_successful_deploy = frame_dict
frame.last_successful_deploy_at = datetime.now(timezone.utc)
await update_frame(db, redis, frame)

if must_reboot:
await update_frame(db, redis, frame)
await log(db, redis, int(frame.id), "stdinfo", "Deployed! Rebooting device after boot config changes")
await exec_command(db, redis, frame, ssh, "sudo reboot")
else:
await exec_command(db, redis, frame, ssh, "sudo systemctl restart frameos.service")
await exec_command(db, redis, frame, ssh, "sudo systemctl status frameos.service")
await update_frame(db, redis, frame)

except Exception as e:
await log(db, redis, id, "stderr", str(e))
Expand Down Expand Up @@ -353,15 +370,24 @@ async def make_local_modifications(db: Session, redis: Redis,
raise

with open(os.path.join(source_dir, "src", "scenes", "scenes.nim"), "w") as f:
f.write(write_scenes_nim(frame))
source = write_scenes_nim(frame)
f.write(source)
if frame.debug:
await log(db, redis, int(frame.id), "stdout", f"Generated scenes.nim:\n{source}")

drivers = drivers_for_device(frame.device)
with open(os.path.join(source_dir, "src", "drivers", "drivers.nim"), "w") as f:
f.write(write_drivers_nim(drivers))
source = write_drivers_nim(drivers)
f.write(source)
if frame.debug:
await log(db, redis, int(frame.id), "stdout", f"Generated drivers.nim:\n{source}")

if drivers.get("waveshare"):
with open(os.path.join(source_dir, "src", "drivers", "waveshare", "driver.nim"), "w") as wf:
wf.write(write_waveshare_driver_nim(drivers))
source = write_waveshare_driver_nim(drivers)
wf.write(source)
if frame.debug:
await log(db, redis, int(frame.id), "stdout", f"Generated waveshare driver:\n{source}")


def compile_line_md5(input_str: str) -> str:
Expand Down
1 change: 1 addition & 0 deletions backend/list_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"BlackWhiteYellowRed": "Black/White/Yellow/Red",
"FourGray": "4 Grayscale",
"SevenColor": "7 Color",
"SpectraSixColor": "Spectra 6 Color",
}.get(v.color_option, v.color_option)
code = "" if v.code == "" else f" ({v.code.upper()})"
output = {
Expand Down
4 changes: 3 additions & 1 deletion frameos/src/drivers/waveshare/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ This folder is synced from https://github.com/waveshareteam/e-Paper/blob/master/
To update:

1. Copy new C sources (`EPD_*` files) to `frameos/src/drivers/waveshare/ePaper`
- Rename `EPD_7in5b_V2_old.*` as `EPD_7in5b_V2.*`
- Rename `EPD_7in5b_V2.*` to `EPD_7in5b_V2_gray.*`
2. Run `cd frameos/src/drivers/waveshare/ePaper && make` to generate new `.nim` files
3. Run `cd backend && python3 list_devices.py` and verify the driver is listed with the right resolution and color
4. If not, you might need to edit the auto-detection routine in `convert_waveshare_source` in `backend/app/drivers/waveshare.py`.
5. If the color remains `Unknown` and the `Display` function takes just one parameter, update the `VARIANT_COLORS` dictionary with the right color.
6. Finally, copy the output of `list_devices.py` into `frontend/src/devices.ts`
6. Finally, copy the output of `list_devices.py` into `frontend/src/devices.ts`
70 changes: 65 additions & 5 deletions frameos/src/drivers/waveshare/ePaper/DEV_Config.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
#
******************************************************************************/
#include "DEV_Config.h"
#include "Debug.h"

int GPIO_Handle;
int SPI_Handle;
Expand All @@ -41,6 +40,8 @@ int EPD_DC_PIN;
int EPD_CS_PIN;
int EPD_BUSY_PIN;
int EPD_PWR_PIN;
int EPD_MOSI_PIN;
int EPD_SCLK_PIN;

/**
* GPIO read and write
Expand Down Expand Up @@ -110,7 +111,6 @@ static int DEV_Equipment_Testing(void)
fclose(fp);

printf("Current environment: ");

char systems[][9] = {"Raspbian", "Debian", "NixOS"};
int detected = 0;
for(int i=0; i<3; i++) {
Expand All @@ -125,7 +125,6 @@ static int DEV_Equipment_Testing(void)
printf("Perhaps you meant to 'make JETSON' instead?\n");
return -1;
}

return 0;
}

Expand All @@ -136,17 +135,80 @@ void DEV_GPIO_Init(void)
EPD_CS_PIN = 8;
EPD_PWR_PIN = 18;
EPD_BUSY_PIN = 24;
EPD_MOSI_PIN = 10;
EPD_SCLK_PIN = 11;

DEV_GPIO_Mode(EPD_BUSY_PIN, 0);
DEV_GPIO_Mode(EPD_RST_PIN, 1);
DEV_GPIO_Mode(EPD_DC_PIN, 1);
DEV_GPIO_Mode(EPD_CS_PIN, 1);
DEV_GPIO_Mode(EPD_PWR_PIN, 1);
// DEV_GPIO_Mode(EPD_MOSI_PIN, 0);
// DEV_GPIO_Mode(EPD_SCLK_PIN, 1);

DEV_Digital_Write(EPD_CS_PIN, 1);
DEV_Digital_Write(EPD_PWR_PIN, 1);

}

void DEV_SPI_SendnData(UBYTE *Reg)
{
UDOUBLE size;
size = sizeof(Reg);
for(UDOUBLE i=0 ; i<size ; i++)
{
DEV_SPI_SendData(Reg[i]);
}
}

void DEV_SPI_SendData(UBYTE Reg)
{
UBYTE i,j=Reg;
DEV_GPIO_Mode(EPD_MOSI_PIN, 1);
DEV_Digital_Write(EPD_CS_PIN, 0);
for(i = 0; i<8; i++)
{
DEV_Digital_Write(EPD_SCLK_PIN, 0);
if (j & 0x80)
{
DEV_Digital_Write(EPD_MOSI_PIN, 1);
}
else
{
DEV_Digital_Write(EPD_MOSI_PIN, 0);
}

DEV_Digital_Write(EPD_SCLK_PIN, 1);
j = j << 1;
}
DEV_Digital_Write(EPD_SCLK_PIN, 0);
DEV_Digital_Write(EPD_CS_PIN, 1);
}

UBYTE DEV_SPI_ReadData()
{
UBYTE i,j=0xff;
DEV_GPIO_Mode(EPD_MOSI_PIN, 0);
DEV_Digital_Write(EPD_CS_PIN, 0);
for(i = 0; i<8; i++)
{
DEV_Digital_Write(EPD_SCLK_PIN, 0);
j = j << 1;
if (DEV_Digital_Read(EPD_MOSI_PIN))
{
j = j | 0x01;
}
else
{
j= j & 0xfe;
}
DEV_Digital_Write(EPD_SCLK_PIN, 1);
}
DEV_Digital_Write(EPD_SCLK_PIN, 0);
DEV_Digital_Write(EPD_CS_PIN, 1);
return j;
}

/******************************************************************************
function: Module Initialize, the library and initialize the pins, SPI protocol
parameter:
Expand All @@ -157,10 +219,8 @@ UBYTE DEV_Module_Init(void)
if(DEV_Equipment_Testing() < 0) {
return 1;
}

char buffer[NUM_MAXBUF];
FILE *fp;

fp = popen("cat /proc/cpuinfo | grep 'Raspberry Pi 5'", "r");
if (fp == NULL) {
Debug("It is not possible to determine the model of the Raspberry PI\n");
Expand Down
6 changes: 6 additions & 0 deletions frameos/src/drivers/waveshare/ePaper/DEV_Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ extern int EPD_DC_PIN;
extern int EPD_CS_PIN;
extern int EPD_BUSY_PIN;
extern int EPD_PWR_PIN;
extern int EPD_MOSI_PIN;
extern int EPD_SCLK_PIN;

/*------------------------------------------------------------------------------------------------------*/
void DEV_Digital_Write(UWORD Pin, UBYTE Value);
Expand All @@ -84,6 +86,10 @@ void DEV_SPI_WriteByte(UBYTE Value);
void DEV_SPI_Write_nByte(uint8_t *pData, uint32_t Len);
void DEV_Delay_ms(UDOUBLE xms);

void DEV_SPI_SendData(UBYTE Reg);
void DEV_SPI_SendnData(UBYTE *Reg);
UBYTE DEV_SPI_ReadData();

UBYTE DEV_Module_Init(void);
void DEV_Module_Exit(void);

Expand Down
Loading

0 comments on commit fa2e102

Please sign in to comment.