diff --git a/lib/ServoESP32/.clang-format b/lib/ServoESP32/.clang-format new file mode 100644 index 0000000000..333c3bc87b --- /dev/null +++ b/lib/ServoESP32/.clang-format @@ -0,0 +1,83 @@ +# Google C/C++ Code Style settings +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# Author: Kehan Xue, kehan.xue (at) gmail.com + +Language: Cpp +BasedOnStyle: Google +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: None +AlignOperands: Align +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never # To avoid conflict, set this "Never" and each "if statement" should include brace when coding +AllowShortLambdasOnASingleLine: Inline +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BreakBeforeBraces: Custom +BraceWrapping: +AfterCaseLabel: false +AfterClass: false +AfterStruct: false +AfterControlStatement: Never +AfterEnum: false +AfterFunction: false +AfterNamespace: false +AfterUnion: false +AfterExternBlock: false +BeforeCatch: false +BeforeElse: false +BeforeLambdaBody: false +IndentBraces: false +SplitEmptyFunction: false +SplitEmptyRecord: false +SplitEmptyNamespace: false +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +ColumnLimit: 120 +CompactNamespaces: false +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false # Make sure the * or & align on the left +EmptyLineBeforeAccessModifier: LogicalBlock +FixNamespaceComments: true +IncludeBlocks: Preserve +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Left +ReflowComments: false +# SeparateDefinitionBlocks: Always # Only support since clang-format 14 +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: c++11 +TabWidth: 4 +UseTab: Never \ No newline at end of file diff --git a/lib/ServoESP32/README.md b/lib/ServoESP32/README.md index da968843fd..a0155d0ec8 100644 --- a/lib/ServoESP32/README.md +++ b/lib/ServoESP32/README.md @@ -8,23 +8,49 @@ Base on [servo library for stm32f4 (d2a4a47)](https://github.com/arduino-librari The interface is similar to Arduino/Servo: https://www.arduino.cc/en/Reference/Servo -But the function `atach()` is different: +But the function `attach()` is different: ```c bool attach( int pin, int channel = CHANNEL_NOT_ATTACHED, - int minAngle = MIN_ANGLE, - int maxAngle = MAX_ANGLE, - int minPulseWidth = MIN_PULSE_WIDTH, - int maxPulseWidth = MAX_PULSE_WIDTH + int minAngle = DEFAULT_MIN_ANGLE, + int maxAngle = DEFAULT_MAX_ANGLE, + int minPulseWidthUs = DEFAULT_MIN_PULSE_WIDTH_US, + int maxPulseWidthUs = DEFAULT_MAX_PULSE_WIDTH_US, + int frequency = DEFAULT_FREQUENCY ); ``` -More information in [source code documentation](https://github.com/RoboticsBrno/ESP32-Arduino-Servo-Library/blob/master/src/Servo.h#L73). +More information in [source code documentation](src/Servo.h). Example: [04-SimpleServoAngles](examples/04-SimpleServoAngles/04-SimpleServoAngles.ino) +There are also a ServoFloat and ServoDouble variant available. Use one of these when working in radians. + +Example: : [05-SimpleServoRadians](examples/05-SimpleServoRadians/05-SimpleServoRadians.ino) + +### IMPORTANT INFO +According testings, the frequency for ESP32 S2/S3/C3 has to be set at least to 200 Hz. Here is an example, how to set just frequency: + +```cpp +Servo servo1; +const int servoPin = 4; +const int frequency = 200; // Hz + +servo1.attach( + servoPin, + Servo::CHANNEL_NOT_ATTACHED, + Servo::DEFAULT_MIN_ANGLE, + Servo::DEFAULT_MAX_ANGLE, + Servo::DEFAULT_MIN_PULSE_WIDTH_US, + Servo::DEFAULT_MAX_PULSE_WIDTH_US, + frequency +); +``` + +For more information look at the [PR25](https://github.com/RoboticsBrno/ServoESP32/pull/25) + ## PlatformIO This library is also available at the [PlatformIO](https://platformio.org) as [ServoESP32](https://platformio.org/lib/show/1739/ServoESP32). diff --git a/lib/ServoESP32/examples/05-SimpleServoRadians/05-SimpleServoRadians.ino b/lib/ServoESP32/examples/05-SimpleServoRadians/05-SimpleServoRadians.ino new file mode 100644 index 0000000000..2a8f972f09 --- /dev/null +++ b/lib/ServoESP32/examples/05-SimpleServoRadians/05-SimpleServoRadians.ino @@ -0,0 +1,37 @@ +#include + +/* + * Description: + * Example for using floating point angle and radians. + */ + +static const int servoPin = 4; + +ServoFloat servo1; + +float deg2rad(float in) { + return in * M_PI / 180.0; +} + +const float minAngle = deg2rad(45.0); +const float maxAngle = deg2rad(120.0); +const float stepAngle = deg2rad(1.0); + +void setup() { + Serial.begin(115200); + servo1.attach(servoPin, Servo::CHANNEL_NOT_ATTACHED, minAngle, maxAngle); +} + +void loop() { + for (float angleRadians = minAngle; angleRadians <= maxAngle; angleRadians += stepAngle) { + servo1.write(angleRadians); + Serial.println(angleRadians); + delay(20); + } + + for (float angleRadians = maxAngle; angleRadians >= minAngle; angleRadians -= stepAngle) { + servo1.write(angleRadians); + Serial.println(angleRadians); + delay(20); + } +} \ No newline at end of file diff --git a/lib/ServoESP32/library.json b/lib/ServoESP32/library.json index 4ec20009c1..b926772ed9 100644 --- a/lib/ServoESP32/library.json +++ b/lib/ServoESP32/library.json @@ -4,19 +4,17 @@ "description": "Generate RC servo signal on a selected pins with ESP32 device and Arduino framework.", "homepage": "https://github.com/RoboticsBrno/ServoESP32/", "license": "MIT", - "repository": - { + "repository": { "type": "git", "url": "https://github.com/RoboticsBrno/ServoESP32.git" }, - "authors": - { - "name": "Jaroslav Paral", - "email": "paral@robotikabrno.cz", - "url": "http://www.robotikabrno.cz", - "maintainer": true + "authors": { + "name": "Jaroslav Paral", + "email": "paral@robotikabrno.cz", + "url": "http://www.robotikabrno.cz", + "maintainer": true }, - "version": "1.0.3", + "version": "1.1.1", "frameworks": "arduino", "platforms": "espressif32" -} +} \ No newline at end of file diff --git a/lib/ServoESP32/library.properties b/lib/ServoESP32/library.properties index ebe087afa3..4ac9dd8a75 100644 --- a/lib/ServoESP32/library.properties +++ b/lib/ServoESP32/library.properties @@ -1,5 +1,5 @@ name=ServoESP32 -version=1.0.3 +version=1.1.1 author=Jaroslav Paral maintainer=Jaroslav Paral sentence=Generate RC servo signal on a selected pins with ESP32 device and Arduino framework. diff --git a/lib/ServoESP32/src/Servo.cpp b/lib/ServoESP32/src/Servo.cpp index 167c95676c..59667e1e88 100644 --- a/lib/ServoESP32/src/Servo.cpp +++ b/lib/ServoESP32/src/Servo.cpp @@ -24,99 +24,13 @@ * SOFTWARE. *****************************************************************************/ - /* +/** * Arduino srl - www.arduino.org * Base on lib for stm32f4 (d2a4a47): https://github.com/arduino-libraries/Servo/blob/master/src/stm32f4/ServoTimers.h * 2017 Jul 5: Edited by Jaroslav Páral (jarekparal) - paral@robotikabrno.cz */ +// Implementation is in Servo.h #include -int Servo::channel_next_free = 0; - -Servo::Servo() { - _resetFields(); -}; - -Servo::~Servo() { - detach(); -} - -bool Servo::attach(int pin, int channel, - int minAngle, int maxAngle, - int minPulseWidth, int maxPulseWidth) -{ - if(channel == CHANNEL_NOT_ATTACHED) { - if(channel_next_free == CHANNEL_MAX_NUM) { - return false; - } - _channel = channel_next_free; - channel_next_free++; - } else { - _channel = channel; - } - - _pin = pin; - _minAngle = minAngle; - _maxAngle = maxAngle; - _minPulseWidth = minPulseWidth; - _maxPulseWidth = maxPulseWidth; - - ledcSetup(_channel, 50, 16); // channel X, 50 Hz, 16-bit depth - ledcAttachPin(_pin, _channel); - return true; -} - - -bool Servo::detach() { - if (!this->attached()) { - return false; - } - - if(_channel == (channel_next_free - 1)) - channel_next_free--; - - ledcDetachPin(_pin); - _pin = PIN_NOT_ATTACHED; - return true; -} - -void Servo::write(int degrees) { - degrees = constrain(degrees, _minAngle, _maxAngle); - writeMicroseconds(_angleToUs(degrees)); -} - -void Servo::writeMicroseconds(int pulseUs) { - if (!attached()) { - return; - } - pulseUs = constrain(pulseUs, _minPulseWidth, _maxPulseWidth); - _pulseWidthDuty = _usToDuty(pulseUs); - ledcWrite(_channel, _pulseWidthDuty); -} - -int Servo::read() { - return _usToAngle(readMicroseconds()); -} - -int Servo::readMicroseconds() { - if (!this->attached()) { - return 0; - } - int duty = ledcRead(_channel); - return _dutyToUs(duty); -} - -bool Servo::attached() const { return _pin != PIN_NOT_ATTACHED; } - -int Servo::attachedPin() const { return _pin; } - -void Servo::_resetFields(void) { - _pin = PIN_NOT_ATTACHED; - _pulseWidthDuty = 0; - _channel = CHANNEL_NOT_ATTACHED; - _minAngle = MIN_ANGLE; - _maxAngle = MAX_ANGLE; - _minPulseWidth = MIN_PULSE_WIDTH; - _maxPulseWidth = MAX_PULSE_WIDTH; -} \ No newline at end of file +int ServoBase::channel_next_free = 0; \ No newline at end of file diff --git a/lib/ServoESP32/src/Servo.h b/lib/ServoESP32/src/Servo.h index a6697e7f7f..c6af9c6841 100644 --- a/lib/ServoESP32/src/Servo.h +++ b/lib/ServoESP32/src/Servo.h @@ -24,89 +24,131 @@ * SOFTWARE. *****************************************************************************/ - /* +/** * Arduino srl - www.arduino.org - * Base on lib for stm32f4 (d2a4a47): https://github.com/arduino-libraries/Servo/blob/master/src/stm32f4/ServoTimers.h + * Base on lib for stm32f4 (d2a4a47): + * https://github.com/arduino-libraries/Servo/blob/master/src/stm32f4/ServoTimers.h * 2017 Jul 5: Edited by Jaroslav Páral (jarekparal) - paral@robotikabrno.cz */ +// clang-format off #pragma once #include "Arduino.h" -class Servo { - // Default min/max pulse widths (in microseconds) and angles (in - // degrees). Values chosen for Arduino compatibility. These values - // are part of the public API; DO NOT CHANGE THEM. - static const int MIN_ANGLE = 0; - static const int MAX_ANGLE = 180; - - static const int MIN_PULSE_WIDTH = 544; // the shortest pulse sent to a servo - static const int MAX_PULSE_WIDTH = 2400; // the longest pulse sent to a servo - static const int MAX_COMPARE = ((1 << 16) - 1); // 65535 - - static const int TAU_MSEC = 20; - static const int TAU_USEC = (TAU_MSEC * 1000); - - static const int CHANNEL_MAX_NUM = 16; - -public: +class ServoBase { + protected: + // The main purpose of ServoBase is to make sure that multiple instances of + // ServoTemplate class with different types share channel_next_free. + static int channel_next_free; +}; + +template +class ServoTemplate : public ServoBase { +// From esp32-hal-ledc.c +#ifdef SOC_LEDC_SUPPORT_HS_MODE +#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM << 1) +#else +#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM) +#endif + public: + /** + * Default min/max pulse widths (in microseconds) and angles + * (in degrees). Values chosen for Arduino compatibility. + * These values are part of the public API; DO NOT CHANGE THEM. + */ + static constexpr int DEFAULT_MIN_ANGLE = 0; + static constexpr int DEFAULT_MAX_ANGLE = 180; + + static const int DEFAULT_MIN_PULSE_WIDTH_US = 544; // the shortest pulse sent to a servo + static const int DEFAULT_MAX_PULSE_WIDTH_US = 2400; // the longest pulse sent to a servo + + static const int DEFAULT_FREQUENCY = 50; + + static constexpr int TIMER_RESOLUTION = (16 < SOC_LEDC_TIMER_BIT_WIDE_NUM) ? 16 : SOC_LEDC_TIMER_BIT_WIDE_NUM; // std::min(16, SOC_LEDC_TIMER_BIT_WIDE_NUM); + static constexpr int PERIOD_TICKS = (1 << TIMER_RESOLUTION) - 1; + static const int CHANNEL_NOT_ATTACHED = -1; // Pin number of unattached pins static const int PIN_NOT_ATTACHED = -1; - + /** - * @brief Construct a new Servo instance. + * @brief Construct a new ServoTemplate instance. * * The new instance will not be attached to any pin. */ - Servo(); + ServoTemplate() { _resetFields(); } /** - * @brief Destruct a Servo instance. + * @brief Destruct a ServoTemplate instance. * * Call _() and detach(). */ - ~Servo(); + ~ServoTemplate() { detach(); } - /** + /** * @brief Associate this instance with a servomotor whose input is * connected to pin. - - * @param pin Pin connected to the servo pulse wave input. This + * @param pin Pin connected to the servo pulse width input. This * pin must be capable of PWM output (all ESP32 pins). * * @param channel Channel which is set to ESP32 Arduino function ledcSetup(). * Channel must be number between 0 - 15. * It is possible to use automatic channel setup with constant * Servo::CHANNEL_NOT_ATTACHED. - * - * @param minAngle Target angle (in degrees) associated with - * minPulseWidth. Defaults to - * MIN_ANGLE = 0. - * - * @param maxAngle Target angle (in degrees) associated with - * maxPulseWidth. Defaults to - * MAX_ANGLE = 180. - * - * @param minPulseWidth Minimum pulse width to write to pin, in - * microseconds. This will be associated - * with a minAngle degree angle. Defaults to - * MIN_PULSE_WIDTH = 544. - * - * @param maxPulseWidth Maximum pulse width to write to pin, in - * microseconds. This will be associated - * with a maxAngle degree angle. Defaults to - * MAX_PULSE_WIDTH = 2400. + * + * @param minAngle Target angle (in degrees or radians) associated with + * minPulseWidthUs. Defaults to DEFAULT_MIN_ANGLE = 0. + * + * @param maxAngle Target angle (in degrees or radians) associated with + * maxPulseWidthUs. Defaults to DEFAULT_MAX_ANGLE = 180. + * + * @param minPulseWidthUs Minimum pulse width to write to pin, in + * microseconds. This will be associated + * with a minAngle angle. Defaults to + * DEFAULT_MIN_PULSE_WIDTH_US = 544. + * + * @param maxPulseWidthUs Maximum pulse width to write to pin, in + * microseconds. This will be associated + * with a maxAngle angle. Defaults to + * DEFAULT_MAX_PULSE_WIDTH_US = 2400. + * + * @param frequency Frequency in hz to send PWM at. + * Defaults to DEFAULT_FREQUENCY. * * @sideeffect May set pinMode(pin, PWM). * * @return true if successful, false when pin doesn't support PWM. */ - bool attach(int pin, int channel = CHANNEL_NOT_ATTACHED, - int minAngle = MIN_ANGLE, int maxAngle = MAX_ANGLE, - int minPulseWidth = MIN_PULSE_WIDTH, int maxPulseWidth = MAX_PULSE_WIDTH); + bool attach(int pin, int channel = CHANNEL_NOT_ATTACHED, T minAngle = DEFAULT_MIN_ANGLE, + T maxAngle = DEFAULT_MAX_ANGLE, int minPulseWidthUs = DEFAULT_MIN_PULSE_WIDTH_US, + int maxPulseWidthUs = DEFAULT_MAX_PULSE_WIDTH_US, int frequency = DEFAULT_FREQUENCY) { + int tempPeriodUs = std::round(1000000.0 / frequency); + if (tempPeriodUs <= maxPulseWidthUs) { + return false; + } + if (channel == CHANNEL_NOT_ATTACHED) { + if (channel_next_free == LEDC_CHANNELS) { + return false; + } + _channel = channel_next_free; + channel_next_free++; + } else { + _channel = channel; + } + + _pin = pin; + _minAngle = minAngle; + _maxAngle = maxAngle; + _minPulseWidthUs = minPulseWidthUs; + _maxPulseWidthUs = maxPulseWidthUs; + _periodUs = tempPeriodUs; + + ledcSetup(_channel, frequency, TIMER_RESOLUTION); + ledcAttachPin(_pin, _channel); + return true; + } /** * @brief Stop driving the servo pulse train. @@ -115,77 +157,134 @@ class Servo { * * @return true if this call did anything, false otherwise. */ - bool detach(); + bool detach() { + if (!this->attached()) { + return false; + } + + if (_channel == (channel_next_free - 1)) + channel_next_free--; + + ledcDetachPin(_pin); + _pin = PIN_NOT_ATTACHED; + return true; + } /** * @brief Set the servomotor target angle. * - * @param angle Target angle, in degrees. If the target angle is - * outside the range specified at attach() time, it + * @param angle Target angle, in degrees or radians. If the target + * angle is outside the range specified at attach() time, it * will be clamped to lie in that range. * - * @see Servo::attach() + * @see ServoTemplate::attach() */ - void write(int degrees); + void write(T angle) { + angle = constrain(angle, _minAngle, _maxAngle); + writeMicroseconds(_angleToUs(angle)); + } /** * @brief Set the pulse width, in microseconds. * - * @param pulseWidth Pulse width to send to the servomotor, in - * microseconds. If outside of the range - * specified at attach() time, it is clamped to - * lie in that range. + * @param pulseWidthUs Pulse width to send to the servomotor, in + * microseconds. If outside of the range + * specified at attach() time, it is clamped to + * lie in that range. * - * @see Servo::attach() + * @see ServoTemplate::attach() */ - void writeMicroseconds(int pulseUs); + void writeMicroseconds(int pulseWidthUs) { + if (!attached()) { + return; + } + pulseWidthUs = constrain(pulseWidthUs, _minPulseWidthUs, _maxPulseWidthUs); + _pulseWidthTicks = _usToTicks(pulseWidthUs); + ledcWrite(_channel, _pulseWidthTicks); + } /** - * Get the servomotor's target angle, in degrees. This will + * Get the servomotor's target angle, in degrees or radians. This will * lie inside the range specified at attach() time. * - * @see Servo::attach() + * @see ServoTemplate::attach() */ - int read(); + T read() const { return _usToAngle(readMicroseconds()); } /** * Get the current pulse width, in microseconds. This will * lie within the range specified at attach() time. * - * @see Servo::attach() + * @see ServoTemplate::attach() */ - int readMicroseconds(); - + int readMicroseconds() const { + if (!this->attached()) { + return 0; + } + int duty = ledcRead(_channel); + return _ticksToUs(duty); + } + /** * @brief Check if this instance is attached to a servo. * @return true if this instance is attached to a servo, false otherwise. - * @see Servo::attachedPin() + * @see ServoTemplate::attachedPin() */ - bool attached() const; + bool attached() const { return _pin != PIN_NOT_ATTACHED; } /** * @brief Get the pin this instance is attached to. * @return Pin number if currently attached to a pin, PIN_NOT_ATTACHED * otherwise. - * @see Servo::attach() + * @see ServoTemplate::attach() */ - int attachedPin() const; - + int attachedPin() const { return _pin; } -private: - void _resetFields(void); + private: + void _resetFields(void) { + _pin = PIN_NOT_ATTACHED; + _pulseWidthTicks = 0; + _channel = CHANNEL_NOT_ATTACHED; + _minAngle = DEFAULT_MIN_ANGLE; + _maxAngle = DEFAULT_MAX_ANGLE; + _minPulseWidthUs = DEFAULT_MIN_PULSE_WIDTH_US; + _maxPulseWidthUs = DEFAULT_MAX_PULSE_WIDTH_US; + _periodUs = 1000000 / DEFAULT_FREQUENCY; + } - int _usToDuty(int us) { return map(us, 0, TAU_USEC, 0, MAX_COMPARE); } - int _dutyToUs(int duty) { return map(duty, 0, MAX_COMPARE, 0, TAU_USEC); } - int _usToAngle(int us) { return map(us, _minPulseWidth, _maxPulseWidth, _minAngle, _maxAngle); } - int _angleToUs(int angle){ return map(angle, _minAngle, _maxAngle, _minPulseWidth, _maxPulseWidth); } + T mapTemplate(T x, T in_min, T in_max, T out_min, T out_max) const { + // FIXME TD-er: Disable this check till we have C++17 + /* + if constexpr (std::is_floating_point_v) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; + } else */ + { + // Use normal map with integers, because extra care is needed. + return map(x, in_min, in_max, out_min, out_max); + } + } - static int channel_next_free; + int _usToTicks(int us) const { return std::round((PERIOD_TICKS * us) / _periodUs); } + int _ticksToUs(int duty) const { return std::round((_periodUs * duty) / PERIOD_TICKS); } + T _usToAngle(int us) const { return mapTemplate((T)us, (T)_minPulseWidthUs, (T)_maxPulseWidthUs, _minAngle, _maxAngle); } + int _angleToUs(T angle) const { + return (int)mapTemplate(angle, _minAngle, _maxAngle, _minPulseWidthUs, _maxPulseWidthUs); + } int _pin; - int _pulseWidthDuty; + int _pulseWidthTicks; int _channel; - int _min, _max; - int _minPulseWidth, _maxPulseWidth; - int _minAngle, _maxAngle; -}; \ No newline at end of file + int _minPulseWidthUs, _maxPulseWidthUs; + T _minAngle, _maxAngle; + int _periodUs; +}; + +// For backwards compatability, naming the int version simply "Servo" allow +// users to upgrade library without complications. +using Servo = ServoTemplate; + +// Use ServoFloat for float precision +using ServoFloat = ServoTemplate; + +// Use ServoDouble for double precision +using ServoDouble = ServoTemplate; diff --git a/src/_P036_FrameOLED.ino b/src/_P036_FrameOLED.ino index 9d9efdc5b7..95cc6770c5 100644 --- a/src/_P036_FrameOLED.ino +++ b/src/_P036_FrameOLED.ino @@ -14,8 +14,11 @@ // Added to the main repository with some optimizations and some limitations. // As long as the device is not enabled, no RAM is wasted. // +// @tonhuisman: 2023-09-16 +// CHG: Some improvements and optimizations, improved struct alignment to reduce bin size, uncrustify sources // @uwekaditz: 2023-08-10 -// BUG: Individual font setting can only enlarge or maximize the font, if more than 1 line should be displayed (it was buggy not only for ticker!) +// BUG: Individual font setting can only enlarge or maximize the font, if more than 1 line should be displayed +// (it was buggy not only for ticker!) // BUG: CalculateIndividualFontSettings() must be called until the font fits (it was buggy not only for ticker!) // BUG: Compiler error for '#ifdef P036_FONT_CALC_LOG' // @tonhuisman: 2023-08-08 @@ -213,19 +216,20 @@ # ifdef P036_CHECK_HEAP # include "src/Helpers/Memory.h" # endif // ifdef P036_CHECK_HEAP +# include "src/ESPEasyCore/ESPEasyNetwork.h" # define PLUGIN_036 # define PLUGIN_ID_036 36 # define PLUGIN_NAME_036 "Display - OLED SSD1306/SH1106 Framed" # define PLUGIN_VALUENAME1_036 "OLED" -# define P036_EVENT_DISPLAY 0 // event: #display=0/1 -# define P036_EVENT_CONTRAST 1 // event: #contrast=0/1/2 -# define P036_EVENT_FRAME 2 // event: #frame=1..n -# define P036_EVENT_LINE 3 // event: #line=1..n -# define P036_EVENT_LINECNT 4 // event: #linecount=1..4 -# define P036_EVENT_RESTORE 5 // event: #restore=1..n -# define P036_EVENT_SCROLL 6 // event: #scroll=ePSS_VerySlow..ePSS_Ticker +# define P036_EVENT_DISPLAY 0 // event: #display=0/1 +# define P036_EVENT_CONTRAST 1 // event: #contrast=0/1/2 +# define P036_EVENT_FRAME 2 // event: #frame=1..n +# define P036_EVENT_LINE 3 // event: #line=1..n +# define P036_EVENT_LINECNT 4 // event: #linecount=1..4 +# define P036_EVENT_RESTORE 5 // event: #restore=1..n +# define P036_EVENT_SCROLL 6 // event: #scroll=ePSS_VerySlow..ePSS_Ticker # if P036_SEND_EVENTS void P036_SendEvent(struct EventStruct *event, uint8_t eventId, @@ -309,7 +313,7 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) OLedFormController(F("controller"), nullptr, P036_CONTROLLER); { - const int optionValues[P36_MaxSizesCount] = + const int optionValues[] = { static_cast(p036_resolution::pix128x64), static_cast(p036_resolution::pix128x32), static_cast(p036_resolution::pix64x48) }; @@ -335,12 +339,7 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) } # endif // if P036_ENABLE_LEFT_ALIGN { -# if P036_ENABLE_TICKER - const int optionCnt = 6; -# else // if P036_ENABLE_TICKER - const int optionCnt = 5; -# endif // if P036_ENABLE_TICKER - const __FlashStringHelper *options[optionCnt] = { + const __FlashStringHelper *options[] = { F("Very Slow"), F("Slow"), F("Fast"), @@ -350,7 +349,7 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) F("Ticker"), # endif // if P036_ENABLE_TICKER }; - const int optionValues[optionCnt] = + const int optionValues[] = { static_cast(ePageScrollSpeed::ePSS_VerySlow), static_cast(ePageScrollSpeed::ePSS_Slow), static_cast(ePageScrollSpeed::ePSS_Fast), @@ -360,6 +359,7 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) static_cast(ePageScrollSpeed::ePSS_Ticker), # endif // if P036_ENABLE_TICKER }; + constexpr int optionCnt = NR_ELEMENTS(optionValues); addFormSelector(F("Scroll"), F("scroll"), optionCnt, options, optionValues, P036_SCROLL); } @@ -367,8 +367,8 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) addFormPinSelect(PinSelectPurpose::Generic_input, formatGpioName_input_optional(F("Display button")), F("taskdevicepin3"), CONFIG_PIN3); { - const __FlashStringHelper *options[2] = { F("Input"), F("Input pullup") }; - const int optionValues[2] = + const __FlashStringHelper *options[] = { F("Input"), F("Input pullup") }; + const int optionValues[] = { static_cast(eP036pinmode::ePPM_Input), static_cast(eP036pinmode::ePPM_InputPullUp) }; addFormSelector(F("Pin mode"), F("pinmode"), 2, options, optionValues, @@ -394,12 +394,12 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) uint8_t choice = 0; bitWrite(choice, 0, bitRead(P036_FLAGS_0, P036_FLAG_SEND_EVENTS)); bitWrite(choice, 1, bitRead(P036_FLAGS_0, P036_FLAG_EVENTS_FRAME_LINE)); - const __FlashStringHelper *options[3] = { + const __FlashStringHelper *options[] = { F("None"), F("Display & Contrast"), F("Display, Contrast, Frame, Line & Linecount") }; - const int optionValues[3] = { 0, 1, 3 }; // Bitmap + const int optionValues[] = { 0, 1, 3 }; // Bitmap addFormSelector(F("Generate events"), F("generateEvents"), 3, options, optionValues, choice); # ifndef P036_LIMIT_BUILD_SIZE @@ -450,7 +450,7 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) static_cast(eHeaderContent::eUserDef2), # endif // if P036_USERDEF_HEADERS }; - constexpr int nrOptions9 = sizeof(options9) / sizeof(options9[0]); + constexpr int nrOptions9 = NR_ELEMENTS(options9); addFormSelector(F("Header"), F("header"), nrOptions9, options9, optionValues9, get8BitFromUL(P036_FLAGS_0, P036_FLAG_HEADER)); // HeaderContent addFormSelector(F("Header (alternate)"), F("headerAlternate"), nrOptions9, options9, optionValues9, @@ -468,9 +468,9 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) addFormSubHeader(F("Lines")); # if P036_ENABLE_LEFT_ALIGN { - const __FlashStringHelper *optionsAlignment[3] = + const __FlashStringHelper *optionsAlignment[] = { F("left"), F("center"), F("right") }; - const int optionValuesAlignment[3] = + const int optionValuesAlignment[] = { static_cast(eAlignment::eLeft), static_cast(eAlignment::eCenter), static_cast(eAlignment::eRight) @@ -499,9 +499,9 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) P036_CheckHeap(F("_LOAD: After loadDisplayLines()")); # endif // P036_CHECK_HEAP - const __FlashStringHelper *optionsFont[5] = + const __FlashStringHelper *optionsFont[] = { F("Use smallest"), F("Reduce to smaller"), F("None"), F("Enlarge to bigger"), F("Use biggest") }; - const int optionValuesFont[5] = + const int optionValuesFont[] = { static_cast(eModifyFont::eMinimize), static_cast(eModifyFont::eReduce), static_cast(eModifyFont::eNone), @@ -509,15 +509,14 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) static_cast(eModifyFont::eMaximize) }; - const __FlashStringHelper *optionsAlignment[4] = + const __FlashStringHelper *optionsAlignment[] = { F("Use global"), F("left"), F("center"), F("right") }; - const int optionValuesAlignment[4] = + const int optionValuesAlignment[] = { static_cast(eAlignment::eGlobal), static_cast(eAlignment::eLeft), static_cast(eAlignment::eCenter), static_cast(eAlignment::eRight) }; - uint8_t AlignmentChoice[P36_Nlines]; addRowLabel(F("Line")); html_table(F("sub")); @@ -553,16 +552,17 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) F("") // class name ); html_TD(); // alignment - AlignmentChoice[varNr] = get3BitFromUL(P036_lines.DisplayLinesV1[varNr].ModifyLayout, P036_FLAG_ModifyLayout_Alignment); + const uint8_t AlignmentChoice = get3BitFromUL(P036_lines.DisplayLinesV1[varNr].ModifyLayout, + P036_FLAG_ModifyLayout_Alignment); addSelector(getPluginCustomArgName(varNr + 200), 4, optionsAlignment, optionValuesAlignment, - nullptr, // attr[], - AlignmentChoice[varNr], // selectedIndex, - false, // reloadonchange, - true, // enabled, - F("") // class name + nullptr, // attr[], + AlignmentChoice, // selectedIndex, + false, // reloadonchange, + true, // enabled, + F("") // class name ); } html_end_table(); @@ -928,19 +928,18 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) P036_data->P036_DisplayPage(event); } else { + P036_data->HeaderContent = static_cast(get8BitFromUL(P036_FLAGS_0, P036_FLAG_HEADER)); // HeaderContent + P036_data->HeaderContentAlternative = static_cast(get8BitFromUL(P036_FLAGS_0, P036_FLAG_HEADER_ALTERNATIVE)); - P036_data->HeaderContent = static_cast(get8BitFromUL(P036_FLAGS_0, P036_FLAG_HEADER)); // HeaderContent - P036_data->HeaderContentAlternative = static_cast(get8BitFromUL(P036_FLAGS_0, P036_FLAG_HEADER_ALTERNATIVE)); - - // HeaderContentAlternative - P036_data->display_header(); // Update Header + // HeaderContentAlternative + P036_data->display_header(); // Update Header - if (P036_data->isInitialized() && P036_data->display_wifibars()) { - // WiFi symbol was updated. - P036_data->update_display(); + if (P036_data->isInitialized() && P036_data->display_wifibars()) { + // WiFi symbol was updated. + P036_data->update_display(); + } } } - } success = true; break; @@ -1057,12 +1056,12 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) addLog(LOG_LEVEL_INFO, F("P036_PLUGIN_WRITE ...")); # endif // PLUGIN_036_DEBUG - bool bUpdateDisplay = false; - bool bDisplayON = false; - uint8_t eventId = 0; - const String command = parseString(string, 1); - const String subcommand = parseString(string, 2); - int LineNo = event->Par1; + bool bUpdateDisplay = false; + bool bDisplayON = false; + uint8_t eventId = 0; + const String command = parseString(string, 1); + const String subcommand = parseString(string, 2); + int LineNo = event->Par1; # if P036_SEND_EVENTS const bool sendEvents = bitRead(P036_FLAGS_0, P036_FLAG_SEND_EVENTS); // Bit 28 Send Events # endif // if P036_SEND_EVENTS @@ -1198,8 +1197,9 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) get4BitFromUL(P036_FLAGS_0, P036_FLAG_SETTINGS_VERSION), // Bit23-20 Version CustomTaskSettings LineNo); - if (LineNo == 0) + if (LineNo == 0) { LineNo = 1; // after restoring all contents start with first Line + } eventId = P036_EVENT_RESTORE; bUpdateDisplay = true; } @@ -1272,20 +1272,21 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) *currentLine = parseStringKeepCaseNoTrim(string, 3); *currentLine = P036_data->P36_parseTemplate(*currentLine, LineNo - 1); - if (!P036_data->bUseTicker) { + # if P036_ENABLE_TICKER + + if (!P036_data->bUseTicker) + # endif // if P036_ENABLE_TICKER + { // calculate Pix length of new content, not necessary for ticker uint16_t PixLength = P036_data->CalcPixLength(LineNo - 1); if (PixLength > 255) { - String str_error = F("Pixel length of "); - str_error += PixLength; - str_error += F(" too long for line! Max. 255 pix!"); - addHtmlError(str_error); + addHtmlError(strformat(F("Pixel length of %d too long for line! Max. 255 pix!"), PixLength)); const unsigned int strlen = currentLine->length(); if (strlen > 0) { - const float fAvgPixPerChar = static_cast(PixLength) / strlen; + const float fAvgPixPerChar = static_cast(PixLength) / strlen; const unsigned int iCharToRemove = ceilf((static_cast(PixLength - 255)) / fAvgPixPerChar); // shorten string because OLED controller can not handle such long strings @@ -1325,7 +1326,7 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) (eventId == P036_EVENT_SCROLL))) { // display was OFF, turn it ON P036_data->display->displayOn(); - P036_SetDisplayOn(1); // Save the fact that the display is now ON + P036_SetDisplayOn(1); // Save the fact that the display is now ON # if P036_SEND_EVENTS if (sendEvents) { @@ -1339,13 +1340,15 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) } if (P036_DisplayIsOn) { - P036_data->bLineScrollEnabled=false; // disable scrolling temporary + P036_data->bLineScrollEnabled = false; // disable scrolling temporary # if P036_ENABLE_TICKER - if (P036_data->bUseTicker) - P036_data->P036_JumpToPage(event, 0); // Restart the Ticker + + if (P036_data->bUseTicker) { + P036_data->P036_JumpToPage(event, 0); // Restart the Ticker + } else # endif // if P036_ENABLE_TICKER - P036_data->P036_JumpToPageOfLine(event, LineNo - 1); // Start to display the selected page, function needs 65ms! + P036_data->P036_JumpToPageOfLine(event, LineNo - 1); // Start to display the selected page, function needs 65ms! # if P036_SEND_EVENTS if (sendEvents && bitRead(P036_FLAGS_0, P036_FLAG_EVENTS_FRAME_LINE) && (currentFrame != P036_data->currentFrameToDisplay)) { @@ -1357,22 +1360,22 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) # ifdef PLUGIN_036_DEBUG if (eventId == P036_EVENT_LINE) { - String log; + String log; - if (loglevelActiveFor(LOG_LEVEL_INFO) && - log.reserve(200)) { // estimated + if (loglevelActiveFor(LOG_LEVEL_INFO) && + log.reserve(200)) { // estimated log = F("[P36] Line: "); - log += LineNo; - log += F(" Content:"); + log += LineNo; + log += F(" Content:"); log += P036_data->LineContent->DisplayLinesV1[LineNo - 1].Content; - log += F(" Length:"); + log += F(" Length:"); log += P036_data->LineContent->DisplayLinesV1[LineNo - 1].Content.length(); - log += F(" Pix: "); + log += F(" Pix: "); log += P036_data->display->getStringWidth(P036_data->LineContent->DisplayLinesV1[LineNo - 1].Content); - log += F(" Reserved:"); + log += F(" Reserved:"); log += P036_data->LineContent->DisplayLinesV1[LineNo - 1].reserved; - addLogMove(LOG_LEVEL_INFO, log); - delay(5); // otherwise it is may be to fast for the serial monitor + addLogMove(LOG_LEVEL_INFO, log); + delay(5); // FIXME otherwise it is maybe too fast for the serial monitor } } # endif // PLUGIN_036_DEBUG @@ -1381,12 +1384,9 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) # ifdef PLUGIN_036_DEBUG if (!success && loglevelActiveFor(LOG_LEVEL_INFO)) { - String log = F("[P36] Cmd: "); - log += command; - log += F(" SubCmd:"); - log += subcommand; - log += F(" Success:"); - log += boolToString(success); + String log = concat(F("[P36] Cmd: "), command); + log += concat(F(" SubCmd:"), subcommand); + log += F(" Success:false"); addLogMove(LOG_LEVEL_INFO, log); } # endif // PLUGIN_036_DEBUG diff --git a/src/src/PluginStructs/P036_data_struct.cpp b/src/src/PluginStructs/P036_data_struct.cpp index dd20d1a781..dab5d0c070 100644 --- a/src/src/PluginStructs/P036_data_struct.cpp +++ b/src/src/PluginStructs/P036_data_struct.cpp @@ -170,18 +170,18 @@ const tSizeSettings& P036_data_struct::getDisplaySizeSettings(p036_resolution di return SizeSettings[index]; } -bool P036_data_struct::init(taskIndex_t taskIndex, - uint8_t LoadVersion, - uint8_t Type, - uint8_t Address, - uint8_t Sda, - uint8_t Scl, - p036_resolution Disp_resolution, - bool Rotated, - uint8_t Contrast, - uint16_t DisplayTimer, +bool P036_data_struct::init(taskIndex_t taskIndex, + uint8_t LoadVersion, + uint8_t Type, + uint8_t Address, + uint8_t Sda, + uint8_t Scl, + p036_resolution Disp_resolution, + bool Rotated, + uint8_t Contrast, + uint16_t DisplayTimer, ePageScrollSpeed ScrollSpeed, - uint8_t NrLines) { + uint8_t NrLines) { reset(); lastWiFiState = P36_WIFI_STATE_UNSET; @@ -262,7 +262,7 @@ bool P036_data_struct::init(taskIndex_t taskIndex, } bRunning = NetworkConnected(); - + return isInitialized(); } @@ -307,9 +307,9 @@ void P036_data_struct::RestoreLineContent(taskIndex_t taskIndex, void P036_data_struct::setNrLines(struct EventStruct *event, uint8_t NrLines) { if ((NrLines >= 1) && (NrLines <= P36_MAX_LinesPerPage)) { prepare_pagescrolling(static_cast(P036_SCROLL), NrLines); // Recalculate font - MaxFramesToDisplay = 0xFF; // Recalculate page indicator - CalcMaxPageCount(); // Update max page count - nextFrameToDisplay = 0; // Reset to first page + MaxFramesToDisplay = 0xFF; // Recalculate page indicator + CalcMaxPageCount(); // Update max page count + nextFrameToDisplay = 0; // Reset to first page } } @@ -397,7 +397,7 @@ void P036_data_struct::display_header() { newString = userDef2; break; # endif // if P036_USERDEF_HEADERS - default: + case eHeaderContent::eNone: return; } @@ -550,7 +550,11 @@ int16_t P036_data_struct::GetHeaderHeight() const { } int16_t P036_data_struct::GetIndicatorTop() const { - if (bHideFooter || bUseTicker) { + if (bHideFooter + # if P036_ENABLE_TICKER + || bUseTicker + # endif // if P036_ENABLE_TICKER + ) { // no footer (indicator) -> returm max. display height return getDisplaySizeSettings(disp_resolution).Height; } @@ -587,15 +591,17 @@ tIndividualFontSettings P036_data_struct::CalculateIndividualFontSettings(uint8_ switch (iModifyFont) { case eModifyFont::eEnlarge: + if (ScrollingPages.linesPerFrameDef > 1) { // Font can only be enlarged if more than 1 line is displayed - lFontIndex--; + lFontIndex--; if (lFontIndex < IdxForBiggestFont) { lFontIndex = IdxForBiggestFont; } result.IdxForBiggestFontUsed = lFontIndex; } break; case eModifyFont::eMaximize: + if (ScrollingPages.linesPerFrameDef > 1) { // Font can only be maximized if more than 1 line is displayed lFontIndex = IdxForBiggestFont; @@ -612,7 +618,7 @@ tIndividualFontSettings P036_data_struct::CalculateIndividualFontSettings(uint8_ case eModifyFont::eMinimize: lFontIndex = P36_MaxFontCount - 1; break; - default: + case eModifyFont::eNone: lFontIndex = FontIndex; break; } @@ -634,8 +640,9 @@ tIndividualFontSettings P036_data_struct::CalculateIndividualFontSettings(uint8_ // just one lines per frame -> no space inbetween lSpace = 0; lTop = (MaxHeight - lHeight) / 2; + if (lHeight > MaxHeight) { - result.NextLineNo = 0xFF; // settings do not fit + result.NextLineNo = 0xFF; // settings do not fit } } else { if (deltaHeight >= (lLinesPerFrame - 1)) { @@ -654,14 +661,11 @@ tIndividualFontSettings P036_data_struct::CalculateIndividualFontSettings(uint8_ // max lines for used display and smallest font reached -> use special space between the lines and return 'fits' // overlapping (lSpace<0) depends on the absolute display height switch (disp_resolution) { - case p036_resolution::pix128x64: lSpace = bHideFooter ? 0 : -2; - break; - case p036_resolution::pix128x32: lSpace = -2; + case p036_resolution::pix128x64: lSpace = bHideFooter ? 0 : -2; break; - case p036_resolution::pix64x48: lSpace = -1; + case p036_resolution::pix128x32: lSpace = -2; break; - default: - lSpace = 0; + case p036_resolution::pix64x48: lSpace = -1; break; } } else { @@ -683,21 +687,20 @@ tIndividualFontSettings P036_data_struct::CalculateIndividualFontSettings(uint8_ String log1; if (log1.reserve(140)) { // estimated - delay(10); // otherwise it is may be to fast for the serial monitor + delay(10); // FIXME otherwise it is maybe too fast for the serial monitor log1 = F("IndividualFontSettings:"); - log1 += F(" result.NextLineNo:"); log1 += result.NextLineNo; - log1 += F(" result.IdxForBiggestFontUsed:"); log1 += result.IdxForBiggestFontUsed; - log1 += F(" LineNo:"); log1 += LineNo; - log1 += F(" LinesPerFrame:"); log1 += LinesPerFrame; + log1 += concat(F(" result.NextLineNo:"), result.NextLineNo); + log1 += concat(F(" result.IdxForBiggestFontUsed:"), result.IdxForBiggestFontUsed); + log1 += concat(F(" LineNo:"), LineNo); + log1 += concat(F(" LinesPerFrame:"), LinesPerFrame); + if (result.NextLineNo != 0xFF) { - log1 += F(" FrameNo:"); log1 += FrameNo; - log1 += F(" lTop:"); log1 += lTop; - log1 += F(" lSpace:"); log1 += lSpace; + log1 += strformat(F(" FrameNo:%d lTop:%d lSpace:%d"), FrameNo, lTop, lSpace); } addLogMove(LOG_LEVEL_INFO, log1); } } -#endif // # ifdef P036_CHECK_INDIVIDUAL_FONT +# endif // # ifdef P036_CHECK_INDIVIDUAL_FONT return result; } @@ -723,12 +726,12 @@ tFontSettings P036_data_struct::CalculateFontSettings(uint8_t lDefaultLines) { # ifdef P036_FONT_CALC_LOG if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLogMove(LOG_LEVEL_INFO, - strformat(F("P036 CalculateFontSettings lines: %d, height: %d, header: %s, footer: %s"), - iLinesPerFrame, - iHeight, - boolToString(!bHideHeader), - boolToString(!bHideFooter))); + addLog(LOG_LEVEL_INFO, + strformat(F("P036 CalculateFontSettings lines: %d, height: %d, header: %s, footer: %s"), + iLinesPerFrame, + iHeight, + boolToString(!bHideHeader).c_str(), + boolToString(!bHideFooter).c_str())); } # endif // ifdef P036_FONT_CALC_LOG @@ -738,9 +741,9 @@ tFontSettings P036_data_struct::CalculateFontSettings(uint8_t lDefaultLines) { # ifdef P036_FONT_CALC_LOG if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLogMove(LOG_LEVEL_INFO, - strformat(F("CalculateFontSettings LinesPerFrame: %d, iHeight: %d, maxFontHeight: %d"), - iLinesPerFrame, iHeight, iMaxHeightForFont)); + addLog(LOG_LEVEL_INFO, + strformat(F("CalculateFontSettings LinesPerFrame: %d, iHeight: %d, maxFontHeight: %d"), + iLinesPerFrame, iHeight, iMaxHeightForFont)); } # endif // ifdef P036_FONT_CALC_LOG @@ -753,11 +756,8 @@ tFontSettings P036_data_struct::CalculateFontSettings(uint8_t lDefaultLines) { for (i = 0; i < P36_MaxFontCount - 1; i++) { // check available fonts for the line setting # ifdef P036_FONT_CALC_LOG - delay(5); // otherwise it is may be to fast for the serial monitor - log1 = F(" -> i: "); - log1 += i; - log1 += F(", h: "); - log1 += FontSizes[i].Height; + delay(5); // FIXME otherwise it is maybe too fast for the serial monitor + log1 = strformat(F(" -> i: %d, h: %d"), i, FontSizes[i].Height); # endif // ifdef P036_FONT_CALC_LOG if (FontSizes[i].Height > iMaxHeightForFont) { @@ -766,16 +766,14 @@ tFontSettings P036_data_struct::CalculateFontSettings(uint8_t lDefaultLines) { } iFontIndex = i; // save the current index # ifdef P036_FONT_CALC_LOG - log1 += F(", fontIdx: "); - log1 += iFontIndex; + log1 += concat(F(", fontIdx: "), iFontIndex); # endif // ifdef P036_FONT_CALC_LOG break; } if (iFontIndex < 0) { # ifdef P036_FONT_CALC_LOG - log1 += F(", no font fits, fontIdx: "); - log1 += iFontIndex; + log1 += concat(F(", no font fits, fontIdx: "), iFontIndex); addLogMove(LOG_LEVEL_INFO, log1); # endif // ifdef P036_FONT_CALC_LOG break; @@ -813,9 +811,6 @@ tFontSettings P036_data_struct::CalculateFontSettings(uint8_t lDefaultLines) { break; case p036_resolution::pix64x48: result.Space = -1; break; - default: - result.Space = 0; - break; } iFontIndex = P36_MaxFontCount - 1; } @@ -867,13 +862,9 @@ tFontSettings P036_data_struct::CalculateFontSettings(uint8_t lDefaultLines) { if (log1.reserve(140)) { // estimated for (uint8_t i = 0; i < P36_Nlines; i++) { - delay(5); // otherwise it is may be to fast for the serial monitor - log1.clear(); - log1 = F("Line["); log1 += i; - log1 += F("]: Frame:"); log1 += LineSettings[i].frame; - log1 += F(" FontIdx:"); log1 += LineSettings[i].fontIdx; - log1 += F(" ypos:"); log1 += LineSettings[i].ypos - TopLineOffset; - log1 += F(" FontHeight:"); log1 += LineSettings[i].FontHeight; + delay(5); // FIXME otherwise it is maybe too fast for the serial monitor + log1 = strformat(F("Line[%d]: Frame:%d FontIdx:%d ypos:%d FontHeight:%d"), i, LineSettings[i].frame, + LineSettings[i].fontIdx, LineSettings[i].ypos - TopLineOffset, LineSettings[i].FontHeight); addLogMove(LOG_LEVEL_INFO, log1); } } @@ -889,29 +880,17 @@ tFontSettings P036_data_struct::CalculateFontSettings(uint8_t lDefaultLines) { String log1; if (log1.reserve(140)) { // estimated - delay(5); // otherwise it is may be to fast for the serial monitor - log1 = F("CalculateFontSettings: Font:"); - log1 += result.FontName(); - log1 += F(" Idx:"); - log1 += iFontIndex; - log1 += F(" Top:"); - log1 += result.Top; - log1 += F(" FontHeight:"); - log1 += result.Height; - log1 += F(" Space:"); - log1 += result.Space; - log1 += F(" HeightForLines:"); - log1 += iHeight; - log1 += F(" LinesPerFrame:"); - log1 += iLinesPerFrame; + delay(5); // FIXME otherwise it is maybe too fast for the serial monitor + log1 = concat(F("CalculateFontSettings: Font:"), result.FontName()); + log1 += strformat(F(" Idx:%d Top:%d FontHeight:%d Space:%d"), iFontIndex, result.Top, result.Height, result.Space); + log1 += concat(F(" HeightForLines:"), iHeight); + log1 += concat(F(" LinesPerFrame:"), iLinesPerFrame); if (lDefaultLines == 0) { - log1 += F(" DefaultLinesPerFrame:"); - log1 += ScrollingPages.linesPerFrameDef; + log1 += concat(F(" DefaultLinesPerFrame:"), ScrollingPages.linesPerFrameDef); } else { - log1 += F(" DefaultLines:"); - log1 += lDefaultLines; + log1 += concat(F(" DefaultLines:"), lDefaultLines); } addLogMove(LOG_LEVEL_INFO, log1); } @@ -928,14 +907,13 @@ void P036_data_struct::prepare_pagescrolling(ePageScrollSpeed lscrollspeed, } # if P036_ENABLE_TICKER bUseTicker = (lscrollspeed == ePageScrollSpeed::ePSS_Ticker); -# else // if P036_ENABLE_TICKER - bUseTicker = false; -# endif //if P036_ENABLE_TICKER if (bUseTicker) { ScrollingPages.linesPerFrameDef = 1; } - else { + else +# endif //if P036_ENABLE_TICKER + { ScrollingPages.linesPerFrameDef = NrLines; } CalculateFontSettings(0); @@ -948,27 +926,23 @@ uint8_t P036_data_struct::display_scroll(ePageScrollSpeed lscrollspeed, int lTas } int iPageScrollTime; - int iCharToRemove; + int iCharToRemove = 0; # ifdef PLUGIN_036_DEBUG - String log; - if (loglevelActiveFor(LOG_LEVEL_INFO) && - log.reserve(32)) { - log = F("Start Scrolling: Speed: "); - log += static_cast(lscrollspeed); - addLogMove(LOG_LEVEL_INFO, log); + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, concat(F("Start Scrolling: Speed: "), static_cast(lscrollspeed))); } # endif // PLUGIN_036_DEBUG ScrollingLines.wait = 0; // calculate total page scrolling time - if (lscrollspeed == ePageScrollSpeed::ePSS_Instant) { - // no scrolling, just the handling time to build the new page - iPageScrollTime = P36_PageScrollTick - P36_PageScrollTimer; - } else if (lscrollspeed == ePageScrollSpeed::ePSS_Ticker) { - // for ticker, no scrolling, just the handling time to build the new page + if ((lscrollspeed == ePageScrollSpeed::ePSS_Instant) // no scrolling, just the handling time to build the new page + # if P036_ENABLE_TICKER // for ticker, no scrolling, just the handling time to build the new page + || (lscrollspeed == ePageScrollSpeed::ePSS_Ticker) + # endif // if P036_ENABLE_TICKER + ) { iPageScrollTime = P36_PageScrollTick - P36_PageScrollTimer; } else { iPageScrollTime = (P36_MaxDisplayWidth / (P36_PageScrollPix * static_cast(lscrollspeed))) * P36_PageScrollTick; @@ -978,11 +952,7 @@ uint8_t P036_data_struct::display_scroll(ePageScrollSpeed lscrollspeed, int lTas # ifdef PLUGIN_036_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log; - log.reserve(32); - log = F("PageScrollTime: "); - log += iPageScrollTime; - addLogMove(LOG_LEVEL_INFO, log); + addLog(LOG_LEVEL_INFO, concat(F("PageScrollTime: "), iPageScrollTime)); } # endif // PLUGIN_036_DEBUG @@ -1031,13 +1001,18 @@ uint8_t P036_data_struct::display_scroll(ePageScrollSpeed lscrollspeed, int lTas ScrollingLines.SLine[j].LastWidth = PixLengthLineOut; // while page scrolling this line is right aligned } - if ((bUseTicker || (PixLengthLineIn > getDisplaySizeSettings(disp_resolution).Width)) && + if (( + # if P036_ENABLE_TICKER + bUseTicker || + # endif // if P036_ENABLE_TICKER + (PixLengthLineIn > getDisplaySizeSettings(disp_resolution).Width)) && (iScrollTime > 0)) { // width of the line > display width -> scroll line + # if P036_ENABLE_TICKER + if (bUseTicker) { -# if P036_ENABLE_TICKER ScrollingLines.SLine[j].Width = 0; - uint16_t AddPixTicker; + uint16_t AddPixTicker = 0; switch (textAlignment) { case TEXT_ALIGN_CENTER: ScrollingLines.SLine[j].CurrentLeft = getDisplaySizeSettings(disp_resolution).PixLeft + @@ -1048,8 +1023,8 @@ uint8_t P036_data_struct::display_scroll(ePageScrollSpeed lscrollspeed, int lTas getDisplaySizeSettings(disp_resolution).Width; AddPixTicker = getDisplaySizeSettings(disp_resolution).Width; // full width at begin break; - default: ScrollingLines.SLine[j].CurrentLeft = getDisplaySizeSettings(disp_resolution).PixLeft; - AddPixTicker = 0; + default: ScrollingLines.SLine[j].CurrentLeft = getDisplaySizeSettings(disp_resolution).PixLeft; + break; } ScrollingLines.SLine[j].fPixSum = ScrollingLines.SLine[j].CurrentLeft; @@ -1078,52 +1053,41 @@ uint8_t P036_data_struct::display_scroll(ePageScrollSpeed lscrollspeed, int lTas ScrollingLines.Ticker.IdxEnd++; ScrollingLines.SLine[j].Width += PixForChar; } -# endif // if P036_ENABLE_TICKER - } - else { - ScrollingLines.SLine[j].SLcontent = ScrollingPages.In[j].SPLcontent; - ScrollingLines.SLine[j].SLidx = ScrollingPages.In[j].SPLidx; // index to LineSettings[] - ScrollingLines.SLine[j].Width = PixLengthLineIn; // while page scrolling this line is left aligned - ScrollingLines.SLine[j].CurrentLeft = getDisplaySizeSettings(disp_resolution).PixLeft; - ScrollingLines.SLine[j].fPixSum = getDisplaySizeSettings(disp_resolution).PixLeft; - - // pix change per scrolling line tick - ScrollingLines.SLine[j].dPix = - (static_cast(PixLengthLineIn - getDisplaySizeSettings(disp_resolution).Width)) / iScrollTime; + } else + # endif // if P036_ENABLE_TICKER + { + ScrollingLines.SLine[j].SLcontent = ScrollingPages.In[j].SPLcontent; + ScrollingLines.SLine[j].SLidx = ScrollingPages.In[j].SPLidx; // index to LineSettings[] + ScrollingLines.SLine[j].Width = PixLengthLineIn; // while page scrolling this line is left aligned + ScrollingLines.SLine[j].CurrentLeft = getDisplaySizeSettings(disp_resolution).PixLeft; + ScrollingLines.SLine[j].fPixSum = getDisplaySizeSettings(disp_resolution).PixLeft; + + // pix change per scrolling line tick + ScrollingLines.SLine[j].dPix = + (static_cast(PixLengthLineIn - getDisplaySizeSettings(disp_resolution).Width)) / iScrollTime; } -# ifdef P036_SCROLL_CALC_LOG + # ifdef P036_SCROLL_CALC_LOG if (loglevelActiveFor(LOG_LEVEL_INFO)) { - delay(5); // otherwise it is may be to fast for the serial monitor - String log; - log.reserve(32); - log = F("Line: "); - log += (j + 1); - log += F(" width: "); - log += ScrollingLines.SLine[j].Width; - log += F(" dPix: "); - log += ScrollingLines.SLine[j].dPix; - addLogMove(LOG_LEVEL_INFO, log); -# if P036_ENABLE_TICKER + delay(5); // FIXME otherwise it is maybe too fast for the serial monitor + addLog(LOG_LEVEL_INFO, strformat(F("Line: %d width: %d dPix: %d"), + j + 1, ScrollingLines.SLine[j].Width, ScrollingLines.SLine[j].dPix)); + # if P036_ENABLE_TICKER if (bUseTicker) { - delay(5); // otherwise it is may be to fast for the serial monitor + delay(5); // FIXME otherwise it is maybe too fast for the serial monitor String log1; log1.reserve(200); - log1 = F("+++ iScrollTime: "); - log1 += iScrollTime; - log1 += F(" StrLength: "); - log1 += ScrollingLines.Ticker.len; - log1 += F(" StrInPix: "); - log1 += display->getStringWidth(ScrollingLines.Ticker.Tcontent); - log1 += F(" PixPerChar: "); - log1 += ScrollingLines.Ticker.TickerAvgPixPerChar; + log1 = concat(F("+++ iScrollTime: "), iScrollTime); + log1 += concat(F(" StrLength: "), ScrollingLines.Ticker.len); + log1 += concat(F(" StrInPix: "), display->getStringWidth(ScrollingLines.Ticker.Tcontent)); + log1 += concat(F(" PixPerChar: "), ScrollingLines.Ticker.TickerAvgPixPerChar); addLogMove(LOG_LEVEL_INFO, log1); } -# endif // if P036_ENABLE_TICKER + # endif // if P036_ENABLE_TICKER } -# endif // P036_SCROLL_CALC_LOG + # endif // P036_SCROLL_CALC_LOG } } @@ -1172,19 +1136,19 @@ uint8_t P036_data_struct::display_scroll(ePageScrollSpeed lscrollspeed, int lTas if (loglevelActiveFor(LOG_LEVEL_INFO) && log.reserve(128)) { - delay(5); // otherwise it is may be to fast for the serial monitor - log = F("Line: "); log += (j + 1); - log += F(" LineIn: "); log += LineInStr; - log += F(" Length: "); log += strlen; - log += F(" PixLength: "); log += PixLengthLineIn; - log += F(" AvgPixPerChar: "); log += fAvgPixPerChar; - log += F(" CharsRemoved: "); log += iCharToRemove; - addLogMove(LOG_LEVEL_INFO, log); + delay(5); // FIXME otherwise it is maybe too fast for the serial monitor + log = concat(F("Line: "), j + 1); + log += concat(F(" LineIn: "), LineInStr); + log += concat(F(" Length: "), strlen); + log += concat(F(" PixLength: "), PixLengthLineIn); + log += concat(F(" AvgPixPerChar: "), fAvgPixPerChar); + log += concat(F(" CharsRemoved: "), iCharToRemove); + addLog(LOG_LEVEL_INFO, log); log.clear(); - log += F(" -> Changed to: "); log += ScrollingPages.In[j].SPLcontent; - log += F(" Length: "); log += ScrollingPages.In[j].SPLcontent.length(); + log += concat(F(" -> Changed to: "), ScrollingPages.In[j].SPLcontent); + log += concat(F(" Length: "), ScrollingPages.In[j].SPLcontent.length()); display->setFont(FontSizes[LineSettings[ScrollingPages.In[j].SPLidx].fontIdx].fontData); - log += F(" PixLength: "); log += display->getStringWidth(ScrollingPages.In[j].SPLcontent); + log += concat(F(" PixLength: "), display->getStringWidth(ScrollingPages.In[j].SPLcontent)); addLogMove(LOG_LEVEL_INFO, log); } # endif // P036_SCROLL_CALC_LOG @@ -1204,8 +1168,8 @@ uint8_t P036_data_struct::display_scroll(ePageScrollSpeed lscrollspeed, int lTas if (bLineScrollEnabled) { // shorten string on left side because line is displayed right aligned while scrolling - // using ceil() because otherwise overlapping the new text - iCharToRemove = ceil( + // using ceilf() because otherwise overlapping the new text + iCharToRemove = ceilf( (static_cast(PixLengthLineOut - MaxPixWidthForPageScrolling)) / fAvgPixPerChar); ScrollingPages.Out[j].SPLcontent = ScrollingPages.Out[j].SPLcontent.substring(iCharToRemove); bCheckLengthRight = true; @@ -1213,25 +1177,25 @@ uint8_t P036_data_struct::display_scroll(ePageScrollSpeed lscrollspeed, int lTas switch (ScrollingPages.Out[j].Alignment) { case TEXT_ALIGN_LEFT: // shorten string on right side because line is left aligned - // using ceil() because otherwise overlapping the max of 255 pixels - iCharToRemove = ceil( + // using ceilf() because otherwise overlapping the max of 255 pixels + iCharToRemove = ceilf( (static_cast(PixLengthLineOut - MaxPixWidthForPageScrolling)) / fAvgPixPerChar); ScrollingPages.Out[j].SPLcontent = ScrollingPages.Out[j].SPLcontent.substring(0, strlen - iCharToRemove); bCheckLengthLeft = true; break; case TEXT_ALIGN_RIGHT: // shorten string on left side because line is right aligned - // using ceil() because otherwise overlapping the new text - iCharToRemove = ceil( + // using ceilf() because otherwise overlapping the new text + iCharToRemove = ceilf( (static_cast(PixLengthLineOut - MaxPixWidthForPageScrolling)) / fAvgPixPerChar); ScrollingPages.Out[j].SPLcontent = ScrollingPages.Out[j].SPLcontent.substring(iCharToRemove); bCheckLengthRight = true; break; - default: // TEXT_ALIGN_CENTER + default: // TEXT_ALIGN_CENTER/TEST_ALIGN_CENTER_BOTH // shorten string on both sides because line is displayed centered - // using ceil() because otherwise overlapping the new text on left side - // using ceil() because otherwise overlapping the max of 255 pixels on right side - iCharToRemove = ceil( + // using ceilf() because otherwise overlapping the new text on left side + // using ceilf() because otherwise overlapping the max of 255 pixels on right side + iCharToRemove = ceilf( (static_cast(PixLengthLineOut - MaxPixWidthForPageScrolling)) / (2 * fAvgPixPerChar)); ScrollingPages.Out[j].SPLcontent = ScrollingPages.Out[j].SPLcontent.substring(0, strlen - iCharToRemove); ScrollingPages.Out[j].SPLcontent = ScrollingPages.Out[j].SPLcontent.substring(iCharToRemove); @@ -1278,20 +1242,20 @@ uint8_t P036_data_struct::display_scroll(ePageScrollSpeed lscrollspeed, int lTas if (loglevelActiveFor(LOG_LEVEL_INFO) && log.reserve(128)) { - delay(5); // otherwise it is may be to fast for the serial monitor - log = F("Line: "); log += (j + 1); - log += F(" LineOut: "); log += LineOutStr; - log += F(" Length: "); log += strlen; - log += F(" PixLength: "); log += PixLengthLineOut; - log += F(" AvgPixPerChar: "); log += fAvgPixPerChar; - log += F(" CharsRemoved: "); log += iCharToRemove; - addLogMove(LOG_LEVEL_INFO, log); - delay(5); // otherwise it is may be to fast for the serial monitor + delay(5); // FIXME otherwise it is maybe too fast for the serial monitor + log = concat(F("Line: "), j + 1); + log += concat(F(" LineOut: "), LineOutStr); + log += concat(F(" Length: "), strlen); + log += concat(F(" PixLength: "), PixLengthLineOut); + log += concat(F(" AvgPixPerChar: "), fAvgPixPerChar); + log += concat(F(" CharsRemoved: "), iCharToRemove); + addLog(LOG_LEVEL_INFO, log); + delay(5); // FIXME otherwise it is maybe too fast for the serial monitor log.clear(); - log += F(" -> Changed to: "); log += ScrollingPages.Out[j].SPLcontent; - log += F(" Length: "); log += ScrollingPages.Out[j].SPLcontent.length(); + log += concat(F(" -> Changed to: "), ScrollingPages.Out[j].SPLcontent); + log += concat(F(" Length: "), ScrollingPages.Out[j].SPLcontent.length()); display->setFont(FontSizes[LineSettings[ScrollingPages.Out[j].SPLidx].fontIdx].fontData); - log += F(" PixLength: "); log += display->getStringWidth(ScrollingPages.Out[j].SPLcontent); + log += concat(F(" PixLength: "), display->getStringWidth(ScrollingPages.Out[j].SPLcontent)); addLogMove(LOG_LEVEL_INFO, log); } # endif // P036_SCROLL_CALC_LOG @@ -1333,30 +1297,31 @@ uint8_t P036_data_struct::display_scroll_timer(bool initialScroll, } } - if (!bUseTicker) { - for (uint8_t j = 0; j < ScrollingPages.linesPerFrameOut; j++) { - if ((initialScroll && (lscrollspeed < ePageScrollSpeed::ePSS_Instant)) || - !initialScroll) { - // scrolling, prepare scrolling page out to right - DrawScrollingPageLine(&ScrollingPages.Out[j], ScrollingLines.SLine[j].LastWidth, TEXT_ALIGN_RIGHT); - } - } + # if P036_ENABLE_TICKER - for (uint8_t j = 0; j < ScrollingPages.linesPerFrameIn; j++) { - // non-scrolling or scrolling prepare scrolling page in from left - DrawScrollingPageLine(&ScrollingPages.In[j], ScrollingLines.SLine[j].Width, TEXT_ALIGN_LEFT); - } - } -# if P036_ENABLE_TICKER - else { + if (bUseTicker) { // for Ticker start with the set alignment display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(FontSizes[LineSettings[ScrollingLines.SLine[0].SLidx].fontIdx].fontData); display->drawString(ScrollingLines.SLine[0].CurrentLeft, LineSettings[ScrollingLines.SLine[0].SLidx].ypos, ScrollingLines.Ticker.Tcontent.substring(ScrollingLines.Ticker.IdxStart, ScrollingLines.Ticker.IdxEnd)); + } else + # endif // if P036_ENABLE_TICKER + { + for (uint8_t j = 0; j < ScrollingPages.linesPerFrameOut; j++) { + if ((initialScroll && (lscrollspeed < ePageScrollSpeed::ePSS_Instant)) || + !initialScroll) { + // scrolling, prepare scrolling page out to right + DrawScrollingPageLine(&ScrollingPages.Out[j], ScrollingLines.SLine[j].LastWidth, TEXT_ALIGN_RIGHT); + } + } + + for (uint8_t j = 0; j < ScrollingPages.linesPerFrameIn; j++) { + // non-scrolling or scrolling prepare scrolling page in from left + DrawScrollingPageLine(&ScrollingPages.In[j], ScrollingLines.SLine[j].Width, TEXT_ALIGN_LEFT); + } } -# endif // if P036_ENABLE_TICKER update_display(); @@ -1414,12 +1379,17 @@ void P036_data_struct::display_scrolling_lines() { display->setFont(FontSizes[LineSettings[ScrollingLines.SLine[i].SLidx].fontIdx].fontData); - if (bUseTicker || (((iCurrentLeft - getDisplaySizeSettings(disp_resolution).PixLeft) + - ScrollingLines.SLine[i].Width) >= getDisplaySizeSettings(disp_resolution).Width)) { + if ( + # if P036_ENABLE_TICKER + bUseTicker || + # endif // if P036_ENABLE_TICKER + (((iCurrentLeft - getDisplaySizeSettings(disp_resolution).PixLeft) + + ScrollingLines.SLine[i].Width) >= getDisplaySizeSettings(disp_resolution).Width)) { display->setTextAlignment(TEXT_ALIGN_LEFT); + # if P036_ENABLE_TICKER + if (bUseTicker) { -#if P036_ENABLE_TICKER display->drawString(iCurrentLeft, LineSettings[ScrollingLines.SLine[0].SLidx].ypos, ScrollingLines.Ticker.Tcontent.substring(ScrollingLines.Ticker.IdxStart, ScrollingLines.Ticker.IdxEnd)); @@ -1428,14 +1398,14 @@ void P036_data_struct::display_scrolling_lines() { iCurrentLeft -= getDisplaySizeSettings(disp_resolution).PixLeft; while (true) { - if (ScrollingLines.Ticker.IdxEnd >= ScrollingLines.Ticker.len) {// end of string + if (ScrollingLines.Ticker.IdxEnd >= ScrollingLines.Ticker.len) { // end of string break; } - uint8_t c = ScrollingLines.Ticker.Tcontent.charAt(ScrollingLines.Ticker.IdxEnd); - uint8_t PixForChar = display->getCharWidth(c);// PixForChar can be 0 if c is non ascii + const uint8_t c = ScrollingLines.Ticker.Tcontent.charAt(ScrollingLines.Ticker.IdxEnd); + const uint8_t PixForChar = display->getCharWidth(c); // PixForChar can be 0 if c is non ascii if ((static_cast(ScrollingLines.SLine[0].Width + PixForChar) + iCurrentLeft) >= ScrollingLines.Ticker.MaxPixLen) { - break; // no more characters necessary to add + break; // no more characters necessary to add } ScrollingLines.Ticker.IdxEnd++; ScrollingLines.SLine[0].Width += PixForChar; @@ -1446,20 +1416,20 @@ void P036_data_struct::display_scrolling_lines() { ScrollingLines.Ticker.TickerAvgPixPerChar; while (ScrollingLines.SLine[0].fPixSum < fCurrentPixLeft) { - uint8_t c = ScrollingLines.Ticker.Tcontent.charAt(ScrollingLines.Ticker.IdxStart); - uint8_t PixForChar = display->getCharWidth(c);// PixForChar can be 0 if c is non ascii + const uint8_t c = ScrollingLines.Ticker.Tcontent.charAt(ScrollingLines.Ticker.IdxStart); + const uint8_t PixForChar = display->getCharWidth(c); // PixForChar can be 0 if c is non ascii ScrollingLines.SLine[0].fPixSum += static_cast(PixForChar); ScrollingLines.Ticker.IdxStart++; if (ScrollingLines.Ticker.IdxStart >= ScrollingLines.Ticker.IdxEnd) { - ScrollingLines.SLine[0].Width = 0;// Stop scrolling + ScrollingLines.SLine[0].Width = 0; // Stop scrolling -# ifdef PLUGIN_036_DEBUG + # ifdef PLUGIN_036_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, F("Ticker finished")); } -# endif // PLUGIN_036_DEBUG + # endif // PLUGIN_036_DEBUG break; } @@ -1468,19 +1438,24 @@ void P036_data_struct::display_scrolling_lines() { } } break; -#endif // if P036_ENABLE_TICKER - } else { + } else + # endif // if P036_ENABLE_TICKER + { display->drawString(iCurrentLeft, - LineSettings[ScrollingLines.SLine[i].SLidx].ypos, - ScrollingLines.SLine[i].SLcontent); + LineSettings[ScrollingLines.SLine[i].SLidx].ypos, + ScrollingLines.SLine[i].SLcontent); } } else { - if (!bUseTicker) { - // line scrolling finished -> line is shown as aligned right - display->setTextAlignment(TEXT_ALIGN_RIGHT); - display->drawString(P36_MaxDisplayWidth - getDisplaySizeSettings(disp_resolution).PixLeft, - LineSettings[ScrollingLines.SLine[i].SLidx].ypos, - ScrollingLines.SLine[i].SLcontent); + # if P036_ENABLE_TICKER + + if (!bUseTicker) + # endif // if P036_ENABLE_TICKER + { + // line scrolling finished -> line is shown as aligned right + display->setTextAlignment(TEXT_ALIGN_RIGHT); + display->drawString(P36_MaxDisplayWidth - getDisplaySizeSettings(disp_resolution).PixLeft, + LineSettings[ScrollingLines.SLine[i].SLidx].ypos, + ScrollingLines.SLine[i].SLcontent); } ScrollingLines.SLine[i].Width = 0; // Stop scrolling } @@ -1683,7 +1658,11 @@ void P036_data_struct::P036_DisplayPage(struct EventStruct *event) # if P036_FEATURE_DISPLAY_PREVIEW && P036_FEATURE_ALIGN_PREVIEW - if (!bUseTicker) { + # if P036_ENABLE_TICKER + + if (!bUseTicker) + # endif // if P036_ENABLE_TICKER + { // Preview: Center or Right-Align add spaces on the left const bool isAlignCenter = ScrollingPages.In[i].Alignment == OLEDDISPLAY_TEXT_ALIGNMENT::TEXT_ALIGN_CENTER; const bool isAlignRight = ScrollingPages.In[i].Alignment == OLEDDISPLAY_TEXT_ALIGNMENT::TEXT_ALIGN_RIGHT; @@ -1742,10 +1721,14 @@ void P036_data_struct::P036_DisplayPage(struct EventStruct *event) update_display(); - const bool bScrollWithoutWifi = bitRead(PCONFIG_LONG(0), 24); // Bit 24 - const bool bScrollLines = bitRead(PCONFIG_LONG(0), 17); // Bit 17 + const bool bScrollWithoutWifi = bitRead(PCONFIG_LONG(0), 24); // Bit 24 + const bool bScrollLines = bitRead(PCONFIG_LONG(0), 17); // Bit 17 bRunning = NetworkConnected() || bScrollWithoutWifi; - bLineScrollEnabled = ((bScrollLines || bUseTicker) && bRunning);// scroll lines only if WifiIsConnected, + bLineScrollEnabled = ((bScrollLines + # if P036_ENABLE_TICKER + || bUseTicker + # endif // if P036_ENABLE_TICKER + ) && bRunning); // scroll lines only if WifiIsConnected, // WifiIsConnected, // otherwise too slow @@ -1793,12 +1776,12 @@ String P036_data_struct::P36_parseTemplate(String& tmpString, uint8_t lineIdx) { OLEDDISPLAY_TEXT_ALIGNMENT iTextAlignment = getTextAlignment(static_cast(iAlignment)); -# if P036_ENABLE_TICKER + # if P036_ENABLE_TICKER if (bUseTicker) { iTextAlignment = TEXT_ALIGN_RIGHT; // ticker is always right aligned } -# endif // if P036_ENABLE_TICKER + # endif // if P036_ENABLE_TICKER switch (iTextAlignment) { case TEXT_ALIGN_LEFT: @@ -1832,7 +1815,8 @@ void P036_data_struct::setTextAlignment(eAlignment aAlignment) { switch (aAlignment) { case eAlignment::eLeft: textAlignment = TEXT_ALIGN_LEFT; break; case eAlignment::eRight: textAlignment = TEXT_ALIGN_RIGHT; break; - default: textAlignment = TEXT_ALIGN_CENTER; + case eAlignment::eCenter: // Fall through + case eAlignment::eGlobal: textAlignment = TEXT_ALIGN_CENTER; break; } MaxFramesToDisplay = 0xFF; // Recalculate page indicator @@ -1841,11 +1825,12 @@ void P036_data_struct::setTextAlignment(eAlignment aAlignment) { OLEDDISPLAY_TEXT_ALIGNMENT P036_data_struct::getTextAlignment(eAlignment aAlignment) const { switch (aAlignment) { - case eAlignment::eGlobal: return textAlignment; break; - case eAlignment::eLeft: return TEXT_ALIGN_LEFT; break; - case eAlignment::eRight: return TEXT_ALIGN_RIGHT; break; - default: return TEXT_ALIGN_CENTER; + case eAlignment::eLeft: return TEXT_ALIGN_LEFT; + case eAlignment::eRight: return TEXT_ALIGN_RIGHT; + case eAlignment::eCenter: return TEXT_ALIGN_CENTER; + case eAlignment::eGlobal: break; } + return textAlignment; } uint8_t P036_data_struct::GetTextLeftMargin(OLEDDISPLAY_TEXT_ALIGNMENT _textAlignment) const { @@ -1861,7 +1846,7 @@ uint8_t P036_data_struct::GetTextLeftMargin(OLEDDISPLAY_TEXT_ALIGNMENT _textAlig if (_textAlignment == TEXT_ALIGN_RIGHT) { return getDisplaySizeSettings(disp_resolution).Width + getDisplaySizeSettings(disp_resolution).PixLeft; } - else { return getDisplaySizeSettings(disp_resolution).PixLeft; } + return getDisplaySizeSettings(disp_resolution).PixLeft; } # endif // if P036_ENABLE_LEFT_ALIGN @@ -1945,18 +1930,19 @@ void P036_data_struct::CalcMaxPageCount(void) { String log1; if (log1.reserve(140)) { // estimated - log1 = F("CalcMaxPageCount: MaxFramesToDisplay:"); log1 += MaxFramesToDisplay; - addLogMove(LOG_LEVEL_INFO, log1); + log1 = concat(F("CalcMaxPageCount: MaxFramesToDisplay:"), MaxFramesToDisplay); + addLog(LOG_LEVEL_INFO, log1); for (uint8_t i = 0; i < P36_Nlines; i++) { log1.clear(); - delay(5); // otherwise it is may be to fast for the serial monitor - log1 = F("Line["); log1 += i; - log1 += F("]: Frame:"); log1 += LineSettings[i].frame; - log1 += F(" DisplayedPageNo:"); log1 += LineSettings[i].DisplayedPageNo; - log1 += F(" FontIdx:"); log1 += LineSettings[i].fontIdx; - log1 += F(" ypos:"); log1 += LineSettings[i].ypos - TopLineOffset; - log1 += F(" FontHeight:"); log1 += LineSettings[i].FontHeight; + delay(5); // FIXME otherwise it is maybe too fast for the serial monitor + log1 = strformat(F("Line[%d]: Frame:%d DisplayedPageNo:%d FontIdx:%d ypos:%d FontHeight:%d"), + i, + LineSettings[i].frame, + LineSettings[i].DisplayedPageNo, + LineSettings[i].fontIdx, + LineSettings[i].ypos - TopLineOffset, + LineSettings[i].FontHeight); addLogMove(LOG_LEVEL_INFO, log1); } } @@ -1976,7 +1962,7 @@ uint16_t P036_data_struct::TrimStringTo255Chars(tScrollingPageLines *ScrollingPa // shorten string because OLED controller can not handle such long strings const int strlen = ScrollingPageLine->SPLcontent.length(); const float fAvgPixPerChar = static_cast(PixLengthLine) / strlen; - const int iCharToRemove = ceil((static_cast(PixLengthLine - 255)) / fAvgPixPerChar); + const int iCharToRemove = ceilf((static_cast(PixLengthLine - 255)) / fAvgPixPerChar); ScrollingPageLine->SPLcontent = ScrollingPageLine->SPLcontent.substring(0, strlen - iCharToRemove); PixLengthLine = display->getStringWidth(ScrollingPageLine->SPLcontent); } @@ -1990,13 +1976,9 @@ void P036_data_struct::DrawScrollingPageLine(tScrollingPageLines *Scrollin int16_t LeftOffset = 0; switch (textAlignment) { - case TEXT_ALIGN_LEFT: LeftOffset = -P36_MaxDisplayWidth; - break; - case TEXT_ALIGN_RIGHT: LeftOffset = 0; - break; - default: - LeftOffset = 0; - break; + case TEXT_ALIGN_LEFT: LeftOffset = -P36_MaxDisplayWidth; break; + case TEXT_ALIGN_RIGHT: LeftOffset = 0; break; + default: LeftOffset = 0; break; } display->setFont(FontSizes[LineSettings[ScrollingPageLine->SPLidx].fontIdx].fontData); @@ -2020,43 +2002,44 @@ void P036_data_struct::DrawScrollingPageLine(tScrollingPageLines *Scrollin } void P036_data_struct::CreateScrollingPageLine(tScrollingPageLines *ScrollingPageLine, uint8_t Counter) { + # if P036_ENABLE_TICKER + if (bUseTicker) { -# if P036_ENABLE_TICKER ScrollingPageLine->SPLcontent = EMPTY_STRING; -# endif // if P036_ENABLE_TICKER - } - else { - String tmpString(LineContent->DisplayLinesV1[Counter].Content); - - ScrollingPageLine->SPLcontent = P36_parseTemplate(tmpString, Counter); - - if (ScrollingPageLine->SPLcontent.length() > 0) { - const int splitIdx = ScrollingPageLine->SPLcontent.indexOf(F("<|>")); // check for split token - - if (splitIdx >= 0) { - // split line into left and right part - tmpString = ScrollingPageLine->SPLcontent; - tmpString.replace(F("<|>"), F(" ")); // replace in tmpString the split token with one space char - display->setFont(FontSizes[LineSettings[Counter].fontIdx].fontData); - uint16_t pixlength = display->getStringWidth(tmpString); // pixlength without split token but with one space char - tmpString = ' '; - const uint16_t charlength = display->getStringWidth(tmpString); // pix length for a space char - pixlength += charlength; - - while (pixlength <= getDisplaySizeSettings(disp_resolution).Width) { - // add more space chars until pixlength of the final line is almost the display width - tmpString += ' '; // add another space char + } else + # endif // if P036_ENABLE_TICKER + { + String tmpString(LineContent->DisplayLinesV1[Counter].Content); + + ScrollingPageLine->SPLcontent = P36_parseTemplate(tmpString, Counter); + + if (ScrollingPageLine->SPLcontent.length() > 0) { + const int splitIdx = ScrollingPageLine->SPLcontent.indexOf(F("<|>")); // check for split token + + if (splitIdx >= 0) { + // split line into left and right part + tmpString = ScrollingPageLine->SPLcontent; + tmpString.replace(F("<|>"), F(" ")); // replace in tmpString the split token with one space char + display->setFont(FontSizes[LineSettings[Counter].fontIdx].fontData); + uint16_t pixlength = display->getStringWidth(tmpString); // pixlength without split token but with one space char + tmpString = ' '; + const uint16_t charlength = display->getStringWidth(tmpString); // pix length for a space char pixlength += charlength; + + while (pixlength <= getDisplaySizeSettings(disp_resolution).Width) { + // add more space chars until pixlength of the final line is almost the display width + tmpString += ' '; // add another space char + pixlength += charlength; + } + ScrollingPageLine->SPLcontent.replace(F("<|>"), tmpString); // replace in final line the split token with space chars } - ScrollingPageLine->SPLcontent.replace(F("<|>"), tmpString); // replace in final line the split token with space chars } - } - const eAlignment iAlignment = - static_cast(get3BitFromUL(LineContent->DisplayLinesV1[Counter].ModifyLayout, P036_FLAG_ModifyLayout_Alignment)); + const eAlignment iAlignment = + static_cast(get3BitFromUL(LineContent->DisplayLinesV1[Counter].ModifyLayout, P036_FLAG_ModifyLayout_Alignment)); - ScrollingPageLine->Alignment = getTextAlignment(iAlignment); - ScrollingPageLine->SPLidx = Counter; // index to LineSettings[] -} + ScrollingPageLine->Alignment = getTextAlignment(iAlignment); + ScrollingPageLine->SPLidx = Counter; // index to LineSettings[] + } } # if P036_FEATURE_DISPLAY_PREVIEW diff --git a/src/src/PluginStructs/P036_data_struct.h b/src/src/PluginStructs/P036_data_struct.h index 65a41510d1..83c65056bc 100644 --- a/src/src/PluginStructs/P036_data_struct.h +++ b/src/src/PluginStructs/P036_data_struct.h @@ -46,7 +46,7 @@ # define P036_USERDEF_HEADERS 1 // Enable User defined headers # endif // ifndef P036_USERDEF_HEADERS # ifndef P036_ENABLE_TICKER -# define P036_ENABLE_TICKER 1 // Enable ticker function +# define P036_ENABLE_TICKER 1 // Enable ticker function # endif // ifndef # else // ifndef P036_LIMIT_BUILD_SIZE # if defined(P036_SEND_EVENTS) && P036_SEND_EVENTS @@ -144,6 +144,7 @@ # define P036_FLAG_REDUCE_LINE_NO 2 // Bit 2 Reduce line number to fit individual line font settings enum class eHeaderContent : uint8_t { + eNone = 0u, eSSID = 1u, eSysName = 2u, eIP = 3u, @@ -171,10 +172,10 @@ enum class p036_resolution : uint8_t { }; enum class ePageScrollSpeed : uint8_t { - ePSS_VerySlow = 1u, // 800ms - ePSS_Slow = 2u, // 400ms - ePSS_Fast = 4u, // 200ms - ePSS_VeryFast = 8u, // 100ms + ePSS_VerySlow = 1u, // 800ms + ePSS_Slow = 2u, // 400ms + ePSS_Fast = 4u, // 200ms + ePSS_VeryFast = 8u, // 100ms ePSS_Instant = 32u, // 20ms ePSS_Ticker = 255u // tickerspeed depends on line length }; @@ -192,6 +193,9 @@ typedef struct { uint16_t LastWidth = 0; // width of last line in pix uint16_t Width = 0; // width in pix uint8_t SLidx = 0; // index to DisplayLinesV1 + uint8_t reserved22; // Fillers added to achieve better instance/memory alignment (multiple of 8) + uint8_t reserved23; + uint8_t reserved24; } tScrollLine; typedef struct { @@ -201,6 +205,10 @@ typedef struct { uint16_t IdxEnd = 0; // End index of TickerContent for displaying (right side) uint16_t TickerAvgPixPerChar = 0; // max of average pixel per character or pix change per scroll time (100ms) int16_t MaxPixLen = 0; // Max pix length to display (display width + 2*TickerAvgPixPerChar) + # ifdef ESP8266 // Helpful on ESP8266 only, it seems + uint8_t reserved15; // Fillers added to achieve better instance/memory alignment (multiple of 8) + uint8_t reserved16; + # endif // ifdef ESP8266 } tTicker; typedef struct { @@ -208,7 +216,7 @@ typedef struct { # if P036_ENABLE_TICKER tTicker Ticker; # endif // if P036_ENABLE_TICKER - uint16_t wait = 0; // waiting time before scrolling + uint16_t wait = 0; // waiting time before scrolling } tScrollingLines; typedef struct { @@ -312,6 +320,8 @@ typedef struct { uint8_t MaxLines; // max. line count uint8_t WiFiIndicatorLeft; // left of WiFi indicator uint8_t WiFiIndicatorWidth; // width of WiFi indicator + uint8_t reserved7; // Fillers added to achieve better instance/memory alignment (multiple of 8) + uint8_t reserved8; } tSizeSettings; typedef struct { @@ -320,6 +330,11 @@ typedef struct { uint8_t ypos = 0; // ypos for this line uint8_t fontIdx = 0; // font index for this line uint8_t FontHeight = 0; // font height for this line + # ifdef ESP8266 // Helpful on ESP8266 only, it seems + uint8_t reserved6; // Fillers added to achieve better instance/memory alignment (multiple of 8) + uint8_t reserved7; + uint8_t reserved8; + # endif // ifdef ESP8266 } tLineSettings; typedef struct { @@ -352,18 +367,18 @@ struct P036_data_struct : public PluginTaskData_base { static const tSizeSettings& getDisplaySizeSettings(p036_resolution disp_resolution); - bool init(taskIndex_t taskIndex, - uint8_t LoadVersion, - uint8_t Type, - uint8_t Address, - uint8_t Sda, - uint8_t Scl, - p036_resolution Disp_resolution, - bool Rotated, - uint8_t Contrast, - uint16_t DisplayTimer, + bool init(taskIndex_t taskIndex, + uint8_t LoadVersion, + uint8_t Type, + uint8_t Address, + uint8_t Sda, + uint8_t Scl, + p036_resolution Disp_resolution, + bool Rotated, + uint8_t Contrast, + uint16_t DisplayTimer, ePageScrollSpeed ScrollSpeed, - uint8_t NrLines); + uint8_t NrLines); bool isInitialized() const; @@ -475,14 +490,16 @@ struct P036_data_struct : public PluginTaskData_base { bool bReduceLinesPerFrame = false; // frames - uint8_t MaxFramesToDisplay = 0; // total number of frames to display + uint8_t MaxFramesToDisplay = 0; // total number of frames to display uint8_t currentFrameToDisplay = 0; - uint8_t nextFrameToDisplay = 0; // next frame because content changed in PLUGIN_WRITE - uint8_t frameCounter = 0; // need to keep track of framecounter from call to call - uint8_t disableFrameChangeCnt = 0; // counter to disable frame change after JumpToPage in case PLUGIN_READ already scheduled - bool bPageScrollDisabled = true; // first page after INIT or after JumpToPage without scrolling + uint8_t nextFrameToDisplay = 0; // next frame because content changed in PLUGIN_WRITE + uint8_t frameCounter = 0; // need to keep track of framecounter from call to call + uint8_t disableFrameChangeCnt = 0; // counter to disable frame change after JumpToPage in case PLUGIN_READ already scheduled + bool bPageScrollDisabled = true; // first page after INIT or after JumpToPage without scrolling bool bRunning = false; // page updates are rumming = (NetworkConnected() || bScrollWithoutWifi) - bool bUseTicker = false; // scroll line like a ticker + # if P036_ENABLE_TICKER + bool bUseTicker = false; // scroll line like a ticker + # endif // if P036_ENABLE_TICKER OLEDDISPLAY_TEXT_ALIGNMENT textAlignment = TEXT_ALIGN_CENTER;