From 2fd9971f413bf4d34da5c3de9fc57c31ebcf6a4f Mon Sep 17 00:00:00 2001 From: Mike La Spina Date: Mon, 7 Jun 2021 14:15:09 -0500 Subject: [PATCH] Add Laser Based I2C Ammeter Feature (#21835) --- Marlin/Configuration_adv.h | 9 +++ Marlin/src/feature/ammeter.cpp | 49 ++++++++++++++ Marlin/src/feature/ammeter.h | 44 +++++++++++++ Marlin/src/feature/spindle_laser.cpp | 8 +++ Marlin/src/lcd/HD44780/marlinui_HD44780.cpp | 23 ++++++- Marlin/src/lcd/dogm/dogm_Statusscreen.h | 42 +++++++++++- Marlin/src/lcd/dogm/status/ammeter.h | 71 +++++++++++++++++++++ Marlin/src/lcd/dogm/status/cooler.h | 64 +++++++++---------- Marlin/src/lcd/dogm/status_screen_DOGM.cpp | 25 ++++++++ Marlin/src/libs/numtostr.cpp | 9 +++ Marlin/src/libs/numtostr.h | 3 + ini/features.ini | 1 + 12 files changed, 314 insertions(+), 34 deletions(-) create mode 100644 Marlin/src/feature/ammeter.cpp create mode 100644 Marlin/src/feature/ammeter.h create mode 100644 Marlin/src/lcd/dogm/status/ammeter.h diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index efde7cde19..50bc85801f 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -3283,6 +3283,15 @@ //#define AIR_ASSIST_PIN 44 // Override the default Air Assist pin #endif + // + // Laser I2C Ammeter (High precision INA226 low/high side module) + // + //#define I2C_AMMETER + #if ENABLED(I2C_AMMETER) + #define I2C_AMMETER_IMAX .1 // Calibration value for the expected current range in Amps (use float e.g. 1.0) + #define I2C_AMMETER_SHUNT_RESISTOR .1 // Calibration shunt resistor value in ohms + #endif + //#define SPINDLE_SERVO // A servo converting an angle to spindle power #ifdef SPINDLE_SERVO #define SPINDLE_SERVO_NR 0 // Index of servo used for spindle control diff --git a/Marlin/src/feature/ammeter.cpp b/Marlin/src/feature/ammeter.cpp new file mode 100644 index 0000000000..01e1084474 --- /dev/null +++ b/Marlin/src/feature/ammeter.cpp @@ -0,0 +1,49 @@ + /** + * Marlin 3D Printer Firmware + * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "../inc/MarlinConfig.h" + +#if ENABLED(I2C_AMMETER) + #include "ammeter.h" + + INA226 ina; + + Ammeter ammeter; + + float Ammeter::scale; + float Ammeter::current; + + void Ammeter::init() { + ina.begin(); + ina.configure(INA226_AVERAGES_16, INA226_BUS_CONV_TIME_1100US, INA226_SHUNT_CONV_TIME_1100US, INA226_MODE_SHUNT_BUS_CONT); + ina.calibrate(I2C_AMMETER_SHUNT_RESISTOR,I2C_AMMETER_IMAX); + } + + float Ammeter::read() { + scale = 1; + current = ina.readShuntCurrent(); + if (current <= .0001) current = 0; // Cleanup lsb bit amplification errors + if (current < .1) scale = 1000; + return current * scale; + } + +#endif //I2C_AMMETER diff --git a/Marlin/src/feature/ammeter.h b/Marlin/src/feature/ammeter.h new file mode 100644 index 0000000000..cc60a2d28a --- /dev/null +++ b/Marlin/src/feature/ammeter.h @@ -0,0 +1,44 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#pragma once + +#include "../inc/MarlinConfigPre.h" + +#include +#include + +#ifndef I2C_AMMETER_IMAX + #define I2C_AMMETER_IMAX .500 // Calibration range 500 Milli Amps +#endif + +class Ammeter { +private: + static float scale; + +public: + static float current; + static void init(); + static float read(); + +}; + +extern Ammeter ammeter; diff --git a/Marlin/src/feature/spindle_laser.cpp b/Marlin/src/feature/spindle_laser.cpp index 100b7c4b26..58dc5ef101 100644 --- a/Marlin/src/feature/spindle_laser.cpp +++ b/Marlin/src/feature/spindle_laser.cpp @@ -34,6 +34,10 @@ #include "../module/servo.h" #endif +#if ENABLED(I2C_AMMETER) + #include "../feature/ammeter.h" +#endif + SpindleLaser cutter; uint8_t SpindleLaser::power; #if ENABLED(LASER_FEATURE) @@ -74,6 +78,10 @@ void SpindleLaser::init() { #if ENABLED(AIR_ASSIST) OUT_WRITE(AIR_ASSIST_PIN, !AIR_ASSIST_ACTIVE); // Init Air Assist OFF #endif + #if ENABLED(I2C_AMMETER) + ammeter.init(); // Init I2C Ammeter + #endif + } #if ENABLED(SPINDLE_LASER_PWM) diff --git a/Marlin/src/lcd/HD44780/marlinui_HD44780.cpp b/Marlin/src/lcd/HD44780/marlinui_HD44780.cpp index 50c80c9fa0..ab3d415c15 100644 --- a/Marlin/src/lcd/HD44780/marlinui_HD44780.cpp +++ b/Marlin/src/lcd/HD44780/marlinui_HD44780.cpp @@ -50,6 +50,10 @@ #include "../../feature/cooler.h" #endif +#if ENABLED(I2C_AMMETER) + #include "../../feature/ammeter.h" +#endif + #if ENABLED(AUTO_BED_LEVELING_UBL) #include "../../feature/bedlevel/bedlevel.h" #endif @@ -588,12 +592,26 @@ FORCE_INLINE void _draw_cooler_status(const char prefix, const bool blink) { #if ENABLED(LASER_COOLANT_FLOW_METER) FORCE_INLINE void _draw_flowmeter_status() { - lcd_put_u8str("~ "); + lcd_put_u8str("~"); lcd_put_u8str(ftostr11ns(cooler.flowrate)); lcd_put_wchar('L'); } #endif +#if ENABLED(I2C_AMMETER) + FORCE_INLINE void _draw_ammeter_status() { + lcd_put_u8str(" "); + ammeter.read(); + if (ammeter.current <= .999) { + lcd_put_u8str(ftostr3ns(ammeter.current)); + lcd_put_u8str("mA"); + } else { + lcd_put_u8str(ftostr12ns(ammeter.current)); + lcd_put_wchar('A'); + } + } +#endif + FORCE_INLINE void _draw_bed_status(const bool blink) { _draw_heater_status(H_BED, TERN0(HAS_LEVELING, blink && planner.leveling_active) ? '_' : LCD_STR_BEDTEMP[0], blink); } @@ -835,6 +853,9 @@ void MarlinUI::draw_status_screen() { #if ENABLED(LASER_COOLANT_FLOW_METER) _draw_flowmeter_status(); #endif + #if ENABLED(I2C_AMMETER) + _draw_ammeter_status(); + #endif #endif // LCD_WIDTH >= 20 diff --git a/Marlin/src/lcd/dogm/dogm_Statusscreen.h b/Marlin/src/lcd/dogm/dogm_Statusscreen.h index 61d22a28ec..db0b66777d 100644 --- a/Marlin/src/lcd/dogm/dogm_Statusscreen.h +++ b/Marlin/src/lcd/dogm/dogm_Statusscreen.h @@ -107,7 +107,18 @@ #define STATUS_FLOWMETER_BYTEWIDTH BW(STATUS_FLOWMETER_WIDTH) #endif - +// +// Laser Ammeter +// +#if !STATUS_AMMETER_WIDTH && ENABLED(I2C_AMMETER) + #include "status/ammeter.h" +#endif +#ifndef STATUS_AMMETER_WIDTH + #define STATUS_AMMETER_WIDTH 0 +#endif +#ifndef STATUS_AMMETER_BYTEWIDTH + #define STATUS_AMMETER_BYTEWIDTH BW(STATUS_AMMETER_WIDTH) +#endif // // Bed @@ -603,6 +614,32 @@ #endif #endif +#if ENABLED(I2C_AMMETER) + #if STATUS_AMMETER_WIDTH + + #ifndef STATUS_AMMETER_X + #define STATUS_AMMETER_X (LCD_PIXEL_WIDTH - (STATUS_AMMETER_BYTEWIDTH + STATUS_FLOWMETER_BYTEWIDTH + STATUS_FAN_BYTEWIDTH + STATUS_CUTTER_BYTEWIDTH + STATUS_COOLER_BYTEWIDTH) * 8) + #endif + + #ifndef STATUS_AMMETER_HEIGHT + #define STATUS_AMMETER_HEIGHT(S) (sizeof(status_ammeter_bmp1) / (STATUS_AMMETER_BYTEWIDTH)) + #endif + + #ifndef STATUS_AMMETER_Y + #define STATUS_AMMETER_Y(S) (18 - STATUS_AMMETER_HEIGHT(S)) + #endif + + #ifndef STATUS_AMMETER_TEXT_X + #define STATUS_AMMETER_TEXT_X (STATUS_AMMETER_X + 7) + #endif + + static_assert( + sizeof(status_ammeter_bmp1) == (STATUS_AMMETER_BYTEWIDTH) * STATUS_AMMETER_HEIGHT(0), + "Status ammeter bitmap (status_ammeter_bmp1) dimensions don't match data." + ); + #endif +#endif + // // Bed Bitmap Properties // @@ -696,6 +733,9 @@ #if ENABLED(LASER_COOLANT_FLOW_METER) #define DO_DRAW_FLOWMETER 1 #endif +#if ENABLED(I2C_AMMETER) + #define DO_DRAW_AMMETER 1 +#endif #if HAS_TEMP_CHAMBER && STATUS_CHAMBER_WIDTH && HOTENDS <= 4 #define DO_DRAW_CHAMBER 1 diff --git a/Marlin/src/lcd/dogm/status/ammeter.h b/Marlin/src/lcd/dogm/status/ammeter.h new file mode 100644 index 0000000000..c98d1eb401 --- /dev/null +++ b/Marlin/src/lcd/dogm/status/ammeter.h @@ -0,0 +1,71 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#pragma once + +// +// lcd/dogm/status/ammeter.h - Status Screen Laser Ammeter bitmaps +// +#if ENABLED(I2C_AMMETER) + + #define STATUS_AMMETER_WIDTH 20 + + const unsigned char status_ammeter_bmp_mA[] PROGMEM = { + B00000000,B11111100,B00000000, + B00000011,B00000011,B00000000, + B00000100,B00000000,B10000000, + B00001000,B00000000,B01000000, + B00010000,B00000110,B00100000, + B00010000,B00001001,B00100000, + B00100000,B00001001,B00010000, + B00100011,B01001111,B00010000, + B11100010,B10101001,B00011100, + B00100010,B10101001,B00010000, + B00100010,B10101001,B00010000, + B00010000,B00000000,B00100000, + B00010000,B00000000,B00100000, + B00001000,B00000000,B01000000, + B00000100,B00000000,B10000000, + B00000011,B00000011,B00000000, + B00000000,B11111100,B00000000 + }; + +const unsigned char status_ammeter_bmp_A[] PROGMEM = { + B00000000,B11111100,B00000000, + B00000011,B00000011,B00000000, + B00000100,B00000000,B10000000, + B00001000,B00000000,B01000000, + B00010000,B00000000,B00100000, + B00010000,B00110000,B00100000, + B00100000,B01001000,B00010000, + B00100000,B01001000,B00010000, + B11100000,B01111000,B00011100, + B00100000,B01001000,B00010000, + B00100000,B01001000,B00010000, + B00010000,B01001000,B00100000, + B00010000,B00000000,B00100000, + B00001000,B00000000,B01000000, + B00000100,B00000000,B10000000, + B00000011,B00000011,B00000000, + B00000000,B11111100,B00000000, +}; + +#endif diff --git a/Marlin/src/lcd/dogm/status/cooler.h b/Marlin/src/lcd/dogm/status/cooler.h index 6cf67a4b62..65c28ec28e 100644 --- a/Marlin/src/lcd/dogm/status/cooler.h +++ b/Marlin/src/lcd/dogm/status/cooler.h @@ -29,40 +29,40 @@ #define STATUS_COOLER_WIDTH 22 const unsigned char status_cooler_bmp2[] PROGMEM = { - B00000100,B00000010,B00000000, - B00000100,B10010010,B01000000, - B00010101,B00001010,B10000000, - B00001110,B00000111,B00000000, - B00111111,B10111111,B11000000, - B00001110,B00000111,B00000000, - B00010101,B00001010,B10000000, - B00100100,B00100010,B01000000, - B00000100,B00100000,B00000000, - B00000001,B00100100,B00000000, - B00000000,B10101000,B00000000, - B00000000,B01110000,B00000000, - B00000111,B11111111,B00000000, - B00000000,B01110000,B00000000, - B00000000,B10101000,B00000000, - B00000001,B00100100,B00000000 + B00000001,B00000000,B10000000, + B00000001,B00100100,B10010000, + B00000101,B01000010,B10100000, + B00000011,B10000001,B11000000, + B00001111,B11101111,B11110000, + B00000011,B10000001,B11000000, + B00000101,B01000010,B10100000, + B00001001,B00001000,B10010000, + B00000001,B00001000,B00000000, + B00000000,B01001001,B00000000, + B00000000,B00101010,B00000000, + B00000000,B00011100,B00000000, + B00000001,B11111111,B11000000, + B00000000,B00011100,B00000000, + B00000000,B00101010,B00000000, + B00000000,B01001001,B00000000 }; const unsigned char status_cooler_bmp1[] PROGMEM = { - B00000100,B00000010,B00000000, - B00000100,B10010010,B01000000, - B00010101,B00001010,B10000000, - B00001010,B00000101,B00000000, - B00110001,B11011000,B11000000, - B00001010,B00000101,B00000000, - B00010101,B00001010,B10000000, - B00100100,B00100010,B01000000, - B00000100,B00100000,B00000000, - B00000001,B00100100,B00000000, - B00000000,B10101000,B00000000, - B00000000,B01010000,B00000000, - B00000111,B10001111,B00000000, - B00000000,B01010000,B00000000, - B00000000,B10101000,B00000000, - B00000001,B00100100,B00000000 + B00000001,B00000000,B10000000, + B00000001,B00100100,B10010000, + B00000101,B01000010,B10100000, + B00000010,B10000001,B01000000, + B00001100,B01110110,B00110000, + B00000010,B10000001,B01000000, + B00000101,B01000010,B10100000, + B00001001,B00001000,B10010000, + B00000001,B00001000,B00000000, + B00000000,B01001001,B00000000, + B00000000,B00101010,B00000000, + B00000000,B00010100,B00000000, + B00000001,B11100011,B11000000, + B00000000,B00010100,B00000000, + B00000000,B00101010,B00000000, + B00000000,B01001001,B00000000 }; #endif diff --git a/Marlin/src/lcd/dogm/status_screen_DOGM.cpp b/Marlin/src/lcd/dogm/status_screen_DOGM.cpp index f05958e675..00e8af66e2 100644 --- a/Marlin/src/lcd/dogm/status_screen_DOGM.cpp +++ b/Marlin/src/lcd/dogm/status_screen_DOGM.cpp @@ -205,6 +205,14 @@ FORCE_INLINE void _draw_centered_temp(const celsius_t temp, const uint8_t tx, co } #endif +#if DO_DRAW_AMMETER + FORCE_INLINE void _draw_centered_current(const float current, const uint8_t tx, const uint8_t ty) { + const char *str = ftostr31ns(current); + const uint8_t len = str[0] != ' ' ? 3 : str[1] != ' ' ? 2 : 1; + lcd_put_u8str(tx - len * (INFO_FONT_WIDTH) / 2 + 1, ty, &str[3-len]); + } +#endif + #if DO_DRAW_HOTENDS // Draw hotend bitmap with current and target temperatures @@ -404,6 +412,13 @@ FORCE_INLINE void _draw_centered_temp(const celsius_t temp, const uint8_t tx, co } #endif +#if DO_DRAW_AMMETER + FORCE_INLINE void _draw_ammeter_status() { + if (PAGE_CONTAINS(28 - INFO_FONT_ASCENT, 28 - 1)) + _draw_centered_current(ammeter.read(), STATUS_AMMETER_TEXT_X, 28); + } +#endif + // // Before homing, blink '123' <-> '???'. // Homed but unknown... '123' <-> ' '. @@ -677,6 +692,13 @@ void MarlinUI::draw_status_screen() { u8g.drawBitmapP(STATUS_FLOWMETER_X, flowmetery, STATUS_FLOWMETER_BYTEWIDTH, flowmeterh, blink && cooler.flowpulses ? status_flowmeter_bmp2 : status_flowmeter_bmp1); #endif + // Laser Ammeter + #if DO_DRAW_AMMETER + const uint8_t ammetery = STATUS_AMMETER_Y(status_ammeter_bmp_mA), + ammeterh = STATUS_AMMETER_HEIGHT(status_ammeter_bmp_mA); + if (PAGE_CONTAINS(ammetery, ammetery + ammeterh - 1)) + u8g.drawBitmapP(STATUS_AMMETER_X, ammetery, STATUS_AMMETER_BYTEWIDTH, ammeterh, (ammeter.current < .1) ? status_ammeter_bmp_mA : status_ammeter_bmp_A); + #endif // Heated Bed TERN_(DO_DRAW_BED, _draw_bed_status(blink)); @@ -690,6 +712,9 @@ void MarlinUI::draw_status_screen() { // Flowmeter TERN_(DO_DRAW_FLOWMETER, _draw_flowmeter_status()); + // Flowmeter + TERN_(DO_DRAW_AMMETER, _draw_ammeter_status()); + // Fan, if a bitmap was provided #if DO_DRAW_FAN if (PAGE_CONTAINS(STATUS_FAN_TEXT_Y - INFO_FONT_ASCENT, STATUS_FAN_TEXT_Y - 1)) { diff --git a/Marlin/src/libs/numtostr.cpp b/Marlin/src/libs/numtostr.cpp index 1e1ac05710..a1e320844a 100644 --- a/Marlin/src/libs/numtostr.cpp +++ b/Marlin/src/libs/numtostr.cpp @@ -217,6 +217,15 @@ const char* ftostr41ns(const_float_t f) { return &conv[2]; } +// Convert unsigned float to string with 123 format +const char* ftostr3ns(const_float_t f) { + const long i = UINTFLOAT(f, 3); + conv[4] = DIGIMOD(i, 100); + conv[5] = DIGIMOD(i, 10); + conv[6] = DIGIMOD(i, 1); + return &conv[4]; +} + // Convert signed float to fixed-length string with 12.34 / _2.34 / -2.34 or -23.45 / 123.45 format const char* ftostr42_52(const_float_t f) { if (f <= -10 || f >= 100) return ftostr52(f); // -23.45 / 123.45 diff --git a/Marlin/src/libs/numtostr.h b/Marlin/src/libs/numtostr.h index b058f3cdf6..5ebf6e1b22 100644 --- a/Marlin/src/libs/numtostr.h +++ b/Marlin/src/libs/numtostr.h @@ -74,6 +74,9 @@ const char* ftostr31ns(const_float_t x); // Convert unsigned float to string with 123.4 format const char* ftostr41ns(const_float_t x); +// Convert unsigned float to string with 123 format +const char* ftostr3ns(const_float_t x); + // Convert signed float to fixed-length string with 12.34 / _2.34 / -2.34 or -23.45 / 123.45 format const char* ftostr42_52(const_float_t x); diff --git a/ini/features.ini b/ini/features.ini index f8f995c69f..15f6c2a138 100644 --- a/ini/features.ini +++ b/ini/features.ini @@ -30,6 +30,7 @@ HAS_L64XX = Arduino-L6470@0.8.0 NEOPIXEL_LED = adafruit/Adafruit NeoPixel@~1.8.0 src_filter=+ TEMP_.+_IS_MAX31865 = Adafruit MAX31865 library@~1.1.0 +I2C_AMMETER = peterus/INA226Lib@1.1.2 USES_LIQUIDCRYSTAL = fmalpartida/LiquidCrystal@1.5.0 USES_LIQUIDCRYSTAL_I2C = marcoschwartz/LiquidCrystal_I2C@1.1.4 USES_LIQUIDTWI2 = LiquidTWI2@1.2.7