merged with master

This commit is contained in:
grcasanova
2020-07-06 21:53:10 +02:00
26 changed files with 898 additions and 354 deletions

View File

@@ -35,14 +35,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define FONT_HEIGHT 14 // actually 13 for "ariel 10" but want a little extra space
#define FONT_HEIGHT_16 (ArialMT_Plain_16[1] + 1)
#ifdef USE_SH1106
#define SCREEN_WIDTH 132
#else
// This means the *visible* area (sh1106 can address 132, but shows 128 for example)
#define SCREEN_WIDTH 128
#endif
#define SCREEN_HEIGHT 64
#define TRANSITION_FRAMERATE 30 // fps
#define IDLE_FRAMERATE 10 // in fps
#define IDLE_FRAMERATE 1 // in fps
#define COMPASS_DIAM 44
#define NUM_EXTRA_FRAMES 2 // text message and debug frame
@@ -55,7 +52,16 @@ static FrameCallback normalFrames[MAX_NUM_NODES + NUM_EXTRA_FRAMES];
static uint32_t targetFramerate = IDLE_FRAMERATE;
static char btPIN[16] = "888888";
uint8_t imgBattery[16] = {0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C};
uint8_t imgBattery[16] = { 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C };
uint8_t imgSatellite[8] = { 0x70, 0x71, 0x22, 0xFA, 0xFA, 0x22, 0x71, 0x70 };
uint32_t dopThresholds[5] = { 2000, 1000, 500, 200, 100 };
// if defined a pixel will blink to show redraws
// #define SHOW_REDRAWS
#ifdef SHOW_REDRAWS
static bool heartbeat = false;
#endif
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
@@ -143,38 +149,40 @@ static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char *
}
}
/// Draw a series of fields in a row, wrapping to multiple rows if needed
/// @return the max y we ended up printing to
static uint32_t drawRows(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
{
// The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT);
#if 0
/// Draw a series of fields in a row, wrapping to multiple rows if needed
/// @return the max y we ended up printing to
static uint32_t drawRows(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
{
// The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT);
const char **f = fields;
int xo = x, yo = y;
const int COLUMNS = 2; // hardwired for two columns per row....
int col = 0; // track which column we are on
while (*f) {
display->drawString(xo, yo, *f);
xo += SCREEN_WIDTH / COLUMNS;
// Wrap to next row, if needed.
if (++col >= COLUMNS) {
xo = x;
yo += FONT_HEIGHT;
col = 0;
const char **f = fields;
int xo = x, yo = y;
const int COLUMNS = 2; // hardwired for two columns per row....
int col = 0; // track which column we are on
while (*f) {
display->drawString(xo, yo, *f);
xo += SCREEN_WIDTH / COLUMNS;
// Wrap to next row, if needed.
if (++col >= COLUMNS) {
xo = x;
yo += FONT_HEIGHT;
col = 0;
}
f++;
}
if (col != 0) {
// Include last incomplete line in our total.
yo += FONT_HEIGHT;
}
f++;
}
if (col != 0) {
// Include last incomplete line in our total.
yo += FONT_HEIGHT;
}
return yo;
}
return yo;
}
#endif
// Draw power bars or a charging indicator on an image of a battery, determined by battery charge voltage or percentage.
static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, PowerStatus *powerStatus)
static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, const PowerStatus *powerStatus)
{
static const uint8_t powerBar[3] = {0x81, 0xBD, 0xBD};
static const uint8_t lightning[8] = {0xA1, 0xA1, 0xA5, 0xAD, 0xB5, 0xA5, 0x85, 0x85};
@@ -183,12 +191,12 @@ static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *img
imgBuffer[i] = 0x81;
}
// If charging, draw a charging indicator
if (powerStatus->charging) {
if (powerStatus->getIsCharging()) {
memcpy(imgBuffer + 3, lightning, 8);
// If not charging, Draw power bars
} else {
for (int i = 0; i < 4; i++) {
if (powerStatus->batteryChargePercent >= 25 * i)
if (powerStatus->getBatteryChargePercent() >= 25 * i)
memcpy(imgBuffer + 1 + (i * 3), powerBar, 3);
}
}
@@ -196,49 +204,50 @@ static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *img
}
// Draw nodes status
static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, int nodesOnline, int nodesTotal)
static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *nodeStatus)
{
char usersString[20];
sprintf(usersString, "%d/%d", nodesOnline, nodesTotal);
sprintf(usersString, "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal());
display->drawFastImage(x, y, 8, 8, imgUser);
display->drawString(x + 10, y - 2, usersString);
}
// Draw GPS status summary
static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, GPS *gps)
static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps)
{
if (!gps->isConnected) {
if (!gps->getIsConnected())
{
display->drawString(x, y - 2, "No GPS");
return;
}
display->drawFastImage(x, y, 6, 8, gps->hasLock() ? imgPositionSolid : imgPositionEmpty);
if (!gps->hasLock()) {
display->drawFastImage(x, y, 6, 8, gps->getHasLock() ? imgPositionSolid : imgPositionEmpty);
if (!gps->getHasLock())
{
display->drawString(x + 8, y - 2, "No sats");
return;
}
if (gps->dop <= 100) {
display->drawString(x + 8, y - 2, "Ideal");
return;
}
if (gps->dop <= 200) {
display->drawString(x + 8, y - 2, "Exc.");
return;
}
if (gps->dop <= 500) {
display->drawString(x + 8, y - 2, "Good");
return;
}
if (gps->dop <= 1000) {
display->drawString(x + 8, y - 2, "Mod.");
return;
}
if (gps->dop <= 2000) {
display->drawString(x + 8, y - 2, "Fair");
return;
}
if (gps->dop > 0) {
display->drawString(x + 8, y - 2, "Poor");
return;
}
else
{
char satsString[3];
uint8_t bar[2] = { 0 };
//Draw DOP signal bars
for(int i = 0; i < 5; i++)
{
if (gps->getDOP() <= dopThresholds[i])
bar[0] = ~((1 << (5 - i)) - 1);
else
bar[0] = 0b10000000;
//bar[1] = bar[0];
display->drawFastImage(x + 9 + (i * 2), y, 2, 8, bar);
}
//Draw satellite image
display->drawFastImage(x + 24, y, 8, 8, imgSatellite);
//Draw the number of satellites
sprintf(satsString, "%d", gps->getNumSatellites());
display->drawString(x + 34, y - 2, satsString);
}
}
@@ -381,28 +390,41 @@ static bool hasPosition(NodeInfo *n)
static size_t nodeIndex;
static int8_t prevFrame = -1;
// Draw the compass and arrow pointing to location
static void drawCompass(OLEDDisplay *display, int16_t compassX, int16_t compassY, float headingRadian)
// Draw the arrow pointing to a node's location
static void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, float headingRadian)
{
// display->drawXbm(compassX, compassY, compass_width, compass_height,
// (const uint8_t *)compass_bits);
Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially
float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
Point leftArrow(tip.x - arrowOffsetX, tip.y - arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y - arrowOffsetY);
Point *points[] = {&tip, &tail, &leftArrow, &rightArrow};
Point *arrowPoints[] = {&tip, &tail, &leftArrow, &rightArrow};
for (int i = 0; i < 4; i++) {
points[i]->rotate(headingRadian);
points[i]->scale(COMPASS_DIAM * 0.6);
points[i]->translate(compassX, compassY);
arrowPoints[i]->rotate(headingRadian);
arrowPoints[i]->scale(COMPASS_DIAM * 0.6);
arrowPoints[i]->translate(compassX, compassY);
}
drawLine(display, tip, tail);
drawLine(display, leftArrow, tip);
drawLine(display, rightArrow, tip);
}
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
// Draw the compass heading
static void drawCompassHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading)
{
Point N1(-0.04f, -0.65f), N2( 0.04f, -0.65f);
Point N3(-0.04f, -0.55f), N4( 0.04f, -0.55f);
Point *rosePoints[] = {&N1, &N2, &N3, &N4};
for (int i = 0; i < 4; i++) {
rosePoints[i]->rotate(myHeading);
rosePoints[i]->scale(COMPASS_DIAM);
rosePoints[i]->translate(compassX, compassY);
}
drawLine(display, N1, N3);
drawLine(display, N2, N4);
drawLine(display, N1, N4);
}
/// Convert an integer GPS coords to a floating point
@@ -422,10 +444,13 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes();
n = nodeDB.getNodeByIndex(nodeIndex);
}
// We just changed to a new node screen, ask that node for updated state
displayedNodeNum = n->num;
service.sendNetworkPing(displayedNodeNum, true);
// We just changed to a new node screen, ask that node for updated state if it's older than 2 minutes
if(sinceLastSeen(n) > 120)
{
service.sendNetworkPing(displayedNodeNum, true);
}
}
NodeInfo *node = nodeDB.getNodeByIndex(nodeIndex);
@@ -456,29 +481,40 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
// coordinates for the center of the compass/circle
int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 1, compassY = y + SCREEN_HEIGHT / 2;
int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 5, compassY = y + SCREEN_HEIGHT / 2;
bool hasNodeHeading = false;
if (ourNode && hasPosition(ourNode) && hasPosition(node)) { // display direction toward node
Position &op = ourNode->position, &p = node->position;
float d = latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
if (d < 2000)
snprintf(distStr, sizeof(distStr), "%.0f m", d);
else
snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000);
if(ourNode && hasPosition(ourNode))
{
Position &op = ourNode->position;
float myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
drawCompassHeading(display, compassX, compassY, myHeading);
// FIXME, also keep the guess at the operators heading and add/substract
// it. currently we don't do this and instead draw north up only.
float bearingToOther = bearing(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
float myHeading = estimatedHeading(DegD(p.latitude_i), DegD(p.longitude_i));
headingRadian = bearingToOther - myHeading;
drawCompass(display, compassX, compassY, headingRadian);
} else { // direction to node is unknown so display question mark
if(hasPosition(node))
{
// display direction toward node
hasNodeHeading = true;
Position &p = node->position;
float d = latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
if (d < 2000)
snprintf(distStr, sizeof(distStr), "%.0f m", d);
else
snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000);
// FIXME, also keep the guess at the operators heading and add/substract
// it. currently we don't do this and instead draw north up only.
float bearingToOther = bearing(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
headingRadian = bearingToOther - myHeading;
drawNodeHeading(display, compassX, compassY, headingRadian);
}
}
if(!hasNodeHeading)
// direction to node is unknown so display question mark
// Debug info for gps lock errors
// DEBUG_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node));
display->drawString(compassX - FONT_HEIGHT / 4, compassY - FONT_HEIGHT / 2, "?");
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
}
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
// Must be after distStr is populated
drawColumns(display, x, y, fields);
@@ -576,6 +612,11 @@ void Screen::setup()
// twice initially.
ui.update();
ui.update();
// Subscribe to status updates
powerStatusObserver.observe(&powerStatus->onNewStatus);
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
}
void Screen::doTask()
@@ -637,14 +678,7 @@ void Screen::doTask()
// While showing the bootscreen or Bluetooth pair screen all of our
// standard screen switching is stopped.
if (showingNormalScreen) {
// TODO(girts): decouple nodeDB from screen.
// standard screen loop handling ehre
// If the # nodes changes, we need to regen our list of screens
if (nodeDB.updateGUI || nodeDB.updateTextMessage) {
setFrames();
nodeDB.updateGUI = false;
nodeDB.updateTextMessage = false;
}
// standard screen loop handling here
}
ui.update();
@@ -669,8 +703,8 @@ void Screen::setFrames()
DEBUG_MSG("showing standard frames\n");
showingNormalScreen = true;
size_t numnodes = nodeDB.getNumNodes();
// We don't show the node info our our node (if we have it yet - we should)
size_t numnodes = nodeStatus->getNumTotal();
if (numnodes > 0)
numnodes--;
@@ -750,20 +784,26 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
snprintf(channelStr, sizeof(channelStr), "#%s", channelName.c_str());
// Display power status
if (powerStatus.haveBattery)
drawBattery(display, x, y + 2, imgBattery, &powerStatus);
if (powerStatus->getHasBattery())
drawBattery(display, x, y + 2, imgBattery, powerStatus);
else
display->drawFastImage(x, y + 2, 16, 8, powerStatus.usb ? imgUSB : imgPower);
display->drawFastImage(x, y + 2, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower);
// Display nodes status
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodesOnline, nodesTotal);
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus);
// Display GPS status
drawGPS(display, x + (SCREEN_WIDTH * 0.66), y + 2, gps);
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
}
const char *fields[] = {channelStr, nullptr};
uint32_t yo = drawRows(display, x, y + FONT_HEIGHT, fields);
display->drawString(x, y + FONT_HEIGHT, channelStr);
display->drawLogBuffer(x, yo);
display->drawLogBuffer(x, y + (FONT_HEIGHT * 2));
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
#ifdef SHOW_REDRAWS
if (heartbeat)
display->setPixel(0, 0);
heartbeat = !heartbeat;
#endif
}
// adjust Brightness cycle trough 1 to 254 as long as attachDuringLongPress is true
@@ -781,4 +821,20 @@ void Screen::adjustBrightness()
dispdev.setBrightness(brightness);
}
int Screen::handleStatusUpdate(const 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())
setFrames();
prevFrame = -1;
nodeDB.updateGUI = false;
nodeDB.updateTextMessage = false;
break;
}
setPeriod(1); // Update the screen right away
return 0;
}
} // namespace meshtastic