Skip to content

Commit 46b3910

Browse files
authored
Merge pull request #713 from Quency-D/dev-meshpocket
add heltec meshpocket board.
2 parents a3aa66a + 8fa31e0 commit 46b3910

File tree

12 files changed

+524
-18
lines changed

12 files changed

+524
-18
lines changed

boards/heltec_mesh_pocket.json

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"build": {
3+
"arduino": {
4+
"ldscript": "nrf52840_s140_v6.ld"
5+
},
6+
"core": "nRF5",
7+
"cpu": "cortex-m4",
8+
"extra_flags": "-DNRF52840_XXAA",
9+
"f_cpu": "64000000L",
10+
"hwids": [
11+
["0x239A", "0x4405"],
12+
["0x239A", "0x0029"],
13+
["0x239A", "0x002A"]
14+
],
15+
"usb_product": "HT-n5262",
16+
"mcu": "nrf52840",
17+
"variant": "heltec_mesh_pocket",
18+
"variants_dir": "variants",
19+
"bsp": {
20+
"name": "adafruit"
21+
},
22+
"softdevice": {
23+
"sd_flags": "-DS140",
24+
"sd_name": "s140",
25+
"sd_version": "6.1.1",
26+
"sd_fwid": "0x00B6"
27+
},
28+
"bootloader": {
29+
"settings_addr": "0xFF000"
30+
}
31+
},
32+
"connectivity": ["bluetooth"],
33+
"debug": {
34+
"jlink_device": "nRF52840_xxAA",
35+
"onboard_tools": ["jlink"],
36+
"svd_path": "nrf52840.svd",
37+
"openocd_target": "nrf52840-mdk-rs"
38+
},
39+
"frameworks": ["arduino"],
40+
"name": "Heltec nrf (Adafruit BSP)",
41+
"upload": {
42+
"maximum_ram_size": 248832,
43+
"maximum_size": 815104,
44+
"speed": 115200,
45+
"protocol": "nrfutil",
46+
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
47+
"use_1200bps_touch": true,
48+
"require_upload_port": true,
49+
"wait_for_upload_port": true
50+
},
51+
"url": "https://heltec.org/project/meshpocket/",
52+
"vendor": "Heltec"
53+
}

