Changed measurements to be async to keep handling button presses

Implemented calibration screen
Implemented pre-move stabilization
Had to remove the nice font to save on program space :-(
This commit is contained in:
Mark van Renswoude 2020-02-01 16:55:33 +01:00
parent 1ca611e504
commit 7ec4fa3394
32 changed files with 920 additions and 392 deletions

View File

@ -3,7 +3,7 @@ As I really do not want to explain to my employer how I broke my desk, this docu
## Startup ## Startup
1. Height sensor must report at least 3 values within 5mm of each other in 500 ms intervals to be considered stable. 1. Height sensor must report at least 3 values within 10mm of each other to be considered stable.
1. If the EEPROM has not been initialized yet, the height offset setup screen must be displayed. 1. If the EEPROM has not been initialized yet, the height offset setup screen must be displayed.
1. Otherwise, the home screen must be displayed. 1. Otherwise, the home screen must be displayed.

View File

@ -4,7 +4,7 @@
https: //github.com/MvRens/AdafruitGFXFontTools https: //github.com/MvRens/AdafruitGFXFontTools
Source: FreeSansBold18pt7b.h Source: FreeSansBold18pt7b.h
Filter: [0-9\.mSTOPMenu] Filter: [ 0-9\.mSTOPMenuERCalibrt]
*/ */
@ -52,54 +52,101 @@ const uint8_t FreeSansBold18pt7bTrimmedBitmaps[] PROGMEM = {
0xE0, 0x3D, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x83, 0xE0, 0x3D, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x83,
0xF7, 0xE3, 0xFB, 0xFF, 0xFC, 0xFF, 0xFE, 0x3F, 0xDF, 0x07, 0xCF, 0x80, 0xF7, 0xE3, 0xFB, 0xFF, 0xFC, 0xFF, 0xFE, 0x3F, 0xDF, 0x07, 0xCF, 0x80,
0x07, 0x80, 0x03, 0xDF, 0x03, 0xE7, 0xC3, 0xE3, 0xFF, 0xF0, 0xFF, 0xF0, 0x07, 0x80, 0x03, 0xDF, 0x03, 0xE7, 0xC3, 0xE3, 0xFF, 0xF0, 0xFF, 0xF0,
0x3F, 0xF0, 0x07, 0xE0, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x3F, 0xF0, 0x07, 0xE0, 0x00, 0x00, 0xFF, 0x00, 0x07, 0xFF, 0x80, 0x3F,
0x00, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xC3, 0xF8, 0x1F, 0x87, 0xE0, 0x1F, 0x9F, 0x80,
0x81, 0xFF, 0xFF, 0x81, 0xFF, 0xFB, 0xC3, 0xDF, 0xFB, 0xC3, 0xDF, 0xFB, 0x1F, 0x3E, 0x00, 0x1F, 0x7C, 0x00, 0x3F, 0xF0, 0x00, 0x03, 0xE0, 0x00,
0xC3, 0xDF, 0xFB, 0xC3, 0xDF, 0xF9, 0xC7, 0xDF, 0xF9, 0xE7, 0x9F, 0xF9, 0x07, 0xC0, 0x00, 0x0F, 0x80, 0x00, 0x1F, 0x00, 0x00, 0x3E, 0x00, 0x00,
0xE7, 0x9F, 0xF9, 0xE7, 0x9F, 0xF9, 0xE7, 0x9F, 0xF8, 0xFF, 0x1F, 0xF8, 0x7C, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x7D, 0xF0, 0x00, 0xFB,
0xFF, 0x1F, 0xF8, 0xFF, 0x1F, 0xF8, 0xFF, 0x1F, 0xF8, 0x7F, 0x1F, 0xF8, 0xF0, 0x03, 0xF3, 0xF0, 0x0F, 0xC7, 0xF0, 0x3F, 0x87, 0xFF, 0xFE, 0x07,
0x7E, 0x1F, 0xF8, 0x7E, 0x1F, 0xF8, 0x7E, 0x1F, 0xF8, 0x3E, 0x1F, 0x00, 0xFF, 0xF8, 0x03, 0xFF, 0xC0, 0x01, 0xFE, 0x00, 0xFF, 0xFF, 0xDF, 0xFF,
0x7F, 0x00, 0x01, 0xFF, 0xF0, 0x01, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0x01, 0xFB, 0xFF, 0xFF, 0x7F, 0xFF, 0xEF, 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00,
0xFC, 0x1F, 0xC1, 0xF8, 0x03, 0xF1, 0xF8, 0x00, 0xFC, 0xF8, 0x00, 0x3E, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7F, 0xFF,
0x7C, 0x00, 0x1F, 0x7C, 0x00, 0x07, 0xFE, 0x00, 0x03, 0xFF, 0x00, 0x01, 0xCF, 0xFF, 0xF9, 0xFF, 0xFF, 0x3F, 0xFF, 0xE7, 0xC0, 0x00, 0xF8, 0x00,
0xFF, 0x80, 0x00, 0xFF, 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x3F, 0xF0, 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00,
0x1F, 0xF8, 0x00, 0x0F, 0xBE, 0x00, 0x0F, 0x9F, 0x00, 0x07, 0xCF, 0xC0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00,
0x07, 0xE3, 0xF0, 0x07, 0xE0, 0xFE, 0x0F, 0xE0, 0x7F, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0x81,
0xFF, 0xE0, 0x03, 0xFF, 0xE0, 0x00, 0x3F, 0x80, 0x00, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0x81, 0xFF, 0xFB, 0xC3,
0xFF, 0xE3, 0xFF, 0xFE, 0x7F, 0xFF, 0xEF, 0x80, 0xFF, 0xF0, 0x0F, 0xFE, 0xDF, 0xFB, 0xC3, 0xDF, 0xFB, 0xC3, 0xDF, 0xFB, 0xC3, 0xDF, 0xF9, 0xC7,
0x00, 0xFF, 0xC0, 0x1F, 0xF8, 0x03, 0xFF, 0x00, 0x7F, 0xE0, 0x1F, 0xFC, 0xDF, 0xF9, 0xE7, 0x9F, 0xF9, 0xE7, 0x9F, 0xF9, 0xE7, 0x9F, 0xF9, 0xE7,
0x07, 0xEF, 0xFF, 0xFD, 0xFF, 0xFF, 0x3F, 0xFF, 0xC7, 0xFF, 0xE0, 0xF8, 0x9F, 0xF8, 0xFF, 0x1F, 0xF8, 0xFF, 0x1F, 0xF8, 0xFF, 0x1F, 0xF8, 0xFF,
0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x1F, 0xF8, 0x7F, 0x1F, 0xF8, 0x7E, 0x1F, 0xF8, 0x7E, 0x1F, 0xF8, 0x7E,
0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x00, 0x07, 0x1F, 0xF8, 0x3E, 0x1F, 0x00, 0x7F, 0x00, 0x01, 0xFF, 0xF0, 0x01, 0xFF,
0xF8, 0x01, 0xFF, 0xF0, 0x3F, 0xFF, 0x87, 0xFF, 0xFC, 0x7E, 0x0F, 0xCF, 0xFC, 0x03, 0xFF, 0xFF, 0x01, 0xFC, 0x1F, 0xC1, 0xF8, 0x03, 0xF1, 0xF8,
0xC0, 0x7E, 0xF8, 0x03, 0xEF, 0x80, 0x3E, 0xF8, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0xFC, 0xF8, 0x00, 0x3E, 0x7C, 0x00, 0x1F, 0x7C, 0x00, 0x07, 0xFE,
0xFF, 0x00, 0x07, 0xFF, 0xC0, 0x3F, 0xFF, 0x81, 0xFF, 0xFC, 0x03, 0xFF, 0x00, 0x03, 0xFF, 0x00, 0x01, 0xFF, 0x80, 0x00, 0xFF, 0xC0, 0x00, 0x7F,
0xE0, 0x01, 0xFF, 0x00, 0x03, 0xF0, 0x00, 0x1F, 0xF8, 0x01, 0xFF, 0x80, 0xE0, 0x00, 0x3F, 0xF0, 0x00, 0x1F, 0xF8, 0x00, 0x0F, 0xBE, 0x00, 0x0F,
0x1F, 0xFC, 0x03, 0xFF, 0xE0, 0x7E, 0x7F, 0xFF, 0xE3, 0xFF, 0xFC, 0x1F, 0x9F, 0x00, 0x07, 0xCF, 0xC0, 0x07, 0xE3, 0xF0, 0x07, 0xE0, 0xFE, 0x0F,
0xFF, 0x00, 0x3F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xE0, 0x0F, 0xFF, 0xE0, 0x03, 0xFF, 0xE0, 0x00, 0x3F,
0xFF, 0xF0, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, 0x80, 0x00, 0xFF, 0xFC, 0x1F, 0xFF, 0xE3, 0xFF, 0xFE, 0x7F, 0xFF, 0xEF,
0xF0, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x03, 0x80, 0xFF, 0xF0, 0x0F, 0xFE, 0x00, 0xFF, 0xC0, 0x1F, 0xF8, 0x03, 0xFF,
0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0x00, 0x7F, 0xE0, 0x1F, 0xFC, 0x07, 0xEF, 0xFF, 0xFD, 0xFF, 0xFF, 0x3F,
0xC0, 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0xFF, 0xC7, 0xFF, 0xE0, 0xF8, 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7C,
0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xE0, 0x07, 0xFE, 0x03, 0xFF, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0xF8,
0xE0, 0xFF, 0xF8, 0x7E, 0x1F, 0x1F, 0x03, 0xCF, 0x80, 0xFB, 0xE0, 0x1E, 0x00, 0x1F, 0x00, 0x00, 0xFF, 0xFF, 0x07, 0xFF, 0xFE, 0x3F, 0xFF, 0xF9,
0xFF, 0xFF, 0xBF, 0xFF, 0xEF, 0xFF, 0xFB, 0xE0, 0x00, 0xF8, 0x00, 0x3F, 0xFF, 0xFF, 0xCF, 0x80, 0x3F, 0x7C, 0x00, 0xFB, 0xE0, 0x07, 0xDF, 0x00,
0x03, 0xE7, 0xE1, 0xF9, 0xFF, 0xFC, 0x3F, 0xFE, 0x07, 0xFF, 0x00, 0x7F, 0x3E, 0xF8, 0x01, 0xF7, 0xC0, 0x0F, 0x3E, 0x00, 0xF9, 0xFF, 0xFF, 0x8F,
0x00, 0xF8, 0xF8, 0x3F, 0x1F, 0x7F, 0x9F, 0xF3, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xF8, 0x7F, 0xFF, 0xC3, 0xFF, 0xFF, 0x1F, 0x00, 0xFC, 0xF8, 0x03,
0xFF, 0xFF, 0xFF, 0xC3, 0xF8, 0x7F, 0xF8, 0x3F, 0x07, 0xFE, 0x07, 0xC0, 0xE7, 0xC0, 0x1F, 0x3E, 0x00, 0xF9, 0xF0, 0x07, 0xCF, 0x80, 0x3E, 0x7C,
0xFF, 0xC0, 0xF8, 0x1F, 0xF8, 0x1F, 0x03, 0xFF, 0x03, 0xE0, 0x7F, 0xE0, 0x01, 0xF3, 0xE0, 0x0F, 0x9F, 0x00, 0x7C, 0xF8, 0x03, 0xF7, 0xC0, 0x0F,
0x7C, 0x0F, 0xFC, 0x0F, 0x81, 0xFF, 0x81, 0xF0, 0x3F, 0xF0, 0x3E, 0x07, 0xC0, 0x07, 0xF8, 0x01, 0xFF, 0xF0, 0x3F, 0xFF, 0x87, 0xFF, 0xFC, 0x7E,
0xFE, 0x07, 0xC0, 0xFF, 0xC0, 0xF8, 0x1F, 0xF8, 0x1F, 0x03, 0xFF, 0x03, 0x0F, 0xCF, 0xC0, 0x7E, 0xF8, 0x03, 0xEF, 0x80, 0x3E, 0xF8, 0x00, 0x0F,
0xE0, 0x7F, 0xE0, 0x7C, 0x0F, 0x80, 0xF8, 0xF8, 0x7D, 0xFF, 0x3F, 0xFF, 0xC0, 0x00, 0xFF, 0x00, 0x07, 0xFF, 0xC0, 0x3F, 0xFF, 0x81, 0xFF, 0xFC,
0xDF, 0xFF, 0xEF, 0xE1, 0xFF, 0xE0, 0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x03, 0xFF, 0xE0, 0x01, 0xFF, 0x00, 0x03, 0xF0, 0x00, 0x1F, 0xF8, 0x01,
0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x01, 0xFF, 0x80, 0xFF, 0xC0, 0x7F, 0xFF, 0x80, 0x1F, 0xFC, 0x03, 0xFF, 0xE0, 0x7E, 0x7F, 0xFF, 0xE3, 0xFF,
0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xE0, 0xF8, 0xFC, 0x1F, 0xFF, 0x00, 0x3F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x01, 0xFF, 0x80, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x0F,
0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x01, 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x1F,
0xFF, 0x80, 0xFF, 0xC0, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0x9F, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x3E,
0xF7, 0xC7, 0xE3, 0xE0 }; 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7C,
0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xF8, 0x07, 0xFF,
0x83, 0xFF, 0xF1, 0xFF, 0xFE, 0x7C, 0x1F, 0xBE, 0x03, 0xE0, 0x00, 0xF8,
0x01, 0xFE, 0x0F, 0xFF, 0x8F, 0xFF, 0xE7, 0xF8, 0xFB, 0xF0, 0x3E, 0xF8,
0x0F, 0xBE, 0x07, 0xEF, 0xC3, 0xFB, 0xFF, 0xFE, 0x7F, 0xFF, 0x8F, 0xFB,
0xF1, 0xF8, 0xFC, 0xF8, 0x00, 0x3E, 0x00, 0x0F, 0x80, 0x03, 0xE0, 0x00,
0xF8, 0x00, 0x3E, 0x00, 0x0F, 0x80, 0x03, 0xE7, 0xE0, 0xFB, 0xFC, 0x3F,
0xFF, 0xCF, 0xFF, 0xF3, 0xF8, 0x7E, 0xFC, 0x0F, 0xBF, 0x03, 0xFF, 0x80,
0x7F, 0xE0, 0x1F, 0xF8, 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x7F, 0xF0, 0x3F,
0xFC, 0x0F, 0xBF, 0x87, 0xEF, 0xFF, 0xF3, 0xFF, 0xFC, 0xFB, 0xFC, 0x3E,
0x7E, 0x00, 0x07, 0xE0, 0x07, 0xFE, 0x03, 0xFF, 0xE0, 0xFF, 0xF8, 0x7E,
0x1F, 0x1F, 0x03, 0xCF, 0x80, 0xFB, 0xE0, 0x1E, 0xFF, 0xFF, 0xBF, 0xFF,
0xEF, 0xFF, 0xFB, 0xE0, 0x00, 0xF8, 0x00, 0x3F, 0x03, 0xE7, 0xE1, 0xF9,
0xFF, 0xFC, 0x3F, 0xFE, 0x07, 0xFF, 0x00, 0x7F, 0x00, 0xFF, 0xFF, 0xF0,
0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xF8, 0xF8, 0x3F, 0x1F, 0x7F,
0x9F, 0xF3, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xC3, 0xF8, 0x7F,
0xF8, 0x3F, 0x07, 0xFE, 0x07, 0xC0, 0xFF, 0xC0, 0xF8, 0x1F, 0xF8, 0x1F,
0x03, 0xFF, 0x03, 0xE0, 0x7F, 0xE0, 0x7C, 0x0F, 0xFC, 0x0F, 0x81, 0xFF,
0x81, 0xF0, 0x3F, 0xF0, 0x3E, 0x07, 0xFE, 0x07, 0xC0, 0xFF, 0xC0, 0xF8,
0x1F, 0xF8, 0x1F, 0x03, 0xFF, 0x03, 0xE0, 0x7F, 0xE0, 0x7C, 0x0F, 0x80,
0xF8, 0xF8, 0x7D, 0xFF, 0x3F, 0xFF, 0xDF, 0xFF, 0xEF, 0xE1, 0xFF, 0xE0,
0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF,
0x01, 0xFF, 0x80, 0xFF, 0xC0, 0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F,
0xFC, 0x07, 0xFE, 0x03, 0xE0, 0xF8, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xE1,
0xF8, 0x3E, 0x07, 0xC0, 0xF8, 0x1F, 0x03, 0xE0, 0x7C, 0x0F, 0x81, 0xF0,
0x3E, 0x07, 0xC0, 0xF8, 0x1F, 0x03, 0xE0, 0x00, 0x3E, 0x1F, 0x0F, 0x87,
0xC3, 0xE7, 0xFF, 0xFF, 0xFF, 0x3E, 0x1F, 0x0F, 0x87, 0xC3, 0xE1, 0xF0,
0xF8, 0x7C, 0x3E, 0x1F, 0x0F, 0x87, 0xF3, 0xF8, 0xFC, 0x3E, 0xF8, 0x0F,
0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x01, 0xFF, 0x80, 0xFF, 0xC0, 0x7F, 0xE0,
0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x01, 0xFF,
0x80, 0xFF, 0xC0, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0x9F, 0xF7,
0xC7, 0xE3, 0xE0 };
const GFXglyph FreeSansBold18pt7bTrimmedGlyphs[] PROGMEM = { const GFXglyph FreeSansBold18pt7bTrimmedGlyphs[] PROGMEM = {
{ 0, 0, 0, 10, 0, 1 }, // 0x20 ' '
{ 0, 0, 0, 0, 0, 0 }, // 0x21 '!'
{ 0, 0, 0, 0, 0, 0 }, // 0x22 '"'
{ 0, 0, 0, 0, 0, 0 }, // 0x23 '#'
{ 0, 0, 0, 0, 0, 0 }, // 0x24 '$'
{ 0, 0, 0, 0, 0, 0 }, // 0x25 '%'
{ 0, 0, 0, 0, 0, 0 }, // 0x26 '&'
{ 0, 0, 0, 0, 0, 0 }, // 0x27 '''
{ 0, 0, 0, 0, 0, 0 }, // 0x28 '('
{ 0, 0, 0, 0, 0, 0 }, // 0x29 ')'
{ 0, 0, 0, 0, 0, 0 }, // 0x2A '*'
{ 0, 0, 0, 0, 0, 0 }, // 0x2B '+'
{ 0, 0, 0, 0, 0, 0 }, // 0x2C ','
{ 0, 0, 0, 0, 0, 0 }, // 0x2D '-'
{ 0, 5, 5, 9, 2, -4 }, // 0x2E '.' { 0, 5, 5, 9, 2, -4 }, // 0x2E '.'
{ 0, 0, 0, 0, 0, 0 }, // 0x2F '/' { 0, 0, 0, 0, 0, 0 }, // 0x2F '/'
{ 4, 17, 25, 19, 1, -24 }, // 0x30 '0' { 4, 17, 25, 19, 1, -24 }, // 0x30 '0'
@ -121,9 +168,9 @@ const GFXglyph FreeSansBold18pt7bTrimmedGlyphs[] PROGMEM = {
{ 0, 0, 0, 0, 0, 0 }, // 0x40 '@' { 0, 0, 0, 0, 0, 0 }, // 0x40 '@'
{ 0, 0, 0, 0, 0, 0 }, // 0x41 'A' { 0, 0, 0, 0, 0, 0 }, // 0x41 'A'
{ 0, 0, 0, 0, 0, 0 }, // 0x42 'B' { 0, 0, 0, 0, 0, 0 }, // 0x42 'B'
{ 0, 0, 0, 0, 0, 0 }, // 0x43 'C' { 521, 23, 26, 25, 1, -25 }, // 0x43 'C'
{ 0, 0, 0, 0, 0, 0 }, // 0x44 'D' { 0, 0, 0, 0, 0, 0 }, // 0x44 'D'
{ 0, 0, 0, 0, 0, 0 }, // 0x45 'E' { 596, 19, 26, 23, 3, -25 }, // 0x45 'E'
{ 0, 0, 0, 0, 0, 0 }, // 0x46 'F' { 0, 0, 0, 0, 0, 0 }, // 0x46 'F'
{ 0, 0, 0, 0, 0, 0 }, // 0x47 'G' { 0, 0, 0, 0, 0, 0 }, // 0x47 'G'
{ 0, 0, 0, 0, 0, 0 }, // 0x48 'H' { 0, 0, 0, 0, 0, 0 }, // 0x48 'H'
@ -131,14 +178,14 @@ const GFXglyph FreeSansBold18pt7bTrimmedGlyphs[] PROGMEM = {
{ 0, 0, 0, 0, 0, 0 }, // 0x4A 'J' { 0, 0, 0, 0, 0, 0 }, // 0x4A 'J'
{ 0, 0, 0, 0, 0, 0 }, // 0x4B 'K' { 0, 0, 0, 0, 0, 0 }, // 0x4B 'K'
{ 0, 0, 0, 0, 0, 0 }, // 0x4C 'L' { 0, 0, 0, 0, 0, 0 }, // 0x4C 'L'
{ 521, 24, 26, 30, 3, -25 }, // 0x4D 'M' { 658, 24, 26, 30, 3, -25 }, // 0x4D 'M'
{ 0, 0, 0, 0, 0, 0 }, // 0x4E 'N' { 0, 0, 0, 0, 0, 0 }, // 0x4E 'N'
{ 599, 25, 26, 27, 1, -25 }, // 0x4F 'O' { 736, 25, 26, 27, 1, -25 }, // 0x4F 'O'
{ 681, 19, 26, 24, 3, -25 }, // 0x50 'P' { 818, 19, 26, 24, 3, -25 }, // 0x50 'P'
{ 0, 0, 0, 0, 0, 0 }, // 0x51 'Q' { 0, 0, 0, 0, 0, 0 }, // 0x51 'Q'
{ 0, 0, 0, 0, 0, 0 }, // 0x52 'R' { 880, 21, 26, 25, 3, -25 }, // 0x52 'R'
{ 743, 20, 26, 24, 2, -25 }, // 0x53 'S' { 949, 20, 26, 24, 2, -25 }, // 0x53 'S'
{ 808, 19, 26, 23, 2, -25 }, // 0x54 'T' { 1014, 19, 26, 23, 2, -25 }, // 0x54 'T'
{ 0, 0, 0, 0, 0, 0 }, // 0x55 'U' { 0, 0, 0, 0, 0, 0 }, // 0x55 'U'
{ 0, 0, 0, 0, 0, 0 }, // 0x56 'V' { 0, 0, 0, 0, 0, 0 }, // 0x56 'V'
{ 0, 0, 0, 0, 0, 0 }, // 0x57 'W' { 0, 0, 0, 0, 0, 0 }, // 0x57 'W'
@ -151,31 +198,31 @@ const GFXglyph FreeSansBold18pt7bTrimmedGlyphs[] PROGMEM = {
{ 0, 0, 0, 0, 0, 0 }, // 0x5E '^' { 0, 0, 0, 0, 0, 0 }, // 0x5E '^'
{ 0, 0, 0, 0, 0, 0 }, // 0x5F '_' { 0, 0, 0, 0, 0, 0 }, // 0x5F '_'
{ 0, 0, 0, 0, 0, 0 }, // 0x60 '`' { 0, 0, 0, 0, 0, 0 }, // 0x60 '`'
{ 0, 0, 0, 0, 0, 0 }, // 0x61 'a' { 1076, 18, 19, 20, 1, -18 }, // 0x61 'a'
{ 0, 0, 0, 0, 0, 0 }, // 0x62 'b' { 1119, 18, 26, 22, 2, -25 }, // 0x62 'b'
{ 0, 0, 0, 0, 0, 0 }, // 0x63 'c' { 0, 0, 0, 0, 0, 0 }, // 0x63 'c'
{ 0, 0, 0, 0, 0, 0 }, // 0x64 'd' { 0, 0, 0, 0, 0, 0 }, // 0x64 'd'
{ 870, 18, 19, 20, 1, -18 }, // 0x65 'e' { 1178, 18, 19, 20, 1, -18 }, // 0x65 'e'
{ 0, 0, 0, 0, 0, 0 }, // 0x66 'f' { 0, 0, 0, 0, 0, 0 }, // 0x66 'f'
{ 0, 0, 0, 0, 0, 0 }, // 0x67 'g' { 0, 0, 0, 0, 0, 0 }, // 0x67 'g'
{ 0, 0, 0, 0, 0, 0 }, // 0x68 'h' { 0, 0, 0, 0, 0, 0 }, // 0x68 'h'
{ 0, 0, 0, 0, 0, 0 }, // 0x69 'i' { 1221, 5, 26, 10, 2, -25 }, // 0x69 'i'
{ 0, 0, 0, 0, 0, 0 }, // 0x6A 'j' { 0, 0, 0, 0, 0, 0 }, // 0x6A 'j'
{ 0, 0, 0, 0, 0, 0 }, // 0x6B 'k' { 0, 0, 0, 0, 0, 0 }, // 0x6B 'k'
{ 0, 0, 0, 0, 0, 0 }, // 0x6C 'l' { 1238, 5, 26, 9, 2, -25 }, // 0x6C 'l'
{ 913, 27, 19, 31, 2, -18 }, // 0x6D 'm' { 1255, 27, 19, 31, 2, -18 }, // 0x6D 'm'
{ 978, 17, 19, 21, 2, -18 }, // 0x6E 'n' { 1320, 17, 19, 21, 2, -18 }, // 0x6E 'n'
{ 0, 0, 0, 0, 0, 0 }, // 0x6F 'o' { 0, 0, 0, 0, 0, 0 }, // 0x6F 'o'
{ 0, 0, 0, 0, 0, 0 }, // 0x70 'p' { 0, 0, 0, 0, 0, 0 }, // 0x70 'p'
{ 0, 0, 0, 0, 0, 0 }, // 0x71 'q' { 0, 0, 0, 0, 0, 0 }, // 0x71 'q'
{ 0, 0, 0, 0, 0, 0 }, // 0x72 'r' { 1361, 11, 19, 14, 2, -18 }, // 0x72 'r'
{ 0, 0, 0, 0, 0, 0 }, // 0x73 's' { 0, 0, 0, 0, 0, 0 }, // 0x73 's'
{ 0, 0, 0, 0, 0, 0 }, // 0x74 't' { 1388, 9, 23, 12, 1, -22 }, // 0x74 't'
{ 1019, 17, 19, 21, 2, -18 } }; // 0x75 'u' { 1414, 17, 19, 21, 2, -18 } }; // 0x75 'u'
const GFXfont FreeSansBold18pt7bTrimmed PROGMEM = { const GFXfont FreeSansBold18pt7bTrimmed PROGMEM = {
(uint8_t *)FreeSansBold18pt7bTrimmedBitmaps, (uint8_t *)FreeSansBold18pt7bTrimmedBitmaps,
(GFXglyph *)FreeSansBold18pt7bTrimmedGlyphs, (GFXglyph *)FreeSansBold18pt7bTrimmedGlyphs,
0x2E, 0x75, 42 }; 0x20, 0x75, 42 };
// Approx. 1571 bytes // Approx. 2064 bytes

View File

@ -2,6 +2,6 @@
REM It is a nuisance to keep the filter up-to-date with whatever is printed REM It is a nuisance to keep the filter up-to-date with whatever is printed
REM in the actual source, but the space savings are worth it. REM in the actual source, but the space savings are worth it.
AdafruitGFXFontTrim [ 0-9\.mSTOPMenuER] FreeSansBold18pt7b.h AdafruitGFXFontTrim "[ 0-9\.mSTOPMenuERCalibrt]" FreeSansBold18pt7b.h
pause pause

View File

@ -12,9 +12,9 @@ class Config
Buttons Buttons
*/ */
static const uint8_t ButtonPinUp = 3; static const uint8_t ButtonPinTop = 3;
static const uint8_t ButtonPinMenu = 5; static const uint8_t ButtonPinMiddle = 5;
static const uint8_t ButtonPinDown = 6; static const uint8_t ButtonPinBottom = 6;
/* /*
@ -38,7 +38,7 @@ class Config
static const uint16_t DisplayIdleTime = 10000; static const uint16_t DisplayIdleTime = 10000;
static const uint16_t DisplayMoveRefreshRate = 1000; static const uint16_t DisplayRefreshRate = 1000;
/* /*
@ -59,6 +59,7 @@ class Config
// How much the measurements can change to still be considered "stable" // How much the measurements can change to still be considered "stable"
static const uint8_t HeightMeasurementDeltaStable = 10; static const uint8_t HeightMeasurementDeltaStable = 10;
static const uint8_t HeightMeasurementDeltaStableCount = 3; static const uint8_t HeightMeasurementDeltaStableCount = 3;
static const uint16_t HeightMeasurementDeltaStableMoveTimeout = 2000;
// How far in advance to stop the motor // How far in advance to stop the motor
static const uint8_t HeightMeasurementDeltaStop = 0; static const uint8_t HeightMeasurementDeltaStop = 0;
@ -99,10 +100,13 @@ class Config
#define ColorDarkerGray 0x2965 #define ColorDarkerGray 0x2965
#define ColorStopRed 0xF9A6 #define ColorStopRed 0xF9A6
#define ColorDarkRed 0x4861
#define ColorSoftGreen 0x2BE7 #define ColorSoftGreen 0x2BE7
#define ColorSoftBlue 0x3376 #define ColorSoftBlue 0x3376
#define ColorDarkBlue 0x0907 #define ColorDarkBlue 0x0907
#define ColorOrange 0xF443
// Init sequence // Init sequence
static const uint16_t ColorInitSeqBackground = ColorBlack; static const uint16_t ColorInitSeqBackground = ColorBlack;
@ -139,7 +143,8 @@ class Config
// Move error / overcurrent // Move error / overcurrent
static const uint16_t ColorMoveErrorText = ColorStopRed; static const uint16_t ColorErrorBackground = ColorDarkRed;
static const uint16_t ColorErrorText = ColorStopRed;
// Menu // Menu
@ -149,8 +154,11 @@ class Config
static const uint16_t ColorMenuSelectedText = ColorWhite; static const uint16_t ColorMenuSelectedText = ColorWhite;
static const uint16_t ColorMenuSelectedBackground = ColorDarkBlue; static const uint16_t ColorMenuSelectedBackground = ColorDarkBlue;
static const uint16_t ColorCalibrateBackground = ColorBlack;
static const uint16_t ColorCalibrateIndicators = ColorWhite; static const uint16_t ColorCalibrateIndicators = ColorWhite;
static const uint16_t ColorCalibrateValue = ColorWhite; static const uint16_t ColorCalibrateValue = ColorWhite;
static const uint16_t ColorCalibrateInvalidValue = ColorOrange;
static const uint16_t ColorCalibrateDigitMarker = ColorWhite;
}; };
#endif #endif

View File

@ -2,7 +2,7 @@
#define __metrics #define __metrics
#include <stdint.h> #include <stdint.h>
#include "fonts/FreeSansBold18pt7b.trimmed.h" //#include "fonts/FreeSansBold18pt7b.trimmed.h"
class Metrics class Metrics
{ {
@ -14,6 +14,7 @@ class Metrics
*/ */
static constexpr const GFXfont* SmallFont = nullptr; static constexpr const GFXfont* SmallFont = nullptr;
static const uint8_t SmallFontTextSize = 1;
static const uint16_t SmallFontBaseline = 0; static const uint16_t SmallFontBaseline = 0;
static const uint16_t SmallFontHeight = 8; static const uint16_t SmallFontHeight = 8;
@ -23,15 +24,21 @@ class Metrics
/* /*
Large font Large font
Uses a trimmed version of the FreeSansBold 18pt font. Originally used a trimmed version of the FreeSansBold 18pt font.
Unfortunately due to program size constraints (I already ordered
PCBs based on the ATMega328P) I'll use a scaled version of the
built-in font for now.
*/ */
static constexpr const GFXfont* LargeFont = &FreeSansBold18pt7bTrimmed; static constexpr const GFXfont* LargeFont = nullptr;//&FreeSansBold18pt7bTrimmed;
static const uint8_t LargeFontTextSize = 3;
static const uint16_t LargeFontBaseline = 25; //static const uint16_t LargeFontBaseline = 25;
static const uint16_t LargeFontHeight = Metrics::LargeFontBaseline; //static const uint16_t LargeFontHeight = Metrics::LargeFontBaseline;
static const uint16_t LargeFontMaxHeight = 42; //static const uint16_t LargeFontMaxHeight = 42;
static const uint16_t LargeFontBaseline = 0;
static const uint16_t LargeFontHeight = 24;
static const uint16_t LargeFontMaxHeight = 24;
/* /*

15
src/include/screenids.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef __screenids
#define __screenids
enum class ScreenId
{
Home,
Calibrate,
Move,
MoveOvercurrent,
MoveSensorError,
Menu,
Manual
};
#endif

View File

@ -1,9 +1,9 @@
#include "./control.h" #include "./control.h"
#include <Wire.h> #include <Wire.h>
#include "./motor.h"
#include "./Control.h"
#include "./settings.h"
#include "./debug.h" #include "./debug.h"
#include "./motor.h"
#include "./state.h"
#include "./settings.h"
#include "include/config.h" #include "include/config.h"
@ -12,6 +12,7 @@ ControlManager Control = ControlManager();
VL53L0XInitResult ControlManager::init() VL53L0XInitResult ControlManager::init()
{ {
dln("[ CONTROL ] Initializing");
Wire.begin(); Wire.begin();
this->heightSensor.setTimeout(500); this->heightSensor.setTimeout(500);
@ -26,45 +27,182 @@ VL53L0XInitResult ControlManager::init()
ControlUpdateResult ControlManager::update() ControlUpdateResult ControlManager::update()
{ {
// Always read the range if in async reading mode, even if we're not supposed to
// be moving anymore, so it clears the status.
bool asyncReading = heightSensor.asyncReading();
uint16_t measurement = asyncReading
? heightSensor.asyncReadRangeSingleMillimeters()
: VL53L0XRangeNotReady;
vl("[ CONTROL ] Update: asyncReading = "); vl(asyncReading); vl(", measurement = "); vl(measurement);
vl(", moveDirection = "); vl((uint8_t)this->moveDirection); vl(", stabilizationStart = "); vln(this->stabilizationStart);
bool moving = this->moveDirection != MoveDirection::None && this->stabilizationStart == 0;
if (!moving && this->stabilizationStart == 0)
{
vl("[ CONTROL ] Idle");
return ControlUpdateResult::Idle;
}
// Check for over-current
// TODO: don't check current every update
if (moving && motorIsOvercurrent())
{
dln("[ CONTROL ] Overcurrent detected!");
this->moveStop();
return ControlUpdateResult::Overcurrent;
}
// Read sensor
if (!asyncReading)
{
vln("[ CONTROL ] Starting async read");
heightSensor.asyncStartReadRangeSingleMillimeters();
}
if (measurement != VL53L0XRangeNotReady)
{
if (measurement > Config::HeightMeasurementMax)
// Treat invalid results as a timeout, so the checks further on are more readable
measurement = VL53L0XRangeTimeout;
dl("[ CONTROL ] Measurement: "); dln(measurement);
this->lastMeasurement = measurement != VL53L0XRangeTimeout ? measurement : 0;
}
if (measurement != VL53L0XRangeNotReady && measurement != VL53L0XRangeTimeout)
{
this->currentHeight = measurement;
this->lastValidMeasurement = CurrentTime;
// Check if target has been reached
if (moving && this->targetReached())
{
this->moveStop();
return ControlUpdateResult::TargetReached;
}
}
if (measurement == VL53L0XRangeTimeout && moving)
{
// While moving, allow incidental invalid results
if (CurrentTime - lastValidMeasurement >= Config::HeightMeasurementAbortTimeout)
{
dln("[ CONTROL ] Timeout while moving!");
this->moveStop();
return ControlUpdateResult::SensorError;
}
}
if (measurement == VL53L0XRangeTimeout && this->moveDirection == MoveDirection::None)
{
// In pure stabilization (not pre-move), immediately return the sensor error
return ControlUpdateResult::SensorError;
}
if (this->stabilizationStart && this->moveDirection != MoveDirection::None)
{
// Pre-move stabilization
if (this->stabilized())
{
dln("[ CONTROL ] Pre-move stabilization successful");
// Sensor looks good, let's go!
motorStart(this->moveDirection == MoveDirection::Up ? MotorDirection::Up : MotorDirection::Down);
return ControlUpdateResult::Moving;
}
else if (CurrentTime - this->stabilizationStart >= Config::HeightMeasurementDeltaStableMoveTimeout)
{
dln("[ CONTROL ] Timeout in pre-move stabilization!");
// Timeout expired, abort the move
this->stabilizationStart = 0;
this->moveDirection = MoveDirection::None;
return ControlUpdateResult::SensorError;
}
}
return moving ? ControlUpdateResult::Moving : ControlUpdateResult::Stabilizing;
} }
void ControlManager::stabilizeStart() void ControlManager::stabilizeStart()
{ {
this->stabilizing = true; dln("[ CONTROL ] Starting stabilization");
this->stabilizationStart = CurrentTime;
this->stabilizeTarget = 0;
} }
bool ControlManager::stabilized() bool ControlManager::stabilized()
{ {
// TODO: stabilized if (this->stabilizeTarget == 0)
{
this->stabilizeTarget = this->currentHeight;
this->stabilizationLastCheck = this->lastValidMeasurement;
return false;
}
// Only count if we have a new measurement
if (this->stabilizationLastCheck == this->lastValidMeasurement)
return false;
this->stabilizationLastCheck = this->lastValidMeasurement;
int16_t delta = this->currentHeight - this->stabilizeTarget;
if (abs(delta) <= Config::HeightMeasurementDeltaStable)
{
this->stabilizeCount++;
if (this->stabilizeCount >= Config::HeightMeasurementDeltaStableCount)
{
dln("[ CONTROL ] Stable");
this->stabilizationStart = 0;
return true;
}
}
else
{
// If it's this much off, chances are the actual value is somewhere in the middle
this->stabilizeTarget += (delta / 2);
this->stabilizeCount = 0;
}
return false;
} }
void ControlManager::movePrepare(uint16_t height) void ControlManager::moveStart(uint16_t height)
{ {
dl("[ CONTROL ] Starting move to: "); dln(height);
this->moveTarget = height; this->moveTarget = height;
this->moveDirection = height > this->currentHeight ? MoveDirection::Up : MoveDirection::Down; this->moveDirection = height > this->currentHeight ? MoveDirection::Up : MoveDirection::Down;
}
// Wait for a stable result
bool ControlManager::moveStart()
{
// TODO: moveStart - wait for stable / timeout
this->moveDirection = MoveDirection::None;
this->stabilizeStart(); this->stabilizeStart();
//this->sensorError = true;
return false;
motorStart(this->moveDirection == MoveDirection::Up ? MotorDirection::Up : MotorDirection::Down);
return true;
} }
void ControlManager::moveStop() void ControlManager::moveStop()
{ {
dln("[ CONTROL ] Stopping move");
motorStop(); motorStop();
this->moveDirection = MoveDirection::None; this->moveDirection = MoveDirection::None;
} }
@ -84,8 +222,52 @@ void ControlManager::snapToPreset()
} }
bool ControlManager::targetReached()
{
// Checks depend on the direction, so it returns true immediately if we've overshot the target
switch (this->moveDirection)
{
case MoveDirection::Up:
if (this->currentHeight >= this->moveTarget - Config::HeightMeasurementDeltaStop)
{
// Snap to the target
if (this->currentHeight - this->moveTarget <= Config::HeightMeasurementDeltaOnTarget)
this->currentHeight = this->moveTarget;
dln("[ CONTROL ] Target reached");
return true;
}
break;
case MoveDirection::Down:
if (this->currentHeight <= this->moveTarget + Config::HeightMeasurementDeltaStop)
{
// Snap to the target
if (this->moveTarget - this->currentHeight <= Config::HeightMeasurementDeltaOnTarget)
this->currentHeight = this->moveTarget;
dln("[ CONTROL ] Target reached");
return true;
}
break;
default:
break;
}
return false;
}
void ControlManager::getDisplayHeight(char* buffer, uint16_t value) void ControlManager::getDisplayHeight(char* buffer, uint16_t value)
{ {
if (value == 0)
{
buffer[0] = '-';
buffer[1] = 0;
return;
}
uint8_t displayValue = (value + Settings.Height.Offset) / 10; uint8_t displayValue = (value + Settings.Height.Offset) / 10;
if (displayValue > 99) if (displayValue > 99)
@ -99,148 +281,3 @@ void ControlManager::getDisplayHeight(char* buffer, uint16_t value)
buffer[4] = 'm'; buffer[4] = 'm';
buffer[5] = 0; buffer[5] = 0;
} }
/*
bool controlCheckTargetReached()
{
dl("controlCheckTargetReached: direction = "); dl((uint8_t)Control.getMoveDirection()); dl(", currentHeight = "); dln(Control.getCurrentHeight());
switch (Control.getMoveDirection())
{
case Direction::Up:
if (Control.getCurrentHeight() >= Control.getMoveTarget() - Config::HeightMeasurementDeltaStop)
{
if (Control.getCurrentHeight() - Control.getMoveTarget() <= Config::HeightMeasurementDeltaOnTarget)
Control.getCurrentHeight() = Control.getMoveTarget();
controlStop();
return true;
}
break;
case Direction::Down:
if (Control.getCurrentHeight() <= Control.getMoveTarget() + Config::HeightMeasurementDeltaStop)
{
if (Control.getMoveTarget() - Control.getCurrentHeight() <= Config::HeightMeasurementDeltaOnTarget)
Control.getCurrentHeight() = Control.getMoveTarget();
controlStop();
return true;
}
break;
default:
break;
}
return false;
}
bool controlCheckOverCurrent()
{
if (motorIsOverCurrent())
{
dln("controlCheckOverCurrent: overcurrent detected!");
controlStop();
return true;
}
return false;
}
if (Control.getMoveDirection() != Direction::None)
{
if (controlCheckOverCurrent())
screenManager.show<MoveOverCurrentScreen>();
else
updateHeight();
}
else if (Control.SensorError)
updateHeight();
void updateHeight()
{
uint16_t measurement;
if (heightSensorGetRange(&measurement))
{
Control.getCurrentHeight() = measurement;
if (Control.getMoveDirection() != Direction::None)
{
lastValidMeasurement = CurrentTime;
if (controlCheckTargetReached())
screenManager.show<HomeScreen>();
}
}
else if (Control.getMoveDirection() != Direction::None && CurrentTime - lastValidMeasurement >= Config::HeightMeasurementAbortTimeout)
{
dln("Out of range timeout!");
Control.moveStop();
screenManager.show<MoveSensorErrorScreen>();
}
}
bool heightSensorGetRange(uint16_t* measurement)
{
*measurement = heightSensor.readRangeSingleMillimeters();
dl("Range: "); dln(*measurement);
return *measurement <= Config::HeightMeasurementMax;
}
uint16_t reference = 0;
uint8_t closeCount = 0;
uint16_t measurement;
while (true)
{
if (heightSensorGetRange(&measurement))
{
initSequenceDisplayHeight(measurement);
if (abs(measurement - reference) <= Config::HeightMeasurementDeltaStable)
closeCount++;
else
{
reference = measurement;
closeCount = 0;
}
}
else
{
initSequenceDisplayHeight(0);
reference = 0;
closeCount = 0;
}
if (closeCount < Config::HeightMeasurementDeltaStableCount)
delay(500);
else
break;
}
initSequenceSuccess(InitSequenceStep::HeightSensorTest);
return reference;
*/

View File

@ -7,19 +7,20 @@
enum class ControlUpdateResult enum class ControlUpdateResult
{ {
Idle = 0, Idle,
Moving = 1, Stabilizing,
TargetReached = 2, Moving,
SensorError = 3, TargetReached,
OverCurrent = 4 SensorError,
Overcurrent
}; };
enum class MoveDirection enum class MoveDirection
{ {
None = 0, None,
Up = 1, Up,
Down = 2 Down
}; };
@ -34,8 +35,7 @@ class ControlManager
void stabilizeStart(); void stabilizeStart();
bool stabilized(); bool stabilized();
void movePrepare(uint16_t height); void moveStart(uint16_t height);
bool moveStart();
void moveStop(); void moveStop();
void snapToPreset(); void snapToPreset();
@ -48,18 +48,26 @@ class ControlManager
uint16_t getCurrentHeight() { return this->currentHeight; } uint16_t getCurrentHeight() { return this->currentHeight; }
uint16_t getLastMeasurement() { return this->lastMeasurement; }
MoveDirection getMoveDirection() { return this->moveDirection; } MoveDirection getMoveDirection() { return this->moveDirection; }
uint16_t getMoveTarget() { return this->moveTarget; } uint16_t getMoveTarget() { return this->moveTarget; }
bool getIsStabilizing() { return this->stabilizationStart != 0; }
private: private:
bool targetReached();
VL53L0X heightSensor = VL53L0X(); VL53L0X heightSensor = VL53L0X();
uint16_t currentHeight; uint16_t currentHeight;
uint16_t lastMeasurement = 0;
uint32_t lastValidMeasurement = 0;
MoveDirection moveDirection; MoveDirection moveDirection;
uint16_t moveTarget; uint16_t moveTarget;
bool stabilizing = false; uint32_t stabilizationStart = 0;
uint32_t lastValidMeasurement = 0; uint8_t stabilizeCount = 0;
uint16_t stabilizeTarget = 0;
uint32_t stabilizationLastCheck = 0;
}; };

View File

@ -3,12 +3,12 @@
#include <Arduino.h> #include <Arduino.h>
#define SerialDebug #define DebugLog
//#define VerboseLog
#ifdef DebugLog
#ifdef SerialDebug #define DebugInit() Serial.begin(115200)
#define DebugInit() Serial.begin(9600)
#define dl(value) Serial.print(value) #define dl(value) Serial.print(value)
#define dln(value) Serial.println(value) #define dln(value) Serial.println(value)
#else #else
@ -18,4 +18,13 @@
#endif #endif
#ifdef VerboseLog
#define vl(value) Serial.print(value)
#define vln(value) Serial.println(value)
#else
#define vl(value)
#define vln(value)
#endif
#endif #endif

View File

@ -31,9 +31,9 @@ void motorStop()
} }
bool motorIsOverCurrent() bool motorIsOvercurrent()
{ {
// TODO: implement motorIsOverCurrent // TODO: implement motorIsOvercurrent
return false; return false;
} }

