Skip to content

Commit

Permalink
add led and wakeup support
Browse files Browse the repository at this point in the history
  • Loading branch information
tsl0922 committed Nov 16, 2024
1 parent 107f639 commit 0cfacae
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 55 deletions.
69 changes: 55 additions & 14 deletions EPD/EPD_ble.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
#include <string.h>
#include "nordic_common.h"
#include "ble_srv_common.h"
#include "nrf_delay.h"
#include "nrf_gpio.h"
#include "nrf_nvmc.h"
#include "nrf_soc.h"
#include "nrf_log.h"
#include "EPD_4in2.h"
#include "EPD_4in2_V2.h"
Expand All @@ -26,6 +29,7 @@
#define BLE_UUID_EPD_CHARACTERISTIC 0x0002

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#define EPD_CONFIG_SIZE (sizeof(epd_config_t) / sizeof(uint8_t))

/** EPD drivers */
static epd_driver_t epd_drivers[] = {
Expand Down Expand Up @@ -60,7 +64,7 @@ static void epd_config_load(epd_config_t *cfg)
static void epd_config_save(epd_config_t *cfg)
{
nrf_nvmc_page_erase(BLE_EPD_CONFIG_ADDR);
nrf_nvmc_write_bytes(BLE_EPD_CONFIG_ADDR, (uint8_t*)cfg, sizeof(epd_config_t) / sizeof(uint8_t));
nrf_nvmc_write_bytes(BLE_EPD_CONFIG_ADDR, (uint8_t*)cfg, EPD_CONFIG_SIZE);
}

/**@brief Function for handling the @ref BLE_GAP_EVT_CONNECTED event from the S110 SoftDevice.
Expand Down Expand Up @@ -155,6 +159,21 @@ static void epd_service_process(ble_epd_t * p_epd, uint8_t * p_data, uint16_t le
DEV_Delay_ms(200);
break;

case EPD_CMD_SET_CONFIG:
if (length < 2) return;
memcpy(&p_epd->config, &p_data[1], (length - 1 > EPD_CONFIG_SIZE) ? EPD_CONFIG_SIZE : length - 1);
epd_config_save(&p_epd->config);
break;

case EPD_CMD_SYS_RESET:
NVIC_SystemReset();
break;

case EPD_CMD_SYS_SLEEP:
ble_epd_sleep_prepare(p_epd);
sd_power_system_off();
break;

default:
break;
}
Expand Down Expand Up @@ -206,10 +225,12 @@ void ble_epd_on_ble_evt(ble_epd_t * p_epd, ble_evt_t * p_ble_evt)
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
nrf_gpio_pin_toggle(p_epd->config.led_pin);
on_connect(p_epd, p_ble_evt);
break;

case BLE_GAP_EVT_DISCONNECTED:
nrf_gpio_pin_toggle(p_epd->config.led_pin);
on_disconnect(p_epd, p_ble_evt);
break;

Expand Down Expand Up @@ -285,19 +306,8 @@ static uint32_t epd_service_init(ble_epd_t * p_epd)
return err_code;
}

uint32_t ble_epd_init(ble_epd_t * p_epd)
static void epd_config_init(ble_epd_t * p_epd)
{
if (p_epd == NULL)
{
return NRF_ERROR_NULL;
}

// Initialize the service structure.
p_epd->conn_handle = BLE_CONN_HANDLE_INVALID;
p_epd->is_notification_enabled = false;

// Load epd config
epd_config_load(&p_epd->config);
bool save_config = false;
if (p_epd->config.mosi_pin == 0xFF && p_epd->config.sclk_pin == 0xFF &&
p_epd->config.cs_pin == 0xFF && p_epd->config.dc_pin == 0xFF &&
Expand Down Expand Up @@ -330,9 +340,40 @@ uint32_t ble_epd_init(ble_epd_t * p_epd)
p_epd->config.driver_id = p_epd->driver->id;
save_config = true;
}
if (save_config) {
if (save_config)
{
epd_config_save(&p_epd->config);
}
}

void ble_epd_sleep_prepare(ble_epd_t * p_epd)
{
// Turn off led
nrf_gpio_pin_set(p_epd->config.led_pin);
// Prepare wakeup pin
nrf_gpio_cfg_sense_input(p_epd->config.wakeup_pin, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_HIGH);
}

uint32_t ble_epd_init(ble_epd_t * p_epd)
{
if (p_epd == NULL)
{
return NRF_ERROR_NULL;
}

// Initialize the service structure.
p_epd->conn_handle = BLE_CONN_HANDLE_INVALID;
p_epd->is_notification_enabled = false;

// Load epd config
epd_config_load(&p_epd->config);
epd_config_init(p_epd);

// Init led pin
nrf_gpio_cfg_output(p_epd->config.led_pin);
nrf_gpio_pin_clear(p_epd->config.led_pin);
nrf_delay_ms(50);
nrf_gpio_pin_set(p_epd->config.led_pin);

// Add the service.
return epd_service_init(p_epd);
Expand Down
26 changes: 19 additions & 7 deletions EPD/EPD_ble.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,24 @@ typedef struct
uint8_t busy_pin;
uint8_t bs_pin;
uint8_t driver_id;
uint8_t wakeup_pin;
uint8_t led_pin;
} epd_config_t;

/**< EPD Service command IDs. */
enum EPD_CMDS
{
EPD_CMD_SET_PINS,
EPD_CMD_INIT,
EPD_CMD_CLEAR,
EPD_CMD_SEND_COMMAND,
EPD_CMD_SEND_DATA,
EPD_CMD_DISPLAY,
EPD_CMD_SLEEP,
EPD_CMD_SET_PINS, /**< set EPD pin mapping. */
EPD_CMD_INIT, /**< init EPD display driver */
EPD_CMD_CLEAR, /**< clear EPD screen */
EPD_CMD_SEND_COMMAND, /**< send command to EPD */
EPD_CMD_SEND_DATA, /**< send data to EPD */
EPD_CMD_DISPLAY, /**< diaplay EPD ram on screen */
EPD_CMD_SLEEP, /**< EPD enter sleep mode */

EPD_CMD_SET_CONFIG = 0x90, /**< set full EPD config */
EPD_CMD_SYS_RESET = 0x91, /**< MCU reset */
EPD_CMD_SYS_SLEEP = 0x92, /**< MCU enter sleep mode */
};

/**< EPD driver IDs. */
Expand Down Expand Up @@ -86,6 +92,12 @@ typedef struct
epd_config_t config; /**< EPD config */
} ble_epd_t;