examples/simple_repeater/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,7 @@ void setup() {
818818
#ifdef DISPLAY_CLASS
819819
if (display.begin()) {
820820
display.startFrame();
821+
display.setCursor(0, 0);
821822
display.print("Please wait...");
822823
display.endFrame();
823824
}

examples/simple_room_server/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,7 @@ void setup() {
993993
#ifdef DISPLAY_CLASS
994994
if (display.begin()) {
995995
display.startFrame();
996+
display.setCursor(0, 0);
996997
display.print("Please wait...");
997998
display.endFrame();
998999
}

src/helpers/ui/GxEPDDisplay.cpp

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
#define DISPLAY_ROTATION 3
66
#endif
77

8-
#define SCALE_X 1.5625f // 200 / 128
9-
#define SCALE_Y 1.5625f // 200 / 128
10-
118
bool GxEPDDisplay::begin() {
129
display.epd2.selectSPI(SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
1310
SPI1.begin();
@@ -84,7 +81,7 @@ void GxEPDDisplay::setColor(Color c) {
8481
void GxEPDDisplay::setCursor(int x, int y) {
8582
display_crc.update<int>(x);
8683
display_crc.update<int>(y);
87-
display.setCursor(x*SCALE_X, (y+10)*SCALE_Y);
84+
display.setCursor((x+offset_x)*scale_x, (y+offset_y)*scale_y);
8885
}
8986

9087
void GxEPDDisplay::print(const char* str) {
@@ -97,15 +94,15 @@ void GxEPDDisplay::fillRect(int x, int y, int w, int h) {
9794
display_crc.update<int>(y);
9895
display_crc.update<int>(w);
9996
display_crc.update<int>(h);
100-
display.fillRect(x*SCALE_X, y*SCALE_Y, w*SCALE_X, h*SCALE_Y, _curr_color);
97+
display.fillRect(x*scale_x, y*scale_y, w*scale_x, h*scale_y, _curr_color);
10198
}
10299

103100
void GxEPDDisplay::drawRect(int x, int y, int w, int h) {
104101
display_crc.update<int>(x);
105102
display_crc.update<int>(y);
106103
display_crc.update<int>(w);
107104
display_crc.update<int>(h);
108-
display.drawRect(x*SCALE_X, y*SCALE_Y, w*SCALE_X, h*SCALE_Y, _curr_color);
105+
display.drawRect(x*scale_x, y*scale_y, w*scale_x, h*scale_y, _curr_color);
109106
}
110107

111108
void GxEPDDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) {
@@ -115,24 +112,24 @@ void GxEPDDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) {
115112
display_crc.update<int>(h);
116113
display_crc.update<uint8_t>(bits, w * h / 8);
117114
// Calculate the base position in display coordinates
118-
uint16_t startX = x * SCALE_X;
119-
uint16_t startY = y * SCALE_Y;
115+
uint16_t startX = x * scale_x;
116+
uint16_t startY = y * scale_y;
120117

121118
// Width in bytes for bitmap processing
122119
uint16_t widthInBytes = (w + 7) / 8;
123120

124121
// Process the bitmap row by row
125122
for (uint16_t by = 0; by < h; by++) {
126123
// Calculate the target y-coordinates for this logical row
127-
int y1 = startY + (int)(by * SCALE_Y);
128-
int y2 = startY + (int)((by + 1) * SCALE_Y);
124+
int y1 = startY + (int)(by * scale_y);
125+
int y2 = startY + (int)((by + 1) * scale_y);
129126
int block_h = y2 - y1;
130127

131128
// Scan across the row bit by bit
132129
for (uint16_t bx = 0; bx < w; bx++) {
133130
// Calculate the target x-coordinates for this logical column
134-
int x1 = startX + (int)(bx * SCALE_X);
135-
int x2 = startX + (int)((bx + 1) * SCALE_X);
131+
int x1 = startX + (int)(bx * scale_x);
132+
int x2 = startX + (int)((bx + 1) * scale_x);
136133
int block_w = x2 - x1;
137134

138135
// Get the current bit
@@ -153,7 +150,7 @@ uint16_t GxEPDDisplay::getTextWidth(const char* str) {
153150
int16_t x1, y1;
154151
uint16_t w, h;
155152
display.getTextBounds(str, 0, 0, &x1, &y1, &w, &h);
156-
return ceil((w + 1) / SCALE_X);
153+
return ceil((w + 1) / scale_x);
157154
}
158155

159156
void GxEPDDisplay::endFrame() {

src/helpers/ui/GxEPDDisplay.h

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@
1313
#include <Fonts/FreeSansBold12pt7b.h>
1414
#include <Fonts/FreeSans18pt7b.h>
1515

16-
#define GxEPD2_DISPLAY_CLASS GxEPD2_BW
17-
#define GxEPD2_DRIVER_CLASS GxEPD2_150_BN // DEPG0150BN 200x200, SSD1681, (FPC8101), TTGO T5 V2.4.1
18-
1916
#include <epd/GxEPD2_150_BN.h> // 1.54" b/w
17+
#include <epd/GxEPD2_213_B74.h> // 2.13" b/w
2018
#include <CRC32.h>
2119

2220
#include "DisplayDriver.h"
@@ -26,7 +24,19 @@
2624

2725
class GxEPDDisplay : public DisplayDriver {
2826

27+
#if defined(HELTEC_MESH_POCKET)
28+
GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT> display;
29+
const float scale_x = EINK_SCALE_X;
30+
const float scale_y = EINK_SCALE_Y;
31+
const float offset_x = EINK_X_OFFSET;
32+
const float offset_y = EINK_Y_OFFSET;
33+
#else
2934
GxEPD2_BW<GxEPD2_150_BN, 200> display;
35+
const float scale_x = 1.5625f;
36+
const float scale_y = 1.5625f;
37+
const float offset_x = 0;
38+
const float offset_y = 10;
39+
#endif
3040
bool _init = false;
3141
bool _isOn = false;
3242
uint16_t _curr_color;
@@ -35,8 +45,11 @@ class GxEPDDisplay : public DisplayDriver {
3545

3646
public:
3747
// there is a margin in y...
38-
GxEPDDisplay() : DisplayDriver(128, 128), display(GxEPD2_150_BN(DISP_CS, DISP_DC, DISP_RST, DISP_BUSY)) {
39-
}
48+
#if defined(HELTEC_MESH_POCKET)
49+
GxEPDDisplay() : DisplayDriver(128, 128), display(EINK_DISPLAY_MODEL(PIN_DISPLAY_CS, PIN_DISPLAY_DC, PIN_DISPLAY_RST, PIN_DISPLAY_BUSY)) {}
50+
#else
51+
GxEPDDisplay() : DisplayDriver(128, 128), display(GxEPD2_150_BN(DISP_CS, DISP_DC, DISP_RST, DISP_BUSY)) {}
52+
#endif
4053

4154
bool begin();
4255

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#include <Arduino.h>
2+
#include "MeshPocket.h"
3+
#include <bluefruit.h>
4+
#include <Wire.h>
5+
6+
static BLEDfu bledfu;
7+
8+
static void connect_callback(uint16_t conn_handle)
9+
{
10+
(void)conn_handle;
11+
MESH_DEBUG_PRINTLN("BLE client connected");
12+
}
13+
14+
static void disconnect_callback(uint16_t conn_handle, uint8_t reason)
15+
{
16+
(void)conn_handle;
17+
(void)reason;
18+
19+
MESH_DEBUG_PRINTLN("BLE client disconnected");
20+
}
21+
22+
void HeltecMeshPocket::begin() {
23+
// for future use, sub-classes SHOULD call this from their begin()
24+
startup_reason = BD_STARTUP_NORMAL;
25+
Serial.begin(115200);
26+
pinMode(PIN_VBAT_READ, INPUT);
27+
28+
pinMode(PIN_USER_BTN, INPUT);
29+
}
30+
31+
bool HeltecMeshPocket::startOTAUpdate(const char* id, char reply[]) {
32+
// Config the peripheral connection with maximum bandwidth
33+
// more SRAM required by SoftDevice
34+
// Note: All config***() function must be called before begin()
35+
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
36+
Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16);
37+
38+
Bluefruit.begin(1, 0);
39+
// Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4
40+
Bluefruit.setTxPower(4);
41+
// Set the BLE device name
42+
Bluefruit.setName("MESH_POCKET_OTA");
43+
44+
Bluefruit.Periph.setConnectCallback(connect_callback);
45+
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
46+
47+
// To be consistent OTA DFU should be added first if it exists
48+
bledfu.begin();
49+
50+
// Set up and start advertising
51+
// Advertising packet
52+
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
53+
Bluefruit.Advertising.addTxPower();
54+
Bluefruit.Advertising.addName();
55+
56+
/* Start Advertising
57+
- Enable auto advertising if disconnected
58+
- Interval: fast mode = 20 ms, slow mode = 152.5 ms
59+
- Timeout for fast mode is 30 seconds
60+
- Start(timeout) with timeout = 0 will advertise forever (until connected)
61+
62+
For recommended advertising interval
63+
https://developer.apple.com/library/content/qa/qa1931/_index.html
64+
*/
65+
Bluefruit.Advertising.restartOnDisconnect(true);
66+
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
67+
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
68+
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
69+
70+
strcpy(reply, "OK - started");
71+
return true;
72+
}

variants/mesh_pocket/MeshPocket.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#pragma once
2+
3+
#include <Arduino.h>
4+
#include <MeshCore.h>
5+
6+
// built-ins
7+
#define PIN_VBAT_READ 29
8+
#define PIN_BAT_CTL 34
9+
#define MV_LSB (3000.0F / 4096.0F) // 12-bit ADC with 3.0V input range
10+
11+
class HeltecMeshPocket : public mesh::MainBoard {
12+
protected:
13+
uint8_t startup_reason;
14+
15+
public:
16+
void begin();
17+
uint8_t getStartupReason() const override { return startup_reason; }
18+
19+
20+
21+
uint16_t getBattMilliVolts() override {
22+
int adcvalue = 0;
23+
analogReadResolution(12);
24+
analogReference(AR_INTERNAL_3_0);
25+
pinMode(PIN_BAT_CTL, OUTPUT); // battery adc can be read only ctrl pin set to high
26+
pinMode(PIN_VBAT_READ, INPUT);
27+
digitalWrite(PIN_BAT_CTL, HIGH);
28+
29+
delay(10);
30+
adcvalue = analogRead(PIN_VBAT_READ);
31+
digitalWrite(PIN_BAT_CTL, LOW);
32+
33+
return (uint16_t)((float)adcvalue * MV_LSB * 4.9);
34+
}
35+
36+
const char* getManufacturerName() const override {
37+
return "Heltec MeshPocket";
38+
}
39+
40+
void reboot() override {
41+
NVIC_SystemReset();
42+
}
43+
44+
bool startOTAUpdate(const char* id, char reply[]) override;
45+
};

0 commit comments

Comments
 (0)