SENSORS: Gyro/accel aligmnet fix
[betaflight.git] / src / main / io / display.c
blob5a1c793f6e8584737e67cc274fbc927e1f3c5041
1 /*
2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
18 #include <stdbool.h>
19 #include <stdint.h>
20 #include <stdarg.h>
21 #include <string.h>
23 #include "platform.h"
24 #include "version.h"
26 #include "build_config.h"
28 #include "drivers/serial.h"
29 #include "drivers/system.h"
30 #include "drivers/display_ug2864hsweg01.h"
31 #include "drivers/sensor.h"
32 #include "drivers/accgyro.h"
33 #include "drivers/compass.h"
35 #include "common/printf.h"
36 #include "common/maths.h"
37 #include "common/axis.h"
38 #include "common/typeconversion.h"
40 #ifdef DISPLAY
42 #include "sensors/battery.h"
43 #include "sensors/sensors.h"
44 #include "sensors/compass.h"
45 #include "sensors/acceleration.h"
46 #include "sensors/gyro.h"
47 #include "sensors/barometer.h"
49 #include "rx/rx.h"
51 #include "io/rc_controls.h"
53 #include "flight/pid.h"
54 #include "flight/imu.h"
55 #include "flight/failsafe.h"
57 #ifdef GPS
58 #include "io/gps.h"
59 #include "flight/navigation_rewrite.h"
60 #endif
62 #include "config/runtime_config.h"
64 #include "config/config.h"
66 #include "display.h"
68 controlRateConfig_t *getControlRateConfig(uint8_t profileIndex);
70 #define MICROSECONDS_IN_A_SECOND (1000 * 1000)
72 #define DISPLAY_UPDATE_FREQUENCY (MICROSECONDS_IN_A_SECOND / 5)
73 #define PAGE_CYCLE_FREQUENCY (MICROSECONDS_IN_A_SECOND * 5)
75 static uint32_t nextDisplayUpdateAt = 0;
76 static bool displayPresent = false;
78 static rxConfig_t *rxConfig;
80 #define PAGE_TITLE_LINE_COUNT 1
82 static char lineBuffer[SCREEN_CHARACTER_COLUMN_COUNT + 1];
84 #define HALF_SCREEN_CHARACTER_COLUMN_COUNT (SCREEN_CHARACTER_COLUMN_COUNT / 2)
85 #define IS_SCREEN_CHARACTER_COLUMN_COUNT_ODD (SCREEN_CHARACTER_COLUMN_COUNT & 1)
87 #if defined(DISPLAY_ARMED_BITMAP)
88 static uint8_t armedBitmapRLE [] = { 128, 32,
89 '\x00','\x00','\x87','\xc0','\xe0','\xf8','\xfc','\xfc', // 0x0008
90 '\x02','\x7e','\x3e','\x1f','\x0f','\x0f','\x06','\xcf', // 0x0010
91 '\xff','\xff','\x04','\x7f','\x1f','\x8e','\xe0','\xf0', // 0x0018
92 '\xfc','\xfe','\x7f','\x3f','\x0f','\x0f','\x06','\x8f', // 0x0020
93 '\xcf','\xff','\xff','\x04','\x7f','\x1f','\x8e','\xe0', // 0x0028
94 '\xf0','\xfc','\xfe','\x7f','\x3f','\x0f','\x0f','\x06', // 0x0030
95 '\xcf','\xef','\xff','\xff','\x03','\x7f','\x1f','\x0f', // 0x0038
96 '\x0f','\x06','\xcf','\xff','\xff','\x04','\x7f','\x1f', // 0x0040
97 '\x8e','\xe0','\xf0','\xfc','\xfe','\xff','\xbf','\x8f', // 0x0048
98 '\x8f','\x05','\x0f','\x0f','\x08','\x07','\x07','\x02', // 0x0050
99 '\x83','\xe3','\xf1','\xfd','\xfe','\x7f','\x3f','\x0f', // 0x0058
100 '\x0f','\x07','\xcf','\xff','\xff','\x04','\x7f','\x1f', // 0x0060
101 '\x0e','\x00','\x00','\x03','\x80','\xc0','\xf0','\xf8', // 0x0068
102 '\xfe','\x7f','\x3f','\x1f','\x0f','\x0f','\x06','\x8f', // 0x0070
103 '\xef','\xff','\xff','\x03','\x7f','\x3f','\x8f','\xc7', // 0x0078
104 '\xf3','\xf8','\xfe','\xff','\x3f','\x1f','\x0f','\x0f', // 0x0080
105 '\x06','\x8f','\xcf','\xff','\xff','\x04','\x3f','\x9f', // 0x0088
106 '\xc3','\xf1','\xf8','\xfc','\xff','\x3f','\x1f','\x07', // 0x0090
107 '\x03','\x00','\x00','\x03','\x80','\xc0','\xf0','\xf8', // 0x0098
108 '\xfe','\xff','\x3f','\x1f','\x07','\x03','\x00','\x00', // 0x00a0
109 '\x03','\x80','\xc0','\xf0','\xf8','\xfe','\xff','\x3f', // 0x00a8
110 '\x9f','\xc7','\xf3','\xf8','\xfc','\xff','\xff','\x03', // 0x00b0
111 '\xf7','\xf3','\xf3','\x04','\xf1','\xf1','\x03','\xf0', // 0x00b8
112 '\xf0','\x09','\xf8','\xfe','\xff','\xff','\x03','\xf7', // 0x00c0
113 '\xf3','\xf0','\xf0','\x06','\xf8','\x7c','\x7e','\x3f', // 0x00c8
114 '\x3f','\x02','\x1f','\x07','\x03','\x00','\x00','\x86', // 0x00d0
116 #endif
118 static const char* const pageTitles[] = {
119 FC_NAME,
120 "ARMED",
121 "STATUS"
124 static const char* const gpsFixTypeText[] = {
125 "NO",
126 "2D",
127 "3D"
130 static const char* tickerCharacters = "|/-\\"; // use 2/4/8 characters so that the divide is optimal.
131 #define TICKER_CHARACTER_COUNT (sizeof(tickerCharacters) / sizeof(char))
133 static uint32_t nextPageAt;
134 static bool forcePageChange;
135 static pageId_e currentPageId;
137 void resetDisplay(void) {
138 displayPresent = ug2864hsweg01InitI2C();
141 void LCDprint(uint8_t i) {
142 i2c_OLED_send_char(i);
145 void padLineBufferToChar(uint8_t toChar)
147 uint8_t length = strlen(lineBuffer);
148 while (length < toChar - 1) {
149 lineBuffer[length++] = ' ';
151 lineBuffer[length] = 0;
154 void padLineBuffer(void)
156 padLineBufferToChar(sizeof(lineBuffer));
159 void padHalfLineBuffer(void)
161 uint8_t halfLineIndex = sizeof(lineBuffer) / 2;
162 padLineBufferToChar(halfLineIndex);
165 // LCDbar(n,v) : draw a bar graph - n number of chars for width, v value in % to display
166 void drawHorizonalPercentageBar(uint8_t width,uint8_t percent) {
167 uint8_t i, j;
169 if (percent > 100)
170 percent = 100;
172 j = (width * percent) / 100;
174 for (i = 0; i < j; i++)
175 LCDprint(159); // full
177 if (j < width)
178 LCDprint(154 + (percent * width * 5 / 100 - 5 * j)); // partial fill
180 for (i = j + 1; i < width; i++)
181 LCDprint(154); // empty
184 void updateTicker(void)
186 static uint8_t tickerIndex = 0;
187 i2c_OLED_set_xy(SCREEN_CHARACTER_COLUMN_COUNT - 1, 0);
188 i2c_OLED_send_char(tickerCharacters[tickerIndex]);
189 tickerIndex++;
190 tickerIndex = tickerIndex % TICKER_CHARACTER_COUNT;
193 void updateRxStatus(void)
195 i2c_OLED_set_xy(SCREEN_CHARACTER_COLUMN_COUNT - 2, 0);
196 char rxStatus = '!';
197 if (rxIsReceivingSignal()) {
198 rxStatus = 'r';
199 } if (rxAreFlightChannelsValid()) {
200 rxStatus = 'R';
202 i2c_OLED_send_char(rxStatus);
205 void updateFailsafeStatus(void)
207 char failsafeIndicator = '?';
208 switch (failsafePhase()) {
209 case FAILSAFE_IDLE:
210 failsafeIndicator = '-';
211 break;
212 case FAILSAFE_RX_LOSS_DETECTED:
213 failsafeIndicator = 'R';
214 break;
215 #if defined(NAV)
216 case FAILSAFE_RETURN_TO_HOME:
217 failsafeIndicator = 'H';
218 break;
219 #endif
220 case FAILSAFE_LANDING:
221 failsafeIndicator = 'l';
222 break;
223 case FAILSAFE_LANDED:
224 failsafeIndicator = 'L';
225 break;
226 case FAILSAFE_RX_LOSS_MONITORING:
227 failsafeIndicator = 'M';
228 break;
229 case FAILSAFE_RX_LOSS_RECOVERED:
230 failsafeIndicator = 'r';
231 break;
233 i2c_OLED_set_xy(SCREEN_CHARACTER_COLUMN_COUNT - 3, 0);
234 i2c_OLED_send_char(failsafeIndicator);
237 void showTitle(void)
239 #if defined(DISPLAY_ARMED_BITMAP)
240 if (currentPageId != PAGE_ARMED) {
241 i2c_OLED_set_line(0);
242 i2c_OLED_send_string(pageTitles[currentPageId]);
244 #else
245 i2c_OLED_set_line(0);
246 i2c_OLED_send_string(pageTitles[currentPageId]);
247 #endif
250 void showWelcomePage(void)
252 uint8_t rowIndex = PAGE_TITLE_LINE_COUNT;
254 tfp_sprintf(lineBuffer, "v%s (%s)", FC_VERSION_STRING, shortGitRevision);
255 i2c_OLED_set_line(rowIndex++);
256 i2c_OLED_send_string(lineBuffer);
258 i2c_OLED_set_line(rowIndex++);
259 i2c_OLED_send_string(targetName);
262 #if defined(DISPLAY_ARMED_BITMAP)
263 // RLE compressed bitmaps must be 128 width with vertical data orientation, and size included in file.
264 void bitmapDecompressAndShow(uint8_t *bitmap)
266 uint8_t data = 0, count = 0;
267 uint16_t i;
268 uint8_t width = *bitmap;
269 bitmap++;
270 uint8_t height = *bitmap;
271 bitmap++;
272 uint16_t bitmapSize = (width * height) / 8;
273 for (i = 0; i < bitmapSize; i++) {
274 if(count == 0) {
275 data = *bitmap;
276 bitmap++;
277 if(data == *bitmap) {
278 bitmap++;
279 count = *bitmap;
280 bitmap++;
282 else {
283 count = 1;
286 count--;
287 i2c_OLED_send_byte(data);
291 void showArmedPage(void)
293 i2c_OLED_set_line(2);
294 bitmapDecompressAndShow(armedBitmapRLE);
296 #else
297 void showArmedPage(void)
300 #endif
302 void showStatusPage(void)
304 uint8_t rowIndex = PAGE_TITLE_LINE_COUNT;
306 if (feature(FEATURE_VBAT)) {
307 i2c_OLED_set_line(rowIndex++);
308 tfp_sprintf(lineBuffer, "V: %d.%1d ", vbat / 10, vbat % 10);
309 padLineBufferToChar(12);
310 i2c_OLED_send_string(lineBuffer);
312 uint8_t batteryPercentage = calculateBatteryPercentage();
313 drawHorizonalPercentageBar(10, batteryPercentage);
316 if (feature(FEATURE_CURRENT_METER)) {
317 i2c_OLED_set_line(rowIndex++);
318 tfp_sprintf(lineBuffer, "mAh: %d", mAhDrawn);
319 padLineBufferToChar(12);
320 i2c_OLED_send_string(lineBuffer);
322 uint8_t capacityPercentage = calculateBatteryCapacityRemainingPercentage();
323 drawHorizonalPercentageBar(10, capacityPercentage);
326 rowIndex++;
328 #ifdef GPS
329 if (feature(FEATURE_GPS)) {
330 tfp_sprintf(lineBuffer, "Sats: %d", gpsSol.numSat);
331 padHalfLineBuffer();
332 i2c_OLED_set_line(rowIndex);
333 i2c_OLED_send_string(lineBuffer);
335 tfp_sprintf(lineBuffer, "Fix: %s", gpsFixTypeText[gpsSol.fixType]);
336 padHalfLineBuffer();
337 i2c_OLED_set_xy(HALF_SCREEN_CHARACTER_COLUMN_COUNT, rowIndex++);
338 i2c_OLED_send_string(lineBuffer);
340 tfp_sprintf(lineBuffer, "HDOP: %d.%1d", gpsSol.hdop / 100, gpsSol.hdop % 100);
341 padLineBuffer();
342 i2c_OLED_set_line(rowIndex++);
343 i2c_OLED_send_string(lineBuffer);
345 tfp_sprintf(lineBuffer, "La/Lo: %d/%d", gpsSol.llh.lat / GPS_DEGREES_DIVIDER, gpsSol.llh.lon / GPS_DEGREES_DIVIDER);
346 padLineBuffer();
347 i2c_OLED_set_line(rowIndex++);
348 i2c_OLED_send_string(lineBuffer);
351 #endif
353 #ifdef MAG
354 if (sensors(SENSOR_MAG)) {
355 tfp_sprintf(lineBuffer, "HDG: %d", DECIDEGREES_TO_DEGREES(attitude.values.yaw));
356 padHalfLineBuffer();
357 i2c_OLED_set_line(rowIndex);
358 i2c_OLED_send_string(lineBuffer);
360 #endif
362 #ifdef BARO
363 if (sensors(SENSOR_BARO)) {
364 int32_t alt = baroCalculateAltitude();
365 tfp_sprintf(lineBuffer, "Alt: %d", alt / 100);
366 padHalfLineBuffer();
367 i2c_OLED_set_xy(HALF_SCREEN_CHARACTER_COLUMN_COUNT, rowIndex);
368 i2c_OLED_send_string(lineBuffer);
370 #endif
374 void updateDisplay(void)
376 uint32_t now = micros();
377 static uint8_t previousArmedState = 0;
379 bool pageChanging = false;
380 bool updateNow = (int32_t)(now - nextDisplayUpdateAt) >= 0L;
382 if (!updateNow) {
383 return;
386 nextDisplayUpdateAt = now + DISPLAY_UPDATE_FREQUENCY;
388 bool armedState = ARMING_FLAG(ARMED) ? true : false;
389 bool armedStateChanged = armedState != previousArmedState;
390 previousArmedState = armedState;
392 if (armedState) {
393 if (!armedStateChanged) {
394 return;
396 currentPageId = PAGE_ARMED;
397 pageChanging = true;
398 } else {
399 if (armedStateChanged) {
400 currentPageId = PAGE_STATUS;
401 pageChanging = true;
404 if ((currentPageId == PAGE_WELCOME) && ((int32_t)(now - nextPageAt) >= 0L)) {
405 currentPageId = PAGE_STATUS;
406 pageChanging = true;
409 if (forcePageChange) {
410 pageChanging = true;
411 forcePageChange = false;
415 if (pageChanging) {
416 // Some OLED displays do not respond on the first initialisation so refresh the display
417 // when the page changes in the hopes the hardware responds. This also allows the
418 // user to power off/on the display or connect it while powered.
419 if (!displayPresent) {
420 resetDisplay();
423 if (!displayPresent) {
424 return;
427 i2c_OLED_clear_display_quick();
428 showTitle();
431 if (!displayPresent) {
432 return;
435 switch(currentPageId) {
436 case PAGE_WELCOME:
437 showWelcomePage();
438 break;
439 case PAGE_ARMED:
440 showArmedPage();
441 break;
442 case PAGE_STATUS:
443 showStatusPage();
444 break;
447 if (!armedState) {
448 updateFailsafeStatus();
449 updateRxStatus();
450 updateTicker();
454 void displaySetPage(pageId_e newPageId)
456 currentPageId = newPageId;
457 forcePageChange = true;
460 void displayInit(rxConfig_t *rxConfigToUse)
462 delay(200);
463 resetDisplay();
464 delay(200);
466 rxConfig = rxConfigToUse;
468 displaySetPage(PAGE_WELCOME);
469 displaySetNextPageChangeAt(micros() + (1000 * 1000 * 5));
471 updateDisplay();
474 void displaySetNextPageChangeAt(uint32_t futureMicros)
476 nextPageAt = futureMicros;
478 #endif