/**@brief Function for preparing sleep mode.
*
* @param[in] p_epd EPD Service structure.
*/
void ble_epd_sleep_prepare(ble_epd_t * p_epd);

/**@brief Function for initializing the EPD Service.
*
* @param[out] p_epd EPD Service structure. This structure must be supplied
Expand Down
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

4.2 寸电子墨水屏固件,带有一个[网页版上位机](https://tsl0922.github.io/EPD-nRF51/),可以通过蓝牙传输图像到墨水屏。

理论上支持所有 nRF51 系列 MCU,内置 3 个微雪 4.2 寸墨水屏驱动(可切换),同时还支持自定义墨水屏到 MCU 的引脚映射。
理论上支持所有 nRF51 系列 MCU,内置 3 个微雪 4.2 寸墨水屏驱动(可切换),同时还支持自定义墨水屏到 MCU 的引脚映射,支持睡眠唤醒(NFC / 无线充电器)

## 支持设备

Expand All @@ -14,7 +14,8 @@
ROM:128K

驱动:EPD_4in2
引脚映射:0508090A0B0C0D
屏幕引脚:0508090A0B0C0D
线圈引脚:07
```

![](html/images/1.jpg)
Expand All @@ -27,9 +28,13 @@
ROM:256K

驱动:EPD_4in2b_V2
引脚映射:0A0B0C0D0E0F10
屏幕引脚:0A0B0C0D0E0F10
线圈引脚:09
LED 引脚:05
```

![](html/images/2.jpg)

默认驱动和引脚映射为黑白双色版本,其它版本需要切换驱动并修改引脚映射。

## 上位机
Expand Down
Binary file modified html/images/1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added html/images/2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 22 additions & 8 deletions html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
<body>
<div class="main">
<h3>4.2 寸电子墨水屏蓝牙控制器(nRF51)</h3>
<a href="https://github.com/tsl0922/EPD-nRF51" class="github-corner" aria-label="View source on Github"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#ddd; color:#151513; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
<fieldset>
<legend>蓝牙</legend>
<div style="float: right;">
Expand Down Expand Up @@ -90,21 +89,36 @@ <h3>4.2 寸电子墨水屏蓝牙控制器(nRF51)</h3>
<li>
<b>指令列表(指令和参数全部要使用十六进制):</b>
<ul>
<li><code>00</code>+<code>引脚配置</code>: 设置引脚映射(见上面引脚配置)</li>
<li><code>01</code>+<code>驱动 ID</code>: 驱动初始化(支持的驱动 ID: <code>01</code>/<code>02</code>/<code>03</code></li>
<li><code>02</code>: 清空屏幕(把屏幕刷为白色)</li>
<li><code>03</code>+<code>命令</code>: 发送命令到屏幕(请参考屏幕主控手册)</li>
<li><code>04</code>+<code>数据</code>: 写入数据到屏幕内存(同上)</li>
<li><code>05</code>: 刷新屏幕(显示已写入屏幕内存的数据)</li>
<li><code>06</code>: 让屏幕进入睡眠状态</li>
<li>驱动相关:
<ul>
<li><code>00</code>+<code>引脚配置</code>: 设置引脚映射(见上面引脚配置)</li>
<li><code>01</code>+<code>驱动 ID</code>: 驱动初始化(支持的驱动 ID: <code>01</code>/<code>02</code>/<code>03</code></li>
<li><code>02</code>: 清空屏幕(把屏幕刷为白色)</li>
<li><code>03</code>+<code>命令</code>: 发送命令到屏幕(请参考屏幕主控手册)</li>
<li><code>04</code>+<code>数据</code>: 写入数据到屏幕内存(同上)</li>
<li><code>05</code>: 刷新屏幕(显示已写入屏幕内存的数据)</li>
<li><code>06</code>: 屏幕睡眠</li>
</ul>
</li>
<li>系统相关:
<ul>
<li><code>90</code>+<code>配置</code>: 写入配置信息(重启生效,格式参考源码 <code>epd_config_t</code></li>
<li><code>91</code>: 系统重启</li>
<li><code>92</code>: 系统睡眠</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>
系统睡眠后可通过线圈(NFC/无线充电器)唤醒(需正确配置线圈对应的引脚才有效),否则一旦系统睡眠只有重新上电才能开启蓝牙。如果价签上带有 LED,系统启动时 LED 会闪一下(需正确配置 LED 对应的引脚才有效),以便知道系统是否已经被线圈唤醒。
</p>
<p style="color: #666;">
<strong>致谢:</strong>屏幕驱动代码来自微雪 <a href="https://www.waveshare.net/wiki/E-Paper_Shield" target="_blank">E-Paper Shield</a>,本网页代码最初基于 <a href="https://github.com/atc1441/ATC_TLSR_Paper" target="_blank">atc1441/ATC_TLSR_Paper</a> 项目的网页控制端代码修改而来。
</p>
</fieldset>
</div>
<a href="https://github.com/tsl0922/EPD-nRF51" class="github-corner" aria-label="View source on Github"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#ddd; color:#151513; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
<script type="text/javascript" src="js/dithering.js"></script>
<script type="text/javascript" src="js/main.js"></script>
</body>
Expand Down
49 changes: 26 additions & 23 deletions html/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,20 @@ async function sendCommand(cmd) {
}

async function sendcmd(cmdTXT) {
let cmd = hexToBytes(cmdTXT);
addLog(`发送命令: ${cmdTXT}`);
await sendCommand(cmd);
await sendCommand(hexToBytes(cmdTXT));
}

async function setDriver() {
let epdDriver = document.getElementById("epddriver").value;
let pins = document.getElementById("epdpins").value;
const epdDriver = document.getElementById("epddriver").value;
const pins = document.getElementById("epdpins").value;
await sendcmd("00" + pins);
await sendcmd("01" + epdDriver);
await sendcmd("06");
}

async function clearscreen() {
if(confirm('确认清除屏幕内容?')) {
await sendcmd("01");
await sendcmd("02");
await sendcmd("06");
}
}

Expand All @@ -76,11 +72,10 @@ async function sendIMGArray(imgArray, type = 'bw'){

async function sendimg(cmdIMG) {
startTime = new Date().getTime();
let epdDriver = document.getElementById("epddriver").value;
let imgArray = cmdIMG.replace(/(?:\r\n|\r|\n|,|0x| )/g, '');
const epdDriver = document.getElementById("epddriver").value;
const imgArray = cmdIMG.replace(/(?:\r\n|\r|\n|,|0x| )/g, '');
const bwArrLen = (canvas.width/8) * canvas.height * 2;

await sendcmd("01");
if (imgArray.length == bwArrLen * 2) {
await sendcmd("0310");
await sendIMGArray(imgArray.slice(0, bwArrLen - 1));
Expand All @@ -92,16 +87,14 @@ async function sendimg(cmdIMG) {
}
await sendcmd("05");

let sendTime = (new Date().getTime() - startTime) / 1000.0;
const sendTime = (new Date().getTime() - startTime) / 1000.0;
addLog(`发送完成!耗时: ${sendTime}s`);
setStatus(`发送完成!耗时: ${sendTime}s`);

await sendcmd("06");
}

function updateButtonStatus() {
let connected = gattServer != null && gattServer.connected;
let status = connected ? null : 'disabled';
const connected = gattServer != null && gattServer.connected;
const status = connected ? null : 'disabled';
document.getElementById("sendcmdbutton").disabled = status;
document.getElementById("clearscreenbutton").disabled = status;
document.getElementById("sendimgbutton").disabled = status;
Expand All @@ -117,8 +110,10 @@ function disconnect() {

async function preConnect() {
if (gattServer != null && gattServer.connected) {
if (bleDevice != null && bleDevice.gatt.connected)
if (bleDevice != null && bleDevice.gatt.connected) {
await sendcmd("06");
bleDevice.gatt.disconnect();
}
}
else {
connectTrys = 0;
Expand Down Expand Up @@ -159,11 +154,13 @@ async function connect() {

await epdCharacteristic.startNotifications();
epdCharacteristic.addEventListener('characteristicvaluechanged', (event) => {
addLog(`> 收到配置:${bytesToHex(event.target.value.buffer.slice(0,8))}`);
addLog(`> 收到配置:${bytesToHex(event.target.value.buffer)}`);
document.getElementById("epdpins").value = bytesToHex(event.target.value.buffer.slice(0, 7));
document.getElementById("epddriver").value = bytesToHex(event.target.value.buffer.slice(7, 8));
});

await sendcmd("01");

document.getElementById("connectbutton").innerHTML = '断开';
updateButtonStatus();
}
Expand All @@ -174,8 +171,8 @@ function setStatus(statusText) {
}

function addLog(logTXT) {
var today = new Date();
var time = ("0" + today.getHours()).slice(-2) + ":" + ("0" + today.getMinutes()).slice(-2) + ":" + ("0" + today.getSeconds()).slice(-2) + " : ";
const today = new Date();
const time = ("0" + today.getHours()).slice(-2) + ":" + ("0" + today.getMinutes()).slice(-2) + ":" + ("0" + today.getSeconds()).slice(-2) + " : ";
document.getElementById("log").innerHTML += time + logTXT + '<br>';
console.log(time + logTXT);
while ((document.getElementById("log").innerHTML.match(/<br>/g) || []).length > 10) {
Expand All @@ -198,15 +195,21 @@ function bytesToHex(data) {
}

function intToHex(intIn) {
var stringOut = "";
stringOut = ("0000" + intIn.toString(16)).substr(-4)
let stringOut = ("0000" + intIn.toString(16)).substr(-4)
return stringOut.substring(2, 4) + stringOut.substring(0, 2);
}

function updateImageData(canvas) {
const epdDriver = document.getElementById("epddriver").value;
const dithering = document.getElementById('dithering').value;
document.getElementById('cmdIMAGE').value = bytesToHex(canvas2bytes(canvas, 'bw'));
if (document.getElementById('dithering').value.startsWith('bwr')) {
document.getElementById('cmdIMAGE').value += bytesToHex(canvas2bytes(canvas, 'bwr'));
if (epdDriver === '03') {
if (dithering.startsWith('bwr')) {
document.getElementById('cmdIMAGE').value += bytesToHex(canvas2bytes(canvas, 'bwr'));
} else {
const count = document.getElementById('cmdIMAGE').value.length;
document.getElementById('cmdIMAGE').value += 'F'.repeat(count);
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ static void conn_params_init(void)
*/
static void sleep_mode_enter(void)
{
// Prepare wakeup pin
ble_epd_sleep_prepare(&m_epd);

// Go to system-off mode (this function will not return; wakeup will cause a reset).
uint32_t err_code = sd_power_system_off();
APP_ERROR_CHECK(err_code);
Expand Down

0 comments on commit 0cfacae

Please sign in to comment.