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