View File

@ -5,14 +5,14 @@
// Low-level functions to control the motor // Low-level functions to control the motor
enum class MotorDirection enum class MotorDirection
{ {
Up = 0, Up,
Down = 1 Down
}; };
extern void motorInit(); extern void motorInit();
extern void motorStart(MotorDirection direction); extern void motorStart(MotorDirection direction);
extern void motorStop(); extern void motorStop();
extern bool motorIsOverCurrent(); extern bool motorIsOvercurrent();
#endif #endif

View File

@ -3,43 +3,53 @@
#include "include/metrics.h" #include "include/metrics.h"
Adafruit_GFX* BaseScreen::getDisplay()
{
return this->screenManager->getDisplay();
}
uint16_t BaseScreen::printCentered(const char* text, int16_t y) uint16_t BaseScreen::printCentered(const char* text, int16_t y)
{ {
auto display = this->getDisplay();
int16_t textX; int16_t textX;
int16_t textY; int16_t textY;
uint16_t textW; uint16_t textW;
uint16_t textH; uint16_t textH;
display->getTextBounds(text, 0, 0, &textX, &textY, &textW, &textH); this->display->getTextBounds(text, 0, 0, &textX, &textY, &textW, &textH);
textX = (Config::DisplayWidth - textW) / 2; textX = (Config::DisplayWidth - textW) / 2;
display->setCursor(textX, y); this->display->setCursor(textX, y);
display->print(text); this->display->print(text);
return textW; return textW;
} }
void BaseScreen::drawLargeTextLineCentered(const char* text, int16_t y, uint16_t textColor, uint16_t backgroundColor)
{
this->display->fillRect(0, y, Config::DisplayWidth, Metrics::LargeTextLineHeight, backgroundColor);
this->display->setTextColor(textColor);
this->printCentered(text, y + Metrics::LargeTextLineYOffset);
}
void BaseScreen::drawArrowLeft(int16_t x, int16_t y, uint16_t color) void BaseScreen::drawArrowLeft(int16_t x, int16_t y, uint16_t color)
{ {
this->getDisplay()->fillTriangle( this->display->fillTriangle(
x + Metrics::HArrowWidth, y, // Top right x + Metrics::HArrowWidth, y, // Top right
x, y + (Metrics::HArrowHeight / 2), // Middle left x, y + (Metrics::HArrowHeight / 2), // Middle left
x + Metrics::HArrowWidth, y + Metrics::HArrowHeight, // Bottom right x + Metrics::HArrowWidth, y + Metrics::HArrowHeight, // Bottom right
color);
}
void BaseScreen::drawArrowRight(int16_t x, int16_t y, uint16_t color)
{
this->display->fillTriangle(
x, y, // Top left
x + + Metrics::HArrowWidth, y + (Metrics::HArrowHeight / 2), // Middle right
x, y + Metrics::HArrowHeight, // Bottom left
color); color);
} }
void BaseScreen::drawArrowUp(int16_t x, int16_t y, uint16_t color) void BaseScreen::drawArrowUp(int16_t x, int16_t y, uint16_t color)
{ {
this->getDisplay()->fillTriangle( this->display->fillTriangle(
x + (Metrics::VArrowWidth / 2), y, // Top middle x + (Metrics::VArrowWidth / 2), y, // Top middle
x, y + Metrics::VArrowHeight, // Bottom left x, y + Metrics::VArrowHeight, // Bottom left
x + Metrics::VArrowWidth, y + Metrics::VArrowHeight, // Bottom right x + Metrics::VArrowWidth, y + Metrics::VArrowHeight, // Bottom right
@ -49,7 +59,7 @@ void BaseScreen::drawArrowUp(int16_t x, int16_t y, uint16_t color)
void BaseScreen::drawArrowDown(int16_t x, int16_t y, uint16_t color) void BaseScreen::drawArrowDown(int16_t x, int16_t y, uint16_t color)
{ {
this->getDisplay()->fillTriangle( this->display->fillTriangle(
x, y, // Top left x, y, // Top left
x + Metrics::VArrowWidth, y, // Top right x + Metrics::VArrowWidth, y, // Top right
x + (Metrics::VArrowWidth / 2), y + Metrics::VArrowHeight, // Bottom middle x + (Metrics::VArrowWidth / 2), y + Metrics::VArrowHeight, // Bottom middle

View File

@ -3,6 +3,7 @@
#include <Adafruit_ST7789.h> #include <Adafruit_ST7789.h>
#include "include/config.h" #include "include/config.h"
#include "include/screenids.h"
class ScreenManager; class ScreenManager;
@ -10,18 +11,19 @@ class ScreenManager;
enum class Button enum class Button
{ {
Up = 0, Top,
Menu = 1, Middle,
Down = 2 Bottom
}; };
class BaseScreen class BaseScreen
{ {
public: public:
BaseScreen(ScreenManager* screenManager) BaseScreen(ScreenManager* screenManager, Adafruit_GFX* display)
{ {
this->screenManager = screenManager; this->screenManager = screenManager;
this->display = display;
} }
virtual ~BaseScreen() {} virtual ~BaseScreen() {}
@ -30,18 +32,20 @@ class BaseScreen
virtual void onButton(Button button) = 0; virtual void onButton(Button button) = 0;
virtual void onTick() = 0; virtual void onTick() = 0;
protected: virtual ScreenId screenId() = 0;
ScreenManager* getScreenManager() { return this->screenManager; }
Adafruit_GFX* getDisplay();
protected:
uint16_t printCentered(const char* text, int16_t y); uint16_t printCentered(const char* text, int16_t y);
void drawLargeTextLineCentered(const char* text, int16_t y, uint16_t textColor, uint16_t backgroundColor);
void drawArrowLeft(int16_t x, int16_t y, uint16_t color); void drawArrowLeft(int16_t x, int16_t y, uint16_t color);
void drawArrowRight(int16_t x, int16_t y, uint16_t color);
void drawArrowUp(int16_t x, int16_t y, uint16_t color); void drawArrowUp(int16_t x, int16_t y, uint16_t color);
void drawArrowDown(int16_t x, int16_t y, uint16_t color); void drawArrowDown(int16_t x, int16_t y, uint16_t color);
private:
ScreenManager* screenManager; ScreenManager* screenManager;
Adafruit_GFX* display;
}; };
@ -60,7 +64,6 @@ class ScreenManager
inline void tick() { this->getCurrentScreen()->onTick(); } inline void tick() { this->getCurrentScreen()->onTick(); }
inline BaseScreen* getCurrentScreen() { return this->currentScreen; } inline BaseScreen* getCurrentScreen() { return this->currentScreen; }
inline Adafruit_GFX* getDisplay() { return this->display; }
template<class T> void show() template<class T> void show()
@ -71,17 +74,22 @@ class ScreenManager
delete this->currentScreen; delete this->currentScreen;
} }
this->currentScreen = new T(this); this->currentScreen = new T(this, this->display);
this->currentScreen->onShow(); this->currentScreen->onShow();
} }
template<class T> void show(ScreenId onlyIfNotOn)
{
if (this->currentScreen == nullptr || this->currentScreen->screenId() != onlyIfNotOn)
this->show<T>();
}
void displayOff(); void displayOff();
void displayOn(); void displayOn();
private: private:
Adafruit_ST7789* display; Adafruit_ST7789* display;
BaseScreen* currentScreen = nullptr; BaseScreen* currentScreen = nullptr;
}; };

View File

@ -1,19 +1,205 @@
#include "./calibrate.h" #include "./calibrate.h"
#include "./home.h"
#include "include/metrics.h"
#include "lib/settings.h"
void CalibrateScreen::onShow() void CalibrateScreen::onShow()
{ {
//auto display = this->getDisplay(); this->display->setFont(Metrics::LargeFont);
this->display->setTextSize(Metrics::LargeFontTextSize);
// TODO: implement CalibrateScreen this->display->fillScreen(Config::ColorHomeBackground);
this->drawTitle();
this->height[CalibrateStepCurrent] = Control.getCurrentHeight() + Settings.Height.Offset;
this->height[CalibrateStepMax] = 1969; // TODO: read min/max from settings
this->height[CalibrateStepMin] = 0;
this->drawArrowUp(Metrics::ArrowMargin, Metrics::LargeTextLineHeight + Metrics::LargeTextLineVArrowYOffset, Config::ColorCalibrateIndicators);
this->drawArrowRight(Metrics::ArrowMargin, Metrics::MiddleLargeTextLineY + Metrics::LargeTextLineHArrowYOffset, Config::ColorCalibrateIndicators);
this->display->setTextColor(Config::ColorCalibrateIndicators);
this->display->setCursor(Metrics::ArrowMargin, Config::DisplayHeight - Metrics::LargeTextLineHeight + Metrics::LargeTextLineYOffset);
this->display->print("OK");
this->drawSetHeight();
}
// Credit: https://stackoverflow.com/questions/101439/the-most-efficient-way-to-implement-an-integer-based-power-function-powint-int
uint16_t ipow(uint16_t base, uint8_t exp)
{
int result = 1;
for (;;)
{
if (exp & 1)
result *= base;
exp >>= 1;
if (!exp)
break;
base *= base;
}
return result;
} }
void CalibrateScreen::onButton(Button button) void CalibrateScreen::onButton(Button button)
{ {
switch (button)
{
case Button::Top:
{
uint16_t height = this->height[this->step];
uint16_t increment = ipow(10, 3 - this->editingDigit);
uint16_t modulus = increment * 10;
uint16_t remainder = height % modulus;
uint16_t offset = height - remainder;
height = offset + ((remainder + increment) % modulus);
if (height > 1999)
height %= 2000;
this->height[this->step] = height;
this->drawSetHeight();
break;
}
case Button::Middle:
{
this->editingDigit++;
if (this->editingDigit > 3)
this->editingDigit = 0;
this->drawSetHeight();
break;
}
case Button::Bottom:
{
if (!this->isValidHeight())
return;
this->step = (CalibrateStep)(this->step + 1);
if (this->step == CalibrateStepCount)
{
// TODO: store new settings
this->screenManager->show<HomeScreen>();
}
else
{
this->drawTitle();
this->drawSetHeight();
}
break;
}
}
} }
void CalibrateScreen::onTick() void CalibrateScreen::onTick()
{ {
} }
bool CalibrateScreen::isValidHeight()
{
switch (this->step)
{
case CalibrateStepCurrent:
return this->height[CalibrateStepCurrent] >= Control.getCurrentHeight();
case CalibrateStepMin:
return this->height[CalibrateStepMin] <= this->height[CalibrateStepCurrent];
case CalibrateStepMax:
return
this->height[CalibrateStepMax] > this->height[CalibrateStepMin] &&
this->height[CalibrateStepMax] >= this->height[CalibrateStepCurrent];
default:
return true;
}
}
void CalibrateScreen::drawTitle()
{
switch (this->step)
{
case CalibrateStepCurrent:
this->drawLargeTextLineCentered("Calibrate", 0, Config::ColorMenuHeaderText, Config::ColorMenuHeaderBackground);
break;
case CalibrateStepMin:
this->drawLargeTextLineCentered("Minimum", 0, Config::ColorMenuHeaderText, Config::ColorMenuHeaderBackground);
break;
case CalibrateStepMax:
this->drawLargeTextLineCentered("Maximum", 0, Config::ColorMenuHeaderText, Config::ColorMenuHeaderBackground);
break;
default:
break;
}
}
void CalibrateScreen::drawSetHeight()
{
char heightText[7];
uint16_t height = this->height[this->step];
if (height > 999)
heightText[0] = '0' + ((height / 1000) % 10);
else
heightText[0] = '0';
heightText[1] = '.';
heightText[2] = '0' + ((height / 100) % 10);
heightText[3] = '0' + ((height / 10) % 10);
heightText[4] = '0' + (height % 10);
heightText[5] = 'm';
heightText[6] = 0;
int16_t textX;
int16_t textY;
uint16_t textW;
uint16_t textH;
this->display->getTextBounds(&heightText[0], 0, 0, &textX, &textY, &textW, &textH);
textX = (Config::DisplayWidth - textW) / 2;
uint8_t editingChar = this->editingDigit;
// Skip the dot
if (editingChar > 0)
editingChar++;
if (this->lastTextWidth > 0)
this->display->fillRect((Config::DisplayWidth - this->lastTextWidth) / 2, Metrics::MiddleLargeTextLineY, this->lastTextWidth, Metrics::LargeTextLineHeight, Config::ColorCalibrateBackground);
this->lastTextWidth = textW;
this->display->setTextColor(this->isValidHeight() ? Config::ColorCalibrateValue : Config::ColorCalibrateInvalidValue);
this->display->setCursor(textX, Metrics::MiddleLargeTextLineY + Metrics::LargeTextLineYOffset);
// Draw each character ourselves so we can keep track of where the line should be drawn
for (uint8_t i = 0; i < sizeof(heightText); i++)
{
int16_t cursorStart = this->display->getCursorX();
this->display->print(heightText[i]);
if (i == editingChar)
this->display->drawFastHLine(cursorStart, Metrics::MiddleLargeTextLineY + Metrics::LargeTextLineHeight - 1, this->display->getCursorX() - cursorStart, Config::ColorCalibrateDigitMarker);
}
}

View File

@ -1,10 +1,21 @@
#ifndef __screen_calibrate #ifndef __screen_calibrate
#define __screen_calibrate #define __screen_calibrate
#include "include/screenids.h"
#include "../screen.h" #include "../screen.h"
#include "../control.h" #include "../control.h"
enum CalibrateStep : uint8_t
{
CalibrateStepCurrent = 0,
CalibrateStepMax,
CalibrateStepMin,
CalibrateStepCount
};
/* /*
* Calibrate screen * Calibrate screen
* Configures the absolute height (calculates the offset) and * Configures the absolute height (calculates the offset) and
@ -13,13 +24,24 @@
class CalibrateScreen : public BaseScreen class CalibrateScreen : public BaseScreen
{ {
public: public:
CalibrateScreen(ScreenManager* screenManager) : BaseScreen(screenManager) { } CalibrateScreen(ScreenManager* screenManager, Adafruit_GFX* display) : BaseScreen(screenManager, display) { }
void onShow(); void onShow();
void onButton(Button button); void onButton(Button button);
void onTick(); void onTick();
ScreenId screenId() { return ScreenId::Calibrate; };
private: private:
bool isValidHeight();
void drawTitle();
void drawSetHeight();
CalibrateStep step = CalibrateStepCurrent;
uint16_t height[CalibrateStepCount];
uint16_t lastTextWidth = 0;
uint8_t editingDigit = 0;
}; };
#endif #endif

View File

@ -1,5 +1,6 @@
#include "./home.h" #include "./home.h"
#include "./move.h" #include "./move.h"
#include "./move-sensorerror.h"
#include "include/config.h" #include "include/config.h"
#include "include/metrics.h" #include "include/metrics.h"
#include "lib/settings.h" #include "lib/settings.h"
@ -13,10 +14,10 @@ void HomeScreen::onShow()
{ {
this->showTime = CurrentTime; this->showTime = CurrentTime;
auto display = this->getDisplay(); this->display->setFont(Metrics::LargeFont);
this->display->setTextSize(Metrics::LargeFontTextSize);
display->setFont(Metrics::LargeFont); this->display->fillScreen(Config::ColorHomeBackground);
display->fillScreen(Config::ColorHomeBackground);
this->drawPreset1(); this->drawPreset1();
this->drawMenu(); this->drawMenu();
@ -30,33 +31,35 @@ void HomeScreen::onButton(Button button)
{ {
if (this->idle) if (this->idle)
{ {
this->getScreenManager()->displayOn(); this->screenManager->displayOn();
this->idle = false; this->idle = false;
this->showTime = CurrentTime; this->showTime = CurrentTime;
// Preset buttons activate immediately // Preset buttons activate immediately
if (button == Button::Menu) if (button == Button::Middle)
return; return;
} }
uint16_t targetHeight;
switch (button) switch (button)
{ {
case Button::Up: case Button::Top:
Control.movePrepare(Settings.Height.Preset[0]); targetHeight = Settings.Height.Preset[0];
this->getScreenManager()->show<MoveScreen>();
Control.moveStart();
break; break;
case Button::Down: case Button::Bottom:
Control.movePrepare(Settings.Height.Preset[1]); targetHeight = Settings.Height.Preset[1];
this->getScreenManager()->show<MoveScreen>();
Control.moveStart();
break; break;
case Button::Menu: case Button::Middle:
this->getScreenManager()->show<MenuScreen>(); this->screenManager->show<MenuScreen>();
break; return;
} }
Control.moveStart(targetHeight);
this->screenManager->show<MoveScreen>();
} }
@ -64,7 +67,7 @@ void HomeScreen::onTick()
{ {
if (!this->idle && CurrentTime - this->showTime >= Config::DisplayIdleTime) if (!this->idle && CurrentTime - this->showTime >= Config::DisplayIdleTime)
{ {
this->getScreenManager()->displayOff(); this->screenManager->displayOff();
this->idle = true; this->idle = true;
} }
} }
@ -84,13 +87,12 @@ void HomeScreen::drawPreset2()
void HomeScreen::drawNonPresetHeight() void HomeScreen::drawNonPresetHeight()
{ {
auto display = this->getDisplay();
auto y = Metrics::LargeTextLineHeight; auto y = Metrics::LargeTextLineHeight;
if (Control.getCurrentHeight() != Settings.Height.Preset[0] && if (Control.getCurrentHeight() != Settings.Height.Preset[0] &&
Control.getCurrentHeight() != Settings.Height.Preset[1]) Control.getCurrentHeight() != Settings.Height.Preset[1])
{ {
display->setTextColor(Config::ColorNonPresetText); this->display->setTextColor(Config::ColorNonPresetText);
this->drawHeight(y, Control.getCurrentHeight()); this->drawHeight(y, Control.getCurrentHeight());
} }
} }
@ -98,7 +100,6 @@ void HomeScreen::drawNonPresetHeight()
void HomeScreen::drawPreset(int16_t y, uint16_t value) void HomeScreen::drawPreset(int16_t y, uint16_t value)
{ {
auto display = this->getDisplay();
uint16_t textColor; uint16_t textColor;
uint16_t backgroundColor; uint16_t backgroundColor;
uint16_t arrowColor; uint16_t arrowColor;
@ -117,12 +118,12 @@ void HomeScreen::drawPreset(int16_t y, uint16_t value)
arrowColor = Config::ColorPresetArrow; arrowColor = Config::ColorPresetArrow;
} }
display->fillRect(0, y, Config::DisplayWidth, Metrics::LargeTextLineHeight, backgroundColor); this->display->fillRect(0, y, Config::DisplayWidth, Metrics::LargeTextLineHeight, backgroundColor);
if (arrowColor) if (arrowColor)
this->drawArrowLeft(Metrics::ArrowMargin, y + Metrics::LargeTextLineHArrowYOffset, arrowColor); this->drawArrowLeft(Metrics::ArrowMargin, y + Metrics::LargeTextLineHArrowYOffset, arrowColor);
display->setTextColor(textColor); this->display->setTextColor(textColor);
this->drawHeight(y, value); this->drawHeight(y, value);
} }
@ -138,10 +139,8 @@ void HomeScreen::drawHeight(int16_t y, uint16_t value)
void HomeScreen::drawMenu() void HomeScreen::drawMenu()
{ {
auto display = this->getDisplay();
this->drawArrowLeft(Metrics::ArrowMargin, Metrics::MiddleLargeTextLineY, Config::ColorHomeMenuArrow); this->drawArrowLeft(Metrics::ArrowMargin, Metrics::MiddleLargeTextLineY, Config::ColorHomeMenuArrow);
display->setTextColor(Config::ColorHomeMenuText); this->display->setTextColor(Config::ColorHomeMenuText);
this->printCentered("Menu", Metrics::MiddleLargeTextLineY + Metrics::LargeTextLineYOffset); this->printCentered("Menu", Metrics::MiddleLargeTextLineY + Metrics::LargeTextLineYOffset);
} }

View File

@ -1,6 +1,7 @@
#ifndef __screen_home #ifndef __screen_home
#define __screen_home #define __screen_home
#include "include/screenids.h"
#include "../screen.h" #include "../screen.h"
#include "../Control.h" #include "../Control.h"
@ -12,12 +13,14 @@
class HomeScreen : public BaseScreen class HomeScreen : public BaseScreen
{ {
public: public:
HomeScreen(ScreenManager* screenManager) : BaseScreen(screenManager) { } HomeScreen(ScreenManager* screenManager, Adafruit_GFX* display) : BaseScreen(screenManager, display) { }
void onShow(); void onShow();
void onButton(Button button); void onButton(Button button);
void onTick(); void onTick();
ScreenId screenId() { return ScreenId::Home; };
private: private:
uint32_t showTime = 0; uint32_t showTime = 0;
bool idle = false; bool idle = false;

View File

@ -3,8 +3,6 @@
void ManualScreen::onShow() void ManualScreen::onShow()
{ {
//auto display = this->getDisplay();
// TODO: implement ManualScreen // TODO: implement ManualScreen
} }

View File

@ -1,6 +1,7 @@
#ifndef __screen_manual #ifndef __screen_manual
#define __screen_manual #define __screen_manual
#include "include/screenids.h"
#include "../screen.h" #include "../screen.h"
#include "../Control.h" #include "../Control.h"
@ -12,12 +13,14 @@
class ManualScreen : public BaseScreen class ManualScreen : public BaseScreen
{ {
public: public:
ManualScreen(ScreenManager* screenManager) : BaseScreen(screenManager) { } ManualScreen(ScreenManager* screenManager, Adafruit_GFX* display) : BaseScreen(screenManager, display) { }
void onShow(); void onShow();
void onButton(Button button); void onButton(Button button);
void onTick(); void onTick();
ScreenId screenId() { return ScreenId::Manual; };
private: private:
}; };

View File

@ -3,8 +3,6 @@
void MenuScreen::onShow() void MenuScreen::onShow()
{ {
//auto display = this->getDisplay();
// TODO: implement MenuScreen // TODO: implement MenuScreen
} }

View File

@ -1,6 +1,7 @@
#ifndef __screen_menu #ifndef __screen_menu
#define __screen_menu #define __screen_menu
#include "include/screenids.h"
#include "../screen.h" #include "../screen.h"
#include "../Control.h" #include "../Control.h"
@ -13,12 +14,14 @@
class MenuScreen : public BaseScreen class MenuScreen : public BaseScreen
{ {
public: public:
MenuScreen(ScreenManager* screenManager) : BaseScreen(screenManager) { } MenuScreen(ScreenManager* screenManager, Adafruit_GFX* display) : BaseScreen(screenManager, display) { }
void onShow(); void onShow();
void onButton(Button button); void onButton(Button button);
void onTick(); void onTick();
ScreenId screenId() { return ScreenId::Menu; };
private: private:
}; };

View File

@ -1,19 +1,17 @@
#include "./move-overcurrent.h" #include "./move-overcurrent.h"
void MoveOverCurrentScreen::onShow() void MoveOvercurrentScreen::onShow()
{ {
//auto display = this->getDisplay(); // TODO: implement MoveOvercurrentScreen
// TODO: implement MoveOverCurrentScreen
} }
void MoveOverCurrentScreen::onButton(Button button) void MoveOvercurrentScreen::onButton(Button button)
{ {
} }
void MoveOverCurrentScreen::onTick() void MoveOvercurrentScreen::onTick()
{ {
} }

View File

@ -1,6 +1,7 @@
#ifndef __screen_move_overcurrent #ifndef __screen_move_overcurrent
#define __screen_move_overcurrent #define __screen_move_overcurrent
#include "include/screenids.h"
#include "../screen.h" #include "../screen.h"
#include "../Control.h" #include "../Control.h"
@ -9,15 +10,17 @@
* Move overcurrent screen * Move overcurrent screen
* Displays a warning that the motor driver has reached the set maximum current. * Displays a warning that the motor driver has reached the set maximum current.
*/ */
class MoveOverCurrentScreen : public BaseScreen class MoveOvercurrentScreen : public BaseScreen
{ {
public: public:
MoveOverCurrentScreen(ScreenManager* screenManager) : BaseScreen(screenManager) { } MoveOvercurrentScreen(ScreenManager* screenManager, Adafruit_GFX* display) : BaseScreen(screenManager, display) { }
void onShow(); void onShow();
void onButton(Button button); void onButton(Button button);
void onTick(); void onTick();
ScreenId screenId() { return ScreenId::MoveOvercurrent; };
private: private:
}; };

View File

@ -1,4 +1,5 @@
#include "./move-sensorerror.h" #include "./move-sensorerror.h"
#include "./home.h"
#include "include/config.h" #include "include/config.h"
#include "include/metrics.h" #include "include/metrics.h"
#include "lib/control.h" #include "lib/control.h"
@ -7,15 +8,21 @@
void MoveSensorErrorScreen::onShow() void MoveSensorErrorScreen::onShow()
{ {
auto display = this->getDisplay();
auto y = Metrics::LargeTextLineHeight + Metrics::LargeTextLineYOffset; auto y = Metrics::LargeTextLineHeight + Metrics::LargeTextLineYOffset;
display->setFont(Metrics::LargeFont); this->display->fillScreen(Config::ColorErrorBackground);
display->setTextColor(Config::ColorMoveErrorText);
this->display->setFont(Metrics::LargeFont);
this->display->setTextSize(Metrics::LargeFontTextSize);
this->display->setTextColor(Config::ColorErrorText);
this->printCentered("ERROR", y); this->printCentered("ERROR", y);
y += Metrics::LargeTextLineHeight; y += Metrics::LargeTextLineHeight;
display->setFont(Metrics::SmallFont); this->display->setFont(Metrics::SmallFont);
this->display->setTextSize(Metrics::SmallFontTextSize);
this->printCentered("height sensor failed", y); this->printCentered("height sensor failed", y);
y += Metrics::SmallTextLineHeight; y += Metrics::SmallTextLineHeight;
@ -24,8 +31,14 @@ void MoveSensorErrorScreen::onShow()
this->currentHeightY = y; this->currentHeightY = y;
this->display->setFont(Metrics::LargeFont);
this->display->setTextSize(Metrics::LargeFontTextSize);
this->lastRefresh = CurrentTime; this->lastRefresh = CurrentTime;
this->drawCurrentHeight(); this->drawLastMeasurement();
Control.stabilizeStart();
} }
@ -36,19 +49,28 @@ void MoveSensorErrorScreen::onButton(Button button)
void MoveSensorErrorScreen::onTick() void MoveSensorErrorScreen::onTick()
{ {
if (Control.stabilized())
{
this->screenManager->show<HomeScreen>();
return;
}
if (CurrentTime - this->lastRefresh >= Config::DisplayRefreshRate)
{
this->drawLastMeasurement();
this->lastRefresh = CurrentTime;
}
} }
void MoveSensorErrorScreen::drawCurrentHeight() void MoveSensorErrorScreen::drawLastMeasurement()
{ {
auto display = this->getDisplay();
char currentHeightText[6]; char currentHeightText[6];
Control.getDisplayHeight(&currentHeightText[0], Control.getCurrentHeight()); Control.getDisplayHeight(&currentHeightText[0], Control.getLastMeasurement());
if (this->lastTextWidth > 0) if (this->lastTextWidth > 0)
display->fillRect((Config::DisplayWidth - this->lastTextWidth) / 2, this->currentHeightY, this->lastTextWidth, Metrics::LargeTextLineHeight, Config::ColorMoveBackground); this->display->fillRect((Config::DisplayWidth - this->lastTextWidth) / 2, this->currentHeightY, this->lastTextWidth, Metrics::LargeTextLineHeight, Config::ColorErrorBackground);
display->setTextColor(Config::ColorMoveTarget); this->display->setTextColor(Config::ColorMoveTarget);
this->lastTextWidth = this->printCentered(&currentHeightText[0], this->currentHeightY + Metrics::LargeFontBaseline); this->lastTextWidth = this->printCentered(&currentHeightText[0], this->currentHeightY + Metrics::LargeFontBaseline);
} }

View File

@ -1,6 +1,7 @@
#ifndef __screen_move_sensorerror #ifndef __screen_move_sensorerror
#define __screen_move_sensorerror #define __screen_move_sensorerror
#include "include/screenids.h"
#include "../screen.h" #include "../screen.h"
#include "../Control.h" #include "../Control.h"
@ -13,18 +14,20 @@
class MoveSensorErrorScreen : public BaseScreen class MoveSensorErrorScreen : public BaseScreen
{ {
public: public:
MoveSensorErrorScreen(ScreenManager* screenManager) : BaseScreen(screenManager) { } MoveSensorErrorScreen(ScreenManager* screenManager, Adafruit_GFX* display) : BaseScreen(screenManager, display) { }
void onShow(); void onShow();
void onButton(Button button); void onButton(Button button);
void onTick(); void onTick();
ScreenId screenId() { return ScreenId::MoveSensorError; };
private: private:
uint32_t lastRefresh; uint32_t lastRefresh;
uint8_t currentHeightY; uint8_t currentHeightY;
uint16_t lastTextWidth = 0; uint16_t lastTextWidth = 0;
void drawCurrentHeight(); void drawLastMeasurement();
}; };
#endif #endif

View File

@ -9,25 +9,28 @@
void MoveScreen::onShow() void MoveScreen::onShow()
{ {
auto display = this->getDisplay();
auto startY = Metrics::LargeTextLineHeight; auto startY = Metrics::LargeTextLineHeight;
auto arrowY = startY + Metrics::LargeTextLineHeight + Metrics::LargeTextLineVArrowYOffset; auto arrowY = startY + Metrics::LargeTextLineHeight + Metrics::LargeTextLineVArrowYOffset;
auto arrowX = (Config::DisplayWidth - Metrics::VArrowWidth) / 2; auto arrowX = (Config::DisplayWidth - Metrics::VArrowWidth) / 2;
auto stopY = Config::DisplayHeight - Metrics::LargeTextLineHeight; auto stopY = Config::DisplayHeight - Metrics::LargeTextLineHeight;
display->fillScreen(Config::ColorMoveBackground); this->display->fillScreen(Config::ColorMoveBackground);
// Stop // Stop
display->setFont(Metrics::SmallFont); this->display->setFont(Metrics::SmallFont);
display->setTextColor(Config::ColorMoveStop); this->display->setTextSize(Metrics::SmallFontTextSize);
this->display->setTextColor(Config::ColorMoveStop);
this->printCentered("Press any button to", stopY - Metrics::SmallTextLineHeight); this->printCentered("Press any button to", stopY - Metrics::SmallTextLineHeight);
display->setFont(Metrics::LargeFont); this->display->setFont(Metrics::LargeFont);
this->display->setTextSize(Metrics::LargeFontTextSize);
this->printCentered("STOP", stopY + Metrics::LargeTextLineYOffset); this->printCentered("STOP", stopY + Metrics::LargeTextLineYOffset);
char targetHeightText[6]; char targetHeightText[6];
Control.getDisplayHeight(&targetHeightText[0], Control.getMoveTarget()); Control.getDisplayHeight(&targetHeightText[0], Control.getMoveTarget());
display->setTextColor(Config::ColorMoveCurrent); this->display->setTextColor(Config::ColorMoveCurrent);
// Target and arrow // Target and arrow
if (Control.getMoveDirection() == MoveDirection::Up) if (Control.getMoveDirection() == MoveDirection::Up)
@ -53,32 +56,37 @@ void MoveScreen::onShow()
void MoveScreen::onButton(Button button) void MoveScreen::onButton(Button button)
{ {
Control.moveStop(); Control.moveStop();
this->getScreenManager()->show<HomeScreen>(); this->screenManager->show<HomeScreen>();
} }
void MoveScreen::onTick() void MoveScreen::onTick()
{ {
bool isStabilizing = Control.getIsStabilizing();
// Don't update every tick, monitoring the current height is more // Don't update every tick, monitoring the current height is more
// important and the flicker would be unpleasant as well. // important and the flicker would be unpleasant as well.
if (CurrentTime - this->lastRefresh >= Config::DisplayMoveRefreshRate) if (this->lastIsStabilizing != isStabilizing || CurrentTime - this->lastRefresh >= Config::DisplayRefreshRate)
{ {
this->drawCurrentHeight(); this->drawCurrentHeight();
this->lastRefresh = CurrentTime; this->lastRefresh = CurrentTime;
this->lastIsStabilizing = isStabilizing;
} }
} }
void MoveScreen::drawCurrentHeight() void MoveScreen::drawCurrentHeight()
{ {
auto display = this->getDisplay();
char currentHeightText[6]; char currentHeightText[6];
Control.getDisplayHeight(&currentHeightText[0], Control.getCurrentHeight());
if (Control.getIsStabilizing())
Control.getDisplayHeight(&currentHeightText[0], 0);
else
Control.getDisplayHeight(&currentHeightText[0], Control.getCurrentHeight());
if (this->lastTextWidth > 0) if (this->lastTextWidth > 0)
display->fillRect((Config::DisplayWidth - this->lastTextWidth) / 2, this->currentHeightY, this->lastTextWidth, Metrics::LargeTextLineHeight, Config::ColorMoveBackground); this->display->fillRect((Config::DisplayWidth - this->lastTextWidth) / 2, this->currentHeightY, this->lastTextWidth, Metrics::LargeTextLineHeight, Config::ColorMoveBackground);
display->setTextColor(Config::ColorMoveTarget); this->display->setTextColor(Config::ColorMoveTarget);
this->lastTextWidth = this->printCentered(&currentHeightText[0], this->currentHeightY + Metrics::LargeFontBaseline); this->lastTextWidth = this->printCentered(&currentHeightText[0], this->currentHeightY + Metrics::LargeTextLineYOffset);
} }

View File

@ -1,6 +1,7 @@
#ifndef __screen_move #ifndef __screen_move
#define __screen_move #define __screen_move
#include "include/screenids.h"
#include "../screen.h" #include "../screen.h"
#include "../Control.h" #include "../Control.h"
@ -12,16 +13,19 @@
class MoveScreen : public BaseScreen class MoveScreen : public BaseScreen
{ {
public: public:
MoveScreen(ScreenManager* screenManager) : BaseScreen(screenManager) { } MoveScreen(ScreenManager* screenManager, Adafruit_GFX* display) : BaseScreen(screenManager, display) { }
void onShow(); void onShow();
void onButton(Button button); void onButton(Button button);
void onTick(); void onTick();
ScreenId screenId() { return ScreenId::Move; };
private: private:
uint32_t lastRefresh; uint32_t lastRefresh;
uint8_t currentHeightY; uint8_t currentHeightY;
uint16_t lastTextWidth = 0; uint16_t lastTextWidth = 0;
bool lastIsStabilizing = true;
void drawCurrentHeight(); void drawCurrentHeight();
}; };

View File

@ -6,7 +6,9 @@
struct SettingsHeights struct SettingsHeights
{ {
uint8_t Offset; uint16_t Offset;
uint16_t Minimum;
uint16_t Maximum;
uint16_t Preset[2]; uint16_t Preset[2];
}; };

View File

@ -1,5 +1,6 @@
// Slightly modified version of https://github.com/pololu/vl53l0x-arduino // Slightly modified version of https://github.com/pololu/vl53l0x-arduino
// which returns an error code if initialization fails. // which returns an error code if initialization fails, and allows for
// asynchronous readings.
// Most of the functionality of this library is based on the VL53L0X API // Most of the functionality of this library is based on the VL53L0X API
// provided by ST (STSW-IMG005), and some of the explanatory comments are quoted // provided by ST (STSW-IMG005), and some of the explanatory comments are quoted
@ -866,6 +867,89 @@ uint16_t VL53L0X::readRangeSingleMillimeters(void)
return readRangeContinuousMillimeters(); return readRangeContinuousMillimeters();
} }
// Asynchronous reading - call asyncStartReadSingle to begin a measurement,
// then call asyncReadSingle in the loop to determine if a measurement is
// available. This will return VL53L0XRangeNotReady if not ready, or VL53L0XRangeTimeout if a timeout occured.
void VL53L0X::asyncStartReadRangeSingleMillimeters()
{
if (asyncReading())
return;
async_state = VL53L0XAsyncStateWaitingForStart;
writeReg(0x80, 0x01);
writeReg(0xFF, 0x01);
writeReg(0x00, 0x00);
writeReg(0x91, stop_variable);
writeReg(0x00, 0x01);
writeReg(0xFF, 0x00);
writeReg(0x80, 0x00);
writeReg(SYSRANGE_START, 0x01);
// "Wait until start bit has been cleared"
startTimeout();
}
uint16_t VL53L0X::asyncReadRangeSingleMillimeters()
{
if (async_state == VL53L0XAsyncStateWaitingForStart)
{
if (readReg(SYSRANGE_START) & 0x01)
{
if (checkTimeoutExpired())
{
did_timeout = true;
async_state = VL53L0XAsyncStateNotStarted;
return VL53L0XRangeTimeout;
}
return VL53L0XRangeNotReady;
}
else
{
startTimeout();
async_state = VL53L0XAsyncStateWaitingForStatus;
}
}
if (async_state == VL53L0XAsyncStateWaitingForStatus)
{
if ((readReg(RESULT_INTERRUPT_STATUS) & 0x07) == 0)
{
if (checkTimeoutExpired())
{
did_timeout = true;
async_state = VL53L0XAsyncStateNotStarted;
return VL53L0XRangeTimeout;
}
return VL53L0XRangeNotReady;
}
else
{
// assumptions: Linearity Corrective Gain is 1000 (default);
// fractional ranging is not enabled
uint16_t range = readReg16Bit(RESULT_RANGE_STATUS + 10);
writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01);
async_state = VL53L0XAsyncStateNotStarted;
return range;
}
}
return VL53L0XRangeNotStarted;
}
// Indicates whether an async read has started, but not yet finished by
// calling asyncReadRangleSingleMillimeters.
bool VL53L0X::asyncReading()
{
return (async_state != VL53L0XAsyncStateNotStarted);
}
// Did a timeout occur in one of the read functions since the last call to // Did a timeout occur in one of the read functions since the last call to
// timeoutOccurred()? // timeoutOccurred()?
bool VL53L0X::timeoutOccurred() bool VL53L0X::timeoutOccurred()

View File

@ -9,14 +9,23 @@
enum class VL53L0XInitResult enum class VL53L0XInitResult
{ {
Success = 0, Success,
InvalidIdentification = 1, InvalidIdentification,
GetSpadInfoFailed = 2, GetSpadInfoFailed,
VHVCalibrationFailed = 3, VHVCalibrationFailed,
PhaseCalibrationFailed = 4, PhaseCalibrationFailed,
}; };
static const uint16_t VL53L0XRangeNotStarted = 65533;
static const uint16_t VL53L0XRangeNotReady = 65534;
static const uint16_t VL53L0XRangeTimeout = 65535;
static const uint8_t VL53L0XAsyncStateNotStarted = 0;
static const uint8_t VL53L0XAsyncStateWaitingForStart = 1;
static const uint8_t VL53L0XAsyncStateWaitingForStatus = 2;
class VL53L0X class VL53L0X
{ {
public: public:
@ -141,6 +150,10 @@ class VL53L0X
uint16_t readRangeContinuousMillimeters(void); uint16_t readRangeContinuousMillimeters(void);
uint16_t readRangeSingleMillimeters(void); uint16_t readRangeSingleMillimeters(void);
void asyncStartReadRangeSingleMillimeters();
uint16_t asyncReadRangeSingleMillimeters();
bool asyncReading();
inline void setTimeout(uint16_t timeout) { io_timeout = timeout; } inline void setTimeout(uint16_t timeout) { io_timeout = timeout; }
inline uint16_t getTimeout(void) { return io_timeout; } inline uint16_t getTimeout(void) { return io_timeout; }
bool timeoutOccurred(void); bool timeoutOccurred(void);
@ -171,6 +184,8 @@ class VL53L0X
uint8_t stop_variable; // read by init and used when starting measurement; is StopVariable field of VL53L0X_DevData_t structure in API uint8_t stop_variable; // read by init and used when starting measurement; is StopVariable field of VL53L0X_DevData_t structure in API
uint32_t measurement_timing_budget_us; uint32_t measurement_timing_budget_us;
uint8_t async_state = VL53L0XAsyncStateNotStarted;
bool getSpadInfo(uint8_t * count, bool * type_is_aperture); bool getSpadInfo(uint8_t * count, bool * type_is_aperture);
void getSequenceStepEnables(SequenceStepEnables * enables); void getSequenceStepEnables(SequenceStepEnables * enables);

View File

@ -4,6 +4,7 @@
#include <Bounce2.h> #include <Bounce2.h>
#include "./include/config.h" #include "./include/config.h"
#include "./include/screenids.h"
#include "./lib/debug.h" #include "./lib/debug.h"
#include "./lib/settings.h" #include "./lib/settings.h"
#include "./lib/screen.h" #include "./lib/screen.h"
@ -21,8 +22,8 @@
enum class InitSequenceStep enum class InitSequenceStep
{ {
EEPROM = 0, EEPROM = 0,
HeightSensorInit = 1, HeightSensorInit,
HeightSensorTest = 2, HeightSensorTest,
Last = HeightSensorTest Last = HeightSensorTest
}; };
@ -30,7 +31,7 @@ enum class InitSequenceStep
// Forward declarations // Forward declarations
inline void setupHeightSensor(); inline void setupHeightSensor();
inline uint16_t testHeightSensor(); inline void testHeightSensor();
void initSequenceStart(); void initSequenceStart();
void initSequenceSuccess(InitSequenceStep step); void initSequenceSuccess(InitSequenceStep step);
@ -45,6 +46,7 @@ auto screenManager = ScreenManager(&display);
Bounce buttons[3]; Bounce buttons[3];
/* /*
Setup Setup
@ -53,11 +55,11 @@ Bounce buttons[3];
void setup() void setup()
{ {
DebugInit(); DebugInit();
dln("Debug log active"); dln("[ MAIN ] Debug log started");
buttons[0].attach(Config::ButtonPinUp, INPUT_PULLUP); buttons[0].attach(Config::ButtonPinTop, INPUT_PULLUP);
buttons[1].attach(Config::ButtonPinMenu, INPUT_PULLUP); buttons[1].attach(Config::ButtonPinMiddle, INPUT_PULLUP);
buttons[2].attach(Config::ButtonPinDown, INPUT_PULLUP); buttons[2].attach(Config::ButtonPinBottom, INPUT_PULLUP);
display.init(Config::DisplayWidth, Config::DisplayHeight, SPI_MODE3); display.init(Config::DisplayWidth, Config::DisplayHeight, SPI_MODE3);
display.setRotation(Config::DisplayRotation); display.setRotation(Config::DisplayRotation);
@ -73,8 +75,10 @@ void setup()
// Initialize VL53L0X sensor // Initialize VL53L0X sensor
CurrentTime = millis();
setupHeightSensor(); setupHeightSensor();
auto currentHeight = testHeightSensor(); testHeightSensor();
initSequenceEnd(); initSequenceEnd();
@ -84,7 +88,6 @@ void setup()
if (initialized) if (initialized)
{ {
// Control.getCurrentHeight() = currentHeight;
Control.snapToPreset(); Control.snapToPreset();
screenManager.show<HomeScreen>(); screenManager.show<HomeScreen>();
@ -135,8 +138,26 @@ inline void setupHeightSensor()
inline uint16_t testHeightSensor() inline void testHeightSensor()
{ {
Control.stabilizeStart();
while (true)
{
if (Control.update() != ControlUpdateResult::SensorError)
{
initSequenceDisplayHeight(Control.getCurrentHeight());
if (Control.stabilized())
break;
}
else
initSequenceDisplayHeight(0);
CurrentTime = millis();
}
initSequenceSuccess(InitSequenceStep::HeightSensorTest);
} }
@ -158,11 +179,11 @@ void loop()
break; break;
case ControlUpdateResult::SensorError: case ControlUpdateResult::SensorError:
screenManager.show<MoveSensorErrorScreen>(); screenManager.show<MoveSensorErrorScreen>(ScreenId::MoveSensorError);
break; break;
case ControlUpdateResult::OverCurrent: case ControlUpdateResult::Overcurrent:
screenManager.show<MoveOverCurrentScreen>(); screenManager.show<MoveOvercurrentScreen>(ScreenId::MoveOvercurrent);
break; break;
default: default:
@ -174,21 +195,28 @@ void loop()
buttons[1].update(); buttons[1].update();
buttons[2].update(); buttons[2].update();
if (buttons[0].fell())
{
dln("[ MAIN ] Button pressed: Top");
screenManager.button(Button::Top);
}
if (buttons[0].rose()) if (buttons[1].fell())
screenManager.button(Button::Up); {
dln("[ MAIN ] Button pressed: Middle");
screenManager.button(Button::Middle);
}
if (buttons[1].rose()) if (buttons[2].fell())
screenManager.button(Button::Menu); {
dln("[ MAIN ] Button pressed: Bottom");
if (buttons[2].rose()) screenManager.button(Button::Bottom);
screenManager.button(Button::Down); }
screenManager.tick(); screenManager.tick();
} }
/* /*
Helper functions for the status display during the initialization sequence Helper functions for the status display during the initialization sequence