From 2b373048c695a59eeb14c6d53553228d5f6cd45c Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Fri, 16 Oct 2020 17:00:27 +0800 Subject: [PATCH 1/8] fix battery voltage sensing on NRF52 boards --- src/Power.cpp | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Power.cpp b/src/Power.cpp index 12a63811..62d07003 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -18,6 +18,21 @@ Power *power; using namespace meshtastic; +#if defined(NRF52_SERIES) +/* + * Internal Reference is +/-0.6V, with an adjustable gain of 1/6, 1/5, 1/4, + * 1/3, 1/2 or 1, meaning 3.6, 3.0, 2.4, 1.8, 1.2 or 0.6V for the ADC levels. + * + * External Reference is VDD/4, with an adjustable gain of 1, 2 or 4, meaning + * VDD/4, VDD/2 or VDD for the ADC levels. + * + * Default settings are internal reference with 1/6 gain (GND..3.6V ADC range) + */ +#define AREF_VOLTAGE 3.6 +#else +#define AREF_VOLTAGE 3.3 +#endif + /** * If this board has a battery level sensor, set this to a valid implementation */ @@ -48,9 +63,13 @@ class AnalogBatteryLevel : public HasBatteryLevel */ virtual float getBattVoltage() { + uint32_t raw = analogRead(BATTERY_PIN); + + // Tested ttgo eink nrf52 board and the reported value is perfect + // DEBUG_MSG("raw val %u", raw); return #ifdef BATTERY_PIN - 1000.0 * analogRead(BATTERY_PIN) * 2.0 * (3.3 / 1024.0); + 1000.0 * 2.0 * (AREF_VOLTAGE / 1024.0) * raw; #else NAN; #endif @@ -68,10 +87,18 @@ bool Power::analogInit() { #ifdef BATTERY_PIN DEBUG_MSG("Using analog input for battery level\n"); + + // disable any internal pullups + pinMode(BATTERY_PIN, INPUT); + #ifndef NO_ESP32 // ESP32 needs special analog stuff adcAttachPin(BATTERY_PIN); #endif +#ifdef NRF52_SERIES + analogReference(AR_INTERNAL); // 3.6V +#endif + // adcStart(BATTERY_PIN); analogReadResolution(10); // Default of 12 is not very linear. Recommended to use 10 or 11 depending on needed resolution. batteryLevel = &analogLevel; From f3b93d55fbb4bc5e55025a96878cbaf8758968b4 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Fri, 16 Oct 2020 17:03:04 +0800 Subject: [PATCH 2/8] oops fix for esp32 --- src/Power.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Power.cpp b/src/Power.cpp index 62d07003..7d67c733 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -63,13 +63,11 @@ class AnalogBatteryLevel : public HasBatteryLevel */ virtual float getBattVoltage() { - uint32_t raw = analogRead(BATTERY_PIN); - // Tested ttgo eink nrf52 board and the reported value is perfect // DEBUG_MSG("raw val %u", raw); return #ifdef BATTERY_PIN - 1000.0 * 2.0 * (AREF_VOLTAGE / 1024.0) * raw; + 1000.0 * 2.0 * (AREF_VOLTAGE / 1024.0) * analogRead(BATTERY_PIN); #else NAN; #endif From 485c476f1728b7f271970b8f527a31add5773c93 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 18 Oct 2020 09:32:12 +0800 Subject: [PATCH 3/8] cleaner battery debug messages --- src/Power.cpp | 3 ++- src/PowerStatus.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Power.cpp b/src/Power.cpp index 7d67c733..3cb43427 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -146,7 +146,8 @@ void Power::readPowerStatus() const PowerStatus powerStatus = PowerStatus(hasBattery ? OptTrue : OptFalse, batteryLevel->isVBUSPlug() ? OptTrue : OptFalse, batteryLevel->isChargeing() ? OptTrue : OptFalse, batteryVoltageMv, batteryChargePercent); - DEBUG_MSG("Read power stat %d\n", powerStatus.getHasUSB()); + DEBUG_MSG("Battery: hasUSB=%d, isCharging=%d, batMv=%d, batPct=%d\n", powerStatus.getHasUSB(), + powerStatus.getIsCharging(), powerStatus.getBatteryVoltageMv(), powerStatus.getBatteryChargePercent()); newStatus.notifyObservers(&powerStatus); // If we have a battery at all and it is less than 10% full, force deep sleep diff --git a/src/PowerStatus.h b/src/PowerStatus.h index c9bb89a0..e9c192fb 100644 --- a/src/PowerStatus.h +++ b/src/PowerStatus.h @@ -82,7 +82,7 @@ class PowerStatus : public Status isCharging = newStatus->isCharging; } if (isDirty) { - DEBUG_MSG("Battery %dmV %d%%\n", batteryVoltageMv, batteryChargePercent); + // DEBUG_MSG("Battery %dmV %d%%\n", batteryVoltageMv, batteryChargePercent); onNewStatus.notifyObservers(this); } return 0; From 8fd3cb1aac44228a7307d96b756d3c2952ebc09b Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 18 Oct 2020 09:44:29 +0800 Subject: [PATCH 4/8] add crude charging detection for 'dumb' voltage based battery sensors --- src/Power.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/Power.cpp b/src/Power.cpp index 3cb43427..ae51e803 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -52,10 +52,13 @@ class AnalogBatteryLevel : public HasBatteryLevel { float v = getBattVoltage() / 1000; - if (v < 2.1) + if (v < noBatVolt) return -1; // If voltage is super low assume no battery installed - return 100 * (v - 3.27) / (4.2 - 3.27); + if (v > chargingVolt) + return 0; // While charging we can't report % full on the battery + + return 100 * (v - emptyVolt) / (fullVolt - emptyVolt); } /** @@ -76,7 +79,20 @@ class AnalogBatteryLevel : public HasBatteryLevel /** * return true if there is a battery installed in this unit */ - virtual bool isBatteryConnect() { return getBattVoltage() != -1; } + virtual bool isBatteryConnect() { return getBattPercentage() != -1; } + + /// If we see a battery voltage higher than physics allows - assume charger is pumping + /// in power + virtual bool isVBUSPlug() { return getBattVoltage() > chargingVolt; } + + /// Assume charging if we have a battery and external power is connected. + /// we can't be smart enough to say 'full'? + virtual bool isChargeing() { return isBatteryConnect() && isVBUSPlug(); } + + private: + /// If we see a battery voltage higher than physics allows - assume charger is pumping + /// in power + const float fullVolt = 4.2, emptyVolt = 3.27, chargingVolt = 4.3, noBatVolt = 2.1; } analogLevel; Power::Power() : OSThread("Power") {} @@ -146,7 +162,7 @@ void Power::readPowerStatus() const PowerStatus powerStatus = PowerStatus(hasBattery ? OptTrue : OptFalse, batteryLevel->isVBUSPlug() ? OptTrue : OptFalse, batteryLevel->isChargeing() ? OptTrue : OptFalse, batteryVoltageMv, batteryChargePercent); - DEBUG_MSG("Battery: hasUSB=%d, isCharging=%d, batMv=%d, batPct=%d\n", powerStatus.getHasUSB(), + DEBUG_MSG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d\n", powerStatus.getHasUSB(), powerStatus.getIsCharging(), powerStatus.getBatteryVoltageMv(), powerStatus.getBatteryChargePercent()); newStatus.notifyObservers(&powerStatus); From ad7a474a52c0761df5bcc3ef849f09b80d3bf4db Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 21 Oct 2020 12:48:04 +0800 Subject: [PATCH 5/8] update buildscript to generate universal/regionless roms --- bin/build-all.sh | 83 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/bin/build-all.sh b/bin/build-all.sh index 63dfc01c..55c816d0 100755 --- a/bin/build-all.sh +++ b/bin/build-all.sh @@ -9,11 +9,10 @@ COUNTRIES="US EU433 EU865 CN JP ANZ KR" #COUNTRIES=CN BOARDS_ESP32="tlora-v2 tlora-v1 tlora-v2-1-1.6 tbeam heltec tbeam0.7" +# BOARDS_ESP32=tbeam # FIXME note nrf52840dk build is for some reason only generating a BIN file but not a HEX file nrf52840dk-geeksville is fine BOARDS_NRF52="lora-relay-v1" -BOARDS="$BOARDS_ESP32 $BOARDS_NRF52" -#BOARDS=tbeam OUTDIR=release/latest @@ -22,22 +21,61 @@ ARCHIVEDIR=release/archive rm -f $OUTDIR/firmware* -mkdir -p $OUTDIR/bins $OUTDIR/elfs -rm -f $OUTDIR/bins/* +mkdir -p $OUTDIR/bins +rm -r $OUTDIR/bins/* +mkdir -p $OUTDIR/bins/universal $OUTDIR/elfs/universal # build the named environment and copy the bins to the release directory -function do_build { - echo "Building for $BOARD with $PLATFORMIO_BUILD_FLAGS" +function do_build() { + BOARD=$1 + COUNTRY=$2 + isNrf=$3 + + echo "Building $COUNTRY for $BOARD with $PLATFORMIO_BUILD_FLAGS" rm -f .pio/build/$BOARD/firmware.* # The shell vars the build tool expects to find - export HW_VERSION="1.0-$COUNTRY" export APP_VERSION=$VERSION - export COUNTRY + + # Are we building a universal/regionless rom? + if [ "x$COUNTRY" != "x" ] + then + export HW_VERSION="1.0-$COUNTRY" + export COUNTRY + basename=firmware-$BOARD-$COUNTRY-$VERSION + else + export HW_VERSION="1.0" + unset COUNTRY + basename=universal/firmware-$BOARD-$VERSION + fi pio run --jobs 4 --environment $BOARD # -v SRCELF=.pio/build/$BOARD/firmware.elf - cp $SRCELF $OUTDIR/elfs/firmware-$BOARD-$COUNTRY-$VERSION.elf + cp $SRCELF $OUTDIR/elfs/$basename.elf + + if [ "$isNrf" = "false" ] + then + echo "Copying ESP32 bin file" + SRCBIN=.pio/build/$BOARD/firmware.bin + cp $SRCBIN $OUTDIR/bins/$basename.bin + else + echo "Generating NRF52 uf2 file" + SRCHEX=.pio/build/$BOARD/firmware.hex + bin/uf2conv.py $SRCHEX -c -o $OUTDIR/bins/$basename.uf2 -f 0xADA52840 + fi +} + +function do_boards() { + BOARDS=$1 + isNrf=$2 + for board in $BOARDS; do + for country in $COUNTRIES; do + do_build $board $country "$isNrf" + done + + # Build universal + do_build $board "" "$isNrf" + done } # Make sure our submodules are current @@ -46,26 +84,16 @@ git submodule update # Important to pull latest version of libs into all device flavors, otherwise some devices might be stale platformio lib update -for COUNTRY in $COUNTRIES; do - for BOARD in $BOARDS; do - do_build $BOARD - done - - echo "Copying ESP32 bin files" - for BOARD in $BOARDS_ESP32; do - SRCBIN=.pio/build/$BOARD/firmware.bin - cp $SRCBIN $OUTDIR/bins/firmware-$BOARD-$COUNTRY-$VERSION.bin - done - - echo "Generating NRF52 uf2 files" - for BOARD in $BOARDS_NRF52; do - SRCHEX=.pio/build/$BOARD/firmware.hex - bin/uf2conv.py $SRCHEX -c -o $OUTDIR/bins/firmware-$BOARD-$COUNTRY-$VERSION.uf2 -f 0xADA52840 - done -done +do_boards $BOARDS_ESP32 "false" +do_boards $BOARDS_NRF52 "true" # keep the bins in archive also -cp $OUTDIR/bins/firmware* $OUTDIR/elfs/firmware* $ARCHIVEDIR +cp $OUTDIR/bins/firmware* $OUTDIR/elfs/firmware* $OUTDIR/bins/universal/firmware* $OUTDIR/elfs/universal/firmware* $ARCHIVEDIR + +echo Updating android bins $OUTDIR/forandroid +rm -rf $OUTDIR/forandroid +mkdir -p $OUTDIR/forandroid +cp -a $OUTDIR/bins/universal/*.bin $OUTDIR/forandroid/ cat >$OUTDIR/curfirmwareversion.xml < @@ -79,6 +107,7 @@ Generated by bin/buildall.sh --> XML +echo Generating $ARCHIVEDIR/firmware-$VERSION.zip rm -f $ARCHIVEDIR/firmware-$VERSION.zip zip --junk-paths $ARCHIVEDIR/firmware-$VERSION.zip $OUTDIR/bins/firmware-*-$VERSION.* images/system-info.bin bin/device-install.sh bin/device-update.sh From 0b3c25f6d954856c1f7f226242ad003e028596c0 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 21 Oct 2020 16:50:09 +0800 Subject: [PATCH 6/8] use correct code for "talking to phone" fixes OTA update while a router --- src/esp32/BluetoothSoftwareUpdate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/esp32/BluetoothSoftwareUpdate.cpp b/src/esp32/BluetoothSoftwareUpdate.cpp index 4fa518cc..12e30d1c 100644 --- a/src/esp32/BluetoothSoftwareUpdate.cpp +++ b/src/esp32/BluetoothSoftwareUpdate.cpp @@ -72,7 +72,7 @@ int update_data_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_ crc.update(data, len); Update.write(data, len); updateActualSize += len; - powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG); // Not exactly correct, but we want to force the device to not sleep now + powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); return 0; } From a5d7bacdbf3523fb0f0c4286daf0bbe155c54302 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 21 Oct 2020 17:27:13 +0800 Subject: [PATCH 7/8] Show current region on the boot screen --- src/graphics/Screen.cpp | 17 ++++++++++------- src/main.cpp | 19 +++++++++++-------- src/mesh/MeshRadio.h | 5 ++++- src/mesh/NodeDB.cpp | 3 +++ src/mesh/RadioInterface.cpp | 29 ++++++++++++++++------------- 5 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 350b27d4..b6e626fd 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -92,21 +92,24 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1 // needs to be drawn relative to x and y // draw centered left to right and centered above the one line of app text - display->drawXbm(x + (SCREEN_WIDTH - icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - icon_height) / 2, icon_width, - icon_height, (const uint8_t *)icon_bits); + display->drawXbm(x + (SCREEN_WIDTH - icon_width) / 2, + y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - icon_height) / 2 + 2, + icon_width, icon_height, (const uint8_t *)icon_bits); display->setFont(FONT_MEDIUM); display->setTextAlignment(TEXT_ALIGN_LEFT); const char *title = "meshtastic.org"; display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title); display->setFont(FONT_SMALL); - const char *region = xstr(HW_VERSION); - if (*region && region[3] == '-') // Skip past 1.0- in the 1.0-EU865 string - region += 4; + + const char *region = myRegion ? myRegion->name : NULL; + if (region) + display->drawString(x + 0, y + 0, region); + char buf[16]; snprintf(buf, sizeof(buf), "%s", xstr(APP_VERSION)); // Note: we don't bother printing region or now, it makes the string too long - display->drawString(SCREEN_WIDTH - 20, 0, buf); + display->drawString(x + SCREEN_WIDTH - display->getStringWidth(buf), y + 0, buf); screen->forceDisplay(); } @@ -1139,7 +1142,7 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg) // DEBUG_MSG("Screen got status update %d\n", arg->getStatusType()); switch (arg->getStatusType()) { case STATUS_TYPE_NODE: - if (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) { + if (showingNormalScreen && (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal())) { setFrames(); // Regen the list of screens } nodeDB.updateGUI = false; diff --git a/src/main.cpp b/src/main.cpp index e258ec52..2781b770 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -290,14 +290,6 @@ void setup() // Initialize the screen first so we can show the logo while we start up everything else. screen = new graphics::Screen(SSD1306_ADDRESS); -#if defined(ST7735_CS) || defined(HAS_EINK) - screen->setup(); -#else - if (ssd1306_found) - screen->setup(); -#endif - - screen->print("Started...\n"); readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time) @@ -338,6 +330,17 @@ void setup() service.init(); + // Don't call screen setup until after nodedb is setup (because we need + // the current region name) +#if defined(ST7735_CS) || defined(HAS_EINK) + screen->setup(); +#else + if (ssd1306_found) + screen->setup(); +#endif + + screen->print("Started...\n"); + // We have now loaded our saved preferences from flash // ONCE we will factory reset the GPS for bug #327 diff --git a/src/mesh/MeshRadio.h b/src/mesh/MeshRadio.h index 2cee8f89..be99032f 100644 --- a/src/mesh/MeshRadio.h +++ b/src/mesh/MeshRadio.h @@ -16,4 +16,7 @@ struct RegionInfo { const char *name; // EU433 etc }; -extern const RegionInfo regions[]; \ No newline at end of file +extern const RegionInfo regions[]; +extern const RegionInfo *myRegion; + +extern void initRegion(); \ No newline at end of file diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index cf47cfd1..1febc0ae 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -245,6 +245,9 @@ void NodeDB::init() } } + // Update the global myRegion + initRegion(); + strncpy(myNodeInfo.firmware_version, optstr(APP_VERSION), sizeof(myNodeInfo.firmware_version)); strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model)); diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 8fefd80f..c35075c6 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -26,7 +26,20 @@ const RegionInfo regions[] = { RDEF(Unset, 903.08f, 2.16f, 13, 0) // Assume US freqs if unset, Must be last }; -static const RegionInfo *myRegion; +const RegionInfo *myRegion; + +void initRegion() +{ + if (!myRegion) { + const RegionInfo *r = regions; + for (; r->code != RegionCode_Unset && r->code != radioConfig.preferences.region; r++) + ; + myRegion = r; + DEBUG_MSG("Wanted region %d, using %s\n", radioConfig.preferences.region, r->name); + + myNodeInfo.num_channels = myRegion->numChannels; // Tell our android app how many channels we have + } +} /** * ## LoRaWAN for North America @@ -91,20 +104,10 @@ void printPacket(const char *prefix, const MeshPacket *p) DEBUG_MSG(")\n"); } -RadioInterface::RadioInterface() +RadioInterface::RadioInterface() { assert(sizeof(PacketHeader) == 4 || sizeof(PacketHeader) == 16); // make sure the compiler did what we expected - if (!myRegion) { - const RegionInfo *r = regions; - for (; r->code != RegionCode_Unset && r->code != radioConfig.preferences.region; r++) - ; - myRegion = r; - DEBUG_MSG("Wanted region %d, using %s\n", radioConfig.preferences.region, r->name); - - myNodeInfo.num_channels = myRegion->numChannels; // Tell our android app how many channels we have - } - // Can't print strings this early - serial not setup yet // DEBUG_MSG("Set meshradio defaults name=%s\n", channelSettings.name); } @@ -120,7 +123,7 @@ bool RadioInterface::init() // we now expect interfaces to operate in promiscous mode // radioIf.setThisAddress(nodeDB.getNodeNum()); // Note: we must do this here, because the nodenum isn't inited at constructor // time. - + return true; } From 14c4022c1871aaa32d99edf6c6b4e6d6188ec3cf Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 21 Oct 2020 19:18:03 +0800 Subject: [PATCH 8/8] 1.1.6 (and screen layout tweaks) --- bin/build-all.sh | 10 +++++----- bin/version.sh | 2 +- src/graphics/Screen.cpp | 5 ++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/bin/build-all.sh b/bin/build-all.sh index 55c816d0..9f0888f0 100755 --- a/bin/build-all.sh +++ b/bin/build-all.sh @@ -66,9 +66,9 @@ function do_build() { } function do_boards() { - BOARDS=$1 - isNrf=$2 - for board in $BOARDS; do + declare boards=$1 + declare isNrf=$2 + for board in $boards; do for country in $COUNTRIES; do do_build $board $country "$isNrf" done @@ -84,8 +84,8 @@ git submodule update # Important to pull latest version of libs into all device flavors, otherwise some devices might be stale platformio lib update -do_boards $BOARDS_ESP32 "false" -do_boards $BOARDS_NRF52 "true" +do_boards "$BOARDS_ESP32" "false" +do_boards "$BOARDS_NRF52" "true" # keep the bins in archive also cp $OUTDIR/bins/firmware* $OUTDIR/elfs/firmware* $OUTDIR/bins/universal/firmware* $OUTDIR/elfs/universal/firmware* $ARCHIVEDIR diff --git a/bin/version.sh b/bin/version.sh index ecc6ad74..4e8e08ae 100644 --- a/bin/version.sh +++ b/bin/version.sh @@ -1,3 +1,3 @@ -export VERSION=1.1.5 \ No newline at end of file +export VERSION=1.1.6 \ No newline at end of file diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index b6e626fd..7773cdc3 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -92,9 +92,8 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1 // needs to be drawn relative to x and y // draw centered left to right and centered above the one line of app text - display->drawXbm(x + (SCREEN_WIDTH - icon_width) / 2, - y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - icon_height) / 2 + 2, - icon_width, icon_height, (const uint8_t *)icon_bits); + display->drawXbm(x + (SCREEN_WIDTH - icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - icon_height) / 2 + 2, + icon_width, icon_height, (const uint8_t *)icon_bits); display->setFont(FONT_MEDIUM); display->setTextAlignment(TEXT_ALIGN_LEFT);