Skip to content

M355 case light improvements (replaces PR #5685) #6797

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 31, 2017
Merged

M355 case light improvements (replaces PR #5685) #6797

merged 1 commit into from
May 31, 2017

Conversation

Bob-the-Kuhn
Copy link
Contributor

This PR is a 1.1x version of PR #5685.

Initial load is just of the functional files. The Configuration_adv.h changes will be uploaded shortly.

This code has not been tested.


This code currently is modelled after the M42 command:
M355 Sxxx - xxx varies from 0-255, on/off for digital pins is 127/128

There have been discussions about using the following methods:

  1. M42 system - one parameter (S) & on/off for digital at 127/128
  2. Two parameters - S is Boolean, P overrides & decision is at 127/128
  3. Two parameters - S is Boolean, P overrides & decision is at 0/1

@Bob-the-Kuhn Bob-the-Kuhn added the T: Design Concept Technical ideas about ways and methods. label May 19, 2017
@Bob-the-Kuhn
Copy link
Contributor Author

I've verified that this change is 95% functional. I still need to check that the "useable PWM" routine is working 100%. I know it connects up to the 3 PWMs I've tried so far. I also need to check the PWMs on non-2560 boards.

I need a pointer to an LCD menu type that gives immediate feedback as you twirl the encoder. Right now the routine is using "MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(int3" which only calls the function when the encoder button is pressed. I expect the user would like to see the LED/lights dim/brighten as they twirl the knob.

@thinkyhead
Copy link
Member

Let me provide a 'realtime' edit macro for you… hold on…

@thinkyhead
Copy link
Member

thinkyhead commented May 21, 2017

#6810 adds a flag to do live editing. When this flag is set to true the callback will be called on every value change. For example:

#if HAS_LCD_CONTRAST

  void lcd_callback_set_contrast() { set_lcd_contrast(lcd_contrast); }

#endif // HAS_LCD_CONTRAST

. . .

    #if HAS_LCD_CONTRAST
      MENU_ITEM_EDIT_CALLBACK(int3, MSG_CONTRAST, &lcd_contrast, 0, 63,
        lcd_callback_set_contrast, true);
    #endif

@Bob-the-Kuhn
Copy link
Contributor Author

@thinkyhead - thanks. The live update works nicely.

@@ -176,7 +176,7 @@
* M304 - Set bed PID parameters P I and D. (Requires PIDTEMPBED)
* M350 - Set microstepping mode. (Requires digital microstepping pins.)
* M351 - Toggle MS1 MS2 pins directly. (Requires digital microstepping pins.)
* M355 - Turn the Case Light on/off and set its brightness. (Requires CASE_LIGHT_PIN)
* M355 - Sets Case Light brightness. (Requires CASE_LIGHT_PIN)
Copy link
Member

@thinkyhead thinkyhead May 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency, please phrase descriptions of G-codes as if they are prefaced with "This item will…" and not "This item…".

BAD: "Sets the frequency to 100 and opens the valve"
GOOD: "Set the frequency to 100 and open the valve"

I'm adding this guideline and others to the code and documentation style guides under construction. In general, just imitate the existing styles. Try to camouflage your code so it looks like it was always there.

#endif
uint8_t case_light_brightness;

void update_case_light() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indent by 2 spaces.

#else
SERIAL_PROTOCOLLNPGM("Cap:TOGGLE_LIGHTS:0");
SERIAL_PROTOCOLLNPGM("Cap:CASE_LIGHT_BRIGHTNESS:0");
Copy link
Member

@thinkyhead thinkyhead May 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@repetier Have you already been following this? I already documented Cap:TOGGLE_LIGHTS on the RepRap wiki so maybe you already incorporated it…? Sorry there's so much mucking with these things immediately after release. I thought these features were more developed.

@Bob-the-Kuhn I'm not in agreement with dropping the separate on/off function. I think it makes good sense to keep them separate, so that the light may be toggled off and on without needing to specify a brightness every time. Imagine a case light interface in which rotating the dial sets the brightness, while clicking toggles the on/off state. When toggling that on/off state, it should retain the brightness that was last set.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I was not following this. This change is not good as you say it is not possible to toggle light on/off. Server e.g. detects Cap:TOGGLE_LIGHTS and if not present you can not control lights in interface. Currently We use only S0/S1 to toggle light on/off which is what this capability defines. g as you can still toggle it - and I would like to see it continued to work, you should still send Cap:TOGGLE_LIGHTS:1
The fact that you can adjust brightness is an additional feature, so if that is the case add the capability. That way if server ever learns to adjust brightness it can detect it and until then it will just toggle and user has to manually adjust brightness.

Also PLEASE keep S0/1 for toggle and not switch to T0/1 - hosts use S and is just a pain if such things change knowing there are 100 forks having S still then.

if (!useable_hardware_PWM(CASE_LIGHT_PIN)) {
SERIAL_ECHO_START;
SERIAL_ECHOPGM("Case lights ");
(case_light_brightness >= CASE_LIGHT_THRESHOLD) ? SERIAL_ECHOLNPGM("on") : SERIAL_ECHOLNPGM("off");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

serialprintPGM(case_light_brightness >= CASE_LIGHT_THRESHOLD ? PSTR("on") : PSTR("off"));

SERIAL_ECHOPGM("Case lights ");
(case_light_brightness >= CASE_LIGHT_THRESHOLD) ? SERIAL_ECHOLNPGM("on") : SERIAL_ECHOLNPGM("off");
}
else SERIAL_PROTOCOLLNPAIR("echo:Case lights ", case_light_brightness);
Copy link
Member

@thinkyhead thinkyhead May 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use SERIAL_ECHO_START. Don't preface with another echo:.

@@ -3489,6 +3487,7 @@ void kill_screen(const char* lcd_msg) {
ENCODER_DIRECTION_NORMAL(); \
if ((int32_t)encoderPosition < 0) encoderPosition = 0; \
if ((int32_t)encoderPosition > maxEditValue) encoderPosition = maxEditValue; \
bool do_it_live = liveEdit;\
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this line. I thought I got it in the conflict resolution I just did, but apparently not.

else
MENU_ITEM(function, MSG_LIGHTS_ON, toggle_case_light);
case_light_brightness_int = case_light_brightness;
MENU_ITEM_EDIT_CALLBACK(int3, MSG_CASE_LIGHTS, &case_light_brightness_int, 0, 255,case_light_level, true);
Copy link
Member

@thinkyhead thinkyhead May 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reduce indentation by 2 spaces. Insert 1 space after 255,.

#ifndef MSG_LIGHTS_ON
#define MSG_LIGHTS_ON _UxGT("Case light on")
#ifndef MSG_CASE_LIGHTS
#define MSG_CASE_LIGHTS _UxGT("Case lights")
Copy link
Member

@thinkyhead thinkyhead May 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Case light" is the more colloquial usage (even if you have 2 or more bulbs).

@Bob-the-Kuhn
Copy link
Contributor Author

I'll implement all the above, including the on/off function.

@Bob-the-Kuhn
Copy link
Contributor Author

I just looked at the RepRap wiki & it doesn't mention variable intensity.

Looks like we'll need to modify it.

I'm not a fan of changing published documentation. Either we change the documentation or we drop the variable intensity. I'd rather change the documentation in this case.

The issues/questions I see are:

  • Has the RepRap firmware implemented S0 off / S1 on for M355?
  • It looks like the M42 command at one time was S0 off / S1 on but is now S0-255. Is this correct?
  • For M42 S0-255 - does RepRap implement the on/off (non PWM) function at 127/128 like Marlin does?

My preference is to make M355 as close to the current M42 as possible (S0-255 with the on/off (non PWM) function at 127/128). The code would have one variable for intensity and one variable for on/off.

I definitely want to hear from the Repetier folks on this.

I'll put together the LCD menu that implements:

  • variable intensity
  • on/off
  • on resumes with the last intensity

@Bob-the-Kuhn
Copy link
Contributor Author

Does adding a parameter just for the on/off function help?

I'm wondering if the following is useful:
T - toggle light
T1 - turn light on at last intensity
T0 - turn light off

Both S and T could be in the g-code at the same time.

@Bob-the-Kuhn
Copy link
Contributor Author

As long as I'm playing - how about changing the power on default from on/off to 0-255?

@dot-bob dot-bob mentioned this pull request May 22, 2017
@repetier
Copy link

@Bob-the-Kuhn Please keep S0/1 for on off. It is not the documentation that needs to be changed but also all host softwares using this already!
Such on/off and intensity is quite simple. Add e.g. P0-255 for power/pwm however and set default 255 on startup. M355 Px changes power but does not enable/disable (if enabled it would change intensity of course). S1 would enable stored power. M355 S1 P128 would enable with 50% power. That keeps everything compatible with the possibility to change intensity.

@Bob-the-Kuhn
Copy link
Contributor Author

How about this?

 * M355: Turn case light on/off and set brightness
 *
 *   P<byte>  Set case light brightness (PWM pin required)
 *   P 0-127 off (if not using a PWM pin)
 *   P 128-255 on
 *
 *   S<bool>  Set case light on/off
 *   S        Toggle case light
 *      
 *   S parameter takes precedence over P parameter
 *   
 *   When S turns on the light on a PWM pin then the current brightness level is used/restored
 *
 *   M355 P200 S0 turns off the light & sets the brightness level
 *   M355 S1 turns on the light with a brightness of 200 (assuming a PWM pin)

I added a toggle function. Keep it or get rid of it?

@repetier
Copy link

Quite good but for case of non pwm for brightness P should simply have no function. On and off is already defined. No need to add a case where setting P also has the S function if S is omitted.

@Bob-the-Kuhn
Copy link
Contributor Author

Bob-the-Kuhn commented May 22, 2017

/**
 * M355: Turn case light on/off and set brightness
 *
 *   P<byte>  Set case light brightness (PWM pin required - ignored otherwise)
 *
 *   S<bool>  Set case light on/off
 *      
 *   When S turns on the light on a PWM pin then the current brightness level is used/restored
 *
 *   M355 P200 S0 turns off the light & sets the brightness level
 *   M355 S1 turns on the light with a brightness of 200 (assuming a PWM pin)

@Bob-the-Kuhn
Copy link
Contributor Author

@thinkyhead - right now it says Cap:CASE_LIGHT_BRIGHTNESS:1. Do you want me to change it back to TOGGLE and add a toggle function?

If you do want a toggle function I'm thinking "S" with no value after it would do the toggle.

@repetier
Copy link

M355 S1/0 is the toggle function, so you should put back
SERIAL_PROTOCOLLNPGM("Cap:TOGGLE_LIGHTS:1");
to indicate it is still working.
Cap:CASE_LIGHT_BRIGHTNESS:1 would then inidicate that P attribute has a function.

@Bob-the-Kuhn
Copy link
Contributor Author

Bob-the-Kuhn commented May 23, 2017

Is this what you had in mind?

    // CASE LIGHTS (M355)
    #if HAS_CASE_LIGHT
      SERIAL_PROTOCOLLNPGM("Cap:TOGGLE_LIGHTS:1");
      bool useable_hardware_PWM(uint8_t pin);
      if(useable_hardware_PWM(CASE_LIGHT_PIN)) {
        SERIAL_PROTOCOLLNPGM("Cap:CASE_LIGHT_BRIGHTNESS:1");
      }
      else 
        SERIAL_PROTOCOLLNPGM("Cap:CASE_LIGHT_BRIGHTNESS:0");
    #else
      SERIAL_PROTOCOLLNPGM("Cap:TOGGLE_LIGHTS:0");      
      SERIAL_PROTOCOLLNPGM("Cap:CASE_LIGHT_BRIGHTNESS:0");
    #endif

@thinkyhead
Copy link
Member

thinkyhead commented May 23, 2017

if(useable_hardware_PWM(CASE_LIGHT_PIN)) {

Something like that will work. Is that evaluated at compile-time? If the useable_hardware_PWM function didn't need to be declared here it would be cleaner. Maybe we can add macros in Conditionals*.h or pins.h for each pin that can work with or without PWM, then use them in the code in those cases.

@@ -2487,7 +2513,7 @@ void kill_screen(const char* lcd_msg) {
MENU_ITEM(submenu, MSG_FILAMENT, lcd_control_filament_menu);

#if HAS_LCD_CONTRAST
MENU_ITEM_EDIT_CALLBACK(int3, MSG_CONTRAST, &lcd_contrast, LCD_CONTRAST_MIN, LCD_CONTRAST_MAX, lcd_callback_set_contrast, true);
MENU_ITEM_EDIT_CALLBACK(int3, MSG_CONTRAST, (int) &lcd_contrast, LCD_CONTRAST_MIN, LCD_CONTRAST_MAX, lcd_callback_set_contrast, true);
Copy link
Member

@thinkyhead thinkyhead May 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An address should not be cast to an int, but an int*.

MENU_ITEM_EDIT_CALLBACK(int3, MSG_CASE_LIGHT_BRIGHTNESS, &case_light_brightness_int, 0, 255, level_case_light, true);
}
MENU_ITEM(function, MSG_CASE_LIGHT_ON, on_case_light);
MENU_ITEM(function, MSG_CASE_LIGHT_OFF, off_case_light);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this is even better:

    MENU_ITEM_EDIT_CALLBACK(bool, MSG_CASE_LIGHT, &case_light_on, update_case_light);

…then no other functions are needed for toggling.

#define MSG_CASE_LIGHT_OFF _UxGT("Light OFF")
#endif
#ifndef MSG_CASE_LIGHT_TOGGLE
#define MSG_CASE_LIGHT_TOGGLE _UxGT("Light TOGGLE")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This string doesn't seem to be used anymore.

#endif
#ifndef MSG_CASE_LIGHT_OFF
#define MSG_CASE_LIGHT_OFF _UxGT("Light OFF")
#endif
Copy link
Member

@thinkyhead thinkyhead May 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These on/off messages can go away by using the bool callback below. "On" and "Off" will be automatically shown and translated.

@@ -665,6 +671,7 @@
#define UBL_MESH_MAX_Y (Y_MAX_POS - (UBL_MESH_INSET))
#endif


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra line fell in.

@repetier
Copy link

@Bob-the-Kuhn Yes that is the solution I mean.

@Bob-the-Kuhn
Copy link
Contributor Author

@thinkyhead - I've implemented all of the requested changes.

The LCD menu changes were pretty straight forward.

Changing the USEABLE_HARDWARE_PWM function to a series of macros may not be implemented the way you expected. 75% of the macros you see are involved in checking that other functions have not already grabbed the PWM.

Copy link
Member

@thinkyhead thinkyhead left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functionally, looks fine. Just some tweaks needed, and decide on a better home for the PWM macros.

#error "unknown CPU"
#endif

#define USEABLE_HARDWARE_PWM(p) (PWM_PINS(p) && !PWM_CHK(p))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add these kinds of macros to the fastio.h and HAL suite and limit things in the Conditionals.h files to things which change depending on user configuration.

#if HAS_CASE_LIGHT
SERIAL_PROTOCOLLNPGM("Cap:TOGGLE_LIGHTS:1");
bool USEABLE_HARDWARE_PWM(uint8_t pin);
if(USEABLE_HARDWARE_PWM(CASE_LIGHT_PIN)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One space between if and the opening parenthesis, please.

pinMode(CASE_LIGHT_PIN, OUTPUT); // digitalWrite doesn't set the port mode
uint8_t case_light_bright = (uint8_t)case_light_brightness;
if (case_light_on) {
if(USEABLE_HARDWARE_PWM(CASE_LIGHT_PIN)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if *space* (

if (parser.seen('S')) {
if (parser.has_value()) { case_light_on = parser.value_bool();}
update_case_light();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    uint8_t args = 0;
    if (parser.seen('P')) ++args, case_light_brightness = parser.value_byte();
    if (parser.seen('S')) ++args, case_light_on = parser.value_bool(); // S by itself = S1
    if (args) update_case_light();

SERIAL_ECHOLN("Case light: off");
}
else {
if (!USEABLE_HARDWARE_PWM(CASE_LIGHT_PIN)) {SERIAL_ECHOLN("Case light: on"); }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No braces needed around SERIAL_ECHOLN. The macro is wrapped in do{}while(0).

@thinkyhead
Copy link
Member

thinkyhead commented May 29, 2017

75% of the macros you see are involved in checking that other functions have not already grabbed the PWM.

I reckon we could probably make a nice class to manage all the PWMs. I made a nice function specifically for Timer5 that works pretty well. Check it out…

/**
 * Set Timer 5 PWM frequency in Hz, from 3.8Hz up to ~16MHz
 * with a minimum resolution of 100 steps.
 */
uint16_t set_pwm_frequency_hz(const float &hz, const float dca, const float dcb, const float dcc) {
  float count = 0;
  if (hz > 0 && (dca || dcb || dcc)) {
    count = float(F_CPU) / hz;            // 1x prescaler, TOP for 16MHz base freq.
    uint16_t prescaler;                   // Range of 30.5Hz (65535) 64.5KHz (>31)

         if (count >= 255. * 256.) { prescaler = 1024; SET_CS(5, PRESCALER_1024); }
    else if (count >= 255. * 64.)  { prescaler = 256;  SET_CS(5,  PRESCALER_256); }
    else if (count >= 255. * 8.)   { prescaler = 64;   SET_CS(5,   PRESCALER_64); }
    else if (count >= 255.)        { prescaler = 8;    SET_CS(5,    PRESCALER_8); }
    else                           { prescaler = 1;    SET_CS(5,    PRESCALER_1); }

    count /= float(prescaler);
    pwm_top = round(count);               // Get the rounded count

    ICR5 = (uint16_t)pwm_top - 1;         // Subtract 1 for TOP
    OCR5A = pwm_top * abs(dca);           // Update and scale DCs
    OCR5B = pwm_top * abs(dcb);
    OCR5C = pwm_top * abs(dcc);
    _SET_COM(5, A, dca ? (dca < 0 ? COM_SET_CLEAR : COM_CLEAR_SET) : COM_NORMAL); // Set compare modes
    _SET_COM(5, B, dcb ? (dcb < 0 ? COM_SET_CLEAR : COM_CLEAR_SET) : COM_NORMAL);
    _SET_COM(5, C, dcc ? (dcc < 0 ? COM_SET_CLEAR : COM_CLEAR_SET) : COM_NORMAL);

    SET_WGM(5, FAST_PWM_ICRn);            // Fast PWM with ICR5 as TOP

    //SERIAL_ECHOLNPGM("Timer 5 Settings:");
    //SERIAL_ECHOLNPAIR("  Prescaler=", prescaler);
    //SERIAL_ECHOLNPAIR("        TOP=", ICR5);
    //SERIAL_ECHOLNPAIR("      OCR5A=", OCR5A);
    //SERIAL_ECHOLNPAIR("      OCR5B=", OCR5B);
    //SERIAL_ECHOLNPAIR("      OCR5C=", OCR5C);
  }
  else {
    // Restore the default for Timer 5
    SET_WGM(5, PWM_PC_8);                 // PWM 8-bit (Phase Correct)
    SET_COMS(5, NORMAL, NORMAL, NORMAL);  // Do nothing
    SET_CS(5, PRESCALER_64);              // 16MHz / 64 = 250KHz
    OCR5A = OCR5B = OCR5C = 0;
  }
  return round(count);
}

@Bob-the-Kuhn
Copy link
Contributor Author

All requested changes have been made.

==============================

Configuration_adv.h changes

==============================

add "live" LCD update

==============================

P & S version

==============================

final (hopefully) tested version

==============================

update M115 capabilities print

==============================

Menu changes portion of the requested changes

==============================

changed USEABLE_HARDWARE_PWM from a function to a series of macros

==============================

changes per review
@Bob-the-Kuhn Bob-the-Kuhn merged commit 65bd4c8 into MarlinFirmware:bugfix-1.1.x May 31, 2017
@Bob-the-Kuhn Bob-the-Kuhn deleted the M355-case-lightimprovements-(1.1.x) branch June 14, 2017 22:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T: Design Concept Technical ideas about ways and methods.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants