Skip to content

Commit 3dc2437

Browse files
authored
iNav / HDZero HD OSD support (#22)
* first pass at HDZero / iNav support - multipage fonts and HD canvas resolution * a few fixes and some nice font help * add HD fonts and control files
1 parent ff37005 commit 3dc2437

File tree

9 files changed

+225
-81
lines changed

9 files changed

+225
-81
lines changed

Makefile.dji

+2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ goggle_ipk: osd_dji
4949
chmod +x ipk/goggle/build/data/opt/bin/osd_dji
5050
mkdir -p ipk/goggle/build/data/opt/fonts
5151
cp font.bin ipk/goggle/build/data/opt/fonts/
52+
cp font_hd.bin ipk/goggle/build/data/opt/fonts/
53+
cp font_hd_2.bin ipk/goggle/build/data/opt/fonts/
5254
cd ipk/goggle/build/control && tar czvf ../control.tar.gz .
5355
cd ipk/goggle/build/data && tar czvf ../data.tar.gz .
5456
cd ipk/goggle/build && tar czvf "../../${IPK_NAME}" ./control.tar.gz ./data.tar.gz ./debian-binary

font_hd.bin

864 KB
Binary file not shown.

font_hd_2.bin

864 KB
Binary file not shown.

ipk/airunit/control/control

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Package: msp-osd
2-
Version: 0.3.1
2+
Version: 0.4.0
33
Maintainer: bri3d
44
Description: MSP OSD service for the DJI HD FPV airunit.
55
Architecture: pigeon-airside

ipk/goggle/control/control

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Package: msp-osd
2-
Version: 0.3.1
2+
Version: 0.4.0
33
Maintainer: bri3d
44
Description: MSP OSD service for the DJI HD FPV goggles. Press back button for five seconds to enter/exit MSP OSD mode.
55
Architecture: pigeon-glasses

msp_displayport.c

+17-8
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,25 @@
11
#include "msp.h"
22
#include "msp_displayport.h"
33

4-
#define MSP_COLS 30
5-
64
static void process_draw_string(displayport_vtable_t *display_driver, uint8_t *payload) {
75
if(!display_driver || !display_driver->draw_character) return;
86
uint8_t row = payload[0];
97
uint8_t col = payload[1];
10-
uint8_t attrs = payload[2]; // there is support for hw blink with these but it seems unused
8+
uint8_t attrs = payload[2]; // iNav uses this to specify which font page to draw from
119
uint8_t str_len;
1210
for(str_len = 1; str_len < 255; str_len++) {
1311
if(payload[2 + str_len] == '\0') {
1412
break;
1513
}
1614
}
1715
for(uint8_t idx = 0; idx < (str_len - 1); idx++) {
18-
display_driver->draw_character(col, row, payload[3+idx]);
19-
col++;
20-
if(col > MSP_COLS) {
21-
col = 0;
22-
row++;
16+
uint16_t character = payload[3 + idx];
17+
if(attrs & 0x1) {
18+
// shift over a page if one was specified
19+
character |= 0x100;
2320
}
21+
display_driver->draw_character(col, row, character);
22+
col++;
2423
}
2524
}
2625

@@ -34,6 +33,13 @@ static void process_draw_complete(displayport_vtable_t *display_driver) {
3433
display_driver->draw_complete();
3534
}
3635

36+
static void process_set_options(displayport_vtable_t *display_driver, uint8_t *payload) {
37+
if(!display_driver || !display_driver->set_options) return;
38+
uint8_t font = payload[0];
39+
uint8_t is_hd = payload[1];
40+
display_driver->set_options(font, is_hd);
41+
}
42+
3743
static void process_open(displayport_vtable_t *display_driver) {
3844

3945
}
@@ -66,6 +72,9 @@ int displayport_process_message(displayport_vtable_t *display_driver, msp_msg_t
6672
case 4: // 4 -> Draw Screen
6773
process_draw_complete(display_driver);
6874
break;
75+
case 5: // 5 -> Set Options (HDZero/iNav)
76+
process_set_options(display_driver, &msg->payload[1]);
77+
break;
6978
default:
7079
break;
7180
}

msp_displayport.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
#include <stdint.h>
22

3-
typedef void (*draw_character_func)(uint32_t x, uint32_t y, uint8_t c);
3+
typedef void (*draw_character_func)(uint32_t x, uint32_t y, uint16_t c);
4+
typedef void (*set_options_func)(uint8_t font, uint8_t is_hd);
45
typedef void (*clear_screen_func)();
56
typedef void (*draw_complete_func)();
67

78
typedef struct displayport_vtable_s {
89
draw_character_func draw_character;
910
clear_screen_func clear_screen;
1011
draw_complete_func draw_complete;
12+
set_options_func set_options;
1113
} displayport_vtable_t;
1214

1315
int displayport_process_message(displayport_vtable_t *display_driver, msp_msg_t *msg);

osd_dji_udp.c

+127-46
Original file line numberDiff line numberDiff line change
@@ -21,30 +21,22 @@
2121
#include "msp.h"
2222
#include "msp_displayport.h"
2323

24-
#define OSD_WIDTH 31
25-
#define OSD_HEIGHT 15
26-
27-
#define X_OFFSET 180
28-
#define Y_OFFSET 0
29-
3024
#define PORT 7654
3125

3226
#define WIDTH 1440
3327
#define HEIGHT 810
3428
#define BYTES_PER_PIXEL 4
3529
#define PLANE_ID 6
3630

37-
#define FONT_WIDTH 36
38-
#define FONT_HEIGHT 54
31+
#define NUM_CHARS 256
3932

4033
#define INPUT_FILENAME "/dev/input/event0"
4134
#define SPLASH_STRING "MSP OSD WAITING FOR DATA..."
4235
#define SHUTDOWN_STRING "MSP OSD SHUTTING DOWN..."
4336

44-
#define FALLBACK_FONT_PATH "/blackbox/font.bin"
45-
#define ENTWARE_FONT_PATH "/opt/fonts/font.bin"
46-
#define SDCARD_FONT_PATH "/storage/sdcard0/font.bin"
47-
#define FONT_FILE_SIZE 1990656
37+
#define FALLBACK_FONT_PATH "/blackbox/font"
38+
#define ENTWARE_FONT_PATH "/opt/fonts/font"
39+
#define SDCARD_FONT_PATH "/storage/sdcard0/font"
4840

4941
#define EV_CODE_BACK 0xc9
5042

@@ -60,40 +52,81 @@
6052
( (((data) >> 24) & 0x000000FF) | (((data) >> 8) & 0x0000FF00) | \
6153
(((data) << 8) & 0x00FF0000) | (((data) << 24) & 0xFF000000) )
6254

55+
#define MAX_DISPLAY_X 50
56+
#define MAX_DISPLAY_Y 18
57+
58+
typedef struct display_info_s {
59+
uint8_t char_width;
60+
uint8_t char_height;
61+
uint8_t font_width;
62+
uint8_t font_height;
63+
uint16_t x_offset;
64+
uint16_t y_offset;
65+
} display_info_t;
66+
6367
static volatile sig_atomic_t quit = 0;
64-
dji_display_state_t *dji_display;
65-
uint8_t character_map[OSD_WIDTH][OSD_HEIGHT];
66-
displayport_vtable_t *display_driver;
67-
void *font;
68-
uint8_t which_fb = 0;
68+
static dji_display_state_t *dji_display;
69+
static uint16_t character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y];
70+
static displayport_vtable_t *display_driver;
71+
static void *font_page_1 = NULL;
72+
static void *font_page_2 = NULL;
73+
static uint8_t which_fb = 0;
74+
75+
#define SD_DISPLAY_INFO {.char_width = 31, .char_height = 15, .font_width = 36, .font_height = 54, .x_offset = 180, .y_offset = 0}
76+
77+
static display_info_t sd_display_info = SD_DISPLAY_INFO;
78+
79+
static display_info_t hd_display_info = {
80+
.char_width = 50,
81+
.char_height = 18,
82+
.font_width = 24,
83+
.font_height = 36,
84+
.x_offset = 120,
85+
.y_offset = 80
86+
};
87+
88+
static display_info_t current_display_info = SD_DISPLAY_INFO;
6989

7090
static void sig_handler(int _)
7191
{
7292
quit = 1;
7393
}
7494

75-
static void draw_character(uint32_t x, uint32_t y, uint8_t c)
95+
static void draw_character(uint32_t x, uint32_t y, uint16_t c)
7696
{
77-
if ((x > (OSD_WIDTH - 1)) || (y > (OSD_HEIGHT - 1))) {
97+
if ((x > (current_display_info.char_width - 1)) || (y > (current_display_info.char_height - 1))) {
7898
return;
7999
}
80100
character_map[x][y] = c;
81101
}
82102

83103
static void draw_screen() {
104+
if (font_page_1 == NULL) {
105+
// give up if we don't have a font loaded
106+
return;
107+
}
108+
void *font;
84109
void *fb_addr = dji_display_get_fb_address(dji_display, which_fb);
85110
// DJI has a backwards alpha channel - FF is transparent, 00 is opaque.
86111
memset(fb_addr, 0x000000FF, WIDTH * HEIGHT * BYTES_PER_PIXEL);
87-
for(int y = 0; y < OSD_HEIGHT; y++) {
88-
for(int x = 0; x < OSD_WIDTH; x++) {
89-
uint8_t c = character_map[x][y];
112+
for(int y = 0; y < current_display_info.char_height; y++) {
113+
for(int x = 0; x < current_display_info.char_width; x++) {
114+
uint16_t c = character_map[x][y];
90115
if (c != 0) {
91-
uint32_t pixel_x = (x * FONT_WIDTH) + X_OFFSET;
92-
uint32_t pixel_y = (y * FONT_HEIGHT) + Y_OFFSET;
93-
uint32_t character_offset = (((FONT_HEIGHT * FONT_WIDTH) * BYTES_PER_PIXEL) * c);
94-
for(uint8_t gx = 0; gx < FONT_WIDTH; gx++) {
95-
for(uint8_t gy = 0; gy < FONT_HEIGHT; gy++) {
96-
uint32_t font_offset = character_offset + (gy * FONT_WIDTH * BYTES_PER_PIXEL) + (gx * BYTES_PER_PIXEL);
116+
font = font_page_1;
117+
if (c > 255) {
118+
c = c & 0xFF;
119+
if (font_page_2 != NULL) {
120+
// fall back to writing page 1 chars if we don't have a page 2 font
121+
font = font_page_2;
122+
}
123+
}
124+
uint32_t pixel_x = (x * current_display_info.font_width) + current_display_info.x_offset;
125+
uint32_t pixel_y = (y * current_display_info.font_height) + current_display_info.y_offset;
126+
uint32_t character_offset = (((current_display_info.font_height * current_display_info.font_width) * BYTES_PER_PIXEL) * c);
127+
for(uint8_t gx = 0; gx < current_display_info.font_width; gx++) {
128+
for(uint8_t gy = 0; gy < current_display_info.font_height; gy++) {
129+
uint32_t font_offset = character_offset + (gy * current_display_info.font_width * BYTES_PER_PIXEL) + (gx * BYTES_PER_PIXEL);
97130
uint32_t target_offset = ((((pixel_x + gx) * BYTES_PER_PIXEL) + ((pixel_y + gy) * WIDTH * BYTES_PER_PIXEL)));
98131
*((uint8_t *)fb_addr + target_offset) = *(uint8_t *)((uint8_t *)font + font_offset + 2);
99132
*((uint8_t *)fb_addr + target_offset + 1) = *(uint8_t *)((uint8_t *)font + font_offset + 1);
@@ -126,33 +159,87 @@ static void msp_callback(msp_msg_t *msp_message)
126159
displayport_process_message(display_driver, msp_message);
127160
}
128161

129-
static int open_font(const char *filename, void** font) {
130-
printf("Opening font: %s\n", filename);
162+
static void get_font_path_with_prefix(char *font_path_dest, const char *font_path, uint8_t len, uint8_t is_hd, uint8_t page) {
163+
char name_buf[len];
164+
if (is_hd) {
165+
snprintf(name_buf, len, "%s_hd", font_path);
166+
} else {
167+
snprintf(name_buf, len, "%s", font_path);
168+
}
169+
if (page > 0) {
170+
snprintf(font_path_dest, len, "%s_%d.bin", name_buf, page + 1);
171+
} else {
172+
snprintf(font_path_dest, len, "%s.bin", name_buf);
173+
}
174+
}
175+
176+
static int open_font(const char *filename, void** font, uint8_t page) {
177+
char file_path[255];
178+
get_font_path_with_prefix(file_path, filename, 255, (current_display_info.font_width < sd_display_info.font_width), page);
179+
printf("Opening font: %s\n", file_path);
131180
struct stat st;
132-
stat(filename, &st);
181+
memset(&st, 0, sizeof(st));
182+
stat(file_path, &st);
133183
size_t filesize = st.st_size;
134-
if(filesize != FONT_FILE_SIZE) {
184+
size_t desired_filesize = current_display_info.font_height * current_display_info.font_width * NUM_CHARS * BYTES_PER_PIXEL;
185+
if(filesize != desired_filesize) {
186+
if (filesize != 0) {
187+
printf("Font was wrong size: %s %d != %d\n", file_path, filesize, desired_filesize);
188+
}
135189
return -1;
136190
}
137-
int fd = open(filename, O_RDONLY, 0);
191+
int fd = open(file_path, O_RDONLY, 0);
138192
if (!fd) {
193+
printf("Could not open file %s\n", file_path);
139194
return -1;
140195
}
141196
void* mmappedData = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0);
142-
assert(mmappedData != MAP_FAILED);
143197
// there is no need to keep an FD open after mmap
144198
close(fd);
145-
*font = mmappedData;
199+
if (mmappedData != MAP_FAILED) {
200+
*font = mmappedData;
201+
} else {
202+
printf("Could not map font %s\n", file_path);
203+
*font = 0;
204+
}
146205
return 0;
147206
}
148207

208+
static void load_font() {
209+
if (open_font(SDCARD_FONT_PATH, &font_page_1, 0) < 0) {
210+
if (open_font(ENTWARE_FONT_PATH, &font_page_1, 0) < 0) {
211+
open_font(FALLBACK_FONT_PATH, &font_page_1, 0);
212+
}
213+
}
214+
if (open_font(SDCARD_FONT_PATH, &font_page_2, 1) < 0) {
215+
if (open_font(ENTWARE_FONT_PATH, &font_page_2, 1) < 0) {
216+
open_font(FALLBACK_FONT_PATH, &font_page_2, 1);
217+
}
218+
}
219+
}
220+
149221
static void close_font(void *font) {
150-
munmap(font, FONT_FILE_SIZE);
222+
if (font != NULL)
223+
{
224+
munmap(font, current_display_info.font_height * current_display_info.font_width * NUM_CHARS * 4);
225+
}
226+
}
227+
228+
static void set_options(uint8_t font_num, uint8_t is_hd) {
229+
clear_screen();
230+
close_font(font_page_1);
231+
close_font(font_page_2);
232+
if(is_hd) {
233+
current_display_info = hd_display_info;
234+
} else {
235+
current_display_info = sd_display_info;
236+
}
237+
load_font();
151238
}
152239

153240
static void display_print_string(const char *string, uint8_t len) {
154241
for(int x = 0; x < len; x++) {
155-
character_map[x][OSD_HEIGHT - 2] = string[x];
242+
character_map[x][current_display_info.char_height - 2] = string[x];
156243
}
157244
draw_complete();
158245
}
@@ -170,14 +257,6 @@ static void stop_display() {
170257
dji_display_state_free(dji_display);
171258
}
172259

173-
static void load_font() {
174-
if (open_font(SDCARD_FONT_PATH, &font) < 0) {
175-
if (open_font(ENTWARE_FONT_PATH, &font) < 0) {
176-
open_font(FALLBACK_FONT_PATH, &font);
177-
}
178-
}
179-
}
180-
181260
int main(int argc, char *argv[])
182261
{
183262
signal(SIGINT, sig_handler);
@@ -189,6 +268,7 @@ int main(int argc, char *argv[])
189268
display_driver->draw_character = &draw_character;
190269
display_driver->clear_screen = &clear_screen;
191270
display_driver->draw_complete = &draw_complete;
271+
display_driver->set_options = &set_options;
192272

193273
msp_state_t *msp_state = calloc(1, sizeof(msp_state_t));
194274
msp_state->cb = &msp_callback;
@@ -238,7 +318,8 @@ int main(int argc, char *argv[])
238318
printf("Switching Enabled/Waiting -> Disabled!\n");
239319
if(display_mode == DISPLAY_RUNNING)
240320
stop_display();
241-
close_font(font);
321+
close_font(font_page_1);
322+
close_font(font_page_2);
242323
display_mode = DISPLAY_DISABLED;
243324
dji_start_goggles(is_v2_goggles);
244325
}

0 commit comments

Comments
 (0)